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.

206 lines (205 loc) 8.61 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 { keys = {}; keyIdentifier = 1000; constructor() { this.keys = {}; } addKey(keyID, key) { const kid = keyID.toString('hex'); this.keys[kid] = key; } async importKey(edhoc, keyType, key) { const keyBuffer = Buffer.alloc(4); keyBuffer.writeInt32LE(this.keyIdentifier++); const keyID = keyBuffer.toString('hex'); const curveKE = this.getCurveForKeyAgreement(edhoc.selectedSuite); const curveSIG = this.getCurveForSignature(edhoc.selectedSuite); switch (keyType) { case edhoc_1.EdhocKeyType.KeyAgreement: case edhoc_1.EdhocKeyType.MakeKeyPair: this.keys[keyID] = key.byteLength > 0 ? Buffer.from(key) : Buffer.from(curveKE.utils.randomPrivateKey()); break; case edhoc_1.EdhocKeyType.Signature: this.keys[keyID] = key.byteLength > 0 ? Buffer.from(key) : Buffer.from(curveSIG.utils.randomPrivateKey()); break; default: this.keys[keyID] = Buffer.from(key); } return keyBuffer; } destroyKey(edhoc, keyID) { const kid = keyID.toString('hex'); if (kid in this.keys === false) { throw new Error(`Key '${kid}' not found`); } delete this.keys[kid]; return true; } makeKeyPair(edhoc, keyID, _privateKeySize, _publicKeySize) { const key = this.getKey(keyID); try { const curveKE = this.getCurveForKeyAgreement(edhoc.selectedSuite); return { privateKey: Buffer.from(key), publicKey: Buffer.from(curveKE.getPublicKey(key)).subarray(curveKE === p256_1.p256 ? 1 : 0) }; } catch (error) { throw new Error(`Wrong key type`); } } keyAgreement(edhoc, keyID, publicKey, _privateKeySize) { const key = this.getKey(keyID); const curveKE = this.getCurveForKeyAgreement(edhoc.selectedSuite); const publicKeyBuffer = this.formatPublicKey(curveKE, publicKey); const sharedSecrect = Buffer.from(curveKE.getSharedSecret(key, new Uint8Array(publicKeyBuffer))); return sharedSecrect.subarray(curveKE === p256_1.p256 ? 1 : 0); } sign(edhoc, keyID, input, _signatureSize) { const key = this.getKey(keyID); const curveSIG = this.getCurveForSignature(edhoc.selectedSuite); const payload = this.formatToBeSigned(curveSIG, input); const signature = curveSIG.sign(payload, new Uint8Array(key)); 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, keyID, input, signature) { const key = this.getKey(keyID); const curveSIG = this.getCurveForSignature(edhoc.selectedSuite); const publicKeyBuffer = this.formatPublicKey(curveSIG, key); const payload = this.formatToBeSigned(curveSIG, input); if (!curveSIG.verify(new Uint8Array(signature), payload, new Uint8Array(publicKeyBuffer))) { throw new Error('Signature not verified'); } return true; } extract(edhoc, keyID, salt, _keySize) { const key = this.getKey(keyID); return Buffer.from((0, hkdf_1.extract)(sha256_1.sha256, new Uint8Array(key), new Uint8Array(salt))); } expand(edhoc, keyID, info, keySize) { const key = this.getKey(keyID); const expanded = Buffer.from((0, hkdf_1.expand)(sha256_1.sha256, new Uint8Array(key), new Uint8Array(info), keySize)); return expanded; } encrypt(edhoc, keyID, nonce, aad, plaintext, _size) { const key = this.getKey(keyID); 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; } decrypt(edhoc, keyID, nonce, aad, ciphertext, _size) { const key = this.getKey(keyID); 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, data, _hashSize) { return Buffer.from((0, sha256_1.sha256)(data)); } getKey(keyID) { const kid = keyID.toString('hex'); if (kid in this.keys === false) { throw new Error(`Key '${kid}' not found`); } return this.keys[kid]; } 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) { 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;