import { AbstractLevelDOWN , AbstractIterator , AbstractOpenOptions , ErrorCallback , AbstractGetOptions , ErrorValueCallback , AbstractOptions , AbstractBatch , AbstractIteratorOptions , ErrorKeyValueCallback , } from 'abstract-leveldown' ; import { ICrypto , IKeyPair } from '../types' ; export interface VirgilEncryptDownOptions { virgilCrypto : ICrypto ; keyPair ? : IKeyPair ; } export type ValueType = string | Buffer ; class VirgilEncryptDownIterator < K > extends AbstractIterator < K , ValueType > { options : AbstractIteratorOptions ; it : AbstractIterator < K , Buffer > ; constructor ( db , options ) { super ( db ) ; this . options = options ; this . it = db . db . iterator ( { ... options , valueAsBuffer : true } ) ; } _next ( callback ) { this . it . next ( ( err , key , encrypted ) => { if ( err ) { return callback ( err , undefined ! , undefined ! ) ; } if ( key === undefined && encrypted === undefined ) { return callback ( undefined , undefined ! , undefined ! ) ; } try { const decrypted = this . db . decrypt ( encrypted ) ; callback ( undefined , key , this . options . valueAsBuffer ? decrypted : decrypted . toString ( 'utf8' ) ) ; } catch ( err ) { callback ( err , undefined ! , undefined ! ) ; } } ) ; } _end ( callback ) { this . it . end ( callback ) ; } } class VirgilEncryptDown < K > extends AbstractLevelDOWN < K , ValueType > { db : AbstractLevelDOWN ; crypto : ICrypto ; keyPair ? : IKeyPair ; constructor ( db , options ) { super ( 'ignored' ) ; this . db = db ; this . crypto = options . virgilCrypto ; this . keyPair = options . keyPair ; } _open ( options , callback ) { this . db . open ( options , callback ) ; } _close ( callback ) { this . db . close ( callback ) ; } _get ( key , options , callback ) { this . db . get ( key , { ... options , asBuffer : true } , ( err , encrypted ) => { if ( err ) { return callback ( err , undefined ! ) ; } try { const decrypted = this . decrypt ( encrypted ) ; callback ( undefined , options . asBuffer ? decrypted : decrypted . toString ( 'utf8' ) ) ; } catch ( error ) { callback ( error , undefined ! ) ; } } ) ; } _put ( key , value , options , callback ) { let encrypted ; try { encrypted = this . encrypt ( value ) ; } catch ( err ) { return setTimeout ( ( ) => callback ( err ) ) ; } this . _db . put ( key , encrypted , options , callback ) ; } _del ( key , options , callback ) { this . db . del ( key , options , callback ) ; } _batch ( ops , options , callback ) { let operations ; try { operations = ops . map ( op => { if ( op . type === 'put' ) { return { ... op , value : this . encrypt ( op . value ) } ; } return op ; } ) ; } catch ( err ) { return setTimeout ( ( ) => callback ( err ) ) ; } this . db . batch ( operations , options , callback ) ; } _clear ( options , callback ) { this . db . clear ( options , callback ) ; } _iterator ( options ) { return new VirgilEncryptDownIterator < K > ( this , options ) ; } setKeyPair ( keyPair ) { this . keyPair = keyPair ; } encrypt ( value ) { if ( ! this . keyPair ) { throw new Error ( 'Cannot encrypt value. Key pair is not set. Call "setKeyPair" first.' ) ; } return this . crypto . signThenEncrypt ( value , this . keyPair . privateKey , this . keyPair . publicKey ) ; } decrypt ( encryptedValue ) { if ( ! this . keyPair ) { throw new Error ( 'Cannot decrypt value. Key pair is not set. Call "setKeyPair" first.' ) ; } return this . crypto . decryptThenVerify ( encryptedValue , this . keyPair . privateKey , this . keyPair . publicKey ) ; } } export default VirgilEncryptDown ;