119 lines
4.3 KiB
JavaScript
119 lines
4.3 KiB
JavaScript
"use strict";
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.Secp256k1 = void 0;
|
|
const encoding_1 = require("@cosmjs/encoding");
|
|
const utils_1 = require("@cosmjs/utils");
|
|
const secp256k1_1 = require("@noble/curves/secp256k1");
|
|
const secp256k1signature_1 = require("./secp256k1signature");
|
|
function unsignedBigIntToBytes(a) {
|
|
(0, utils_1.assert)(a >= 0n);
|
|
let hex = a.toString(16);
|
|
if (hex.length % 2)
|
|
hex = "0" + hex;
|
|
return (0, encoding_1.fromHex)(hex);
|
|
}
|
|
function bytesToUnsignedBigInt(a) {
|
|
return BigInt("0x" + (0, encoding_1.toHex)(a));
|
|
}
|
|
class Secp256k1 {
|
|
/**
|
|
* Takes a 32 byte private key and returns a privkey/pubkey pair.
|
|
*
|
|
* The resulting pubkey is uncompressed. For the use in Cosmos it should
|
|
* be compressed first using `Secp256k1.compressPubkey`.
|
|
*/
|
|
static async makeKeypair(privkey) {
|
|
if (privkey.length !== 32) {
|
|
throw new Error("input data is not a valid secp256k1 private key");
|
|
}
|
|
if (!secp256k1_1.secp256k1.utils.isValidPrivateKey(privkey)) {
|
|
// not strictly smaller than N
|
|
throw new Error("input data is not a valid secp256k1 private key");
|
|
}
|
|
const out = {
|
|
privkey: privkey,
|
|
// encodes uncompressed as
|
|
// - 1-byte prefix "04"
|
|
// - 32-byte x coordinate
|
|
// - 32-byte y coordinate
|
|
pubkey: secp256k1_1.secp256k1.getPublicKey(privkey, false),
|
|
};
|
|
return out;
|
|
}
|
|
/**
|
|
* Creates a signature that is
|
|
* - deterministic (RFC 6979)
|
|
* - lowS signature
|
|
* - DER encoded
|
|
*/
|
|
static async createSignature(messageHash, privkey) {
|
|
if (messageHash.length === 0) {
|
|
throw new Error("Message hash must not be empty");
|
|
}
|
|
if (messageHash.length > 32) {
|
|
throw new Error("Message hash length must not exceed 32 bytes");
|
|
}
|
|
const { recovery, r, s } = secp256k1_1.secp256k1.sign(messageHash, privkey, {
|
|
lowS: true,
|
|
});
|
|
if (typeof recovery !== "number")
|
|
throw new Error("Recovery param missing");
|
|
return new secp256k1signature_1.ExtendedSecp256k1Signature(unsignedBigIntToBytes(r), unsignedBigIntToBytes(s), recovery);
|
|
}
|
|
static async verifySignature(signature, messageHash, pubkey) {
|
|
if (messageHash.length === 0) {
|
|
throw new Error("Message hash must not be empty");
|
|
}
|
|
if (messageHash.length > 32) {
|
|
throw new Error("Message hash length must not exceed 32 bytes");
|
|
}
|
|
const encodedSig = secp256k1_1.secp256k1.Signature.fromDER(signature.toDer());
|
|
return secp256k1_1.secp256k1.verify(encodedSig, messageHash, pubkey, { lowS: false });
|
|
}
|
|
static recoverPubkey(signature, messageHash) {
|
|
const pk = new secp256k1_1.secp256k1.Signature(bytesToUnsignedBigInt(signature.r()), bytesToUnsignedBigInt(signature.s()), signature.recovery).recoverPublicKey(messageHash);
|
|
return pk.toBytes(false);
|
|
}
|
|
/**
|
|
* Takes a compressed or uncompressed pubkey and return a compressed one.
|
|
*
|
|
* This function is idempotent.
|
|
*/
|
|
static compressPubkey(pubkey) {
|
|
switch (pubkey.length) {
|
|
case 33:
|
|
return pubkey;
|
|
case 65:
|
|
return secp256k1_1.secp256k1.Point.fromBytes(pubkey).toBytes(true);
|
|
default:
|
|
throw new Error("Invalid pubkey length");
|
|
}
|
|
}
|
|
/**
|
|
* Takes a compressed or uncompressed pubkey and returns an uncompressed one.
|
|
*
|
|
* This function is idempotent.
|
|
*/
|
|
static uncompressPubkey(pubkey) {
|
|
switch (pubkey.length) {
|
|
case 33:
|
|
return secp256k1_1.secp256k1.Point.fromBytes(pubkey).toBytes(false);
|
|
case 65:
|
|
return pubkey;
|
|
default:
|
|
throw new Error("Invalid pubkey length");
|
|
}
|
|
}
|
|
static trimRecoveryByte(signature) {
|
|
switch (signature.length) {
|
|
case 64:
|
|
return signature;
|
|
case 65:
|
|
return signature.slice(0, 64);
|
|
default:
|
|
throw new Error("Invalid signature length");
|
|
}
|
|
}
|
|
}
|
|
exports.Secp256k1 = Secp256k1;
|
|
//# sourceMappingURL=secp256k1.js.map
|