UNPKG

@ldclabs/cose-ts

Version:

Implemented Keys, Algorithms (RFC9053), COSE (RFC9052) and CWT (RFC8392) in TypeScript.

87 lines 4.07 kB
// (c) 2023-present, LDC Labs. All rights reserved. // See the file LICENSE for licensing terms. import { randomBytes } from './utils'; import { Header } from './header'; import * as iana from './iana'; import { decodeCBOR, encodeCBOR } from './utils'; import { skipTag, withTag, CwtPrefix, Encrypt0MessagePrefix, CBORSelfPrefix } from './tag'; // Encrypt0Message represents a COSE_Encrypt0 object. // // Reference https://datatracker.ietf.org/doc/html/rfc9052#name-single-recipient-encrypted. export class Encrypt0Message { payload; // protected header parameters: iana.HeaderParameterAlg, iana.HeaderParameterCrit. protected = null; // Other header parameters. unprotected = null; static encBytes(protectedHeader, externalData) { return encodeCBOR([ 'Encrypt0', protectedHeader, externalData ?? new Uint8Array() ]); } static async fromBytes(key, coseData, externalData) { const data = skipTag(Encrypt0MessagePrefix, skipTag(CwtPrefix, skipTag(CBORSelfPrefix, coseData))); const [protectedBytes, unprotected, ciphertext] = decodeCBOR(data); const protectedHeader = Header.fromBytes(protectedBytes); const unprotectedHeader = new Header(unprotected); if (protectedHeader.has(iana.HeaderParameterAlg)) { const alg = protectedHeader.getInt(iana.HeaderParameterAlg); if (alg !== key.alg) { throw new Error(`cose-ts: Encrypt0Message.fromBytes: alg mismatch, expected ${alg}, got ${key.alg}`); } } const ivSize = key.nonceSize(); // TODO: support partial iv const iv = unprotectedHeader.getBytes(iana.HeaderParameterIV); if (iv.length !== ivSize) { throw new Error(`cose-ts: Encrypt0Message.fromBytes: iv size mismatch, expected ${ivSize}, got ${iv.length}`); } const plaintext = await key.decrypt(ciphertext, iv, Encrypt0Message.encBytes(protectedBytes, externalData)); return new Encrypt0Message(plaintext, protectedHeader, unprotectedHeader); } static withTag(coseData) { return withTag(Encrypt0MessagePrefix, coseData); } constructor(payload, protectedHeader, unprotected) { this.payload = payload; this.protected = protectedHeader ? new Header(protectedHeader.toRaw()) : null; this.unprotected = unprotected ? new Header(unprotected.toRaw()) : null; } async toBytes(key, externalData) { if (this.protected == null) { this.protected = new Header(); if (key.has(iana.KeyParameterAlg)) { this.protected.setParam(iana.HeaderParameterAlg, key.alg); } } else if (this.protected.has(iana.HeaderParameterAlg)) { const alg = this.protected.getInt(iana.HeaderParameterAlg); if (alg !== key.alg) { throw new Error(`cose-ts: Encrypt0Message.toBytes: alg mismatch, expected ${alg}, got ${key.alg}`); } } if (this.unprotected == null) { this.unprotected = new Header(); if (key.has(iana.KeyParameterKid)) { this.unprotected.setParam(iana.HeaderParameterKid, key.kid); } } const ivSize = key.nonceSize(); // TODO: support partial iv if (!this.unprotected.has(iana.HeaderParameterIV)) { this.unprotected.setParam(iana.HeaderParameterIV, randomBytes(ivSize)); } const iv = this.unprotected.getBytes(iana.HeaderParameterIV); if (iv.length !== ivSize) { throw new Error(`cose-ts: Encrypt0Message.toBytes: iv size mismatch, expected ${ivSize}, got ${iv.length}`); } const protectedBytes = this.protected.toBytes(); const ciphertext = await key.encrypt(this.payload, iv, Encrypt0Message.encBytes(protectedBytes, externalData)); return encodeCBOR([protectedBytes, this.unprotected.toRaw(), ciphertext]); } } //# sourceMappingURL=encrypt0.js.map