@continuonai/rcan-ts 1.2.0 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -109,8 +109,9 @@ interface DelegationHop {
109
109
  scope: string;
110
110
  signature: string;
111
111
  }
112
+ /** RCAN v2.2: ML-DSA-65 is the only valid alg ("ml-dsa-65"). Ed25519 is deprecated. */
112
113
  interface SignatureBlock {
113
- alg: string;
114
+ alg: "ml-dsa-65";
114
115
  kid: string;
115
116
  sig: string;
116
117
  }
@@ -767,7 +768,7 @@ declare function makeTransparencyMessage(ruri: string, disclosure: string, deleg
767
768
  /** The RCAN spec version this SDK implements. */
768
769
  declare const SPEC_VERSION = "2.2.0";
769
770
  /** The SDK release version. */
770
- declare const SDK_VERSION = "1.2.0";
771
+ declare const SDK_VERSION = "1.2.1";
771
772
  /**
772
773
  * Validate version compatibility.
773
774
  *
@@ -1863,81 +1864,55 @@ declare function verifyM2mTrustedToken(token: string, targetRrn: string, options
1863
1864
  }): Promise<M2MTrustedClaims>;
1864
1865
 
1865
1866
  /**
1866
- * RCAN v2.2 Post-Quantum Hybrid Signing — ML-DSA-65 (NIST FIPS 204)
1867
- *
1868
- * Provides MLDSAKeyPair for post-quantum signing alongside the existing
1869
- * Ed25519 SignatureBlock. In hybrid mode a message carries both:
1870
- * - `signature` — Ed25519 SignatureBlock (backward-compat with v2.1)
1871
- * - `pqSig` — MLDSASignatureBlock (new in v2.2)
1867
+ * RCAN v2.2 ML-DSA-65 Signing (NIST FIPS 204)
1872
1868
  *
1873
- * Key sizes (ML-DSA-65, NIST security level 3):
1874
- * Public key: 1952 bytes
1875
- * Private key: 4032 bytes
1876
- * Signature: 3309 bytes
1869
+ * Ed25519 is deprecated. ML-DSA-65 is the ONLY signing algorithm.
1870
+ * All signed messages carry a ``signature`` block with ``alg: "ml-dsa-65"``.
1877
1871
  *
1878
1872
  * Requires: @noble/post-quantum (npm install @noble/post-quantum)
1879
1873
  *
1880
- * Spec: https://rcan.dev/spec#section-7-2
1874
+ * Spec: https://rcan.dev/spec/v2.2#section-7-2
1881
1875
  */
1882
1876
 
1883
- /** @deprecated use PQSignatureBlock */
1884
- type MLDSASignatureBlock = PQSignatureBlock;
1885
1877
  interface MLDSAKeyPairData {
1886
- /** Public key bytes (1952 bytes) */
1887
1878
  publicKey: Uint8Array;
1888
- /** Private key bytes (4032 bytes). Absent for verify-only key pairs. */
1889
1879
  secretKey?: Uint8Array;
1890
- /** 8-char hex key ID */
1891
1880
  keyId: string;
1892
1881
  }
1893
1882
  /**
1894
1883
  * An ML-DSA-65 (CRYSTALS-Dilithium, NIST FIPS 204) key pair.
1895
1884
  *
1896
- * Immutable value object. Build via {@link MLDSAKeyPair.generate} or
1897
- * {@link MLDSAKeyPair.fromPublicKey}.
1885
+ * This is the ONLY signing key type in RCAN v2.2+. Ed25519 is deprecated.
1898
1886
  */
1899
1887
  declare class MLDSAKeyPair {
1900
1888
  readonly keyId: string;
1901
1889
  readonly publicKey: Uint8Array;
1902
1890
  readonly secretKey: Uint8Array | undefined;
1903
1891
  private constructor();
1904
- /** Generate a new ML-DSA-65 key pair. */
1905
1892
  static generate(): Promise<MLDSAKeyPair>;
1906
- /** Build a verify-only key pair from raw public key bytes. */
1907
1893
  static fromPublicKey(publicKey: Uint8Array): Promise<MLDSAKeyPair>;
1908
- /** Build a full key pair from saved bytes (public + secret). */
1909
1894
  static fromKeyMaterial(publicKey: Uint8Array, secretKey: Uint8Array): Promise<MLDSAKeyPair>;
1910
1895
  get hasPrivateKey(): boolean;
1911
- /** Sign raw bytes; returns ML-DSA-65 signature (3309 bytes). */
1912
1896
  signBytes(data: Uint8Array): Promise<Uint8Array>;
1913
- /**
1914
- * Verify an ML-DSA-65 signature.
1915
- * @returns true if valid
1916
- * @throws {Error} on invalid signature
1917
- */
1918
1897
  verifyBytes(data: Uint8Array, signature: Uint8Array): Promise<void>;
1919
1898
  toString(): string;
1920
1899
  }
1921
1900
  /**
1922
- * Add an ML-DSA-65 signature (`pqSig`) to an RCAN message.
1923
- *
1924
- * This is the v2.2 hybrid complement to the existing Ed25519 signature.
1925
- * Call {@link signMessageEd25519} (or the existing signing path) first to set
1926
- * `signature`, then call this to append `pqSig`.
1901
+ * Sign an RCANMessage with ML-DSA-65 (the only signing algorithm in RCAN v2.2).
1927
1902
  *
1928
- * @param msg RCAN message (mutated in place sets `pqSig`)
1929
- * @param keypair ML-DSA-65 key pair with private key
1930
- * @returns The same message cast to include `pqSig`
1903
+ * Sets msg.signature = { alg: "ml-dsa-65", kid, sig }.
1931
1904
  */
1932
- declare function addPQSignature(msg: RCANMessage, keypair: MLDSAKeyPair): Promise<RCANMessage>;
1905
+ declare function signMessage(msg: RCANMessage, keypair: MLDSAKeyPair): Promise<RCANMessage>;
1933
1906
  /**
1934
- * Verify the ML-DSA-65 signature (`pqSig`) on an RCAN message.
1907
+ * Verify the ML-DSA-65 signature on an RCANMessage.
1935
1908
  *
1936
- * @param msg Message with a `pqSig` field
1937
- * @param trustedKeys Trusted ML-DSA public key pairs
1938
- * @param requirePQ If true, raise when `pqSig` is absent (for post-2028 hard mode)
1909
+ * @throws {Error} if signature is missing, alg is not ml-dsa-65, key not found, or invalid.
1939
1910
  */
1940
- declare function verifyPQSignature(msg: RCANMessage, trustedKeys: MLDSAKeyPair[], requirePQ?: boolean): Promise<void>;
1911
+ declare function verifyMessage(msg: RCANMessage, trustedKeys: MLDSAKeyPair[]): Promise<void>;
1912
+ /** @deprecated Use signMessage() — Ed25519 is removed in RCAN v2.2 */
1913
+ declare const addPQSignature: typeof signMessage;
1914
+ /** @deprecated Use verifyMessage() — Ed25519 is removed in RCAN v2.2 */
1915
+ declare function verifyPQSignature(msg: RCANMessage, trustedKeys: MLDSAKeyPair[], _requirePQ?: boolean): Promise<void>;
1941
1916
 
1942
1917
  /**
1943
1918
  * rcan-ts — Official TypeScript SDK for RCAN v1.6
@@ -1951,4 +1926,4 @@ declare const VERSION = "0.6.0";
1951
1926
  /** @deprecated Use SPEC_VERSION from ./version instead */
1952
1927
  declare const RCAN_VERSION = "1.6";
1953
1928
 
1954
- export { AUTHORITY_ERROR_CODES, type ApprovalStatus, AuditChain, AuditError, type AuditExportRequest, type AuthorityAccessPayload, type AuthorityAccessPayloadWire, type AuthorityDataCategory, type AuthorityResponseData, type AuthorityResponsePayload, COMPETITION_SCOPE_LEVEL, CONTRIBUTE_SCOPE_LEVEL, type CachedKey, type ChainVerifyResult, ClockDriftError, type ClockSyncStatus, CommitmentRecord, type CommitmentRecordData, type CommitmentRecordJSON, type CompetitionBadge, type CompetitionEnter, type CompetitionFormat, type CompetitionScore, type ComputeResource, ConfidenceGate, type ConsentRequestParams, type ConsentResponseParams, type ConsentType, type ContributeCancel, type ContributeRequest, type ContributeResult, DEFAULT_LOA_POLICY, DataCategory, type DelegationHop, FIRMWARE_MANIFEST_PATH, FaultCode, type FaultReportParams, type FaultSeverity, type FederationSyncPayload, FederationSyncType, type FirmwareComponent, FirmwareIntegrityError, type FirmwareManifest, type FirmwareManifestWire, GateError, HiTLGate, type IdentityRecord, type JWKEntry, type JWKSDocument, KeyStore, LevelOfAssurance, type ListResult, type LoaPolicy, M2MAuthError, type M2MPeerClaims, type M2MTrustedClaims, M2M_TRUSTED_ISSUER, MLDSAKeyPair, type MLDSAKeyPairData, type MLDSASignatureBlock, type MediaChunk, MediaEncoding, MessageType, NodeClient, type OfflineCommandResult, OfflineModeManager, type OfflineState, PRODUCTION_LOA_POLICY, type PendingApproval, type PersonalResearchResult, QoSAckTimeoutError, QoSLevel, QoSManager, type QoSResult, type QoSSendOptions, RCANAddressError, type RCANAgentConfig, type RCANConfig, RCANConfigAuthorizationError, RCANDelegationChainError, RCANError, RCANGateError, RCANMessage, type RCANMessageData, type RCANMessageEnvelope, RCANMessageError, type RCANMetadata, RCANNodeError, RCANNodeNotFoundError, RCANNodeSyncError, RCANNodeTrustError, RCANRegistryError, type RCANRegistryNode, RCANReplayAttackError, type RCANResolveResult, RCANSignatureError, RCANValidationError, RCANVersionIncompatibleError, RCAN_VERSION, ROLE_JWT_LEVEL, RRF_REVOCATION_CACHE_TTL_MS, RRF_REVOCATION_URL, type RegistrationResult, RegistryClient, type RegistryIdentity, RegistryTier, ReplayCache, type ReplayCheckResult, type ReplayableMessage, type ResearchMetrics, RevocationCache$1 as RevocationCache, type RevocationStatus, type RevocationStatusValue, type Robot, type RobotRegistration, RobotURI, RobotURIError, type RobotURIOptions, Role, type RunType, SAFETY_MESSAGE_TYPE, SCOPE_MIN_ROLE, SDK_VERSION, SPEC_VERSION, type SafetyEvent, type SafetyMessage, type ScopeValidationResult, type SeasonStanding, type SenderType, type SignatureBlock, type StandingEntry, type StreamChunk, type TrainingConsentRequestParams, type TransparencyMessage, TransportEncoding, TransportError, TrustAnchorCache, VERSION, type ValidationResult, type WorkUnitStatus, addDelegationHop, addMediaInline, addMediaRef, addPQSignature, assertClockSynced, authorityAccessFromWire, authorityAccessToWire, canonicalManifestJson, checkClockSync, checkRevocation, decodeBleFrames, decodeCompact, decodeMinimal, encodeBleFrames, encodeCompact, encodeMinimal, extractIdentityFromJwt, extractLoaFromJwt, extractRoleFromJwt, fetchCanonicalSchema, fetchRRFRevocations, isAuthorityRequestValid, isM2mTrustedRevoked, isPreemptedBy, isSafetyMessage, makeCloudRelayMessage, makeCompetitionEnter, makeCompetitionScore, makeConfigUpdate, makeConsentDeny, makeConsentGrant, makeConsentRequest, makeContributeCancel, makeContributeRequest, makeContributeResult, makeEstopMessage, makeEstopWithQoS, makeFaultReport, makeFederationSync, makeKeyRotationMessage, makePersonalResearchResult, makeResumeMessage, makeRevocationBroadcast, makeSeasonStanding, makeStopMessage, makeStreamChunk, makeTrainingConsentDeny, makeTrainingConsentGrant, makeTrainingConsentRequest, makeTrainingDataMessage, makeTransparencyMessage, manifestFromWire, manifestToWire, parseM2mPeerToken, parseM2mTrustedToken, roleFromJwtLevel, selectTransport, validateAuthorityAccess, validateCompetitionScope, validateConfig, validateConfigAgainstSchema, validateConfigUpdate, validateConsentMessage, validateContributeScope, validateCrossRegistryCommand, validateDelegationChain, validateLoaForScope, validateManifest, validateMediaChunks, validateMessage, validateNodeAgainstSchema, validateReplay, validateRoleForScope, validateSafetyMessage, validateTrainingDataMessage, validateURI, validateVersionCompat, verifyM2mTrustedToken, verifyM2mTrustedTokenClaims, verifyPQSignature };
1929
+ export { AUTHORITY_ERROR_CODES, type ApprovalStatus, AuditChain, AuditError, type AuditExportRequest, type AuthorityAccessPayload, type AuthorityAccessPayloadWire, type AuthorityDataCategory, type AuthorityResponseData, type AuthorityResponsePayload, COMPETITION_SCOPE_LEVEL, CONTRIBUTE_SCOPE_LEVEL, type CachedKey, type ChainVerifyResult, ClockDriftError, type ClockSyncStatus, CommitmentRecord, type CommitmentRecordData, type CommitmentRecordJSON, type CompetitionBadge, type CompetitionEnter, type CompetitionFormat, type CompetitionScore, type ComputeResource, ConfidenceGate, type ConsentRequestParams, type ConsentResponseParams, type ConsentType, type ContributeCancel, type ContributeRequest, type ContributeResult, DEFAULT_LOA_POLICY, DataCategory, type DelegationHop, FIRMWARE_MANIFEST_PATH, FaultCode, type FaultReportParams, type FaultSeverity, type FederationSyncPayload, FederationSyncType, type FirmwareComponent, FirmwareIntegrityError, type FirmwareManifest, type FirmwareManifestWire, GateError, HiTLGate, type IdentityRecord, type JWKEntry, type JWKSDocument, KeyStore, LevelOfAssurance, type ListResult, type LoaPolicy, M2MAuthError, type M2MPeerClaims, type M2MTrustedClaims, M2M_TRUSTED_ISSUER, MLDSAKeyPair, type MLDSAKeyPairData, type MediaChunk, MediaEncoding, MessageType, NodeClient, type OfflineCommandResult, OfflineModeManager, type OfflineState, PRODUCTION_LOA_POLICY, type PendingApproval, type PersonalResearchResult, QoSAckTimeoutError, QoSLevel, QoSManager, type QoSResult, type QoSSendOptions, RCANAddressError, type RCANAgentConfig, type RCANConfig, RCANConfigAuthorizationError, RCANDelegationChainError, RCANError, RCANGateError, RCANMessage, type RCANMessageData, type RCANMessageEnvelope, RCANMessageError, type RCANMetadata, RCANNodeError, RCANNodeNotFoundError, RCANNodeSyncError, RCANNodeTrustError, RCANRegistryError, type RCANRegistryNode, RCANReplayAttackError, type RCANResolveResult, RCANSignatureError, RCANValidationError, RCANVersionIncompatibleError, RCAN_VERSION, ROLE_JWT_LEVEL, RRF_REVOCATION_CACHE_TTL_MS, RRF_REVOCATION_URL, type RegistrationResult, RegistryClient, type RegistryIdentity, RegistryTier, ReplayCache, type ReplayCheckResult, type ReplayableMessage, type ResearchMetrics, RevocationCache$1 as RevocationCache, type RevocationStatus, type RevocationStatusValue, type Robot, type RobotRegistration, RobotURI, RobotURIError, type RobotURIOptions, Role, type RunType, SAFETY_MESSAGE_TYPE, SCOPE_MIN_ROLE, SDK_VERSION, SPEC_VERSION, type SafetyEvent, type SafetyMessage, type ScopeValidationResult, type SeasonStanding, type SenderType, type SignatureBlock, type StandingEntry, type StreamChunk, type TrainingConsentRequestParams, type TransparencyMessage, TransportEncoding, TransportError, TrustAnchorCache, VERSION, type ValidationResult, type WorkUnitStatus, addDelegationHop, addMediaInline, addMediaRef, addPQSignature, assertClockSynced, authorityAccessFromWire, authorityAccessToWire, canonicalManifestJson, checkClockSync, checkRevocation, decodeBleFrames, decodeCompact, decodeMinimal, encodeBleFrames, encodeCompact, encodeMinimal, extractIdentityFromJwt, extractLoaFromJwt, extractRoleFromJwt, fetchCanonicalSchema, fetchRRFRevocations, isAuthorityRequestValid, isM2mTrustedRevoked, isPreemptedBy, isSafetyMessage, makeCloudRelayMessage, makeCompetitionEnter, makeCompetitionScore, makeConfigUpdate, makeConsentDeny, makeConsentGrant, makeConsentRequest, makeContributeCancel, makeContributeRequest, makeContributeResult, makeEstopMessage, makeEstopWithQoS, makeFaultReport, makeFederationSync, makeKeyRotationMessage, makePersonalResearchResult, makeResumeMessage, makeRevocationBroadcast, makeSeasonStanding, makeStopMessage, makeStreamChunk, makeTrainingConsentDeny, makeTrainingConsentGrant, makeTrainingConsentRequest, makeTrainingDataMessage, makeTransparencyMessage, manifestFromWire, manifestToWire, parseM2mPeerToken, parseM2mTrustedToken, roleFromJwtLevel, selectTransport, signMessage, validateAuthorityAccess, validateCompetitionScope, validateConfig, validateConfigAgainstSchema, validateConfigUpdate, validateConsentMessage, validateContributeScope, validateCrossRegistryCommand, validateDelegationChain, validateLoaForScope, validateManifest, validateMediaChunks, validateMessage, validateNodeAgainstSchema, validateReplay, validateRoleForScope, validateSafetyMessage, validateTrainingDataMessage, validateURI, validateVersionCompat, verifyM2mTrustedToken, verifyM2mTrustedTokenClaims, verifyMessage, verifyPQSignature };
package/dist/browser.mjs CHANGED
@@ -100,7 +100,7 @@ var RobotURI = class _RobotURI {
100
100
 
101
101
  // src/version.ts
102
102
  var SPEC_VERSION = "2.2.0";
103
- var SDK_VERSION = "1.2.0";
103
+ var SDK_VERSION = "1.2.1";
104
104
  function validateVersionCompat(incomingVersion, localVersion = SPEC_VERSION) {
105
105
  const parseParts = (v) => {
106
106
  const parts = v.split(".");
@@ -279,7 +279,6 @@ var RCANMessage = class _RCANMessage {
279
279
  if (this.mediaChunks !== void 0) obj.mediaChunks = this.mediaChunks;
280
280
  if (this.firmwareHash !== void 0) obj.firmwareHash = this.firmwareHash;
281
281
  if (this.attestationRef !== void 0) obj.attestationRef = this.attestationRef;
282
- if (this.pqSig !== void 0) obj.pqSig = this.pqSig;
283
282
  return obj;
284
283
  }
285
284
  /** Serialize to JSON string */
@@ -3114,21 +3113,15 @@ function fromBase64url(b64) {
3114
3113
  return bytes;
3115
3114
  }
3116
3115
  async function sha256hex(data) {
3117
- const g = typeof globalThis !== "undefined" ? globalThis : {};
3118
- const webcrypto = g.crypto;
3119
- const subtle = webcrypto?.subtle;
3120
- if (subtle) {
3121
- const buf = await subtle.digest("SHA-256", data);
3116
+ if (typeof crypto !== "undefined" && crypto.subtle) {
3117
+ const buf = await crypto.subtle.digest("SHA-256", data.buffer);
3122
3118
  return Array.from(new Uint8Array(buf)).map((b) => b.toString(16).padStart(2, "0")).join("").slice(0, 8);
3123
3119
  }
3124
- const nodeCrypto = await import("crypto").catch(() => null);
3125
- if (nodeCrypto) {
3126
- return nodeCrypto.createHash("sha256").update(data).digest("hex").slice(0, 8);
3127
- }
3128
- throw new Error("No SHA-256 implementation available");
3120
+ const { createHash } = __require("crypto");
3121
+ return createHash("sha256").update(data).digest("hex").slice(0, 8);
3129
3122
  }
3130
3123
  var _mlDsaModule;
3131
- async function requireNoblePostQuantum() {
3124
+ async function requireMlDsa() {
3132
3125
  if (_mlDsaModule) return _mlDsaModule;
3133
3126
  if (typeof __require !== "undefined") {
3134
3127
  try {
@@ -3142,7 +3135,7 @@ async function requireNoblePostQuantum() {
3142
3135
  return _mlDsaModule;
3143
3136
  } catch {
3144
3137
  throw new Error(
3145
- "ML-DSA signing requires @noble/post-quantum. Install with: npm install @noble/post-quantum"
3138
+ "ML-DSA-65 signing requires @noble/post-quantum. Install with: npm install @noble/post-quantum"
3146
3139
  );
3147
3140
  }
3148
3141
  }
@@ -3155,23 +3148,16 @@ var MLDSAKeyPair = class _MLDSAKeyPair {
3155
3148
  this.publicKey = data.publicKey;
3156
3149
  this.secretKey = data.secretKey;
3157
3150
  }
3158
- /** Generate a new ML-DSA-65 key pair. */
3159
3151
  static async generate() {
3160
- const { ml_dsa65 } = await requireNoblePostQuantum();
3161
- const kp = ml_dsa65.keygen();
3152
+ const m = await requireMlDsa();
3153
+ const kp = m.ml_dsa65.keygen();
3162
3154
  const keyId = await sha256hex(kp.publicKey);
3163
- return new _MLDSAKeyPair({
3164
- publicKey: kp.publicKey,
3165
- secretKey: kp.secretKey,
3166
- keyId
3167
- });
3155
+ return new _MLDSAKeyPair({ publicKey: kp.publicKey, secretKey: kp.secretKey, keyId });
3168
3156
  }
3169
- /** Build a verify-only key pair from raw public key bytes. */
3170
3157
  static async fromPublicKey(publicKey) {
3171
3158
  const keyId = await sha256hex(publicKey);
3172
3159
  return new _MLDSAKeyPair({ publicKey, keyId });
3173
3160
  }
3174
- /** Build a full key pair from saved bytes (public + secret). */
3175
3161
  static async fromKeyMaterial(publicKey, secretKey) {
3176
3162
  const keyId = await sha256hex(publicKey);
3177
3163
  return new _MLDSAKeyPair({ publicKey, secretKey, keyId });
@@ -3179,82 +3165,68 @@ var MLDSAKeyPair = class _MLDSAKeyPair {
3179
3165
  get hasPrivateKey() {
3180
3166
  return this.secretKey !== void 0;
3181
3167
  }
3182
- /** Sign raw bytes; returns ML-DSA-65 signature (3309 bytes). */
3183
3168
  async signBytes(data) {
3184
- if (!this.secretKey) {
3185
- throw new Error("Cannot sign: MLDSAKeyPair has no private key (verify-only)");
3186
- }
3187
- const { ml_dsa65 } = await requireNoblePostQuantum();
3188
- return ml_dsa65.sign(data, this.secretKey);
3169
+ if (!this.secretKey) throw new Error("Cannot sign: MLDSAKeyPair has no private key (verify-only)");
3170
+ const m = await requireMlDsa();
3171
+ return m.ml_dsa65.sign(data, this.secretKey);
3189
3172
  }
3190
- /**
3191
- * Verify an ML-DSA-65 signature.
3192
- * @returns true if valid
3193
- * @throws {Error} on invalid signature
3194
- */
3195
3173
  async verifyBytes(data, signature) {
3196
- const { ml_dsa65 } = await requireNoblePostQuantum();
3197
- const ok = ml_dsa65.verify(signature, data, this.publicKey);
3198
- if (!ok) throw new Error("ML-DSA signature verification failed");
3174
+ const m = await requireMlDsa();
3175
+ const ok = m.ml_dsa65.verify(signature, data, this.publicKey);
3176
+ if (!ok) throw new Error("ML-DSA-65 signature verification failed");
3199
3177
  }
3200
3178
  toString() {
3201
- const mode = this.hasPrivateKey ? "private+public" : "public-only";
3202
- return `MLDSAKeyPair(keyId=${this.keyId}, alg=ML-DSA-65, ${mode})`;
3179
+ return `MLDSAKeyPair(keyId=${this.keyId}, alg=ML-DSA-65, ${this.hasPrivateKey ? "private+public" : "public-only"})`;
3203
3180
  }
3204
3181
  };
3205
3182
  function canonicalMessageBytes(msg) {
3206
- const m = msg;
3207
3183
  const payload = {
3208
3184
  rcan: msg.rcan,
3209
- msg_id: m["msgId"] ?? m["msg_id"] ?? "",
3185
+ msg_id: msg["msgId"] ?? "",
3210
3186
  timestamp: msg.timestamp,
3211
3187
  cmd: msg.cmd,
3212
3188
  target: msg.target,
3213
3189
  params: msg.params
3214
3190
  };
3215
- const sorted = JSON.stringify(
3216
- Object.fromEntries(Object.entries(payload).sort()),
3217
- null,
3218
- void 0
3191
+ return new TextEncoder().encode(
3192
+ JSON.stringify(Object.fromEntries(Object.entries(payload).sort()))
3219
3193
  );
3220
- return new TextEncoder().encode(sorted);
3221
3194
  }
3222
- async function addPQSignature(msg, keypair) {
3195
+ async function signMessage(msg, keypair) {
3223
3196
  const payload = canonicalMessageBytes(msg);
3224
3197
  const rawSig = await keypair.signBytes(payload);
3225
- const block = {
3198
+ msg["signature"] = {
3226
3199
  alg: "ml-dsa-65",
3227
3200
  kid: keypair.keyId,
3228
3201
  sig: toBase64url(rawSig)
3229
3202
  };
3230
- msg["pqSig"] = block;
3231
3203
  return msg;
3232
3204
  }
3233
- async function verifyPQSignature(msg, trustedKeys, requirePQ = false) {
3234
- const pqSig = msg["pqSig"];
3235
- if (!pqSig) {
3236
- if (requirePQ) {
3237
- throw new Error("ML-DSA signature (pqSig) required but missing from message");
3238
- }
3239
- return;
3240
- }
3241
- if (pqSig.alg !== "ml-dsa-65") {
3242
- throw new Error(`Unsupported PQ signature algorithm: ${pqSig.alg}`);
3205
+ async function verifyMessage(msg, trustedKeys) {
3206
+ const sig = msg.signature;
3207
+ if (!sig) throw new Error("Message is unsigned \u2014 signature field missing");
3208
+ if (sig.alg !== "ml-dsa-65") {
3209
+ throw new Error(
3210
+ `Unsupported signature algorithm: ${sig.alg}. RCAN v2.2 requires ml-dsa-65 (Ed25519 is deprecated).`
3211
+ );
3243
3212
  }
3244
- const matched = trustedKeys.find((k) => k.keyId === pqSig.kid);
3213
+ const matched = trustedKeys.find((k) => k.keyId === sig.kid);
3245
3214
  if (!matched) {
3246
3215
  throw new Error(
3247
- `No trusted ML-DSA key with kid=${pqSig.kid}. Known kids: [${trustedKeys.map((k) => k.keyId).join(", ")}]`
3216
+ `No trusted ML-DSA-65 key with kid=${sig.kid}. Known kids: [${trustedKeys.map((k) => k.keyId).join(", ")}]`
3248
3217
  );
3249
3218
  }
3250
3219
  let rawSig;
3251
3220
  try {
3252
- rawSig = fromBase64url(pqSig.sig);
3221
+ rawSig = fromBase64url(sig.sig);
3253
3222
  } catch (e) {
3254
- throw new Error(`Invalid base64url ML-DSA signature: ${e}`);
3223
+ throw new Error(`Invalid base64url sig: ${e}`);
3255
3224
  }
3256
- const payload = canonicalMessageBytes(msg);
3257
- await matched.verifyBytes(payload, rawSig);
3225
+ await matched.verifyBytes(canonicalMessageBytes(msg), rawSig);
3226
+ }
3227
+ var addPQSignature = signMessage;
3228
+ async function verifyPQSignature(msg, trustedKeys, _requirePQ = true) {
3229
+ return verifyMessage(msg, trustedKeys);
3258
3230
  }
3259
3231
 
3260
3232
  // src/index.ts
@@ -3382,6 +3354,7 @@ export {
3382
3354
  parseM2mTrustedToken,
3383
3355
  roleFromJwtLevel,
3384
3356
  selectTransport,
3357
+ signMessage,
3385
3358
  validateAuthorityAccess,
3386
3359
  validateCompetitionScope,
3387
3360
  validateConfig,
@@ -3404,6 +3377,7 @@ export {
3404
3377
  validateVersionCompat,
3405
3378
  verifyM2mTrustedToken,
3406
3379
  verifyM2mTrustedTokenClaims,
3380
+ verifyMessage,
3407
3381
  verifyPQSignature
3408
3382
  };
3409
3383
  //# sourceMappingURL=browser.mjs.map