diff --git a/.gitignore b/.gitignore index e383173..6467800 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ build node_modules .DS_Store .idea +libra-web.iml diff --git a/packages/libra-web-account/KeyFactory.ts b/packages/libra-web-account/KeyFactory.ts index bd7f597..052780d 100644 --- a/packages/libra-web-account/KeyFactory.ts +++ b/packages/libra-web-account/KeyFactory.ts @@ -12,10 +12,12 @@ import { Mnemonic } from './Mnemonic' export class Seed { public static fromMnemonic(words: string[] | Mnemonic, salt: string = 'LIBRA'): Seed { const mnemonic: Mnemonic = Array.isArray(words) ? new Mnemonic(words) : words - const mnemonicBytes = mnemonic.toBytes() - const parsedSalt = `${KeyPrefixes.MnemonicSalt}${salt}` - - const bytes = new Pbkdf('sha256').extract(mnemonicBytes, parsedSalt, 2048, 32) + const bytes = new Pbkdf('sha3-256').pbkdf2( + mnemonic.toBytes(), + Buffer.from(`${KeyPrefixes.MnemonicSalt}${salt}`), + 2048, + 32, + ) return new Seed(bytes) } public readonly data: Uint8Array @@ -38,7 +40,7 @@ export class KeyFactory { constructor(seed: Seed) { this.seed = seed - this.hkdf = new Hkdf('sha256') + this.hkdf = new Hkdf('sha3-256') this.masterPrk = this.hkdf.extract(this.seed.data, KeyPrefixes.MasterKeySalt) } @@ -47,7 +49,12 @@ export class KeyFactory { * */ public generateKey(childDepth: number): KeyPair { - const info = `${KeyPrefixes.DerivedKey}${childDepth}` + const childDepthBuffer = Buffer.alloc(8) + childDepthBuffer.writeBigUInt64LE(BigInt(childDepth)) + const info = Buffer.concat([ + Uint8Array.from(Buffer.from(KeyPrefixes.DerivedKey)), + Uint8Array.from(childDepthBuffer), + ]); const secretKey = this.hkdf.expand(this.masterPrk, info, 32) return KeyPair.fromSecretKey(secretKey) diff --git a/packages/libra-web-core-utils/crypto/Hkdf.ts b/packages/libra-web-core-utils/crypto/Hkdf.ts index fed59ab..c19e83e 100644 --- a/packages/libra-web-core-utils/crypto/Hkdf.ts +++ b/packages/libra-web-core-utils/crypto/Hkdf.ts @@ -1,6 +1,7 @@ /* tslint:disable */ const hkdf = require('futoin-hkdf') /* tslint:enable */ +type BuffString = Buffer | string; // Todo: Update implementation to work not only with Node export class Hkdf { @@ -16,7 +17,7 @@ export class Hkdf { return new Uint8Array(prk) } - public expand(prk: Uint8Array, info: string, outputLen: number): Uint8Array { + public expand(prk: Uint8Array, info: BuffString, outputLen: number): Uint8Array { const prkBuffer = Buffer.from(prk) const okm = hkdf.expand(this.hashAlgorithm, this.hashLength, prkBuffer, outputLen, info) return new Uint8Array(okm) diff --git a/packages/libra-web-core-utils/crypto/Pbkdf.ts b/packages/libra-web-core-utils/crypto/Pbkdf.ts index 9a4e283..0e01413 100644 --- a/packages/libra-web-core-utils/crypto/Pbkdf.ts +++ b/packages/libra-web-core-utils/crypto/Pbkdf.ts @@ -1,4 +1,4 @@ -import crypto from 'crypto' +import crypto, {BinaryLike} from 'crypto' export class Pbkdf { private readonly digestAlgorithm: string @@ -9,4 +9,36 @@ export class Pbkdf { public extract(password: Uint8Array, salt: string, iterations: number, outputLen: number) { return new Uint8Array(crypto.pbkdf2Sync(Buffer.from(password), salt, iterations, outputLen, this.digestAlgorithm)) } + + public pbkdf2(password: BinaryLike, salt: Buffer, iterations: number, outputLen: number) { + const hmacLength = crypto.createHmac(this.digestAlgorithm, 'test').digest().length; + const outputBuffer = Buffer.alloc(outputLen); + const hmacOutput = Buffer.alloc(hmacLength); + const block = Buffer.alloc(salt.length + 4); + const leftLength = Math.ceil(outputLen / hmacLength); + const rightLength = outputLen - (leftLength - 1) * hmacLength; + salt.copy(block, 0, 0, salt.length); + for (let i = 1; i <= leftLength; i++) { + block.writeUInt32BE(i, salt.length); + let hmac = crypto + .createHmac(this.digestAlgorithm, password) + .update(block) + .digest(); + hmac.copy(hmacOutput, 0, 0, hmacLength); + for (let j = 1; j < iterations; j++) { + hmac = crypto + .createHmac(this.digestAlgorithm, password) + .update(hmac) + .digest(); + for (let k = 0; k < hmacLength; k++) { + // tslint:disable-next-line:no-bitwise + hmacOutput[k] ^= hmac[k]; + } + } + const destPos = (i - 1) * hmacLength; + const len = i === leftLength ? rightLength : hmacLength; + hmacOutput.copy(outputBuffer, destPos, 0, len); + } + return outputBuffer; + } } diff --git a/test/query.test.ts b/test/query.test.ts index 308fc97..f199004 100644 --- a/test/query.test.ts +++ b/test/query.test.ts @@ -15,7 +15,7 @@ describe('LibraClient.query*()', () => { console.log('User 1 address is', account1Address.toHex()) let account1State = await client.getAccountState(account1Address) - const account2 = wallet.getAccount(0) + const account2 = wallet.getAccount(1) const account2Address = account2.getAddress() console.log('User 2 address is', account2Address.toHex()) let account2State = await client.getAccountState(account2Address)