From c202bfd158b0e7e7a90412320ec4f0f115a8102f Mon Sep 17 00:00:00 2001 From: Georgi Tsonev Date: Wed, 6 Mar 2024 14:23:51 +0200 Subject: [PATCH 1/6] fix: fix recoverPublicKey, createKey and getKeys functions --- packages/biometric-ed25519/package.json | 5 ++- packages/biometric-ed25519/src/index.ts | 34 ++++++++----------- packages/biometric-ed25519/src/utils.ts | 45 +++++++++++++++---------- pnpm-lock.yaml | 38 +++------------------ 4 files changed, 48 insertions(+), 74 deletions(-) diff --git a/packages/biometric-ed25519/package.json b/packages/biometric-ed25519/package.json index 3e0dad25a7..e4b9fe6ec8 100644 --- a/packages/biometric-ed25519/package.json +++ b/packages/biometric-ed25519/package.json @@ -13,19 +13,18 @@ "author": "Pagoda", "license": "ISC", "dependencies": { - "@aws-crypto/sha256-js": "4.0.0", "@hexagon/base64": "1.1.26", "@near-js/crypto": "workspace:*", "@near-js/utils": "workspace:*", + "@noble/curves": "1.2.0", + "@noble/hashes": "1.3.3", "asn1-parser": "1.1.8", "bn.js": "5.2.1", "borsh": "1.0.0", "buffer": "6.0.3", - "@noble/curves": "1.2.0", "fido2-lib": "3.4.1" }, "devDependencies": { - "@aws-sdk/types": "3.329.0", "@types/node": "18.11.18" } } diff --git a/packages/biometric-ed25519/src/index.ts b/packages/biometric-ed25519/src/index.ts index 57b31af01e..6378cfc6ef 100644 --- a/packages/biometric-ed25519/src/index.ts +++ b/packages/biometric-ed25519/src/index.ts @@ -1,6 +1,6 @@ import base64 from '@hexagon/base64'; import { ed25519 } from '@noble/curves/ed25519'; -import { Sha256 } from '@aws-crypto/sha256-js'; +import { sha256} from '@noble/hashes/sha256'; import { Buffer } from 'buffer'; import asn1 from 'asn1-parser'; import { KeyPair } from '@near-js/crypto'; @@ -11,7 +11,8 @@ import { get64BytePublicKeyFromPEM, preformatGetAssertReq, publicKeyCredentialToJSON, - recoverPublicKey + recoverPublicKey, + uint8ArrayToBigInt } from './utils'; import { Fido2 } from './fido2'; import { AssertionResponse } from './index.d'; @@ -69,9 +70,7 @@ export const createKey = async (username: string): Promise => { }); const publicKey = result.authnrData.get('credentialPublicKeyPem'); const publicKeyBytes = get64BytePublicKeyFromPEM(publicKey); - const edSha256 = new Sha256(); - edSha256.update(Buffer.from(publicKeyBytes)); - const secretKey = await edSha256.digest(); + const secretKey = sha256.create().update(Buffer.from(publicKeyBytes)).digest(); const pubKey = ed25519.getPublicKey(secretKey); return KeyPair.fromString(baseEncode(new Uint8Array(Buffer.concat([secretKey, Buffer.from(pubKey)])))); }); @@ -101,27 +100,22 @@ export const getKeys = async (username: string): Promise<[KeyPair, KeyPair]> => //@ts-ignore const parser = asn1?.ASN1?.parse || window?.ASN1?.parse; const rAndS = parser(new Uint8Array(signature)); - const clientDataSha256 = new Sha256(); - clientDataSha256.update( + const clientDataJSONHash = sha256.create().update( Buffer.from(new Uint8Array(base64.toArrayBuffer(getAssertionResponse.response.clientDataJSON, true))) - ); - const clientDataJSONHash = await clientDataSha256.digest(); + ).digest(); const authenticatorDataJSONHash = Buffer.from(new Uint8Array(base64.toArrayBuffer(getAssertionResponse.response.authenticatorData, true))); - const authenticatorAndClientDataJSONHash = Buffer.concat([authenticatorDataJSONHash, clientDataJSONHash]); + const authenticatorAndClientDataJSONHash = Buffer.concat([Buffer.from(authenticatorDataJSONHash), Buffer.from(clientDataJSONHash)]); - const correctPKs = await recoverPublicKey(rAndS.children[0].value, rAndS.children[1].value, authenticatorAndClientDataJSONHash, 0); - - const firstEdSha256 = new Sha256(); - firstEdSha256.update(Buffer.from(correctPKs[0])); - const secondEdSha256 = new Sha256(); - secondEdSha256.update(Buffer.from(correctPKs[1])); + const r = rAndS.children[0].value; + const s = rAndS.children[1].value; + const correctPKs = await recoverPublicKey(uint8ArrayToBigInt(r), uint8ArrayToBigInt(s), authenticatorAndClientDataJSONHash, 0); - const firstEDSecret = await firstEdSha256.digest(); + const firstEDSecret = sha256.create().update(Buffer.from(correctPKs[0])).digest(); const firstEDPublic = ed25519.getPublicKey(firstEDSecret); - const secondEDSecret = await secondEdSha256.digest(); + const secondEDSecret = sha256.create().update(Buffer.from(correctPKs[1])).digest(); const secondEDPublic = ed25519.getPublicKey(secondEDSecret); - const firstKeyPair = KeyPair.fromString(baseEncode(new Uint8Array(Buffer.concat([firstEDSecret, Buffer.from(firstEDPublic)])))); - const secondKeyPair = KeyPair.fromString(baseEncode(new Uint8Array(Buffer.concat([secondEDSecret, Buffer.from(secondEDPublic)])))); + const firstKeyPair = KeyPair.fromString(baseEncode(new Uint8Array(Buffer.concat([Buffer.from(firstEDSecret), Buffer.from(firstEDPublic)])))); + const secondKeyPair = KeyPair.fromString(baseEncode(new Uint8Array(Buffer.concat([Buffer.from(secondEDSecret), Buffer.from(secondEDPublic)])))); return [firstKeyPair, secondKeyPair]; }); }; diff --git a/packages/biometric-ed25519/src/utils.ts b/packages/biometric-ed25519/src/utils.ts index eed9457067..9a0f1eff64 100644 --- a/packages/biometric-ed25519/src/utils.ts +++ b/packages/biometric-ed25519/src/utils.ts @@ -1,6 +1,6 @@ import base64 from '@hexagon/base64'; -import { secp256k1 } from '@noble/curves/secp256k1'; -import { Sha256 } from '@aws-crypto/sha256-js'; +import { p256 } from '@noble/curves/p256'; +import { sha256} from '@noble/hashes/sha256'; import { PublicKey } from '@near-js/crypto'; export const preformatMakeCredReq = (makeCredReq) => { @@ -25,7 +25,7 @@ export const preformatMakeCredReq = (makeCredReq) => { export const get64BytePublicKeyFromPEM = (publicKey: PublicKey) => { const prefix = '\n'; const publicKeyBase64 = publicKey.toString().split(prefix); - return base64.toArrayBuffer(`${publicKeyBase64[1]}${publicKeyBase64[2]}`).slice(27); + return base64.toArrayBuffer(`${publicKeyBase64[1]}${publicKeyBase64[2]}`).slice(27, 59); }; export const validateUsername = (name: string): string => { @@ -77,22 +77,33 @@ export const recoverPublicKey = async (r, s, message, recovery) => { if (recovery !== 0 && recovery !== 1) { throw new Error('Invalid recovery parameter'); } - - const hash = new Sha256(); - hash.update(message); - const sigObjQ = new secp256k1.Signature(r, s); - sigObjQ.addRecoveryBit(0); - const sigObjP = new secp256k1.Signature(r, s); - sigObjP.addRecoveryBit(1); + const sigObjQ = new p256.Signature(r, s).addRecoveryBit(0); + const sigObjP = new p256.Signature(r, s).addRecoveryBit(1); + const hash = sha256.create().update(message).digest(); - const h = await hash.digest(); + const res = [] - const Q = sigObjQ.recoverPublicKey(h); - const P = sigObjP.recoverPublicKey(h); + let Q, P; - return [ - Buffer.from(Q.toRawBytes()).subarray(1, 65), - Buffer.from(P.toRawBytes()).subarray(1, 65) - ]; + try { + Q = sigObjQ.recoverPublicKey(hash); + res.push(Q.toRawBytes()); + } catch (e) { + throw e; + } + + try { + P = sigObjP.recoverPublicKey(hash); + res.push(P.toRawBytes()); + } catch (e) { + throw e; + } + + return res }; + +export const uint8ArrayToBigInt = (uint8Array: Uint8Array) => { + const array = Array.from(uint8Array); + return BigInt("0x" + array.map(byte => byte.toString(16).padStart(2, '0')).join("")); +} \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e319d6e3f1..0b4e85e6b9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -117,9 +117,6 @@ importers: packages/biometric-ed25519: dependencies: - '@aws-crypto/sha256-js': - specifier: 4.0.0 - version: 4.0.0 '@hexagon/base64': specifier: 1.1.26 version: 1.1.26 @@ -132,6 +129,9 @@ importers: '@noble/curves': specifier: 1.2.0 version: 1.2.0 + '@noble/hashes': + specifier: 1.3.3 + version: 1.3.3 asn1-parser: specifier: 1.1.8 version: 1.1.8 @@ -148,9 +148,6 @@ importers: specifier: 3.4.1 version: 3.4.1 devDependencies: - '@aws-sdk/types': - specifier: 3.329.0 - version: 3.329.0 '@types/node': specifier: 18.11.18 version: 18.11.18 @@ -627,34 +624,6 @@ packages: '@jridgewell/trace-mapping': 0.3.18 dev: true - /@aws-crypto/sha256-js@4.0.0: - resolution: {integrity: sha512-MHGJyjE7TX9aaqXj7zk2ppnFUOhaDs5sP+HtNS0evOxn72c+5njUmyJmpGd7TfyoDznZlHMmdo/xGUdu2NIjNQ==} - dependencies: - '@aws-crypto/util': 4.0.0 - '@aws-sdk/types': 3.329.0 - tslib: 1.14.1 - dev: false - - /@aws-crypto/util@4.0.0: - resolution: {integrity: sha512-2EnmPy2gsFZ6m8bwUQN4jq+IyXV3quHAcwPOS6ZA3k+geujiqI8aRokO2kFJe+idJ/P3v4qWI186rVMo0+zLDQ==} - dependencies: - '@aws-sdk/types': 3.329.0 - '@aws-sdk/util-utf8-browser': 3.259.0 - tslib: 1.14.1 - dev: false - - /@aws-sdk/types@3.329.0: - resolution: {integrity: sha512-wFBW4yciDfzQBSFmWNaEvHShnSGLMxSu9Lls6EUf6xDMavxSB36bsrVRX6CyAo/W0NeIIyEOW1LclGPgJV1okg==} - engines: {node: '>=14.0.0'} - dependencies: - tslib: 2.6.1 - - /@aws-sdk/util-utf8-browser@3.259.0: - resolution: {integrity: sha512-UvFa/vR+e19XookZF8RzFZBrw2EUkQWxiBW0yYQAhvk3C+QVGl0H3ouca8LDBlBfQKXwmW3huo/59H8rwb1wJw==} - dependencies: - tslib: 2.6.1 - dev: false - /@babel/code-frame@7.22.5: resolution: {integrity: sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ==} engines: {node: '>=6.9.0'} @@ -8446,6 +8415,7 @@ packages: /tslib@1.14.1: resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} + dev: true /tslib@2.6.1: resolution: {integrity: sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig==} From 66280b4fe28642833ca5c30a14acae1bd4d36e45 Mon Sep 17 00:00:00 2001 From: Georgi Tsonev Date: Wed, 6 Mar 2024 16:03:37 +0200 Subject: [PATCH 2/6] fix: remove try catch block --- packages/biometric-ed25519/src/index.ts | 2 +- packages/biometric-ed25519/src/utils.ts | 28 ++++++------------------- 2 files changed, 7 insertions(+), 23 deletions(-) diff --git a/packages/biometric-ed25519/src/index.ts b/packages/biometric-ed25519/src/index.ts index 6378cfc6ef..407eb9939f 100644 --- a/packages/biometric-ed25519/src/index.ts +++ b/packages/biometric-ed25519/src/index.ts @@ -1,6 +1,6 @@ import base64 from '@hexagon/base64'; import { ed25519 } from '@noble/curves/ed25519'; -import { sha256} from '@noble/hashes/sha256'; +import { sha256 } from '@noble/hashes/sha256'; import { Buffer } from 'buffer'; import asn1 from 'asn1-parser'; import { KeyPair } from '@near-js/crypto'; diff --git a/packages/biometric-ed25519/src/utils.ts b/packages/biometric-ed25519/src/utils.ts index 9a0f1eff64..c9b5647a3e 100644 --- a/packages/biometric-ed25519/src/utils.ts +++ b/packages/biometric-ed25519/src/utils.ts @@ -1,6 +1,6 @@ import base64 from '@hexagon/base64'; import { p256 } from '@noble/curves/p256'; -import { sha256} from '@noble/hashes/sha256'; +import { sha256 } from '@noble/hashes/sha256'; import { PublicKey } from '@near-js/crypto'; export const preformatMakeCredReq = (makeCredReq) => { @@ -82,28 +82,12 @@ export const recoverPublicKey = async (r, s, message, recovery) => { const sigObjP = new p256.Signature(r, s).addRecoveryBit(1); const hash = sha256.create().update(message).digest(); - const res = [] - - let Q, P; - - try { - Q = sigObjQ.recoverPublicKey(hash); - res.push(Q.toRawBytes()); - } catch (e) { - throw e; - } - - try { - P = sigObjP.recoverPublicKey(hash); - res.push(P.toRawBytes()); - } catch (e) { - throw e; - } - - return res + const Q = sigObjQ.recoverPublicKey(hash); + const P = sigObjP.recoverPublicKey(hash); + return [Q.toRawBytes().subarray(1, 33), P.toRawBytes().subarray(1, 33)]; }; export const uint8ArrayToBigInt = (uint8Array: Uint8Array) => { const array = Array.from(uint8Array); - return BigInt("0x" + array.map(byte => byte.toString(16).padStart(2, '0')).join("")); -} \ No newline at end of file + return BigInt('0x' + array.map(byte => byte.toString(16).padStart(2, '0')).join('')); +}; \ No newline at end of file From f049ea88e283482dd00baf6e640f299239e1dcc6 Mon Sep 17 00:00:00 2001 From: Georgi Tsonev Date: Wed, 6 Mar 2024 16:09:11 +0200 Subject: [PATCH 3/6] fix: create buffer from secretKey --- packages/biometric-ed25519/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/biometric-ed25519/src/index.ts b/packages/biometric-ed25519/src/index.ts index 407eb9939f..f0016e4511 100644 --- a/packages/biometric-ed25519/src/index.ts +++ b/packages/biometric-ed25519/src/index.ts @@ -72,7 +72,7 @@ export const createKey = async (username: string): Promise => { const publicKeyBytes = get64BytePublicKeyFromPEM(publicKey); const secretKey = sha256.create().update(Buffer.from(publicKeyBytes)).digest(); const pubKey = ed25519.getPublicKey(secretKey); - return KeyPair.fromString(baseEncode(new Uint8Array(Buffer.concat([secretKey, Buffer.from(pubKey)])))); + return KeyPair.fromString(baseEncode(new Uint8Array(Buffer.concat([Buffer.from(secretKey), Buffer.from(pubKey)])))); }); }; From 4b5aae441fc3c10a5a3bf4d559ed2867c5e9abf1 Mon Sep 17 00:00:00 2001 From: Georgi Tsonev Date: Wed, 6 Mar 2024 16:25:01 +0200 Subject: [PATCH 4/6] chore: add a changeset file --- .changeset/cuddly-plants-wave.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/cuddly-plants-wave.md diff --git a/.changeset/cuddly-plants-wave.md b/.changeset/cuddly-plants-wave.md new file mode 100644 index 0000000000..b482efec4d --- /dev/null +++ b/.changeset/cuddly-plants-wave.md @@ -0,0 +1,5 @@ +--- +"@near-js/biometric-ed25519": patch +--- + +Fix recoverPublicKey(), createKey() and getKeys() methods. From e46653d47081405ff57a01373965a9e3898341eb Mon Sep 17 00:00:00 2001 From: vikinatora Date: Thu, 7 Mar 2024 08:50:08 +0200 Subject: [PATCH 5/6] fix: change rpc.ci api with lava node --- packages/providers/test/providers.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/providers/test/providers.test.js b/packages/providers/test/providers.test.js index 509860b9e3..e9f9374437 100644 --- a/packages/providers/test/providers.test.js +++ b/packages/providers/test/providers.test.js @@ -171,7 +171,7 @@ describe('providers', () => { // TODO: Use a near-workspaces Worker when time traveling is available test('json rpc get next light client block', async () => { - const provider = new JsonRpcProvider({ url: 'https://rpc.ci-testnet.near.org' }); + const provider = new JsonRpcProvider({ url: 'https://near.lava.build/' }); const stat = await provider.status(); // Get block in at least the last epoch (epoch duration 43,200 blocks on mainnet and testnet) From e7e6d83f89a7f4909e17d9e54494c02843472107 Mon Sep 17 00:00:00 2001 From: Georgi Tsonev Date: Thu, 7 Mar 2024 11:23:36 +0200 Subject: [PATCH 6/6] fix: use testnet rpc --- packages/providers/test/providers.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/providers/test/providers.test.js b/packages/providers/test/providers.test.js index e9f9374437..f66a34c5a1 100644 --- a/packages/providers/test/providers.test.js +++ b/packages/providers/test/providers.test.js @@ -171,7 +171,7 @@ describe('providers', () => { // TODO: Use a near-workspaces Worker when time traveling is available test('json rpc get next light client block', async () => { - const provider = new JsonRpcProvider({ url: 'https://near.lava.build/' }); + const provider = new JsonRpcProvider({ url: 'https://rpc.testnet.near.org' }); const stat = await provider.status(); // Get block in at least the last epoch (epoch duration 43,200 blocks on mainnet and testnet)