implemented verification of JWT signatures
This commit is contained in:
		
							parent
							
								
									4b63e38c1f
								
							
						
					
					
						commit
						6ec723ec1f
					
				| @ -14,6 +14,27 @@ | |||||||
|         return err; |         return err; | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|  |   , _binStr: { | ||||||
|  |       bufferToBinStr: function (buf) { | ||||||
|  |         return Array.prototype.map.call(new Uint8Array(buf), function(ch) { | ||||||
|  |           return String.fromCharCode(ch); | ||||||
|  |         }).join(''); | ||||||
|  |       } | ||||||
|  |     , binStrToBuffer: function (str) { | ||||||
|  |         var buf; | ||||||
|  | 
 | ||||||
|  |         if ('undefined' !== typeof Uint8Array) { | ||||||
|  |           buf = new Uint8Array(str.length); | ||||||
|  |         } else { | ||||||
|  |           buf = []; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         Array.prototype.forEach.call(str, function (ch, ind) { | ||||||
|  |           buf[ind] = ch.charCodeAt(0); | ||||||
|  |         }); | ||||||
|  |         return buf; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|   , _base64: { |   , _base64: { | ||||||
|       atob: function (base64) { |       atob: function (base64) { | ||||||
|         // atob must be called from the global context
 |         // atob must be called from the global context
 | ||||||
| @ -43,6 +64,12 @@ | |||||||
|         b64 = b64.replace(/=+/g, ''); |         b64 = b64.replace(/=+/g, ''); | ||||||
|         return b64; |         return b64; | ||||||
|       } |       } | ||||||
|  |     , urlSafeToBuffer: function (str) { | ||||||
|  |         return OAUTH3._binStr.binStrToBuffer(OAUTH3._base64.decodeUrlSafe(str)); | ||||||
|  |       } | ||||||
|  |     , bufferToUrlSafe: function (buf) { | ||||||
|  |         return OAUTH3._base64.encodeUrlSafe(OAUTH3._binStr.bufferToBinStr(buf)); | ||||||
|  |       } | ||||||
|     } |     } | ||||||
|   , uri: { |   , uri: { | ||||||
|       normalize: function (uri) { |       normalize: function (uri) { | ||||||
| @ -181,15 +208,26 @@ | |||||||
|         // { header: {}, payload: {}, signature: '' }
 |         // { header: {}, payload: {}, signature: '' }
 | ||||||
|         var parts = str.split(/\./g); |         var parts = str.split(/\./g); | ||||||
|         var jsons = parts.slice(0, 2).map(function (urlsafe64) { |         var jsons = parts.slice(0, 2).map(function (urlsafe64) { | ||||||
|           var b64 = OAUTH3._base64.decodeUrlSafe(urlsafe64); |           return JSON.parse(OAUTH3._base64.decodeUrlSafe(urlsafe64)); | ||||||
|           return b64; |  | ||||||
|         }); |         }); | ||||||
| 
 | 
 | ||||||
|         return { |         return { header: jsons[0], payload: jsons[1] }; | ||||||
|           header: JSON.parse(jsons[0]) |       } | ||||||
|         , payload: JSON.parse(jsons[1]) |     , verify: function (str, pubKey) { | ||||||
|         , signature: parts[2] // should remain url-safe base64
 |         var parts = str.split(/\./g); | ||||||
|         }; |         var data = OAUTH3._binStr.binStrToBuffer(parts.slice(0, 2).join('.')); | ||||||
|  |         var signature = OAUTH3._base64.urlSafeToBuffer(parts[2]); | ||||||
|  | 
 | ||||||
|  |         var keyPromise; | ||||||
|  |         if (pubKey instanceof OAUTH3._browser.window.CryptoKey) { | ||||||
|  |           keyPromise = OAUTH3.PromiseA.resolve(pubKey); | ||||||
|  |         } else { | ||||||
|  |           keyPromise = OAUTH3._browser.window.crypto.subtle.importKey('jwk', pubKey, {name: 'ECDSA', namedCurve: pubKey.crv}, false, ['verify']); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return keyPromise.then(function (key) { | ||||||
|  |           return OAUTH3._browser.window.crypto.subtle.verify({name: 'ECDSA', hash: {name: 'SHA-256'}}, key, signature, data); | ||||||
|  |         }); | ||||||
|       } |       } | ||||||
|     , freshness: function (tokenMeta, staletime, _now) { |     , freshness: function (tokenMeta, staletime, _now) { | ||||||
|         staletime = staletime || (15 * 60); |         staletime = staletime || (15 * 60); | ||||||
|  | |||||||
| @ -3,33 +3,6 @@ | |||||||
| 
 | 
 | ||||||
|   var OAUTH3 = exports.OAUTH3 = exports.OAUTH3 || require('./oauth3.core.js').OAUTH3; |   var OAUTH3 = exports.OAUTH3 = exports.OAUTH3 || require('./oauth3.core.js').OAUTH3; | ||||||
| 
 | 
 | ||||||
|   OAUTH3.utils.bufferToBinStr = function (buf) { |  | ||||||
|     return Array.prototype.map.call(new Uint8Array(buf), function(ch) { |  | ||||||
|       return String.fromCharCode(ch); |  | ||||||
|     }).join(''); |  | ||||||
|   }; |  | ||||||
|   OAUTH3.utils.binStrToBuffer = function (str) { |  | ||||||
|     var buf; |  | ||||||
| 
 |  | ||||||
|     if ('undefined' !== typeof Uint8Array) { |  | ||||||
|       buf = new Uint8Array(str.length); |  | ||||||
|     } else { |  | ||||||
|       buf = []; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     Array.prototype.forEach.call(str, function (ch, ind) { |  | ||||||
|       buf[ind] = ch.charCodeAt(0); |  | ||||||
|     }); |  | ||||||
|     return buf; |  | ||||||
|   }; |  | ||||||
| 
 |  | ||||||
|   OAUTH3._base64.urlSafeToBuffer = function (str) { |  | ||||||
|     return OAUTH3.utils.binStrToBuffer(OAUTH3._base64.decodeUrlSafe(str)); |  | ||||||
|   }; |  | ||||||
|   OAUTH3._base64.bufferToUrlSafe = function (buf) { |  | ||||||
|     return OAUTH3._base64.encodeUrlSafe(OAUTH3.utils.bufferToBinStr(buf)); |  | ||||||
|   }; |  | ||||||
| 
 |  | ||||||
|   OAUTH3.crypto = {}; |   OAUTH3.crypto = {}; | ||||||
|   OAUTH3.crypto.fingerprintJWK = function (jwk) { |   OAUTH3.crypto.fingerprintJWK = function (jwk) { | ||||||
|     var keys; |     var keys; | ||||||
| @ -51,7 +24,7 @@ | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     var jwkStr = '{' + keys.map(function (name) { return name+':'+jwk[name]; }).join(',') + '}'; |     var jwkStr = '{' + keys.map(function (name) { return name+':'+jwk[name]; }).join(',') + '}'; | ||||||
|     return window.crypto.subtle.digest({name: 'SHA-256'}, OAUTH3.utils.binStrToBuffer(jwkStr)) |     return window.crypto.subtle.digest({name: 'SHA-256'}, OAUTH3._binStr.binStrToBuffer(jwkStr)) | ||||||
|     .then(OAUTH3._base64.bufferToUrlSafe); |     .then(OAUTH3._base64.bufferToUrlSafe); | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
| @ -59,7 +32,7 @@ | |||||||
|     var kekPromise, ecdsaPromise, secretPromise; |     var kekPromise, ecdsaPromise, secretPromise; | ||||||
|     var salt = window.crypto.getRandomValues(new Uint8Array(16)); |     var salt = window.crypto.getRandomValues(new Uint8Array(16)); | ||||||
| 
 | 
 | ||||||
|     kekPromise = window.crypto.subtle.importKey('raw', OAUTH3.utils.binStrToBuffer(ppid), {name: 'PBKDF2'}, false, ['deriveKey']) |     kekPromise = window.crypto.subtle.importKey('raw', OAUTH3._binStr.binStrToBuffer(ppid), {name: 'PBKDF2'}, false, ['deriveKey']) | ||||||
|     .then(function (key) { |     .then(function (key) { | ||||||
|       var opts = {name: 'PBKDF2', salt: salt, iterations: 8192, hash: {name: 'SHA-256'}}; |       var opts = {name: 'PBKDF2', salt: salt, iterations: 8192, hash: {name: 'SHA-256'}}; | ||||||
|       return window.crypto.subtle.deriveKey(opts, key, {name: 'AES-GCM', length: 128}, false, ['encrypt']); |       return window.crypto.subtle.deriveKey(opts, key, {name: 'AES-GCM', length: 128}, false, ['encrypt']); | ||||||
| @ -92,8 +65,8 @@ | |||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     return OAUTH3.PromiseA.all([kekPromise, ecdsaPromise, secretPromise]).then(function (keys) { |     return OAUTH3.PromiseA.all([kekPromise, ecdsaPromise, secretPromise]).then(function (keys) { | ||||||
|       var ecdsaJwk  = OAUTH3.utils.binStrToBuffer(JSON.stringify(keys[1].privateKey)); |       var ecdsaJwk  = OAUTH3._binStr.binStrToBuffer(JSON.stringify(keys[1].privateKey)); | ||||||
|       var secretJwk = OAUTH3.utils.binStrToBuffer(JSON.stringify(keys[2])); |       var secretJwk = OAUTH3._binStr.binStrToBuffer(JSON.stringify(keys[2])); | ||||||
|       var ecdsaIv  = window.crypto.getRandomValues(new Uint8Array(12)); |       var ecdsaIv  = window.crypto.getRandomValues(new Uint8Array(12)); | ||||||
|       var secretIv = window.crypto.getRandomValues(new Uint8Array(12)); |       var secretIv = window.crypto.getRandomValues(new Uint8Array(12)); | ||||||
| 
 | 
 | ||||||
| @ -119,7 +92,7 @@ | |||||||
|     var encJwk = OAUTH3._base64.urlSafeToBuffer(storedObj.privateKey); |     var encJwk = OAUTH3._base64.urlSafeToBuffer(storedObj.privateKey); | ||||||
|     var iv     = OAUTH3._base64.urlSafeToBuffer(storedObj.ecdsaIv); |     var iv     = OAUTH3._base64.urlSafeToBuffer(storedObj.ecdsaIv); | ||||||
| 
 | 
 | ||||||
|     return window.crypto.subtle.importKey('raw', OAUTH3.utils.binStrToBuffer(ppid), {name: 'PBKDF2'}, false, ['deriveKey']) |     return window.crypto.subtle.importKey('raw', OAUTH3._binStr.binStrToBuffer(ppid), {name: 'PBKDF2'}, false, ['deriveKey']) | ||||||
|     .then(function (key) { |     .then(function (key) { | ||||||
|       var opts = {name: 'PBKDF2', salt: salt, iterations: 8192, hash: {name: 'SHA-256'}}; |       var opts = {name: 'PBKDF2', salt: salt, iterations: 8192, hash: {name: 'SHA-256'}}; | ||||||
|       return window.crypto.subtle.deriveKey(opts, key, {name: 'AES-GCM', length: 128}, false, ['decrypt']); |       return window.crypto.subtle.deriveKey(opts, key, {name: 'AES-GCM', length: 128}, false, ['decrypt']); | ||||||
| @ -127,7 +100,7 @@ | |||||||
|     .then(function (key) { |     .then(function (key) { | ||||||
|       return window.crypto.subtle.decrypt({name: 'AES-GCM', iv: iv}, key, encJwk); |       return window.crypto.subtle.decrypt({name: 'AES-GCM', iv: iv}, key, encJwk); | ||||||
|     }) |     }) | ||||||
|     .then(OAUTH3.utils.bufferToBinStr) |     .then(OAUTH3._binStr.bufferToBinStr) | ||||||
|     .then(JSON.parse) |     .then(JSON.parse) | ||||||
|     .then(function (jwk) { |     .then(function (jwk) { | ||||||
|       return window.crypto.subtle.importKey('jwk', jwk, {name: 'ECDSA', namedCurve: jwk.crv}, false, ['sign']) |       return window.crypto.subtle.importKey('jwk', jwk, {name: 'ECDSA', namedCurve: jwk.crv}, false, ['sign']) | ||||||
| @ -140,7 +113,7 @@ | |||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   OAUTH3.crypto._getKey = function (ppid) { |   OAUTH3.crypto._getKey = function (ppid) { | ||||||
|     return window.crypto.subtle.digest({name: 'SHA-256'}, OAUTH3.utils.binStrToBuffer(ppid)) |     return window.crypto.subtle.digest({name: 'SHA-256'}, OAUTH3._binStr.binStrToBuffer(ppid)) | ||||||
|     .then(function (hash) { |     .then(function (hash) { | ||||||
|       var name = 'kek-' + OAUTH3._base64.bufferToUrlSafe(hash); |       var name = 'kek-' + OAUTH3._base64.bufferToUrlSafe(hash); | ||||||
|       var promise; |       var promise; | ||||||
| @ -168,7 +141,7 @@ | |||||||
|       , OAUTH3._base64.encodeUrlSafe(JSON.stringify(payload, null)) |       , OAUTH3._base64.encodeUrlSafe(JSON.stringify(payload, null)) | ||||||
|       ].join('.'); |       ].join('.'); | ||||||
| 
 | 
 | ||||||
|       return window.crypto.subtle.sign({name: 'ECDSA', hash: {name: 'SHA-256'}}, key, OAUTH3.utils.binStrToBuffer(input)) |       return window.crypto.subtle.sign({name: 'ECDSA', hash: {name: 'SHA-256'}}, key, OAUTH3._binStr.binStrToBuffer(input)) | ||||||
|       .then(function (signature) { |       .then(function (signature) { | ||||||
|         return input + '.' + OAUTH3._base64.bufferToUrlSafe(signature); |         return input + '.' + OAUTH3._base64.bufferToUrlSafe(signature); | ||||||
|       }); |       }); | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user