| 
									
										
										
										
											2017-03-14 14:33:11 -06:00
										 |  |  | ;(function () { | 
					
						
							|  |  |  | 'use strict'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   var createHash = require('create-hash'); | 
					
						
							|  |  |  |   var pbkdf2 = require('pbkdf2'); | 
					
						
							|  |  |  |   var aes = require('browserify-aes'); | 
					
						
							| 
									
										
										
										
											2017-03-21 17:27:52 -06:00
										 |  |  |   var ec = require('elliptic/lib/elliptic/ec')('p256'); | 
					
						
							| 
									
										
										
										
											2017-03-14 14:33:11 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-17 17:14:47 -06:00
										 |  |  |   function sha256(buf) { | 
					
						
							|  |  |  |     return createHash('sha256').update(buf).digest(); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2017-03-14 14:33:11 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-20 16:11:14 -06:00
										 |  |  |   function runPbkdf2(password, salt) { | 
					
						
							| 
									
										
										
										
											2017-03-14 14:33:11 -06:00
										 |  |  |     // Derived AES key is 128 bit, and the function takes a size in bytes.
 | 
					
						
							| 
									
										
										
										
											2017-03-20 16:11:14 -06:00
										 |  |  |     return pbkdf2.pbkdf2Sync(password, Buffer(salt), 8192, 16, 'sha256'); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-20 17:41:20 -06:00
										 |  |  |   function encrypt(key, iv, data) { | 
					
						
							| 
									
										
										
										
											2017-03-20 16:11:14 -06:00
										 |  |  |     var cipher = aes.createCipheriv('aes-128-gcm', Buffer(key), Buffer(iv)); | 
					
						
							| 
									
										
										
										
											2017-03-14 14:33:11 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-17 17:14:47 -06:00
										 |  |  |     return Buffer.concat([cipher.update(Buffer(data)), cipher.final(), cipher.getAuthTag()]); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-20 17:41:20 -06:00
										 |  |  |   function decrypt(key, iv, data) { | 
					
						
							| 
									
										
										
										
											2017-03-20 16:11:14 -06:00
										 |  |  |     var decipher = aes.createDecipheriv('aes-128-gcm', Buffer(key), Buffer(iv)); | 
					
						
							| 
									
										
										
										
											2017-03-14 14:33:11 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |     decipher.setAuthTag(Buffer(data.slice(-16))); | 
					
						
							| 
									
										
										
										
											2017-03-17 17:14:47 -06:00
										 |  |  |     return Buffer.concat([decipher.update(Buffer(data.slice(0, -16))), decipher.final()]); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-21 17:16:40 -06:00
										 |  |  |   function bnToBuffer(bn, size) { | 
					
						
							|  |  |  |     var buf = bn.toArrayLike(Buffer); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!size || buf.length === size) { | 
					
						
							|  |  |  |       return buf; | 
					
						
							|  |  |  |     } else if (buf.length < size) { | 
					
						
							|  |  |  |       return Buffer.concat([Buffer(size-buf.length).fill(0), buf]); | 
					
						
							|  |  |  |     } else if (buf.length > size) { | 
					
						
							|  |  |  |       throw new Error('EC signature number bigger than expected'); | 
					
						
							| 
									
										
										
										
											2017-03-17 17:14:47 -06:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2017-03-21 17:16:40 -06:00
										 |  |  |     throw new Error('invalid size "'+size+'" converting BigNumber to Buffer'); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   function bnToB64(bn) { | 
					
						
							|  |  |  |     var b64 = bnToBuffer(bn).toString('base64'); | 
					
						
							| 
									
										
										
										
											2017-03-17 17:14:47 -06:00
										 |  |  |     return b64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=*$/, ''); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   function genEcdsaKeyPair() { | 
					
						
							|  |  |  |     var key = ec.genKeyPair(); | 
					
						
							|  |  |  |     var pubJwk = { | 
					
						
							|  |  |  |       key_ops: ['verify'] | 
					
						
							|  |  |  |     , kty: 'EC' | 
					
						
							|  |  |  |     , crv: 'P-256' | 
					
						
							| 
									
										
										
										
											2017-03-21 17:16:40 -06:00
										 |  |  |     , x: bnToB64(key.getPublic().getX()) | 
					
						
							|  |  |  |     , y: bnToB64(key.getPublic().getY()) | 
					
						
							| 
									
										
										
										
											2017-03-17 17:14:47 -06:00
										 |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     var privJwk = JSON.parse(JSON.stringify(pubJwk)); | 
					
						
							|  |  |  |     privJwk.key_ops = ['sign']; | 
					
						
							| 
									
										
										
										
											2017-03-20 16:11:14 -06:00
										 |  |  |     privJwk.d = bnToB64(key.getPrivate()); | 
					
						
							| 
									
										
										
										
											2017-03-17 17:14:47 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |     return {privateKey: privJwk, publicKey: pubJwk}; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   function sign(jwk, msg) { | 
					
						
							|  |  |  |     var key = ec.keyFromPrivate(Buffer(jwk.d, 'base64')); | 
					
						
							|  |  |  |     var sig = key.sign(sha256(msg)); | 
					
						
							| 
									
										
										
										
											2017-03-20 16:11:14 -06:00
										 |  |  |     return Buffer.concat([bnToBuffer(sig.r, 32), bnToBuffer(sig.s, 32)]); | 
					
						
							| 
									
										
										
										
											2017-03-17 17:14:47 -06:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   function verify(jwk, msg, signature) { | 
					
						
							|  |  |  |     var key = ec.keyFromPublic({x: Buffer(jwk.x, 'base64'), y: Buffer(jwk.y, 'base64')}); | 
					
						
							|  |  |  |     var sig = { | 
					
						
							|  |  |  |       r: Buffer(signature.slice(0, signature.length/2)) | 
					
						
							|  |  |  |     , s: Buffer(signature.slice(signature.length/2)) | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |     return key.verify(sha256(msg), sig); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2017-03-14 14:33:11 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-20 16:11:14 -06:00
										 |  |  |   function promiseWrap(func) { | 
					
						
							|  |  |  |     return function() { | 
					
						
							|  |  |  |       var args = arguments; | 
					
						
							| 
									
										
										
										
											2017-03-21 17:16:40 -06:00
										 |  |  |       // This fallback file should only be used when the browser doesn't support everything we
 | 
					
						
							|  |  |  |       // need with WebCrypto. Since it is only used in the browser we should be able to assume
 | 
					
						
							|  |  |  |       // that OAUTH3 has been placed in the global scope and that we can access it here.
 | 
					
						
							|  |  |  |       return new OAUTH3.PromiseA(function (resolve) { | 
					
						
							| 
									
										
										
										
											2017-03-20 16:11:14 -06:00
										 |  |  |         resolve(func.apply(null, args)); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   exports.sha256  = promiseWrap(sha256); | 
					
						
							|  |  |  |   exports.pbkdf2  = promiseWrap(runPbkdf2); | 
					
						
							|  |  |  |   exports.encrypt = promiseWrap(encrypt); | 
					
						
							|  |  |  |   exports.decrypt = promiseWrap(decrypt); | 
					
						
							|  |  |  |   exports.sign    = promiseWrap(sign); | 
					
						
							|  |  |  |   exports.verify  = promiseWrap(verify); | 
					
						
							|  |  |  |   exports.genEcdsaKeyPair = promiseWrap(genEcdsaKeyPair); | 
					
						
							| 
									
										
										
										
											2017-03-14 14:33:11 -06:00
										 |  |  | }()); |