UNPKG

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
"use strict"; 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;