edhoc
Version:
A Node.js implementation of EDHOC (Ephemeral Diffie-Hellman Over COSE) protocol for lightweight authenticated key exchange in IoT and other constrained environments.
152 lines (151 loc) • 6.74 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.DefaultEdhocCryptoManager = void 0;
const edhoc_1 = require("./edhoc");
const ed25519_1 = require("@noble/curves/ed25519");
const p256_1 = require("@noble/curves/p256");
const sha256_1 = require("@noble/hashes/sha256");
const hkdf_1 = require("@noble/hashes/hkdf");
const crypto_1 = require("crypto");
class DefaultEdhocCryptoManager {
generateKeyPair(edhoc) {
const curveKE = this.getCurveForKeyAgreement(edhoc.selectedSuite);
const privateKey = Buffer.from(curveKE.utils.randomPrivateKey());
const publicKey = Buffer.from(curveKE.getPublicKey(privateKey)).subarray(curveKE === p256_1.p256 ? 1 : 0);
return { publicKey, privateKey };
}
keyAgreement(edhoc, privateKey, peerPublicKey) {
const curveKE = this.getCurveForKeyAgreement(edhoc.selectedSuite);
const publicKeyBuffer = this.formatPublicKey(curveKE, peerPublicKey);
const sharedSecret = Buffer.from(curveKE.getSharedSecret(privateKey, new Uint8Array(publicKeyBuffer)));
return sharedSecret.subarray(curveKE === p256_1.p256 ? 1 : 0);
}
sign(edhoc, privateKey, input) {
const curveSIG = this.getCurveForSignature(edhoc.selectedSuite);
const payload = this.formatToBeSigned(curveSIG, input);
const signature = curveSIG.sign(payload, new Uint8Array(privateKey));
if (signature instanceof Uint8Array) {
return Buffer.from(signature);
}
else if ('toCompactRawBytes' in signature) {
return Buffer.from(signature.toCompactRawBytes());
}
else {
throw new Error('Unsupported signature type');
}
}
async verify(edhoc, publicKey, input, signature) {
const curveSIG = this.getCurveForSignature(edhoc.selectedSuite);
const publicKeyBuffer = this.formatPublicKey(curveSIG, publicKey);
const payload = this.formatToBeSigned(curveSIG, input);
if (!curveSIG.verify(new Uint8Array(signature), payload, new Uint8Array(publicKeyBuffer))) {
throw new Error('Signature not verified');
}
return true;
}
hkdfExtract(_edhoc, ikm, salt) {
return Buffer.from((0, hkdf_1.extract)(sha256_1.sha256, new Uint8Array(ikm), new Uint8Array(salt)));
}
hkdfExpand(_edhoc, prk, info, length) {
return Buffer.from((0, hkdf_1.expand)(sha256_1.sha256, new Uint8Array(prk), new Uint8Array(info), length));
}
async encrypt(edhoc, key, nonce, aad, plaintext) {
const algorithm = this.getAlgorithm(edhoc.selectedSuite);
const options = {
authTagLength: this.getTagLength(edhoc.selectedSuite)
};
const cipher = (0, crypto_1.createCipheriv)(algorithm, key, nonce, options);
cipher.setAAD(aad, { plaintextLength: Buffer.byteLength(plaintext) });
const update = Buffer.byteLength(plaintext) === 0 ? Buffer.alloc(0) : plaintext;
const encrypted = Buffer.concat([
cipher.update(update),
cipher.final(),
cipher.getAuthTag()
]);
return encrypted;
}
async decrypt(edhoc, key, nonce, aad, ciphertext) {
const tagLength = this.getTagLength(edhoc.selectedSuite);
const algorithm = this.getAlgorithm(edhoc.selectedSuite);
const options = { authTagLength: tagLength };
const decipher = (0, crypto_1.createDecipheriv)(algorithm, key, nonce, options);
decipher.setAuthTag(ciphertext.subarray(ciphertext.length - tagLength));
decipher.setAAD(aad, { plaintextLength: ciphertext.length - tagLength });
const decrypted = decipher.update(ciphertext.subarray(0, ciphertext.length - tagLength));
decipher.final();
return decrypted;
}
async hash(_edhoc, input) {
return Buffer.from((0, sha256_1.sha256)(input));
}
formatToBeSigned(curve, payload) {
if (curve === p256_1.p256) {
return Buffer.from((0, sha256_1.sha256)(payload));
}
else if (curve === ed25519_1.ed25519) {
return payload;
}
else {
throw new Error(`Unsupported curve ${curve}`);
}
}
formatPublicKey(curve, key) {
if (curve === p256_1.p256) {
if (key.byteLength === 65 && key[0] === 0x04) {
// Already X9.63 uncompressed (0x04 || x || y)
return key;
}
const prefix = key.byteLength === 64 ? 0x04 : (key[key.length - 1] & 1) ? 0x03 : 0x02;
return Buffer.concat([Buffer.from([prefix]), key]);
}
else if (curve === ed25519_1.ed25519 || curve === ed25519_1.x25519) {
return key;
}
else {
throw new Error(`Unsupported curve ${curve}`);
}
}
getCurveForSignature(suite) {
if ([edhoc_1.EdhocSuite.Suite2, edhoc_1.EdhocSuite.Suite3, edhoc_1.EdhocSuite.Suite5, edhoc_1.EdhocSuite.Suite6].includes(suite)) {
return p256_1.p256;
}
else if ([edhoc_1.EdhocSuite.Suite0, edhoc_1.EdhocSuite.Suite1, edhoc_1.EdhocSuite.Suite4].includes(suite)) {
return ed25519_1.ed25519;
}
else {
throw new Error(`Unsupported EDHOC suite ${suite} for signature.`);
}
}
getCurveForKeyAgreement(suite) {
if ([edhoc_1.EdhocSuite.Suite2, edhoc_1.EdhocSuite.Suite3, edhoc_1.EdhocSuite.Suite5].includes(suite)) {
return p256_1.p256;
}
else if ([edhoc_1.EdhocSuite.Suite0, edhoc_1.EdhocSuite.Suite1, edhoc_1.EdhocSuite.Suite4, edhoc_1.EdhocSuite.Suite6].includes(suite)) {
return ed25519_1.x25519;
}
else {
throw new Error(`Unsupported EDHOC suite ${suite} for key agreement.`);
}
}
getTagLength(suite) {
return [edhoc_1.EdhocSuite.Suite0, edhoc_1.EdhocSuite.Suite2].includes(suite) ? 8 : 16;
}
getAlgorithm(suite) {
if ([edhoc_1.EdhocSuite.Suite4, edhoc_1.EdhocSuite.Suite5, edhoc_1.EdhocSuite.Suite25].includes(suite)) {
return 'chacha20-poly1305';
}
else if ([edhoc_1.EdhocSuite.Suite6].includes(suite)) {
return 'aes-128-gcm';
}
else if ([edhoc_1.EdhocSuite.Suite24].includes(suite)) {
return 'aes-256-gcm';
}
else if ([edhoc_1.EdhocSuite.Suite0, edhoc_1.EdhocSuite.Suite1, edhoc_1.EdhocSuite.Suite2, edhoc_1.EdhocSuite.Suite3].includes(suite)) {
return 'aes-128-ccm';
}
else {
throw new Error(`Unsupported EDHOC suite ${suite} for encryption.`);
}
}
}
exports.DefaultEdhocCryptoManager = DefaultEdhocCryptoManager;