Add Pen
This commit is contained in:
parent
903166478b
commit
9e4a79f670
@ -4,4 +4,5 @@ export { CosmosBech32Prefix, decodeBech32Pubkey, encodeAddress, isValidAddress }
|
||||
export { unmarshalTx } from "./decoding";
|
||||
export { encodeSecp256k1Signature, makeSignBytes, marshalTx } from "./encoding";
|
||||
export { RestClient, TxsResponse } from "./restclient";
|
||||
export { makeCosmoshubPath, Pen, PrehashType, Secp256k1Pen } from "./pen";
|
||||
export { types };
|
||||
|
||||
49
packages/sdk/src/pen.spec.ts
Normal file
49
packages/sdk/src/pen.spec.ts
Normal file
@ -0,0 +1,49 @@
|
||||
import { Secp256k1, Secp256k1Signature, Sha256 } from "@iov/crypto";
|
||||
import { Encoding } from "@iov/encoding";
|
||||
|
||||
import { Secp256k1Pen } from "./pen";
|
||||
|
||||
const { fromHex } = Encoding;
|
||||
|
||||
describe("Sec256k1Pen", () => {
|
||||
it("can be constructed", () => {
|
||||
const pen = new Secp256k1Pen(
|
||||
"zebra slush diet army arrest purpose hawk source west glimpse custom record",
|
||||
);
|
||||
expect(pen).toBeTruthy();
|
||||
});
|
||||
|
||||
describe("getPubkey", () => {
|
||||
it("returns compressed pubkey", async () => {
|
||||
// special sign fit simple patrol salute grocery chicken wheat radar tonight ceiling
|
||||
// m/44'/118'/0'/0/0
|
||||
// pubkey: 02baa4ef93f2ce84592a49b1d729c074eab640112522a7a89f7d03ebab21ded7b6
|
||||
const pen = new Secp256k1Pen(
|
||||
"special sign fit simple patrol salute grocery chicken wheat radar tonight ceiling",
|
||||
);
|
||||
expect(await pen.getPubkey()).toEqual(
|
||||
fromHex("02baa4ef93f2ce84592a49b1d729c074eab640112522a7a89f7d03ebab21ded7b6"),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("createSignature", () => {
|
||||
it("creates correct signatures", async () => {
|
||||
// special sign fit simple patrol salute grocery chicken wheat radar tonight ceiling
|
||||
// m/44'/118'/0'/0/0
|
||||
// pubkey: 02baa4ef93f2ce84592a49b1d729c074eab640112522a7a89f7d03ebab21ded7b6
|
||||
const pen = new Secp256k1Pen(
|
||||
"special sign fit simple patrol salute grocery chicken wheat radar tonight ceiling",
|
||||
);
|
||||
const data = Encoding.toAscii("foo bar");
|
||||
const signature = await pen.createSignature(data);
|
||||
|
||||
const valid = await Secp256k1.verifySignature(
|
||||
new Secp256k1Signature(signature.slice(0, 32), signature.slice(32, 64)),
|
||||
new Sha256(data).digest(),
|
||||
fromHex("02baa4ef93f2ce84592a49b1d729c074eab640112522a7a89f7d03ebab21ded7b6"),
|
||||
);
|
||||
expect(valid).toEqual(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
87
packages/sdk/src/pen.ts
Normal file
87
packages/sdk/src/pen.ts
Normal file
@ -0,0 +1,87 @@
|
||||
import {
|
||||
Bip39,
|
||||
EnglishMnemonic,
|
||||
Secp256k1,
|
||||
Sha256,
|
||||
Sha512,
|
||||
Slip10,
|
||||
Slip10Curve,
|
||||
Slip10RawIndex,
|
||||
} from "@iov/crypto";
|
||||
|
||||
export type PrehashType = "sha256" | "sha512" | null;
|
||||
|
||||
/**
|
||||
* A pen is the most basic tool you can think of for signing. It works
|
||||
* everywhere and can be used intuitively by everyone. However, it does not
|
||||
* come with a great amount of features. End of semi suitable metaphor.
|
||||
*
|
||||
* This wraps a single keypair and allows for signing.
|
||||
*
|
||||
* Non-goals of this types are: multi account support, persistency, data migrations,
|
||||
* obfuscation of sensitive data.
|
||||
*/
|
||||
export interface Pen {
|
||||
readonly getPubkey: () => Promise<Uint8Array>;
|
||||
readonly createSignature: (signBytes: Uint8Array, prehashType?: PrehashType) => Promise<Uint8Array>;
|
||||
}
|
||||
|
||||
function prehash(bytes: Uint8Array, type: PrehashType): Uint8Array {
|
||||
switch (type) {
|
||||
case null:
|
||||
return new Uint8Array([...bytes]);
|
||||
case "sha256":
|
||||
return new Sha256(bytes).digest();
|
||||
case "sha512":
|
||||
return new Sha512(bytes).digest();
|
||||
default:
|
||||
throw new Error("Unknown prehash type");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The Cosmoshub derivation path in the form `m/44'/118'/0'/0/a`
|
||||
* with 0-based account index `a`.
|
||||
*/
|
||||
export function makeCosmoshubPath(a: number): readonly Slip10RawIndex[] {
|
||||
return [
|
||||
Slip10RawIndex.hardened(44),
|
||||
Slip10RawIndex.hardened(118),
|
||||
Slip10RawIndex.hardened(0),
|
||||
Slip10RawIndex.normal(0),
|
||||
Slip10RawIndex.normal(a),
|
||||
];
|
||||
}
|
||||
|
||||
export class Secp256k1Pen implements Pen {
|
||||
private readonly mnemonic: EnglishMnemonic;
|
||||
private readonly hdPath: readonly Slip10RawIndex[];
|
||||
|
||||
public constructor(mnemonic: string, hdPath: readonly Slip10RawIndex[] = makeCosmoshubPath(0)) {
|
||||
this.mnemonic = new EnglishMnemonic(mnemonic);
|
||||
this.hdPath = hdPath;
|
||||
}
|
||||
|
||||
public async getPubkey(): Promise<Uint8Array> {
|
||||
const privkey = await this.getPrivkey();
|
||||
const uncompressed = (await Secp256k1.makeKeypair(privkey)).pubkey;
|
||||
return Secp256k1.compressPubkey(uncompressed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a fixed length encoding of the signature parameters r (32 bytes) and s (32 bytes).
|
||||
*/
|
||||
public async createSignature(
|
||||
signBytes: Uint8Array,
|
||||
prehashType: PrehashType = "sha256",
|
||||
): Promise<Uint8Array> {
|
||||
const message = prehash(signBytes, prehashType);
|
||||
const signature = await Secp256k1.createSignature(message, await this.getPrivkey());
|
||||
return new Uint8Array([...signature.r(32), ...signature.s(32)]);
|
||||
}
|
||||
|
||||
private async getPrivkey(): Promise<Uint8Array> {
|
||||
const seed = await Bip39.mnemonicToSeed(this.mnemonic);
|
||||
return Slip10.derivePath(Slip10Curve.Secp256k1, seed, this.hdPath).privkey;
|
||||
}
|
||||
}
|
||||
1
packages/sdk/types/index.d.ts
vendored
1
packages/sdk/types/index.d.ts
vendored
@ -3,4 +3,5 @@ export { CosmosBech32Prefix, decodeBech32Pubkey, encodeAddress, isValidAddress }
|
||||
export { unmarshalTx } from "./decoding";
|
||||
export { encodeSecp256k1Signature, makeSignBytes, marshalTx } from "./encoding";
|
||||
export { RestClient, TxsResponse } from "./restclient";
|
||||
export { makeCosmoshubPath, Pen, PrehashType, Secp256k1Pen } from "./pen";
|
||||
export { types };
|
||||
|
||||
32
packages/sdk/types/pen.d.ts
vendored
Normal file
32
packages/sdk/types/pen.d.ts
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
import { Slip10RawIndex } from "@iov/crypto";
|
||||
export declare type PrehashType = "sha256" | "sha512" | null;
|
||||
/**
|
||||
* A pen is the most basic tool you can think of for signing. It works
|
||||
* everywhere and can be used intuitively by everyone. However, it does not
|
||||
* come with a great amount of features. End of semi suitable metaphor.
|
||||
*
|
||||
* This wraps a single keypair and allows for signing.
|
||||
*
|
||||
* Non-goals of this types are: multi account support, persistency, data migrations,
|
||||
* obfuscation of sensitive data.
|
||||
*/
|
||||
export interface Pen {
|
||||
readonly getPubkey: () => Promise<Uint8Array>;
|
||||
readonly createSignature: (signBytes: Uint8Array, prehashType?: PrehashType) => Promise<Uint8Array>;
|
||||
}
|
||||
/**
|
||||
* The Cosmoshub derivation path in the form `m/44'/118'/0'/0/a`
|
||||
* with 0-based account index `a`.
|
||||
*/
|
||||
export declare function makeCosmoshubPath(a: number): readonly Slip10RawIndex[];
|
||||
export declare class Secp256k1Pen implements Pen {
|
||||
private readonly mnemonic;
|
||||
private readonly hdPath;
|
||||
constructor(mnemonic: string, hdPath?: readonly Slip10RawIndex[]);
|
||||
getPubkey(): Promise<Uint8Array>;
|
||||
/**
|
||||
* Creates a fixed length encoding of the signature parameters r (32 bytes) and s (32 bytes).
|
||||
*/
|
||||
createSignature(signBytes: Uint8Array, prehashType?: PrehashType): Promise<Uint8Array>;
|
||||
private getPrivkey;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user