From 8bcce3400804bbe20d59ada80183934e79125574 Mon Sep 17 00:00:00 2001 From: Weiliang Li Date: Wed, 13 Nov 2024 07:22:34 +0900 Subject: [PATCH] Add PublicKey.toBytes (#814) --- CHANGELOG.md | 5 + README.md | 21 +++-- example/browser/package.json | 2 +- example/runtime/main.js | 2 +- package.json | 2 +- pnpm-lock.yaml | 178 +++++++++++++++++------------------ src/index.ts | 4 +- src/keys/PrivateKey.ts | 9 +- src/keys/PublicKey.ts | 34 ++++--- tests/crypt/random.test.ts | 12 +-- tests/integration.test.ts | 23 +++-- tests/keys/known.test.ts | 2 - tests/keys/random.test.ts | 25 ++++- 13 files changed, 174 insertions(+), 145 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 92e38ff..be14a7f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ # Changelog +## 0.4.12 + +- Add `PublicKey.toBytes` and deprecate `PublicKey.compressed` and `PublicKey.uncompressed` +- Save uncompressed public key data for secp256k1 + ## 0.4.11 - Revamp encapsulate/decapsulate diff --git a/README.md b/README.md index eaf93f6..950e78b 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ import { PrivateKey, decrypt, encrypt } from "eciesjs"; const sk = new PrivateKey() const data = Buffer.from("hello world🌍") -const decrypted = decrypt(sk.secret, encrypt(sk.publicKey.compressed, data)) +const decrypted = decrypt(sk.secret, encrypt(sk.publicKey.toBytes(), data)) console.log(Buffer.from(decrypted).toString()) ``` @@ -42,16 +42,18 @@ See [Configuration](#configuration) to control with more granularity. ### Browser -This library is browser-friendly, check the [`example/browser`](./example/browser) directory for details. Currently it's necessary to polyfill `Buffer` for backward compatibility. From v0.5.0, it can run in browsers as is. +This library is browser-friendly, check the [`example/browser`](./example/browser) directory for details. The online demo is hosted [here](https://js-demo.ecies.org/). + +Currently it's necessary to polyfill `Buffer` for backward compatibility. From v0.5.0, it can run in browsers as is. If you want a WASM version to run directly in modern browsers or on some blockchains, you can also try [`ecies-wasm`](https://github.com/ecies/rs-wasm). ### Bun/Deno -For bun/deno, see [`example/runtime`](./example/runtime). There are some limitations currently: +For bun/deno, see [`example/runtime`](./example/runtime). There are some limitations currently, mentioned in [`@ecies/ciphers`](https://github.com/ecies/js-ciphers#known-limitations): -- `xchacha20` does not work on bun -- Only `aes-256-gcm` with 12 bytes nonce works on deno +- `node:crypto`'s `xchacha20` does not work on bun (pure JS implementation is used instead) +- `aes-256-gcm` only works with 12 bytes nonce on deno (deno is not handling package exports correctly) ### React Native @@ -85,7 +87,7 @@ Returns: **Buffer** static fromHex(hex: string): PrivateKey; constructor(secret?: Uint8Array); toHex(): string; -encapsulate(pk: PublicKey): Uint8Array; +encapsulate(pk: PublicKey, compressed?: boolean): Uint8Array; multiply(pk: PublicKey, compressed?: boolean): Uint8Array; equals(other: PrivateKey): boolean; ``` @@ -95,7 +97,6 @@ equals(other: PrivateKey): boolean; ```typescript get secret(): Buffer; readonly publicKey: PublicKey; -private readonly data; ``` ### `PublicKey` @@ -105,17 +106,19 @@ private readonly data; ```typescript static fromHex(hex: string): PublicKey; constructor(data: Uint8Array); +toBytes(compressed?: boolean): Uint8Array; toHex(compressed?: boolean): string; -decapsulate(sk: PrivateKey): Uint8Array; +decapsulate(sk: PrivateKey, compressed?: boolean): Uint8Array; equals(other: PublicKey): boolean; ``` - Properties ```typescript +/** @deprecated - use `PublicKey.toBytes(false)` instead. You may also need `Buffer.from`. */ get uncompressed(): Buffer; +/** @deprecated - use `PublicKey.toBytes()` instead. You may also need `Buffer.from`. */ get compressed(): Buffer; -private readonly data; ``` ## Configuration diff --git a/example/browser/package.json b/example/browser/package.json index 442ad9c..13ad777 100644 --- a/example/browser/package.json +++ b/example/browser/package.json @@ -16,7 +16,7 @@ "eciesjs": "file:../.." }, "devDependencies": { - "vite": "6.0.0-beta.5", + "vite": "6.0.0-beta.9", "vite-bundle-visualizer": "^1.2.1" } } diff --git a/example/runtime/main.js b/example/runtime/main.js index b8a63b3..849f05d 100644 --- a/example/runtime/main.js +++ b/example/runtime/main.js @@ -14,5 +14,5 @@ ECIES_CONFIG.symmetricNonceLength = 12; const sk = new PrivateKey(); const data = Buffer.from("hello world🌍"); -const decrypted = decrypt(sk.secret, encrypt(sk.publicKey.compressed, data)); +const decrypted = decrypt(sk.secret, encrypt(sk.publicKey.toBytes(), data)); console.log(Buffer.from(decrypted).toString()); diff --git a/package.json b/package.json index 28cdcfd..86308f1 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "type": "git", "url": "git+https://github.com/ecies/js.git" }, - "version": "0.4.11", + "version": "0.4.12", "engines": { "node": ">=16", "bun": ">=1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ca4a621..149f9e9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -249,93 +249,93 @@ packages: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} - '@rollup/rollup-android-arm-eabi@4.24.4': - resolution: {integrity: sha512-jfUJrFct/hTA0XDM5p/htWKoNNTbDLY0KRwEt6pyOA6k2fmk0WVwl65PdUdJZgzGEHWx+49LilkcSaumQRyNQw==} + '@rollup/rollup-android-arm-eabi@4.25.0': + resolution: {integrity: sha512-CC/ZqFZwlAIbU1wUPisHyV/XRc5RydFrNLtgl3dGYskdwPZdt4HERtKm50a/+DtTlKeCq9IXFEWR+P6blwjqBA==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.24.4': - resolution: {integrity: sha512-j4nrEO6nHU1nZUuCfRKoCcvh7PIywQPUCBa2UsootTHvTHIoIu2BzueInGJhhvQO/2FTRdNYpf63xsgEqH9IhA==} + '@rollup/rollup-android-arm64@4.25.0': + resolution: {integrity: sha512-/Y76tmLGUJqVBXXCfVS8Q8FJqYGhgH4wl4qTA24E9v/IJM0XvJCGQVSW1QZ4J+VURO9h8YCa28sTFacZXwK7Rg==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.24.4': - resolution: {integrity: sha512-GmU/QgGtBTeraKyldC7cDVVvAJEOr3dFLKneez/n7BvX57UdhOqDsVwzU7UOnYA7AAOt+Xb26lk79PldDHgMIQ==} + '@rollup/rollup-darwin-arm64@4.25.0': + resolution: {integrity: sha512-YVT6L3UrKTlC0FpCZd0MGA7NVdp7YNaEqkENbWQ7AOVOqd/7VzyHpgIpc1mIaxRAo1ZsJRH45fq8j4N63I/vvg==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.24.4': - resolution: {integrity: sha512-N6oDBiZCBKlwYcsEPXGDE4g9RoxZLK6vT98M8111cW7VsVJFpNEqvJeIPfsCzbf0XEakPslh72X0gnlMi4Ddgg==} + '@rollup/rollup-darwin-x64@4.25.0': + resolution: {integrity: sha512-ZRL+gexs3+ZmmWmGKEU43Bdn67kWnMeWXLFhcVv5Un8FQcx38yulHBA7XR2+KQdYIOtD0yZDWBCudmfj6lQJoA==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.24.4': - resolution: {integrity: sha512-py5oNShCCjCyjWXCZNrRGRpjWsF0ic8f4ieBNra5buQz0O/U6mMXCpC1LvrHuhJsNPgRt36tSYMidGzZiJF6mw==} + '@rollup/rollup-freebsd-arm64@4.25.0': + resolution: {integrity: sha512-xpEIXhiP27EAylEpreCozozsxWQ2TJbOLSivGfXhU4G1TBVEYtUPi2pOZBnvGXHyOdLAUUhPnJzH3ah5cqF01g==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.24.4': - resolution: {integrity: sha512-L7VVVW9FCnTTp4i7KrmHeDsDvjB4++KOBENYtNYAiYl96jeBThFfhP6HVxL74v4SiZEVDH/1ILscR5U9S4ms4g==} + '@rollup/rollup-freebsd-x64@4.25.0': + resolution: {integrity: sha512-sC5FsmZGlJv5dOcURrsnIK7ngc3Kirnx3as2XU9uER+zjfyqIjdcMVgzy4cOawhsssqzoAX19qmxgJ8a14Qrqw==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.24.4': - resolution: {integrity: sha512-10ICosOwYChROdQoQo589N5idQIisxjaFE/PAnX2i0Zr84mY0k9zul1ArH0rnJ/fpgiqfu13TFZR5A5YJLOYZA==} + '@rollup/rollup-linux-arm-gnueabihf@4.25.0': + resolution: {integrity: sha512-uD/dbLSs1BEPzg564TpRAQ/YvTnCds2XxyOndAO8nJhaQcqQGFgv/DAVko/ZHap3boCvxnzYMa3mTkV/B/3SWA==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.24.4': - resolution: {integrity: sha512-ySAfWs69LYC7QhRDZNKqNhz2UKN8LDfbKSMAEtoEI0jitwfAG2iZwVqGACJT+kfYvvz3/JgsLlcBP+WWoKCLcw==} + '@rollup/rollup-linux-arm-musleabihf@4.25.0': + resolution: {integrity: sha512-ZVt/XkrDlQWegDWrwyC3l0OfAF7yeJUF4fq5RMS07YM72BlSfn2fQQ6lPyBNjt+YbczMguPiJoCfaQC2dnflpQ==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.24.4': - resolution: {integrity: sha512-uHYJ0HNOI6pGEeZ/5mgm5arNVTI0nLlmrbdph+pGXpC9tFHFDQmDMOEqkmUObRfosJqpU8RliYoGz06qSdtcjg==} + '@rollup/rollup-linux-arm64-gnu@4.25.0': + resolution: {integrity: sha512-qboZ+T0gHAW2kkSDPHxu7quaFaaBlynODXpBVnPxUgvWYaE84xgCKAPEYE+fSMd3Zv5PyFZR+L0tCdYCMAtG0A==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.24.4': - resolution: {integrity: sha512-38yiWLemQf7aLHDgTg85fh3hW9stJ0Muk7+s6tIkSUOMmi4Xbv5pH/5Bofnsb6spIwD5FJiR+jg71f0CH5OzoA==} + '@rollup/rollup-linux-arm64-musl@4.25.0': + resolution: {integrity: sha512-ndWTSEmAaKr88dBuogGH2NZaxe7u2rDoArsejNslugHZ+r44NfWiwjzizVS1nUOHo+n1Z6qV3X60rqE/HlISgw==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-powerpc64le-gnu@4.24.4': - resolution: {integrity: sha512-q73XUPnkwt9ZNF2xRS4fvneSuaHw2BXuV5rI4cw0fWYVIWIBeDZX7c7FWhFQPNTnE24172K30I+dViWRVD9TwA==} + '@rollup/rollup-linux-powerpc64le-gnu@4.25.0': + resolution: {integrity: sha512-BVSQvVa2v5hKwJSy6X7W1fjDex6yZnNKy3Kx1JGimccHft6HV0THTwNtC2zawtNXKUu+S5CjXslilYdKBAadzA==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.24.4': - resolution: {integrity: sha512-Aie/TbmQi6UXokJqDZdmTJuZBCU3QBDA8oTKRGtd4ABi/nHgXICulfg1KI6n9/koDsiDbvHAiQO3YAUNa/7BCw==} + '@rollup/rollup-linux-riscv64-gnu@4.25.0': + resolution: {integrity: sha512-G4hTREQrIdeV0PE2JruzI+vXdRnaK1pg64hemHq2v5fhv8C7WjVaeXc9P5i4Q5UC06d/L+zA0mszYIKl+wY8oA==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.24.4': - resolution: {integrity: sha512-P8MPErVO/y8ohWSP9JY7lLQ8+YMHfTI4bAdtCi3pC2hTeqFJco2jYspzOzTUB8hwUWIIu1xwOrJE11nP+0JFAQ==} + '@rollup/rollup-linux-s390x-gnu@4.25.0': + resolution: {integrity: sha512-9T/w0kQ+upxdkFL9zPVB6zy9vWW1deA3g8IauJxojN4bnz5FwSsUAD034KpXIVX5j5p/rn6XqumBMxfRkcHapQ==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.24.4': - resolution: {integrity: sha512-K03TljaaoPK5FOyNMZAAEmhlyO49LaE4qCsr0lYHUKyb6QacTNF9pnfPpXnFlFD3TXuFbFbz7tJ51FujUXkXYA==} + '@rollup/rollup-linux-x64-gnu@4.25.0': + resolution: {integrity: sha512-ThcnU0EcMDn+J4B9LD++OgBYxZusuA7iemIIiz5yzEcFg04VZFzdFjuwPdlURmYPZw+fgVrFzj4CA64jSTG4Ig==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.24.4': - resolution: {integrity: sha512-VJYl4xSl/wqG2D5xTYncVWW+26ICV4wubwN9Gs5NrqhJtayikwCXzPL8GDsLnaLU3WwhQ8W02IinYSFJfyo34Q==} + '@rollup/rollup-linux-x64-musl@4.25.0': + resolution: {integrity: sha512-zx71aY2oQxGxAT1JShfhNG79PnjYhMC6voAjzpu/xmMjDnKNf6Nl/xv7YaB/9SIa9jDYf8RBPWEnjcdlhlv1rQ==} cpu: [x64] os: [linux] - '@rollup/rollup-win32-arm64-msvc@4.24.4': - resolution: {integrity: sha512-ku2GvtPwQfCqoPFIJCqZ8o7bJcj+Y54cZSr43hHca6jLwAiCbZdBUOrqE6y29QFajNAzzpIOwsckaTFmN6/8TA==} + '@rollup/rollup-win32-arm64-msvc@4.25.0': + resolution: {integrity: sha512-JT8tcjNocMs4CylWY/CxVLnv8e1lE7ff1fi6kbGocWwxDq9pj30IJ28Peb+Y8yiPNSF28oad42ApJB8oUkwGww==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.24.4': - resolution: {integrity: sha512-V3nCe+eTt/W6UYNr/wGvO1fLpHUrnlirlypZfKCT1fG6hWfqhPgQV/K/mRBXBpxc0eKLIF18pIOFVPh0mqHjlg==} + '@rollup/rollup-win32-ia32-msvc@4.25.0': + resolution: {integrity: sha512-dRLjLsO3dNOfSN6tjyVlG+Msm4IiZnGkuZ7G5NmpzwF9oOc582FZG05+UdfTbz5Jd4buK/wMb6UeHFhG18+OEg==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.24.4': - resolution: {integrity: sha512-LTw1Dfd0mBIEqUVCxbvTE/LLo+9ZxVC9k99v1v4ahg9Aak6FpqOfNu5kRkeTAn0wphoC4JU7No1/rL+bBCEwhg==} + '@rollup/rollup-win32-x64-msvc@4.25.0': + resolution: {integrity: sha512-/RqrIFtLB926frMhZD0a5oDa4eFIbyNEwLLloMTEjmqfwZWXywwVVOVmwTsuyhC9HKkVEZcOOi+KV4U9wmOdlg==} cpu: [x64] os: [win32] @@ -428,8 +428,8 @@ packages: color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - cross-spawn@7.0.3: - resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + cross-spawn@7.0.5: + resolution: {integrity: sha512-ZVJrKKYunU38/76t0RMOulHOnUcbU9GbpWKAOZ0mhjr7CX6FVrH+4FrAapSOekrgFQ3f/8gwMEuIft0aKq6Hug==} engines: {node: '>= 8'} debug@4.3.7: @@ -565,12 +565,12 @@ packages: picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} - postcss@8.4.47: - resolution: {integrity: sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==} + postcss@8.4.48: + resolution: {integrity: sha512-GCRK8F6+Dl7xYniR5a4FYbpBzU8XnZVeowqsQFYdcXuSbChgiks7qybSkbvnaeqv0G0B+dd9/jJgH8kkLDQeEA==} engines: {node: ^10 || ^12 || >=14} - rollup@4.24.4: - resolution: {integrity: sha512-vGorVWIsWfX3xbcyAS+I047kFKapHYivmkaT63Smj77XwvLSJos6M1xGqZnBPFQFBRZDOcG1QnYEIxAvTr/HjA==} + rollup@4.25.0: + resolution: {integrity: sha512-uVbClXmR6wvx5R1M3Od4utyLUxrmOcEm3pAtMphn73Apq19PDtHpgZoEvqH2YnnaNUuvKmg2DgRd2Sqv+odyqg==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -601,8 +601,8 @@ packages: stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} - std-env@3.7.0: - resolution: {integrity: sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==} + std-env@3.8.0: + resolution: {integrity: sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w==} string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} @@ -871,58 +871,58 @@ snapshots: '@pkgjs/parseargs@0.11.0': optional: true - '@rollup/rollup-android-arm-eabi@4.24.4': + '@rollup/rollup-android-arm-eabi@4.25.0': optional: true - '@rollup/rollup-android-arm64@4.24.4': + '@rollup/rollup-android-arm64@4.25.0': optional: true - '@rollup/rollup-darwin-arm64@4.24.4': + '@rollup/rollup-darwin-arm64@4.25.0': optional: true - '@rollup/rollup-darwin-x64@4.24.4': + '@rollup/rollup-darwin-x64@4.25.0': optional: true - '@rollup/rollup-freebsd-arm64@4.24.4': + '@rollup/rollup-freebsd-arm64@4.25.0': optional: true - '@rollup/rollup-freebsd-x64@4.24.4': + '@rollup/rollup-freebsd-x64@4.25.0': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.24.4': + '@rollup/rollup-linux-arm-gnueabihf@4.25.0': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.24.4': + '@rollup/rollup-linux-arm-musleabihf@4.25.0': optional: true - '@rollup/rollup-linux-arm64-gnu@4.24.4': + '@rollup/rollup-linux-arm64-gnu@4.25.0': optional: true - '@rollup/rollup-linux-arm64-musl@4.24.4': + '@rollup/rollup-linux-arm64-musl@4.25.0': optional: true - '@rollup/rollup-linux-powerpc64le-gnu@4.24.4': + '@rollup/rollup-linux-powerpc64le-gnu@4.25.0': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.24.4': + '@rollup/rollup-linux-riscv64-gnu@4.25.0': optional: true - '@rollup/rollup-linux-s390x-gnu@4.24.4': + '@rollup/rollup-linux-s390x-gnu@4.25.0': optional: true - '@rollup/rollup-linux-x64-gnu@4.24.4': + '@rollup/rollup-linux-x64-gnu@4.25.0': optional: true - '@rollup/rollup-linux-x64-musl@4.24.4': + '@rollup/rollup-linux-x64-musl@4.25.0': optional: true - '@rollup/rollup-win32-arm64-msvc@4.24.4': + '@rollup/rollup-win32-arm64-msvc@4.25.0': optional: true - '@rollup/rollup-win32-ia32-msvc@4.24.4': + '@rollup/rollup-win32-ia32-msvc@4.25.0': optional: true - '@rollup/rollup-win32-x64-msvc@4.24.4': + '@rollup/rollup-win32-x64-msvc@4.25.0': optional: true '@types/estree@1.0.6': {} @@ -942,7 +942,7 @@ snapshots: istanbul-reports: 3.1.7 magic-string: 0.30.12 magicast: 0.3.5 - std-env: 3.7.0 + std-env: 3.8.0 test-exclude: 7.0.1 tinyrainbow: 1.2.0 vitest: 2.1.4(@types/node@22.9.0) @@ -1025,7 +1025,7 @@ snapshots: color-name@1.1.4: {} - cross-spawn@7.0.3: + cross-spawn@7.0.5: dependencies: path-key: 3.1.1 shebang-command: 2.0.0 @@ -1077,7 +1077,7 @@ snapshots: foreground-child@3.3.0: dependencies: - cross-spawn: 7.0.3 + cross-spawn: 7.0.5 signal-exit: 4.1.0 fsevents@2.3.3: @@ -1170,34 +1170,34 @@ snapshots: picocolors@1.1.1: {} - postcss@8.4.47: + postcss@8.4.48: dependencies: nanoid: 3.3.7 picocolors: 1.1.1 source-map-js: 1.2.1 - rollup@4.24.4: + rollup@4.25.0: dependencies: '@types/estree': 1.0.6 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.24.4 - '@rollup/rollup-android-arm64': 4.24.4 - '@rollup/rollup-darwin-arm64': 4.24.4 - '@rollup/rollup-darwin-x64': 4.24.4 - '@rollup/rollup-freebsd-arm64': 4.24.4 - '@rollup/rollup-freebsd-x64': 4.24.4 - '@rollup/rollup-linux-arm-gnueabihf': 4.24.4 - '@rollup/rollup-linux-arm-musleabihf': 4.24.4 - '@rollup/rollup-linux-arm64-gnu': 4.24.4 - '@rollup/rollup-linux-arm64-musl': 4.24.4 - '@rollup/rollup-linux-powerpc64le-gnu': 4.24.4 - '@rollup/rollup-linux-riscv64-gnu': 4.24.4 - '@rollup/rollup-linux-s390x-gnu': 4.24.4 - '@rollup/rollup-linux-x64-gnu': 4.24.4 - '@rollup/rollup-linux-x64-musl': 4.24.4 - '@rollup/rollup-win32-arm64-msvc': 4.24.4 - '@rollup/rollup-win32-ia32-msvc': 4.24.4 - '@rollup/rollup-win32-x64-msvc': 4.24.4 + '@rollup/rollup-android-arm-eabi': 4.25.0 + '@rollup/rollup-android-arm64': 4.25.0 + '@rollup/rollup-darwin-arm64': 4.25.0 + '@rollup/rollup-darwin-x64': 4.25.0 + '@rollup/rollup-freebsd-arm64': 4.25.0 + '@rollup/rollup-freebsd-x64': 4.25.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.25.0 + '@rollup/rollup-linux-arm-musleabihf': 4.25.0 + '@rollup/rollup-linux-arm64-gnu': 4.25.0 + '@rollup/rollup-linux-arm64-musl': 4.25.0 + '@rollup/rollup-linux-powerpc64le-gnu': 4.25.0 + '@rollup/rollup-linux-riscv64-gnu': 4.25.0 + '@rollup/rollup-linux-s390x-gnu': 4.25.0 + '@rollup/rollup-linux-x64-gnu': 4.25.0 + '@rollup/rollup-linux-x64-musl': 4.25.0 + '@rollup/rollup-win32-arm64-msvc': 4.25.0 + '@rollup/rollup-win32-ia32-msvc': 4.25.0 + '@rollup/rollup-win32-x64-msvc': 4.25.0 fsevents: 2.3.3 semver@7.6.3: {} @@ -1216,7 +1216,7 @@ snapshots: stackback@0.0.2: {} - std-env@3.7.0: {} + std-env@3.8.0: {} string-width@4.2.3: dependencies: @@ -1284,8 +1284,8 @@ snapshots: vite@5.4.10(@types/node@22.9.0): dependencies: esbuild: 0.21.5 - postcss: 8.4.47 - rollup: 4.24.4 + postcss: 8.4.48 + rollup: 4.25.0 optionalDependencies: '@types/node': 22.9.0 fsevents: 2.3.3 @@ -1304,7 +1304,7 @@ snapshots: expect-type: 1.1.0 magic-string: 0.30.12 pathe: 1.1.2 - std-env: 3.7.0 + std-env: 3.8.0 tinybench: 2.9.0 tinyexec: 0.3.1 tinypool: 1.0.1 diff --git a/src/index.ts b/src/index.ts index c17984a..f622e59 100644 --- a/src/index.ts +++ b/src/index.ts @@ -34,9 +34,7 @@ export function encrypt(receiverRawPK: string | Uint8Array, msg: Uint8Array): Bu : PublicKey.fromHex(receiverRawPK); const sharedKey = ephemeralSK.encapsulate(receiverPK, isHkdfKeyCompressed()); - const ephemeralPK = isEphemeralKeyCompressed() - ? ephemeralSK.publicKey.compressed - : ephemeralSK.publicKey.uncompressed; + const ephemeralPK = ephemeralSK.publicKey.toBytes(isEphemeralKeyCompressed()); const encrypted = symEncrypt(sharedKey, msg); return Buffer.from(concatBytes(ephemeralPK, encrypted)); diff --git a/src/keys/PrivateKey.ts b/src/keys/PrivateKey.ts index e10872b..ba74471 100644 --- a/src/keys/PrivateKey.ts +++ b/src/keys/PrivateKey.ts @@ -18,6 +18,7 @@ export class PrivateKey { private readonly data: Uint8Array; public readonly publicKey: PublicKey; + /** @description From version 0.5.0, `Uint8Array` will be returned instead of `Buffer`. */ get secret(): Buffer { // TODO: Uint8Array return Buffer.from(this.data); @@ -50,19 +51,17 @@ export class PrivateKey { * [1]: Two reasons: the public keys are "random" bytes (albeit secp256k1 public keys are **not uniformly** random), and ephemeral keys are generated in every encryption. * * @param pk - Receiver's public key. - * @param compressed - Whether to use compressed or uncompressed public keys in the key derivation (secp256k1 only). + * @param compressed - (default: `false`) Whether to use compressed or uncompressed public keys in the key derivation (secp256k1 only). * @returns Shared secret, derived with HKDF-SHA256. */ public encapsulate(pk: PublicKey, compressed: boolean = false): Uint8Array { - const senderPoint = compressed - ? this.publicKey.compressed - : this.publicKey.uncompressed; + const senderPoint = this.publicKey.toBytes(compressed); const sharedPoint = this.multiply(pk, compressed); return getSharedKey(senderPoint, sharedPoint); } public multiply(pk: PublicKey, compressed: boolean = false): Uint8Array { - return getSharedPoint(this.data, pk.compressed, compressed); + return getSharedPoint(this.data, pk.toBytes(true), compressed); } public equals(other: PrivateKey): boolean { diff --git a/src/keys/PublicKey.ts b/src/keys/PublicKey.ts index 5af3cea..236a14c 100644 --- a/src/keys/PublicKey.ts +++ b/src/keys/PublicKey.ts @@ -9,27 +9,37 @@ export class PublicKey { } private readonly data: Uint8Array; // always compressed if secp256k1 + private readonly dataUncompressed: Uint8Array | null; + private get _uncompressed(): Uint8Array { + return this.dataUncompressed !== null ? this.dataUncompressed : this.data; + } + + /** @deprecated - use `PublicKey.toBytes(false)` instead. You may also need `Buffer.from`. */ get uncompressed(): Buffer { - // TODO: Uint8Array - return Buffer.from(convertPublicKeyFormat(this.data, false)); + return Buffer.from(this._uncompressed); // TODO: Uint8Array } + /** @deprecated - use `PublicKey.toBytes()` instead. You may also need `Buffer.from`. */ get compressed(): Buffer { - // TODO: Uint8Array - return Buffer.from(this.data); + return Buffer.from(this.data); // TODO: Uint8Array } constructor(data: Uint8Array) { - this.data = convertPublicKeyFormat(data, true); + // data can be either compressed or uncompressed if secp256k1 + const compressed = convertPublicKeyFormat(data, true); + const uncompressed = convertPublicKeyFormat(data, false); + this.data = compressed; + this.dataUncompressed = + compressed.length !== uncompressed.length ? uncompressed : null; + } + + public toBytes(compressed: boolean = true): Uint8Array { + return compressed ? this.data : this._uncompressed; } public toHex(compressed: boolean = true): string { - if (compressed) { - return bytesToHex(this.data); - } else { - return bytesToHex(this.uncompressed); - } + return bytesToHex(this.toBytes(compressed)); } /** @@ -38,11 +48,11 @@ export class PublicKey { * @see PrivateKey.encapsulate * * @param sk - Receiver's private key. - * @param compressed - Whether to use compressed or uncompressed public keys in the key derivation (secp256k1 only). + * @param compressed - (default: `false`) Whether to use compressed or uncompressed public keys in the key derivation (secp256k1 only). * @returns Shared secret, derived with HKDF-SHA256. */ public decapsulate(sk: PrivateKey, compressed: boolean = false): Uint8Array { - const senderPoint = compressed ? this.data : this.uncompressed; + const senderPoint = this.toBytes(compressed); const sharedPoint = sk.multiply(this, compressed); return getSharedKey(senderPoint, sharedPoint); } diff --git a/tests/crypt/random.test.ts b/tests/crypt/random.test.ts index f032853..41c43cc 100644 --- a/tests/crypt/random.test.ts +++ b/tests/crypt/random.test.ts @@ -6,7 +6,7 @@ import { ECIES_CONFIG, PrivateKey, decrypt, encrypt } from "../../src"; import { EllipticCurve } from "../../src/config"; const encoder = new TextEncoder(); -const TEXT = Buffer.from(encoder.encode("hello world🌍")); +const TEXT = encoder.encode("hello world🌍"); interface TestParameter { curve: EllipticCurve; @@ -60,18 +60,18 @@ describe.each(params)( ); function checkCompressed(sk: PrivateKey) { - const encrypted = encrypt(sk.publicKey.compressed, TEXT); - expect(decrypt(sk.secret, encrypted)).toStrictEqual(TEXT); + const encrypted = encrypt(sk.publicKey.toBytes(), TEXT); + expect(decrypt(sk.secret, encrypted)).toStrictEqual(Buffer.from(TEXT)); } function checkUncompressed(sk: PrivateKey) { - const encrypted = encrypt(sk.publicKey.uncompressed, TEXT); - expect(decrypt(sk.secret, encrypted)).toStrictEqual(TEXT); + const encrypted = encrypt(sk.publicKey.toBytes(false), TEXT); + expect(decrypt(sk.secret, encrypted)).toStrictEqual(Buffer.from(TEXT)); } function checkHex(sk: PrivateKey) { const encrypted = encrypt(sk.publicKey.toHex(), TEXT); - expect(decrypt(bytesToHex(sk.secret), encrypted)).toStrictEqual(TEXT); + expect(decrypt(bytesToHex(sk.secret), encrypted)).toStrictEqual(Buffer.from(TEXT)); } function testRandom() { diff --git a/tests/integration.test.ts b/tests/integration.test.ts index ec387f7..040b8e0 100644 --- a/tests/integration.test.ts +++ b/tests/integration.test.ts @@ -1,7 +1,7 @@ import { describe, expect, it } from "vitest"; import { bytesToHex, bytesToUtf8 } from "@noble/ciphers/utils"; -import { fetch, ProxyAgent, RequestInit } from "undici"; +import { ProxyAgent, request } from "undici"; import { decrypt, encrypt, PrivateKey } from "../src"; import { decodeHex } from "../src/utils"; @@ -32,20 +32,19 @@ describe("test encrypt and decrypt against python version", () => { }); }); -async function eciesApi(url: string, body: { data: string; pub?: string; prv?: string }) { - const config: RequestInit = { +async function eciesApi( + url: string, + params: { data: string; pub?: string; prv?: string } +) { + const proxy = process.env.https_proxy || process.env.http_proxy; + + const { body } = await request(url, { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded", }, - }; - const proxy = process.env.https_proxy || process.env.http_proxy; - if (proxy) { - config.dispatcher = new ProxyAgent(`${proxy}`); - } - - return await fetch(url, { - ...config, - body: new URLSearchParams(body), + body: new URLSearchParams(params).toString(), + dispatcher: proxy !== undefined ? new ProxyAgent(proxy) : undefined, }); + return body; } diff --git a/tests/keys/known.test.ts b/tests/keys/known.test.ts index d6f15d0..c7f3817 100644 --- a/tests/keys/known.test.ts +++ b/tests/keys/known.test.ts @@ -45,8 +45,6 @@ describe("test known keys", () => { const k2 = new PrivateKey(three); expect(k1.secret).toStrictEqual(Buffer.from(two)); expect(k2.secret).toStrictEqual(Buffer.from(three)); - expect(k1.equals(k1)).toBe(true); - expect(k1.equals(k2)).toBe(false); checkHkdf( k1, diff --git a/tests/keys/random.test.ts b/tests/keys/random.test.ts index 751f058..dca71e2 100644 --- a/tests/keys/random.test.ts +++ b/tests/keys/random.test.ts @@ -25,10 +25,31 @@ describe("test random keys", () => { expect(PublicKey.fromHex(sk.publicKey.toHex(compressed))).toStrictEqual(sk.publicKey); } + function checkBytes(sk: PrivateKey) { + expect(Buffer.from(sk.publicKey.toBytes())).toStrictEqual(sk.publicKey.compressed); + expect(Buffer.from(sk.publicKey.toBytes(false))).toStrictEqual( + sk.publicKey.uncompressed + ); + } + + function checkEquals(k1: PrivateKey, k2: PrivateKey) { + expect(k1).toStrictEqual(k1); + expect(k1).not.toStrictEqual(k2); + expect(k1.publicKey).toStrictEqual(k1.publicKey); + expect(k1.publicKey).not.toStrictEqual(k2.publicKey); + + expect(k1.equals(k1)).toBe(true); + expect(k1.equals(k2)).toBe(false); + expect(k1.publicKey.equals(k1.publicKey)).toBe(true); + expect(k1.publicKey.equals(k2.publicKey)).toBe(false); + } + function testRandom(compressed: boolean = false) { checkMultiply(new PrivateKey(), new PrivateKey(), compressed); checkEncapsulate(new PrivateKey(), new PrivateKey(), compressed); checkHex(new PrivateKey(), compressed); + checkBytes(new PrivateKey()); + checkEquals(new PrivateKey(), new PrivateKey()); } it("tests secp256k1", () => { @@ -38,17 +59,13 @@ describe("test random keys", () => { it("tests x25519", () => { ECIES_CONFIG.ellipticCurve = "x25519"; - testRandom(); - ECIES_CONFIG.ellipticCurve = "secp256k1"; }); it("tests ed25519", () => { ECIES_CONFIG.ellipticCurve = "ed25519"; - testRandom(); - ECIES_CONFIG.ellipticCurve = "secp256k1"; }); });