diff --git a/packages/util-crypto/src/nacl/decrypt.js b/packages/util-crypto/src/nacl/decrypt.js index 827d9d34c..7d04c0316 100644 --- a/packages/util-crypto/src/nacl/decrypt.js +++ b/packages/util-crypto/src/nacl/decrypt.js @@ -7,15 +7,15 @@ const nacl = require('tweetnacl'); /** @name naclDecrypt - @signature naclDecrypt (message: Uint8Array, secretKey: Uint8Array, nonce: Uint8Array): Uint8Array + @signature naclDecrypt (message: Uint8Array, nonce: Uint8Array, secret: Uint8Array): Uint8Array @summary Decrypts a message using the supplied secretKey and nonce @description - Returns an decrypted message, using the `secretKey` and `nonce`. + Returns an decrypted message, using the `secret` and `nonce`. @example import { naclDecrypt } from '@polkadot/util-crypto'; naclDecrypt([...], [...], [...]) // => [...] */ -module.exports = function naclDecrypt (message: Uint8Array, secret: Uint8Array, nonce: Uint8Array): ?Uint8Array { - return nacl.secretbox.open(message, nonce, secret); +module.exports = function naclDecrypt (encrypted: Uint8Array, nonce: Uint8Array, secret: Uint8Array): ?Uint8Array { + return nacl.secretbox.open(encrypted, nonce, secret); }; diff --git a/packages/util-crypto/src/nacl/decrypt.spec.js b/packages/util-crypto/src/nacl/decrypt.spec.js index fae6915f4..10dcc8727 100644 --- a/packages/util-crypto/src/nacl/decrypt.spec.js +++ b/packages/util-crypto/src/nacl/decrypt.spec.js @@ -11,7 +11,7 @@ describe('naclDecrypt', () => { const { encrypted, nonce } = naclEncrypt(message, secret); expect( - naclDecrypt(encrypted, secret, nonce) + naclDecrypt(encrypted, nonce, secret) ).toEqual( message ); diff --git a/packages/util-crypto/src/nacl/encrypt.js b/packages/util-crypto/src/nacl/encrypt.js index 1ed8c67f9..722d1415d 100644 --- a/packages/util-crypto/src/nacl/encrypt.js +++ b/packages/util-crypto/src/nacl/encrypt.js @@ -14,7 +14,7 @@ const randomAsU8a = require('../random/asU8a'); /** @name naclEncrypt - @signature naclEncrypt (message: Uint8Array, secretKey: Uint8Array, nonce?: Uint8Array): { encrypted: Uint8Array, nonce: Uint8Array } + @signature naclEncrypt (message: Uint8Array, secret: Uint8Array, nonce?: Uint8Array): { encrypted: Uint8Array, nonce: Uint8Array } @summary Encrypts a message using the supplied secretKey and nonce @description Returns an encrypted message, using the `secretKey` and `nonce`. If the `nonce` was not supplied, a random value is generated. @@ -24,10 +24,8 @@ const randomAsU8a = require('../random/asU8a'); naclSign([...], [...]) // => [...] */ module.exports = function naclEncrypt (message: Uint8Array, secret: Uint8Array, nonce: Uint8Array = randomAsU8a(24)): Encrypted { - const encrypted = nacl.secretbox(message, nonce, secret); - return { - encrypted, + encrypted: nacl.secretbox(message, nonce, secret), nonce }; }; diff --git a/packages/util-keyring/src/address/decode.js b/packages/util-keyring/src/address/decode.js index 76f9dc6c1..3a46ce178 100644 --- a/packages/util-keyring/src/address/decode.js +++ b/packages/util-keyring/src/address/decode.js @@ -8,10 +8,18 @@ const bs58 = require('bs58'); const assert = require('@polkadot/util/assert'); const bufferToU8a = require('@polkadot/util/buffer/toU8a'); +const isHex = require('@polkadot/util/is/hex'); +const isU8a = require('@polkadot/util/is/u8a'); +const u8aToU8a = require('@polkadot/util/u8a/toU8a'); const blake2b = require('@polkadot/util-crypto/blake2/asU8a512'); -module.exports = function decode (encoded: string): Uint8Array { - const decoded = bufferToU8a(bs58.decode(encoded)); +module.exports = function decode (encoded: string | Uint8Array): Uint8Array { + if (isU8a(encoded) || isHex(encoded)) { + return u8aToU8a(encoded); + } + + // flowlint-next-line unclear-type:off + const decoded = bufferToU8a(bs58.decode(((encoded: any): string))); assert(decoded[0] === 42, 'Invalid decoded address prefix'); assert(decoded.length === 32 + 1 + 2, 'Invalid decoded address length'); diff --git a/packages/util-keyring/src/address/decode.spec.js b/packages/util-keyring/src/address/decode.spec.js index 40958ff72..90af85826 100644 --- a/packages/util-keyring/src/address/decode.spec.js +++ b/packages/util-keyring/src/address/decode.spec.js @@ -15,6 +15,22 @@ describe('decode', () => { ); }); + it('converts a publicKey (u8a) as-is', () => { + expect( + decode(new Uint8Array([1, 2, 3])) + ).toEqual( + new Uint8Array([1, 2, 3]) + ); + }); + + it('converts a publicKey (hex) as-is', () => { + expect( + decode('0x01020304') + ).toEqual( + new Uint8Array([1, 2, 3, 4]) + ); + }); + it('fails when prefix is invalid', () => { expect( () => decode('BXHKtJjKdXjvbvTg6qK275sgcKKq8dnU38MQQAiR3LN2stP') diff --git a/packages/util-keyring/src/decrypt.js b/packages/util-keyring/src/decrypt.js deleted file mode 100644 index c7683b8a4..000000000 --- a/packages/util-keyring/src/decrypt.js +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2017-2018 Jaco Greeff -// This software may be modified and distributed under the terms -// of the ISC license. See the LICENSE file for details. -// @flow - -import type { KeyringPair } from './types'; - -const createPair = require('./pair'); - -module.exports = function decrypt (encoded: Uint8Array, passphrase: Uint8Array | string): KeyringPair { - const pair = createPair({ - publicKey: new Uint8Array(32), - secretKey: new Uint8Array(64) - }); - - pair.decodePkcs8(encoded, passphrase); - - return pair; -}; diff --git a/packages/util-keyring/src/decrypt.spec.js b/packages/util-keyring/src/decrypt.spec.js deleted file mode 100644 index b792aa721..000000000 --- a/packages/util-keyring/src/decrypt.spec.js +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2017-2018 Jaco Greeff -// This software may be modified and distributed under the terms -// of the ISC license. See the LICENSE file for details. - -const u8aFromString = require('@polkadot/util/u8a/fromString'); - -const index = require('./index'); - -describe('decrypt', () => { - const publicKey = new Uint8Array([47, 140, 97, 41, 216, 22, 207, 81, 195, 116, 188, 127, 8, 195, 230, 62, 209, 86, 207, 120, 174, 251, 74, 101, 80, 217, 123, 135, 153, 121, 119, 238]); - const seedOne = u8aFromString('12345678901234567890123456789012'); - let keypair; - let encrypted; - - beforeEach(() => { - keypair = index(); - - keypair.addFromSeed(seedOne); - encrypted = keypair.encrypt(publicKey, 'test'); - - keypair = index(); - }); - - it('decrypts a previously encrypted', () => { - expect( - keypair.decrypt(encrypted, 'test').publicKey() - ).toEqual(publicKey); - }); -}); diff --git a/packages/util-keyring/src/encrypt.js b/packages/util-keyring/src/encrypt.js deleted file mode 100644 index a7bdf8601..000000000 --- a/packages/util-keyring/src/encrypt.js +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright 2017-2018 Jaco Greeff -// This software may be modified and distributed under the terms -// of the ISC license. See the LICENSE file for details. -// @flow - -import type { KeyringPair } from './types'; - -module.exports = function encrypt (pair: KeyringPair, passphrase: Uint8Array | string): Uint8Array { - return pair.encodePkcs8(passphrase); -}; diff --git a/packages/util-keyring/src/encrypt.spec.js b/packages/util-keyring/src/encrypt.spec.js deleted file mode 100644 index 1da925d2e..000000000 --- a/packages/util-keyring/src/encrypt.spec.js +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2017-2018 Jaco Greeff -// This software may be modified and distributed under the terms -// of the ISC license. See the LICENSE file for details. - -const u8aFromString = require('@polkadot/util/u8a/fromString'); -const u8aConcat = require('@polkadot/util/u8a/concat'); - -const index = require('./index'); - -const { PKCS8_DIVIDER, PKCS8_HEADER } = require('./pair/defaults'); - -describe('encrypt', () => { - const publicKey = new Uint8Array([47, 140, 97, 41, 216, 22, 207, 81, 195, 116, 188, 127, 8, 195, 230, 62, 209, 86, 207, 120, 174, 251, 74, 101, 80, 217, 123, 135, 153, 121, 119, 238]); - const seedOne = u8aFromString('12345678901234567890123456789012'); - let keypair; - - beforeEach(() => { - keypair = index(); - - keypair.addFromSeed(seedOne); - }); - - it('encrypts the key', () => { - expect( - keypair.encrypt(publicKey, 'test') - ).toEqual( - u8aConcat( - PKCS8_HEADER, seedOne, PKCS8_DIVIDER, publicKey - ) - ); - }); - - it('returns throws for non-existing', () => { - expect( - () => keypair.encrypt(new Uint8Array([]), 'test') - ).toThrow(/Unable to retrieve/); - }); -}); diff --git a/packages/util-keyring/src/index.js b/packages/util-keyring/src/index.js index 2c3c58c68..107061488 100644 --- a/packages/util-keyring/src/index.js +++ b/packages/util-keyring/src/index.js @@ -3,34 +3,34 @@ // of the ISC license. See the LICENSE file for details. // @flow -import type { KeyringInstance, KeyringPair } from './types'; +import type { KeyringInstance, KeyringPair, KeyringPair$Json, KeyringPair$Meta } from './types'; const naclKeypairFromSeed = require('@polkadot/util-crypto/nacl/keypair/fromSeed'); +const hexToU8a = require('@polkadot/util/hex/toU8a'); const addressDecode = require('./address/decode'); -const decrypt = require('./decrypt'); -const encrypt = require('./encrypt'); const createPair = require('./pair'); const createPairs = require('./pairs'); module.exports = function keyring (): KeyringInstance { const pairs = createPairs(); + const addFromAddress = (address: string | Uint8Array, meta?: KeyringPair$Meta, defaultEncoded?: Uint8Array) => + pairs.add(createPair({ publicKey: addressDecode(address) }, meta, defaultEncoded)); return { - addFromSeed: (seed: Uint8Array): KeyringPair => - pairs.add(createPair(naclKeypairFromSeed(seed))), - decrypt: (encrypted: Uint8Array, passphrase: Uint8Array | string): KeyringPair => - pairs.add(decrypt(encrypted, passphrase)), - encrypt: (publicKey: Uint8Array, passphrase: Uint8Array | string): Uint8Array => - encrypt(pairs.get(publicKey), passphrase), - getAddress: (address: string): KeyringPair => - pairs.get( - addressDecode(address) - ), - getPair: (publicKey: Uint8Array): KeyringPair => - pairs.get(publicKey), + addFromAddress, + addFromJson: ({ address, encoded, meta }: KeyringPair$Json): KeyringPair => + addFromAddress(address, meta, hexToU8a(encoded)), + addFromSeed: (seed: Uint8Array, meta?: KeyringPair$Meta): KeyringPair => + pairs.add(createPair(naclKeypairFromSeed(seed), meta)), + getPair: (address: string | Uint8Array): KeyringPair => + pairs.get(address), getPairs: pairs.all, getPublicKeys: (): Array => - pairs.all().map(({ publicKey }) => publicKey()) + pairs.all().map(({ publicKey }) => publicKey()), + removePair: (address: string | Uint8Array): void => + pairs.remove(address), + toJson: (address: string | Uint8Array, passphrase?: string): KeyringPair$Json => + pairs.get(address).toJson(passphrase) }; }; diff --git a/packages/util-keyring/src/index.spec.js b/packages/util-keyring/src/index.spec.js index 664993562..c6f3659f8 100644 --- a/packages/util-keyring/src/index.spec.js +++ b/packages/util-keyring/src/index.spec.js @@ -39,4 +39,14 @@ describe('keypair', () => { keypair.getPair(publicKeyOne).publicKey() ).toEqual(publicKeyOne); }); + + it('allows adding from JSON', () => { + expect( + keypair.addFromJson( + JSON.parse('{"address":"5GoKvZWG5ZPYL1WUovuHW3zJBWBP5eT8CbqjdRY4Q6iMaDtZ","encoded":"0xb4a14995d25ab609f3686e9fa45f1fb237cd833f33f00d4b12c51858ca070d96972e47d73aae5eeb0fc06f923826cf0943fdb02c2c2ee30ef52a7912663053940d1da4da66b3a3f520ae07422c1c94b2d95690fca9d1f4a997623bb2923a8833280e19e7f72c3c5cfa343974e60e2b3dc53b404fdaf330756daad5e4e3","encoding":{"content":"pkcs8","type":"xsalsa20-poly1305","version":"0"},"meta":{"isTesting":true,"name":"alice"}}') + ).publicKey() + ).toEqual( + new Uint8Array([209, 114, 167, 76, 218, 76, 134, 89, 18, 195, 43, 160, 168, 10, 87, 174, 105, 171, 174, 65, 14, 92, 203, 89, 222, 232, 78, 47, 68, 50, 219, 79]) + ); + }); }); diff --git a/packages/util-keyring/src/pair/decode.js b/packages/util-keyring/src/pair/decode.js index adbd15671..e99fdc171 100644 --- a/packages/util-keyring/src/pair/decode.js +++ b/packages/util-keyring/src/pair/decode.js @@ -4,23 +4,44 @@ // @flow const u8aConcat = require('@polkadot/util/u8a/concat'); +const u8aFixLength = require('@polkadot/util/u8a/fixLength'); +const u8aFromString = require('@polkadot/util/u8a/fromString'); const assert = require('@polkadot/util/assert'); +const naclDecrypt = require('@polkadot/util-crypto/nacl/decrypt'); const naclFromSeed = require('@polkadot/util-crypto/nacl/keypair/fromSeed'); const { PKCS8_DIVIDER, PKCS8_HEADER } = require('./defaults'); const KEY_LENGTH = 32; const SEED_OFFSET = PKCS8_HEADER.length; +const DIV_OFFSET = SEED_OFFSET + KEY_LENGTH; const PUBLIC_OFFSET = SEED_OFFSET + KEY_LENGTH + PKCS8_DIVIDER.length; -module.exports = function decode (encoded: Uint8Array, passphrase?: Uint8Array | string) { - // TODO: decrypt using passphrase +module.exports = function decode (_encrypted?: Uint8Array, passphrase?: string) { + assert(_encrypted, `No encrypted data available to decode`); + + // flowlint-next-line unclear-type:off + const encrypted = ((_encrypted: any): Uint8Array); + // flowlint-next-line sketchy-null-string:off + const encoded = passphrase + // flowlint-next-line unclear-type:off + ? ((naclDecrypt(encrypted.subarray(24), encrypted.subarray(0, 24), u8aFixLength(u8aFromString(passphrase), 256, true)): any): Uint8Array) + : encrypted; + + assert(encoded, `Unable to unencrypt using the supplied passphrase`); + + const header = encoded.subarray(0, PKCS8_HEADER.length); + const divider = encoded.subarray(DIV_OFFSET, DIV_OFFSET + PKCS8_DIVIDER.length); + + assert(header.toString() === PKCS8_HEADER.toString(), 'Invalid Pkcs8 header found in body'); + assert(divider.toString() === PKCS8_DIVIDER.toString(), 'Invalid Pkcs8 divider found in body'); + const publicKey = encoded.subarray(PUBLIC_OFFSET, PUBLIC_OFFSET + KEY_LENGTH); const seed = encoded.subarray(SEED_OFFSET, SEED_OFFSET + KEY_LENGTH); const secretKey = u8aConcat(seed, publicKey); const validate = naclFromSeed(seed); - assert(validate.publicKey.toString() === publicKey.toString(), 'Pkcs8 decoded keys are not matching'); + assert(validate.publicKey.toString() === publicKey.toString(), 'Pkcs8 decoded publicKeys are not matching'); return { publicKey, diff --git a/packages/util-keyring/src/pair/decode.spec.js b/packages/util-keyring/src/pair/decode.spec.js new file mode 100644 index 000000000..10e37c57b --- /dev/null +++ b/packages/util-keyring/src/pair/decode.spec.js @@ -0,0 +1,21 @@ +// Copyright 2017-2018 Jaco Greeff +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. + +const keyring = require('../testingPairs')(); + +describe('decode', () => { + it('fails when no data provided', () => { + expect( + () => keyring.alice.decodePkcs8() + ).toThrow(/No encrypted data available/); + }); + + it('returns correct publicKey from encoded', () => { + expect( + () => keyring.alice.decodePkcs8( + keyring.alice.encodePkcs8('testing'), 'testing' + ) + ).not.toThrow(); + }); +}); diff --git a/packages/util-keyring/src/pair/encode.js b/packages/util-keyring/src/pair/encode.js index 83f0eff8a..ecd247280 100644 --- a/packages/util-keyring/src/pair/encode.js +++ b/packages/util-keyring/src/pair/encode.js @@ -4,16 +4,26 @@ // @flow const u8aConcat = require('@polkadot/util/u8a/concat'); +const u8aFixLength = require('@polkadot/util/u8a/fixLength'); +const u8aFromString = require('@polkadot/util/u8a/fromString'); +const naclEncrypt = require('@polkadot/util-crypto/nacl/encrypt'); const { PKCS8_DIVIDER, PKCS8_HEADER } = require('./defaults'); -module.exports = function encode (secretKey: Uint8Array, passphrase?: Uint8Array | string): Uint8Array { - // TODO: encrypt using passphrase - - return u8aConcat( +module.exports = function encode (secretKey: Uint8Array, passphrase?: string): Uint8Array { + const encoded = u8aConcat( PKCS8_HEADER, secretKey.subarray(0, 32), PKCS8_DIVIDER, secretKey.subarray(32, 64) ); + + // flowlint-next-line sketchy-null-string:off + if (!passphrase) { + return encoded; + } + + const { encrypted, nonce } = naclEncrypt(encoded, u8aFixLength(u8aFromString(passphrase), 256, true)); + + return u8aConcat(nonce, encrypted); }; diff --git a/packages/util-keyring/src/pair/encode.spec.js b/packages/util-keyring/src/pair/encode.spec.js new file mode 100644 index 000000000..f93d6498c --- /dev/null +++ b/packages/util-keyring/src/pair/encode.spec.js @@ -0,0 +1,23 @@ +// Copyright 2017-2018 Jaco Greeff +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. + +const keyring = require('../testingPairs')(); +const { PKCS8_DIVIDER, PKCS8_HEADER } = require('./defaults'); + +const PKCS8_LENGTH = PKCS8_DIVIDER.length + PKCS8_HEADER.length + 64; +const ENCODED_LENGTH = 125; + +describe('encode', () => { + it('returns PKCS8 when no passphrase supplied', () => { + expect( + keyring.alice.encodePkcs8() + ).toHaveLength(PKCS8_LENGTH); + }); + + it('returns encoded PKCS8 when passphrase supplied', () => { + expect( + keyring.alice.encodePkcs8('testing') + ).toHaveLength(ENCODED_LENGTH); + }); +}); diff --git a/packages/util-keyring/src/pair/index.js b/packages/util-keyring/src/pair/index.js index f22e9ed92..8d6222d0e 100644 --- a/packages/util-keyring/src/pair/index.js +++ b/packages/util-keyring/src/pair/index.js @@ -4,7 +4,7 @@ // @flow import type { KeypairType } from '@polkadot/util-crypto/types'; -import type { KeyringPair, KeyringPair$Meta } from '../types'; +import type { KeyringPair, KeyringPair$Json, KeyringPair$Meta } from '../types'; import type { PairState } from './types'; const naclSign = require('@polkadot/util-crypto/nacl/sign'); @@ -15,33 +15,38 @@ const decode = require('./decode'); const encode = require('./encode'); const getMeta = require('./getMeta'); const setMeta = require('./setMeta'); +const toJson = require('./toJson'); -module.exports = function pair ({ publicKey, secretKey }: KeypairType): KeyringPair { +module.exports = function pair ({ publicKey, secretKey }: $Shape, meta?: KeyringPair$Meta = {}, defaultEncoded?: Uint8Array): KeyringPair { const state: PairState = { address: encodeAddress(publicKey), - meta: {} + meta: { ...meta } }; return { address: (): string => state.address, - decodePkcs8: (encoded: Uint8Array, passphrase?: Uint8Array | string): void => { - const decoded = decode(encoded, passphrase); + decodePkcs8: (encoded?: Uint8Array, passphrase?: string): void => { + const decoded = decode(encoded || defaultEncoded, passphrase); publicKey = decoded.publicKey; secretKey = decoded.secretKey; state.address = encodeAddress(publicKey); }, - encodePkcs8: (passphrase?: Uint8Array | string): Uint8Array => + encodePkcs8: (passphrase?: string): Uint8Array => encode(secretKey, passphrase), getMeta: (): KeyringPair$Meta => getMeta(state), + hasSecretKey: (): boolean => + !!(secretKey && secretKey.length !== 0), publicKey: (): Uint8Array => publicKey, setMeta: (meta: KeyringPair$Meta): void => setMeta(state, meta), sign: (message: Uint8Array): Uint8Array => naclSign(message, secretKey), + toJson: (passphrase?: string): KeyringPair$Json => + toJson(state, encode(secretKey, passphrase), !!passphrase), verify: (message: Uint8Array, signature: Uint8Array): boolean => naclVerify(message, signature, publicKey) }; diff --git a/packages/util-keyring/src/pair/setMeta.js b/packages/util-keyring/src/pair/setMeta.js index e35613c99..59354f921 100644 --- a/packages/util-keyring/src/pair/setMeta.js +++ b/packages/util-keyring/src/pair/setMeta.js @@ -7,5 +7,8 @@ import type { KeyringPair$Meta } from '../types'; import type { PairState } from './types'; module.exports = function setMeta (state: PairState, meta: KeyringPair$Meta): void { - state.meta = meta; + state.meta = { + ...state.meta, + ...meta + }; }; diff --git a/packages/util-keyring/src/pair/toJson.js b/packages/util-keyring/src/pair/toJson.js new file mode 100644 index 000000000..8573bd33d --- /dev/null +++ b/packages/util-keyring/src/pair/toJson.js @@ -0,0 +1,26 @@ +// Copyright 2017-2018 Jaco Greeff +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. +// @flow + +import type { KeyringPair$Json } from '../types'; +import type { PairState } from './types'; + +const u8aToHex = require('@polkadot/util/u8a/toHex'); + +module.exports = function toJson ({ address, meta }: PairState, encoded: Uint8Array, isEncrypted: boolean): KeyringPair$Json { + return { + address, + encoded: u8aToHex(encoded), + encoding: { + content: 'pkcs8', + type: isEncrypted + ? 'xsalsa20-poly1305' + : 'none', + version: '0' + }, + meta: { + ...meta + } + }; +}; diff --git a/packages/util-keyring/src/pair/toJson.spec.js b/packages/util-keyring/src/pair/toJson.spec.js new file mode 100644 index 000000000..8c7b34841 --- /dev/null +++ b/packages/util-keyring/src/pair/toJson.spec.js @@ -0,0 +1,39 @@ +// Copyright 2017-2018 Jaco Greeff +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. + +const keyring = require('../testingPairs')(); + +describe('toJson', () => { + it('creates an unencoded output with no passphrase', () => { + expect( + keyring.alice.toJson() + ).toMatchObject({ + address: '5GoKvZWG5ZPYL1WUovuHW3zJBWBP5eT8CbqjdRY4Q6iMaDtZ', + encoded: '0x3053020101300506032b657004220420416c696365202020202020202020202020202020202020202020202020202020a123032100d172a74cda4c865912c32ba0a80a57ae69abae410e5ccb59dee84e2f4432db4f', + encoding: { + content: 'pkcs8', + type: 'none', + version: '0' + }, + meta: { + name: 'alice', + isTesting: true + } + }); + }); + + it('creates an encoded output with passphrase', () => { + const json = keyring.alice.toJson('testing'); + + expect(json.encoded).toHaveLength(252); + expect(json).toMatchObject({ + address: '5GoKvZWG5ZPYL1WUovuHW3zJBWBP5eT8CbqjdRY4Q6iMaDtZ', + encoding: { + content: 'pkcs8', + type: 'xsalsa20-poly1305', + version: '0' + } + }); + }); +}); diff --git a/packages/util-keyring/src/pairs.js b/packages/util-keyring/src/pairs.js index a8ccd001e..985d728fc 100644 --- a/packages/util-keyring/src/pairs.js +++ b/packages/util-keyring/src/pairs.js @@ -10,7 +10,12 @@ type KeyringPairMap = { }; const assert = require('@polkadot/util/assert'); +const isHex = require('@polkadot/util/is/hex'); +const isU8a = require('@polkadot/util/is/u8a'); const u8aToHex = require('@polkadot/util/u8a/toHex'); +const u8aToU8a = require('@polkadot/util/u8a/toU8a'); + +const addressDecode = require('./address/decode'); module.exports = function pairs (): KeyringPairs { const self: KeyringPairMap = {}; @@ -24,12 +29,22 @@ module.exports = function pairs (): KeyringPairs { all: (): Array => // flowlint-next-line unclear-type:off ((Object.values(self): any): Array), - get: (publicKey: Uint8Array): KeyringPair => { - const pair = self[publicKey]; + get: (address: string | Uint8Array): KeyringPair => { + const pair = self[addressDecode(address)]; - assert(pair, `Unable to retrieve keypair for publicKey '${u8aToHex(publicKey)}'`); + assert(pair, () => { + const formatted: string = isU8a(address) || isHex(address) + ? u8aToHex(u8aToU8a(address)) + // flowlint-next-line unclear-type:off + : ((address: any): string); + + return `Unable to retrieve keypair '${formatted}'`; + }); return pair; + }, + remove: (address: string | Uint8Array): void => { + delete self[addressDecode(address)]; } }; }; diff --git a/packages/util-keyring/src/testing.js b/packages/util-keyring/src/testing.js index 64c721304..c265537ac 100644 --- a/packages/util-keyring/src/testing.js +++ b/packages/util-keyring/src/testing.js @@ -36,11 +36,14 @@ const SEEDS = { module.exports = function testKeyring (): KeyringInstance { const keyring = createKeyring(); - Object.keys(SEEDS).forEach((name) => { - keyring - .addFromSeed(SEEDS[name]) - .setMeta({ name }); - }); + Object + .keys(SEEDS) + .forEach((name) => { + keyring.addFromSeed(SEEDS[name], { + isTesting: true, + name + }); + }); return keyring; }; diff --git a/packages/util-keyring/src/types.js b/packages/util-keyring/src/types.js index 00e840864..9e67a9dc2 100644 --- a/packages/util-keyring/src/types.js +++ b/packages/util-keyring/src/types.js @@ -8,29 +8,42 @@ export type KeyringPair$Meta = { [string]: any } +export type KeyringPair$Json = { + address: string, + encoded: string, + encoding: { + content: 'pkcs8', + type: 'xsalsa20-poly1305' | 'none', + version: '0' + }, + meta: KeyringPair$Meta +}; + export type KeyringPair = { address: () => string, - decodePkcs8: (encoded: Uint8Array, passphrase: Uint8Array | string) => void, - encodePkcs8: (passphrase: Uint8Array | string) => Uint8Array, + decodePkcs8: (encoded: Uint8Array, passphrase?: string) => void, + encodePkcs8: (passphrase?: string) => Uint8Array, getMeta: () => KeyringPair$Meta, + hasSecretKey: () => boolean, publicKey: () => Uint8Array, setMeta: (meta: KeyringPair$Meta) => void, sign (message: Uint8Array): Uint8Array, + toJson (passphrase?: string): KeyringPair$Json, verify (message: Uint8Array, signature: Uint8Array): boolean }; export type KeyringPairs = { add: (pair: KeyringPair) => KeyringPair, all: () => Array, - get: (publicKey: Uint8Array) => KeyringPair + get: (address: string | Uint8Array) => KeyringPair, + remove: (address: string | Uint8Array) => void }; export type KeyringInstance = { - addFromSeed (seed: Uint8Array): KeyringPair, - decrypt (encrypted: Uint8Array, secret: Uint8Array | string): KeyringPair, - encrypt (publicKey: Uint8Array, secret: Uint8Array | string): Uint8Array, - getAddress (address: string): KeyringPair, - getPair (publicKey: Uint8Array): KeyringPair, + addFromAddress (address: string | Uint8Array, meta?: KeyringPair$Meta): KeyringPair, + addFromSeed (seed: Uint8Array, meta?: KeyringPair$Meta): KeyringPair, + addFromJson (pair: KeyringPair$Json): KeyringPair, + getPair (address: string | Uint8Array): KeyringPair, getPairs (): Array, getPublicKeys (): Array }; diff --git a/packages/util/package.json b/packages/util/package.json index d1c64416b..1fb96003a 100644 --- a/packages/util/package.json +++ b/packages/util/package.json @@ -29,7 +29,7 @@ "test": "echo \"Tests only available from root wrapper\"" }, "devDependencies": { - "@polkadot/api-jsonrpc": "^0.9.15" + "@polkadot/api-jsonrpc": "^0.10.2" }, "dependencies": { "babel-runtime": "^6.26.0", diff --git a/packages/util/src/is/hex.js b/packages/util/src/is/hex.js index 3fc5850c5..46c90e685 100644 --- a/packages/util/src/is/hex.js +++ b/packages/util/src/is/hex.js @@ -9,7 +9,7 @@ const HEX_REGEX = /^0x[a-fA-F0-9]+$/; /** @name isHex - @signature isHex (value: string, bitLength: number = -1): boolean + @signature isHex (value: mixed, bitLength: number = -1): boolean @summary Tests for a hex string. @description Checks to see if the input value is a `0x` prefixed hex string. Optionally (`bitLength` !== -1) checks to see if the bitLength is correct. @@ -19,10 +19,12 @@ const HEX_REGEX = /^0x[a-fA-F0-9]+$/; isHex('0x1234'); // => true isHex('0x1234', 8); // => false */ -module.exports = function isHex (value: string, bitLength: number = -1): boolean { +module.exports = function isHex (value: mixed, bitLength: number = -1): boolean { + // $FlowFixMe for the regex we have a string const isValidHex = value === '0x' || (isString(value) && HEX_REGEX.test(value)); if (isValidHex && bitLength !== -1) { + // $FlowFixMe type is a string as checked above return value.length === (2 + bitLength / 4); } diff --git a/packages/util/src/u8a/fixLength.js b/packages/util/src/u8a/fixLength.js index 5cba61fe9..745166d0a 100644 --- a/packages/util/src/u8a/fixLength.js +++ b/packages/util/src/u8a/fixLength.js @@ -16,7 +16,7 @@ u8aFixLength('0x12', 16) // => 0x0012 u8aFixLength('0x1234', 8) // => 0x12 */ -module.exports = function u8aFixLength (value: Uint8Array, bitLength: number = -1): Uint8Array { +module.exports = function u8aFixLength (value: Uint8Array, bitLength: number = -1, atStart?: boolean = false): Uint8Array { const byteLength = Math.ceil(bitLength / 8); if (bitLength === -1 || value.length === byteLength) { @@ -29,7 +29,11 @@ module.exports = function u8aFixLength (value: Uint8Array, bitLength: number = - const result = new Uint8Array(byteLength); - result.set(value, byteLength - value.length); + if (atStart) { + result.set(value, 0); + } else { + result.set(value, byteLength - value.length); + } return result; }; diff --git a/yarn.lock b/yarn.lock index e2e85040f..e9c8c0f41 100644 --- a/yarn.lock +++ b/yarn.lock @@ -79,9 +79,9 @@ lodash "^4.2.0" to-fast-properties "^2.0.0" -"@polkadot/api-jsonrpc@^0.9.15": - version "0.9.15" - resolved "https://registry.yarnpkg.com/@polkadot/api-jsonrpc/-/api-jsonrpc-0.9.15.tgz#e210f5ce82d7c41c11ddf3cd83e521ede9f1ddae" +"@polkadot/api-jsonrpc@^0.10.2": + version "0.10.2" + resolved "https://registry.yarnpkg.com/@polkadot/api-jsonrpc/-/api-jsonrpc-0.10.2.tgz#55836832ee3e48437edfabeca3e8f5fd20367f13" dependencies: babel-runtime "^6.26.0"