Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added secp256k1 algo for key generation #780

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 31 additions & 1 deletion lib/utils/key_pair.d.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

60 changes: 59 additions & 1 deletion lib/utils/key_pair.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"js-sha256": "^0.9.0",
"mustache": "^4.0.0",
"node-fetch": "^2.6.1",
"secp256k1": "^4.0.3",
"text-encoding-utf-8": "^1.0.2",
"tweetnacl": "^1.0.1"
},
Expand Down
85 changes: 75 additions & 10 deletions src/utils/key_pair.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import nacl from 'tweetnacl';
import { base_encode, base_decode } from './serialize';
import { Assignable } from './enums';
import { randomBytes } from 'crypto';
import secp256k1 from 'secp256k1';

export type Arrayish = string | ArrayLike<number>;

Expand All @@ -12,19 +14,22 @@ export interface Signature {
/** All supported key types */
export enum KeyType {
ED25519 = 0,
SECP256K1 = 1
}

function key_type_to_str(keyType: KeyType): string {
switch (keyType) {
case KeyType.ED25519: return 'ed25519';
default: throw new Error(`Unknown key type ${keyType}`);
case KeyType.ED25519: return 'ed25519';
case KeyType.SECP256K1: return 'secp256k1';
default: throw new Error(`Unknown key type ${keyType}`);
}
}

function str_to_key_type(keyType: string): KeyType {
switch (keyType.toLowerCase()) {
case 'ed25519': return KeyType.ED25519;
default: throw new Error(`Unknown key type ${keyType}`);
case 'ed25519': return KeyType.ED25519;
case 'secp256k1': return KeyType.SECP256K1;
default: throw new Error(`Unknown key type ${keyType}`);
}
}

Expand All @@ -44,6 +49,7 @@ export class PublicKey extends Assignable {

static fromString(encodedKey: string): PublicKey {
const parts = encodedKey.split(':');
//TODO: ask what to do in this case

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need advice on how to deal with this

if (parts.length === 1) {
return new PublicKey({ keyType: KeyType.ED25519, data: base_decode(parts[0]) });
} else if (parts.length === 2) {
Expand All @@ -59,8 +65,9 @@ export class PublicKey extends Assignable {

verify(message: Uint8Array, signature: Uint8Array): boolean {
switch (this.keyType) {
case KeyType.ED25519: return nacl.sign.detached.verify(message, signature, this.data);
default: throw new Error(`Unknown key type ${this.keyType}`);
case KeyType.ED25519: return nacl.sign.detached.verify(message, signature, this.data);
case KeyType.SECP256K1: return secp256k1.ecdsaVerify(signature, message, this.data);
default: throw new Error(`Unknown key type ${this.keyType}`);
}
}
}
Expand All @@ -77,19 +84,22 @@ export abstract class KeyPair {
*/
static fromRandom(curve: string): KeyPair {
switch (curve.toUpperCase()) {
case 'ED25519': return KeyPairEd25519.fromRandom();
default: throw new Error(`Unknown curve ${curve}`);
case 'ED25519': return KeyPairEd25519.fromRandom();
case 'SECP256K1': return KeyPairSecp256k1.fromRandom();
default: throw new Error(`Unknown curve ${curve}`);
}
}

static fromString(encodedKey: string): KeyPair {
const parts = encodedKey.split(':');
//TODO: clarify what we must do here
if (parts.length === 1) {
return new KeyPairEd25519(parts[0]);
} else if (parts.length === 2) {
switch (parts[0].toUpperCase()) {
case 'ED25519': return new KeyPairEd25519(parts[1]);
default: throw new Error(`Unknown curve: ${parts[0]}`);
case 'ED25519': return new KeyPairEd25519(parts[1]);
case 'SECP256K1': return new KeyPairSecp256k1(parts[1]);
default: throw new Error(`Unknown curve: ${parts[0]}`);
}
} else {
throw new Error('Invalid encoded key format, must be <curve>:<encoded key>');
Expand Down Expand Up @@ -149,3 +159,58 @@ export class KeyPairEd25519 extends KeyPair {
return this.publicKey;
}
}
/**
* This class provides key pair functionality for Secp256k1 curve:
* generating key pairs, encoding key pairs, signing and verifying.
*/
export class KeyPairSecp256k1 extends KeyPair {
readonly publicKey: PublicKey;
readonly secretKey: string;

/**
* Construct an instance of key pair given a secret key.
* It's generally assumed that these are encoded in base58.
* @param {string} secretKey
*/
constructor(secretKey: string) {
super();
this.publicKey = new PublicKey({
keyType: KeyType.SECP256K1,
data: secp256k1.publicKeyCreate(base_decode(secretKey))
});
this.secretKey = secretKey;
}

/**
* Generate a new random keypair.
* @example
* const keyRandom = KeyPair.fromRandom();
* keyRandom.publicKey
* // returns [PUBLIC_KEY]
*
* keyRandom.secretKey
* // returns [SECRET_KEY]
*/
static fromRandom() {
// TODO: find better way to generate PK
const secretKey = randomBytes(32);
return new KeyPairSecp256k1(base_encode(secretKey));
}

sign(message: Uint8Array): Signature {
const { signature } = secp256k1.ecdsaSign(message, base_decode(this.secretKey));
return { signature, publicKey: this.publicKey };
}

verify(message: Uint8Array, signature: Uint8Array): boolean {
return this.publicKey.verify(message, signature);
}

toString(): string {
return `secp256k1:${this.secretKey}`;
}

getPublicKey(): PublicKey {
return this.publicKey;
}
}
Loading