UNPKG

@swtc/keypairs

Version:
327 lines 14.5 kB
import brorand from "brorand"; import hashjs from "hash.js"; import { eddsa, ec } from "elliptic"; import { Signature, sm2 as Secp256k1Gm } from "@swtc/sm.js"; import BN from "bn.js"; import { Factory as AddressCodecFactory } from "@swtc/address-codec"; import { SM3, funcAssert as assert, funcHexToBytes as hexToBytes, funcBytesToHex as bytesToHex } from "@swtc/common"; import { derivePrivateKey, accountPublicFromPublicGenerator } from "./secp256k1"; import { derivePrivateKey as derivePrivateKeyGm, accountPublicFromPublicGenerator as accountPublicFromPublicGeneratorGm } from "./sm2p256v1"; import { computePublicKeyHash, computePublicKeyHashGm, seedFromPhrase, seedFromPhraseGm } from "./utils"; const Ed25519 = eddsa("ed25519"); const Secp256k1 = ec("secp256k1"); // const Secp256k1Gm = sm2 function hashOpen(message) { return hashjs.sha512().update(message).digest().slice(0, 32); } function hashGm(message) { return new SM3().update(message).digest().slice(0, 32); } export function Factory(chain_or_token = "jingtum") { const addressCodec = AddressCodecFactory(chain_or_token); const guomi = addressCodec.guomi; const token = addressCodec.token; const hash = guomi ? hashGm : hashOpen; const secp256k1 = { deriveKeypair: (entropy, options = {}) => { const prefix = "00"; const privateKey = prefix + derivePrivateKey(entropy, options).toString(16, 64).toUpperCase(); const publicKey = bytesToHex(Secp256k1.keyFromPrivate(privateKey.slice(2)) .getPublic() .encodeCompressed()); return { privateKey, publicKey }; }, deriveKeypairWithPrivateKey: (rawPrivateKey) => { const prefix = "00"; const privateKey = prefix + rawPrivateKey.toUpperCase(); const publicKey = bytesToHex(Secp256k1.keyFromPrivate(privateKey.slice(2)) .getPublic() .encodeCompressed()); return { privateKey, publicKey }; }, sign: (message, privateKey) => { return bytesToHex(Secp256k1.sign(hash(message), hexToBytes(privateKey), { canonical: true }).toDER()); }, verify: (message, signature, publicKey) => { return Secp256k1.verify(hash(message), signature, hexToBytes(publicKey)); }, signTx: (message, privateKey) => { return bytesToHex(Secp256k1.sign(message, hexToBytes(privateKey), { canonical: true }).toDER()); }, verifyTx: (message, signature, publicKey) => { return Secp256k1.verify(message, signature, hexToBytes(publicKey)); } }; const sm2p256v1 = { deriveKeypair: (entropy, options = {}) => { const prefix = "00"; const pri = derivePrivateKeyGm(entropy, options); const privateKey = prefix + pri.toString(16, 64).toUpperCase(); const publicKey = bytesToHex(Secp256k1Gm.SM2KeyPair(null, pri).pub.encodeCompressed()); return { privateKey, publicKey }; }, deriveKeypairWithPrivateKey: (rawPrivateKey) => { const prefix = "00"; const pri = new BN(rawPrivateKey, "hex"); const privateKey = prefix + rawPrivateKey.toUpperCase(); const publicKey = bytesToHex(Secp256k1Gm.SM2KeyPair(null, pri).pub.encodeCompressed()); return { privateKey, publicKey }; }, sign: (message, privateKey) => { const keypair = Secp256k1Gm.SM2KeyPair(null, new BN(privateKey, "hex")); return bytesToHex(new Signature(keypair.sign(hash(message))).toDER()); }, verify: (message, signature, publicKey) => { const keypair = Secp256k1Gm.SM2KeyPair(publicKey); const sig = new Signature(signature, "hex"); return keypair.verify(hash(message), sig.r, sig.s); }, signTx: (message, privateKey) => { if (typeof message === "string" && /^[0-9A-F]{16}/i.test(message)) { // if in hex format message = hexToBytes(message); } const keypair = Secp256k1Gm.SM2KeyPair(null, new BN(privateKey, "hex")); return bytesToHex(new Signature(keypair.sign(message)).toDER()); // return this.sign(message, privateKey) }, verifyTx: (message, signature, publicKey) => { if (typeof message === "string" && /^[0-9A-F]{16}/i.test(message)) { // if in hex format message = hexToBytes(message); } // if (typeof signature === "string" && /^[0-9A-F]{16}/i.test(signature)) { // // if in hex format // signature = hexToBytes(signature) // } const keypair = Secp256k1Gm.SM2KeyPair(publicKey); const sig = new Signature(signature, "hex"); return keypair.verify(hash(message), sig.r, sig.s); } }; const ed25519 = { deriveKeypair: (entropy) => { const prefix = "ED"; const rawPrivateKey = hash(entropy); const privateKey = prefix + bytesToHex(rawPrivateKey); const publicKey = prefix + bytesToHex(Ed25519.keyFromSecret(rawPrivateKey).pubBytes()); return { privateKey, publicKey }; }, deriveKeypairWithPrivateKey: (rawPrivateKey) => { const prefix = "ED"; const privateKey = prefix + rawPrivateKey.toUpperCase(); const publicKey = prefix + bytesToHex(Ed25519.keyFromSecret(hexToBytes(rawPrivateKey)).pubBytes()); return { privateKey, publicKey }; }, sign: (message, privateKey) => { // caution: Ed25519.sign interprets all strings as hex, stripping // any non-hex characters without warning assert(Array.isArray(message), "message must be array of octets"); return bytesToHex(Ed25519.sign(message, hexToBytes(privateKey).slice(1)).toBytes()); }, verify: (message, signature, publicKey) => { return Ed25519.verify(message, hexToBytes(signature), hexToBytes(publicKey).slice(1)); }, signTx: (message, privateKey) => { // caution: Ed25519.sign interprets all strings as hex, stripping // any non-hex characters without warning return Ed25519.sign(message, hexToBytes(privateKey).slice(1)).toHex(); }, verifyTx: (message, signature, publicKey) => { return Ed25519.verify(message, hexToBytes(signature), hexToBytes(publicKey).slice(1)); } }; const ed25519guomi = { deriveKeypair: (entropy) => { const prefix = "ED"; const rawPrivateKey = hash(entropy); const privateKey = prefix + bytesToHex(rawPrivateKey); const publicKey = prefix + bytesToHex(Ed25519.keyFromSecret(rawPrivateKey).pubBytes()); return { privateKey, publicKey }; }, deriveKeypairWithPrivateKey: (rawPrivateKey) => { const prefix = "ED"; const privateKey = prefix + rawPrivateKey.toUpperCase(); const publicKey = prefix + bytesToHex(Ed25519.keyFromSecret(hexToBytes(rawPrivateKey)).pubBytes()); return { privateKey, publicKey }; }, sign: (message, privateKey) => { // caution: Ed25519.sign interprets all strings as hex, stripping // any non-hex characters without warning assert(Array.isArray(message), "message must be array of octets"); return bytesToHex(Ed25519.sign(message, hexToBytes(privateKey).slice(1)).toBytes()); }, verify: (message, signature, publicKey) => { return Ed25519.verify(message, hexToBytes(signature), hexToBytes(publicKey).slice(1)); }, signTx: (message, privateKey) => { // caution: Ed25519.sign interprets all strings as hex, stripping // any non-hex characters without warning return Ed25519.sign(message, hexToBytes(privateKey).slice(1)).toHex(); }, verifyTx: (message, signature, publicKey) => { return Ed25519.verify(message, hexToBytes(signature), hexToBytes(publicKey).slice(1)); } }; function generateSeed(options = {}) { assert(!options.entropy || options.entropy.length >= 16, "entropy too short"); const entropy = options.entropy ? options.entropy.slice(0, 16) : brorand(16); const type = options.algorithm === "ed25519" ? "ed25519" : guomi ? "sm2p256v1" : "secp256k1"; return addressCodec.encodeSeed(entropy, type); } function select(algorithm) { const methods = { secp256k1, sm2p256v1, ed25519, ed25519guomi }; // return guomi ? methods[`${algorithm}guomi`] : methods[algorithm] return algorithm === "ed25519" ? guomi ? methods.ed25519guomi : methods.ed25519 : methods[algorithm]; } function getAlgorithmFromKey(key) { const bytes = hexToBytes(key); return bytes.length === 33 && bytes[0] === 0xed ? "ed25519" : guomi ? "sm2p256v1" : "secp256k1"; } function sign(messageHex, privateKey) { const algorithm = getAlgorithmFromKey(privateKey); return select(algorithm).sign(hexToBytes(messageHex), privateKey); } function verify(messageHex, signature, publicKey) { const algorithm = getAlgorithmFromKey(publicKey); return select(algorithm).verify(hexToBytes(messageHex), signature, publicKey); } function signTx(messageHex, privateKey) { const algorithm = getAlgorithmFromKey(privateKey); return select(algorithm).signTx(messageHex, privateKey); } function verifyTx(messageHex, signature, publicKey) { const algorithm = getAlgorithmFromKey(publicKey); return select(algorithm).verifyTx(messageHex, signature, publicKey); } function deriveKeypair(secret_or_private_key, algorithm = guomi ? "sm2p256v1" : "secp256k1") { const secret = secret_or_private_key; let private_key = secret_or_private_key; let keypair; let method; if (typeof secret_or_private_key !== "string") { // use privateKey to generate keypairs, using algorithm throw new Error("deriving keypair requires secret or private key"); } else if (/^s/.test(secret_or_private_key)) { // use secret to derive, secret has algorithm information already const decoded = addressCodec.decodeSeed(secret); algorithm = decoded.type === "ed25519" ? "ed25519" : guomi ? "sm2p256v1" : "secp256k1"; method = select(algorithm); keypair = method.deriveKeypair(decoded.bytes); } else { // use private key to derive, could be raw privateKey or prefixed privateKey if (secret_or_private_key.length === 64) { // raw private key, has no information on algorithm algorithm = algorithm === "ed25519" ? "ed25519" : guomi ? "sm2p256v1" : "secp256k1"; } else if (secret_or_private_key.length === 66) { // prefixed private key, has information on algorithm if (secret_or_private_key.slice(0, 2) === "00") { algorithm = guomi ? "sm2p256v1" : "secp256k1"; } else if (secret_or_private_key.slice(0, 2).toUpperCase() === "ED") { algorithm = "ed25519"; } else { throw new Error("deriving keypair requires correct prefixed private key"); } private_key = secret_or_private_key.slice(2); } else { throw new Error("deriving keypair requires valid private key"); } method = select(algorithm); keypair = method.deriveKeypairWithPrivateKey(private_key); } const messageToVerify = hash("This test message should verify."); const signature = method.sign(messageToVerify, keypair.privateKey); if (method.verify(messageToVerify, signature, keypair.publicKey) !== true) { throw new Error("derived keypair did not generate verifiable signature"); } return keypair; } function deriveAddressFromBytes(publicKeyBytes) { return addressCodec.encodeAccountID(Buffer.from(guomi ? computePublicKeyHashGm(publicKeyBytes) : computePublicKeyHash(publicKeyBytes))); } function deriveAddress(publicKey) { return deriveAddressFromBytes(hexToBytes(publicKey)); } function deriveNodeAddress(publicKey) { const generatorBytes = addressCodec.decodeNodePublic(publicKey); const accountPublicBytes = guomi ? accountPublicFromPublicGeneratorGm(generatorBytes) : accountPublicFromPublicGenerator(generatorBytes); return deriveAddressFromBytes(accountPublicBytes); } function convertAddressToBytes(address) { return addressCodec.decodeAddress(address).toJSON().data; } function convertBytesToAddress(bytes) { return addressCodec.encodeAddress(bytes); } return { // secp256k1, // secp256k1guomi, // ed25519, // decodeSeed: addressCodec.decodeSeed guomi, token, // for swtc libs addressCodec, deriveKeyPair: deriveKeypair, hash, seedFromPhrase: guomi ? seedFromPhraseGm : seedFromPhrase, signTx, verifyTx, convertAddressToBytes, convertBytesToAddress, checkAddress: addressCodec.isValidAddress, isValidAddress: addressCodec.isValidAddress, // standards deriveKeypair, generateSeed, sign, verify, deriveAddress, deriveNodeAddress }; } //# sourceMappingURL=keypairs.js.map