chedder
Version:
146 lines (126 loc) • 4.44 kB
text/typescript
import {Assignable} from './enums.js';
import * as nacl from '../tweetnacl/sign.js';
import { base_encode, base_decode } from './serialize.js';
//import { Assignable } from './enums';
export type Arrayish = string | ArrayLike<number>;
export interface Signature {
signature: Uint8Array;
publicKey: PublicKey;
}
/** All supported key types */
export enum KeyType {
ED25519 = 0,
}
function key_type_to_str(keyType: KeyType): string {
switch (keyType) {
case KeyType.ED25519: return 'ed25519';
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}`);
}
}
/**
* PublicKey representation that has type and bytes of the key.
*/
export class PublicKey extends Assignable {
keyType!: KeyType
data!: Uint8Array
static from(value: string | PublicKey): PublicKey {
if (typeof value === 'string') {
return PublicKey.fromString(value);
}
return value;
}
static fromString(encodedKey: string): PublicKey {
const parts = encodedKey.split(':');
if (parts.length === 1) {
return new PublicKey({keyType:KeyType.ED25519, data:base_decode(parts[0])});
} else if (parts.length === 2) {
return new PublicKey({keyType:str_to_key_type(parts[0]),data:base_decode(parts[1])});
} else {
throw new Error('Invalid encoded key format, must be <curve>:<encoded key>');
}
}
toString(): string {
return `${key_type_to_str(this.keyType)}:${base_encode(this.data)}`;
}
}
export abstract class KeyPair {
abstract sign(message: Uint8Array): Signature;
abstract verify(message: Uint8Array, signature: Uint8Array): boolean;
abstract toString(): string;
abstract getPublicKey(): PublicKey;
/**
* @param curve Name of elliptical curve, case-insensitive
* @returns Random KeyPair based on the curve
*/
static fromRandom(curve: string): KeyPair {
switch (curve.toUpperCase()) {
case 'ED25519': return KeyPairEd25519.fromRandom();
default: throw new Error(`Unknown curve ${curve}`);
}
}
static fromString(encodedKey: string): KeyPair {
const parts = encodedKey.split(':');
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]}`);
}
} else {
throw new Error('Invalid encoded key format, must be <curve>:<encoded key>');
}
}
}
/**
* This class provides key pair functionality for Ed25519 curve:
* generating key pairs, encoding key pairs, signing and verifying.
*/
export class KeyPairEd25519 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();
const keyPair = nacl.sign_keyPair_fromSecretKey(base_decode(secretKey));
this.publicKey = new PublicKey({keyType:KeyType.ED25519, data:keyPair.publicKey});
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() {
const newKeyPair = nacl.sign_keyPair();
return new KeyPairEd25519(base_encode(newKeyPair.secretKey));
}
sign(message: Uint8Array): Signature {
const signature = nacl.sign_detached(message, base_decode(this.secretKey));
return { signature, publicKey: this.publicKey };
}
verify(message: Uint8Array, signature: Uint8Array): boolean {
return nacl.sign_detached_verify(message, signature, this.publicKey.data);
}
toString(): string {
return `ed25519:${this.secretKey}`;
}
getPublicKey(): PublicKey {
return this.publicKey;
}
}