import { decrypt , ecCurve , EncryptedMessage , getPubKeyPoint , IMetadata , Point , PolyIDAndShares , Polynomial , PolynomialID , PublicPolynomial , PublicPolynomialMap , PublicShare , PublicSharePolyIDShareIndexMap , Share , ShareDescriptionMap , ShareMap , ShareStore , StringifiedType , toPrivKeyECC , } from "@tkey/common-types" ; import BN from "bn.js" ; import stringify from "json-stable-stringify" ; import CoreError from "./errors" ; import { polyCommitmentEval } from "./lagrangeInterpolatePolynomial" ; class Metadata implements IMetadata { pubKey : Point ; publicPolynomials : PublicPolynomialMap ; publicShares : PublicSharePolyIDShareIndexMap ; polyIDList : PolyIDAndShares [ ] ; generalStore : { [ moduleName ] : unknown ; } ; tkeyStore : { [ moduleName ] : unknown ; } ; scopedStore : { [ moduleName ] : unknown ; } ; nonce : number ; constructor ( input ) { this . publicPolynomials = { } ; this . publicShares = { } ; this . generalStore = { } ; this . tkeyStore = { } ; this . scopedStore = { } ; this . pubKey = input ; this . polyIDList = [ ] ; this . nonce = 0 ; } static fromJSON ( value ) { const { pubKey , polyIDList , generalStore , tkeyStore , scopedStore , nonce } = value ; const point = Point . fromCompressedPub ( pubKey ) ; const metadata = new Metadata ( point ) ; const unserializedPolyIDList = [ ] ; if ( generalStore ) metadata . generalStore = generalStore ; if ( tkeyStore ) metadata . tkeyStore = tkeyStore ; if ( scopedStore ) metadata . scopedStore = scopedStore ; if ( nonce ) metadata . nonce = nonce ; for ( let i = 0 ; i < polyIDList . length ; i += 1 ) { const serializedPolyID = polyIDList [ i ] ; const arrPolyID = serializedPolyID . split ( "|" ) ; const zeroIndex = arrPolyID . findIndex ( ( v ) => v === "0x0" ) ; const firstHalf = arrPolyID . slice ( 0 , zeroIndex ) ; const secondHalf = arrPolyID . slice ( zeroIndex + 1 , arrPolyID . length ) ; const pubPolyID = firstHalf . join ( "|" ) ; const pointCommitments = [ ] ; firstHalf . forEach ( ( compressedCommitment ) => { pointCommitments . push ( Point . fromCompressedPub ( compressedCommitment ) ) ; } ) ; const publicPolynomial = new PublicPolynomial ( pointCommitments ) ; metadata . publicPolynomials [ pubPolyID ] = publicPolynomial ; unserializedPolyIDList . push ( [ pubPolyID , secondHalf ] ) ; } metadata . polyIDList = unserializedPolyIDList ; return metadata ; } getShareIndexesForPolynomial ( polyID ) { const matchingPolyIDs = this . polyIDList . filter ( ( tuple ) => tuple [ 0 ] === polyID ) ; if ( matchingPolyIDs . length < 1 ) { throw CoreError . default ( "there is no matching polyID" ) ; } else if ( matchingPolyIDs . length > 1 ) { throw CoreError . default ( "there is more than one matching polyID" ) ; } return matchingPolyIDs [ 0 ] [ 1 ] ; } getLatestPublicPolynomial ( ) { return this . publicPolynomials [ this . polyIDList [ this . polyIDList . length - 1 ] [ 0 ] ] ; } addPublicShare ( polynomialID , publicShare ) { if ( ! ( polynomialID in this . publicShares ) ) { this . publicShares [ polynomialID ] = { } ; } this . publicShares [ polynomialID ] [ publicShare . shareIndex . toString ( "hex" ) ] = publicShare ; } setGeneralStoreDomain ( key , obj ) { this . generalStore [ key ] = obj ; } getGeneralStoreDomain ( key ) { return this . generalStore [ key ] ; } deleteGeneralStoreDomain ( key ) { delete this . generalStore [ key ] ; } setTkeyStoreDomain ( key , arr ) { this . tkeyStore [ key ] = arr ; } getTkeyStoreDomain ( key ) { return this . tkeyStore [ key ] ; } addFromPolynomialAndShares ( polynomial , shares ) { const publicPolynomial = polynomial . getPublicPolynomial ( ) ; const polyID = publicPolynomial . getPolynomialID ( ) ; this . publicPolynomials [ polyID ] = publicPolynomial ; const shareIndexArr = [ ] ; if ( Array . isArray ( shares ) ) { for ( let i = 0 ; i < shares . length ; i += 1 ) { this . addPublicShare ( publicPolynomial . getPolynomialID ( ) , shares [ i ] . getPublicShare ( ) ) ; shareIndexArr . push ( shares [ i ] . shareIndex . toString ( "hex" ) ) ; } } else { for ( const k in shares ) { if ( Object . prototype . hasOwnProperty . call ( shares , k ) ) { this . addPublicShare ( publicPolynomial . getPolynomialID ( ) , shares [ k ] . getPublicShare ( ) ) ; shareIndexArr . push ( shares [ k ] . shareIndex . toString ( "hex" ) ) ; } } } this . polyIDList . push ( [ polyID , shareIndexArr ] ) ; } setScopedStore ( domain , data ) { this . scopedStore [ domain ] = data ; } async getEncryptedShare ( shareStore ) { const pubShare = shareStore . share . getPublicShare ( ) ; const encryptedShareStore = this . scopedStore . encryptedShares ; if ( ! encryptedShareStore ) { throw CoreError . encryptedShareStoreUnavailable ( `${ shareStore }` ) ; } const encryptedShare = encryptedShareStore [ pubShare . shareCommitment . x . toString ( "hex" ) ] ; if ( ! encryptedShare ) { throw CoreError . encryptedShareStoreUnavailable ( `${ shareStore }` ) ; } const rawDecrypted = await decrypt ( toPrivKeyECC ( shareStore . share . share ) , encryptedShare as EncryptedMessage ) ; return ShareStore . fromJSON ( JSON . parse ( rawDecrypted . toString ( ) ) ) ; } getShareDescription ( ) { return this . getGeneralStoreDomain ( "shareDescriptions" ) as ShareDescriptionMap ; } addShareDescription ( shareIndex , description ) { const currentSD = this . getGeneralStoreDomain ( "shareDescriptions" ) || { } ; if ( currentSD [ shareIndex ] ) { currentSD [ shareIndex ] . push ( description ) ; } else { currentSD [ shareIndex ] = [ description ] ; } this . setGeneralStoreDomain ( "shareDescriptions" , currentSD ) ; } deleteShareDescription ( shareIndex , description ) { const currentSD = this . getGeneralStoreDomain ( "shareDescriptions" ) ; const index = currentSD [ shareIndex ] . indexOf ( description ) ; if ( index > - 1 ) { currentSD [ shareIndex ] . splice ( index , 1 ) ; } else { throw CoreError . default ( `No share description found for the given shareIndex: ${ shareIndex } 
        and description: ${ description }` ) ; } } updateShareDescription ( shareIndex , oldDescription , newDescription ) { const currentSD = this . getGeneralStoreDomain ( "shareDescriptions" ) ; const index = currentSD [ shareIndex ] . indexOf ( oldDescription ) ; if ( index > - 1 ) { currentSD [ shareIndex ] [ index ] = newDescription ; } else { throw CoreError . default ( `No share description found for the given shareIndex:
        ${ shareIndex } and description: ${ oldDescription }` ) ; } } shareToShareStore ( share ) { const pubkey = getPubKeyPoint ( share ) ; let returnShare ; for ( let i = this . polyIDList . length - 1 ; i >= 0 ; i -= 1 ) { const el = this . polyIDList [ i ] [ 0 ] ; for ( let t = 0 ; t < this . polyIDList [ i ] [ 1 ] . length ; t += 1 ) { const shareIndex = this . polyIDList [ i ] [ 1 ] [ t ] ; let pubShare ; if ( this . publicShares [ el ] ) { if ( this . publicShares [ el ] [ shareIndex ] ) { pubShare = this . publicShares [ el ] [ shareIndex ] ; } } if ( ! pubShare ) { pubShare = new PublicShare ( shareIndex , polyCommitmentEval ( this . publicPolynomials [ el ] . polynomialCommitments , new BN ( shareIndex , "hex" ) ) ) ; } if ( pubShare . shareCommitment . x . eq ( pubkey . x ) && pubShare . shareCommitment . y . eq ( pubkey . y ) ) { const tempShare = new Share ( pubShare . shareIndex , share ) ; return new ShareStore ( tempShare , el ) ; } } } if ( ! returnShare ) { throw CoreError . fromCode ( 1307 ) ; } return returnShare ; } clone ( ) { return Metadata . fromJSON ( JSON . parse ( stringify ( this ) ) ) ; } toJSON ( ) { const serializedPolyIDList = [ ] ; for ( let i = 0 ; i < this . polyIDList . length ; i += 1 ) { const polyID = this . polyIDList [ i ] [ 0 ] ; const shareIndexes = this . polyIDList [ i ] [ 1 ] ; const sortedShareIndexes = shareIndexes . sort ( ( a , b ) => new BN ( a , "hex" ) . cmp ( new BN ( b , "hex" ) ) ) ; const serializedPolyID = polyID . split ( `|` ) . concat ( "0x0" ) . concat ( ... sortedShareIndexes ) . join ( "|" ) ; serializedPolyIDList . push ( serializedPolyID ) ; } return { pubKey : this . pubKey . encode ( "elliptic-compressed" , { ec : ecCurve } ) . toString ( ) , polyIDList : serializedPolyIDList , scopedStore : this . scopedStore , generalStore : this . generalStore , tkeyStore : this . tkeyStore , nonce : this . nonce , } ; } } export default Metadata ;