Merge pull request #1047 from cosmos/implement-Secp256k1.uncompressPubkey
Add Secp256k1.uncompressPubkey
This commit is contained in:
commit
f74df61f83
@ -9,6 +9,7 @@ and this project adheres to
|
||||
### Changed
|
||||
|
||||
- all: The TypeScript compilation target is now ES2018.
|
||||
- @cosmjs/crypto: Add `Secp256k1.uncompressPubkey`.
|
||||
- @cosmjs/faucet: Set default value of `FAUCET_GAS_LIMIT` to 100_000 to better
|
||||
support Cosmos SDK 0.45 chains.
|
||||
- @cosmjs/stargate: The `AminoTypes` now always requires an argument of type
|
||||
|
||||
@ -570,6 +570,30 @@ describe("Secp256k1", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("uncompressPubkey", () => {
|
||||
it("throws for a pubkey with invalid length", () => {
|
||||
const pubkey = fromHex("aa".repeat(32));
|
||||
expect(() => Secp256k1.uncompressPubkey(pubkey)).toThrowError(/invalid pubkey length/i);
|
||||
});
|
||||
|
||||
it("returns an uncompressPubkey pubkey unchanged", () => {
|
||||
// Test data generated at https://iancoleman.io/bitcoin-key-compression/
|
||||
const pubkey = fromHex(
|
||||
"044f04181eeba35391b858633a765c4a0c189697b40d216354d50890d350c7029013b587a681e836cc187a8164b98a5848a2b89b3173315fdd0740d5032e259cd5",
|
||||
);
|
||||
expect(Secp256k1.uncompressPubkey(pubkey)).toEqual(pubkey);
|
||||
});
|
||||
|
||||
it("uncompresses a compressed pubkey", () => {
|
||||
// Test data generated at https://iancoleman.io/bitcoin-key-compression/
|
||||
const uncompressed = fromHex(
|
||||
"044f04181eeba35391b858633a765c4a0c189697b40d216354d50890d350c7029013b587a681e836cc187a8164b98a5848a2b89b3173315fdd0740d5032e259cd5",
|
||||
);
|
||||
const compressed = fromHex("034f04181eeba35391b858633a765c4a0c189697b40d216354d50890d350c70290");
|
||||
expect(Secp256k1.uncompressPubkey(compressed)).toEqual(uncompressed);
|
||||
});
|
||||
});
|
||||
|
||||
describe("trimRecoveryByte", () => {
|
||||
it("throws for a signature with invalid length", () => {
|
||||
const signature = fromHex("aa".repeat(66));
|
||||
|
||||
@ -8,11 +8,26 @@ const secp256k1 = new elliptic.ec("secp256k1");
|
||||
const secp256k1N = new BN("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", "hex");
|
||||
|
||||
export interface Secp256k1Keypair {
|
||||
/** A 32 byte private key */
|
||||
readonly pubkey: Uint8Array;
|
||||
/**
|
||||
* A raw secp256k1 public key.
|
||||
*
|
||||
* The type itself does not give you any guarantee if this is
|
||||
* compressed or uncompressed. If you are unsure where the data
|
||||
* is coming from, use `Secp256k1.compressPubkey` or
|
||||
* `Secp256k1.uncompressPubkey` (both idempotent) before processing it.
|
||||
*/
|
||||
readonly privkey: Uint8Array;
|
||||
}
|
||||
|
||||
export 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`.
|
||||
*/
|
||||
public static async makeKeypair(privkey: Uint8Array): Promise<Secp256k1Keypair> {
|
||||
if (privkey.length !== 32) {
|
||||
// is this check missing in secp256k1.validatePrivateKey?
|
||||
@ -43,10 +58,12 @@ export class Secp256k1 {
|
||||
return out;
|
||||
}
|
||||
|
||||
// Creates a signature that is
|
||||
// - deterministic (RFC 6979)
|
||||
// - lowS signature
|
||||
// - DER encoded
|
||||
/**
|
||||
* Creates a signature that is
|
||||
* - deterministic (RFC 6979)
|
||||
* - lowS signature
|
||||
* - DER encoded
|
||||
*/
|
||||
public static async createSignature(
|
||||
messageHash: Uint8Array,
|
||||
privkey: Uint8Array,
|
||||
@ -110,6 +127,11 @@ export class Secp256k1 {
|
||||
return fromHex(keypair.getPublic(false, "hex"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a compressed or uncompressed pubkey and return a compressed one.
|
||||
*
|
||||
* This function is idempotent.
|
||||
*/
|
||||
public static compressPubkey(pubkey: Uint8Array): Uint8Array {
|
||||
switch (pubkey.length) {
|
||||
case 33:
|
||||
@ -121,6 +143,22 @@ export class Secp256k1 {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a compressed or uncompressed pubkey and returns an uncompressed one.
|
||||
*
|
||||
* This function is idempotent.
|
||||
*/
|
||||
public static uncompressPubkey(pubkey: Uint8Array): Uint8Array {
|
||||
switch (pubkey.length) {
|
||||
case 33:
|
||||
return Uint8Array.from(secp256k1.keyFromPublic(pubkey).getPublic(false, "array"));
|
||||
case 65:
|
||||
return pubkey;
|
||||
default:
|
||||
throw new Error("Invalid pubkey length");
|
||||
}
|
||||
}
|
||||
|
||||
public static trimRecoveryByte(signature: Uint8Array): Uint8Array {
|
||||
switch (signature.length) {
|
||||
case 64:
|
||||
|
||||
Loading…
Reference in New Issue
Block a user