From bb907b769d065c77163a375eabfade3f93e0d7e7 Mon Sep 17 00:00:00 2001 From: sparkplug0025 Date: Tue, 4 Jan 2022 09:47:27 -0500 Subject: [PATCH 1/8] WIP add session signature with SubtleCrypto --- packages/webapp/src/_app/eos/sessions.ts | 83 ++++++++++++++++++++++-- 1 file changed, 76 insertions(+), 7 deletions(-) diff --git a/packages/webapp/src/_app/eos/sessions.ts b/packages/webapp/src/_app/eos/sessions.ts index fad39d4b7..e4e280c48 100644 --- a/packages/webapp/src/_app/eos/sessions.ts +++ b/packages/webapp/src/_app/eos/sessions.ts @@ -1,6 +1,11 @@ import dayjs from "dayjs"; import { generateKeyPair, sha256 } from "eosjs/dist/eosjs-key-conversions"; -import { KeyType } from "eosjs/dist/eosjs-numeric"; +import { + Key, + KeyType, + publicKeyToString, + signatureToString, +} from "eosjs/dist/eosjs-numeric"; import { PrivateKey } from "eosjs/dist/eosjs-jssig"; import { hexToUint8Array, SerialBuffer } from "eosjs/dist/eosjs-serialize"; import { get as idbGet, set as idbSet } from "idb-keyval"; @@ -17,6 +22,7 @@ interface SessionKeyData { privateKey: string; expiration: Date; lastSequence: number; + subtleKey?: CryptoKeyPair; } class SessionKeysStorage { @@ -55,11 +61,21 @@ export const generateSessionKey = async ( secureEnv: true, }); + const subtleKey = await crypto.subtle.generateKey( + { + name: "ECDSA", + namedCurve: "P-256", + }, + false, // not extractable + ["sign", "verify"] + ); + const expiration = dayjs().add(expirationSeconds, "seconds").toDate(); return { publicKey: publicKey.toString(), privateKey: privateKey.toString(), + subtleKey, lastSequence: 0, expiration, }; @@ -70,7 +86,15 @@ export const newSessionTransaction = async ( sessionKeyData: SessionKeyData, description = DEFAULT_SESSION_DESCRIPTION ) => { - const key = sessionKeyData.publicKey; + let key = sessionKeyData.publicKey; + + if (sessionKeyData.subtleKey?.publicKey) { + const subtlePubKey = sessionKeyData.subtleKey.publicKey; + const eosKey = await subtleToEosPublicKey(subtlePubKey); + console.info("subtle eos pubkey >>>", eosKey); + key = publicKeyToString(eosKey); + } + const expiration = sessionKeyData.expiration.toISOString().slice(0, -4) + "000"; @@ -129,14 +153,14 @@ export const signSessionTransaction = async ( const sequence = sessionKey.lastSequence + 1; const verbs = convertActionsToVerbs(actions); - const signatureAuthSha = await makeSignatureAuthSha( + const signatureAuthData = await makeSignatureAuthSha( edenContractAccount, authorizerAccount, sequence, verbs ); - const signature = await signSha(sessionKey, signatureAuthSha); + const signature = await signData(sessionKey, signatureAuthData); const data: SessionSignRequest = { signature: signature.toString(), @@ -164,7 +188,8 @@ const makeSignatureAuthSha = async ( sequence, verbs ); - return sha256(Buffer.from(signatureAuthBytes)); + return signatureAuthBytes; + // return sha256(Buffer.from(signatureAuthBytes)); }; const convertActionsToVerbs = (actions: any[]) => @@ -194,7 +219,51 @@ const serializeSignatureAuth = async ( return buffer.asUint8Array(); }; -const signSha = (sessionKey: SessionKeyData, sha: number[]) => { +const signData = async ( + sessionKey: SessionKeyData, + data: Uint8Array +): Promise => { const privateKey = PrivateKey.fromString(sessionKey.privateKey); - return privateKey.sign(sha, false); + const signature = privateKey.sign(data, true); + console.info("eos signature >>>", signature, signature.toString()); + + if (sessionKey.subtleKey?.privateKey) { + console.info("i will sign with subtle >>>", sessionKey.subtleKey); + + const privateKey = sessionKey.subtleKey.privateKey; + const signature = await crypto.subtle.sign( + { name: "ECDSA", hash: "SHA-256" }, + privateKey, + data + ); + const signatureBytes = new Uint8Array(signature); + // const r = new Uint8Array(signature.slice(0, 32)); + // const s = new Uint8Array(signature.slice(32, 64)); + const sigDataWithRecovery = { + type: KeyType.r1, + data: new Uint8Array([32].concat(Array.from(signatureBytes))), + }; + console.info( + "signature from subtle", + signatureBytes, + signature, + sigDataWithRecovery, + signatureToString(sigDataWithRecovery) + ); + return signatureToString(sigDataWithRecovery); + } + return signature.toString(); +}; + +const subtleToEosPublicKey = async ( + subtlePublicKey: CryptoKey +): Promise => { + const rawKey = await crypto.subtle.exportKey("raw", subtlePublicKey); + const x = new Uint8Array(rawKey.slice(1, 33)); + const y = new Uint8Array(rawKey.slice(33, 65)); + console.info("raw data pubkey >>>", rawKey, x, y); + const data = new Uint8Array([y[31] & 1 ? 3 : 2].concat(Array.from(x))); + const key = { type: KeyType.r1, data }; + console.info("subtle to eoskey >>>", key); + return key; }; From 8158489749a81381753fead6b44fd7ed4a4afb39 Mon Sep 17 00:00:00 2001 From: sparkplug0025 Date: Tue, 4 Jan 2022 11:07:17 -0500 Subject: [PATCH 2/8] wip getRecoveryParam --- packages/webapp/src/_app/eos/sessions.ts | 55 +++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/packages/webapp/src/_app/eos/sessions.ts b/packages/webapp/src/_app/eos/sessions.ts index e4e280c48..c58c9394f 100644 --- a/packages/webapp/src/_app/eos/sessions.ts +++ b/packages/webapp/src/_app/eos/sessions.ts @@ -6,14 +6,18 @@ import { publicKeyToString, signatureToString, } from "eosjs/dist/eosjs-numeric"; +import * as ser from "eosjs/dist/eosjs-serialize"; import { PrivateKey } from "eosjs/dist/eosjs-jssig"; import { hexToUint8Array, SerialBuffer } from "eosjs/dist/eosjs-serialize"; import { get as idbGet, set as idbSet } from "idb-keyval"; import { SessionSignRequest } from "@edenos/common"; +import { ec as elliptic } from "elliptic"; import { edenContractAccount, box } from "config"; import { eosDefaultApi, eosJsonRpc } from "_app"; +const EC = new elliptic("p256") as any; + const DEFAULT_EXPIRATION_SECONDS = 30 * 24 * 60 * 60; // 30 days const DEFAULT_SESSION_DESCRIPTION = "eden login"; @@ -237,14 +241,63 @@ const signData = async ( data ); const signatureBytes = new Uint8Array(signature); + + const eosPubKey = await subtleToEosPublicKey( + sessionKey.subtleKey.publicKey! + ); + const pubKey = EC.keyFromPublic( + eosPubKey.data.subarray(0, 33) + ).getPublic(); + const hash = new Uint8Array( + await crypto.subtle.digest("SHA-256", data.slice()) + ); + + console.info(eosPubKey, pubKey, hash, signatureBytes); + + const fixup = (x: Uint8Array): Uint8Array => { + const a = Array.from(x); + while (a.length < 32) { + a.unshift(0); + } + while (a.length > 32) { + if (a.shift() !== 0) { + throw new Error("Signature has an r or s that is too big"); + } + } + return new Uint8Array(a); + }; + + const der = new ser.SerialBuffer({ array: signatureBytes }); + if (der.get() !== 0x30) { + throw new Error("Signature missing DER prefix"); + } + if (der.get() !== der.array.length - 2) { + throw new Error("Signature has bad length"); + } + if (der.get() !== 0x02) { + throw new Error("Signature has bad r marker"); + } + const r = fixup(der.getUint8Array(der.get())); + if (der.get() !== 0x02) { + throw new Error("Signature has bad s marker"); + } + const s = fixup(der.getUint8Array(der.get())); + + const recid = EC.getKeyRecoveryParam(hash, signatureBytes, pubKey); + const recidByte = 27 + 4 + recid; + // const r = new Uint8Array(signature.slice(0, 32)); // const s = new Uint8Array(signature.slice(32, 64)); const sigDataWithRecovery = { type: KeyType.r1, - data: new Uint8Array([32].concat(Array.from(signatureBytes))), + data: new Uint8Array( + [recidByte].concat(Array.from(signatureBytes)) + ), }; console.info( "signature from subtle", + recid, + recidByte, signatureBytes, signature, sigDataWithRecovery, From 1ef951ce6378064a937cd10d93d7c5134b31955c Mon Sep 17 00:00:00 2001 From: sparkplug0025 Date: Wed, 5 Jan 2022 09:39:32 -0500 Subject: [PATCH 3/8] sign wip --- packages/webapp/package.json | 2 + packages/webapp/src/_app/eos/sessions.ts | 146 +++++++++++++++-------- yarn.lock | 7 ++ 3 files changed, 108 insertions(+), 47 deletions(-) diff --git a/packages/webapp/package.json b/packages/webapp/package.json index 16b94fa19..56be3d5ab 100644 --- a/packages/webapp/package.json +++ b/packages/webapp/package.json @@ -22,6 +22,7 @@ "async-sema": "^3.1.1", "cids": "^1.1.6", "dayjs": "^1.10.4", + "elliptic": "^6.5.4", "eosjs": "^21.0.3", "graphiql": "^1.4.2", "graphql": "^15.5.1", @@ -51,6 +52,7 @@ "zod": "^3.0.0-beta.1" }, "devDependencies": { + "@types/elliptic": "^6.4.14", "@types/node": "~14.0.23", "@types/nprogress": "^0.2.0", "@types/react": "~17.0.0", diff --git a/packages/webapp/src/_app/eos/sessions.ts b/packages/webapp/src/_app/eos/sessions.ts index c58c9394f..010302157 100644 --- a/packages/webapp/src/_app/eos/sessions.ts +++ b/packages/webapp/src/_app/eos/sessions.ts @@ -1,13 +1,13 @@ import dayjs from "dayjs"; -import { generateKeyPair, sha256 } from "eosjs/dist/eosjs-key-conversions"; +import { generateKeyPair } from "eosjs/dist/eosjs-key-conversions"; import { Key, KeyType, publicKeyToString, signatureToString, } from "eosjs/dist/eosjs-numeric"; -import * as ser from "eosjs/dist/eosjs-serialize"; import { PrivateKey } from "eosjs/dist/eosjs-jssig"; +import { Signature } from "eosjs/dist/Signature"; import { hexToUint8Array, SerialBuffer } from "eosjs/dist/eosjs-serialize"; import { get as idbGet, set as idbSet } from "idb-keyval"; import { SessionSignRequest } from "@edenos/common"; @@ -245,65 +245,65 @@ const signData = async ( const eosPubKey = await subtleToEosPublicKey( sessionKey.subtleKey.publicKey! ); - const pubKey = EC.keyFromPublic( - eosPubKey.data.subarray(0, 33) - ).getPublic(); - const hash = new Uint8Array( - await crypto.subtle.digest("SHA-256", data.slice()) - ); - console.info(eosPubKey, pubKey, hash, signatureBytes); - - const fixup = (x: Uint8Array): Uint8Array => { - const a = Array.from(x); - while (a.length < 32) { - a.unshift(0); - } - while (a.length > 32) { - if (a.shift() !== 0) { - throw new Error("Signature has an r or s that is too big"); - } - } - return new Uint8Array(a); - }; + // const pubKey = EC.keyFromPublic( + // eosPubKey.data.subarray(0, 33) + // ).getPublic(); + // const hash = new Uint8Array( + // await crypto.subtle.digest("SHA-256", data) + // ); - const der = new ser.SerialBuffer({ array: signatureBytes }); - if (der.get() !== 0x30) { - throw new Error("Signature missing DER prefix"); - } - if (der.get() !== der.array.length - 2) { - throw new Error("Signature has bad length"); - } - if (der.get() !== 0x02) { - throw new Error("Signature has bad r marker"); - } - const r = fixup(der.getUint8Array(der.get())); - if (der.get() !== 0x02) { - throw new Error("Signature has bad s marker"); - } - const s = fixup(der.getUint8Array(der.get())); + // const ecSignature = { + // r: signatureBytes.slice(0, 32), + // s: signatureBytes.slice(32, 64), + // }; + // console.info(ecSignature, eosPubKey, pubKey, hash, signatureBytes); + // const recid = EC.getKeyRecoveryParam(hash, ecSignature, pubKey); + // const recidByte = 27 + 4 + recid; <<<--- still presenting failures - const recid = EC.getKeyRecoveryParam(hash, signatureBytes, pubKey); - const recidByte = 27 + 4 + recid; + let recId = 31; - // const r = new Uint8Array(signature.slice(0, 32)); - // const s = new Uint8Array(signature.slice(32, 64)); const sigDataWithRecovery = { type: KeyType.r1, - data: new Uint8Array( - [recidByte].concat(Array.from(signatureBytes)) - ), + data: new Uint8Array([recId].concat(Array.from(signatureBytes))), }; console.info( "signature from subtle", - recid, - recidByte, + recId, signatureBytes, signature, sigDataWithRecovery, signatureToString(sigDataWithRecovery) ); - return signatureToString(sigDataWithRecovery); + + const sessionPubKeyString = publicKeyToString(eosPubKey); + let x = signatureToString(sigDataWithRecovery); + let y = Signature.fromString(x); + let recoveredKey = y.recover(data, true).toString(); + + console.info( + "recoveredKey >>>", + recoveredKey.toString(), + sessionPubKeyString + ); + + if (recoveredKey.toString() === sessionPubKeyString) { + return x; + } + + recId += 1; + sigDataWithRecovery.data[0] = recId; + x = signatureToString(sigDataWithRecovery); + y = Signature.fromString(x); + recoveredKey = y.recover(data, true).toString(); + + console.info( + "recoveredKey 2 >>>", + recoveredKey.toString(), + sessionPubKeyString + ); + + return x; } return signature.toString(); }; @@ -320,3 +320,55 @@ const subtleToEosPublicKey = async ( console.info("subtle to eoskey >>>", key); return key; }; + +function toDER(pr: number[], ps: number[]) { + let r = [...pr]; + let s = [...ps]; + + // Pad values + if (r[0] & 0x80) r = [0].concat(r); + // Pad values + if (s[0] & 0x80) s = [0].concat(s); + + r = rmPadding(r); + s = rmPadding(s); + + while (!s[0] && !(s[1] & 0x80)) { + s = s.slice(1); + } + var arr = [0x02]; + constructLength(arr, r.length); + arr = arr.concat(r); + arr.push(0x02); + constructLength(arr, s.length); + var backHalf = arr.concat(s); + var res = [0x30]; + constructLength(res, backHalf.length); + res = res.concat(backHalf); + return res; +} + +function rmPadding(buf: number[]) { + var i = 0; + var len = buf.length - 1; + while (!buf[i] && !(buf[i + 1] & 0x80) && i < len) { + i++; + } + if (i === 0) { + return buf; + } + return buf.slice(i); +} + +function constructLength(arr: number[], len: number) { + if (len < 0x80) { + arr.push(len); + return; + } + var octets = 1 + ((Math.log(len) / Math.LN2) >>> 3); + arr.push(octets | 0x80); + while (--octets) { + arr.push((len >>> (octets << 3)) & 0xff); + } + arr.push(len); +} diff --git a/yarn.lock b/yarn.lock index c7be123ca..5abd5ca13 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2101,6 +2101,13 @@ dependencies: "@types/bn.js" "*" +"@types/elliptic@^6.4.14": + version "6.4.14" + resolved "https://registry.yarnpkg.com/@types/elliptic/-/elliptic-6.4.14.tgz#7bbaad60567a588c1f08b10893453e6b9b4de48e" + integrity sha512-z4OBcDAU0GVwDTuwJzQCiL6188QvZMkvoERgcVjq0/mPM8jCfdwZ3x5zQEVoL9WCAru3aG5wl3Z5Ww5wBWn7ZQ== + dependencies: + "@types/bn.js" "*" + "@types/express-serve-static-core@^4.17.18": version "4.17.21" resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.21.tgz#a427278e106bca77b83ad85221eae709a3414d42" From b9d3bc664447c2bb7e389b7e0e27c6b66099fc62 Mon Sep 17 00:00:00 2001 From: sparkplug0025 Date: Wed, 5 Jan 2022 09:42:23 -0500 Subject: [PATCH 4/8] sign wip --- packages/webapp/src/_app/eos/sessions.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/webapp/src/_app/eos/sessions.ts b/packages/webapp/src/_app/eos/sessions.ts index 010302157..b8a4a1274 100644 --- a/packages/webapp/src/_app/eos/sessions.ts +++ b/packages/webapp/src/_app/eos/sessions.ts @@ -277,9 +277,9 @@ const signData = async ( ); const sessionPubKeyString = publicKeyToString(eosPubKey); - let x = signatureToString(sigDataWithRecovery); - let y = Signature.fromString(x); - let recoveredKey = y.recover(data, true).toString(); + let signatureStr = signatureToString(sigDataWithRecovery); + let signatureEcObj = Signature.fromString(signatureStr); + let recoveredKey = signatureEcObj.recover(data, true).toString(); console.info( "recoveredKey >>>", @@ -288,14 +288,14 @@ const signData = async ( ); if (recoveredKey.toString() === sessionPubKeyString) { - return x; + return signatureStr; } recId += 1; sigDataWithRecovery.data[0] = recId; - x = signatureToString(sigDataWithRecovery); - y = Signature.fromString(x); - recoveredKey = y.recover(data, true).toString(); + signatureStr = signatureToString(sigDataWithRecovery); + signatureEcObj = Signature.fromString(signatureStr); + recoveredKey = signatureEcObj.recover(data, true).toString(); console.info( "recoveredKey 2 >>>", @@ -303,7 +303,7 @@ const signData = async ( sessionPubKeyString ); - return x; + return signatureStr; } return signature.toString(); }; From b042bd61b1b5974bb08928755f9e1c434cb0755c Mon Sep 17 00:00:00 2001 From: sparkplug0025 Date: Thu, 6 Jan 2022 09:40:43 -0500 Subject: [PATCH 5/8] flipping s-value --- packages/webapp/package.json | 1 + packages/webapp/src/_app/eos/sessions.ts | 93 +++++++++++++++--------- yarn.lock | 2 +- 3 files changed, 60 insertions(+), 36 deletions(-) diff --git a/packages/webapp/package.json b/packages/webapp/package.json index 56be3d5ab..8730f0e76 100644 --- a/packages/webapp/package.json +++ b/packages/webapp/package.json @@ -20,6 +20,7 @@ "@tailwindcss/forms": "^0.3.4", "@types/react-virtualized": "^9.21.13", "async-sema": "^3.1.1", + "bn.js": "^5.2.0", "cids": "^1.1.6", "dayjs": "^1.10.4", "elliptic": "^6.5.4", diff --git a/packages/webapp/src/_app/eos/sessions.ts b/packages/webapp/src/_app/eos/sessions.ts index b8a4a1274..2cad4d460 100644 --- a/packages/webapp/src/_app/eos/sessions.ts +++ b/packages/webapp/src/_app/eos/sessions.ts @@ -12,6 +12,7 @@ import { hexToUint8Array, SerialBuffer } from "eosjs/dist/eosjs-serialize"; import { get as idbGet, set as idbSet } from "idb-keyval"; import { SessionSignRequest } from "@edenos/common"; import { ec as elliptic } from "elliptic"; +import BN from "bn.js"; import { edenContractAccount, box } from "config"; import { eosDefaultApi, eosJsonRpc } from "_app"; @@ -246,64 +247,86 @@ const signData = async ( sessionKey.subtleKey.publicKey! ); - // const pubKey = EC.keyFromPublic( - // eosPubKey.data.subarray(0, 33) - // ).getPublic(); - // const hash = new Uint8Array( - // await crypto.subtle.digest("SHA-256", data) - // ); + const pubKey = EC.keyFromPublic( + eosPubKey.data.subarray(0, 33) + ).getPublic(); + const hash = new Uint8Array( + await crypto.subtle.digest("SHA-256", data) + ); // const ecSignature = { // r: signatureBytes.slice(0, 32), // s: signatureBytes.slice(32, 64), // }; - // console.info(ecSignature, eosPubKey, pubKey, hash, signatureBytes); - // const recid = EC.getKeyRecoveryParam(hash, ecSignature, pubKey); - // const recidByte = 27 + 4 + recid; <<<--- still presenting failures - let recId = 31; + const r = signatureBytes.slice(0, 32); + const s = signatureBytes.slice(32, 64); + + const flippedS: BN = EC.n.sub(new BN(s)); + const flippedSBytes = new Uint8Array(flippedS.toArray()); + + console.info( + "s vs flipped s >>> ", + s, + flippedSBytes, + flippedS.toString() + ); + + const ecSignature = { + r, + s: flippedSBytes, + }; + + console.info(ecSignature, eosPubKey, pubKey, hash, signatureBytes); + const recid = EC.getKeyRecoveryParam(hash, ecSignature, pubKey); + const recidByte = 27 + 4 + recid; // <<<--- still presenting failures + + // let recId = 31; const sigDataWithRecovery = { type: KeyType.r1, - data: new Uint8Array([recId].concat(Array.from(signatureBytes))), + data: new Uint8Array( + [recidByte].concat(Array.from(r), Array.from(flippedSBytes)) + ), }; console.info( "signature from subtle", - recId, + recidByte, signatureBytes, signature, sigDataWithRecovery, signatureToString(sigDataWithRecovery) ); + return signatureToString(sigDataWithRecovery); - const sessionPubKeyString = publicKeyToString(eosPubKey); - let signatureStr = signatureToString(sigDataWithRecovery); - let signatureEcObj = Signature.fromString(signatureStr); - let recoveredKey = signatureEcObj.recover(data, true).toString(); + // const sessionPubKeyString = publicKeyToString(eosPubKey); + // let signatureStr = signatureToString(sigDataWithRecovery); + // let signatureEcObj = Signature.fromString(signatureStr); + // let recoveredKey = signatureEcObj.recover(data, true).toString(); - console.info( - "recoveredKey >>>", - recoveredKey.toString(), - sessionPubKeyString - ); + // console.info( + // "recoveredKey >>>", + // recoveredKey.toString(), + // sessionPubKeyString + // ); - if (recoveredKey.toString() === sessionPubKeyString) { - return signatureStr; - } + // if (recoveredKey.toString() === sessionPubKeyString) { + // return signatureStr; + // } - recId += 1; - sigDataWithRecovery.data[0] = recId; - signatureStr = signatureToString(sigDataWithRecovery); - signatureEcObj = Signature.fromString(signatureStr); - recoveredKey = signatureEcObj.recover(data, true).toString(); + // recId += 1; + // sigDataWithRecovery.data[0] = recId; + // signatureStr = signatureToString(sigDataWithRecovery); + // signatureEcObj = Signature.fromString(signatureStr); + // recoveredKey = signatureEcObj.recover(data, true).toString(); - console.info( - "recoveredKey 2 >>>", - recoveredKey.toString(), - sessionPubKeyString - ); + // console.info( + // "recoveredKey 2 >>>", + // recoveredKey.toString(), + // sessionPubKeyString + // ); - return signatureStr; + // return signatureStr; } return signature.toString(); }; diff --git a/yarn.lock b/yarn.lock index 5abd5ca13..13a4b60a7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3034,7 +3034,7 @@ bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.11.9, bn.js@^4.4.0: resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== -bn.js@^5.0.0, bn.js@^5.1.1: +bn.js@^5.0.0, bn.js@^5.1.1, bn.js@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.0.tgz#358860674396c6997771a9d051fcc1b57d4ae002" integrity sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw== From 1609229802b25ad0e4aab527ea943ad9bfb02127 Mon Sep 17 00:00:00 2001 From: sparkplug0025 Date: Thu, 6 Jan 2022 10:38:37 -0500 Subject: [PATCH 6/8] completes sublte crypto signature --- packages/webapp/src/_app/eos/sessions.ts | 89 ++++-------------------- 1 file changed, 15 insertions(+), 74 deletions(-) diff --git a/packages/webapp/src/_app/eos/sessions.ts b/packages/webapp/src/_app/eos/sessions.ts index 2cad4d460..a638ff748 100644 --- a/packages/webapp/src/_app/eos/sessions.ts +++ b/packages/webapp/src/_app/eos/sessions.ts @@ -228,13 +228,7 @@ const signData = async ( sessionKey: SessionKeyData, data: Uint8Array ): Promise => { - const privateKey = PrivateKey.fromString(sessionKey.privateKey); - const signature = privateKey.sign(data, true); - console.info("eos signature >>>", signature, signature.toString()); - if (sessionKey.subtleKey?.privateKey) { - console.info("i will sign with subtle >>>", sessionKey.subtleKey); - const privateKey = sessionKey.subtleKey.privateKey; const signature = await crypto.subtle.sign( { name: "ECDSA", hash: "SHA-256" }, @@ -254,81 +248,28 @@ const signData = async ( await crypto.subtle.digest("SHA-256", data) ); - // const ecSignature = { - // r: signatureBytes.slice(0, 32), - // s: signatureBytes.slice(32, 64), - // }; - const r = signatureBytes.slice(0, 32); - const s = signatureBytes.slice(32, 64); - - const flippedS: BN = EC.n.sub(new BN(s)); - const flippedSBytes = new Uint8Array(flippedS.toArray()); - - console.info( - "s vs flipped s >>> ", - s, - flippedSBytes, - flippedS.toString() - ); - - const ecSignature = { - r, - s: flippedSBytes, - }; - - console.info(ecSignature, eosPubKey, pubKey, hash, signatureBytes); - const recid = EC.getKeyRecoveryParam(hash, ecSignature, pubKey); - const recidByte = 27 + 4 + recid; // <<<--- still presenting failures - - // let recId = 31; + let s = signatureBytes.slice(32, 64); + const sBN = new BN(s); + + // flip s-value if bigger than half of curve n + if (sBN.gt(EC.n.divn(2))) { + const flippedS: BN = EC.n.sub(sBN); + const flippedSBytes = new Uint8Array(flippedS.toArray()); + s = flippedSBytes; + } + const recId = 31 + EC.getKeyRecoveryParam(hash, { r, s }, pubKey); const sigDataWithRecovery = { type: KeyType.r1, - data: new Uint8Array( - [recidByte].concat(Array.from(r), Array.from(flippedSBytes)) - ), + data: new Uint8Array([recId].concat(Array.from(r), Array.from(s))), }; - console.info( - "signature from subtle", - recidByte, - signatureBytes, - signature, - sigDataWithRecovery, - signatureToString(sigDataWithRecovery) - ); return signatureToString(sigDataWithRecovery); - - // const sessionPubKeyString = publicKeyToString(eosPubKey); - // let signatureStr = signatureToString(sigDataWithRecovery); - // let signatureEcObj = Signature.fromString(signatureStr); - // let recoveredKey = signatureEcObj.recover(data, true).toString(); - - // console.info( - // "recoveredKey >>>", - // recoveredKey.toString(), - // sessionPubKeyString - // ); - - // if (recoveredKey.toString() === sessionPubKeyString) { - // return signatureStr; - // } - - // recId += 1; - // sigDataWithRecovery.data[0] = recId; - // signatureStr = signatureToString(sigDataWithRecovery); - // signatureEcObj = Signature.fromString(signatureStr); - // recoveredKey = signatureEcObj.recover(data, true).toString(); - - // console.info( - // "recoveredKey 2 >>>", - // recoveredKey.toString(), - // sessionPubKeyString - // ); - - // return signatureStr; + } else { + const privateKey = PrivateKey.fromString(sessionKey.privateKey); + const signature = privateKey.sign(data, true); + return signature.toString(); } - return signature.toString(); }; const subtleToEosPublicKey = async ( From a0a4d41b951764f374e569bfff61d37592035f45 Mon Sep 17 00:00:00 2001 From: sparkplug0025 Date: Mon, 10 Jan 2022 09:16:28 -0500 Subject: [PATCH 7/8] refactors session signature --- packages/webapp/src/_app/eos/sessions.ts | 172 ++++++----------------- packages/webapp/src/pages/sessions.tsx | 6 + 2 files changed, 52 insertions(+), 126 deletions(-) diff --git a/packages/webapp/src/_app/eos/sessions.ts b/packages/webapp/src/_app/eos/sessions.ts index a638ff748..ef15da9db 100644 --- a/packages/webapp/src/_app/eos/sessions.ts +++ b/packages/webapp/src/_app/eos/sessions.ts @@ -1,13 +1,10 @@ import dayjs from "dayjs"; -import { generateKeyPair } from "eosjs/dist/eosjs-key-conversions"; import { Key, KeyType, publicKeyToString, signatureToString, } from "eosjs/dist/eosjs-numeric"; -import { PrivateKey } from "eosjs/dist/eosjs-jssig"; -import { Signature } from "eosjs/dist/Signature"; import { hexToUint8Array, SerialBuffer } from "eosjs/dist/eosjs-serialize"; import { get as idbGet, set as idbSet } from "idb-keyval"; import { SessionSignRequest } from "@edenos/common"; @@ -23,11 +20,9 @@ const DEFAULT_EXPIRATION_SECONDS = 30 * 24 * 60 * 60; // 30 days const DEFAULT_SESSION_DESCRIPTION = "eden login"; interface SessionKeyData { - publicKey: string; - privateKey: string; + subtleKey: CryptoKeyPair; expiration: Date; lastSequence: number; - subtleKey?: CryptoKeyPair; } class SessionKeysStorage { @@ -61,25 +56,9 @@ export const sessionKeysStorage = new SessionKeysStorage(); export const generateSessionKey = async ( expirationSeconds = DEFAULT_EXPIRATION_SECONDS ) => { - // TODO: to be replaced with SubtleCrypto Apis - const { publicKey, privateKey } = generateKeyPair(KeyType.r1, { - secureEnv: true, - }); - - const subtleKey = await crypto.subtle.generateKey( - { - name: "ECDSA", - namedCurve: "P-256", - }, - false, // not extractable - ["sign", "verify"] - ); - + const subtleKey = await generateSubtleCryptoKeyPair(); const expiration = dayjs().add(expirationSeconds, "seconds").toDate(); - return { - publicKey: publicKey.toString(), - privateKey: privateKey.toString(), subtleKey, lastSequence: 0, expiration, @@ -91,14 +70,9 @@ export const newSessionTransaction = async ( sessionKeyData: SessionKeyData, description = DEFAULT_SESSION_DESCRIPTION ) => { - let key = sessionKeyData.publicKey; - - if (sessionKeyData.subtleKey?.publicKey) { - const subtlePubKey = sessionKeyData.subtleKey.publicKey; - const eosKey = await subtleToEosPublicKey(subtlePubKey); - console.info("subtle eos pubkey >>>", eosKey); - key = publicKeyToString(eosKey); - } + const subtlePubKey = sessionKeyData.subtleKey.publicKey!; + const eosKey = await subtleToEosPublicKey(subtlePubKey); + const key = publicKeyToString(eosKey); const expiration = sessionKeyData.expiration.toISOString().slice(0, -4) + "000"; @@ -181,6 +155,17 @@ export const signSessionTransaction = async ( return response.json(); }; +// Generates a Subtle Crypto EC P-256 R1 key +const generateSubtleCryptoKeyPair = () => + crypto.subtle.generateKey( + { + name: "ECDSA", + namedCurve: "P-256", + }, + false, // not extractable + ["sign", "verify"] + ); + const makeSignatureAuthSha = async ( contract: string, account: string, @@ -194,7 +179,6 @@ const makeSignatureAuthSha = async ( verbs ); return signatureAuthBytes; - // return sha256(Buffer.from(signatureAuthBytes)); }; const convertActionsToVerbs = (actions: any[]) => @@ -228,48 +212,38 @@ const signData = async ( sessionKey: SessionKeyData, data: Uint8Array ): Promise => { - if (sessionKey.subtleKey?.privateKey) { - const privateKey = sessionKey.subtleKey.privateKey; - const signature = await crypto.subtle.sign( - { name: "ECDSA", hash: "SHA-256" }, - privateKey, - data - ); - const signatureBytes = new Uint8Array(signature); - - const eosPubKey = await subtleToEosPublicKey( - sessionKey.subtleKey.publicKey! - ); - - const pubKey = EC.keyFromPublic( - eosPubKey.data.subarray(0, 33) - ).getPublic(); - const hash = new Uint8Array( - await crypto.subtle.digest("SHA-256", data) - ); - - const r = signatureBytes.slice(0, 32); - let s = signatureBytes.slice(32, 64); - const sBN = new BN(s); - - // flip s-value if bigger than half of curve n - if (sBN.gt(EC.n.divn(2))) { - const flippedS: BN = EC.n.sub(sBN); - const flippedSBytes = new Uint8Array(flippedS.toArray()); - s = flippedSBytes; - } + const privateKey = sessionKey.subtleKey.privateKey!; + const signature = await crypto.subtle.sign( + { name: "ECDSA", hash: "SHA-256" }, + privateKey, + data + ); + const signatureBytes = new Uint8Array(signature); + + const eosPubKey = await subtleToEosPublicKey( + sessionKey.subtleKey.publicKey! + ); - const recId = 31 + EC.getKeyRecoveryParam(hash, { r, s }, pubKey); - const sigDataWithRecovery = { - type: KeyType.r1, - data: new Uint8Array([recId].concat(Array.from(r), Array.from(s))), - }; - return signatureToString(sigDataWithRecovery); - } else { - const privateKey = PrivateKey.fromString(sessionKey.privateKey); - const signature = privateKey.sign(data, true); - return signature.toString(); + const pubKey = EC.keyFromPublic(eosPubKey.data.subarray(0, 33)).getPublic(); + const hash = new Uint8Array(await crypto.subtle.digest("SHA-256", data)); + + const r = signatureBytes.slice(0, 32); + let s = signatureBytes.slice(32, 64); + const sBN = new BN(s); + + // flip s-value if bigger than half of curve n + if (sBN.gt(EC.n.divn(2))) { + const flippedS: BN = EC.n.sub(sBN); + const flippedSBytes = new Uint8Array(flippedS.toArray()); + s = flippedSBytes; } + + const recId = 31 + EC.getKeyRecoveryParam(hash, { r, s }, pubKey); + const sigDataWithRecovery = { + type: KeyType.r1, + data: new Uint8Array([recId].concat(Array.from(r), Array.from(s))), + }; + return signatureToString(sigDataWithRecovery); }; const subtleToEosPublicKey = async ( @@ -278,61 +252,7 @@ const subtleToEosPublicKey = async ( const rawKey = await crypto.subtle.exportKey("raw", subtlePublicKey); const x = new Uint8Array(rawKey.slice(1, 33)); const y = new Uint8Array(rawKey.slice(33, 65)); - console.info("raw data pubkey >>>", rawKey, x, y); const data = new Uint8Array([y[31] & 1 ? 3 : 2].concat(Array.from(x))); const key = { type: KeyType.r1, data }; - console.info("subtle to eoskey >>>", key); return key; }; - -function toDER(pr: number[], ps: number[]) { - let r = [...pr]; - let s = [...ps]; - - // Pad values - if (r[0] & 0x80) r = [0].concat(r); - // Pad values - if (s[0] & 0x80) s = [0].concat(s); - - r = rmPadding(r); - s = rmPadding(s); - - while (!s[0] && !(s[1] & 0x80)) { - s = s.slice(1); - } - var arr = [0x02]; - constructLength(arr, r.length); - arr = arr.concat(r); - arr.push(0x02); - constructLength(arr, s.length); - var backHalf = arr.concat(s); - var res = [0x30]; - constructLength(res, backHalf.length); - res = res.concat(backHalf); - return res; -} - -function rmPadding(buf: number[]) { - var i = 0; - var len = buf.length - 1; - while (!buf[i] && !(buf[i + 1] & 0x80) && i < len) { - i++; - } - if (i === 0) { - return buf; - } - return buf.slice(i); -} - -function constructLength(arr: number[], len: number) { - if (len < 0x80) { - arr.push(len); - return; - } - var octets = 1 + ((Math.log(len) / Math.LN2) >>> 3); - arr.push(octets | 0x80); - while (--octets) { - arr.push((len >>> (octets << 3)) & 0xff); - } - arr.push(len); -} diff --git a/packages/webapp/src/pages/sessions.tsx b/packages/webapp/src/pages/sessions.tsx index 19396b813..5a084ff82 100644 --- a/packages/webapp/src/pages/sessions.tsx +++ b/packages/webapp/src/pages/sessions.tsx @@ -1,3 +1,5 @@ +import toast from "react-hot-toast"; + import { Button, onError, @@ -37,6 +39,8 @@ export const Sessions = () => { await sessionKeysStorage.saveKey(newSessionKey); + toast.success("Session created successfully"); + return { newSessionKey }; } catch (e) { console.error(e); @@ -61,6 +65,8 @@ export const Sessions = () => { ualAccount.accountName, actions ); + + toast.success("Induction sample submitted successfully!"); } catch (e) { console.error(e); onError(e as Error); From eacb5addeb26377345b66e3445f733dbff3123ba Mon Sep 17 00:00:00 2001 From: sparkplug0025 Date: Mon, 10 Jan 2022 09:26:38 -0500 Subject: [PATCH 8/8] fix build --- packages/webapp/src/_app/eos/secret-publisher.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/webapp/src/_app/eos/secret-publisher.ts b/packages/webapp/src/_app/eos/secret-publisher.ts index 33a9d04fc..b474ef845 100644 --- a/packages/webapp/src/_app/eos/secret-publisher.ts +++ b/packages/webapp/src/_app/eos/secret-publisher.ts @@ -201,7 +201,7 @@ const deriveEcdhSecret = ( return eosPrivateKeyA .toElliptic() .derive(eosPublicKeyB.toElliptic().getPublic()) - .toArrayLike(Uint8Array).buffer; + .toArrayLike(Uint8Array as any).buffer; }; const hkdfSha256FromEcdh = async (