From 6bbcd8cab7f6651f7312cd5bd670df4b86dc0808 Mon Sep 17 00:00:00 2001 From: Tankred Hase Date: Tue, 14 May 2013 16:05:31 +0200 Subject: [PATCH] added rsa module and tests --- src/js/crypto/aes-cbc.js | 1 - src/js/crypto/rsa.js | 94 +++++++++++++++++++++++++++++++++++++++ test/unit/aes-test.js | 12 ++--- test/unit/forge-test.js | 96 ++++------------------------------------ test/unit/index.html | 2 + test/unit/rsa-test.js | 50 +++++++++++++++++++++ 6 files changed, 158 insertions(+), 97 deletions(-) create mode 100644 src/js/crypto/rsa.js create mode 100644 test/unit/rsa-test.js diff --git a/src/js/crypto/aes-cbc.js b/src/js/crypto/aes-cbc.js index 7d109e8..41ceb38 100644 --- a/src/js/crypto/aes-cbc.js +++ b/src/js/crypto/aes-cbc.js @@ -30,7 +30,6 @@ app.crypto.AesCBC = function(forge) { * @param ciphertext [String] The base64 encoded ciphertext * @param key [String] The base64 encoded key * @param iv [String] The base64 encoded IV - * @param iv [String] The base64 encoded HMAC * @return [String] The decrypted plaintext in UTF8 */ this.decrypt = function(ciphertext, key, iv) { diff --git a/src/js/crypto/rsa.js b/src/js/crypto/rsa.js new file mode 100644 index 0000000..2f73fdd --- /dev/null +++ b/src/js/crypto/rsa.js @@ -0,0 +1,94 @@ +/** + * A Wrapper for Forge's RSA encryption + */ +app.crypto.RSA = function(forge) { + 'use strict'; + + var publicKey = null, + privateKey = null; + + /** + * Initializes the RSA module by passing the user's keypair + * The private key is option and required only for decryption + * and signing + */ + this.init = function(pubkeyPem, privkeyPem) { + publicKey = forge.pki.publicKeyFromPem(pubkeyPem); + if (privkeyPem) { + privateKey = forge.pki.privateKeyFromPem(privkeyPem); + } + }; + + /** + * Generate RSA keypair with the corresponding keysize + */ + this.generateKeypair = function(keySize, callback) { + forge.rsa.generateKeyPair({ + bits: keySize, + workerScript: app.config.workerPath + '/../../lib/forge/prime.worker.js' + }, function(err, keypair) { + if (err || !keypair.publicKey || !keypair.privateKey) { + callback({ + errMsg: 'RSA keygeneration failed!', + err: err + }); + return; + } + + publicKey = keypair.publicKey; + privateKey = keypair.privateKey; + + callback(); + }); + }; + + /** + * Exports user's keypair as PEMs + */ + this.exportKeys = function() { + return { + pubkeyPem: forge.pki.publicKeyToPem(publicKey), + privkeyPem: forge.pki.privateKeyToPem(privateKey) + }; + }; + + /** + * Encrypt a String using RSA with PKCS#1 v1.5 padding + * @param plaintext [String] The input string in UTF8 + * @return [String] The base64 encoded ciphertext + */ + this.encrypt = function(plaintext) { + var ct = publicKey.encrypt(plaintext); + return forge.util.encode64(ct); + }; + + /** + * Decrypt a String using RSA with PKCS#1 v1.5 padding + * @param ciphertext [String] The base64 encoded ciphertext + * @return [String] The decrypted plaintext in UTF8 + */ + this.decrypt = function(ciphertext) { + // parse base64 input to utf8 + var ctUtf8 = forge.util.decode64(ciphertext); + return privateKey.decrypt(ctUtf8); + }; + + this.sign = function(input) { + var sha = forge.md.sha256.create(); + sha.update(input); + + var sig = privateKey.sign(sha); + return forge.util.encode64(sig); + }; + + this.verify = function(input, sig) { + // parse base64 signature to utf8 + var sigUtf8 = forge.util.decode64(sig); + + var sha = forge.md.sha256.create(); + sha.update(input); + + return publicKey.verify(sha.digest().getBytes(), sigUtf8); + }; + +}; \ No newline at end of file diff --git a/test/unit/aes-test.js b/test/unit/aes-test.js index 57c3706..bf4609d 100644 --- a/test/unit/aes-test.js +++ b/test/unit/aes-test.js @@ -1,17 +1,11 @@ module("AES Crypto"); var aes_test = { - keySize: 128 + keySize: 128, + util: new app.crypto.Util(window, uuid), + test_message: new TestData().generateBigString(1000) }; -test("Init", 1, function() { - // init dependencies - aes_test.util = new app.crypto.Util(window, uuid); - ok(aes_test.util, 'Util'); - // generate test data - aes_test.test_message = new TestData().generateBigString(1000); -}); - test("CBC mode", 4, function() { var aes = new app.crypto.AesCBC(forge); diff --git a/test/unit/forge-test.js b/test/unit/forge-test.js index 60c3e34..65306ec 100644 --- a/test/unit/forge-test.js +++ b/test/unit/forge-test.js @@ -1,6 +1,6 @@ module("Forge Crypto"); -var rsa_test = { +var forge_rsa_test = { keySize: 1024, test_message: '06a9214036b8a15b512e03d534120006' }; @@ -13,15 +13,15 @@ var forge_aes_test = { test("SHA-1 Hash", 1, function() { var sha1 = forge.md.sha1.create(); sha1.update(forge_aes_test.test_message); - var digest = sha1.digest().getBytes(); - ok(digest); + var digest = sha1.digest().toHex(); + ok(digest, digest); }); test("SHA-256 Hash", 1, function() { - rsa_test.md = forge.md.sha256.create(); - rsa_test.md.update(forge_aes_test.test_message); - var digest = rsa_test.md.digest().getBytes(); - ok(digest); + forge_rsa_test.md = forge.md.sha256.create(); + forge_rsa_test.md.update(forge_aes_test.test_message); + var digest = forge_rsa_test.md.digest().toHex(); + ok(digest, digest); }); test("HMAC SHA-256", 1, function() { @@ -34,85 +34,7 @@ test("HMAC SHA-256", 1, function() { hmac.start('sha256', key); hmac.update(iv); hmac.update(forge_aes_test.test_message); - var result = hmac.digest().toHex(); + var digest = hmac.digest().toHex(); - ok(result); -}); - -test("PBKDF2", 1, function() { - var util = new app.crypto.Util(window, uuid); - - var salt = util.base642Str("vbhmLjC+Ub6MSbhS6/CkOwxB25wvwRkSLP2DzDtYb+4="); - var expect = '5223bd44b0523090b21e9d38a749b090'; - - var dk = forge.pkcs5.pbkdf2('password', salt, 1000, 16); - - equal(expect, forge.util.bytesToHex(dk)); -}); - -asyncTest("RSA Generate Keypair", 1, function() { - - forge.rsa.generateKeyPair({ - bits: rsa_test.keySize, - workerScript: app.config.workerPath + '/../lib/forge/prime.worker.js' - }, function(err, keypair) { - ok(!err && keypair); - - rsa_test.keypair = keypair; - - start(); - }); - -}); - -test("RSA Encrypt", 1, function() { - rsa_test.ct = rsa_test.keypair.publicKey.encrypt(rsa_test.test_message); - ok(rsa_test.ct); -}); - -test("RSA Decrypt", 1, function() { - var pt = rsa_test.keypair.privateKey.decrypt(rsa_test.ct); - equal(rsa_test.test_message, pt); -}); - -test("RSA Sign", 1, function() { - var sha = forge.md.sha256.create(); - sha.update(forge_aes_test.test_message); - - rsa_test.sig = rsa_test.keypair.privateKey.sign(sha); - ok(rsa_test.sig); -}); - -test("RSA Verify", 1, function() { - var res = rsa_test.keypair.publicKey.verify(rsa_test.md.digest().getBytes(), rsa_test.sig); - ok(res); -}); - -test("AES-128-CBC Encrypt", 1, function() { - var util = new app.crypto.Util(window, uuid); - - forge_aes_test.key = util.base642Str(util.random(forge_aes_test.keySize)); - forge_aes_test.iv = util.base642Str(util.random(forge_aes_test.keySize)); - var input = forge_aes_test.test_message; - - // encrypt - var enCipher = forge.aes.createEncryptionCipher(forge_aes_test.key); - enCipher.start(forge_aes_test.iv); - enCipher.update(forge.util.createBuffer(input)); - enCipher.finish(); - - forge_aes_test.ct = enCipher.output.getBytes(); - ok(forge_aes_test.ct); -}); - -test("AES-128-CBC Decrypt", 1, function() { - var input = forge_aes_test.test_message; - - // decrypt - var deCipher = forge.aes.createDecryptionCipher(forge_aes_test.key); - deCipher.start(forge_aes_test.iv); - deCipher.update(forge.util.createBuffer(forge_aes_test.ct)); - deCipher.finish(); - - equal(input, deCipher.output, 'En/Decrypt length: ' + input.length); + ok(digest, digest); }); \ No newline at end of file diff --git a/test/unit/index.html b/test/unit/index.html index cd72b44..01f0e6b 100644 --- a/test/unit/index.html +++ b/test/unit/index.html @@ -42,6 +42,7 @@ + @@ -56,6 +57,7 @@ + diff --git a/test/unit/rsa-test.js b/test/unit/rsa-test.js new file mode 100644 index 0000000..30620c2 --- /dev/null +++ b/test/unit/rsa-test.js @@ -0,0 +1,50 @@ +module("RSA Crypto"); + +var rsa_test = { + keySize: 1024, + rsa: new app.crypto.RSA(forge), + test_message: '06a9214036b8a15b512e03d534120006' +}; + +asyncTest("Generate keypair", 1, function() { + rsa_test.rsa.generateKeypair(rsa_test.keySize, function(err) { + ok(!err); + + start(); + }); +}); + +test("Export keys", 2, function() { + rsa_test.keypair = rsa_test.rsa.exportKeys(); + + ok(rsa_test.keypair.pubkeyPem.indexOf('-----BEGIN PUBLIC KEY-----') === 0, rsa_test.keypair.pubkeyPem); + ok(rsa_test.keypair.privkeyPem.indexOf('-----BEGIN RSA PRIVATE KEY-----') === 0, rsa_test.keypair.privkeyPem); +}); + +test("Init", 2, function() { + rsa_test.rsa.init(rsa_test.keypair.pubkeyPem, rsa_test.keypair.privkeyPem); + var exported = rsa_test.rsa.exportKeys(); + + ok(exported.pubkeyPem.indexOf('-----BEGIN PUBLIC KEY-----') === 0); + ok(exported.privkeyPem.indexOf('-----BEGIN RSA PRIVATE KEY-----') === 0); +}); + +test("Encrypt", 1, function() { + rsa_test.ct = rsa_test.rsa.encrypt(rsa_test.test_message); + ok(rsa_test.ct); +}); + +test("Decrypt", 1, function() { + var pt = rsa_test.rsa.decrypt(rsa_test.ct); + equal(pt, rsa_test.test_message); +}); + +test("Sign", 1, function() { + rsa_test.sig = rsa_test.rsa.sign(rsa_test.test_message); + ok(rsa_test.sig); +}); + +test("Verify", 1, function() { + var res = rsa_test.rsa.verify(rsa_test.test_message, rsa_test.sig); + ok(res); +}); \ No newline at end of file