UNPKG

@polkadot/keyring

Version:
185 lines (184 loc) • 8.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.createPair = void 0; const util_1 = require("@polkadot/util"); const util_crypto_1 = require("@polkadot/util-crypto"); const decode_js_1 = require("./decode.js"); const encode_js_1 = require("./encode.js"); const toJson_js_1 = require("./toJson.js"); const SIG_TYPE_NONE = new Uint8Array(); const TYPE_FROM_SEED = { ecdsa: util_crypto_1.secp256k1PairFromSeed, ed25519: util_crypto_1.ed25519PairFromSeed, ethereum: util_crypto_1.secp256k1PairFromSeed, sr25519: util_crypto_1.sr25519PairFromSeed }; const TYPE_PREFIX = { ecdsa: new Uint8Array([2]), ed25519: new Uint8Array([0]), ethereum: new Uint8Array([2]), sr25519: new Uint8Array([1]) }; const TYPE_SIGNATURE = { ecdsa: (m, p) => (0, util_crypto_1.secp256k1Sign)(m, p, 'blake2'), ed25519: util_crypto_1.ed25519Sign, ethereum: (m, p) => (0, util_crypto_1.secp256k1Sign)(m, p, 'keccak'), sr25519: util_crypto_1.sr25519Sign }; const TYPE_ADDRESS = { ecdsa: (p) => p.length > 32 ? (0, util_crypto_1.blake2AsU8a)(p) : p, ed25519: (p) => p, ethereum: (p) => p.length === 20 ? p : (0, util_crypto_1.keccakAsU8a)((0, util_crypto_1.secp256k1Expand)(p)), sr25519: (p) => p }; function isLocked(secretKey) { return !secretKey || (0, util_1.u8aEmpty)(secretKey); } function vrfHash(proof, context, extra) { return (0, util_crypto_1.blake2AsU8a)((0, util_1.u8aConcat)(context || '', extra || '', proof)); } /** * @name createPair * @summary Creates a keyring pair object * @description Creates a keyring pair object with provided account public key, metadata, and encoded arguments. * The keyring pair stores the account state including the encoded address and associated metadata. * * It has properties whose values are functions that may be called to perform account actions: * * - `address` function retrieves the address associated with the account. * - `decodedPkcs8` function is called with the account passphrase and account encoded public key. * It decodes the encoded public key using the passphrase provided to obtain the decoded account public key * and associated secret key that are then available in memory, and changes the account address stored in the * state of the pair to correspond to the address of the decoded public key. * - `encodePkcs8` function when provided with the correct passphrase associated with the account pair * and when the secret key is in memory (when the account pair is not locked) it returns an encoded * public key of the account. * - `meta` is the metadata that is stored in the state of the pair, either when it was originally * created or set via `setMeta`. * - `publicKey` returns the public key stored in memory for the pair. * - `sign` may be used to return a signature by signing a provided message with the secret * key (if it is in memory) using Nacl. * - `toJson` calls another `toJson` function and provides the state of the pair, * it generates arguments to be passed to the other `toJson` function including an encoded public key of the account * that it generates using the secret key from memory (if it has been made available in memory) * and the optionally provided passphrase argument. It passes a third boolean argument to `toJson` * indicating whether the public key has been encoded or not (if a passphrase argument was provided then it is encoded). * The `toJson` function that it calls returns a JSON object with properties including the `address` * and `meta` that are assigned with the values stored in the corresponding state variables of the account pair, * an `encoded` property that is assigned with the encoded public key in hex format, and an `encoding` * property that indicates whether the public key value of the `encoded` property is encoded or not. */ function createPair({ toSS58, type }, { publicKey, secretKey }, meta = {}, encoded = null, encTypes) { const decodePkcs8 = (passphrase, userEncoded) => { const decoded = (0, decode_js_1.decodePair)(passphrase, userEncoded || encoded, encTypes); if (decoded.secretKey.length === 64) { publicKey = decoded.publicKey; secretKey = decoded.secretKey; } else { const pair = TYPE_FROM_SEED[type](decoded.secretKey); publicKey = pair.publicKey; secretKey = pair.secretKey; } }; const recode = (passphrase) => { isLocked(secretKey) && encoded && decodePkcs8(passphrase, encoded); encoded = (0, encode_js_1.encodePair)({ publicKey, secretKey }, passphrase); // re-encode, latest version encTypes = undefined; // swap to defaults, latest version follows return encoded; }; const encodeAddress = () => { const raw = TYPE_ADDRESS[type](publicKey); return type === 'ethereum' ? (0, util_crypto_1.ethereumEncode)(raw) : toSS58(raw); }; return { get address() { return encodeAddress(); }, get addressRaw() { const raw = TYPE_ADDRESS[type](publicKey); return type === 'ethereum' ? raw.slice(-20) : raw; }, get isLocked() { return isLocked(secretKey); }, get meta() { return meta; }, get publicKey() { return publicKey; }, get type() { return type; }, // eslint-disable-next-line sort-keys decodePkcs8, derive: (suri, meta) => { if (type === 'ethereum') { throw new Error('Unable to derive on this keypair'); } else if (isLocked(secretKey)) { throw new Error('Cannot derive on a locked keypair'); } const { path } = (0, util_crypto_1.keyExtractPath)(suri); const derived = (0, util_crypto_1.keyFromPath)({ publicKey, secretKey }, path, type); return createPair({ toSS58, type }, derived, meta, null); }, encodePkcs8: (passphrase) => { return recode(passphrase); }, lock: () => { secretKey = new Uint8Array(); }, setMeta: (additional) => { meta = (0, util_1.objectSpread)({}, meta, additional); }, sign: (message, options = {}) => { if (isLocked(secretKey)) { throw new Error('Cannot sign with a locked key pair'); } return (0, util_1.u8aConcat)(options.withType ? TYPE_PREFIX[type] : SIG_TYPE_NONE, TYPE_SIGNATURE[type]((0, util_1.u8aToU8a)(message), { publicKey, secretKey })); }, toJson: (passphrase) => { // NOTE: For ecdsa and ethereum, the publicKey cannot be extracted from the address. For these // pass the hex-encoded publicKey through to the address portion of the JSON (before decoding) // unless the publicKey is already an address const address = ['ecdsa', 'ethereum'].includes(type) ? publicKey.length === 20 ? (0, util_1.u8aToHex)(publicKey) : (0, util_1.u8aToHex)((0, util_crypto_1.secp256k1Compress)(publicKey)) : encodeAddress(); return (0, toJson_js_1.pairToJson)(type, { address, meta }, recode(passphrase), !!passphrase); }, unlock: (passphrase) => { return decodePkcs8(passphrase); }, verify: (message, signature, signerPublic) => { return (0, util_crypto_1.signatureVerify)(message, signature, TYPE_ADDRESS[type]((0, util_1.u8aToU8a)(signerPublic))).isValid; }, vrfSign: (message, context, extra) => { if (isLocked(secretKey)) { throw new Error('Cannot sign with a locked key pair'); } if (type === 'sr25519') { return (0, util_crypto_1.sr25519VrfSign)(message, { secretKey }, context, extra); } const proof = TYPE_SIGNATURE[type]((0, util_1.u8aToU8a)(message), { publicKey, secretKey }); return (0, util_1.u8aConcat)(vrfHash(proof, context, extra), proof); }, vrfVerify: (message, vrfResult, signerPublic, context, extra) => { if (type === 'sr25519') { return (0, util_crypto_1.sr25519VrfVerify)(message, vrfResult, publicKey, context, extra); } const result = (0, util_crypto_1.signatureVerify)(message, (0, util_1.u8aConcat)(TYPE_PREFIX[type], vrfResult.subarray(32)), TYPE_ADDRESS[type]((0, util_1.u8aToU8a)(signerPublic))); return result.isValid && (0, util_1.u8aEq)(vrfResult.subarray(0, 32), vrfHash(vrfResult.subarray(32), context, extra)); } }; } exports.createPair = createPair;