diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 73431fbfe..24defbf53 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -32,6 +32,11 @@ jobs:
run: |
deno fmt --check
deno task test
+ - name: Run deno test for /x/dhkem-x25519
+ working-directory: ./x/dhkem-x25519
+ run: |
+ deno fmt --check
+ deno task test
- name: Run deno test for /x/dhkem-secp256k1
working-directory: ./x/dhkem-secp256k1
run: |
diff --git a/.github/workflows/ci_browser.yml b/.github/workflows/ci_browser.yml
index 2fc14c7ee..a31eaa6e8 100644
--- a/.github/workflows/ci_browser.yml
+++ b/.github/workflows/ci_browser.yml
@@ -26,6 +26,8 @@ jobs:
deno task minify > test/runtimes/browsers/pages/src/hpke.js
mkdir test/runtimes/browsers/pages/core
cp -rf core/test/runtimes/browsers/pages/* test/runtimes/browsers/pages/core
+ mkdir test/runtimes/browsers/pages/dhkem-x25519
+ cp -rf x/dhkem-x25519/test/runtimes/browsers/pages/* test/runtimes/browsers/pages/dhkem-x25519
mkdir test/runtimes/browsers/pages/dhkem-secp256k1
cp -rf x/dhkem-secp256k1/test/runtimes/browsers/pages/* test/runtimes/browsers/pages/dhkem-secp256k1
deno task minify > test/runtimes/browsers/pages/dhkem-secp256k1/src/hpke.js
@@ -33,6 +35,11 @@ jobs:
run: |
deno task dnt
deno task minify > ../test/runtimes/browsers/pages/core/src/hpke-core.js
+ deno task minify > ../test/runtimes/browsers/pages/dhkem-x25519/src/hpke-core.js
+ - working-directory: ./x/dhkem-x25519
+ run: |
+ deno task dnt
+ deno task minify > ../../test/runtimes/browsers/pages/dhkem-x25519/src/hpke-dhkem-x25519.js
- working-directory: ./x/dhkem-secp256k1
run: |
deno task dnt
@@ -56,5 +63,7 @@ jobs:
npx playwright test
- working-directory: ./core/test/runtimes/browsers
run: npm install && npx playwright install && npx playwright test
+ - working-directory: ./x/dhkem-x25519/test/runtimes/browsers
+ run: npm install && npx playwright install && npx playwright test
- working-directory: ./x/dhkem-secp256k1/test/runtimes/browsers
run: npm install && npx playwright install && npx playwright test
diff --git a/.github/workflows/ci_bun.yml b/.github/workflows/ci_bun.yml
index 1d74150df..48e46550f 100644
--- a/.github/workflows/ci_bun.yml
+++ b/.github/workflows/ci_bun.yml
@@ -39,16 +39,27 @@ jobs:
nohup bun src/index.js &
sleep 3
deno test hpke.spec.ts --allow-net
- - working-directory: ./core/
+ - working-directory: ./core
run: |
deno task dnt
deno task minify > test/runtimes/hpke-core.js
+ deno task minify > ../x/dhkem-x25519/test/runtimes/hpke-core.js
- name: Run test for core
working-directory: ./core/test/runtimes/bun
run: |
nohup bun src/index.js &
sleep 3
deno test core.spec.ts --allow-net
+ - working-directory: ./x/dhkem-x25519
+ run: |
+ deno task dnt
+ deno task minify > test/runtimes/hpke-dhkem-x25519.js
+ - name: Run test for dhkem-x25519
+ working-directory: ./x/dhkem-x25519/test/runtimes/bun
+ run: |
+ nohup bun src/index.js &
+ sleep 3
+ deno test dhkem-x25519.spec.ts --allow-net
- working-directory: ./x/dhkem-secp256k1/
run: |
deno task dnt
diff --git a/.github/workflows/ci_cloudflare.yml b/.github/workflows/ci_cloudflare.yml
index bf8dc00d7..f4cb77683 100644
--- a/.github/workflows/ci_cloudflare.yml
+++ b/.github/workflows/ci_cloudflare.yml
@@ -24,7 +24,6 @@ jobs:
npm install -g esbuild
deno task dnt
deno task minify > test/runtimes/hpke.js
- deno task minify > core/test/runtimes/hpke.js
deno task minify > x/dhkem-secp256k1/test/runtimes/hpke.js
- name: Run test
working-directory: ./test/runtimes/cloudflare
@@ -33,10 +32,11 @@ jobs:
nohup npm start &
sleep 3
deno test hpke.spec.ts --allow-net
- - working-directory: ./core/
+ - working-directory: ./core
run: |
deno task dnt
deno task minify > test/runtimes/hpke-core.js
+ deno task minify > ../x/dhkem-x25519/test/runtimes/hpke-core.js
- name: Run test for core
working-directory: ./core/test/runtimes/cloudflare
run: |
@@ -44,7 +44,18 @@ jobs:
nohup npm start &
sleep 3
deno test core.spec.ts --allow-net
- - working-directory: ./x/dhkem-secp256k1/
+ - working-directory: ./x/dhkem-x25519
+ run: |
+ deno task dnt
+ deno task minify > test/runtimes/hpke-dhkem-x25519.js
+ - name: Run test for dhkem-x25519
+ working-directory: ./x/dhkem-x25519/test/runtimes/cloudflare
+ run: |
+ npm install
+ nohup npm start &
+ sleep 3
+ deno test dhkem-x25519.spec.ts --allow-net
+ - working-directory: ./x/dhkem-secp256k1
run: |
deno task dnt
deno task minify > test/runtimes/hpke-dhkem-secp256k1.js
diff --git a/.github/workflows/ci_node.yml b/.github/workflows/ci_node.yml
index 22ebe5258..7979e8d4e 100644
--- a/.github/workflows/ci_node.yml
+++ b/.github/workflows/ci_node.yml
@@ -32,6 +32,11 @@ jobs:
run: |
deno task dnt
deno task minify > ./npm/hpke-core.min.js
+ - name: Run dnt & minify for /x/dhkem-x25519
+ working-directory: ./x/dhkem-x25519
+ run: |
+ deno task dnt
+ deno task minify > ./npm/hpke-dhkem-x25519.min.js
- name: Run dnt & minify for /x/dhkem-secp256k1
working-directory: ./x/dhkem-secp256k1
run: |
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index f0716fcb8..35051eccc 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -48,6 +48,28 @@ jobs:
env:
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
+ publish-dhkem-x25519:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ - uses: actions/setup-node@v3
+ with:
+ node-version: 18
+ registry-url: https://registry.npmjs.org/
+ - uses: denoland/setup-deno@v1
+ with:
+ deno-version: v1.x
+ - name: Run dnt & minify
+ working-directory: ./x/dhkem-x25519
+ run: |
+ npm install -g esbuild
+ deno task dnt
+ deno task minify > ./npm/hpke-dhkem-x25519.min.js
+ - working-directory: ./x/dhkem-x25519/npm
+ run: npm publish
+ env:
+ NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
+
publish-dhkem-secp256k1:
runs-on: ubuntu-latest
steps:
diff --git a/dnt.ts b/dnt.ts
index 9ced28770..4ed19b773 100644
--- a/dnt.ts
+++ b/dnt.ts
@@ -3,6 +3,7 @@ import { build, emptyDir } from "dnt";
await emptyDir("./npm");
await emptyDir("./core/npm");
await emptyDir("./x/dhkem-secp256k1/npm");
+await emptyDir("./x/dhkem-x25519/npm");
await build({
entryPoints: ["./mod.ts"],
diff --git a/x/dhkem-x25519/README.md b/x/dhkem-x25519/README.md
new file mode 100644
index 000000000..c33884fc4
--- /dev/null
+++ b/x/dhkem-x25519/README.md
@@ -0,0 +1,247 @@
+
@hpke/dhkem-x25519
+
+
+
+
+
+
+[Documentation](https://doc.deno.land/https://deno.land/x/hpke/x/dhkem-x25519/mod.ts)
+
+
+
+## Index
+
+- [Supported Environments](#supported-environments)
+- [Installation](#installation)
+ - [Web Browser](#web-browser)
+ - [Node.js](#nodejs)
+ - [Deno](#deno)
+ - [Cloudflare Workers](#cloudflare-workers)
+- [Usage](#usage)
+- [Contributing](#contributing)
+
+## Supported Environments
+
+- **Web Browser**: [Web Cryptography API](https://www.w3.org/TR/WebCryptoAPI/)
+ supported browsers
+ - Confirmed: Chrome, Firefox, Edge, Safari, Opera, Vivaldi, Brave
+- **Node.js**: 16.x, 17.x, 18.x, 19.x, 20.x
+- **Deno**: 1.x (1.15-)
+- **Cloudflare Workers**
+- **bun**: 0.x (0.3.0-)
+
+## Installation
+
+### Web Browser
+
+Followings are how to use with typical CDNs. Other CDNs can be used as well.
+
+Using esm.sh:
+
+```html
+
+
+
+
+
+```
+
+Using unpkg:
+
+```html
+
+
+```
+
+### Node.js
+
+Using npm:
+
+```sh
+npm install @hpke/dhkem-x25519
+```
+
+Using yarn:
+
+```sh
+yarn add @hpke/dhkem-x25519
+```
+
+### Deno
+
+Using deno.land:
+
+```js
+// use a specific version
+import * as hpke from "https://deno.land/x/hpke@0.22.2/mod.ts";
+import * as x25519 from "https://deno.land/x/hpke@0.22.2/x/dhkem-x25519/mod.ts";
+
+// use the latest stable version
+import * as hpke from "https://deno.land/x/hpke/mod.ts";
+import * as x25519 from "https://deno.land/x/hpke/x/dhkem-x25519/mod.ts";
+```
+
+### Cloudflare Workers
+
+Downloads a single js file from esm.sh:
+
+```sh
+curl -sS -o $YOUR_SRC_PATH/hpke.min.js https://esm.sh/v86/hpke-js@0.22.2/es2022/hpke.min.js
+curl -sS -o $YOUR_SRC_PATH/hpke-dhkem-x25519.min.js https://esm.sh/v86/@hpke/dhkem-x25519@0.22.2/es2022/hpke-dhkem-x25519.min.js
+```
+
+## Usage
+
+This section shows some typical usage examples.
+
+### Browsers
+
+```html
+
+
+
+
+
+
+
+```
+
+### Node.js
+
+```js
+const { KdfId, AeadId, CipherSuite } = require("hpke-js");
+const { DhkemX25519HkdfSha256 } = require("@hpke/dhkem-x25519");
+
+async function doHpke() {
+ // setup
+ const suite = new CipherSuite({
+ kem: new DhkemX25519HkdfSha256(),
+ kdf: KdfId.HkdfSha256,
+ aead: AeadId.Aes128Gcm,
+ });
+
+ const rkp = await suite.generateKeyPair();
+
+ const sender = await suite.createSenderContext({
+ recipientPublicKey: rkp.publicKey,
+ });
+
+ const recipient = await suite.createRecipientContext({
+ recipientKey: rkp.privateKey,
+ enc: sender.enc,
+ });
+
+ // encrypt
+ const ct = await sender.seal(new TextEncoder().encode("my-secret-message"));
+
+ // decrypt
+ try {
+ const pt = await recipient.open(ct);
+
+ console.log("decrypted: ", new TextDecoder().decode(pt));
+ // decrypted: my-secret-message
+ } catch (err) {
+ console.log("failed to decrypt.");
+ }
+}
+
+doHpke();
+```
+
+### Deno
+
+```js
+import { KdfId, AeadId, CipherSuite } from "https://deno.land/x/hpke@0.22.2/mod.ts";
+import { DhkemX25519HkdfSha256 } from "https://deno.land/x/hpke@0.22.2/x/dhkem-x25519/mod.ts";
+
+async function doHpke() {
+ // setup
+ const suite = new CipherSuite({
+ kem: new DhkemX25519HkdfSha256(),
+ kdf: KdfId.HkdfSha256,
+ aead: AeadId.Aes128Gcm,
+ });
+
+ const rkp = await suite.generateKeyPair();
+
+ const sender = await suite.createSenderContext({
+ recipientPublicKey: rkp.publicKey,
+ });
+
+ const recipient = await suite.createRecipientContext({
+ recipientKey: rkp.privateKey,
+ enc: sender.enc,
+ });
+
+ // encrypt
+ const ct = await sender.seal(new TextEncoder().encode("my-secret-message"));
+
+ try {
+ // decrypt
+ const pt = await recipient.open(ct);
+
+ console.log("decrypted: ", new TextDecoder().decode(pt));
+ // decrypted: my-secret-message
+ } catch (_err: unknown) {
+ console.log("failed to decrypt.");
+ }
+}
+
+doHpke();
+```
+
+## Contributing
+
+We welcome all kind of contributions, filing issues, suggesting new features or
+sending PRs.
diff --git a/x/dhkem-x25519/deno.json b/x/dhkem-x25519/deno.json
new file mode 100644
index 000000000..b795a6814
--- /dev/null
+++ b/x/dhkem-x25519/deno.json
@@ -0,0 +1,33 @@
+{
+ "imports": {
+ "testing/": "https://deno.land/std@0.195.0/testing/",
+ "dnt": "https://deno.land/x/dnt@0.38.0/mod.ts"
+ },
+ "fmt": {
+ "include": [
+ "deno.json",
+ "dnt.ts",
+ "mod.ts",
+ "test/"
+ ],
+ "exclude": [
+ "test/runtimes/bun",
+ "test/runtimes/browsers/node_modules",
+ "test/runtimes/cloudflare"
+ ]
+ },
+ "lint": {
+ "include": ["mod.ts", "test/"],
+ "exclude": [
+ "test/runtimes/bun",
+ "test/runtimes/browsers/node_modules",
+ "test/runtimes/cloudflare"
+ ]
+ },
+ "tasks": {
+ "test": "deno fmt && deno lint && deno test test -A --fail-fast --doc --coverage=coverage --parallel --allow-read",
+ "cov": "deno coverage ./coverage --lcov --exclude='test'",
+ "dnt": "deno run -A dnt.ts $(git describe --tags $(git rev-list --tags --max-count=1))",
+ "minify": "esbuild npm/esm/x/dhkem-x25519/mod.js --bundle --format=esm --minify"
+ }
+}
diff --git a/x/dhkem-x25519/deno.lock b/x/dhkem-x25519/deno.lock
new file mode 100644
index 000000000..ceeaa3fdc
--- /dev/null
+++ b/x/dhkem-x25519/deno.lock
@@ -0,0 +1,185 @@
+{
+ "version": "2",
+ "remote": {
+ "https://deno.land/std@0.140.0/_util/assert.ts": "e94f2eb37cebd7f199952e242c77654e43333c1ac4c5c700e929ea3aa5489f74",
+ "https://deno.land/std@0.140.0/_util/os.ts": "3b4c6e27febd119d36a416d7a97bd3b0251b77c88942c8f16ee5953ea13e2e49",
+ "https://deno.land/std@0.140.0/bytes/bytes_list.ts": "67eb118e0b7891d2f389dad4add35856f4ad5faab46318ff99653456c23b025d",
+ "https://deno.land/std@0.140.0/bytes/equals.ts": "fc16dff2090cced02497f16483de123dfa91e591029f985029193dfaa9d894c9",
+ "https://deno.land/std@0.140.0/bytes/mod.ts": "763f97d33051cc3f28af1a688dfe2830841192a9fea0cbaa55f927b49d49d0bf",
+ "https://deno.land/std@0.140.0/fmt/colors.ts": "30455035d6d728394781c10755351742dd731e3db6771b1843f9b9e490104d37",
+ "https://deno.land/std@0.140.0/fs/_util.ts": "0fb24eb4bfebc2c194fb1afdb42b9c3dda12e368f43e8f2321f84fc77d42cb0f",
+ "https://deno.land/std@0.140.0/fs/ensure_dir.ts": "9dc109c27df4098b9fc12d949612ae5c9c7169507660dcf9ad90631833209d9d",
+ "https://deno.land/std@0.140.0/hash/sha256.ts": "803846c7a5a8a5a97f31defeb37d72f519086c880837129934f5d6f72102a8e8",
+ "https://deno.land/std@0.140.0/io/buffer.ts": "bd0c4bf53db4b4be916ca5963e454bddfd3fcd45039041ea161dbf826817822b",
+ "https://deno.land/std@0.140.0/path/_constants.ts": "df1db3ffa6dd6d1252cc9617e5d72165cd2483df90e93833e13580687b6083c3",
+ "https://deno.land/std@0.140.0/path/_interface.ts": "ee3b431a336b80cf445441109d089b70d87d5e248f4f90ff906820889ecf8d09",
+ "https://deno.land/std@0.140.0/path/_util.ts": "c1e9686d0164e29f7d880b2158971d805b6e0efc3110d0b3e24e4b8af2190d2b",
+ "https://deno.land/std@0.140.0/path/common.ts": "bee563630abd2d97f99d83c96c2fa0cca7cee103e8cb4e7699ec4d5db7bd2633",
+ "https://deno.land/std@0.140.0/path/glob.ts": "cb5255638de1048973c3e69e420c77dc04f75755524cb3b2e160fe9277d939ee",
+ "https://deno.land/std@0.140.0/path/mod.ts": "d3e68d0abb393fb0bf94a6d07c46ec31dc755b544b13144dee931d8d5f06a52d",
+ "https://deno.land/std@0.140.0/path/posix.ts": "293cdaec3ecccec0a9cc2b534302dfe308adb6f10861fa183275d6695faace44",
+ "https://deno.land/std@0.140.0/path/separator.ts": "fe1816cb765a8068afb3e8f13ad272351c85cbc739af56dacfc7d93d710fe0f9",
+ "https://deno.land/std@0.140.0/path/win32.ts": "31811536855e19ba37a999cd8d1b62078235548d67902ece4aa6b814596dd757",
+ "https://deno.land/std@0.140.0/streams/conversion.ts": "712585bfa0172a97fb68dd46e784ae8ad59d11b88079d6a4ab098ff42e697d21",
+ "https://deno.land/std@0.181.0/_util/asserts.ts": "178dfc49a464aee693a7e285567b3d0b555dc805ff490505a8aae34f9cfb1462",
+ "https://deno.land/std@0.181.0/_util/os.ts": "d932f56d41e4f6a6093d56044e29ce637f8dcc43c5a90af43504a889cf1775e3",
+ "https://deno.land/std@0.181.0/fs/_util.ts": "65381f341af1ff7f40198cee15c20f59951ac26e51ddc651c5293e24f9ce6f32",
+ "https://deno.land/std@0.181.0/fs/ensure_dir.ts": "dc64c4c75c64721d4e3fb681f1382f803ff3d2868f08563ff923fdd20d071c40",
+ "https://deno.land/std@0.181.0/fs/expand_glob.ts": "e4f56259a0a70fe23f05215b00de3ac5e6ba46646ab2a06ebbe9b010f81c972a",
+ "https://deno.land/std@0.181.0/fs/walk.ts": "ea95ffa6500c1eda6b365be488c056edc7c883a1db41ef46ec3bf057b1c0fe32",
+ "https://deno.land/std@0.181.0/path/_constants.ts": "e49961f6f4f48039c0dfed3c3f93e963ca3d92791c9d478ac5b43183413136e0",
+ "https://deno.land/std@0.181.0/path/_interface.ts": "6471159dfbbc357e03882c2266d21ef9afdb1e4aa771b0545e90db58a0ba314b",
+ "https://deno.land/std@0.181.0/path/_util.ts": "d7abb1e0dea065f427b89156e28cdeb32b045870acdf865833ba808a73b576d0",
+ "https://deno.land/std@0.181.0/path/common.ts": "ee7505ab01fd22de3963b64e46cff31f40de34f9f8de1fff6a1bd2fe79380000",
+ "https://deno.land/std@0.181.0/path/glob.ts": "d479e0a695621c94d3fd7fe7abd4f9499caf32a8de13f25073451c6ef420a4e1",
+ "https://deno.land/std@0.181.0/path/mod.ts": "bf718f19a4fdd545aee1b06409ca0805bd1b68ecf876605ce632e932fe54510c",
+ "https://deno.land/std@0.181.0/path/posix.ts": "8b7c67ac338714b30c816079303d0285dd24af6b284f7ad63da5b27372a2c94d",
+ "https://deno.land/std@0.181.0/path/separator.ts": "0fb679739d0d1d7bf45b68dacfb4ec7563597a902edbaf3c59b50d5bcadd93b1",
+ "https://deno.land/std@0.181.0/path/win32.ts": "d186344e5583bcbf8b18af416d13d82b35a317116e6460a5a3953508c3de5bba",
+ "https://deno.land/std@0.182.0/_util/asserts.ts": "178dfc49a464aee693a7e285567b3d0b555dc805ff490505a8aae34f9cfb1462",
+ "https://deno.land/std@0.182.0/_util/os.ts": "d932f56d41e4f6a6093d56044e29ce637f8dcc43c5a90af43504a889cf1775e3",
+ "https://deno.land/std@0.182.0/fmt/colors.ts": "d67e3cd9f472535241a8e410d33423980bec45047e343577554d3356e1f0ef4e",
+ "https://deno.land/std@0.182.0/fs/_util.ts": "65381f341af1ff7f40198cee15c20f59951ac26e51ddc651c5293e24f9ce6f32",
+ "https://deno.land/std@0.182.0/fs/empty_dir.ts": "c3d2da4c7352fab1cf144a1ecfef58090769e8af633678e0f3fabaef98594688",
+ "https://deno.land/std@0.182.0/fs/expand_glob.ts": "e4f56259a0a70fe23f05215b00de3ac5e6ba46646ab2a06ebbe9b010f81c972a",
+ "https://deno.land/std@0.182.0/fs/walk.ts": "920be35a7376db6c0b5b1caf1486fb962925e38c9825f90367f8f26b5e5d0897",
+ "https://deno.land/std@0.182.0/path/_constants.ts": "e49961f6f4f48039c0dfed3c3f93e963ca3d92791c9d478ac5b43183413136e0",
+ "https://deno.land/std@0.182.0/path/_interface.ts": "6471159dfbbc357e03882c2266d21ef9afdb1e4aa771b0545e90db58a0ba314b",
+ "https://deno.land/std@0.182.0/path/_util.ts": "d7abb1e0dea065f427b89156e28cdeb32b045870acdf865833ba808a73b576d0",
+ "https://deno.land/std@0.182.0/path/common.ts": "ee7505ab01fd22de3963b64e46cff31f40de34f9f8de1fff6a1bd2fe79380000",
+ "https://deno.land/std@0.182.0/path/glob.ts": "d479e0a695621c94d3fd7fe7abd4f9499caf32a8de13f25073451c6ef420a4e1",
+ "https://deno.land/std@0.182.0/path/mod.ts": "bf718f19a4fdd545aee1b06409ca0805bd1b68ecf876605ce632e932fe54510c",
+ "https://deno.land/std@0.182.0/path/posix.ts": "8b7c67ac338714b30c816079303d0285dd24af6b284f7ad63da5b27372a2c94d",
+ "https://deno.land/std@0.182.0/path/separator.ts": "0fb679739d0d1d7bf45b68dacfb4ec7563597a902edbaf3c59b50d5bcadd93b1",
+ "https://deno.land/std@0.182.0/path/win32.ts": "d186344e5583bcbf8b18af416d13d82b35a317116e6460a5a3953508c3de5bba",
+ "https://deno.land/std@0.195.0/_util/diff.ts": "1a3c044aedf77647d6cac86b798c6417603361b66b54c53331b312caeb447aea",
+ "https://deno.land/std@0.195.0/assert/_constants.ts": "8a9da298c26750b28b326b297316cdde860bc237533b07e1337c021379e6b2a9",
+ "https://deno.land/std@0.195.0/assert/_format.ts": "a69126e8a469009adf4cf2a50af889aca364c349797e63174884a52ff75cf4c7",
+ "https://deno.land/std@0.195.0/assert/assert.ts": "9a97dad6d98c238938e7540736b826440ad8c1c1e54430ca4c4e623e585607ee",
+ "https://deno.land/std@0.195.0/assert/assert_almost_equals.ts": "e15ca1f34d0d5e0afae63b3f5d975cbd18335a132e42b0c747d282f62ad2cd6c",
+ "https://deno.land/std@0.195.0/assert/assert_array_includes.ts": "6856d7f2c3544bc6e62fb4646dfefa3d1df5ff14744d1bca19f0cbaf3b0d66c9",
+ "https://deno.land/std@0.195.0/assert/assert_equals.ts": "a0ee60574e437bcab2dcb79af9d48dc88845f8fd559468d9c21b15fd638ef943",
+ "https://deno.land/std@0.195.0/assert/assert_exists.ts": "407cb6b9fb23a835cd8d5ad804e2e2edbbbf3870e322d53f79e1c7a512e2efd7",
+ "https://deno.land/std@0.195.0/assert/assert_false.ts": "a9962749f4bf5844e3fa494257f1de73d69e4fe0e82c34d0099287552163a2dc",
+ "https://deno.land/std@0.195.0/assert/assert_instance_of.ts": "09fd297352a5b5bbb16da2b5e1a0d8c6c44da5447772648622dcc7df7af1ddb8",
+ "https://deno.land/std@0.195.0/assert/assert_is_error.ts": "b4eae4e5d182272efc172bf28e2e30b86bb1650cd88aea059e5d2586d4160fb9",
+ "https://deno.land/std@0.195.0/assert/assert_match.ts": "c4083f80600bc190309903c95e397a7c9257ff8b5ae5c7ef91e834704e672e9b",
+ "https://deno.land/std@0.195.0/assert/assert_not_equals.ts": "9f1acab95bd1f5fc9a1b17b8027d894509a745d91bac1718fdab51dc76831754",
+ "https://deno.land/std@0.195.0/assert/assert_not_instance_of.ts": "0c14d3dfd9ab7a5276ed8ed0b18c703d79a3d106102077ec437bfe7ed912bd22",
+ "https://deno.land/std@0.195.0/assert/assert_not_match.ts": "3796a5b0c57a1ce6c1c57883dd4286be13a26f715ea662318ab43a8491a13ab0",
+ "https://deno.land/std@0.195.0/assert/assert_not_strict_equals.ts": "ca6c6d645e95fbc873d25320efeb8c4c6089a9a5e09f92d7c1c4b6e935c2a6ad",
+ "https://deno.land/std@0.195.0/assert/assert_object_match.ts": "27439c4f41dce099317566144299468ca822f556f1cc697f4dc8ed61fe9fee4c",
+ "https://deno.land/std@0.195.0/assert/assert_rejects.ts": "45c59724de2701e3b1f67c391d6c71c392363635aad3f68a1b3408f9efca0057",
+ "https://deno.land/std@0.195.0/assert/assert_strict_equals.ts": "5cf29b38b3f8dece95287325723272aa04e04dbf158d886d662fa594fddc9ed3",
+ "https://deno.land/std@0.195.0/assert/assert_string_includes.ts": "b821d39ebf5cb0200a348863c86d8c4c4b398e02012ce74ad15666fc4b631b0c",
+ "https://deno.land/std@0.195.0/assert/assert_throws.ts": "63784e951475cb7bdfd59878cd25a0931e18f6dc32a6077c454b2cd94f4f4bcd",
+ "https://deno.land/std@0.195.0/assert/assertion_error.ts": "4d0bde9b374dfbcbe8ac23f54f567b77024fb67dbb1906a852d67fe050d42f56",
+ "https://deno.land/std@0.195.0/assert/equal.ts": "9f1a46d5993966d2596c44e5858eec821859b45f783a5ee2f7a695dfc12d8ece",
+ "https://deno.land/std@0.195.0/assert/fail.ts": "c36353d7ae6e1f7933d45f8ea51e358c8c4b67d7e7502028598fe1fea062e278",
+ "https://deno.land/std@0.195.0/assert/mod.ts": "08d55a652c22c5da0215054b21085cec25a5da47ce4a6f9de7d9ad36df35bdee",
+ "https://deno.land/std@0.195.0/assert/unimplemented.ts": "d56fbeecb1f108331a380f72e3e010a1f161baa6956fd0f7cf3e095ae1a4c75a",
+ "https://deno.land/std@0.195.0/assert/unreachable.ts": "4600dc0baf7d9c15a7f7d234f00c23bca8f3eba8b140286aaca7aa998cf9a536",
+ "https://deno.land/std@0.195.0/fmt/colors.ts": "a7eecffdf3d1d54db890723b303847b6e0a1ab4b528ba6958b8f2e754cf1b3bc",
+ "https://deno.land/std@0.195.0/testing/_test_suite.ts": "30f018feeb3835f12ab198d8a518f9089b1bcb2e8c838a8b615ab10d5005465c",
+ "https://deno.land/std@0.195.0/testing/asserts.ts": "4da3dca17ff6b852e4e689f7e42562c24c3ee182cc55ff04852811e321538c24",
+ "https://deno.land/std@0.195.0/testing/bdd.ts": "3f446df5ef8e856a869e8eec54c8482590415741ff0b6358a00c43486cc15769",
+ "https://deno.land/x/code_block_writer@12.0.0/mod.ts": "2c3448060e47c9d08604c8f40dee34343f553f33edcdfebbf648442be33205e5",
+ "https://deno.land/x/code_block_writer@12.0.0/utils/string_utils.ts": "60cb4ec8bd335bf241ef785ccec51e809d576ff8e8d29da43d2273b69ce2a6ff",
+ "https://deno.land/x/deno_cache@0.4.1/auth_tokens.ts": "5fee7e9155e78cedf3f6ff3efacffdb76ac1a76c86978658d9066d4fb0f7326e",
+ "https://deno.land/x/deno_cache@0.4.1/cache.ts": "51f72f4299411193d780faac8c09d4e8cbee951f541121ef75fcc0e94e64c195",
+ "https://deno.land/x/deno_cache@0.4.1/deno_dir.ts": "f2a9044ce8c7fe1109004cda6be96bf98b08f478ce77e7a07f866eff1bdd933f",
+ "https://deno.land/x/deno_cache@0.4.1/deps.ts": "8974097d6c17e65d9a82d39377ae8af7d94d74c25c0cbb5855d2920e063f2343",
+ "https://deno.land/x/deno_cache@0.4.1/dirs.ts": "d2fa473ef490a74f2dcb5abb4b9ab92a48d2b5b6320875df2dee64851fa64aa9",
+ "https://deno.land/x/deno_cache@0.4.1/disk_cache.ts": "1f3f5232cba4c56412d93bdb324c624e95d5dd179d0578d2121e3ccdf55539f9",
+ "https://deno.land/x/deno_cache@0.4.1/file_fetcher.ts": "07a6c5f8fd94bf50a116278cc6012b4921c70d2251d98ce1c9f3c352135c39f7",
+ "https://deno.land/x/deno_cache@0.4.1/http_cache.ts": "f632e0d6ec4a5d61ae3987737a72caf5fcdb93670d21032ddb78df41131360cd",
+ "https://deno.land/x/deno_cache@0.4.1/mod.ts": "ef1cda9235a93b89cb175fe648372fc0f785add2a43aa29126567a05e3e36195",
+ "https://deno.land/x/deno_cache@0.4.1/util.ts": "8cb686526f4be5205b92c819ca2ce82220aa0a8dd3613ef0913f6dc269dbbcfe",
+ "https://deno.land/x/dir@1.5.1/data_local_dir/mod.ts": "91eb1c4bfadfbeda30171007bac6d85aadacd43224a5ed721bbe56bc64e9eb66",
+ "https://deno.land/x/dnt@0.38.0/lib/compiler.ts": "209ad2e1b294f93f87ec02ade9a0821f942d2e524104552d0aa8ff87021050a5",
+ "https://deno.land/x/dnt@0.38.0/lib/compiler_transforms.ts": "f21aba052f5dcf0b0595c734450842855c7f572e96165d3d34f8fed2fc1f7ba1",
+ "https://deno.land/x/dnt@0.38.0/lib/mod.deps.ts": "30367fc68bcd2acf3b7020cf5cdd26f817f7ac9ac35c4bfb6c4551475f91bc3e",
+ "https://deno.land/x/dnt@0.38.0/lib/npm_ignore.ts": "57fbb7e7b935417d225eec586c6aa240288905eb095847d3f6a88e290209df4e",
+ "https://deno.land/x/dnt@0.38.0/lib/package_json.ts": "61f35b06e374ed39ca776d29d67df4be7ee809d0bca29a8239687556c6d027c2",
+ "https://deno.land/x/dnt@0.38.0/lib/pkg/dnt_wasm.generated.js": "82aeecfb055af0b2700e1e9b886e4a44fe3bf9cd11a9c4195cb169f53a134b15",
+ "https://deno.land/x/dnt@0.38.0/lib/pkg/snippets/dnt-wasm-a15ef721fa5290c5/helpers.js": "a6b95adc943a68d513fe8ed9ec7d260ac466b7a4bced4e942f733e494bb9f1be",
+ "https://deno.land/x/dnt@0.38.0/lib/shims.ts": "df1bd4d9a196dca4b2d512b1564fff64ac6c945189a273d706391f87f210d7e6",
+ "https://deno.land/x/dnt@0.38.0/lib/test_runner/get_test_runner_code.ts": "4dc7a73a13b027341c0688df2b29a4ef102f287c126f134c33f69f0339b46968",
+ "https://deno.land/x/dnt@0.38.0/lib/test_runner/test_runner.ts": "4d0da0500ec427d5f390d9a8d42fb882fbeccc92c92d66b6f2e758606dbd40e6",
+ "https://deno.land/x/dnt@0.38.0/lib/transform.deps.ts": "e42f2bdef46d098453bdba19261a67cf90b583f5d868f7fe83113c1380d9b85c",
+ "https://deno.land/x/dnt@0.38.0/lib/types.ts": "b8e228b2fac44c2ae902fbb73b1689f6ab889915bd66486c8a85c0c24255f5fb",
+ "https://deno.land/x/dnt@0.38.0/lib/utils.ts": "878b7ac7003a10c16e6061aa49dbef9b42bd43174853ebffc9b67ea47eeb11d8",
+ "https://deno.land/x/dnt@0.38.0/mod.ts": "b13349fe77847cf58e26b40bcd58797a8cec5d71b31a1ca567071329c8489de1",
+ "https://deno.land/x/dnt@0.38.0/transform.ts": "f68743a14cf9bf53bfc9c81073871d69d447a7f9e3453e0447ca2fb78926bb1d",
+ "https://deno.land/x/hpke@0.21.0/mod.ts": "b9e7d697ea421173b8534c79f878cb4b58fefe54a86d0562c0fced64a9561503",
+ "https://deno.land/x/hpke@0.21.0/src/aeads/aesGcm.ts": "7b69653031fe910301235de8327603a92cfa2a055542c7f7f22d151aba47c735",
+ "https://deno.land/x/hpke@0.21.0/src/aeads/chacha20Poly1305.ts": "e2d47b6ceedc5da8459b3b5c9928238a8b74e4a6e48b0f979a387a396d7b704a",
+ "https://deno.land/x/hpke@0.21.0/src/aeads/exportOnly.ts": "6103460a4b426596338c39d66aea20a0b6be65791c03df25372c2e9f6afb55c6",
+ "https://deno.land/x/hpke@0.21.0/src/algorithm.ts": "3ecda38db36b958877d0ca6ea577bd4c8edbfab18d5eff4d908bf91f5ebb48a1",
+ "https://deno.land/x/hpke@0.21.0/src/cipherSuite.ts": "ba3a339d8665f6b1da9f985e4145a02d1850128430b02f1c6034f0e10958d251",
+ "https://deno.land/x/hpke@0.21.0/src/consts.ts": "98cf8b1a4cd370964208cb292bafa196c5722fbb900d486112a0c206a77f2252",
+ "https://deno.land/x/hpke@0.21.0/src/encryptionContext.ts": "2589d10de7e941089ff199c40fbb4d73c9e6bff6e7076f5cc7c9e4885f6a79e7",
+ "https://deno.land/x/hpke@0.21.0/src/errors.ts": "05822bd1d483cd1fdf515b26398f8c270b8561fa94dc818f6bc3c7031415ccf0",
+ "https://deno.land/x/hpke@0.21.0/src/exporterContext.ts": "86c5aa30996cec1af43d3ce06d95a8a89ddb555ce2c61f1616661bd3fef4529b",
+ "https://deno.land/x/hpke@0.21.0/src/identifiers.ts": "f04543410b4332f372f831aff730ad93bd5eb36ffe81c18b11e2c2705d410c29",
+ "https://deno.land/x/hpke@0.21.0/src/interfaces/aeadInterface.ts": "0c5e11e27c7d5cfbc22df3ee8f6ae81e06bfbbe543554e0ab16f9456b96c33e8",
+ "https://deno.land/x/hpke@0.21.0/src/interfaces/aeadKey.ts": "3f070a3630f812feef2b775e17144115a66077c2d5b7060af3eb25f068b001d2",
+ "https://deno.land/x/hpke@0.21.0/src/interfaces/aeadParams.ts": "c541e24d11f2ceef2830dd3aba28c316a884bdc02ac0e9c66d95bc2a23b92292",
+ "https://deno.land/x/hpke@0.21.0/src/interfaces/cipherSuiteParams.ts": "b1580711d88420dbd16ce9e5f9cac0aedde809630de3b9f908e6040dff60a9de",
+ "https://deno.land/x/hpke@0.21.0/src/interfaces/encapsulator.ts": "6a81362683e1eb9f7406bb974b6b7bd5554bc6c93eed3ab3e955a9dda9beca48",
+ "https://deno.land/x/hpke@0.21.0/src/interfaces/encryptionContextInterface.ts": "329c1ee876540713351a5eb77888c48a7296388293a0d320b757e252a08fc328",
+ "https://deno.land/x/hpke@0.21.0/src/interfaces/exporter.ts": "6667607ba48f02ec447f143d1e0050adbe11cd7857c8dce3670dd5a8a9298e91",
+ "https://deno.land/x/hpke@0.21.0/src/interfaces/kdfInterface.ts": "b8b683981ef7e8f8c083a442a2a8ba7ae23c6aa4403714031202f3f50e6dafdd",
+ "https://deno.land/x/hpke@0.21.0/src/interfaces/kemInterface.ts": "d8c7448020108e7a07c9b6a7f5cb06913f8a624411846ae64918057c3a77cb22",
+ "https://deno.land/x/hpke@0.21.0/src/interfaces/kemPrimitives.ts": "17349098c1e496e628a9fb39ba70c20c8a3e8a385e73b70ab535f83bc7902be1",
+ "https://deno.land/x/hpke@0.21.0/src/interfaces/keyInfo.ts": "8cc05de2a7e0c446913393c3a340eda323bc6338bf2152d9aa648c2d47712546",
+ "https://deno.land/x/hpke@0.21.0/src/interfaces/keyScheduleParams.ts": "e0a698815eb123c914de499de71bde80a79b4488e6ce681fbb394037ba2a9645",
+ "https://deno.land/x/hpke@0.21.0/src/interfaces/preSharedKey.ts": "98543f61362b5f77675f4f5f13af4efdf58183c3c309f539d6c6c9cb62124090",
+ "https://deno.land/x/hpke@0.21.0/src/interfaces/recipientContextParams.ts": "02194c133714506d12524c7828ff80464e089534ac8c1a08eb1e7913c6f24ce2",
+ "https://deno.land/x/hpke@0.21.0/src/interfaces/responses.ts": "35d2bf77ab4d32cfe90bf96d3a61bf92a10a3979e90be74f64965da019c46531",
+ "https://deno.land/x/hpke@0.21.0/src/interfaces/senderContextParams.ts": "f57ffc17222e79d87c4f7730fb826c0ba6860c5ace1d06aeddde29c5618f362e",
+ "https://deno.land/x/hpke@0.21.0/src/kdfs/hkdf.ts": "24a8c0edfc41fe5c2d96b270c5ebda8fcbc5f3844ab1ff73eb655bdfd71d1ac2",
+ "https://deno.land/x/hpke@0.21.0/src/kems/dhkem.ts": "894bbe4e4cc481057a4528b44b6b29096d83314c3bd89f57bee768faee5e8e4f",
+ "https://deno.land/x/hpke@0.21.0/src/kems/dhkemPrimitives/ec.ts": "57eee1dd96ad7a48de8cc69d20918c956a397a8d597c0b6ee0e68b85970f473e",
+ "https://deno.land/x/hpke@0.21.0/src/kems/dhkemPrimitives/secp256k1.ts": "74be448865e2800a18ba70419758713c417bf74de66f3d344387317f4cb39072",
+ "https://deno.land/x/hpke@0.21.0/src/kems/dhkemPrimitives/x25519.ts": "1b211e210df98252cb45c45028f77772a0962d3ae403ee48e1160b8620e92438",
+ "https://deno.land/x/hpke@0.21.0/src/kems/dhkemPrimitives/x448.ts": "bf17c0ccb58abe9efa12d38ed7ad494eea89d1cd55739a3648ed055f45d1f59a",
+ "https://deno.land/x/hpke@0.21.0/src/recipientContext.ts": "228ba51bc52df0387617a3f471d43b3ec4f7b6d142c0f7f395d282a09f6ceae9",
+ "https://deno.land/x/hpke@0.21.0/src/senderContext.ts": "e4a4381b9dfbae61309c5ed4fe92db973df57b704c6d200afe8bfc4a3e845e47",
+ "https://deno.land/x/hpke@0.21.0/src/utils/bignum.ts": "2e8749a21cde3ec81e4d353ec001732d60c22d56a717f83bec4c9ddf8227f75c",
+ "https://deno.land/x/hpke@0.21.0/src/utils/misc.ts": "9ec825918998d8df2593571b722d9e4344081152ef9f68012431226960bb2dfd",
+ "https://deno.land/x/hpke@0.21.0/src/webCrypto.ts": "60296a8b402f93a6622b57d140299fe524c3bce334dbf46a23a37b2c520f6f89",
+ "https://deno.land/x/hpke@0.21.0/src/xCryptoKey.ts": "93355b8422f3477b62e2d048ec0c766ccd6c36bfe0649345f092239a984edc2d",
+ "https://deno.land/x/ts_morph@18.0.0/bootstrap/mod.ts": "b53aad517f106c4079971fcd4a81ab79fadc40b50061a3ab2b741a09119d51e9",
+ "https://deno.land/x/ts_morph@18.0.0/bootstrap/ts_morph_bootstrap.js": "6645ac03c5e6687dfa8c78109dc5df0250b811ecb3aea2d97c504c35e8401c06",
+ "https://deno.land/x/ts_morph@18.0.0/common/DenoRuntime.ts": "6a7180f0c6e90dcf23ccffc86aa8271c20b1c4f34c570588d08a45880b7e172d",
+ "https://deno.land/x/ts_morph@18.0.0/common/mod.ts": "01985d2ee7da8d1caee318a9d07664774fbee4e31602bc2bb6bb62c3489555ed",
+ "https://deno.land/x/ts_morph@18.0.0/common/ts_morph_common.js": "845671ca951073400ce142f8acefa2d39ea9a51e29ca80928642f3f8cf2b7700",
+ "https://deno.land/x/ts_morph@18.0.0/common/typescript.js": "d5c598b6a2db2202d0428fca5fd79fc9a301a71880831a805d778797d2413c59",
+ "https://deno.land/x/wasmbuild@0.14.1/cache.ts": "89eea5f3ce6035a1164b3e655c95f21300498920575ade23161421f5b01967f4",
+ "https://deno.land/x/wasmbuild@0.14.1/loader.ts": "d98d195a715f823151cbc8baa3f32127337628379a02d9eb2a3c5902dbccfc02"
+ },
+ "npm": {
+ "specifiers": {
+ "@noble/ciphers@0.1.4": "@noble/ciphers@0.1.4",
+ "@noble/curves@1.1.0": "@noble/curves@1.1.0",
+ "@noble/hashes@1.3.1": "@noble/hashes@1.3.1"
+ },
+ "packages": {
+ "@noble/ciphers@0.1.4": {
+ "integrity": "sha512-d3ZR8vGSpy3v/nllS+bD/OMN5UZqusWiQqkyj7AwzTnhXFH72pF5oB4Ach6DQ50g5kXxC28LdaYBEpsyv9KOUQ==",
+ "dependencies": {}
+ },
+ "@noble/curves@1.1.0": {
+ "integrity": "sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA==",
+ "dependencies": {
+ "@noble/hashes": "@noble/hashes@1.3.1"
+ }
+ },
+ "@noble/hashes@1.3.1": {
+ "integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==",
+ "dependencies": {}
+ }
+ }
+ }
+}
diff --git a/x/dhkem-x25519/dnt.ts b/x/dhkem-x25519/dnt.ts
new file mode 100644
index 000000000..78fa9b42d
--- /dev/null
+++ b/x/dhkem-x25519/dnt.ts
@@ -0,0 +1,61 @@
+import { build, emptyDir } from "dnt";
+
+await emptyDir("./npm");
+
+await build({
+ entryPoints: ["./mod.ts"],
+ outDir: "./npm",
+ typeCheck: false,
+ test: true,
+ declaration: true,
+ scriptModule: "umd",
+ importMap: "./deno.json",
+ compilerOptions: {
+ lib: ["es2021", "dom"],
+ },
+ shims: {
+ deno: "dev",
+ },
+ package: {
+ name: "@hpke/dhkem-x25519",
+ version: Deno.args[0],
+ description:
+ "A Hybrid Public Key Encryption (HPKE) module extension for X25519",
+ repository: {
+ type: "git",
+ url: "git+https://github.com/dajiaji/hpke-js.git",
+ },
+ homepage: "https://github.com/dajiaji/hpke-js#readme",
+ license: "MIT",
+ main: "./script/x/dhkem-x25519/mod.js",
+ types: "./script/x/dhkem-x25519/mod.d.ts",
+ exports: {
+ ".": {
+ "import": "./esm/x/dhkem-x25519/mod.js",
+ "require": "./script/x/dhkem-x25519/mod.js",
+ },
+ "./package.json": "./package.json",
+ },
+ keywords: [
+ "hpke",
+ "rfc9180",
+ "kem",
+ "hkdf",
+ "dh",
+ "x25519",
+ "security",
+ "encryption",
+ ],
+ engines: {
+ "node": ">=16.0.0",
+ },
+ author: "Ajitomi Daisuke",
+ bugs: {
+ url: "https://github.com/dajiaji/hpke-js/issues",
+ },
+ },
+});
+
+// post build steps
+Deno.copyFileSync("../../LICENSE", "npm/LICENSE");
+Deno.copyFileSync("README.md", "npm/README.md");
diff --git a/x/dhkem-x25519/mod.ts b/x/dhkem-x25519/mod.ts
new file mode 100644
index 000000000..c91129eaf
--- /dev/null
+++ b/x/dhkem-x25519/mod.ts
@@ -0,0 +1 @@
+export { DhkemX25519HkdfSha256 } from "../../src/kems/dhkemX25519.ts";
diff --git a/x/dhkem-x25519/test/dhkem-x25519.test.ts b/x/dhkem-x25519/test/dhkem-x25519.test.ts
new file mode 100644
index 000000000..c970236cd
--- /dev/null
+++ b/x/dhkem-x25519/test/dhkem-x25519.test.ts
@@ -0,0 +1,228 @@
+import { assertEquals, assertRejects } from "testing/asserts.ts";
+import { describe, it } from "testing/bdd.ts";
+
+import { AeadId, CipherSuite, KdfId, KemId } from "../../../mod.ts";
+// } from "https://deno.land/x/hpke/mod.ts";
+
+import { DhkemX25519HkdfSha256 } from "../mod.ts";
+import { loadCrypto, loadSubtleCrypto } from "./utils.ts";
+
+describe("DhkemX25519HkdfSha256", () => {
+ describe("with valid parameters", () => {
+ it("should have a correct KEM object", async () => {
+ const api = await loadSubtleCrypto();
+
+ // assert
+ const kem = new DhkemX25519HkdfSha256();
+ kem.init(api);
+ assertEquals(typeof kem, "object");
+ assertEquals(kem.id, KemId.DhkemX25519HkdfSha256);
+ assertEquals(kem.secretSize, 32);
+ assertEquals(kem.encSize, 32);
+ assertEquals(kem.publicKeySize, 32);
+ assertEquals(kem.privateKeySize, 32);
+ });
+ });
+});
+
+describe("generateKeyPair", () => {
+ describe("with valid parameters", () => {
+ it("should return a proper instance", async () => {
+ const api = await loadSubtleCrypto();
+
+ // assert
+ const kem = new DhkemX25519HkdfSha256();
+ kem.init(api);
+ const kp = await kem.generateKeyPair();
+ assertEquals(kp.publicKey.type, "public");
+ assertEquals(kp.publicKey.extractable, true);
+ assertEquals(kp.publicKey.algorithm.name, "X25519");
+ // assertEquals(kp.publicKey.algorithm.namedCurve, "X25519");
+ assertEquals(kp.publicKey.usages.length, 0);
+ // assertEquals(kp.publicKey.usages[0], "deriveBits");
+ assertEquals(kp.privateKey.type, "private");
+ assertEquals(kp.privateKey.extractable, true);
+ assertEquals(kp.privateKey.algorithm.name, "X25519");
+ // assertEquals(kp.privateKey.algorithm.namedCurve, "X25519");
+ assertEquals(kp.privateKey.usages.length, 1);
+ assertEquals(kp.privateKey.usages[0], "deriveBits");
+ });
+ });
+});
+
+describe("deriveKeyPair", () => {
+ describe("with valid parameters", () => {
+ it("should return a proper instance", async () => {
+ const api = await loadSubtleCrypto();
+ const cryptoApi = await loadCrypto();
+
+ // assert
+ const kem = new DhkemX25519HkdfSha256();
+ kem.init(api);
+ const ikm = new Uint8Array(32);
+ cryptoApi.getRandomValues(ikm);
+ const kp = await kem.deriveKeyPair(ikm.buffer);
+ assertEquals(kp.publicKey.type, "public");
+ assertEquals(kp.publicKey.extractable, true);
+ assertEquals(kp.publicKey.algorithm.name, "X25519");
+ // assertEquals(kp.publicKey.algorithm.namedCurve, "X25519");
+ assertEquals(kp.publicKey.usages.length, 0);
+ // assertEquals(kp.publicKey.usages[0], "deriveBits");
+ assertEquals(kp.privateKey.type, "private");
+ assertEquals(kp.privateKey.extractable, true);
+ assertEquals(kp.privateKey.algorithm.name, "X25519");
+ // assertEquals(kp.privateKey.algorithm.namedCurve, "X25519");
+ assertEquals(kp.privateKey.usages.length, 1);
+ assertEquals(kp.privateKey.usages[0], "deriveBits");
+ });
+ });
+});
+
+describe("serialize/deserializePublicKey", () => {
+ describe("with valid parameters", () => {
+ it("should return a proper instance with DhkemX25519HkdfSha256", async () => {
+ const api = await loadSubtleCrypto();
+
+ // assert
+ const kem = new DhkemX25519HkdfSha256();
+ kem.init(api);
+ const kp = await kem.generateKeyPair();
+ const bPubKey = await kem.serializePublicKey(kp.publicKey);
+ const pubKey = await kem.deserializePublicKey(bPubKey);
+ assertEquals(pubKey.type, "public");
+ assertEquals(pubKey.extractable, true);
+ assertEquals(pubKey.algorithm.name, "X25519");
+ // assertEquals(pubKey.algorithm.namedCurve, "X25519");
+ assertEquals(pubKey.usages.length, 0);
+ // assertEquals(pubKey.usages[0], "deriveBits");
+ });
+ });
+});
+
+describe("importKey", () => {
+ describe("with valid parameters", () => {
+ it("should return a valid private key for DhkemX25519HkdfSha256 from raw key", async () => {
+ const api = await loadSubtleCrypto();
+ const kem = new DhkemX25519HkdfSha256();
+ kem.init(api);
+
+ const cryptoApi = await loadCrypto();
+ const rawKey = new Uint8Array(32);
+ cryptoApi.getRandomValues(rawKey);
+ const privKey = await kem.importKey("raw", rawKey, false);
+
+ // assert
+ assertEquals(privKey.usages.length, 1);
+ assertEquals(privKey.usages[0], "deriveBits");
+ });
+
+ it("should return a valid public key for DhkemX25519HkdfSha256 from raw key", async () => {
+ const api = await loadSubtleCrypto();
+ const kem = new DhkemX25519HkdfSha256();
+ kem.init(api);
+
+ const cryptoApi = await loadCrypto();
+ const rawKey = new Uint8Array(32);
+ cryptoApi.getRandomValues(rawKey);
+ const privKey = await kem.importKey("raw", rawKey, true);
+
+ // assert
+ assertEquals(privKey.usages.length, 0);
+ // assertEquals(privKey.usages[0], "deriveBits");
+ });
+ });
+
+ describe("with invalid parameters", () => {
+ it("should throw DeserializeError with invalid DhkemX25519HkdfSha256 private key", async () => {
+ const api = await loadSubtleCrypto();
+ const kem = new DhkemX25519HkdfSha256();
+ kem.init(api);
+
+ const cryptoApi = await loadCrypto();
+ const rawKey = new Uint8Array(33);
+ cryptoApi.getRandomValues(rawKey);
+
+ // assert
+ await assertRejects(
+ () => kem.importKey("raw", rawKey, false),
+ Error,
+ );
+ });
+
+ it("should throw DeserializeError with invalid DhkemX25519HkdfSha256 public key", async () => {
+ const api = await loadSubtleCrypto();
+ const kem = new DhkemX25519HkdfSha256();
+ kem.init(api);
+
+ const cryptoApi = await loadCrypto();
+ const rawKey = new Uint8Array(33);
+ cryptoApi.getRandomValues(rawKey);
+
+ // assert
+ await assertRejects(
+ () => kem.importKey("raw", rawKey, true),
+ Error,
+ );
+ });
+ });
+});
+
+describe("CipherSuite", () => {
+ describe("constructor with DhkemX25519HkdfSha256", () => {
+ it("should have a correct ciphersuite", () => {
+ const suite: CipherSuite = new CipherSuite({
+ kem: new DhkemX25519HkdfSha256(),
+ kdf: KdfId.HkdfSha256,
+ aead: AeadId.ExportOnly,
+ });
+ assertEquals(suite.kem.secretSize, 32);
+ assertEquals(suite.kem.encSize, 32);
+ assertEquals(suite.kem.publicKeySize, 32);
+ assertEquals(suite.kem.privateKeySize, 32);
+
+ // assert
+ assertEquals(suite.kem.id, KemId.DhkemX25519HkdfSha256);
+ assertEquals(suite.kem.id, 0x0020);
+ assertEquals(suite.kdf.id, KdfId.HkdfSha256);
+ assertEquals(suite.kdf.id, 0x0001);
+ assertEquals(suite.aead.id, AeadId.ExportOnly);
+ assertEquals(suite.aead.id, 0xFFFF);
+ });
+ });
+
+ describe("A README example of DhkemX25519HkdfSha256", () => {
+ it("should work normally", async () => {
+ // setup
+ const kem = new DhkemX25519HkdfSha256();
+ const suite = new CipherSuite({
+ kem: kem,
+ kdf: KdfId.HkdfSha256,
+ aead: AeadId.Aes128Gcm,
+ });
+
+ const rkp = await suite.generateKeyPair();
+
+ const sender = await suite.createSenderContext({
+ recipientPublicKey: rkp.publicKey,
+ });
+
+ const recipient = await suite.createRecipientContext({
+ recipientKey: rkp,
+ enc: sender.enc,
+ });
+ assertEquals(sender.enc.byteLength, kem.encSize);
+ assertEquals(sender.enc.byteLength, kem.publicKeySize);
+
+ // encrypt
+ const ct = await sender.seal(
+ new TextEncoder().encode("my-secret-message"),
+ );
+
+ // decrypt
+ const pt = await recipient.open(ct);
+
+ // assert
+ assertEquals(new TextDecoder().decode(pt), "my-secret-message");
+ });
+ });
+});
diff --git a/x/dhkem-x25519/test/runtimes/browsers/dhkem-x25519.spec.ts b/x/dhkem-x25519/test/runtimes/browsers/dhkem-x25519.spec.ts
new file mode 100644
index 000000000..599412640
--- /dev/null
+++ b/x/dhkem-x25519/test/runtimes/browsers/dhkem-x25519.spec.ts
@@ -0,0 +1,9 @@
+import { expect, test } from "@playwright/test";
+
+test("basic test", async ({ page }) => {
+ await page.goto("https://dajiaji.github.io/hpke-js/dhkem-x25519");
+ await page.click("text=run");
+ await page.waitForTimeout(5000);
+ await expect(page.locator("id=pass")).toHaveText("6");
+ await expect(page.locator("id=fail")).toHaveText("0");
+});
diff --git a/x/dhkem-x25519/test/runtimes/browsers/package.json b/x/dhkem-x25519/test/runtimes/browsers/package.json
new file mode 100644
index 000000000..22ec1425b
--- /dev/null
+++ b/x/dhkem-x25519/test/runtimes/browsers/package.json
@@ -0,0 +1,5 @@
+{
+ "devDependencies": {
+ "@playwright/test": "^1.36.1"
+ }
+}
diff --git a/x/dhkem-x25519/test/runtimes/browsers/pages/index.html b/x/dhkem-x25519/test/runtimes/browsers/pages/index.html
new file mode 100644
index 000000000..8f0788ff2
--- /dev/null
+++ b/x/dhkem-x25519/test/runtimes/browsers/pages/index.html
@@ -0,0 +1,86 @@
+
+ @hpke/dhkem-x25519 test
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ pass: |
+ - |
+
+
+ fail: |
+ - |
+
+
+
+
+
diff --git a/x/dhkem-x25519/test/runtimes/browsers/pages/src/.gitkeep b/x/dhkem-x25519/test/runtimes/browsers/pages/src/.gitkeep
new file mode 100644
index 000000000..e69de29bb
diff --git a/x/dhkem-x25519/test/runtimes/browsers/playwright.config.ts b/x/dhkem-x25519/test/runtimes/browsers/playwright.config.ts
new file mode 100644
index 000000000..ec692f847
--- /dev/null
+++ b/x/dhkem-x25519/test/runtimes/browsers/playwright.config.ts
@@ -0,0 +1,20 @@
+// playwright.config.ts
+import { devices, PlaywrightTestConfig } from "@playwright/test";
+
+const config: PlaywrightTestConfig = {
+ projects: [
+ {
+ name: "chromium",
+ use: { ...devices["Desktop Chrome"] },
+ },
+ {
+ name: "firefox",
+ use: { ...devices["Desktop Firefox"] },
+ },
+ {
+ name: "webkit",
+ use: { ...devices["Desktop Safari"] },
+ },
+ ],
+};
+export default config;
diff --git a/x/dhkem-x25519/test/runtimes/bun/dhkem-x25519.spec.ts b/x/dhkem-x25519/test/runtimes/bun/dhkem-x25519.spec.ts
new file mode 100644
index 000000000..d5aa70dbc
--- /dev/null
+++ b/x/dhkem-x25519/test/runtimes/bun/dhkem-x25519.spec.ts
@@ -0,0 +1,17 @@
+import { assertEquals } from "testing/asserts.ts";
+import { describe, it } from "testing/bdd.ts";
+
+describe("Bun", () => {
+ describe("GET /test", () => {
+ it("should return ok", async () => {
+ for (const kdf of ["0x0001", "0x0002", "0x0003"]) {
+ for (const aead of ["0x0001", "0x0002"]) {
+ const res = await fetch(
+ `http://localhost:3003/test?kdf=${kdf}&aead=${aead}`,
+ );
+ assertEquals("ok", await res.text());
+ }
+ }
+ });
+ });
+});
diff --git a/x/dhkem-x25519/test/runtimes/bun/src/index.js b/x/dhkem-x25519/test/runtimes/bun/src/index.js
new file mode 100644
index 000000000..41f93783c
--- /dev/null
+++ b/x/dhkem-x25519/test/runtimes/bun/src/index.js
@@ -0,0 +1,8 @@
+import { testServer } from "../../server.js";
+
+export default {
+ port: 3003,
+ async fetch(request) {
+ return await testServer(request);
+ },
+};
diff --git a/x/dhkem-x25519/test/runtimes/cloudflare/dhkem-x25519.spec.ts b/x/dhkem-x25519/test/runtimes/cloudflare/dhkem-x25519.spec.ts
new file mode 100644
index 000000000..40ff9cb74
--- /dev/null
+++ b/x/dhkem-x25519/test/runtimes/cloudflare/dhkem-x25519.spec.ts
@@ -0,0 +1,17 @@
+import { assertEquals } from "testing/asserts.ts";
+import { describe, it } from "testing/bdd.ts";
+
+describe("Cloudflare Workers", () => {
+ describe("GET /test", () => {
+ it("should return ok", async () => {
+ for (const kdf of ["0x0001", "0x0002", "0x0003"]) {
+ for (const aead of ["0x0001", "0x0002"]) {
+ const res = await fetch(
+ `http://localhost:8788/test?kdf=${kdf}&aead=${aead}`,
+ );
+ assertEquals("ok", await res.text());
+ }
+ }
+ });
+ });
+});
diff --git a/x/dhkem-x25519/test/runtimes/cloudflare/package.json b/x/dhkem-x25519/test/runtimes/cloudflare/package.json
new file mode 100644
index 000000000..0b9a27570
--- /dev/null
+++ b/x/dhkem-x25519/test/runtimes/cloudflare/package.json
@@ -0,0 +1,12 @@
+{
+ "name": "wrangler",
+ "version": "0.1.0",
+ "devDependencies": {
+ "wrangler": "^3.3.0"
+ },
+ "private": true,
+ "scripts": {
+ "start": "wrangler dev --port 8790",
+ "deploy": "wrangler publish"
+ }
+}
diff --git a/x/dhkem-x25519/test/runtimes/cloudflare/src/index.js b/x/dhkem-x25519/test/runtimes/cloudflare/src/index.js
new file mode 100644
index 000000000..2b1b697ea
--- /dev/null
+++ b/x/dhkem-x25519/test/runtimes/cloudflare/src/index.js
@@ -0,0 +1,7 @@
+import { testServer } from "../../server.js";
+
+export default {
+ async fetch(request) {
+ return await testServer(request);
+ },
+};
diff --git a/x/dhkem-x25519/test/runtimes/cloudflare/wrangler.toml b/x/dhkem-x25519/test/runtimes/cloudflare/wrangler.toml
new file mode 100644
index 000000000..ee79b9b87
--- /dev/null
+++ b/x/dhkem-x25519/test/runtimes/cloudflare/wrangler.toml
@@ -0,0 +1,4 @@
+name = "wrangler"
+main = "src/index.js"
+compatibility_date = "2022-07-01"
+node_compat = true
diff --git a/x/dhkem-x25519/test/runtimes/server.js b/x/dhkem-x25519/test/runtimes/server.js
new file mode 100644
index 000000000..bf0f85cb6
--- /dev/null
+++ b/x/dhkem-x25519/test/runtimes/server.js
@@ -0,0 +1,41 @@
+import * as hpke from "./hpke-core.js";
+import { DhkemX25519HkdfSha256 } from "./hpke-dhkem-x25519.js";
+
+export async function testServer(request) {
+ const url = new URL(request.url);
+ if (url.pathname !== "/test") {
+ return new Response("ng: invalid path");
+ }
+ const params = url.searchParams;
+ const kdfStr = params.get("kdf");
+ const aeadStr = params.get("aead");
+ if (kdfStr === null || aeadStr === null) {
+ return new Response("ng: invalid params");
+ }
+ const kem = new DhkemX25519HkdfSha256();
+ const kdf = Number.parseInt(kdfStr);
+ const aead = Number.parseInt(aeadStr);
+ if (Number.isNaN(kdf) || Number.isNaN(aead)) {
+ return new Response("ng: invalid params");
+ }
+
+ try {
+ const suite = new hpke.CipherSuite({ kem: kem, kdf: kdf, aead: aead });
+ 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);
+ if ("hello world!" !== new TextDecoder().decode(pt)) {
+ return new Response("ng");
+ }
+ } catch (e) {
+ return new Response("ng: " + e.message);
+ }
+ return new Response("ok");
+}
diff --git a/x/dhkem-x25519/test/utils.ts b/x/dhkem-x25519/test/utils.ts
new file mode 100644
index 000000000..830172325
--- /dev/null
+++ b/x/dhkem-x25519/test/utils.ts
@@ -0,0 +1,48 @@
+export const isBrowser = () => typeof window !== "undefined";
+
+export const isCloudflareWorkers = () => typeof caches !== "undefined";
+
+export function hexStringToBytes(v: string): Uint8Array {
+ if (v.length === 0) {
+ return new Uint8Array([]);
+ }
+ const res = v.match(/[\da-f]{2}/gi);
+ if (res == null) {
+ throw new Error("hexStringToBytes: not hex string");
+ }
+ return new Uint8Array(res.map(function (h) {
+ return parseInt(h, 16);
+ }));
+}
+
+export async function loadCrypto(): Promise {
+ if (isBrowser() || isCloudflareWorkers()) {
+ if (globalThis.crypto !== undefined) {
+ return globalThis.crypto;
+ }
+ // jsdom
+ }
+
+ try {
+ const { webcrypto } = await import("crypto"); // node:crypto
+ return (webcrypto as unknown as Crypto);
+ } catch (_e: unknown) {
+ throw new Error("Web Cryptograph API not supported");
+ }
+}
+
+export async function loadSubtleCrypto(): Promise {
+ if (isBrowser() || isCloudflareWorkers()) {
+ if (globalThis.crypto !== undefined) {
+ return globalThis.crypto.subtle;
+ }
+ // jsdom
+ }
+
+ try {
+ const { webcrypto } = await import("crypto"); // node:crypto
+ return (webcrypto as unknown as Crypto).subtle;
+ } catch (_e: unknown) {
+ throw new Error("Web Cryptograph API not supported");
+ }
+}