UNPKG

aladinnetwork-blockstack

Version:

The Aladin Javascript library for authentication, identity, and storage.

198 lines 7.18 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const elliptic_1 = require("elliptic"); const crypto_1 = __importDefault(require("crypto")); const keys_1 = require("../keys"); const ecurve = new elliptic_1.ec('secp256k1'); /** * @ignore */ function aes256CbcEncrypt(iv, key, plaintext) { const cipher = crypto_1.default.createCipheriv('aes-256-cbc', key, iv); return Buffer.concat([cipher.update(plaintext), cipher.final()]); } /** * @ignore */ function aes256CbcDecrypt(iv, key, ciphertext) { const cipher = crypto_1.default.createDecipheriv('aes-256-cbc', key, iv); return Buffer.concat([cipher.update(ciphertext), cipher.final()]); } /** * @ignore */ function hmacSha256(key, content) { return crypto_1.default.createHmac('sha256', key).update(content).digest(); } /** * @ignore */ function equalConstTime(b1, b2) { if (b1.length !== b2.length) { return false; } let res = 0; for (let i = 0; i < b1.length; i++) { res |= b1[i] ^ b2[i]; // jshint ignore:line } return res === 0; } /** * @ignore */ function sharedSecretToKeys(sharedSecret) { // generate mac and encryption key from shared secret const hashedSecret = crypto_1.default.createHash('sha512').update(sharedSecret).digest(); return { encryptionKey: hashedSecret.slice(0, 32), hmacKey: hashedSecret.slice(32) }; } /** * @ignore */ function getHexFromBN(bnInput) { const hexOut = bnInput.toString('hex'); if (hexOut.length === 64) { return hexOut; } else if (hexOut.length < 64) { // pad with leading zeros // the padStart function would require node 9 const padding = '0'.repeat(64 - hexOut.length); return `${padding}${hexOut}`; } else { throw new Error('Generated a > 32-byte BN for encryption. Failing.'); } } exports.getHexFromBN = getHexFromBN; /** * Encrypt content to elliptic curve publicKey using ECIES * @param {String} publicKey - secp256k1 public key hex string * @param {String | Buffer} content - content to encrypt * @return {Object} Object containing (hex encoded): * iv (initialization vector), cipherText (cipher text), * mac (message authentication code), ephemeral public key * wasString (boolean indicating with or not to return a buffer or string on decrypt) * * @private * @ignore */ function encryptECIES(publicKey, content) { const isString = (typeof (content) === 'string'); // always copy to buffer const plainText = content instanceof Buffer ? Buffer.from(content) : Buffer.from(content); const ecPK = ecurve.keyFromPublic(publicKey, 'hex').getPublic(); const ephemeralSK = ecurve.genKeyPair(); const ephemeralPK = ephemeralSK.getPublic(); const sharedSecret = ephemeralSK.derive(ecPK); const sharedSecretHex = getHexFromBN(sharedSecret); const sharedKeys = sharedSecretToKeys(Buffer.from(sharedSecretHex, 'hex')); const initializationVector = crypto_1.default.randomBytes(16); const cipherText = aes256CbcEncrypt(initializationVector, sharedKeys.encryptionKey, plainText); const macData = Buffer.concat([initializationVector, Buffer.from(ephemeralPK.encodeCompressed()), cipherText]); const mac = hmacSha256(sharedKeys.hmacKey, macData); return { iv: initializationVector.toString('hex'), ephemeralPK: ephemeralPK.encodeCompressed('hex'), cipherText: cipherText.toString('hex'), mac: mac.toString('hex'), wasString: isString }; } exports.encryptECIES = encryptECIES; /** * Decrypt content encrypted using ECIES * @param {String} privateKey - secp256k1 private key hex string * @param {Object} cipherObject - object to decrypt, should contain: * iv (initialization vector), cipherText (cipher text), * mac (message authentication code), ephemeralPublicKey * wasString (boolean indicating with or not to return a buffer or string on decrypt) * @return {Buffer} plaintext * @throws {Error} if unable to decrypt * @private * @ignore */ function decryptECIES(privateKey, cipherObject) { const ecSK = ecurve.keyFromPrivate(privateKey, 'hex'); const ephemeralPK = ecurve.keyFromPublic(cipherObject.ephemeralPK, 'hex').getPublic(); const sharedSecret = ecSK.derive(ephemeralPK); const sharedSecretBuffer = Buffer.from(getHexFromBN(sharedSecret), 'hex'); const sharedKeys = sharedSecretToKeys(sharedSecretBuffer); const ivBuffer = Buffer.from(cipherObject.iv, 'hex'); const cipherTextBuffer = Buffer.from(cipherObject.cipherText, 'hex'); const macData = Buffer.concat([ivBuffer, Buffer.from(ephemeralPK.encodeCompressed()), cipherTextBuffer]); const actualMac = hmacSha256(sharedKeys.hmacKey, macData); const expectedMac = Buffer.from(cipherObject.mac, 'hex'); if (!equalConstTime(expectedMac, actualMac)) { throw new Error('Decryption failed: failure in MAC check'); } const plainText = aes256CbcDecrypt(ivBuffer, sharedKeys.encryptionKey, cipherTextBuffer); if (cipherObject.wasString) { return plainText.toString(); } else { return plainText; } } exports.decryptECIES = decryptECIES; /** * Sign content using ECDSA * * @param {String} privateKey - secp256k1 private key hex string * @param {Object} content - content to sign * @return {Object} contains: * signature - Hex encoded DER signature * public key - Hex encoded private string taken from privateKey * @private * @ignore */ function signECDSA(privateKey, content) { const contentBuffer = content instanceof Buffer ? content : Buffer.from(content); const ecPrivate = ecurve.keyFromPrivate(privateKey, 'hex'); const publicKey = keys_1.getPublicKeyFromPrivate(privateKey); const contentHash = crypto_1.default.createHash('sha256').update(contentBuffer).digest(); const signature = ecPrivate.sign(contentHash); const signatureString = signature.toDER('hex'); return { signature: signatureString, publicKey }; } exports.signECDSA = signECDSA; /** * @ignore */ function getBuffer(content) { if (content instanceof Buffer) return content; else if (content instanceof ArrayBuffer) return Buffer.from(content); else return Buffer.from(content); } /** * Verify content using ECDSA * @param {String | Buffer} content - Content to verify was signed * @param {String} publicKey - secp256k1 private key hex string * @param {String} signature - Hex encoded DER signature * @return {Boolean} returns true when signature matches publickey + content, false if not * @private * @ignore */ function verifyECDSA(content, publicKey, signature) { const contentBuffer = getBuffer(content); const ecPublic = ecurve.keyFromPublic(publicKey, 'hex'); const contentHash = crypto_1.default.createHash('sha256').update(contentBuffer).digest(); return ecPublic.verify(contentHash, signature); } exports.verifyECDSA = verifyECDSA; //# sourceMappingURL=ec.js.map