import { randomBytes } from "crypto" ; import { SecurityManager } from "./Manager" ; const networkKey = Buffer . from ( [ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12 , 13 , 14 , 15 , 16 ] ) ; const ownNodeId = 1 ; const options = { networkKey , ownNodeId , nonceTimeout : 500 } ; describe ( "lib/security/Manager" , ( ) => { beforeAll ( ( ) => { jest . useFakeTimers ( ) ; } ) ; afterAll ( ( ) => { jest . clearAllTimers ( ) ; jest . useRealTimers ( ) ; } ) ; describe ( "constructor()" , ( ) => { it ( "should set the network key, auth key and encryption key" , ( ) => { const man = new SecurityManager ( options ) ; expect ( man . networkKey ) . toEqual ( networkKey ) ; expect ( Buffer . isBuffer ( man . authKey ) ) . toBeTrue ( ) ; expect ( man . authKey . length ) . toBe ( 16 ) ; expect ( Buffer . isBuffer ( man . encryptionKey ) ) . toBeTrue ( ) ; expect ( man . encryptionKey . length ) . toBe ( 16 ) ; } ) ; it ( "should throw if the network key doesn't have length 16" , ( ) => { expect ( ( ) => new SecurityManager ( { networkKey : Buffer . from ( [ ] ) , ownNodeId : 1 , nonceTimeout : 500 } ) ) . toThrow ( "16 bytes" ) ; } ) ; } ) ; describe ( "generateNonce" , ( ) => { it ( "should return a random Buffer of the given length" , ( ) => { const man = new SecurityManager ( options ) ; const nonce1 = man . generateNonce ( 2 , 8 ) ; const nonce2 = man . generateNonce ( 2 , 8 ) ; const nonce3 = man . generateNonce ( 2 , 8 ) ; expect ( Buffer . isBuffer ( nonce1 ) ) . toBeTrue ( ) ; expect ( Buffer . isBuffer ( nonce2 ) ) . toBeTrue ( ) ; expect ( Buffer . isBuffer ( nonce3 ) ) . toBeTrue ( ) ; expect ( nonce1 . length ) . toBe ( 8 ) ; expect ( nonce2 . length ) . toBe ( 8 ) ; expect ( nonce3 . length ) . toBe ( 8 ) ; } ) ; it ( "should ensure that no collisions happen" , ( ) => { jest . resetModules ( ) ; jest . isolateModules ( ( ) => { const buf1a = Buffer . from ( [ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 ] ) ; const buf1b = Buffer . from ( [ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 9 ] ) ; const buf2 = Buffer . from ( [ 2 , 2 , 3 , 4 , 5 , 6 , 7 , 8 ] ) ; jest . mock ( "crypto" ) ; const crypto = require ( "crypto" ) ; const original = jest . requireActual ( "crypto" ) ; ( crypto . randomBytes as jest . Mock ) . mockReturnValueOnce ( buf1a ) . mockReturnValueOnce ( buf1b ) . mockReturnValueOnce ( buf2 ) ; crypto . createCipheriv = original . createCipheriv ; crypto . createDecipheriv = original . createDecipheriv ; const SM = require ( "./Manager" ) . SecurityManager ; const man = new SM ( options ) ; const nonce1 = man . generateNonce ( 2 , 8 ) ; const nonce2 = man . generateNonce ( 2 , 8 ) ; expect ( nonce1 ) . toEqual ( buf1a ) ; expect ( nonce2 ) . toEqual ( buf2 ) ; expect ( man . getNonce ( 1 ) ) . toEqual ( buf1a ) ; expect ( man . getNonce ( 2 ) ) . toEqual ( buf2 ) ; jest . resetModules ( ) ; } ) ; } ) ; it ( "should store nonces for the current node id" , ( ) => { const man = new SecurityManager ( options ) ; const nonce1 = man . generateNonce ( 2 , 8 ) ; const nonce2 = man . generateNonce ( 2 , 8 ) ; const nonce3 = man . generateNonce ( 2 , 8 ) ; expect ( man . getNonce ( { issuer : 2 , nonceId : man . getNonceId ( nonce1 ) } ) ) . toBeUndefined ( ) ; expect ( man . getNonce ( { issuer : 2 , nonceId : man . getNonceId ( nonce2 ) } ) ) . toBeUndefined ( ) ; expect ( man . getNonce ( { issuer : 2 , nonceId : man . getNonceId ( nonce3 ) } ) ) . toBeUndefined ( ) ; } ) ; it ( "the nonces should expire after the given timeout" , ( ) => { const man = new SecurityManager ( options ) ; const nonce = man . generateNonce ( 2 , 8 ) ; const nonceId = nonce [ 0 ] ; expect ( man . getNonce ( nonceId ) ) . toEqual ( nonce ) ; jest . advanceTimersByTime ( options . nonceTimeout + 50 ) ; expect ( man . getNonce ( nonceId ) ) . toBeUndefined ( ) ; } ) ; it ( `the nonce should be marked as "reserved"` , ( ) => { const man = new SecurityManager ( options ) ; man . generateNonce ( 2 , 8 ) ; expect ( man . getFreeNonce ( ownNodeId ) ) . toBeUndefined ( ) ; } ) ; } ) ; describe ( "getNonceId" , ( ) => { it ( "should return the first byte of the nonce" , ( ) => { const man = new SecurityManager ( options ) ; const nonce1 = man . generateNonce ( 2 , 8 ) ; const nonce2 = man . generateNonce ( 2 , 8 ) ; const nonce3 = man . generateNonce ( 2 , 8 ) ; expect ( man . getNonceId ( nonce1 ) ) . toBe ( nonce1 [ 0 ] ) ; expect ( man . getNonceId ( nonce2 ) ) . toBe ( nonce2 [ 0 ] ) ; expect ( man . getNonceId ( nonce3 ) ) . toBe ( nonce3 [ 0 ] ) ; } ) ; } ) ; describe ( "getNonce" , ( ) => { it ( "should return a previously generated nonce with the same id" , ( ) => { const man = new SecurityManager ( options ) ; const nonce1 = man . generateNonce ( 2 , 8 ) ; const nonce2 = man . generateNonce ( 2 , 8 ) ; const nonce3 = man . generateNonce ( 2 , 8 ) ; const nonceId1 = man . getNonceId ( nonce1 ) ; const nonceId2 = man . getNonceId ( nonce2 ) ; const nonceId3 = man . getNonceId ( nonce3 ) ; expect ( nonce1 ) . toEqual ( man . getNonce ( nonceId1 ) ) ; expect ( nonce2 ) . toEqual ( man . getNonce ( nonceId2 ) ) ; expect ( nonce3 ) . toEqual ( man . getNonce ( nonceId3 ) ) ; } ) ; } ) ; describe ( "setNonce" , ( ) => { it ( "should store a given nonce to be retrieved later" , ( ) => { const man = new SecurityManager ( options ) ; expect ( man . getNonce ( 1 ) ) . toBeUndefined ( ) ; const nonce = randomBytes ( 8 ) ; nonce [ 0 ] = 1 ; man . setNonce ( 1 , { nonce , receiver : 2 } ) ; expect ( man . getNonce ( 1 ) ) . toEqual ( nonce ) ; } ) ; it ( "the nonces should timeout after the given timeout" , ( ) => { const man = new SecurityManager ( options ) ; const nonce = randomBytes ( 8 ) ; const nonceId = nonce [ 0 ] ; man . setNonce ( nonceId , { nonce , receiver : 2 } ) ; expect ( man . getNonce ( nonceId ) ) . toEqual ( nonce ) ; jest . advanceTimersByTime ( options . nonceTimeout + 50 ) ; expect ( man . getNonce ( nonceId ) ) . toBeUndefined ( ) ; } ) ; it ( "should mark the nonce as free" , ( ) => { const man = new SecurityManager ( options ) ; const nonce = randomBytes ( 8 ) ; nonce [ 0 ] = 1 ; man . setNonce ( { issuer : 2 , nonceId : 1 } , { nonce , receiver : options . ownNodeId } ) ; expect ( man . getFreeNonce ( 1 ) ) . toBeUndefined ( ) ; expect ( man . getFreeNonce ( 2 ) ) . toEqual ( nonce ) ; } ) ; it ( "when a free nonce expires, it should no longer be free" , ( ) => { const man = new SecurityManager ( options ) ; const nonce = randomBytes ( 8 ) ; man . setNonce ( { issuer : 2 , nonceId : 1 } , { nonce , receiver : options . ownNodeId } ) ; jest . advanceTimersByTime ( options . nonceTimeout + 50 ) ; expect ( man . getFreeNonce ( 2 ) ) . toBeUndefined ( ) ; } ) ; } ) ; describe ( "hasNonce" , ( ) => { it ( "should return whether a nonce id is in the database" , ( ) => { const man = new SecurityManager ( options ) ; expect ( man . hasNonce ( 1 ) ) . toBeFalse ( ) ; const nonce1 = randomBytes ( 8 ) ; nonce1 [ 0 ] = 1 ; man . setNonce ( 1 , { nonce : nonce1 , receiver : 2 } ) ; expect ( man . hasNonce ( 1 ) ) . toBeTrue ( ) ; const nonce2 = man . generateNonce ( 2 , 8 ) ; const nonceId2 = man . getNonceId ( nonce2 ) ; expect ( man . hasNonce ( nonceId2 ) ) . toBeTrue ( ) ; } ) ; } ) ; describe ( "deleteNonce" , ( ) => { it ( "should remove a nonce from the database" , ( ) => { const man = new SecurityManager ( options ) ; const nonce = man . generateNonce ( 2 , 8 ) ; const nonceId = man . getNonceId ( nonce ) ; man . deleteNonce ( nonceId ) ; expect ( man . getNonce ( nonceId ) ) . toBeUndefined ( ) ; expect ( man . hasNonce ( nonceId ) ) . toBeFalse ( ) ; } ) ; it ( "and all other nonces that were created for the same receiver" , ( ) => { const man = new SecurityManager ( options ) ; const nonce1 = man . generateNonce ( 2 , 8 ) ; const nonceId1 = man . getNonceId ( nonce1 ) ; const nonce2 = man . generateNonce ( 2 , 8 ) ; const nonceId2 = man . getNonceId ( nonce2 ) ; man . deleteNonce ( nonceId1 ) ; expect ( man . getNonce ( nonceId1 ) ) . toBeUndefined ( ) ; expect ( man . hasNonce ( nonceId1 ) ) . toBeFalse ( ) ; expect ( man . getNonce ( nonceId2 ) ) . toBeUndefined ( ) ; expect ( man . hasNonce ( nonceId2 ) ) . toBeFalse ( ) ; } ) ; } ) ; describe ( "deleteAllNoncesForReceiver" , ( ) => { it ( "should only delete the nonces for the given receiver" , ( ) => { const man = new SecurityManager ( options ) ; const nonce1 = man . generateNonce ( 2 , 8 ) ; const nonceId1 = man . getNonceId ( nonce1 ) ; const nonce2 = man . generateNonce ( 2 , 8 ) ; const nonceId2 = man . getNonceId ( nonce2 ) ; const nonce3 = man . generateNonce ( 3 , 8 ) ; const nonceId3 = man . getNonceId ( nonce3 ) ; man . deleteAllNoncesForReceiver ( 2 ) ; expect ( man . getNonce ( nonceId1 ) ) . toBeUndefined ( ) ; expect ( man . hasNonce ( nonceId1 ) ) . toBeFalse ( ) ; expect ( man . getNonce ( nonceId2 ) ) . toBeUndefined ( ) ; expect ( man . hasNonce ( nonceId2 ) ) . toBeFalse ( ) ; expect ( man . getNonce ( nonceId3 ) ) . not . toBeUndefined ( ) ; expect ( man . hasNonce ( nonceId3 ) ) . not . toBeFalse ( ) ; } ) ; } ) ; describe ( "getFreeNonce" , ( ) => { it ( "should reserve the nonce" , ( ) => { const man = new SecurityManager ( options ) ; const nonce = randomBytes ( 8 ) ; nonce [ 0 ] = 1 ; man . setNonce ( { issuer : 2 , nonceId : 1 } , { nonce , receiver : options . ownNodeId } ) ; expect ( man . getFreeNonce ( 2 ) ) . toEqual ( nonce ) ; expect ( man . getFreeNonce ( 2 ) ) . toBeUndefined ( ) ; } ) ; } ) ; it ( "nonces should be stored separately for each node" , ( ) => { const man = new SecurityManager ( options ) ; const nonce1 = man . generateNonce ( 3 , 8 ) ; const nonceId1 = man . getNonceId ( nonce1 ) ; const nonce2 = randomBytes ( 8 ) ; nonce2 [ 0 ] = nonceId1 ; const id2 = { issuer : 4 , nonceId : nonceId1 } ; expect ( man . hasNonce ( id2 ) ) . toBeFalse ( ) ; expect ( man . getNonce ( id2 ) ) . toBeUndefined ( ) ; man . setNonce ( id2 , { nonce : nonce2 , receiver : 1 } ) ; expect ( man . hasNonce ( id2 ) ) . toBeTrue ( ) ; expect ( man . getNonce ( id2 ) ) . toEqual ( nonce2 ) ; } ) ; } ) ;