Skip to content

Instantly share code, notes, and snippets.

@not-ivy
Last active November 6, 2023 00:14
Show Gist options
  • Save not-ivy/50eff8d4e9ead86ab66ed0d1774b53fd to your computer and use it in GitHub Desktop.
Save not-ivy/50eff8d4e9ead86ab66ed0d1774b53fd to your computer and use it in GitHub Desktop.
a tl;dr for hpke

HPKE Flow

sequenceDiagram
  actor Initiator
  actor Recipient
  participant KEM
  participant KDF
  participant AEAD

  Initiator->>KEM: Generate ephemeral key pair
  KEM->>Initiator: Ephemeral public key
  KEM->>Recipient: Encapsulated shared secret

  Initiator->>KDF: Derive shared secret from ephemeral key pair
  KDF->>Initiator: Shared secret

  Initiator->>AEAD: Encrypt message with shared secret
  AEAD->>Initiator: Encrypted message

  Initiator->>Recipient: Encrypted message, Ephemeral public key, Encapsulated shared secret
  Recipient->>KEM: Decrypt encapsulated shared secret using private key
  KEM->>Recipient: Shared secret

  Recipient->>KDF: Derive shared secret from ephemeral key pair
  KDF->>Recipient: Shared secret

  Recipient->>AEAD: Decrypt message with shared secret
  AEAD->>Recipient: Plaintext message
Loading
Primitive Purpose Input Output
KEM Derive shared from public key pair Public key Shared secret
KDF Derive one or more keys from input keying material Input keying material One or more keys
AEAD Encrypt and authenticate plaintext Plaintext, associated data, key Ciphertext, authentication tag

Examples

JavaScript

// Define the KEM, KDF, and AEAD algorithms to use
const kem = 'ECDH-ES';
const kdf = 'HKDF-SHA256';
const aead = 'AES-GCM-128';

// Generate an ephemeral key pair for the initiator
const initiatorKeyPair = crypto.generateKeyPairSync('ec', { namedCurve: 'secp256k1' });
const initiatorPublicKey = initiatorKeyPair.publicKey.export({ format: 'spki', type: 'pem' });

// Generate a shared secret using the KEM
const sharedSecret = crypto.generateSharedSecret(initiatorKeyPair.privateKey, kem);

// Derive a key from the shared secret using the KDF
const key = crypto.pbkdf2Sync(sharedSecret, 'salt', 10000, 32, kdf);

// Encrypt the message using the AEAD
const message = Buffer.from('Hello, world!');
const encryptedMessage = crypto.subtle.encrypt({ name: aead }, key, message);

// Send the encrypted message and the initiator's public key to the recipient
const recipientPublicKey = ...; // Get the recipient's public key

// Decrypt the message using the recipient's private key
const recipientKeyPair = crypto.generateKeyPairSync('ec', { namedCurve: 'secp256k1' });
const recipientPrivateKey = recipientKeyPair.privateKey.import({ format: 'pkcs8', type: 'pem' });

const decryptedMessage = await crypto.subtle.decrypt({ name: aead }, recipientPrivateKey, encryptedMessage);

// Decrypt the message using the shared secret and the KDF
const derivedKey = crypto.pbkdf2Sync(sharedSecret, 'salt', 10000, 32, kdf);
const decryptedMessageBuffer = crypto.subtle.decrypt({ name: aead }, derivedKey, encryptedMessage);

// Convert the decrypted message to a string
const decryptedMessageString = decryptedMessageBuffer.toString();

console.log('Decrypted message:', decryptedMessageString);

Read more

https://developer.apple.com/documentation/cryptokit/hpke

https://www.franziskuskiefer.de/p/tldr-hybrid-public-key-encryption/

https://blog.cloudflare.com/hybrid-public-key-encryption/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment