/* * Copyright 2018 Scytl Secure Electronic Voting SA * * All rights reserved * * See our extended copyright notice in *file 'Copyright.txt' which is part of this source code package */ /* jshint node:true, mocha:true */ 'use strict'; var cryptoPolicy = require('scytl-cryptopolicy'); var asymmetric = require('../lib/index'); var constants = require('../lib/constants'); var bitwise = require('scytl-bitwise'); var codec = require('scytl-codec'); var forge = require('node-forge'); describe('The asymmetric cryptography module should be able to ...', function() { var DATA = 'anystring'; var OTHER_DATA_1 = DATA + 'a'; var OTHER_DATA_2 = DATA + 'b'; var PUBLIC_KEY_PEM = '-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAglypZU45bnf2opnRI+Y51VqouBpvDT33xIB/OtbwKzwVpi+JrjBFtfk33tE9t/dSRs79CK94HRhCWcOiLa2qWPrjeZ9SBiEyScrhIvRZVBF41zBgwQNuRvJCsKmAqlZaFNJDZxEP4repmlBn1CfVFmfrXmOKqwP5F7l9ZtucveRzsfmF1yVPFkW8TMuB3YqMiyymyqHlS8ujCsu5I8tpgPbwuxdMOY94fNhSXrYkY8IuX1g1zdq/Z1jluOaR/UqK4UpnbuJaH/F0VgDNiWh6cTD0DFGEk0b70i5wU4Q3L/S6XZQRvSuADoCbhwBKuFL5pW5n865oLVb5S3wuVdWaGwIDAQAB-----END PUBLIC KEY-----'; var PRIVATE_KEY_PEM = '-----BEGIN RSA PRIVATE KEY-----MIIEowIBAAKCAQEAglypZU45bnf2opnRI+Y51VqouBpvDT33xIB/OtbwKzwVpi+JrjBFtfk33tE9t/dSRs79CK94HRhCWcOiLa2qWPrjeZ9SBiEyScrhIvRZVBF41zBgwQNuRvJCsKmAqlZaFNJDZxEP4repmlBn1CfVFmfrXmOKqwP5F7l9ZtucveRzsfmF1yVPFkW8TMuB3YqMiyymyqHlS8ujCsu5I8tpgPbwuxdMOY94fNhSXrYkY8IuX1g1zdq/Z1jluOaR/UqK4UpnbuJaH/F0VgDNiWh6cTD0DFGEk0b70i5wU4Q3L/S6XZQRvSuADoCbhwBKuFL5pW5n865oLVb5S3wuVdWaGwIDAQABAoIBAC/tn34Wf3kE9BGeGc1oFLVDaqqdVVz5/oEpeR2J7q0GnzMFYUpAhzC7WvY52cYsUPyll1Q9Jx0TUTmteo/uvKWQQFfz4nVMeS+2PoXabolBDzuWlsv/1eiRo0FOYHa/3siu8YcQN9X0DpAkpbfTmT1uoZOHZ3EuucMmOFu7vGn38Grw8bSxpR0uvTtnb8ygC+aB51y38RMyhzQQanrM8FMeAfDAy6IB0Yo7b0c50Cxa6Ax4nqn9LXyGakr5WeAMkgTIOA/GId9SZD4e5eRpq+628pOeR4O9datFltgl6r1+A4ii2VrJsDqeatGtODlX6KRKqwFHoGIa2TjgSZLuorECgYEAxeSZDOOgFsI5mB7RkRzZaQ9znJ15sgdyZiAFZAOUah4hSGdAXNAnZTlrdacduXEu3EfkpuPToX7xZSv5FRYwfBwMwCLeytlGLPjQzWejZGbo4+KqgzWb9fECDYVtDPlJ/+yLih9nt67BHweJKxYydl18rVigdVyy22X86NijSykCgYEAqKPUrXZAo+TJvmTw4tgsibJgvXBYBhmsej8mGNQw+Nyp2gV28sgm61ifIeXKS8teq+MFwGA6cHQedbsCqhMHokdhESZmlbWxhSFLihQcewBxwvrBwbaxI23yXRzwMewznZFL032PpcbqrmwFmcSSEZ3nmbvTH6ShqLW+pzDNp6MCgYBQLzdgxJ7qedqSa/JohTMG4e7rh9d2rpPJE7J7ewPZF8pOpx+qO+Gqn2COdJ+Ts2vUcAETKn9nEaPIZc/wnmQY9dioxbhWo0FPGaaphBPtq9Ez/XUv4zoFppk5V1X/isdUPsmvttf00oeIBiqrXbwmv+yz5JRn2Z7TTXjz9Ev+OQKBgQCUuoCMRzl1EgcXIqEL/0kwW6BUEqufHa9u1Ri9Vw6lvL8T6DPipMEmWK9nzuid9gtVns/ovTVtDgv7GuabplLaPQePf4WDzY11c0rSyS/hDyBFrK+LL5uEOqhAlJAGB2HyOj1clWVF+GvrTpuV5LZKUS/79pmZU7G7QCaX/0Ow7wKBgC/kDH7cmWQnWvvJ5izrx/7PogQVPOLELeUIGLu/hjsSdDKiFCxCUZ948+9NuG+DnpXDWzw//r8mPBRRGGsqFws5Aipp7yjQ3kRDCCzGelPCVhHyfmKqA+8ewXPulKS3/wIyHIvaXmsuAtTfurHtpRyzjKmCBK1Y6WQ3trIXvo7s-----END RSA PRIVATE KEY-----'; var data_; var otherData1_; var otherData2_; var dataParts_; var service_; var signer_; var sigVerifier_; var encrypter_; var decrypter_; var signature_; var encryptedData_; beforeAll(function() { data_ = codec.utf8Encode(DATA); otherData1_ = codec.utf8Encode(OTHER_DATA_1); otherData2_ = codec.utf8Encode(OTHER_DATA_2); dataParts_ = [data_, otherData1_, otherData2_]; service_ = asymmetric.newService(); signer_ = service_.newSigner().init(PRIVATE_KEY_PEM); sigVerifier_ = service_.newSignatureVerifier().init(PUBLIC_KEY_PEM); encrypter_ = service_.newEncrypter().init(PUBLIC_KEY_PEM); decrypter_ = service_.newDecrypter().init(PRIVATE_KEY_PEM); signature_ = signer_.sign(data_); encryptedData_ = encrypter_.encrypt(data_); }); describe('create an asymmetric cryptography service that should be able to ..', function() { it('create a new RsaPublicKey object', function() { var rsaPublicKey1 = service_.newRsaPublicKey({pem: PUBLIC_KEY_PEM}); var rsaPublicKey2 = service_.newRsaPublicKey({n: rsaPublicKey1.n, e: rsaPublicKey1.e}); expect(rsaPublicKey2.n).toEqual(rsaPublicKey1.n); expect(rsaPublicKey2.e).toEqual(rsaPublicKey1.e); var rsaPublicKey1Pem = rsaPublicKey1.toPem(); var offset = rsaPublicKey1Pem.indexOf('\n') + 1; expect(rsaPublicKey1Pem.indexOf('\n', offset)) .toBe(offset + constants.PEM_LINE_LENGTH + 1); var rsaPublicKey2Pem = rsaPublicKey1.toPem(); offset = rsaPublicKey2Pem.indexOf('\n') + 1; expect(rsaPublicKey2Pem.indexOf('\n', offset)) .toBe(offset + constants.PEM_LINE_LENGTH + 1); expect(removeNewLineChars(rsaPublicKey1Pem)) .toBe(removeNewLineChars(PUBLIC_KEY_PEM)); expect(removeNewLineChars(rsaPublicKey2Pem)) .toBe(removeNewLineChars(PUBLIC_KEY_PEM)); }); it('create a new RsaPrivateKey object', function() { var rsaPrivateKey1 = service_.newRsaPrivateKey({pem: PRIVATE_KEY_PEM}); var params = { n: rsaPrivateKey1.n, e: rsaPrivateKey1.e, d: rsaPrivateKey1.d, p: rsaPrivateKey1.p, q: rsaPrivateKey1.q, dP: rsaPrivateKey1.dP, dQ: rsaPrivateKey1.dQ, qInv: rsaPrivateKey1.qInv }; var rsaPrivateKey2 = service_.newRsaPrivateKey(params); expect(rsaPrivateKey2.n).toEqual(rsaPrivateKey1.n); expect(rsaPrivateKey2.e).toEqual(rsaPrivateKey1.e); expect(rsaPrivateKey2.d).toEqual(rsaPrivateKey1.d); expect(rsaPrivateKey2.p).toEqual(rsaPrivateKey1.p); expect(rsaPrivateKey2.q).toEqual(rsaPrivateKey1.q); expect(rsaPrivateKey2.dP).toEqual(rsaPrivateKey1.dP); expect(rsaPrivateKey2.dQ).toEqual(rsaPrivateKey1.dQ); expect(rsaPrivateKey2.qInv).toEqual(rsaPrivateKey1.qInv); var rsaPrivateKey1Pem = rsaPrivateKey1.toPem(); var offset = rsaPrivateKey1Pem.indexOf('\n') + 1; expect(rsaPrivateKey1Pem.indexOf('\n', offset)) .toBe(offset + constants.PEM_LINE_LENGTH + 1); var rsaPrivateKey2Pem = rsaPrivateKey2.toPem(); offset = rsaPrivateKey2Pem.indexOf('\n') + 1; expect(rsaPrivateKey2Pem.indexOf('\n', offset)) .toBe(offset + constants.PEM_LINE_LENGTH + 1); expect(removeNewLineChars(rsaPrivateKey1Pem)) .toBe(removeNewLineChars(PRIVATE_KEY_PEM)); expect(removeNewLineChars(rsaPrivateKey2Pem)) .toBe(removeNewLineChars(PRIVATE_KEY_PEM)); }); it('create a new KeyPair object', function() { var keyPair = service_.newKeyPair(PUBLIC_KEY_PEM, PRIVATE_KEY_PEM); expect(removeNewLineChars(keyPair.publicKey)) .toBe(removeNewLineChars(PUBLIC_KEY_PEM)); expect(removeNewLineChars(keyPair.privateKey)) .toBe(removeNewLineChars(PRIVATE_KEY_PEM)); }); describe( 'create a key pair generator that should be able to ..', function() { it('generate a new key pair', function() { // Note: Key size here is set to value lower than that allowed by // policy to keep test time reasonable. var policy = cryptoPolicy.newInstance(); policy.asymmetric.keyPair.encryption.keyLengthBits = 128; var service = asymmetric.newService({policy: policy}); var keyPairGenerator = service.newKeyPairGenerator(); var keyPair = keyPairGenerator.next(); var publicKey = keyPair.publicKey; var privateKey = keyPair.privateKey; expect(publicKey).toBeDefined(); expect(publicKey).toMatch(/^-----BEGIN PUBLIC/); expect(privateKey).toBeDefined(); expect(privateKey).toMatch(/^-----BEGIN RSA PRIVATE/); var modulus = forge.pki.publicKeyFromPem(publicKey).n; var keyLengthBits = modulus.toString(2).length; expect(keyLengthBits) .toBe(policy.asymmetric.keyPair.encryption.keyLengthBits); }); }); describe('create a signer/signature verifier pair that should be able to ..', function() { it('sign some data and verify the signature', function() { var signature = signer_.sign(data_); expect(sigVerifier_.verify(signature, data_)).toBeTruthy(); }); it('sign some data consisting of multiple parts', function() { // Call sign with no arguments. for (var i = 0; i < dataParts_.length; i++) { signer_.update(dataParts_[i]); } var signature = signer_.sign(); var verified = sigVerifier_.verify( signature, bitwise.concatenate(data_, otherData1_, otherData2_)); expect(verified).toBeTruthy(); // Call sign with argument consisting of last data part. for (var j = 0; j < (dataParts_.length - 1); j++) { signer_.update(dataParts_[j]); } signature = signer_.sign(otherData2_); verified = sigVerifier_.verify( signature, bitwise.concatenate(data_, otherData1_, otherData2_)); expect(verified).toBeTruthy(); }); it('sign some data consisting of multiple parts, using method chaining', function() { // Call sign with no arguments. var signature = signer_.update(data_) .update(otherData1_) .update(otherData2_) .sign(); var verified = sigVerifier_.verify( signature, bitwise.concatenate(data_, otherData1_, otherData2_)); expect(verified).toBeTruthy(); // Call sign with argument consisting of last data part. signature = signer_.update(data_).update(otherData1_).sign(otherData2_); verified = sigVerifier_.verify( signature, bitwise.concatenate(data_, otherData1_, otherData2_)); expect(verified).toBeTruthy(); }); it('verify a signature of data consisting of multiple parts', function() { var signature = signer_.sign(bitwise.concatenate(data_, otherData1_, otherData2_)); // Call verify with no arguments. for (var i = 0; i < dataParts_.length; i++) { sigVerifier_.update(dataParts_[i]); } var verified = sigVerifier_.verify(signature); expect(verified).toBeTruthy(); // Call verify with argument consisting of last data part. for (var j = 0; j < (dataParts_.length - 1); j++) { sigVerifier_.update(dataParts_[j]); } verified = sigVerifier_.verify(signature, otherData2_); expect(verified).toBeTruthy(); }); it('verify a signature of data consisting of multiple parts, using method chaining', function() { var signature = signer_.sign( bitwise.concatenate(data_, otherData1_, otherData2_)); // Calli verify with no arguments. var verified = sigVerifier_.update(data_) .update(otherData1_) .update(otherData2_) .verify(signature); expect(verified).toBeTruthy(); // Call verify with argument consisting of last data part. verified = sigVerifier_.update(data_) .update(otherData1_) .verify(signature, otherData2_); expect(verified).toBeTruthy(); }); it('sign some data of type string and verify the signature', function() { var signature = signer_.update(DATA).update(OTHER_DATA_1).sign(OTHER_DATA_2); var verified = sigVerifier_.update(DATA) .update(OTHER_DATA_1) .verify(signature, OTHER_DATA_2); expect(verified).toBeTruthy(); }); it('sign some data and verify the signature, using RsaPrivateKey and RsaPublicKey objects', function() { var rsaPrivateKey = new service_.newRsaPrivateKey({pem: PRIVATE_KEY_PEM}); var signer = service_.newSigner().init(rsaPrivateKey.toPem()); var rsaPublicKey = new service_.newRsaPublicKey({pem: PUBLIC_KEY_PEM}); var sigVerifier = service_.newSignatureVerifier().init(rsaPublicKey.toPem()); var signature = signer.sign(data_); expect(sigVerifier.verify(signature, data_)).toBeTruthy(); }); it('throw an error when updating the signer with some data before the signer has been initialized with a private key', function() { var signer = service_.newSigner(); expect(function() { signer.update(data_); }).toThrow(); }); it('throw an error when signing some data before the signer has been initialized with a private key', function() { var signer = service_.newSigner(); expect(function() { signer.sign(data_); }).toThrow(); }); it('throw an error when updating the signature verifier with some data before the verifier has been initialized with a public key', function() { var sigVerifier = service_.newSignatureVerifier(); expect(function() { sigVerifier.update(data_); }).toThrow(); }); it('throw an error when verifying a signature before the signature verifier has been initialized with a public key', function() { var sigVerifier = service_.newSignatureVerifier(); expect(function() { sigVerifier.verify(signature_, data_); }).toThrow(); }); it('throw an error when signing without either providing data or having previously updated the signer with some data', function() { var signer = service_.newSigner().init(PRIVATE_KEY_PEM); expect(function() { signer.sign(); }).toThrow(); }); it('throw an error when verifying a signature without either providing data or having previously updated the verifier with some data', function() { var sigVerifier = service_.newSignatureVerifier().init(PUBLIC_KEY_PEM); expect(function() { sigVerifier.verify(signature_); }).toThrow(); }); }); describe( 'create an encrypter/decrypter pair that should be able to ..', function() { it('encrypt and decrypt some data', function() { var encryptedData = encrypter_.encrypt(data_); var decryptedData = decrypter_.decrypt(encryptedData); expect(decryptedData).toEqual(data_); }); it('encrypt and decrypt some data of type string', function() { var encryptedData = encrypter_.encrypt(DATA); var decryptedData = codec.utf8Decode(decrypter_.decrypt(encryptedData)); expect(decryptedData).toBe(DATA); }); it('encrypt and decrypt some data, using RsaPublicKey and RsaPrivateKey objects', function() { var rsaPublicKey = new service_.newRsaPublicKey({pem: PUBLIC_KEY_PEM}); var encrypter = service_.newEncrypter().init(rsaPublicKey.toPem()); var rsaPrivateKey = new service_.newRsaPrivateKey({pem: PRIVATE_KEY_PEM}); var decrypter = service_.newDecrypter().init(rsaPrivateKey.toPem()); var encryptedData = encrypter.encrypt(DATA); var decryptedData = codec.utf8Decode(decrypter.decrypt(encryptedData)); expect(decryptedData).toBe(DATA); }); it('throw an error when encrypting some data before the encrypter has been initialized with a public key', function() { var encrypter = service_.newEncrypter(); expect(function() { encrypter.encrypt(data_); }).toThrow(); }); it('throw an error when decrypting before the decrypter has been initialized with a private key', function() { var decrypter = service_.newDecrypter(); expect(function() { decrypter.decrypt(encryptedData_); }).toThrow(); }); }); }); }); function removeNewLineChars(str) { return str.replace(/(\r\n|\n|\r)/gm, ''); }