From 8b7ad2916d906d9b2ba3a4848d37818cb7c8c1a0 Mon Sep 17 00:00:00 2001 From: Greg Nazario Date: Mon, 25 Nov 2024 15:10:46 -0500 Subject: [PATCH] [secp256k1] Support inputed compressed public keys --- src/core/crypto/secp256k1.ts | 16 +++++++++++++--- tests/unit/secp256k1.test.ts | 21 +++++++++++++++++++++ 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/src/core/crypto/secp256k1.ts b/src/core/crypto/secp256k1.ts index abab873d9..92cea11c4 100644 --- a/src/core/crypto/secp256k1.ts +++ b/src/core/crypto/secp256k1.ts @@ -23,6 +23,9 @@ export class Secp256k1PublicKey extends PublicKey { // Secp256k1 ecdsa public keys contain a prefix indicating compression and two 32-byte coordinates. static readonly LENGTH: number = 65; + // If it's compressed, it is only 33 bytes + static readonly COMPRESSED_LENGTH: number = 33; + // Hex value of the public key private readonly key: Hex; @@ -37,10 +40,17 @@ export class Secp256k1PublicKey extends PublicKey { super(); const hex = Hex.fromHexInput(hexInput); - if (hex.toUint8Array().length !== Secp256k1PublicKey.LENGTH) { - throw new Error(`PublicKey length should be ${Secp256k1PublicKey.LENGTH}`); + const { length } = hex.toUint8Array(); + if (length === Secp256k1PublicKey.LENGTH) { + this.key = hex; + } else if (length === Secp256k1PublicKey.COMPRESSED_LENGTH) { + const point = secp256k1.ProjectivePoint.fromHex(hex.toUint8Array()); + this.key = Hex.fromHexInput(point.toRawBytes(false)); + } else { + throw new Error( + `PublicKey length should be ${Secp256k1PublicKey.LENGTH} or ${Secp256k1PublicKey.COMPRESSED_LENGTH}, received ${length}`, + ); } - this.key = hex; } // region PublicKey diff --git a/tests/unit/secp256k1.test.ts b/tests/unit/secp256k1.test.ts index e95ef544d..32546385e 100644 --- a/tests/unit/secp256k1.test.ts +++ b/tests/unit/secp256k1.test.ts @@ -29,6 +29,27 @@ describe("Secp256k1PublicKey", () => { expect(publicKey2.toUint8Array()).toEqual(hexUint8Array); }); + it("should work with compressed public keys", () => { + const expectedPublicKey = new Secp256k1PublicKey(secp256k1TestObject.publicKey); + const uncompressedPublicKey = Hex.fromHexInput(secp256k1TestObject.publicKey); + expect(uncompressedPublicKey.toUint8Array().length).toEqual(65); + + const point = secp256k1.ProjectivePoint.fromHex(uncompressedPublicKey.toUint8Array()); + const compressedPublicKey = point.toRawBytes(true); + const compressedPublicKeyHex = Hex.fromHexInput(compressedPublicKey); + expect(compressedPublicKey.length).toEqual(33); + + const publicKey1 = new Secp256k1PublicKey(compressedPublicKeyHex.toString()); + expect(publicKey1).toBeInstanceOf(Secp256k1PublicKey); + expect(publicKey1).toEqual(expectedPublicKey); + + const publicKey2 = new Secp256k1PublicKey(compressedPublicKeyHex.toUint8Array()); + expect(publicKey2).toBeInstanceOf(Secp256k1PublicKey); + expect(publicKey2).toEqual(expectedPublicKey); + + expect(publicKey1).toEqual(publicKey2); + }); + it("should throw an error with invalid hex input length", () => { const invalidHexInput = "0123456789abcdef"; // Invalid length expect(() => new Secp256k1PublicKey(invalidHexInput)).toThrowError(