UNPKG

oidc-lib

Version:

A library for creating OIDC Service Providers

326 lines (295 loc) 8.69 kB
module.exports = { 'registerEndpoints': registerEndpoints, 'random': random, 'encrypt_symmetric': encrypt_symmetric, 'decrypt_symmetric': decrypt_symmetric, 'verify_signature': verify_signature, 'digestSha256': digestSha256, 'jwkThumbprint': jwkThumbprint, 'createNumericCode': createNumericCode, 'createB64Code': createB64Code, 'claimHash': claimHash, 'randomString': randomString, 'oidc3136Hash': oidc3136Hash }; var pk; var default_sym_key; var platform_crypto; async function registerEndpoints(global_pk){ pk = global_pk; var conf_key = pk.util.config.default_sym_key; if (conf_key){ default_sym_key = JSON.parse(pk.util.config.default_sym_key); } platform_crypto = pk.util.usingNode() ? require('crypto') : window.crypto; } function random(options){ if (options.kind !== 'code'){ options.kind = 'base64url'; } if (!options.length){ options.length = 48; } return new Promise((resolve, reject) => { if (pk.util.usingNode()){ platform_crypto.randomBytes(options.length, (err, buf) => { if (err){ reject(err); } else { process(options, buf, resolve, reject); } }); } else{ var buf = crypto.getRandomValues(new Uint8Array(options.length)); process(options, buf, resolve, reject); } }); function process(options, buf, resolve, reject){ if (options.kind === 'code'){ var result = ""; for (var i=0; i<options.length; i++){ var digit = buf[i] % 10; result += String.fromCharCode(0x30 + digit); } resolve(result); } else{ resolve(pk.base64url.encode(buf)); } } } async function encrypt_symmetric(input){ if (!pk.util.usingNode()){ alert('simple_crypto encrypt_symetric called in windows') return; } if (typeof input === "object"){ input = JSON.stringify(input); } var b64 = pk.base64url(input); var key = await pk.jose.JWK.asKey(default_sym_key); var cipher = await pk.jose.JWE.createEncrypt({ format: 'compact' }, key).update(b64).final(); return cipher; } async function decrypt_symmetric(cipher, isString, label){ if (!pk.util.usingNode()){ alert('simple_crypto encrypt_symetric called in windows') return; } if (!cipher){ throw("no cipher in decrypt" + (label ? (" of " + label) : "")); } var key = await pk.jose.JWK.asKey(default_sym_key) var decryptResult = await pk.jose.JWE.createDecrypt(key).decrypt(cipher); var result = pk.base64url.decode(new Buffer(decryptResult.plaintext).toString('utf8')); if (!isString){ result = JSON.parse(result); } return (result); } async function verify_signature(signature, input_jwk){ if (!pk.util.usingNode()){ alert('simple_crypto verify_signature called in windows but not implemented there') return; } var possible_err; try{ var jwk = input_jwk; if (typeof input_jwk !== "object"){ jwk = JSON.parse(input_jwk); } var signature_components = signature.split('.'); if (!signature_components){ throw('invalid signature in verify signature - ' + signature); } possible_err = 'Error parsing signature components[0]'; var signature_header = JSON.parse(pk.base64url.decode(signature_components[0])); var signature_alg = signature_header.alg; var verified_result; switch (signature_alg){ case 'RS256': case 'ES256': case 'ES384': case 'ES512': possible_err = 'Error creating jose key: '; var keyStore = pk.jose.JWK.createKeyStore(); var jose_key = await keyStore.add(jwk); var permittedAlgs = ['RS256', 'ES256', 'ES384', 'ES512']; verified_result = await pk.jose.JWS.createVerify(jose_key, {algorithms:permittedAlgs}).verify(signature); break; case 'EdDSA': case 'Ed25519': possible_err = 'Error performing EdDSA verify'; var publicKey_buffer = pk.base64url.toBuffer(jwk.x); // ed25519.Verify(Buffer.from(message, 'utf8'), signature, aliceKeypair.publicKey)) var toBeSigned = signature_components[0] + '.' + signature_components[1]; var toBeSigned_buffer = Buffer.from(toBeSigned, 'utf8'); var signature_buffer = pk.base64url.toBuffer(signature_components[2]); verified_result = pk.forge.pki.ed25519.verify({ message: toBeSigned_buffer, signature: signature_buffer, publicKey: publicKey_buffer }); break; default: throw('unsupported algorithm - ' + signature_alg); } return verified_result; } catch(err){ pk.util.log_error('verify_signature', possible_err, err); var msg = 'verify_signature - ' + possible_err + ': ' + err.message; throw(msg); } } function digestSha256(input, asBuffer){ return new Promise((resolve, reject) => { if (pk.util.usingNode()){ var hasher = platform_crypto.createHash("sha256"); hasher.update(input, "utf-8"); if (asBuffer){ resolve(hasher.digest()); } else{ var b64 = hasher.digest("base64"); resolve(pk.base64url.fromBase64(b64)); } } else { var buffer = Buffer(input, 'utf8'); platform_crypto.subtle.digest("SHA-256", buffer) .then( buf => { if (asBuffer){ resolve(buf) } else{ resolve(pk.base64url.encode(buf)); } }) } }); } function jwkThumbprint(jwk_key){ var toBeSigned; switch (jwk_key.kty){ case 'RSA': toBeSigned = { e: jwk_key.e, kty: jwk_key.kty, n: jwk_key.n }; break; case 'EC': toBeSigned = { crv: jwk_key.crv, kty: jwk_key.kty, x: jwk_key.x, y: jwk_key.y }; break; case 'oct': toBeSigned = { k: jwk_key.k, kty: jwk_key.kty }; break; case 'OKP': toBeSigned = { crv: jwk_key.crv, kty: 'OKP', x: jwk_key.x } break; default: throw "Undefined key type " + jwk_key.kty + " in jwkThumbprint"; } var toBeSignedString = JSON.stringify(toBeSigned); return digestSha256(toBeSignedString); } function createNumericCode(size){ return new Promise((resolve, reject) => { platform_crypto.randomBytes(size, function(err, buf){ if (err){ reject(err); } else { var result = ""; for (var i=0; i<size; i++){ var digit = buf[i] % 10; result += String.fromCharCode(0x30 + digit); } resolve(result); } }); }); } function createB64Code(size){ return new Promise((resolve, reject) => { if (pk.util.usingNode()){ platform_crypto.randomBytes(size, function(err, buf){ if (err){ reject(err); } else { resolve(pk.base64url.encode(buf)); } }); } else { var vector = crypto.getRandomValues(new Uint8Array(size)); var result = pk.base64url.encode(vector); resolve(result); } }); } function claimHash(alg, salt, claimValue){ var hasher = platform_crypto.createHash(alg); if (typeof claimValue !== 'string'){ claimValue = JSON.stringify(claimValue); } hasher.update(salt + claimValue, "utf-8"); var b64 = hasher.digest("base64"); return b64; } function randomString() { return new Promise((resolve, reject) => { if (pk.util.usingNode()){ platform_crypto.randomBytes(48, function(err, buf){ if (err){ reject(err); } else { resolve(pk.base64url.encode(buf)); } }); } else { var vector = crypto.getRandomValues(new Uint8Array(16)); var result = pk.base64url.encode(vector); resolve(result); } }); } // Defined in OIDC 3.1.3.6 access_token hash. function oidc3136Hash(response_type, match, input){ return new Promise((resolve, reject) => { // if the response_type doesn't require the hash, return null var response_type_array = response_type.split(' '); if (response_type_array.indexOf(match) < 0){ resolve(null); } else{ if (pk.util.usingNode()){ // var buffer = Buffer(input, 'ascii'); // resolve(platform_crypto.subtle.digest("SHA-256", buffer)); var hasher = platform_crypto.createHash("sha256"); hasher.update(input, "ascii"); var full = hasher.digest(); var half = full.slice(0, full.length/2); resolve(pk.base64url(half, "ascii")); } else { var buffer = Buffer(input, 'ascii'); resolve(platform_crypto.subtle.digest("SHA-256", buffer)); } } }); }