import * as fs from 'fs' ; import { createHash } from 'crypto' ; import { ec as EC } from 'elliptic' ; const ec = new EC ( 'p192' ) ; type Curve = 'p192' ; export interface Key { curve : Curve ; public : string ; private ? : string ; } type keystore = { [ callsign ] : Key [ ] ; } ; class export class Keystore { readonly path : string ; private _keystore : keystore ; constructor param { string } path constructor ( path ) { this . path = path ; this . _keystore = { } ; if ( ! this . _exists ( ) ) { this . _save ( ) ; } else { this . _keystore = this . _load ( ) ; } } method param { string } callsign param { string } pubkeyHex addPublicKey ( callsign , pubkeyHex ) { this . _addKey ( callsign , pubkeyHex , undefined ) ; } param { string } callsign returns { object } genKeyPair ( callsign ) { const key = ec . genKeyPair ( ) ; const pub = key . getPublic ( 'hex' ) ; const priv = key . getPrivate ( 'hex' ) ; this . _addKey ( callsign , pub , priv ) ; return { public : pub , private : priv , curve : 'p192' } ; } param { string } callsign param { string } pubkeyHex returns { boolean } revoke ( callsign , pubkeyHex ) { if ( ! this . _keystore . hasOwnProperty ( callsign ) ) return false ; const length = this . _keystore [ callsign ] . length ; this . _keystore [ callsign ] = this . _keystore [ callsign ] . filter ( ( key ) => { return key . public !== pubkeyHex ; } ) ; if ( this . _keystore [ callsign ] . length !== length ) { this . _save ( ) ; return true ; } return false ; } method returns { string [ ] } getCallsigns ( ) { return Object . keys ( this . _keystore ) ; } method param { string } callsign returns { string [ ] } getPublicKeys ( callsign ) { if ( ! this . _keystore . hasOwnProperty ( callsign ) ) return [ ] ; return this . _keystore [ callsign ] . map ( key => key . public ) ; } method param { string } callsign returns { Key [ ] } getKeyPairs ( callsign ) { return this . getKeys ( callsign ) . filter ( key => { return typeof key . public === 'string' && typeof key . private === 'string' ; } ) ; } method param { string } callsign returns { Key [ ] } getKeys ( callsign ) { if ( ! this . _keystore . hasOwnProperty ( callsign ) ) return [ ] ; return this . _keystore [ callsign ] ; } method param { string } message param { string } privateHex returns { Buffer } sign ( message , privateHex ) { const key = ec . keyFromPrivate ( privateHex ) ; const hash = this . sha256 ( message ) ; const signature = key . sign ( hash ) . toDER ( ) ; return Buffer . from ( signature ) ; } method param { string } callsign param { string } message param { Buffer } signature returns { boolean } verify ( callsign , message , signature ) { const hash = this . sha256 ( message ) ; for ( let publicKey of this . getPublicKeys ( callsign ) ) { const key = ec . keyFromPublic ( publicKey , 'hex' ) ; if ( key . verify ( hash , signature ) ) return true ; } return false ; } method param { string } message return { Buffer } sha256 ( message ) { return createHash ( 'sha256' ) . update ( message , 'utf8' ) . digest ( ) ; } private _addKey ( callsign , publicHex , privateHex ? ) { if ( typeof callsign !== 'string' ) throw TypeError ( 'callsign must be a string type' ) ; if ( typeof publicHex !== 'string' ) throw TypeError ( 'publicHex must be a string type' ) ; if ( typeof privateHex !== 'undefined' && typeof privateHex !== 'string' ) throw TypeError ( 'if privateHex is not undefined it must be a string type' ) ; if ( ! this . _keystore . hasOwnProperty ( callsign ) ) { this . _keystore [ callsign ] = [ ] ; } const key = { public : publicHex , curve : 'p192' } ; if ( privateHex ) key . private = privateHex ; this . _keystore [ callsign ] . push ( key ) ; this . _save ( ) ; } private _save ( ) { fs . writeFileSync ( this . path , JSON . stringify ( this . _keystore , null , '\t' ) ) ; } private _load ( ) { return JSON . parse ( fs . readFileSync ( this . path ) . toString ( 'utf8' ) ) ; } private _exists ( ) { return fs . existsSync ( this . path ) ; } }