Skip to content

Instantly share code, notes, and snippets.

@alokmenghrajani
Created September 9, 2015 09:07
Show Gist options
  • Save alokmenghrajani/eb7fe614402d3723a451 to your computer and use it in GitHub Desktop.
Save alokmenghrajani/eb7fe614402d3723a451 to your computer and use it in GitHub Desktop.
diff --git a/Gruntfile.js b/Gruntfile.js
index eb0c686..ab823da 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -15,8 +15,8 @@ module.exports = function(grunt) {
},
src: [
'lib/jose-core.js',
+ 'lib/jose-utils.js',
'lib/jose-jwe-webcryptographer.js',
- 'lib/jose-jwe-utils.js',
'lib/jose-jwe-encrypt.js',
'lib/jose-jwe-decrypt.js',
'lib/jose-jws-sign.js',
@@ -31,8 +31,8 @@ module.exports = function(grunt) {
},
src: [
'lib/jose-core.js',
+ 'lib/jose-utils.js',
'lib/jose-jwe-webcryptographer.js',
- 'lib/jose-jwe-utils.js',
'lib/jose-jwe-encrypt.js',
'lib/jose-jwe-decrypt.js',
'lib/jose-jws-sign.js',
diff --git a/README.md b/README.md
index 28cb174..c0a3389 100644
--- a/README.md
+++ b/README.md
@@ -5,14 +5,14 @@ Javascript library for Jose JWE and JWS
Overview
--------
-JavaScript library to sign/verify and encrypt/decrypt data in JSON Web Signatures
-and Web Encryption (JWE) formats. This library is designed to work in the browser
-(tested in Chrome 38). It can do RSA-based public/private crypto as well
-as shared key encryption.
+JavaScript library to sign/verify and encrypt/decrypt data in JSON Web
+Signatures and Web Encryption (JWE) formats. This library is designed to work in
+the browser (tested in Chrome 38). It can do RSA-based public/private crypto as
+well as shared key encryption.
-Both JWE and JWS are encapsulation formats which makes it easy to share signatures
-and ciphertext between different platforms: data signed or encrypted in a browser
-can be verified or decrypted in Go, Java, etc.
+Both JWE and JWS are encapsulation formats which makes it easy to share
+signatures and ciphertext between different platforms: data signed or encrypted
+in a browser can be verified or decrypted in Go, Java, etc.
The library uses compact representation. There is therefore no support for
multiple recipients in JWE messages. It should be easy to add that if needed.
@@ -93,7 +93,7 @@ Example signature
var signer = new JoseJWS.Signer(cryptographer);
signer.addSigner(rsa_key).then(function() {
signer.sign(plaintext.textContent, null, {}).then(function(message) {
-
+
console.log(message);
var verifier = new JoseJWS.Verifier(cryptographer, message);
verifier.addRecipient(rsa_key).then(function() {
diff --git a/lib/jose-jwe-decrypt.js b/lib/jose-jwe-decrypt.js
index 8c2dfac..7e53c50 100644
--- a/lib/jose-jwe-decrypt.js
+++ b/lib/jose-jwe-decrypt.js
@@ -21,13 +21,13 @@
* in mind that decryption mutates the cryptographer.
* @param key_promise Promise<CryptoKey>, either RSA or shared key
*/
-JoseJWE.Decrypter = function (cryptographer, key_promise) {
+JoseJWE.Decrypter = function(cryptographer, key_promise) {
this.cryptographer = cryptographer;
this.key_promise = key_promise;
this.headers = {};
};
-JoseJWE.Decrypter.prototype.getHeaders = function () {
+JoseJWE.Decrypter.prototype.getHeaders = function() {
return this.headers;
};
@@ -37,8 +37,7 @@ JoseJWE.Decrypter.prototype.getHeaders = function () {
* @param cipher_text String
* @return Promise<String>
*/
-JoseJWE.Decrypter.prototype.decrypt = function (cipher_text) {
- var that = this;
+JoseJWE.Decrypter.prototype.decrypt = function(cipher_text) {
// Split cipher_text in 5 parts
var parts = cipher_text.split(".");
if (parts.length != 5) {
@@ -46,17 +45,17 @@ JoseJWE.Decrypter.prototype.decrypt = function (cipher_text) {
}
// part 1: header
- that.headers = JSON.parse(Utils.Base64Url.decode(parts[0]));
- if (!that.headers.alg) {
+ this.headers = JSON.parse(Utils.Base64Url.decode(parts[0]));
+ if (!this.headers.alg) {
return Promise.reject(Error("decrypt: missing alg"));
}
- if (!that.headers.enc) {
+ if (!this.headers.enc) {
return Promise.reject(Error("decrypt: missing enc"));
}
- that.cryptographer.setKeyEncryptionAlgorithm(this.headers.alg);
- that.cryptographer.setContentEncryptionAlgorithm(this.headers.enc);
+ this.cryptographer.setKeyEncryptionAlgorithm(this.headers.alg);
+ this.cryptographer.setContentEncryptionAlgorithm(this.headers.enc);
- if (that.headers.crit) {
+ if (this.headers.crit) {
// We don't support the crit header
return Promise.reject(Error("decrypt: crit is not supported"));
}
@@ -67,12 +66,12 @@ JoseJWE.Decrypter.prototype.decrypt = function (cipher_text) {
// the Million Message Attack on Cryptographic Message Syntax". We currently
// only support RSA-OAEP, so we don't generate a key if unwrapping fails.
var encrypted_cek = Utils.Base64Url.decodeArray(parts[1]);
- var cek_promise = that.key_promise.then(function (key) {
+ var cek_promise = this.key_promise.then(function(key) {
return this.cryptographer.unwrapCek(encrypted_cek, key);
- }.bind(that));
+ }.bind(this));
// part 3: decrypt the cipher text
- var plain_text_promise = that.cryptographer.decrypt(
+ var plain_text_promise = this.cryptographer.decrypt(
cek_promise,
Utils.arrayFromString(parts[0]),
Utils.Base64Url.decodeArray(parts[2]),
diff --git a/lib/jose-jwe-encrypt.js b/lib/jose-jwe-encrypt.js
index c498d57..7b53315 100644
--- a/lib/jose-jwe-encrypt.js
+++ b/lib/jose-jwe-encrypt.js
@@ -20,7 +20,7 @@
* @param cryptographer an instance of WebCryptographer (or equivalent).
* @param key_promise Promise<CryptoKey>, either RSA or shared key
*/
-JoseJWE.Encrypter = function (cryptographer, key_promise) {
+JoseJWE.Encrypter = function(cryptographer, key_promise) {
this.cryptographer = cryptographer;
this.key_promise = key_promise;
this.userHeaders = {};
@@ -39,7 +39,7 @@ JoseJWE.Encrypter = function (cryptographer, key_promise) {
* @param k String
* @param v String
*/
-JoseJWE.Encrypter.prototype.addHeader = function (k, v) {
+JoseJWE.Encrypter.prototype.addHeader = function(k, v) {
this.userHeaders[k] = v;
};
@@ -49,8 +49,7 @@ JoseJWE.Encrypter.prototype.addHeader = function (k, v) {
* @param plain_text String
* @return Promise<String>
*/
-JoseJWE.Encrypter.prototype.encrypt = function (plain_text) {
- var that = this;
+JoseJWE.Encrypter.prototype.encrypt = function(plain_text) {
/**
* Encrypts plain_text with CEK.
*
@@ -58,25 +57,24 @@ JoseJWE.Encrypter.prototype.encrypt = function (plain_text) {
* @param plain_text string
* @return Promise<json>
*/
- var encryptPlainText = function (cek_promise, plain_text) {
- var self = this;
+ var encryptPlainText = function(cek_promise, plain_text) {
// Create header
var headers = {};
- for(var i in self.userHeaders) {
- headers[i] = self.userHeaders[i];
+ for (var i in this.userHeaders) {
+ headers[i] = this.userHeaders[i];
}
- headers.alg = self.cryptographer.getKeyEncryptionAlgorithm();
- headers.enc = self.cryptographer.getContentEncryptionAlgorithm();
+ headers.alg = this.cryptographer.getKeyEncryptionAlgorithm();
+ headers.enc = this.cryptographer.getContentEncryptionAlgorithm();
var jwe_protected_header = Utils.Base64Url.encode(JSON.stringify(headers));
// Create the IV
- var iv = self.cryptographer.createIV();
+ var iv = this.cryptographer.createIV();
// Create the AAD
var aad = Utils.arrayFromString(jwe_protected_header);
plain_text = Utils.arrayFromString(plain_text);
- return self.cryptographer.encrypt(iv, aad, cek_promise, plain_text).then(function (r) {
+ return this.cryptographer.encrypt(iv, aad, cek_promise, plain_text).then(function(r) {
r.header = jwe_protected_header;
r.iv = iv;
return r;
@@ -84,20 +82,20 @@ JoseJWE.Encrypter.prototype.encrypt = function (plain_text) {
};
// Create a CEK key
- var cek_promise = that.cryptographer.createCek();
+ var cek_promise = this.cryptographer.createCek();
// Key & Cek allows us to create the encrypted_cek
- var encrypted_cek = Promise.all([that.key_promise, cek_promise]).then(function (all) {
- var key = all[0];
- var cek = all[1];
- return this.cryptographer.wrapCek(cek, key);
- }.bind(this));
+ var encrypted_cek = Promise.all([this.key_promise, cek_promise]).then(function(all) {
+ var key = all[0];
+ var cek = all[1];
+ return this.cryptographer.wrapCek(cek, key);
+ }.bind(this));
// Cek allows us to encrypy the plain text
- var enc_promise = encryptPlainText.bind(that, cek_promise, plain_text)();
+ var enc_promise = encryptPlainText.bind(this, cek_promise, plain_text)();
// Once we have all the promises, we can base64 encode all the pieces.
- return Promise.all([encrypted_cek, enc_promise]).then(function (all) {
+ return Promise.all([encrypted_cek, enc_promise]).then(function(all) {
var encrypted_cek = all[0];
var data = all[1];
return data.header + "." +
diff --git a/lib/jose-jwe-webcryptographer.js b/lib/jose-jwe-webcryptographer.js
index ee93ec5..36eddef 100644
--- a/lib/jose-jwe-webcryptographer.js
+++ b/lib/jose-jwe-webcryptographer.js
@@ -21,10 +21,9 @@
* duplication or callback vs Promise based API issues.
*/
var WebCryptographer = function() {
- var that = this;
- that.setKeyEncryptionAlgorithm("RSA-OAEP");
- that.setContentEncryptionAlgorithm("A256GCM");
- that.setContentSignAlgorithm("RS256");
+ this.setKeyEncryptionAlgorithm("RSA-OAEP");
+ this.setContentEncryptionAlgorithm("A256GCM");
+ this.setContentSignAlgorithm("RS256");
};
Jose.WebCryptographer = WebCryptographer;
@@ -92,10 +91,9 @@ WebCryptographer.prototype.wrapCek = function(cek, key) {
};
WebCryptographer.prototype.unwrapCek = function(cek, key) {
- var that = this;
- var hack = getCekWorkaround(that.content_encryption);
- var extractable = (that.content_encryption.specific_cek_bytes > 0);
- var key_encryption = that.key_encryption.id;
+ var hack = getCekWorkaround(this.content_encryption);
+ var extractable = (this.content_encryption.specific_cek_bytes > 0);
+ var key_encryption = this.key_encryption.id;
return crypto.subtle.unwrapKey("raw", cek, key, key_encryption, hack.id, extractable, hack.dec_op);
};
diff --git a/lib/jose-jws-sign.js b/lib/jose-jws-sign.js
index b114667..0e1170d 100644
--- a/lib/jose-jws-sign.js
+++ b/lib/jose-jws-sign.js
@@ -45,7 +45,7 @@ JoseJWS.Signer = function (cryptographer) {
*/
JoseJWS.Signer.prototype.addSigner = function (rsa_key, key_id, aad, header) {
var that = this,
- key_promise = isCryptoKey(rsa_key) ? new Promise(function (resolve) {
+ key_promise = Utils.isCryptoKey(rsa_key) ? new Promise(function (resolve) {
resolve(rsa_key);
}) : Jose.Utils.importRsaPrivateKey(rsa_key, aad && aad.alg ? aad.alg : that.cryptographer.getContentSignAlgorithm(), "sign"),
kid_promise;
@@ -54,7 +54,7 @@ JoseJWS.Signer.prototype.addSigner = function (rsa_key, key_id, aad, header) {
kid_promise = new Promise(function (resolve) {
resolve(key_id);
});
- } else if (isCryptoKey(rsa_key)) {
+ } else if (Utils.isCryptoKey(rsa_key)) {
throw new Error("key_id is a mandatory argument when the key is a CryptoKey");
} else {
kid_promise = Jose.WebCryptographer.keyId(rsa_key);
diff --git a/lib/jose-jws-verify.js b/lib/jose-jws-verify.js
index bfacef4..214b5ef 100644
--- a/lib/jose-jws-verify.js
+++ b/lib/jose-jws-verify.js
@@ -126,7 +126,7 @@ JoseJWS.Verifier.prototype.addRecipient = function (rsa_key, key_id, alg) {
var that = this,
kid_promise,
- key_promise = isCryptoKey(rsa_key) ? new Promise(function (resolve) {
+ key_promise = Utils.isCryptoKey(rsa_key) ? new Promise(function (resolve) {
resolve(rsa_key);
}) : Jose.Utils.importRsaPublicKey(rsa_key, alg || that.cryptographer.getContentSignAlgorithm(), "verify");
@@ -134,7 +134,7 @@ JoseJWS.Verifier.prototype.addRecipient = function (rsa_key, key_id, alg) {
kid_promise = new Promise(function (resolve) {
resolve(key_id);
});
- } else if (isCryptoKey(rsa_key)) {
+ } else if (Utils.isCryptoKey(rsa_key)) {
throw new Error("key_id is a mandatory argument when the key is a CryptoKey");
} else {
console.log("it's not safe to not pass a key_id");
diff --git a/lib/jose-jwe-utils.js b/lib/jose-utils.js
similarity index 93%
rename from lib/jose-jwe-utils.js
rename to lib/jose-utils.js
index 070c48b..a8655fd 100644
--- a/lib/jose-jwe-utils.js
+++ b/lib/jose-utils.js
@@ -30,14 +30,9 @@ var Utils = {};
* @return Promise<CryptoKey>
*/
Jose.Utils.importRsaPublicKey = function(rsa_key, alg) {
- var jwk,
- config = null,
- rk,
- usage;
-
- alg = alg || rsa_key.alg || "RSA-OAEP";
-
- usage = getKeyUsageByAlg(alg);
+ var jwk;
+ var config;
+ var usage = getKeyUsageByAlg(alg);
if (usage.publicKey == "wrapKey") {
if (!rsa_key.alg) {
@@ -46,7 +41,7 @@ Jose.Utils.importRsaPublicKey = function(rsa_key, alg) {
jwk = Utils.convertRsaKey(rsa_key, ["n", "e"]);
config = getCryptoConfig(alg);
} else {
- rk = {};
+ var rk = {};
for (var name in rsa_key) {
if (rsa_key.hasOwnProperty(name)) {
rk[name] = rsa_key[name];
@@ -76,14 +71,9 @@ Jose.Utils.importRsaPublicKey = function(rsa_key, alg) {
* @return Promise<CryptoKey>
*/
Jose.Utils.importRsaPrivateKey = function(rsa_key, alg) {
- var jwk,
- config,
- rk,
- usage;
-
- alg = alg || rsa_key.alg || "RSA-OAEP";
-
- usage = getKeyUsageByAlg(alg);
+ var jwk;
+ var config;
+ var usage = getKeyUsageByAlg(alg);
if (usage.privateKey == "unwrapKey") {
if (!rsa_key.alg) {
@@ -92,7 +82,7 @@ Jose.Utils.importRsaPrivateKey = function(rsa_key, alg) {
jwk = Utils.convertRsaKey(rsa_key, ["n", "e", "d", "p", "q", "dp", "dq", "qi"]);
config = getCryptoConfig("RSA-OAEP");
} else {
- rk = {};
+ var rk = {};
for (var name in rsa_key) {
if (rsa_key.hasOwnProperty(name)) {
rk[name] = rsa_key[name];
@@ -144,16 +134,12 @@ Utils.arrayish = function(arr) {
* @return json
*/
Utils.convertRsaKey = function(rsa_key, parameters) {
- var r = {},
- alg;
+ var r = {};
+ var alg;
// Check that we have all the parameters
var missing = [];
- parameters.map(function(p) {
- if (typeof(rsa_key[p]) == "undefined") {
- missing.push(p);
- }
- });
+ parameters.map(function(p){if (typeof(rsa_key[p]) == "undefined") { missing.push(p); }});
if (missing.length > 0) {
Jose.assert(false, "convertRsaKey: Was expecting " + missing.join());
@@ -173,11 +159,9 @@ Utils.convertRsaKey = function(rsa_key, parameters) {
getCryptoConfig(rsa_key.alg);
alg = rsa_key.alg;
} catch (er) {
+ Jose.assert(alg, "convertRsaKey: expecting rsa_key['alg'] to have a valid value");
}
}
-
- Jose.assert(alg, "convertRsaKey: expecting rsa_key['alg'] to have a valid value");
-
r.alg = alg;
// note: we punt on checking key_ops
@@ -353,17 +337,16 @@ Utils.sha256 = function(str) {
});
};
-var isCryptoKey = function(rsa_key) {
-
+Utils.isCryptoKey = function(rsa_key) {
var hexStr = /[a-f0-9]{2}(:[a-f0-9]{2})+/i;
- if (typeof window.CryptoKey != 'undefined') {
+ if (typeof(window.CryptoKey) != 'undefined') {
return rsa_key instanceof CryptoKey;
}
if (rsa_key instanceof Object) {
return hexStr.test(rsa_key.n) &&
- ((hexStr.test(rsa_key.e) || typeof rsa_key.e == 'number') ||
+ ((hexStr.test(rsa_key.e) || typeof(rsa_key.e) == 'number') ||
(hexStr.test(rsa_key.d) &&
hexStr.test(rsa_key.p) &&
hexStr.test(rsa_key.q) &&
diff --git a/test/jose-jwe-test.html b/test/jose-jwe-test.html
index 3cfb96c..7ba8d60 100644
--- a/test/jose-jwe-test.html
+++ b/test/jose-jwe-test.html
@@ -49,7 +49,7 @@
"AlWAyLWybqq6t16VFd7hQd0y6flUK4SlOydB61gwanOsXGOAOv82cHq0E3"+
"eL4HrtZkUuKvnPrMnsUUFlfUdybVzxyjz9JF_XyaY14ardLSjf4L_FNY"
};
- var public_rsa_key = Jose.Utils.importRsaPublicKey(rsa_key);
+ var public_rsa_key = Jose.Utils.importRsaPublicKey(rsa_key, "RSA-OAEP");
var plain_text = "The true sign of intelligence is not knowledge but imagination.";
var encrypter = new JoseJWE.Encrypter(cryptographer, public_rsa_key);
var cipher_text_promise = encrypter.encrypt(plain_text);
@@ -73,7 +73,7 @@
"XFBoMYUZodetZdvTiFvSkQ",
"got expected tag");
- var private_rsa_key = Jose.Utils.importRsaPrivateKey(rsa_key);
+ var private_rsa_key = Jose.Utils.importRsaPrivateKey(rsa_key, "RSA-OAEP");
var decrypter = new JoseJWE.Decrypter(cryptographer, private_rsa_key);
var decrypted_plain_text_promise = cipher_text_promise.then(function(cipher_text){
return decrypter.decrypt(cipher_text);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment