//******************************************************************************* // // Copyright 2014 Microsoft // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //******************************************************************************* /// #region JSCop/JsHint /// /// /// /// /// /* global operations */ /* global msrcryptoHashFunctions */ /* global cryptoMath */ /* global cryptoECC */ /* global msrcryptoPseudoRandom */ /* global msrcryptoJwk */ /* jshint -W016 */ /// btd,dtb,Ecdsa,ecop,msrcrypto /// DeclareVariablesBeforeUse,DeclarePropertiesBeforeUse /// #endregion JSCop/JsHint var msrcryptoEcdsa = function (curve) { var btd = cryptoMath.bytesToDigits, dtb = cryptoMath.digitsToBytes, ecop = new cryptoECC.EllipticCurveOperatorFp(curve), orderByteLength = dtb(curve.order).length; function createKey(privateKeyBytes) { return createKeyInternal(btd(privateKeyBytes)); } function createKeyInternal(privateKeyDigits) { if (!curve.generator.isInMontgomeryForm) { ecop.convertToMontgomeryForm(curve.generator); } var publicKey = curve.allocatePointStorage(); ecop.convertToJacobianForm(publicKey); ecop.convertToMontgomeryForm(publicKey); ecop.scalarMultiply(privateKeyDigits, curve.generator, publicKey); ecop.convertToAffineForm(publicKey); ecop.convertToStandardForm(publicKey); return { publicKey: publicKey, privateKey: privateKeyDigits }; } function generateKey() { var privateKey = [], randomBytes = msrcryptoPseudoRandom.getBytes( curve.order.length * cryptoMath.DIGIT_NUM_BYTES); cryptoMath.reduce( cryptoMath.bytesToDigits(randomBytes), curve.order, privateKey); if (!curve.generator.isInMontgomeryForm) { ecop.convertToMontgomeryForm(curve.generator); } return createKeyInternal(privateKey); } function getDigest(messageBytes) { // if the message length is longer than the curve-order, truncate. if (messageBytes.length > orderByteLength) { messageBytes.length = orderByteLength; } var digest = btd(messageBytes); cryptoMath.reduce(digest, curve.order, digest); return digest; } function sign(privateKey, messageBytes, /*@optional*/ ephemeralKey) { if (!ephemeralKey) { do { ephemeralKey = generateKey(); } while (cryptoMath.isZero(ephemeralKey.publicKey.x) || cryptoMath.compareDigits(curve.order, ephemeralKey.publicKey.x) === 0); } if (!ephemeralKey.publicKey) { throw new Error("ephemeralKey.publicKey missing"); } if (!ephemeralKey.privateKey) { throw new Error("ephemeralKey.privateKey missing"); } var r = ephemeralKey.publicKey.x, k = ephemeralKey.privateKey, d = btd(privateKey.d), digest = getDigest(messageBytes.slice()), s = [], tmp = [], signature = null; cryptoMath.modMul(r, d, curve.order, s); cryptoMath.add(s, digest, s); cryptoMath.reduce(s, curve.order, s); cryptoMath.modInv(k, curve.order, tmp); cryptoMath.modMul(s, tmp, curve.order, s); signature = dtb(r, true, orderByteLength).concat(dtb(s, true, orderByteLength)); return signature; } function verify(publicKey, signatureBytes, messageBytes) { var split = Math.floor(signatureBytes.length / 2), r = btd(signatureBytes.slice(0, split)), s = btd(signatureBytes.slice(split)), digest = getDigest(messageBytes.slice()), u1 = [], u2 = []; var publicPoint = new cryptoECC.EllipticCurvePointFp( curve, false, btd(publicKey.x), btd(publicKey.y), null, false); cryptoMath.modInv(s, curve.order, s); cryptoMath.modMul(digest, s, curve.order, u1); cryptoMath.modMul(r, s, curve.order, u2); var r0 = curve.allocatePointStorage(); ecop.convertToJacobianForm(r0); ecop.convertToMontgomeryForm(r0); if (!curve.generator.isInMontgomeryForm) { ecop.convertToMontgomeryForm(curve.generator); } if (!curve.generator.isAffine) { ecop.convertToStandardForm(curve.generator); } ecop.scalarMultiply(u1, curve.generator, r0); var r1 = curve.allocatePointStorage(); ecop.convertToJacobianForm(r1); ecop.convertToMontgomeryForm(r1); ecop.convertToMontgomeryForm(publicPoint); ecop.scalarMultiply(u2, publicPoint, r1); ecop.convertToAffineForm(r1); ecop.mixedAdd(r0, r1, r0); ecop.convertToAffineForm(r0); ecop.convertToStandardForm(r0); if (r0.isInfinity) { return false; } if (cryptoMath.compareDigits(r0.x, r) === 0) { return true; } return false; } return { createKey: createKey, generateKey: generateKey, sign: sign, verify: verify }; }; if (typeof operations !== "undefined") { msrcryptoEcdsa.curves = { "p-256": cryptoECC.createP256, "p-384": cryptoECC.createP384 }; msrcryptoEcdsa.sign = function ( /*@dynamic*/ p) { var hashName = p.algorithm.hash.name, curve = msrcryptoEcdsa.curves[p.algorithm.namedCurve](), hashFunc = msrcryptoHashFunctions[hashName.toLowerCase()], digest = hashFunc.computeHash(p.buffer); var ecdsa = msrcryptoEcdsa(curve); return ecdsa.sign(p.keyData, digest); }; msrcryptoEcdsa.verify = function ( /*@dynamic*/ p) { var hashName = p.algorithm.hash.name, curve = msrcryptoEcdsa.curves[p.algorithm.namedCurve](), hashFunc = msrcryptoHashFunctions[hashName.toLowerCase()], digest = hashFunc.computeHash(p.buffer); var ecdsa = msrcryptoEcdsa(curve); return ecdsa.verify(p.keyData, p.signature, digest); }; msrcryptoEcdsa.generateKey = function (p) { var curve = msrcryptoEcdsa.curves[p.algorithm.namedCurve](); var ecdsa = msrcryptoEcdsa(curve); var keyPairData = ecdsa.generateKey(); var dtb = cryptoMath.digitsToBytes; // Sometimes the result is a byte short because the byte-conversion // trims leading zeros. We pad the zeros back on if needed. function padTo8BytesIncrement(array) { return msrcryptoUtilities.padFront(array, 0, Math.ceil(array.length / 8) * 8); } var x = padTo8BytesIncrement(dtb(keyPairData.publicKey.x)); var y = padTo8BytesIncrement(dtb(keyPairData.publicKey.y)); var d = padTo8BytesIncrement(dtb(keyPairData.privateKey)); return { type: "keyPairGeneration", keyPair: { publicKey: { keyData: { x: x, y: y }, keyHandle: { algorithm: p.algorithm, extractable: p.extractable, keyUsage: null || p.keyUsage, type: "public" } }, privateKey: { keyData: { x: x, y: y, d: d }, keyHandle: { algorithm: p.algorithm, extractable: p.extractable, keyUsage: null || p.keyUsage, type: "private" } } } }; }; msrcryptoEcdsa.importKey = function (p) { var keyObject = msrcryptoJwk.jwkToKey(p.keyData, p.algorithm, ["x", "y", "d", "crv"]); // If only private key data 'd' is imported, create x and y if (keyObject.d && (!keyObject.x || !keyObject.y)) { var curve = msrcryptoEcdsa.curves[p.algorithm.namedCurve](); var ecdsa = msrcryptoEcdsa(curve); var publicKey = ecdsa.computePublicKey(keyObject.d); keyObject.x = publicKey.x; keyObject.y = publicKey.y; } return { type: "keyImport", keyData: keyObject, keyHandle: { algorithm: p.algorithm, extractable: p.extractable || keyObject.extractable, keyUsage: null || p.keyUsage, // IE11 returns null here type: (keyObject.d) ? "private" : "public" } }; }; msrcryptoEcdsa.exportKey = function (p) { var jsonKeyStringArray = msrcryptoJwk.keyToJwk(p.keyHandle, p.keyData); return { type: "keyExport", keyHandle: jsonKeyStringArray }; }; operations.register("sign", "ecdsa", msrcryptoEcdsa.sign); operations.register("verify", "ecdsa", msrcryptoEcdsa.verify); operations.register("generateKey", "ecdsa", msrcryptoEcdsa.generateKey); operations.register("importKey", "ecdsa", msrcryptoEcdsa.importKey); operations.register("exportKey", "ecdsa", msrcryptoEcdsa.exportKey); }