@hashgraph/cryptography
Version:
Cryptographic utilities and primitives for the Hiero SDK
137 lines (127 loc) • 4.7 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.createKeystore = createKeystore;
exports.loadKeystore = loadKeystore;
var _BadKeyError = _interopRequireDefault(require("../BadKeyError.cjs"));
var crypto = _interopRequireWildcard(require("./aes.cjs"));
var hex = _interopRequireWildcard(require("../encoding/hex.cjs"));
var utf8 = _interopRequireWildcard(require("../encoding/utf8.cjs"));
var hmac = _interopRequireWildcard(require("./hmac.cjs"));
var pbkdf2 = _interopRequireWildcard(require("./pbkdf2.cjs"));
var random = _interopRequireWildcard(require("./random.cjs"));
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
const HMAC_SHA256 = "hmac-sha256";
/**
* @typedef {object} KeystoreKdfParams
* @property {number} dkLen
* @property {string} salt
* @property {number} c
* @property {string} prf
*/
/**
* @typedef {object} KeystoreCipherParams
* @property {string} iv
*/
/**
* @typedef {object} KeystoreCrypto
* @property {string} ciphertext
* @property {KeystoreCipherParams} cipherparams
* @property {string} cipher
* @property {string} kdf
* @property {KeystoreKdfParams} kdfparams
* @property {string} mac
*/
/**
* @typedef {object} Keystore
* @property {number} version
* @property {KeystoreCrypto} crypto
*/
/**
* @param {Uint8Array} privateKey
* @param {string} passphrase
* @returns {Promise<Uint8Array>}
*/
async function createKeystore(privateKey, passphrase) {
// all values taken from https://github.com/ethereumjs/ethereumjs-wallet/blob/de3a92e752673ada1d78f95cf80bc56ae1f59775/src/index.ts#L25
const dkLen = 32;
const c = 262144;
const saltLen = 32;
const salt = await random.bytesAsync(saltLen);
const key = await pbkdf2.deriveKey(hmac.HashAlgorithm.Sha256, passphrase, salt, c, dkLen);
const iv = await random.bytesAsync(16);
// AES-128-CTR with the first half of the derived key and a random IV
const cipherText = await crypto.createCipheriv(crypto.CipherAlgorithm.Aes128Ctr, key.slice(0, 16), iv, privateKey);
const mac = await hmac.hash(hmac.HashAlgorithm.Sha384, key.slice(16), cipherText);
/**
* @type {Keystore}
*/
const keystore = {
version: 1,
crypto: {
ciphertext: hex.encode(cipherText),
cipherparams: {
iv: hex.encode(iv)
},
cipher: crypto.CipherAlgorithm.Aes128Ctr,
kdf: "pbkdf2",
kdfparams: {
dkLen,
salt: hex.encode(salt),
c,
prf: HMAC_SHA256
},
mac: hex.encode(mac)
}
};
return utf8.encode(JSON.stringify(keystore));
}
/**
* @param {Uint8Array} keystoreBytes
* @param {string} passphrase
* @returns {Promise<Uint8Array>}
*/
async function loadKeystore(keystoreBytes, passphrase) {
/**
* @type {Keystore}
*/
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const keystore = JSON.parse(utf8.decode(keystoreBytes));
if (keystore.version !== 1) {
throw new _BadKeyError.default(`unsupported keystore version: ${keystore.version}`);
}
const {
ciphertext,
cipherparams: {
iv
},
cipher,
kdf,
kdfparams: {
dkLen,
salt,
c,
prf
},
mac
} = keystore.crypto;
if (kdf !== "pbkdf2") {
throw new _BadKeyError.default(`unsupported key derivation function:" + ${kdf}`);
}
if (prf !== HMAC_SHA256) {
throw new _BadKeyError.default(`unsupported key derivation hash function: ${prf}`);
}
const saltBytes = hex.decode(salt);
const ivBytes = hex.decode(iv);
const cipherBytes = hex.decode(ciphertext);
const key = await pbkdf2.deriveKey(hmac.HashAlgorithm.Sha256, passphrase, saltBytes, c, dkLen);
const macHex = hex.decode(mac);
const verifyHmac = await hmac.hash(hmac.HashAlgorithm.Sha384, key.slice(16), cipherBytes);
// compare that these two Uint8Arrays are equivalent
if (!macHex.every((b, i) => b === verifyHmac[i])) {
throw new _BadKeyError.default("HMAC mismatch; passphrase is incorrect");
}
return crypto.createDecipheriv(cipher, key.slice(0, 16), ivBytes, cipherBytes);
}