did-sdk-js
Version:
js sdk for did and vc according to mcps did spec
287 lines (286 loc) • 11.5 kB
JavaScript
// import {
// aesCbcEncrypt,
// aesCbcDecrypt,
// aesCbcEncryptSync,
// aesCbcDecryptSync,
// } from './aes';
// import { derive } from './ecdh';
// import { getPublic, decompress, compress } from './ecdsa';
// import {
// hmacSha256Sign,
// hmacSha256Verify,
// hmacSha256SignSync,
// hmacSha256VerifySync,
// } from './hmac';
// import { randomBytes } from './random';
// import { sha512, sha512Sync } from './sha2';
//
// import {
// LENGTH_0,
// KEY_LENGTH,
// IV_LENGTH,
// MAC_LENGTH,
// PREFIXED_KEY_LENGTH,
// ERROR_BAD_MAC,
// } from './constants';
// import {
// PreEncryptOpts,
// isValidPrivateKey,
// Encrypted,
// assert,
// concatBuffers,
// } from './helpers';
Object.defineProperty(exports, "__esModule", { value: true });
exports.decrypt = exports.encrypt = exports.Encrypted = exports.kdf = exports.aesCbcEncrypt = void 0;
const crypto = require("crypto");
const eccrypto_js_1 = require("eccrypto-js");
const enc = require("enc-utils");
const secp256k1 = require('secp256k1');
let CryptoJS = require("crypto-js");
/*
add by gangm: 基于"https://github.com.cnpmjs.org/sigp/ecies-parity.git"和https://hub.fastgit.org/pedrouid/eccrypto-js.git 库
ECIES几个标准:ANSI X9.63, IEEE 1363a 和 ISO/IEC 18033-2
这里选用: IEEE 1363a
为了多语言兼容,这里统一兼容java库org.bouncycastle.crypto中“ECIESWITHSHA256ANDAES-CBC”的实现
aes/cbc模式需要额外添加iv信息:https://github.com/bcgit/bc-java/issues/493
也为了方便后续扩展,加密结果返回原始数据(iv/pubkey/cipher/hmac),上层封装具体序列化方式(比如:publicKey(65) + iv(16) + cipher_text + hmac)
*/
function sha256(msg) {
return crypto.createHash("sha256").update(msg).digest();
}
// aes-cbc-256-256
function aesCbcEncrypt(plainText, secretKey, iv) {
return eccrypto_js_1.aesCbcEncryptSync(iv, secretKey, plainText);
// let key = CryptoJS.enc.Utf8.parse(secretKey.toString());
// let cipher = CryptoJS.AES.encrypt(plainText.toString(), key, {
// iv: key,
// mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7
// }
// ).ciphertext.toString()
//
// return Buffer.from(cipher, 'hex')
}
exports.aesCbcEncrypt = aesCbcEncrypt;
function aesCbcDecrypt(cipherText, secretKey, iv) {
return eccrypto_js_1.aesCbcDecryptSync(iv, secretKey, cipherText);
// from base64 cipher str
// let key = CryptoJS.enc.Utf8.parse(secretKey);
// return CryptoJS.AES.decrypt(cipherText, secretKey, {
// iv: iv,
// mode: CryptoJS.mode.CBC,
// padding: CryptoJS.pad.Pkcs7
// }
// ).toString(CryptoJS.enc.Utf8);
}
function hmacSha256(key, msg) {
return crypto.createHmac("sha256", key).update(msg).digest();
}
// The KDF as implemented in Parity
function kdf(secret, outputLength) {
let ctr = 1;
let written = 0;
let result = Buffer.from('');
while (written < outputLength) {
let ctrs = Buffer.from([ctr >> 24, ctr >> 16, ctr >> 8, ctr]);
let hashResult = sha256(Buffer.concat([secret, ctrs]));
result = Buffer.concat([result, hashResult]);
written += 32;
ctr += 1;
}
return result.slice(0, outputLength);
}
exports.kdf = kdf;
// export function secp256k1Derive(
// publicKey: Buffer,
// privateKey: Buffer,
// compressed?: boolean
// ) {
// let result = secp256k1.ecdhUnsafe(publicKey, privateKey, compressed);
// // result.tri
// return trimLeft(result, KEY_LENGTH);
// }
//
// function derive(privateKeyA: Buffer, publicKeyB: Buffer): Buffer {
// publicKeyB = secp256k1Decompress(publicKeyB)
// return secp256k1Derive(privateKeyA, publicKeyB, false);
// }
// function getPublic(privateKey: Buffer) {
// var compressed = secp256k1.publicKeyCreate(privateKey);
// return secp256k1.publicKeyConvert(compressed, false);
// }
class Encrypted {
constructor(iv, ephemPublicKey, ciphertext, mac) {
this.iv = iv;
this.ephemPublicKey = ephemPublicKey;
this.ciphertext = ciphertext;
this.mac = mac;
}
serialize() {
return enc.concatBuffers(this.iv, this.ephemPublicKey, this.ciphertext, this.mac);
}
static deserialize(cipherData) {
const slice0 = eccrypto_js_1.LENGTH_0;
const slice_iv = eccrypto_js_1.IV_LENGTH;
const slice_pubkey = slice_iv + eccrypto_js_1.PREFIXED_DECOMPRESSED_LENGTH;
const slice_data = cipherData.length - eccrypto_js_1.MAC_LENGTH;
const slice_mac = cipherData.length;
return new Encrypted(cipherData.slice(slice0, slice_iv), cipherData.slice(slice_iv, slice_pubkey), cipherData.slice(slice_pubkey, slice_data), cipherData.slice(slice_data, slice_mac));
}
}
exports.Encrypted = Encrypted;
function encrypt(publicKeyTo, msg, opts) {
opts = opts || {};
let ephemPrivateKey = opts.ephemPrivateKey || crypto.randomBytes(32);
let ephemPublicKey = eccrypto_js_1.getPublic(ephemPrivateKey);
// getSharedKey
publicKeyTo = eccrypto_js_1.decompress(publicKeyTo);
let sharedPx = eccrypto_js_1.derive(ephemPrivateKey, publicKeyTo);
let vz = Buffer.concat([ephemPublicKey, sharedPx]); // 参考java:bouncycastle库
let hash = kdf(vz, eccrypto_js_1.LENGTH_32 + eccrypto_js_1.MAC_LENGTH); // ke.length + km.length
let iv = opts.iv || crypto.randomBytes(eccrypto_js_1.IV_LENGTH);
let encryptionKey = hash.slice(0, eccrypto_js_1.LENGTH_32);
let macKey = hash.slice(eccrypto_js_1.LENGTH_32);
let ciphertext = aesCbcEncrypt(msg, encryptionKey, iv);
let L2 = Buffer.alloc(8); // as described in Shroup's paper and P1363a;保持跟java bouncycastle库一致
L2.fill(0);
let dataToMac = Buffer.concat([ciphertext, L2]);
let HMAC = hmacSha256(macKey, dataToMac);
return new Encrypted(iv, ephemPublicKey, ciphertext, HMAC);
}
exports.encrypt = encrypt;
function decrypt(privateKey, encrypted) {
let sharedPx = eccrypto_js_1.derive(privateKey, eccrypto_js_1.decompress(encrypted.ephemPublicKey));
let vz = Buffer.concat([encrypted.ephemPublicKey, sharedPx]); // 参考java:bouncycastle库
let hash = kdf(vz, eccrypto_js_1.LENGTH_32 + eccrypto_js_1.MAC_LENGTH);
let encryptionKey = hash.slice(0, eccrypto_js_1.LENGTH_32);
let macKey = hash.slice(eccrypto_js_1.LENGTH_32);
let L2 = Buffer.alloc(8); // as described in Shroup's paper and P1363a;保持跟java bouncycastle库一致
L2.fill(0);
let dataToMac = Buffer.concat([encrypted.ciphertext, L2]);
let HMAC = hmacSha256(macKey, dataToMac);
const verifyMac = eccrypto_js_1.equalConstTime(HMAC, encrypted.mac);
eccrypto_js_1.assert(verifyMac, eccrypto_js_1.ERROR_BAD_MAC);
return aesCbcDecrypt(encrypted.ciphertext, encryptionKey, encrypted.iv);
}
exports.decrypt = decrypt;
//
// function getSharedKey(privateKey: Buffer, publicKey: Buffer) {
// publicKey = decompress(publicKey);
// return derive(privateKey, publicKey);
// }
//
// function getEncryptionKey(hash: Buffer) {
// return Buffer.from(hash.slice(LENGTH_0, KEY_LENGTH));
// }
//
// function getMacKey(hash: Buffer) {
// return Buffer.from(hash.slice(KEY_LENGTH));
// }
//
// async function getEciesKeys(privateKey: Buffer, publicKey: Buffer) {
// const sharedKey = getSharedKey(privateKey, publicKey);
// const hash = await sha512(sharedKey);
// return { encryptionKey: getEncryptionKey(hash), macKey: getMacKey(hash) };
// }
//
// function getEciesKeysSync(privateKey: Buffer, publicKey: Buffer) {
// const sharedKey = getSharedKey(privateKey, publicKey);
// const hash = sha512Sync(sharedKey);
// return { encryptionKey: getEncryptionKey(hash), macKey: getMacKey(hash) };
// }
//
// function getEphemKeyPair(opts?: Partial<PreEncryptOpts>) {
// let ephemPrivateKey = opts?.ephemPrivateKey || randomBytes(KEY_LENGTH);
// while (!isValidPrivateKey(ephemPrivateKey)) {
// ephemPrivateKey = opts?.ephemPrivateKey || randomBytes(KEY_LENGTH);
// }
// const ephemPublicKey = getPublic(ephemPrivateKey);
// return { ephemPrivateKey, ephemPublicKey };
// }
//
// export async function encrypt(
// publicKeyTo: Buffer,
// msg: Buffer,
// opts?: Partial<PreEncryptOpts>
// ): Promise<Encrypted> {
// const { ephemPrivateKey, ephemPublicKey } = getEphemKeyPair(opts);
// const { encryptionKey, macKey } = await getEciesKeys(
// ephemPrivateKey,
// publicKeyTo
// );
// const iv = opts?.iv || randomBytes(IV_LENGTH);
// const ciphertext = await aesCbcEncrypt(iv, encryptionKey, msg);
// const dataToMac = concatBuffers(iv, ephemPublicKey, ciphertext);
// const mac = await hmacSha256Sign(macKey, dataToMac);
// return { iv, ephemPublicKey, ciphertext, mac: mac };
// }
//
// export async function decrypt(
// privateKey: Buffer,
// opts: Encrypted
// ): Promise<Buffer> {
// const { ephemPublicKey, iv, mac, ciphertext } = opts;
// const { encryptionKey, macKey } = await getEciesKeys(
// privateKey,
// ephemPublicKey
// );
// const dataToMac = concatBuffers(iv, ephemPublicKey, ciphertext);
// const macTest = await hmacSha256Verify(macKey, dataToMac, mac);
// assert(macTest, ERROR_BAD_MAC);
// const msg = await aesCbcDecrypt(opts.iv, encryptionKey, opts.ciphertext);
// return msg;
// }
//
// export function encryptSync(
// publicKeyTo: Buffer,
// msg: Buffer,
// opts?: Partial<PreEncryptOpts>
// ): Encrypted {
// const { ephemPrivateKey, ephemPublicKey } = getEphemKeyPair(opts);
// const { encryptionKey, macKey } = getEciesKeysSync(
// ephemPrivateKey,
// publicKeyTo
// );
// const iv = opts?.iv || randomBytes(IV_LENGTH);
// const ciphertext = aesCbcEncryptSync(iv, encryptionKey, msg);
// const dataToMac = concatBuffers(iv, ephemPublicKey, ciphertext);
// const mac = hmacSha256SignSync(macKey, dataToMac);
// return { iv, ephemPublicKey, ciphertext, mac: mac };
// }
//
// export async function decryptSync(
// privateKey: Buffer,
// opts: Encrypted
// ): Promise<Buffer> {
// const { ephemPublicKey, iv, mac, ciphertext } = opts;
// const { encryptionKey, macKey } = getEciesKeysSync(
// privateKey,
// ephemPublicKey
// );
// const dataToMac = concatBuffers(iv, ephemPublicKey, ciphertext);
// const macTest = hmacSha256VerifySync(macKey, dataToMac, mac);
// assert(macTest, ERROR_BAD_MAC);
// const msg = aesCbcDecryptSync(opts.iv, encryptionKey, opts.ciphertext);
// return msg;
// }
//
// export function serialize(opts: Encrypted): Buffer {
// const ephemPublicKey = compress(opts.ephemPublicKey);
// return concatBuffers(opts.iv, ephemPublicKey, opts.mac, opts.ciphertext);
// }
//
// export function deserialize(buf: Buffer): Encrypted {
// const slice0 = LENGTH_0;
// const slice1 = slice0 + IV_LENGTH;
// const slice2 = slice1 + PREFIXED_KEY_LENGTH;
// const slice3 = slice2 + MAC_LENGTH;
// const slice4 = buf.length;
// return {
// iv: buf.slice(slice0, slice1),
// ephemPublicKey: decompress(buf.slice(slice1, slice2)),
// mac: buf.slice(slice2, slice3),
// ciphertext: buf.slice(slice3, slice4),
// };
// }
//# sourceMappingURL=ecies.js.map
;