/** * @license * Copyright 2022-2026 Matter.js Authors * SPDX-License-Identifier: Apache-2.0 */ import { DerCodec, DerError, DerRawUint } from "#codec/DerCodec.js"; import { Bytes } from "#util/Bytes.js"; import { SignatureEncodingError } from "./CryptoError.js"; /** * An ECDSA signature. * * Input and output may be IEEE-P1363 or DER encoded. Matter helpfully mixes and matches so we validate input to ensure * the correct encoding is specified. Extraction to bytes must explicitly use {@link bytes} or {@link der} to specify * the desired format. * * Currently we only support 256-bit curves. */ export class EcdsaSignature { #bytes: Bytes; constructor(bytes: Bytes, encoding = "ieee-p1363") { if (encoding === "der") { try { const decoded = DerCodec.decode(bytes); const r = DerCodec.decodeBigUint(decoded?._elements?.[0], 32); const s = DerCodec.decodeBigUint(decoded?._elements?.[1], 32); this.#bytes = Bytes.concat(r, s); } catch (cause) { DerError.accept(cause); throw new SignatureEncodingError("Could not decode DER signature", { cause }); } } else { // Change this if/when we support more curves if (bytes.byteLength !== 64) { throw new SignatureEncodingError("Invalid IEEE P1364 signature length"); } this.#bytes = bytes; } } /** * Access signature in IEEE P1363 format. */ get bytes() { return this.#bytes; } /** * Access signature in DER format. */ get der() { const bytes = Bytes.of(this.#bytes); const bytesPerComponent = bytes.length / 2; return DerCodec.encode({ r: DerRawUint(bytes.slice(0, bytesPerComponent)), s: DerRawUint(bytes.slice(bytesPerComponent)), }); } } export namespace EcdsaSignature { /** * IEEE P1363 encoding. * * This is a simple concatenation of the raw R and S elements */ export const IEEE_P1363 = "ieee-p1363"; /** * DER encoding. * * This encodes R and S elements in a sequence of separate integers. */ export const DER = "der"; export type Encoding = typeof IEEE_P1363 | typeof DER; }