1+ import { mod } from "@noble/curves/abstract/modular.js" ;
12import { bs58 } from "@toruslabs/bs58" ;
23import { INodePub , KEY_TYPE } from "@toruslabs/constants" ;
34import { Ecies , encrypt } from "@toruslabs/eccrypto" ;
4- import BN from "bn.js" ;
5- import { curve , ec as EC } from "elliptic" ;
65import { keccak256 as keccakHash } from "ethereum-cryptography/keccak" ;
76import { sha512 } from "ethereum-cryptography/sha512" ;
87import stringify from "json-stable-stringify" ;
98import log from "loglevel" ;
109
11- import { EncryptedSeed , ImportedShare , KeyType , PrivateKeyData } from "../interfaces" ;
12- import { encParamsBufToHex , generatePrivateKey , getKeyCurve , keccak256 } from "./common" ;
10+ import { EncryptedSeed , ImportedShare , KeyType , Point2D , PrivateKeyData } from "../interfaces" ;
11+ import {
12+ bigintToHex ,
13+ bytesToBase64 ,
14+ bytesToHex ,
15+ bytesToNumberBE ,
16+ bytesToNumberLE ,
17+ Curve ,
18+ encParamsBufToHex ,
19+ generatePrivateKey ,
20+ getKeyCurve ,
21+ getSecp256k1 ,
22+ hexToBytes ,
23+ keccak256 ,
24+ toBigIntBE ,
25+ utf8ToBytes ,
26+ } from "./common" ;
1327import { generateRandomPolynomial } from "./langrangeInterpolatePoly" ;
1428import { generateNonceMetadataParams , getSecpKeyFromEd25519 } from "./metadataUtils" ;
1529
@@ -20,8 +34,7 @@ export function stripHexPrefix(str: string): string {
2034export function toChecksumAddress ( hexAddress : string ) : string {
2135 const address = stripHexPrefix ( hexAddress ) . toLowerCase ( ) ;
2236
23- const buf = Buffer . from ( address , "utf8" ) ;
24- const hash = Buffer . from ( keccakHash ( buf ) ) . toString ( "hex" ) ;
37+ const hash = bytesToHex ( keccakHash ( utf8ToBytes ( address ) ) ) ;
2538 let ret = "0x" ;
2639
2740 for ( let i = 0 ; i < address . length ; i ++ ) {
@@ -35,7 +48,7 @@ export function toChecksumAddress(hexAddress: string): string {
3548 return ret ;
3649}
3750
38- function adjustScalarBytes ( bytes : Buffer ) : Buffer {
51+ function adjustScalarBytes ( bytes : Uint8Array ) : Uint8Array {
3952 // Section 5: For X25519, in order to decode 32 random bytes as an integer scalar,
4053 // set the three least significant bits of the first byte
4154 bytes [ 0 ] &= 248 ; // 0b1111_1000
@@ -47,14 +60,13 @@ function adjustScalarBytes(bytes: Buffer): Buffer {
4760}
4861
4962/** Convenience method that creates public key and other stuff. RFC8032 5.1.5 */
50- export function getEd25519ExtendedPublicKey ( keyBuffer : Buffer ) : {
51- scalar : BN ;
52- point : curve . base . BasePoint ;
63+ export function getEd25519ExtendedPublicKey ( keyBuffer : Uint8Array ) : {
64+ scalar : bigint ;
65+ point : Point2D ;
5366} {
5467 const ed25519Curve = getKeyCurve ( KEY_TYPE . ED25519 ) ;
5568 const len = 32 ;
56- const G = ed25519Curve . g ;
57- const N = ed25519Curve . n ;
69+ const N = ed25519Curve . Point . CURVE ( ) . n ;
5870
5971 if ( keyBuffer . length !== 32 ) {
6072 log . error ( "Invalid seed for ed25519 key derivation" , keyBuffer . length ) ;
@@ -66,161 +78,152 @@ export function getEd25519ExtendedPublicKey(keyBuffer: Buffer): {
6678 if ( hashed . length !== 64 ) {
6779 throw new Error ( "Invalid hash length for ed25519 seed" ) ;
6880 }
69- const head = new BN ( adjustScalarBytes ( Buffer . from ( hashed . slice ( 0 , len ) ) ) , "le" ) ;
70- const scalar = new BN ( head . umod ( N ) , "le" ) ; // The actual private scalar
71- const point = G . mul ( scalar ) as curve . base . BasePoint ; // Point on Edwards curve aka public key
81+ const head = bytesToNumberLE ( adjustScalarBytes ( new Uint8Array ( hashed . slice ( 0 , len ) ) ) ) ;
82+ const scalar = mod ( head , N ) ; // The actual private scalar
83+ const point = ed25519Curve . Point . BASE . multiply ( scalar ) . toAffine ( ) ; // Point on Edwards curve aka public key
7284 return { scalar, point } ;
7385}
7486
75- export function encodeEd25519Point ( point : curve . base . BasePoint ) {
87+ export function encodeEd25519Point ( point : Point2D ) : Uint8Array {
7688 const ed25519Curve = getKeyCurve ( KEY_TYPE . ED25519 ) ;
77-
78- const encodingLength = Math . ceil ( ed25519Curve . n . bitLength ( ) / 8 ) ;
79- const enc = point . getY ( ) . toArrayLike ( Buffer , "le" , encodingLength ) ;
80- enc [ encodingLength - 1 ] |= point . getX ( ) . isOdd ( ) ? 0x80 : 0 ;
81- return enc ;
89+ return ed25519Curve . Point . fromAffine ( point ) . toBytes ( ) ;
8290}
8391
84- export const generateEd25519KeyData = async ( ed25519Seed : Buffer ) : Promise < PrivateKeyData > => {
92+ export const generateEd25519KeyData = async ( ed25519Seed : Uint8Array ) : Promise < PrivateKeyData > => {
8593 const ed25519Curve = getKeyCurve ( KEY_TYPE . ED25519 ) ;
94+ const N = ed25519Curve . Point . CURVE ( ) . n ;
8695
8796 const finalEd25519Key = getEd25519ExtendedPublicKey ( ed25519Seed ) ;
8897 const encryptionKey = getSecpKeyFromEd25519 ( finalEd25519Key . scalar ) ;
89- const encryptedSeed = await encrypt ( Buffer . from ( encryptionKey . point . encodeCompressed ( "hex" ) , "hex" ) , ed25519Seed ) ;
98+
99+ const encPubKeyBytes = getSecp256k1 ( ) . Point . fromAffine ( encryptionKey . point ) . toBytes ( true ) ;
100+ const encryptedSeed = await encrypt ( encPubKeyBytes , ed25519Seed ) ;
90101 const encData : EncryptedSeed = {
91- enc_text : encryptedSeed . ciphertext . toString ( "hex" ) ,
102+ enc_text : bytesToHex ( encryptedSeed . ciphertext ) ,
92103 metadata : encParamsBufToHex ( encryptedSeed ) ,
93- public_key : encodeEd25519Point ( finalEd25519Key . point ) . toString ( "hex" ) ,
104+ public_key : bytesToHex ( encodeEd25519Point ( finalEd25519Key . point ) ) ,
94105 } ;
95106
96- const encDataBase64 = Buffer . from ( JSON . stringify ( encData ) , "utf-8" ) . toString ( "base64" ) ;
97- const metadataPrivNonce = ed25519Curve . genKeyPair ( ) . getPrivate ( ) ;
98- const oauthKey = finalEd25519Key . scalar . sub ( metadataPrivNonce ) . umod ( ed25519Curve . n ) ;
99- const oauthKeyPair = ed25519Curve . keyFromPrivate ( oauthKey . toArrayLike ( Buffer ) ) ;
100- const metadataSigningKey = getSecpKeyFromEd25519 ( oauthKeyPair . getPrivate ( ) ) ;
107+ const encDataBase64 = bytesToBase64 ( utf8ToBytes ( JSON . stringify ( encData ) ) ) ;
108+ const metadataPrivNonce = bytesToNumberBE ( generatePrivateKey ( KEY_TYPE . ED25519 ) ) ;
109+ const oauthKey = mod ( finalEd25519Key . scalar - metadataPrivNonce , N ) ;
110+ const oauthPub = ed25519Curve . Point . BASE . multiply ( oauthKey ) . toAffine ( ) ;
111+ const metadataSigningKey = getSecpKeyFromEd25519 ( oauthKey ) ;
101112 return {
102- oAuthKeyScalar : oauthKeyPair . getPrivate ( ) ,
103- oAuthPubX : oauthKeyPair . getPublic ( ) . getX ( ) ,
104- oAuthPubY : oauthKeyPair . getPublic ( ) . getY ( ) ,
105- SigningPubX : metadataSigningKey . point . getX ( ) ,
106- SigningPubY : metadataSigningKey . point . getY ( ) ,
113+ oAuthKeyScalar : oauthKey ,
114+ oAuthPubX : oauthPub . x ,
115+ oAuthPubY : oauthPub . y ,
116+ SigningPubX : metadataSigningKey . point . x ,
117+ SigningPubY : metadataSigningKey . point . y ,
107118 metadataNonce : metadataPrivNonce ,
108119 metadataSigningKey : metadataSigningKey . scalar ,
109120 encryptedSeed : encDataBase64 ,
110121 finalUserPubKeyPoint : finalEd25519Key . point ,
111122 } ;
112123} ;
113124
114- export const generateSecp256k1KeyData = async ( scalarBuffer : Buffer ) : Promise < PrivateKeyData > => {
125+ export const generateSecp256k1KeyData = async ( scalarBuffer : Uint8Array ) : Promise < PrivateKeyData > => {
115126 const secp256k1Curve = getKeyCurve ( KEY_TYPE . SECP256K1 ) ;
127+ const N = secp256k1Curve . Point . CURVE ( ) . n ;
116128
117- const scalar = new BN ( scalarBuffer ) ;
118- const randomNonce = new BN ( generatePrivateKey ( secp256k1Curve , Buffer ) ) ;
119- const oAuthKey = scalar . sub ( randomNonce ) . umod ( secp256k1Curve . n ) ;
120- const oAuthKeyPair = secp256k1Curve . keyFromPrivate ( oAuthKey . toArrayLike ( Buffer ) ) ;
121- const oAuthPubKey = oAuthKeyPair . getPublic ( ) ;
129+ const scalar = bytesToNumberBE ( scalarBuffer ) ;
130+ const randomNonce = bytesToNumberBE ( generatePrivateKey ( KEY_TYPE . SECP256K1 ) ) ;
131+ const oAuthKey = mod ( scalar - randomNonce , N ) ;
132+ const oAuthPub = secp256k1Curve . Point . BASE . multiply ( oAuthKey ) . toAffine ( ) ;
122133
123- const finalUserKeyPair = secp256k1Curve . keyFromPrivate ( scalar . toString ( "hex" , 64 ) , "hex" ) ;
134+ const finalUserPub = secp256k1Curve . Point . BASE . multiply ( scalar ) . toAffine ( ) ;
124135
125136 return {
126- oAuthKeyScalar : oAuthKeyPair . getPrivate ( ) ,
127- oAuthPubX : oAuthPubKey . getX ( ) ,
128- oAuthPubY : oAuthPubKey . getY ( ) ,
129- SigningPubX : oAuthPubKey . getX ( ) ,
130- SigningPubY : oAuthPubKey . getY ( ) ,
137+ oAuthKeyScalar : oAuthKey ,
138+ oAuthPubX : oAuthPub . x ,
139+ oAuthPubY : oAuthPub . y ,
140+ SigningPubX : oAuthPub . x ,
141+ SigningPubY : oAuthPub . y ,
131142 metadataNonce : randomNonce ,
132143 encryptedSeed : "" ,
133- metadataSigningKey : oAuthKeyPair . getPrivate ( ) ,
134- finalUserPubKeyPoint : finalUserKeyPair . getPublic ( ) ,
144+ metadataSigningKey : oAuthKey ,
145+ finalUserPubKeyPoint : finalUserPub ,
135146 } ;
136147} ;
137148
138- function generateAddressFromEcKey ( keyType : KeyType , key : EC . KeyPair ) : string {
149+ function generateAddressFromPoint ( keyType : KeyType , point : Point2D ) : string {
139150 if ( keyType === KEY_TYPE . SECP256K1 ) {
140- const publicKey = key . getPublic ( ) . encode ( "hex" , false ) . slice ( 2 ) ;
141- const evmAddressLower = `0x${ keccak256 ( Buffer . from ( publicKey , "hex" ) ) . slice ( 64 - 38 ) } ` ;
151+ const uncompressed = bytesToHex ( getSecp256k1 ( ) . Point . fromAffine ( point ) . toBytes ( false ) ) ;
152+ const publicKey = uncompressed . slice ( 2 ) ; // remove 04 prefix
153+ const evmAddressLower = `0x${ keccak256 ( hexToBytes ( publicKey ) ) . slice ( 64 - 38 ) } ` ;
142154 return toChecksumAddress ( evmAddressLower ) ;
143155 } else if ( keyType === KEY_TYPE . ED25519 ) {
144- const publicKey = encodeEd25519Point ( key . getPublic ( ) ) ;
156+ const publicKey = encodeEd25519Point ( point ) ;
145157 const address = bs58 . encode ( publicKey ) ;
146158 return address ;
147159 }
148160 throw new Error ( `Invalid keyType: ${ keyType } ` ) ;
149161}
150162
151- export function generateAddressFromPrivKey ( keyType : KeyType , privateKey : BN ) : string {
163+ export function generateAddressFromPrivKey ( keyType : KeyType , privateKey : bigint ) : string {
152164 const ecCurve = getKeyCurve ( keyType ) ;
153- const key = ecCurve . keyFromPrivate ( privateKey . toString ( "hex" , 64 ) , "hex" ) ;
154- return generateAddressFromEcKey ( keyType , key ) ;
165+ const point = ecCurve . Point . BASE . multiply ( privateKey ) . toAffine ( ) ;
166+ return generateAddressFromPoint ( keyType , point ) ;
155167}
156168
157- export function generateAddressFromPubKey ( keyType : KeyType , publicKeyX : BN , publicKeyY : BN ) : string {
158- const ecCurve = getKeyCurve ( keyType ) ;
159- const key = ecCurve . keyFromPublic ( { x : publicKeyX . toString ( "hex" , 64 ) , y : publicKeyY . toString ( "hex" , 64 ) } ) ;
160- return generateAddressFromEcKey ( keyType , key ) ;
169+ export function generateAddressFromPubKey ( keyType : KeyType , publicKeyX : bigint , publicKeyY : bigint ) : string {
170+ return generateAddressFromPoint ( keyType , { x : publicKeyX , y : publicKeyY } ) ;
161171}
162172
163- export function getPostboxKeyFrom1OutOf1 ( ecCurve : EC , privKey : string , nonce : string ) : string {
164- const privKeyBN = new BN ( privKey , 16 ) ;
165- const nonceBN = new BN ( nonce , 16 ) ;
166- return privKeyBN . sub ( nonceBN ) . umod ( ecCurve . n ) . toString ( "hex" ) ;
173+ export function getPostboxKeyFrom1OutOf1 ( ecCurve : Curve , privKey : string , nonce : string ) : string {
174+ const privKeyBI = toBigIntBE ( privKey ) ;
175+ const nonceBI = toBigIntBE ( nonce ) ;
176+ return bigintToHex ( mod ( privKeyBI - nonceBI , ecCurve . Point . CURVE ( ) . n ) ) ;
167177}
168178
169- export function derivePubKey ( ecCurve : EC , sk : BN ) : curve . base . BasePoint {
170- const skHex = sk . toString ( 16 , 64 ) ;
171- return ecCurve . keyFromPrivate ( skHex , "hex" ) . getPublic ( ) ;
179+ export function derivePubKey ( ecCurve : Curve , sk : bigint ) : Point2D {
180+ return ecCurve . Point . BASE . multiply ( sk ) . toAffine ( ) ;
172181}
173182
174- export const getEncryptionEC = ( ) : EC => {
175- return new EC ( "secp256k1" ) ;
176- } ;
177-
178183export const generateShares = async (
179- ecCurve : EC ,
184+ ecCurve : Curve ,
180185 keyType : KeyType ,
181186 serverTimeOffset : number ,
182187 nodeIndexes : number [ ] ,
183188 nodePubkeys : INodePub [ ] ,
184- privKey : Buffer
189+ privKey : Uint8Array
185190) => {
186191 const keyData = keyType === KEY_TYPE . ED25519 ? await generateEd25519KeyData ( privKey ) : await generateSecp256k1KeyData ( privKey ) ;
187192 const { metadataNonce, oAuthKeyScalar : oAuthKey , encryptedSeed, metadataSigningKey } = keyData ;
188193 const threshold = ~ ~ ( nodePubkeys . length / 2 ) + 1 ;
189194 const degree = threshold - 1 ;
190- const nodeIndexesBn : BN [ ] = [ ] ;
195+ const nodeIndexesBigInt : bigint [ ] = nodeIndexes . map ( ( i ) => BigInt ( i ) ) ;
191196
192- for ( const nodeIndex of nodeIndexes ) {
193- nodeIndexesBn . push ( new BN ( nodeIndex ) ) ;
194- }
195- const oAuthPubKey = ecCurve . keyFromPrivate ( oAuthKey . toString ( "hex" , 64 ) , "hex" ) . getPublic ( ) ;
196- const poly = generateRandomPolynomial ( ecCurve , degree , oAuthKey ) ;
197- const shares = poly . generateShares ( nodeIndexesBn ) ;
197+ const oAuthPub = ecCurve . Point . BASE . multiply ( oAuthKey ) . toAffine ( ) ;
198+ const poly = generateRandomPolynomial ( ecCurve , keyType , degree , oAuthKey ) ;
199+ const shares = poly . generateShares ( nodeIndexesBigInt ) ;
198200 const nonceParams = generateNonceMetadataParams ( serverTimeOffset , "getOrSetNonce" , metadataSigningKey , keyType , metadataNonce , encryptedSeed ) ;
199- const nonceData = Buffer . from ( stringify ( nonceParams . set_data ) , "utf8" ) . toString ( "base64" ) ;
201+ const nonceData = bytesToBase64 ( utf8ToBytes ( stringify ( nonceParams . set_data ) ) ) ;
200202 const sharesData : ImportedShare [ ] = [ ] ;
201203 const encPromises : Promise < Ecies > [ ] = [ ] ;
202- for ( let i = 0 ; i < nodeIndexesBn . length ; i ++ ) {
203- const shareJson = shares [ nodeIndexesBn [ i ] . toString ( "hex" , 64 ) ] . toJSON ( ) as Record < string , string > ;
204+ for ( let i = 0 ; i < nodeIndexesBigInt . length ; i ++ ) {
205+ const shareJson = shares [ bigintToHex ( nodeIndexesBigInt [ i ] ) ] . toJSON ( ) as Record < string , string > ;
204206 if ( ! nodePubkeys [ i ] ) {
205- throw new Error ( `Missing node pub key for node index: ${ nodeIndexesBn [ i ] . toString ( "hex" , 64 ) } ` ) ;
207+ throw new Error ( `Missing node pub key for node index: ${ bigintToHex ( nodeIndexesBigInt [ i ] ) } ` ) ;
206208 }
207- const nodePubKey = getEncryptionEC ( ) . keyFromPublic ( { x : nodePubkeys [ i ] . X , y : nodePubkeys [ i ] . Y } ) ;
208- encPromises . push (
209- encrypt ( Buffer . from ( nodePubKey . getPublic ( ) . encodeCompressed ( "hex" ) , "hex" ) , Buffer . from ( shareJson . share . padStart ( 64 , "0" ) , "hex" ) )
210- ) ;
209+ const nodePubPoint = getSecp256k1 ( ) . Point . fromAffine ( {
210+ x : toBigIntBE ( nodePubkeys [ i ] . X ) ,
211+ y : toBigIntBE ( nodePubkeys [ i ] . Y ) ,
212+ } ) ;
213+ encPromises . push ( encrypt ( nodePubPoint . toBytes ( ) , hexToBytes ( shareJson . share . padStart ( 64 , "0" ) ) ) ) ;
211214 }
212215 const encShares = await Promise . all ( encPromises ) ;
213- for ( let i = 0 ; i < nodeIndexesBn . length ; i += 1 ) {
214- const shareJson = shares [ nodeIndexesBn [ i ] . toString ( "hex" , 64 ) ] . toJSON ( ) as Record < string , string > ;
216+ for ( let i = 0 ; i < nodeIndexesBigInt . length ; i += 1 ) {
217+ const shareJson = shares [ bigintToHex ( nodeIndexesBigInt [ i ] ) ] . toJSON ( ) as Record < string , string > ;
215218 const encParams = encShares [ i ] ;
216219 const encParamsMetadata = encParamsBufToHex ( encParams ) ;
217220 const shareData : ImportedShare = {
218221 encrypted_seed : keyData . encryptedSeed ,
219222 final_user_point : keyData . finalUserPubKeyPoint ,
220- oauth_pub_key_x : oAuthPubKey . getX ( ) . toString ( "hex" ) ,
221- oauth_pub_key_y : oAuthPubKey . getY ( ) . toString ( "hex" ) ,
222- signing_pub_key_x : keyData . SigningPubX . toString ( "hex" ) ,
223- signing_pub_key_y : keyData . SigningPubY . toString ( "hex" ) ,
223+ oauth_pub_key_x : bigintToHex ( oAuthPub . x ) ,
224+ oauth_pub_key_y : bigintToHex ( oAuthPub . y ) ,
225+ signing_pub_key_x : bigintToHex ( keyData . SigningPubX ) ,
226+ signing_pub_key_y : bigintToHex ( keyData . SigningPubY ) ,
224227 encrypted_share : encParamsMetadata . ciphertext ,
225228 encrypted_share_metadata : encParamsMetadata ,
226229 node_index : Number . parseInt ( shareJson . shareIndex , 16 ) ,
0 commit comments