Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b56e40ea5b | ||
|
|
37d1bcb6eb | ||
|
|
f2b8c1fb4e | ||
|
|
92cc8fc4e3 | ||
|
|
701bd9335d | ||
|
|
d176c74715 | ||
|
|
0bf7c799f3 | ||
|
|
2f7d5cd4e1 | ||
|
|
c42b517293 | ||
|
|
e5dd55e419 | ||
|
|
83fdd11c59 | ||
|
|
08de8ce25a | ||
|
|
800bafc00c | ||
|
|
3b383b97ca | ||
|
|
b381c09ecf | ||
|
|
a53008fd67 | ||
|
|
899056c5a2 | ||
|
|
8554d47030 |
@@ -7,5 +7,10 @@ module.exports = {
|
|||||||
project: [
|
project: [
|
||||||
'./tsconfig.json'
|
'./tsconfig.json'
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
...base.rules,
|
||||||
|
// this seems very broken atm, false positives
|
||||||
|
'@typescript-eslint/unbound-method': 'off'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
+1
-1
@@ -8,4 +8,4 @@ plugins:
|
|||||||
- path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.js
|
- path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.js
|
||||||
spec: "@yarnpkg/plugin-interactive-tools"
|
spec: "@yarnpkg/plugin-interactive-tools"
|
||||||
|
|
||||||
yarnPath: .yarn/releases/yarn-2.1.1.js
|
yarnPath: .yarn/releases/yarn-2.1.1.cjs
|
||||||
|
|||||||
@@ -1,5 +1,16 @@
|
|||||||
# CHANGELOG
|
# CHANGELOG
|
||||||
|
|
||||||
|
## 3.4.1 Aug 31, 2020
|
||||||
|
|
||||||
|
- Allow for WASM-as-available `mnemonicToBip39` call (used for Ethereum-compatible keypairs)
|
||||||
|
- Add `isWasm` utility to check for valid WASM headers
|
||||||
|
- `@polkadot/wasm-crypto` 1.4.1
|
||||||
|
|
||||||
|
## 3.3.1 Aug 24, 2020
|
||||||
|
|
||||||
|
- Add `addressRaw` to Keyring interface (if address encoding is different to publicKey)
|
||||||
|
- Allow for Ethereum pair signing and verification
|
||||||
|
|
||||||
## 3.2.1 Aug 17, 2020
|
## 3.2.1 Aug 17, 2020
|
||||||
|
|
||||||
- Allow for (optional, default blake2) hasher to secp256k1 sign/verify functions
|
- Allow for (optional, default blake2) hasher to secp256k1 sign/verify functions
|
||||||
|
|||||||
+1
-1
@@ -9,5 +9,5 @@
|
|||||||
"packages": [
|
"packages": [
|
||||||
"packages/*"
|
"packages/*"
|
||||||
],
|
],
|
||||||
"version": "3.2.1"
|
"version": "3.4.1"
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-5
@@ -21,10 +21,13 @@
|
|||||||
"test:one": "polkadot-dev-run-test"
|
"test:one": "polkadot-dev-run-test"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.11.1",
|
"@babel/core": "^7.11.4",
|
||||||
"@polkadot/dev": "^0.55.38",
|
"@polkadot/dev": "^0.55.43",
|
||||||
"@polkadot/ts": "^0.3.38",
|
"@polkadot/ts": "^0.3.40",
|
||||||
"@types/jest": "^26.0.9"
|
"@types/jest": "^26.0.10",
|
||||||
|
"typedoc": "^0.18.0",
|
||||||
|
"typedoc-plugin-markdown": "^2.4.2",
|
||||||
|
"typedoc-plugin-no-inherit": "^1.1.10"
|
||||||
},
|
},
|
||||||
"version": "3.2.1"
|
"version": "3.4.1"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@polkadot/keyring",
|
"name": "@polkadot/keyring",
|
||||||
"version": "3.2.1",
|
"version": "3.4.1",
|
||||||
"description": "Keyring management",
|
"description": "Keyring management",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
@@ -28,7 +28,7 @@
|
|||||||
"homepage": "https://github.com/polkadot-js/common/tree/master/packages/keyring#readme",
|
"homepage": "https://github.com/polkadot-js/common/tree/master/packages/keyring#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.11.2",
|
"@babel/runtime": "^7.11.2",
|
||||||
"@polkadot/util": "3.2.1",
|
"@polkadot/util": "3.4.1",
|
||||||
"@polkadot/util-crypto": "3.2.1"
|
"@polkadot/util-crypto": "3.4.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -228,13 +228,43 @@ describe('keypair', (): void => {
|
|||||||
expect(pair.address).toEqual('0x4119b2e6c3Cb618F4f0B93ac77f9BeeC7FF02887');
|
expect(pair.address).toEqual('0x4119b2e6c3Cb618F4f0B93ac77f9BeeC7FF02887');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('encodes a pair toJSON', (): void => {
|
||||||
|
const pair = keyring.createFromUri(PHRASE);
|
||||||
|
const json = pair.toJson('password');
|
||||||
|
|
||||||
|
expect(json.address).toEqual('0x03b9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb13077');
|
||||||
|
expect(json.encoding).toEqual({
|
||||||
|
content: ['pkcs8', 'ethereum'],
|
||||||
|
type: ['scrypt', 'xsalsa20-poly1305'],
|
||||||
|
version: '3'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('allows adding from JSON', (): void => {
|
it('allows adding from JSON', (): void => {
|
||||||
const pair = keyring.addFromJson(
|
const pair = keyring.addFromJson(
|
||||||
JSON.parse('{"address":"34wtVv7QQ5NsJQDcyWYnz9J5XrNgYE3VcRfKQ6scSt7o1ixi","encoded":"U8qFEaghhmNV2PgFhjqzmhyUy37Ok7abfFU2MNsBd0sAgAAAAQAAAAgAAAA3+NniKogzNphiMNueB1X0sGA07B6CaXWfpXPx45iSXoTTprwzU5mOoSqUWO0GKHROI72LN+uJ8Yfv6Ll6JOOV3VPKfoVoFmYm+zDrrMPa0gk5E5kUuSijxADcE6zUrliPVr0Ix/qaghu5SJ7RtWDQLBf4Hp86SJ8Gg6gTSSk=","encoding":{"content":["pkcs8","ethereum"],"type":["scrypt","xsalsa20-poly1305"],"version":"3"},"meta":{}}')
|
JSON.parse('{"address":"KWCv1L3QX9LDPwY4VzvLmarEmXjVJidUzZcinvVnmxAJJCBou","encoded":"U8qFEaghhmNV2PgFhjqzmhyUy37Ok7abfFU2MNsBd0sAgAAAAQAAAAgAAAA3+NniKogzNphiMNueB1X0sGA07B6CaXWfpXPx45iSXoTTprwzU5mOoSqUWO0GKHROI72LN+uJ8Yfv6Ll6JOOV3VPKfoVoFmYm+zDrrMPa0gk5E5kUuSijxADcE6zUrliPVr0Ix/qaghu5SJ7RtWDQLBf4Hp86SJ8Gg6gTSSk=","encoding":{"content":["pkcs8","ethereum"],"type":["scrypt","xsalsa20-poly1305"],"version":"3"},"meta":{}}')
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(pair.publicKey).toEqual(hexToU8a('0xb9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb13077'));
|
expect(pair.publicKey).toEqual(hexToU8a('0x03b9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb13077'));
|
||||||
expect(pair.address).toEqual('0x4119b2e6c3Cb618F4f0B93ac77f9BeeC7FF02887');
|
expect(pair.address).toEqual('0x4119b2e6c3Cb618F4f0B93ac77f9BeeC7FF02887');
|
||||||
|
|
||||||
|
pair.decodePkcs8('password');
|
||||||
|
|
||||||
|
expect(pair.isLocked).toBe(false);
|
||||||
|
expect(pair.publicKey).toEqual(hexToU8a('0x03b9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb13077'));
|
||||||
|
expect(pair.address).toBe('0x4119b2e6c3Cb618F4f0B93ac77f9BeeC7FF02887');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('allows for signing/verification', (): void => {
|
||||||
|
const MESSAGE = stringToU8a('just some test message');
|
||||||
|
const signer = keyring.createFromUri(PHRASE);
|
||||||
|
const verifier = keyring.addFromJson(
|
||||||
|
JSON.parse('{"address":"KWCv1L3QX9LDPwY4VzvLmarEmXjVJidUzZcinvVnmxAJJCBou","encoded":"U8qFEaghhmNV2PgFhjqzmhyUy37Ok7abfFU2MNsBd0sAgAAAAQAAAAgAAAA3+NniKogzNphiMNueB1X0sGA07B6CaXWfpXPx45iSXoTTprwzU5mOoSqUWO0GKHROI72LN+uJ8Yfv6Ll6JOOV3VPKfoVoFmYm+zDrrMPa0gk5E5kUuSijxADcE6zUrliPVr0Ix/qaghu5SJ7RtWDQLBf4Hp86SJ8Gg6gTSSk=","encoding":{"content":["pkcs8","ethereum"],"type":["scrypt","xsalsa20-poly1305"],"version":"3"},"meta":{}}')
|
||||||
|
);
|
||||||
|
|
||||||
|
const signature = signer.sign(MESSAGE);
|
||||||
|
|
||||||
|
expect(verifier.verify(MESSAGE, signature)).toBe(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { PairInfo } from './types';
|
|||||||
import { assert, stringToU8a, u8aFixLength } from '@polkadot/util';
|
import { assert, stringToU8a, u8aFixLength } from '@polkadot/util';
|
||||||
import { naclDecrypt, scryptEncode, scryptFromU8a } from '@polkadot/util-crypto';
|
import { naclDecrypt, scryptEncode, scryptFromU8a } from '@polkadot/util-crypto';
|
||||||
|
|
||||||
import { NONCE_LENGTH, PKCS8_DIVIDER, PKCS8_HEADER, PUB_LENGTH, SEC_LENGTH, SEED_LENGTH, SCRYPT_LENGTH } from './defaults';
|
import { ENCODING, NONCE_LENGTH, PKCS8_DIVIDER, PKCS8_HEADER, PUB_LENGTH, SEC_LENGTH, SEED_LENGTH, SCRYPT_LENGTH } from './defaults';
|
||||||
|
|
||||||
const SEED_OFFSET = PKCS8_HEADER.length;
|
const SEED_OFFSET = PKCS8_HEADER.length;
|
||||||
|
|
||||||
@@ -43,7 +43,7 @@ function decodePkcs8 (encoded: Uint8Array): DecodeResult {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function decode (passphrase?: string, encrypted?: Uint8Array | null, encType: KeyringPair$JsonEncodingTypes[] = ['scrypt', 'xsalsa20-poly1305']): DecodeResult {
|
export default function decode (passphrase?: string, encrypted?: Uint8Array | null, encType: KeyringPair$JsonEncodingTypes[] = ENCODING): DecodeResult {
|
||||||
assert(encrypted, 'No encrypted data available to decode');
|
assert(encrypted, 'No encrypted data available to decode');
|
||||||
assert(passphrase || !encType.includes('xsalsa20-poly1305'), 'Password required to decode encypted data');
|
assert(passphrase || !encType.includes('xsalsa20-poly1305'), 'Password required to decode encypted data');
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,9 @@
|
|||||||
// This software may be modified and distributed under the terms
|
// This software may be modified and distributed under the terms
|
||||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||||
|
|
||||||
|
import { KeyringPair$JsonEncodingTypes } from '../types';
|
||||||
|
|
||||||
|
const ENCODING: KeyringPair$JsonEncodingTypes[] = ['scrypt', 'xsalsa20-poly1305'];
|
||||||
const NONCE_LENGTH = 24;
|
const NONCE_LENGTH = 24;
|
||||||
const PKCS8_DIVIDER = new Uint8Array([161, 35, 3, 33, 0]);
|
const PKCS8_DIVIDER = new Uint8Array([161, 35, 3, 33, 0]);
|
||||||
const PKCS8_HEADER = new Uint8Array([48, 83, 2, 1, 1, 48, 5, 6, 3, 43, 101, 112, 4, 34, 4, 32]);
|
const PKCS8_HEADER = new Uint8Array([48, 83, 2, 1, 1, 48, 5, 6, 3, 43, 101, 112, 4, 34, 4, 32]);
|
||||||
@@ -12,6 +15,7 @@ const SEED_LENGTH = 32;
|
|||||||
const SCRYPT_LENGTH = SALT_LENGTH + (3 * 4);
|
const SCRYPT_LENGTH = SALT_LENGTH + (3 * 4);
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
ENCODING,
|
||||||
NONCE_LENGTH,
|
NONCE_LENGTH,
|
||||||
PKCS8_DIVIDER,
|
PKCS8_DIVIDER,
|
||||||
PKCS8_HEADER,
|
PKCS8_HEADER,
|
||||||
|
|||||||
@@ -25,6 +25,11 @@ describe('pair', (): void => {
|
|||||||
).toEqual(
|
).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])
|
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])
|
||||||
);
|
);
|
||||||
|
expect(
|
||||||
|
keyring.alice.addressRaw
|
||||||
|
).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])
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('allows signing', (): void => {
|
it('allows signing', (): void => {
|
||||||
@@ -111,6 +116,7 @@ describe('pair', (): void => {
|
|||||||
const pair = createPair({ toSS58, type: 'ethereum' }, { publicKey: hexToU8a('0x03b9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb13077') });
|
const pair = createPair({ toSS58, type: 'ethereum' }, { publicKey: hexToU8a('0x03b9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb13077') });
|
||||||
|
|
||||||
expect(pair.address).toEqual('0x4119b2e6c3Cb618F4f0B93ac77f9BeeC7FF02887');
|
expect(pair.address).toEqual('0x4119b2e6c3Cb618F4f0B93ac77f9BeeC7FF02887');
|
||||||
|
expect(pair.addressRaw).toEqual(hexToU8a('0x4119b2e6c3Cb618F4f0B93ac77f9BeeC7FF02887'));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ import { Keypair, KeypairType } from '@polkadot/util-crypto/types';
|
|||||||
import { KeyringPair, KeyringPair$Json, KeyringPair$JsonEncodingTypes, KeyringPair$Meta, SignOptions } from '../types';
|
import { KeyringPair, KeyringPair$Json, KeyringPair$JsonEncodingTypes, KeyringPair$Meta, SignOptions } from '../types';
|
||||||
import { PairInfo } from './types';
|
import { PairInfo } from './types';
|
||||||
|
|
||||||
import { assert, u8aConcat } from '@polkadot/util';
|
import { assert, u8aConcat, u8aToHex } from '@polkadot/util';
|
||||||
import { blake2AsU8a, ethereumEncode, keccakAsU8a, keyExtractPath, keyFromPath, naclKeypairFromSeed as naclFromSeed, naclSign, naclVerify, schnorrkelKeypairFromSeed as schnorrkelFromSeed, schnorrkelSign, schnorrkelVerify, secp256k1Compress, secp256k1KeypairFromSeed as secp256k1FromSeed, secp256k1Sign, secp256k1Verify } from '@polkadot/util-crypto';
|
import { blake2AsU8a, ethereumEncode, keccakAsU8a, keyExtractPath, keyFromPath, naclKeypairFromSeed as naclFromSeed, naclSign, naclVerify, schnorrkelKeypairFromSeed as schnorrkelFromSeed, schnorrkelSign, schnorrkelVerify, secp256k1Expand, secp256k1KeypairFromSeed as secp256k1FromSeed, secp256k1Sign, secp256k1Verify, secp256k1Compress } from '@polkadot/util-crypto';
|
||||||
|
|
||||||
import decode from './decode';
|
import decode from './decode';
|
||||||
import encode from './encode';
|
import encode from './encode';
|
||||||
@@ -35,54 +35,28 @@ const TYPE_PREFIX = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const TYPE_SIGNATURE = {
|
const TYPE_SIGNATURE = {
|
||||||
ecdsa: (message: Uint8Array, pair: Partial<Keypair>) =>
|
ecdsa: (m: Uint8Array, p: Partial<Keypair>) => secp256k1Sign(m, p, 'blake2'),
|
||||||
secp256k1Sign(message, pair, 'blake2'),
|
|
||||||
ed25519: naclSign,
|
ed25519: naclSign,
|
||||||
ethereum: (message: Uint8Array, pair: Partial<Keypair>) =>
|
ethereum: (m: Uint8Array, p: Partial<Keypair>) => secp256k1Sign(m, p, 'keccak'),
|
||||||
secp256k1Sign(message, pair, 'keccak'),
|
|
||||||
sr25519: schnorrkelSign
|
sr25519: schnorrkelSign
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const TYPE_ADDRESS = {
|
||||||
|
ecdsa: (p: Uint8Array) => p.length > 32 ? blake2AsU8a(p) : p,
|
||||||
|
ed25519: (p: Uint8Array) => p,
|
||||||
|
ethereum: (p: Uint8Array) => keccakAsU8a(secp256k1Expand(p)),
|
||||||
|
sr25519: (p: Uint8Array) => p
|
||||||
|
};
|
||||||
|
|
||||||
const TYPE_VERIFY = {
|
const TYPE_VERIFY = {
|
||||||
ecdsa: (message: Uint8Array, signature: Uint8Array, publicKey: Uint8Array) =>
|
ecdsa: (m: Uint8Array, s: Uint8Array, p: Uint8Array) => secp256k1Verify(m, s, TYPE_ADDRESS.ecdsa(p), 'blake2'),
|
||||||
secp256k1Verify(message, signature, blake2AsU8a(publicKey), 'blake2'),
|
|
||||||
ed25519: naclVerify,
|
ed25519: naclVerify,
|
||||||
ethereum: (message: Uint8Array, signature: Uint8Array, publicKey: Uint8Array) =>
|
ethereum: (m: Uint8Array, s: Uint8Array, p: Uint8Array) => secp256k1Verify(m, s, TYPE_ADDRESS.ethereum(p), 'keccak', true),
|
||||||
secp256k1Verify(message, signature, keccakAsU8a(publicKey), 'keccak'),
|
|
||||||
sr25519: schnorrkelVerify
|
sr25519: schnorrkelVerify
|
||||||
};
|
};
|
||||||
|
|
||||||
function isEmpty (u8a: Uint8Array): boolean {
|
function isEmpty (u8a: Uint8Array): boolean {
|
||||||
return u8a.reduce((count, u8): number => count + u8, 0) === 0;
|
return u8a.reduce((count, u8) => count + u8, 0) === 0;
|
||||||
}
|
|
||||||
|
|
||||||
function fromSeed (type: KeypairType, seed: Uint8Array): Keypair {
|
|
||||||
return TYPE_FROM_SEED[type](seed);
|
|
||||||
}
|
|
||||||
|
|
||||||
function sign (type: KeypairType, message: Uint8Array, pair: Partial<Keypair>, { withType = false }: SignOptions = {}): Uint8Array {
|
|
||||||
return u8aConcat(
|
|
||||||
// for multi-signatures, i.e. with indicator, append the signature type as per
|
|
||||||
// the MultiSignature enum
|
|
||||||
withType
|
|
||||||
? TYPE_PREFIX[type]
|
|
||||||
: SIG_TYPE_NONE,
|
|
||||||
TYPE_SIGNATURE[type](message, pair)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function verify (type: KeypairType, message: Uint8Array, signature: Uint8Array, publicKey: Uint8Array): boolean {
|
|
||||||
return TYPE_VERIFY[type](message, signature, publicKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getAddress (type: KeypairType, publicKey: Uint8Array, isCompressed = false): Uint8Array {
|
|
||||||
if (type === 'ecdsa' && publicKey.length > 32) {
|
|
||||||
return blake2AsU8a(publicKey);
|
|
||||||
} else if (type === 'ethereum' && isCompressed) {
|
|
||||||
return secp256k1Compress(publicKey).slice(-32);
|
|
||||||
}
|
|
||||||
|
|
||||||
return publicKey;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not 100% correct, since it can be a Uint8Array, but an invalid one - just say "undefined" is anything non-valid
|
// Not 100% correct, since it can be a Uint8Array, but an invalid one - just say "undefined" is anything non-valid
|
||||||
@@ -129,11 +103,24 @@ export default function createPair ({ toSS58, type }: Setup, { publicKey, secret
|
|||||||
return encoded;
|
return encoded;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const encodeAddress = (): string => {
|
||||||
|
const raw = TYPE_ADDRESS[type](publicKey);
|
||||||
|
|
||||||
|
return type === 'ethereum'
|
||||||
|
? ethereumEncode(raw)
|
||||||
|
: toSS58(raw);
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
get address (): string {
|
get address (): string {
|
||||||
|
return encodeAddress();
|
||||||
|
},
|
||||||
|
get addressRaw (): Uint8Array {
|
||||||
|
const raw = TYPE_ADDRESS[type](publicKey);
|
||||||
|
|
||||||
return type === 'ethereum'
|
return type === 'ethereum'
|
||||||
? ethereumEncode(getAddress(type, publicKey))
|
? raw.slice(-20)
|
||||||
: toSS58(getAddress(type, publicKey));
|
: raw;
|
||||||
},
|
},
|
||||||
get isLocked (): boolean {
|
get isLocked (): boolean {
|
||||||
return isLocked(secretKey);
|
return isLocked(secretKey);
|
||||||
@@ -155,7 +142,7 @@ export default function createPair ({ toSS58, type }: Setup, { publicKey, secret
|
|||||||
publicKey = decoded.publicKey;
|
publicKey = decoded.publicKey;
|
||||||
secretKey = decoded.secretKey;
|
secretKey = decoded.secretKey;
|
||||||
} else {
|
} else {
|
||||||
const pair = fromSeed(type, decoded.secretKey);
|
const pair = TYPE_FROM_SEED[type](decoded.secretKey);
|
||||||
|
|
||||||
publicKey = pair.publicKey;
|
publicKey = pair.publicKey;
|
||||||
secretKey = pair.secretKey;
|
secretKey = pair.secretKey;
|
||||||
@@ -177,14 +164,24 @@ export default function createPair ({ toSS58, type }: Setup, { publicKey, secret
|
|||||||
setMeta: (additional: KeyringPair$Meta): void => {
|
setMeta: (additional: KeyringPair$Meta): void => {
|
||||||
meta = { ...meta, ...additional };
|
meta = { ...meta, ...additional };
|
||||||
},
|
},
|
||||||
sign: (message: Uint8Array, options?: SignOptions): Uint8Array => {
|
sign: (message: Uint8Array, options: SignOptions = {}): Uint8Array => {
|
||||||
assert(!isLocked(secretKey), 'Cannot sign with a locked key pair');
|
assert(!isLocked(secretKey), 'Cannot sign with a locked key pair');
|
||||||
|
|
||||||
return sign(type, message, { publicKey, secretKey }, options);
|
return u8aConcat(
|
||||||
|
options.withType
|
||||||
|
? TYPE_PREFIX[type]
|
||||||
|
: SIG_TYPE_NONE,
|
||||||
|
TYPE_SIGNATURE[type](message, { publicKey, secretKey })
|
||||||
|
);
|
||||||
|
},
|
||||||
|
toJson: (passphrase?: string): KeyringPair$Json => {
|
||||||
|
const address = ['ecdsa', 'ethereum'].includes(type)
|
||||||
|
? u8aToHex(secp256k1Compress(publicKey))
|
||||||
|
: encodeAddress();
|
||||||
|
|
||||||
|
return toJson(type, { address, meta }, recode(passphrase), !!passphrase);
|
||||||
},
|
},
|
||||||
toJson: (passphrase?: string): KeyringPair$Json =>
|
|
||||||
toJson(type, { address: toSS58(getAddress(type, publicKey, true)), meta }, recode(passphrase), !!passphrase),
|
|
||||||
verify: (message: Uint8Array, signature: Uint8Array): boolean =>
|
verify: (message: Uint8Array, signature: Uint8Array): boolean =>
|
||||||
verify(type, message, signature, publicKey)
|
TYPE_VERIFY[type](message, signature, publicKey)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ const json: KeyringPair$Json = {
|
|||||||
export default function everybody (): KeyringPair {
|
export default function everybody (): KeyringPair {
|
||||||
const pair: KeyringPair = {
|
const pair: KeyringPair = {
|
||||||
address,
|
address,
|
||||||
|
addressRaw: publicKey,
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
decodePkcs8: (passphrase?: string, encoded?: Uint8Array): void =>
|
decodePkcs8: (passphrase?: string, encoded?: Uint8Array): void =>
|
||||||
undefined,
|
undefined,
|
||||||
|
|||||||
@@ -3,10 +3,12 @@
|
|||||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||||
|
|
||||||
import { KeypairType } from '@polkadot/util-crypto/types';
|
import { KeypairType } from '@polkadot/util-crypto/types';
|
||||||
import { KeyringPair$Json, KeyringPair$Meta } from '../types';
|
import { KeyringPair$Json, KeyringPair$JsonEncodingTypes, KeyringPair$Meta } from '../types';
|
||||||
|
|
||||||
import { base64Encode } from '@polkadot/util-crypto';
|
import { base64Encode } from '@polkadot/util-crypto';
|
||||||
|
|
||||||
|
import { ENCODING } from './defaults';
|
||||||
|
|
||||||
interface PairStateJson {
|
interface PairStateJson {
|
||||||
address: string;
|
address: string;
|
||||||
meta: KeyringPair$Meta;
|
meta: KeyringPair$Meta;
|
||||||
@@ -14,6 +16,9 @@ interface PairStateJson {
|
|||||||
|
|
||||||
// version 2 - nonce, encoded (previous)
|
// version 2 - nonce, encoded (previous)
|
||||||
// version 3 - salt, nonce, encoded
|
// version 3 - salt, nonce, encoded
|
||||||
|
const VERSION = '3';
|
||||||
|
|
||||||
|
const ENC_NONE: KeyringPair$JsonEncodingTypes[] = ['none'];
|
||||||
|
|
||||||
export default function toJson (type: KeypairType, { address, meta }: PairStateJson, encoded: Uint8Array, isEncrypted: boolean): KeyringPair$Json {
|
export default function toJson (type: KeypairType, { address, meta }: PairStateJson, encoded: Uint8Array, isEncrypted: boolean): KeyringPair$Json {
|
||||||
return {
|
return {
|
||||||
@@ -22,9 +27,9 @@ export default function toJson (type: KeypairType, { address, meta }: PairStateJ
|
|||||||
encoding: {
|
encoding: {
|
||||||
content: ['pkcs8', type],
|
content: ['pkcs8', type],
|
||||||
type: isEncrypted
|
type: isEncrypted
|
||||||
? ['scrypt', 'xsalsa20-poly1305']
|
? ENCODING
|
||||||
: ['none'],
|
: ENC_NONE,
|
||||||
version: '3'
|
version: VERSION
|
||||||
},
|
},
|
||||||
meta
|
meta
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ export interface SignOptions {
|
|||||||
|
|
||||||
export interface KeyringPair {
|
export interface KeyringPair {
|
||||||
readonly address: string;
|
readonly address: string;
|
||||||
|
readonly addressRaw: Uint8Array;
|
||||||
readonly meta: KeyringPair$Meta;
|
readonly meta: KeyringPair$Meta;
|
||||||
readonly isLocked: boolean;
|
readonly isLocked: boolean;
|
||||||
readonly publicKey: Uint8Array;
|
readonly publicKey: Uint8Array;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@polkadot/util-crypto",
|
"name": "@polkadot/util-crypto",
|
||||||
"version": "3.2.1",
|
"version": "3.4.1",
|
||||||
"description": "A collection of useful crypto utilities for @polkadot",
|
"description": "A collection of useful crypto utilities for @polkadot",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
@@ -26,12 +26,12 @@
|
|||||||
"homepage": "https://github.com/polkadot-js/common/tree/master/packages/util-crypto#readme",
|
"homepage": "https://github.com/polkadot-js/common/tree/master/packages/util-crypto#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.11.2",
|
"@babel/runtime": "^7.11.2",
|
||||||
"@polkadot/util": "3.2.1",
|
"@polkadot/util": "3.4.1",
|
||||||
"@polkadot/wasm-crypto": "^1.3.1",
|
"@polkadot/wasm-crypto": "^1.4.1",
|
||||||
"base-x": "^3.0.8",
|
"base-x": "^3.0.8",
|
||||||
"bip39": "^3.0.2",
|
"bip39": "^3.0.2",
|
||||||
"blakejs": "^1.1.0",
|
"blakejs": "^1.1.0",
|
||||||
"bn.js": "^5.1.2",
|
"bn.js": "^5.1.3",
|
||||||
"elliptic": "^6.5.3",
|
"elliptic": "^6.5.3",
|
||||||
"js-sha3": "^0.8.0",
|
"js-sha3": "^0.8.0",
|
||||||
"pbkdf2": "^3.1.1",
|
"pbkdf2": "^3.1.1",
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
import sshash from './sshash';
|
import sshash from './sshash';
|
||||||
|
|
||||||
export default function checkChecksum (decoded: Uint8Array): [boolean, number] {
|
export default function checkChecksum (decoded: Uint8Array): [boolean, number] {
|
||||||
const isPublicKey = decoded.length === 35;
|
const isPublicKey = [35, 36].includes(decoded.length);
|
||||||
|
|
||||||
// non-publicKeys has 1 byte checksums, else default to 2
|
// non-publicKeys has 1 byte checksums, else default to 2
|
||||||
const length = decoded.length - (isPublicKey ? 2 : 1);
|
const length = decoded.length - (isPublicKey ? 2 : 1);
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// This software may be modified and distributed under the terms
|
// This software may be modified and distributed under the terms
|
||||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||||
|
|
||||||
import { stringToU8a, u8aToHex } from '@polkadot/util';
|
import { stringToU8a, u8aToHex, hexToU8a } from '@polkadot/util';
|
||||||
|
|
||||||
import testingPairs, { TestKeyringMap } from '../../../keyring/src/testingPairs';
|
import testingPairs, { TestKeyringMap } from '../../../keyring/src/testingPairs';
|
||||||
import decode from './decode';
|
import decode from './decode';
|
||||||
@@ -74,6 +74,14 @@ describe('decodeAddress', (): void => {
|
|||||||
).toEqual(new Uint8Array([42, 44, 10, 0, 0, 0, 0, 0]));
|
).toEqual(new Uint8Array([42, 44, 10, 0, 0, 0, 0, 0]));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('decodes a 33-byte address', (): void => {
|
||||||
|
expect(
|
||||||
|
decode('KWCv1L3QX9LDPwY4VzvLmarEmXjVJidUzZcinvVnmxAJJCBou')
|
||||||
|
).toEqual(
|
||||||
|
hexToU8a('0x03b9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb13077')
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it.skip('allows invalid prefix (in list)', (): void => {
|
it.skip('allows invalid prefix (in list)', (): void => {
|
||||||
expect(
|
expect(
|
||||||
(): Uint8Array => decode('6GfvWUvHvU8otbZ7sFhXH4eYeMcKdUkL61P3nFy52efEPVUx')
|
(): Uint8Array => decode('6GfvWUvHvU8otbZ7sFhXH4eYeMcKdUkL61P3nFy52efEPVUx')
|
||||||
|
|||||||
@@ -5,9 +5,9 @@
|
|||||||
import { allowedSS58 } from './ss58';
|
import { allowedSS58 } from './ss58';
|
||||||
|
|
||||||
const defaults = {
|
const defaults = {
|
||||||
allowedDecodedLengths: [1, 2, 4, 8, 32],
|
allowedDecodedLengths: [1, 2, 4, 8, 32, 33],
|
||||||
// publicKey has prefix + 2 checksum bytes, short only prefix + 1 checksum byte
|
// publicKey has prefix + 2 checksum bytes, short only prefix + 1 checksum byte
|
||||||
allowedEncodedLengths: [3, 4, 6, 10, 35],
|
allowedEncodedLengths: [3, 4, 6, 10, 35, 36],
|
||||||
allowedPrefix: allowedSS58,
|
allowedPrefix: allowedSS58,
|
||||||
prefix: 42
|
prefix: 42
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -67,11 +67,17 @@ describe('encode', (): void => {
|
|||||||
).toEqual('as7QnGMf');
|
).toEqual('as7QnGMf');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('enodes a 8-byte address', (): void => {
|
it('encodes a 8-byte address', (): void => {
|
||||||
expect(
|
expect(
|
||||||
encode(
|
encode(
|
||||||
new Uint8Array([42, 44, 10, 0, 0, 0, 0, 0]), 68
|
new Uint8Array([42, 44, 10, 0, 0, 0, 0, 0]), 68
|
||||||
)
|
)
|
||||||
).toEqual('4q7qY5RBG7Z4wv');
|
).toEqual('4q7qY5RBG7Z4wv');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('encodes an 33-byte address', (): void => {
|
||||||
|
expect(
|
||||||
|
encode('0x03b9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb13077')
|
||||||
|
).toEqual('KWCv1L3QX9LDPwY4VzvLmarEmXjVJidUzZcinvVnmxAJJCBou');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ export default function encode (_key: Uint8Array | string, ss58Format: Prefix =
|
|||||||
|
|
||||||
assert(defaults.allowedDecodedLengths.includes(key.length), `Expected a valid key to convert, with length ${defaults.allowedDecodedLengths.join(', ')}`);
|
assert(defaults.allowedDecodedLengths.includes(key.length), `Expected a valid key to convert, with length ${defaults.allowedDecodedLengths.join(', ')}`);
|
||||||
|
|
||||||
const isPublicKey = key.length === 32;
|
const isPublicKey = [32, 33].includes(key.length);
|
||||||
const input = u8aConcat(new Uint8Array([ss58Format]), key);
|
const input = u8aConcat(new Uint8Array([ss58Format]), key);
|
||||||
const hash = sshash(input);
|
const hash = sshash(input);
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
// This software may be modified and distributed under the terms
|
// This software may be modified and distributed under the terms
|
||||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||||
|
|
||||||
|
import { keccakAsU8a } from '../keccak';
|
||||||
import { ethereumEncode } from './';
|
import { ethereumEncode } from './';
|
||||||
|
|
||||||
describe('formatAddress', () => {
|
describe('formatAddress', () => {
|
||||||
@@ -35,12 +36,6 @@ describe('formatAddress', () => {
|
|||||||
const ADDRESS = '0x4119b2e6c3Cb618F4f0B93ac77f9BeeC7FF02887';
|
const ADDRESS = '0x4119b2e6c3Cb618F4f0B93ac77f9BeeC7FF02887';
|
||||||
|
|
||||||
it('encodes a compressed publicKey', (): void => {
|
it('encodes a compressed publicKey', (): void => {
|
||||||
expect(
|
|
||||||
ethereumEncode('0xb9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb13077')
|
|
||||||
).toEqual(ADDRESS);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('encodes a compressed publicKey (indicator)', (): void => {
|
|
||||||
expect(
|
expect(
|
||||||
ethereumEncode('0x03b9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb13077')
|
ethereumEncode('0x03b9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb13077')
|
||||||
).toEqual(ADDRESS);
|
).toEqual(ADDRESS);
|
||||||
@@ -48,13 +43,15 @@ describe('formatAddress', () => {
|
|||||||
|
|
||||||
it('encodes an expanded publicKey', (): void => {
|
it('encodes an expanded publicKey', (): void => {
|
||||||
expect(
|
expect(
|
||||||
ethereumEncode('0xb9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb1307763fe926c273235fd979a134076d00fd1683cbd35868cb485d4a3a640e52184af')
|
ethereumEncode('0x04b9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb1307763fe926c273235fd979a134076d00fd1683cbd35868cb485d4a3a640e52184af')
|
||||||
).toEqual(ADDRESS);
|
).toEqual(ADDRESS);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('encodes an expanded publicKey (indicator + x/y)', (): void => {
|
it('encodes a pre-hashed key', (): void => {
|
||||||
expect(
|
expect(
|
||||||
ethereumEncode('0x04b9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb1307763fe926c273235fd979a134076d00fd1683cbd35868cb485d4a3a640e52184af')
|
ethereumEncode(
|
||||||
|
keccakAsU8a('0xb9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb1307763fe926c273235fd979a134076d00fd1683cbd35868cb485d4a3a640e52184af')
|
||||||
|
)
|
||||||
).toEqual(ADDRESS);
|
).toEqual(ADDRESS);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -7,20 +7,24 @@ import { assert, u8aToHex, u8aToU8a } from '@polkadot/util';
|
|||||||
import { keccakAsU8a } from '../keccak';
|
import { keccakAsU8a } from '../keccak';
|
||||||
import { secp256k1Expand } from '../secp256k1';
|
import { secp256k1Expand } from '../secp256k1';
|
||||||
|
|
||||||
|
function getH160 (u8a: Uint8Array): Uint8Array {
|
||||||
|
if ([33, 65].includes(u8a.length)) {
|
||||||
|
u8a = keccakAsU8a(secp256k1Expand(u8a));
|
||||||
|
}
|
||||||
|
|
||||||
|
return u8a.slice(-20);
|
||||||
|
}
|
||||||
|
|
||||||
export default function ethereumEncode (addressOrPublic?: string | Uint8Array): string {
|
export default function ethereumEncode (addressOrPublic?: string | Uint8Array): string {
|
||||||
if (!addressOrPublic) {
|
if (!addressOrPublic) {
|
||||||
return '0x';
|
return '0x';
|
||||||
}
|
}
|
||||||
|
|
||||||
let u8aAddress = u8aToU8a(addressOrPublic);
|
const u8aAddress = u8aToU8a(addressOrPublic);
|
||||||
|
|
||||||
assert([20, 32, 33, 64, 65].includes(u8aAddress.length), 'Invalid address or publicKey passed');
|
assert([20, 32, 33, 65].includes(u8aAddress.length), 'Invalid address or publicKey passed');
|
||||||
|
|
||||||
if (u8aAddress.length > 20) {
|
const address = u8aToHex(getH160(u8aAddress), -1, false);
|
||||||
u8aAddress = keccakAsU8a(secp256k1Expand(u8aAddress)).slice(-20);
|
|
||||||
}
|
|
||||||
|
|
||||||
const address = u8aToHex(u8aAddress, -1, false);
|
|
||||||
const hash = u8aToHex(keccakAsU8a(address), -1, false);
|
const hash = u8aToHex(keccakAsU8a(address), -1, false);
|
||||||
let result = '';
|
let result = '';
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||||
|
|
||||||
import { u8aToHex } from '@polkadot/util';
|
import { u8aToHex } from '@polkadot/util';
|
||||||
|
import { waitReady } from '@polkadot/wasm-crypto';
|
||||||
|
|
||||||
import { mnemonicToBip39 } from './';
|
import { mnemonicToBip39 } from './';
|
||||||
|
|
||||||
@@ -14,4 +15,14 @@ describe('mnemonicToBip39Seed', (): void => {
|
|||||||
)
|
)
|
||||||
).toEqual('0x3c121e20de068083b49c2315697fb59a2d9e8643c24e5ea7628132c58969a027');
|
).toEqual('0x3c121e20de068083b49c2315697fb59a2d9e8643c24e5ea7628132c58969a027');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('generates the seed via WASM', async (): Promise<void> => {
|
||||||
|
await waitReady();
|
||||||
|
|
||||||
|
expect(
|
||||||
|
u8aToHex(
|
||||||
|
mnemonicToBip39('seed sock milk update focus rotate barely fade car face mechanic mercy')
|
||||||
|
)
|
||||||
|
).toEqual('0x3c121e20de068083b49c2315697fb59a2d9e8643c24e5ea7628132c58969a027');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
import { mnemonicToSeedSync } from 'bip39';
|
import { mnemonicToSeedSync } from 'bip39';
|
||||||
import { bufferToU8a } from '@polkadot/util';
|
import { bufferToU8a } from '@polkadot/util';
|
||||||
|
|
||||||
// import { bip39ToSeed, isReady } from '@polkadot/wasm-crypto';
|
import { bip39ToSeed, isReady } from '@polkadot/wasm-crypto';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @name toSeed
|
* @name toSeed
|
||||||
@@ -25,9 +25,7 @@ import { bufferToU8a } from '@polkadot/util';
|
|||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
export default function toBip39 (mnemonic: string, password = ''): Uint8Array {
|
export default function toBip39 (mnemonic: string, password = ''): Uint8Array {
|
||||||
// FIXMe Re-add when we have support in the WASM libs again
|
return isReady()
|
||||||
// return isReady()
|
? bip39ToSeed(mnemonic, password)
|
||||||
// ? bip39ToSeed(mnemonic, password)
|
: bufferToU8a(mnemonicToSeedSync(mnemonic, password)).subarray(0, 32);
|
||||||
|
|
||||||
return bufferToU8a(mnemonicToSeedSync(mnemonic, password)).subarray(0, 32);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ describe('secp256k1Compress', (): void => {
|
|||||||
it('compresses a known key', (): void => {
|
it('compresses a known key', (): void => {
|
||||||
expect(
|
expect(
|
||||||
secp256k1Compress(
|
secp256k1Compress(
|
||||||
hexToU8a('0xb9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb1307763fe926c273235fd979a134076d00fd1683cbd35868cb485d4a3a640e52184af')
|
hexToU8a('0x04b9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb1307763fe926c273235fd979a134076d00fd1683cbd35868cb485d4a3a640e52184af')
|
||||||
)
|
)
|
||||||
).toEqual(
|
).toEqual(
|
||||||
hexToU8a('0x03b9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb13077')
|
hexToU8a('0x03b9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb13077')
|
||||||
|
|||||||
@@ -3,16 +3,15 @@
|
|||||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||||
|
|
||||||
import elliptic from 'elliptic';
|
import elliptic from 'elliptic';
|
||||||
|
import { assert } from '@polkadot/util';
|
||||||
import sanitizePublic from './sanitizePublic';
|
|
||||||
|
|
||||||
const EC = elliptic.ec;
|
const EC = elliptic.ec;
|
||||||
const ec = new EC('secp256k1');
|
const ec = new EC('secp256k1');
|
||||||
|
|
||||||
export default function secp256k1Compress (publicKey: Uint8Array): Uint8Array {
|
export default function secp256k1Compress (publicKey: Uint8Array): Uint8Array {
|
||||||
|
assert([33, 65].includes(publicKey.length), 'Invalid publicKey provided');
|
||||||
|
|
||||||
return new Uint8Array(
|
return new Uint8Array(
|
||||||
ec.keyFromPublic(
|
ec.keyFromPublic(publicKey).getPublic().encodeCompressed()
|
||||||
sanitizePublic(publicKey)
|
|
||||||
).getPublic().encodeCompressed()
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,16 +17,6 @@ describe('secp256k1Expand', (): void => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('expands a known key (no indicator)', (): void => {
|
|
||||||
expect(
|
|
||||||
secp256k1Expand(
|
|
||||||
hexToU8a('0xb9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb13077')
|
|
||||||
)
|
|
||||||
).toEqual(
|
|
||||||
hexToU8a('0xb9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb1307763fe926c273235fd979a134076d00fd1683cbd35868cb485d4a3a640e52184af')
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('expands a known full key', (): void => {
|
it('expands a known full key', (): void => {
|
||||||
expect(
|
expect(
|
||||||
secp256k1Expand(
|
secp256k1Expand(
|
||||||
@@ -36,14 +26,4 @@ describe('secp256k1Expand', (): void => {
|
|||||||
hexToU8a('0xb9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb1307763fe926c273235fd979a134076d00fd1683cbd35868cb485d4a3a640e52184af')
|
hexToU8a('0xb9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb1307763fe926c273235fd979a134076d00fd1683cbd35868cb485d4a3a640e52184af')
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('expands a known full key (no indicator)', (): void => {
|
|
||||||
expect(
|
|
||||||
secp256k1Expand(
|
|
||||||
hexToU8a('0xb9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb1307763fe926c273235fd979a134076d00fd1683cbd35868cb485d4a3a640e52184af')
|
|
||||||
)
|
|
||||||
).toEqual(
|
|
||||||
hexToU8a('0xb9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb1307763fe926c273235fd979a134076d00fd1683cbd35868cb485d4a3a640e52184af')
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -3,15 +3,15 @@
|
|||||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||||
|
|
||||||
import elliptic from 'elliptic';
|
import elliptic from 'elliptic';
|
||||||
import { bnToU8a, u8aConcat } from '@polkadot/util';
|
import { assert, bnToU8a, u8aConcat } from '@polkadot/util';
|
||||||
|
|
||||||
import sanitizePublic from './sanitizePublic';
|
|
||||||
|
|
||||||
const EC = elliptic.ec;
|
const EC = elliptic.ec;
|
||||||
const ec = new EC('secp256k1');
|
const ec = new EC('secp256k1');
|
||||||
|
|
||||||
export default function secp256k1Expand (publicKey: Uint8Array): Uint8Array {
|
export default function secp256k1Expand (publicKey: Uint8Array): Uint8Array {
|
||||||
const expanded = ec.keyFromPublic(sanitizePublic(publicKey)).getPublic();
|
assert([33, 65].includes(publicKey.length), 'Invalid publicKey provided');
|
||||||
|
|
||||||
|
const expanded = ec.keyFromPublic(publicKey).getPublic();
|
||||||
|
|
||||||
return u8aConcat(
|
return u8aConcat(
|
||||||
bnToU8a(expanded.getX(), { bitLength: 256, isLe: false }),
|
bnToU8a(expanded.getX(), { bitLength: 256, isLe: false }),
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
// Copyright 2017-2020 @polkadot/util-crypto authors & contributors
|
|
||||||
// This software may be modified and distributed under the terms
|
|
||||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
|
||||||
|
|
||||||
import { assert, u8aConcat } from '@polkadot/util';
|
|
||||||
|
|
||||||
const INDICATOR_COMP = new Uint8Array([3]);
|
|
||||||
const INDICATOR_FULL = new Uint8Array([4]);
|
|
||||||
|
|
||||||
export default function sanitizePublic (publicKey: Uint8Array): Uint8Array {
|
|
||||||
assert([32, 33, 64, 65].includes(publicKey.length), 'Invalid publicKey provided');
|
|
||||||
|
|
||||||
return [33, 65].includes(publicKey.length)
|
|
||||||
? publicKey
|
|
||||||
: publicKey.length === 32
|
|
||||||
? u8aConcat(INDICATOR_COMP, publicKey)
|
|
||||||
: u8aConcat(INDICATOR_FULL, publicKey);
|
|
||||||
}
|
|
||||||
@@ -5,8 +5,9 @@
|
|||||||
import { HashType } from './types';
|
import { HashType } from './types';
|
||||||
|
|
||||||
import elliptic from 'elliptic';
|
import elliptic from 'elliptic';
|
||||||
import { assert, u8aToU8a } from '@polkadot/util';
|
import { assert, u8aEq, u8aToU8a } from '@polkadot/util';
|
||||||
|
|
||||||
|
import secp256k1Expand from './expand';
|
||||||
import hasher from './hasher';
|
import hasher from './hasher';
|
||||||
|
|
||||||
const EC = elliptic.ec;
|
const EC = elliptic.ec;
|
||||||
@@ -16,21 +17,19 @@ const ec = new EC('secp256k1');
|
|||||||
* @name secp256k1Verify
|
* @name secp256k1Verify
|
||||||
* @description Verifies the signature of `message`, using the supplied pair
|
* @description Verifies the signature of `message`, using the supplied pair
|
||||||
*/
|
*/
|
||||||
export default function secp256k1Verify (message: Uint8Array | string, signature: Uint8Array | string, address: Uint8Array | string, hashType: HashType = 'blake2'): boolean {
|
export default function secp256k1Verify (message: Uint8Array | string, signature: Uint8Array | string, address: Uint8Array | string, hashType: HashType = 'blake2', isExpanded = false): boolean {
|
||||||
const signatureU8a = u8aToU8a(signature);
|
const u8a = u8aToU8a(signature);
|
||||||
|
|
||||||
assert(signatureU8a.length === 65, `Expected signature with 65 bytes, ${signatureU8a.length} found instead`);
|
assert(u8a.length === 65, `Expected signature with 65 bytes, ${u8a.length} found instead`);
|
||||||
|
|
||||||
const sig = {
|
|
||||||
r: signatureU8a.slice(0, 32),
|
|
||||||
s: signatureU8a.slice(32, 64)
|
|
||||||
};
|
|
||||||
const recovery = signatureU8a[64];
|
|
||||||
const publicKey = new Uint8Array(
|
const publicKey = new Uint8Array(
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-member-access
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-member-access
|
||||||
ec.recoverPubKey(hasher(hashType, message), sig, recovery)
|
ec.recoverPubKey(hasher(hashType, message), { r: u8a.slice(0, 32), s: u8a.slice(32, 64) }, u8a[64])
|
||||||
.encodeCompressed()
|
.encodeCompressed()
|
||||||
);
|
);
|
||||||
|
|
||||||
return Buffer.compare(hasher(hashType, publicKey), u8aToU8a(address)) === 0;
|
return u8aEq(
|
||||||
|
hasher(hashType, isExpanded ? secp256k1Expand(publicKey) : publicKey),
|
||||||
|
u8aToU8a(address)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@polkadot/util",
|
"name": "@polkadot/util",
|
||||||
"version": "3.2.1",
|
"version": "3.4.1",
|
||||||
"description": "A collection of useful utilities for @polkadot",
|
"description": "A collection of useful utilities for @polkadot",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.11.2",
|
"@babel/runtime": "^7.11.2",
|
||||||
"@types/bn.js": "^4.11.6",
|
"@types/bn.js": "^4.11.6",
|
||||||
"bn.js": "^5.1.2",
|
"bn.js": "^5.1.3",
|
||||||
"camelcase": "^5.3.1",
|
"camelcase": "^5.3.1",
|
||||||
"chalk": "^4.1.0",
|
"chalk": "^4.1.0",
|
||||||
"ip-regex": "^4.1.0"
|
"ip-regex": "^4.1.0"
|
||||||
|
|||||||
@@ -27,3 +27,4 @@ export { default as isToBn } from './toBn';
|
|||||||
export { default as isU8a } from './u8a';
|
export { default as isU8a } from './u8a';
|
||||||
export { default as isUndefined } from './undefined';
|
export { default as isUndefined } from './undefined';
|
||||||
export { default as isUtf8 } from './utf8';
|
export { default as isUtf8 } from './utf8';
|
||||||
|
export { default as isWasm } from './wasm';
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
// Copyright 2017-2020 @polkadot/util authors & contributors
|
||||||
|
// This software may be modified and distributed under the terms
|
||||||
|
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||||
|
|
||||||
|
import u8aEq from '../u8a/eq';
|
||||||
|
|
||||||
|
const WASM_MAGIC = new Uint8Array([0, 97, 115, 109]); // \0asm
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name isWasm
|
||||||
|
* @summary Tests if the input has a WASM header
|
||||||
|
* @description
|
||||||
|
* Checks to see if the input Uint8Array contains a valid WASM header
|
||||||
|
*/
|
||||||
|
export default function isWasm (value?: Uint8Array): boolean {
|
||||||
|
return !!value && u8aEq(value.subarray(0, 4), WASM_MAGIC);
|
||||||
|
}
|
||||||
@@ -13,7 +13,7 @@ import stringToU8a from '../string/toU8a';
|
|||||||
* @name u8aToU8a
|
* @name u8aToU8a
|
||||||
* @summary Creates a Uint8Array value from a Uint8Array, Buffer, string or hex input.
|
* @summary Creates a Uint8Array value from a Uint8Array, Buffer, string or hex input.
|
||||||
* @description
|
* @description
|
||||||
* `null` ior `undefined` nputs returns a `[]` result, Uint8Array values returns the value, hex strings returns a Uint8Array representation.
|
* `null` or `undefined` inputs returns a `[]` result, Uint8Array values returns the value, hex strings returns a Uint8Array representation.
|
||||||
* @example
|
* @example
|
||||||
* <BR>
|
* <BR>
|
||||||
*
|
*
|
||||||
|
|||||||
Reference in New Issue
Block a user