From dc14a2cde9a461dc4fcefa159ff7916c538963c6 Mon Sep 17 00:00:00 2001 From: 0xZensh Date: Sun, 21 Apr 2024 00:26:15 +0800 Subject: [PATCH] Fix: fix `fromBytes` static method, #1 --- pnpm-lock.yaml | 26 +++++++++++++------------- src/aesgcm.test.ts | 5 ++++- src/aesgcm.ts | 8 ++++++-- src/chacha20poly1305.test.ts | 5 ++++- src/chacha20poly1305.ts | 6 +++++- src/ecdsa.test.ts | 5 ++++- src/ecdsa.ts | 5 +++++ src/ed25519.test.ts | 5 ++++- src/ed25519.ts | 6 +++++- src/hmac.test.ts | 4 +++- src/hmac.ts | 7 ++++++- 11 files changed, 59 insertions(+), 23 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f7ba68d..8bbc43e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -6,14 +6,14 @@ settings: dependencies: '@noble/ciphers': - specifier: ^0.4.1 - version: 0.4.1 + specifier: ^0.5.2 + version: 0.5.2 '@noble/curves': - specifier: ^1.3.0 - version: 1.3.0 + specifier: ^1.4.0 + version: 1.4.0 '@noble/hashes': - specifier: ^1.3.3 - version: 1.3.3 + specifier: ^1.4.0 + version: 1.4.0 cborg: specifier: ^4.2.0 version: 4.2.0 @@ -396,18 +396,18 @@ packages: '@jridgewell/sourcemap-codec': 1.4.15 dev: true - /@noble/ciphers@0.4.1: - resolution: {integrity: sha512-QCOA9cgf3Rc33owG0AYBB9wszz+Ul2kramWN8tXG44Gyciud/tbkEqvxRF/IpqQaBpRBNi9f4jdNxqB2CQCIXg==} + /@noble/ciphers@0.5.2: + resolution: {integrity: sha512-GADtQmZCdgbnNp+daPLc3OY3ibEtGGDV/+CzeM3MFnhiQ7ELQKlsHWYq0YbYUXx4jU3/Y1erAxU6r+hwpewqmQ==} dev: false - /@noble/curves@1.3.0: - resolution: {integrity: sha512-t01iSXPuN+Eqzb4eBX0S5oubSqXbK/xXa1Ne18Hj8f9pStxztHCE2gfboSp/dZRLSqfuLpRK2nDXDK+W9puocA==} + /@noble/curves@1.4.0: + resolution: {integrity: sha512-p+4cb332SFCrReJkCYe8Xzm0OWi4Jji5jVdIZRL/PmacmDkFNw6MrrV+gGpiPxLHbV+zKFRywUWbaseT+tZRXg==} dependencies: - '@noble/hashes': 1.3.3 + '@noble/hashes': 1.4.0 dev: false - /@noble/hashes@1.3.3: - resolution: {integrity: sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA==} + /@noble/hashes@1.4.0: + resolution: {integrity: sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==} engines: {node: '>= 16'} dev: false diff --git a/src/aesgcm.test.ts b/src/aesgcm.test.ts index 1b9694e..8408fa3 100644 --- a/src/aesgcm.test.ts +++ b/src/aesgcm.test.ts @@ -8,11 +8,14 @@ import { AesGcmKey } from './aesgcm' describe('AesGcmKey Examples', () => { it('AlgorithmA128GCM', async () => { - const key = AesGcmKey.fromSecret( + let key = AesGcmKey.fromSecret( hexToBytes('849B57219DAE48DE646D07DBB533566E') ) assert.equal(key.alg, iana.AlgorithmA128GCM) + const keyBytes = key.toBytes() + key = AesGcmKey.fromBytes(keyBytes) + const ciphertext = await key.encrypt( utf8ToBytes('This is the content.'), hexToBytes('02D1F7E6F26C43D4868D87CE'), diff --git a/src/aesgcm.ts b/src/aesgcm.ts index 9b4456b..b2fc258 100644 --- a/src/aesgcm.ts +++ b/src/aesgcm.ts @@ -1,16 +1,20 @@ // (c) 2023-present, LDC Labs. All rights reserved. // See the file LICENSE for licensing terms. -import { gcm } from '@noble/ciphers/webcrypto/aes' +import { gcm } from '@noble/ciphers/webcrypto' import * as iana from './iana' import { RawMap, assertBytes } from './map' import { Key, type Encryptor } from './key' -import { randomBytes } from './utils' +import { randomBytes, decodeCBOR } from './utils' // TODO: more checks // AesGcmKey implements content encryption algorithm AES-GCM for COSE as defined in RFC9053. // https://datatracker.ietf.org/doc/html/rfc9053#name-aes-gcm. export class AesGcmKey extends Key implements Encryptor { + static fromBytes(data: Uint8Array): AesGcmKey { + return new AesGcmKey(decodeCBOR(data)) + } + static generate(alg: number, kid?: T): AesGcmKey { return AesGcmKey.fromSecret(randomBytes(getKeySize(alg)), kid) } diff --git a/src/chacha20poly1305.test.ts b/src/chacha20poly1305.test.ts index 853a5f2..39c66a6 100644 --- a/src/chacha20poly1305.test.ts +++ b/src/chacha20poly1305.test.ts @@ -10,13 +10,16 @@ describe('ChaCha20Poly1305Key Examples', () => { // https://github.com/cose-wg/Examples/tree/master/chacha-poly-examples // https://github.com/cose-wg/Examples/pull/104 it('Examples', async () => { - const key = ChaCha20Poly1305Key.fromSecret( + let key = ChaCha20Poly1305Key.fromSecret( hexToBytes( '0F1E2D3C4B5A69788796A5B4C3D2E1F01F2E3D4C5B6A798897A6B5C4D3E2F100' ) ) assert.equal(key.alg, iana.AlgorithmChaCha20Poly1305) + const keyBytes = key.toBytes() + key = ChaCha20Poly1305Key.fromBytes(keyBytes) + const ciphertext = await key.encrypt( utf8ToBytes('This is the content.'), hexToBytes('26682306D4FB28CA01B43B80'), diff --git a/src/chacha20poly1305.ts b/src/chacha20poly1305.ts index b64e1ea..6196b57 100644 --- a/src/chacha20poly1305.ts +++ b/src/chacha20poly1305.ts @@ -5,12 +5,16 @@ import { chacha20poly1305 } from '@noble/ciphers/chacha' import * as iana from './iana' import { RawMap, assertBytes } from './map' import { Key, type Encryptor } from './key' -import { randomBytes } from './utils' +import { randomBytes, decodeCBOR } from './utils' // TODO: more checks // ChaCha20Poly1305Key implements content encryption algorithm ChaCha20/Poly1305 for COSE as defined in RFC9053. // https://datatracker.ietf.org/doc/html/rfc9053#name-chacha20-and-poly1305. export class ChaCha20Poly1305Key extends Key implements Encryptor { + static fromBytes(data: Uint8Array): ChaCha20Poly1305Key { + return new ChaCha20Poly1305Key(decodeCBOR(data)) + } + static generate(kid?: T): ChaCha20Poly1305Key { return ChaCha20Poly1305Key.fromSecret(randomBytes(32), kid) } diff --git a/src/ecdsa.test.ts b/src/ecdsa.test.ts index 8aab7bc..6958496 100644 --- a/src/ecdsa.test.ts +++ b/src/ecdsa.test.ts @@ -13,11 +13,14 @@ describe('ECDSAKey Examples', () => { iana.AlgorithmES384, iana.AlgorithmES512, ]) { - const key = ECDSAKey.generate(alg) + let key = ECDSAKey.generate(alg) assert.equal(key.kty, iana.KeyTypeEC2) assert.equal(key.alg, alg) assert.equal(key.getInt(iana.EC2KeyParameterCrv), getCrv(alg)) + const keyBytes = key.toBytes() + key = ECDSAKey.fromBytes(keyBytes) + const sig = key.sign(utf8ToBytes('This is the content.')) assert.equal(key.verify(utf8ToBytes('This is the content.'), sig), true) assert.equal(key.verify(utf8ToBytes('This is the content'), sig), false) diff --git a/src/ecdsa.ts b/src/ecdsa.ts index 6635ba7..4af776f 100644 --- a/src/ecdsa.ts +++ b/src/ecdsa.ts @@ -8,11 +8,16 @@ import { CurveFn } from '@noble/curves/abstract/weierstrass' import * as iana from './iana' import { RawMap, assertBytes } from './map' import { Key, type Signer, Verifier } from './key' +import { decodeCBOR } from './utils' // TODO: more checks // ECDSAKey implements signature algorithm ECDSA for COSE as defined in RFC9053. // https://datatracker.ietf.org/doc/html/rfc9053#name-ecdsa. export class ECDSAKey extends Key implements Signer, Verifier { + static fromBytes(data: Uint8Array): ECDSAKey { + return new ECDSAKey(decodeCBOR(data)) + } + static generate(alg: number, kid?: T): ECDSAKey { const curve = getCurve(alg) return ECDSAKey.fromSecret(curve.utils.randomPrivateKey(), kid) diff --git a/src/ed25519.test.ts b/src/ed25519.test.ts index b17b8a2..2c5308f 100644 --- a/src/ed25519.test.ts +++ b/src/ed25519.test.ts @@ -8,11 +8,14 @@ import { Ed25519Key } from './ed25519' describe('Ed25519Key Examples', () => { it('Signer and Verifier', () => { - const key = Ed25519Key.generate() + let key = Ed25519Key.generate() assert.equal(key.kty, iana.KeyTypeOKP) assert.equal(key.alg, iana.AlgorithmEdDSA) assert.equal(key.getInt(iana.OKPKeyParameterCrv), iana.EllipticCurveEd25519) + const keyBytes = key.toBytes() + key = Ed25519Key.fromBytes(keyBytes) + const sig = key.sign(utf8ToBytes('This is the content.')) assert.equal(key.verify(utf8ToBytes('This is the content.'), sig), true) assert.equal(key.verify(utf8ToBytes('This is the content'), sig), false) diff --git a/src/ed25519.ts b/src/ed25519.ts index a5550b4..cebe3fc 100644 --- a/src/ed25519.ts +++ b/src/ed25519.ts @@ -5,12 +5,16 @@ import { ed25519 } from '@noble/curves/ed25519' import * as iana from './iana' import { RawMap, assertBytes } from './map' import { Key, type Signer, Verifier } from './key' -import { randomBytes } from './utils' +import { randomBytes, decodeCBOR } from './utils' // TODO: more checks // Ed25519Key implements signature algorithm Ed25519 for COSE as defined in RFC9053. // https://datatracker.ietf.org/doc/html/rfc9053#name-edwards-curve-digital-signa. export class Ed25519Key extends Key implements Signer, Verifier { + static fromBytes(data: Uint8Array): Ed25519Key { + return new Ed25519Key(decodeCBOR(data)) + } + static generate(kid?: T): Ed25519Key { return Ed25519Key.fromSecret(randomBytes(32), kid) } diff --git a/src/hmac.test.ts b/src/hmac.test.ts index a6c56d8..0d3050a 100644 --- a/src/hmac.test.ts +++ b/src/hmac.test.ts @@ -37,7 +37,9 @@ describe('HMACKey Examples', () => { ] for (const [alg, secret, message, tag] of cases) { - const key = HMACKey.fromSecret(hexToBytes(secret), alg) + let key = HMACKey.fromSecret(hexToBytes(secret), alg) + const keyBytes = key.toBytes() + key = HMACKey.fromBytes(keyBytes) assert.equal(bytesToHex(key.mac(hexToBytes(message))).toUpperCase(), tag) } }) diff --git a/src/hmac.ts b/src/hmac.ts index ac95cbc..8e078e3 100644 --- a/src/hmac.ts +++ b/src/hmac.ts @@ -4,13 +4,18 @@ import * as iana from './iana' import { RawMap, assertBytes } from './map' import { Key, type MACer } from './key' -import { randomBytes } from './utils' +import { randomBytes, decodeCBOR } from './utils' import { hmac, getHash } from './hash' + // TODO: more checks // HMACKey implements message authentication code algorithm HMAC for COSE as defined in RFC9053. // https://datatracker.ietf.org/doc/html/rfc9053#name-hash-based-message-authenti. export class HMACKey extends Key implements MACer { + static fromBytes(data: Uint8Array): HMACKey { + return new HMACKey(decodeCBOR(data)) + } + static generate(alg: number, kid?: T): HMACKey { return HMACKey.fromSecret(randomBytes(getKeySize(alg)), alg, kid) }