hpke-js
Index
Supported Features
HPKE Modes
Key Encapsulation Machanisms (KEMs)
KEMs | Browser | Node.js | Deno |
---|
DHKEM (P-256, HKDF-SHA256) | ✅ | ✅ | |
DHKEM (P-384, HKDF-SHA384) | ✅ | ✅ | |
DHKEM (P-521, HKDF-SHA512) | ✅ | ✅ | |
DHKEM (X25519, HKDF-SHA256) | ✅ | ✅ | |
DHKEM (X448, HKDF-SHA512) | | | |
Key Derivation Functions (KDFs)
KDFs | Browser | Node.js | Deno |
---|
HKDF-SHA256 | ✅ | ✅ | |
HKDF-SHA384 | ✅ | ✅ | |
HKDF-SHA512 | ✅ | ✅ | |
Authenticated Encryption with Associated Data (AEAD) Functions
AEADs | Browser | Node.js | Deno |
---|
AES-128-GCM | ✅ | ✅ | |
AES-256-GCM | ✅ | ✅ | |
ChaCha20Poly1305 | ✅ | ✅ | |
Export Only | ✅ | ✅ | |
Warnings and Restrictions
- Although this library has been passed the following test vectors, it has not been formally audited.
- The upper limit of the AEAD sequence number is further rounded to JavaScript Number.MAX_SAFE_INTEGER (
2^53 - 1
).
Installation
Using npm:
npm install hpke-js
Using unpkg CDN:
<script src="https://unpkg.com/hpke-js/dist/hpke.min.js"></script>
<script src="https://unpkg.com/hpke-js@0.8.0/dist/hpke.min.js"></script>
Using jsDelivr CDN:
<script src="https://cdn.jsdelivr.net/npm/hpke-js/dist/hpke.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/hpke-js@0.8.0/dist/hpke.min.js"></script>
Usage
This section shows some typical usage examples.
Base mode
On browser:
<html>
<head></head>
<body>
<script src="https://unpkg.com/hpke-js/dist/hpke.min.js"></script>
<script type="text/javascript">
async function doHpke() {
const suite = new hpke.CipherSuite({
kem: hpke.Kem.DhkemP256HkdfSha256,
kdf: hpke.Kdf.HkdfSha256,
aead: hpke.Aead.Aes128Gcm
});
const rkp = await suite.generateKeyPair();
const sender = await suite.createSenderContext({
recipientPublicKey: rkp.publicKey
});
const recipient = await suite.createRecipientContext({
recipientKey: rkp,
enc: sender.enc,
});
const ct = await sender.seal(new TextEncoder().encode('hello world!'));
const pt = await recipient.open(ct);
alert(new TextDecoder().decode(pt));
}
</script>
<button type="button" onclick="doHpke()">do HPKE</button>
</body>
</html>
On Node.js:
const { Kem, Kdf, Aead, CipherSuite } = require('hpke-js');
async function doHpke() {
const suite = new CipherSuite({
kem: Kem.DhkemP256HkdfSha256,
kdf: Kdf.HkdfSha256,
aead: Aead.Aes128Gcm
});
const rkp = await suite.generateKeyPair();
const sender = await suite.createSenderContext({
recipientPublicKey: rkp.publicKey
});
const recipient = await suite.createRecipientContext({
recipientKey: rkp,
enc: sender.enc,
});
const ct = await sender.seal(new TextEncoder().encode('my-secret-message'));
const pt = await recipient.open(ct);
console.log('decrypted: ', new TextDecoder().decode(pt));
}
doHpke();
Base mode with Single-Shot APIs
On Node.js:
const { Kem, Kdf, Aead, CipherSuite } = require('hpke-js');
async function doHpke() {
const suite = new CipherSuite({
kem: Kem.DhkemP256HkdfSha256,
kdf: Kdf.HkdfSha256,
aead: Aead.Aes128Gcm
});
const rkp = await suite.generateKeyPair();
const pt = new TextEncoder().encode('my-secret-message'),
const { ct, enc } = await suite.seal({ recipientPublicKey: rkp.publicKey }, pt);
const pt = await suite.open({ recipientKey: rkp, enc: enc }, ct);
console.log('decrypted: ', new TextDecoder().decode(pt));
}
doHpke();
Base mode with bidirectional encryption
On Node.js:
const { Kem, Kdf, Aead, CipherSuite } = require('hpke-js');
const te = new TextEncoder();
const td = new TextDecoder();
async function doHpke() {
const suite = new CipherSuite({
kem: Kem.DhkemP256HkdfSha256,
kdf: Kdf.HkdfSha256,
aead: Aead.Aes128Gcm,
});
const rkp = await suite.generateKeyPair();
const sender = await suite.createSenderContext({
recipientPublicKey: rkp.publicKey,
});
const recipient = await suite.createRecipientContext({
recipientKey: rkp,
enc: sender.enc,
});
await sender.setupBidirectional(te.encode('seed-for-key'), te.encode('seed-for-nonce'));
await recipient.setupBidirectional(te.encode('seed-for-key'), te.encode('seed-for-nonce'));
const ct = await sender.seal(te.encode('my-secret-message-s'));
const pt = await recipient.open(ct);
console.log('recipient decrypted: ', td.decode(pt));
const rct = await recipient.seal(te.encode('my-secret-message-r'));
const rpt = await sender.open(rct);
console.log('sender decrypted: ', td.decode(rpt));
}
doHpke();
Base mode with export-only AEAD
On Node.js:
const suite = new CipherSuite({
kem: Kem.DhkemP256HkdfSha256,
kdf: Kdf.HkdfSha256,
aead: Aead.ExportOnly,
});
const rkp = await suite.generateKeyPair();
const sender = await suite.createSenderContext({
recipientPublicKey: rkp.publicKey,
});
const recipient = await suite.createRecipientContext({
recipientKey: rkp,
enc: sender.enc,
});
const te = new TextEncoder();
const pskS = sender.export(te.encode('jugemujugemu'), 32);
const pskR = recipient.export(te.encode('jugemujugemu'), 32);
}
doHpke();
PSK mode
On Node.js:
const { Kem, Kdf, Aead, CipherSuite } = require('hpke-js');
async function doHpke() {
const suite = new CipherSuite({
kem: Kem.DhkemP256HkdfSha256,
kdf: Kdf.HkdfSha256,
aead: Aead.Aes128Gcm
});
const rkp = await suite.generateKeyPair();
const sender = await suite.createSenderContext({
recipientPublicKey: rkp.publicKey,
psk: {
id: new TextEncoder().encode('our-pre-shared-key-id'),
key: new TextEncoder().encode('jugemujugemugokounosurikirekaija'),
}
});
const recipient = await suite.createRecipientContext({
recipientKey: rkp,
enc: sender.enc,
psk: {
id: new TextEncoder().encode('our-pre-shared-key-id'),
key: new TextEncoder().encode('jugemujugemugokounosurikirekaija'),
}
});
const ct = await sender.seal(new TextEncoder().encode('my-secret-message'));
const pt = await recipient.open(ct);
console.log('decrypted: ', new TextDecoder().decode(pt));
}
doHpke();
Auth mode
On Node.js:
const { Kem, Kdf, Aead, CipherSuite } = require('hpke-js');
async function doHpke() {
const suite = new CipherSuite({
kem: Kem.DhkemP256HkdfSha256,
kdf: Kdf.HkdfSha256,
aead: Aead.Aes128Gcm
});
const rkp = await suite.generateKeyPair();
const skp = await suite.generateKeyPair();
const sender = await suite.createSenderContext({
recipientPublicKey: rkp.publicKey,
senderKey: skp
});
const recipient = await suite.createRecipientContext({
recipientKey: rkp,
enc: sender.enc,
senderPublicKey: skp.publicKey
});
const ct = await sender.seal(new TextEncoder().encode('my-secret-message'));
const pt = await recipient.open(ct);
console.log('decrypted: ', new TextDecoder().decode(pt));
}
doHpke();
AuthPSK mode
On Node.js:
const { Kem, Kdf, Aead, CipherSuite } = require('hpke-js');
async function doHpke() {
const suite = new CipherSuite({
kem: Kem.DhkemP256HkdfSha256,
kdf: Kdf.HkdfSha256,
aead: Aead.Aes128Gcm
});
const rkp = await suite.generateKeyPair();
const skp = await suite.generateKeyPair();
const sender = await suite.createSenderContext({
recipientPublicKey: rkp.publicKey,
senderKey: skp,
psk: {
id: new TextEncoder().encode('our-pre-shared-key-id'),
key: new TextEncoder().encode('jugemujugemugokounosurikirekaija'),
}
});
const recipient = await suite.createRecipientContext({
recipientKey: rkp,
enc: sender.enc,
senderPublicKey: skp.publicKey,
psk: {
id: new TextEncoder().encode('our-pre-shared-key-id'),
key: new TextEncoder().encode('jugemujugemugokounosurikirekaija'),
}
});
const ct = await sender.seal(new TextEncoder().encode('my-secret-message'));
const pt = await recipient.open(ct);
console.log('decrypted: ', new TextDecoder().decode(pt));
}
doHpke();
Contributing
We welcome all kind of contributions, filing issues, suggesting new features or sending PRs.
References