@continuonai/rcan-ts 1.2.0 → 1.2.2
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.
- package/dist/browser.d.mts +20 -45
- package/dist/browser.mjs +41 -67
- package/dist/browser.mjs.map +1 -1
- package/dist/index.d.mts +20 -45
- package/dist/index.d.ts +20 -45
- package/dist/index.js +43 -67
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +41 -67
- package/dist/index.mjs.map +1 -1
- package/dist/rcan-validate.js +1 -2
- package/dist/rcan.iife.js +2 -2
- package/package.json +5 -2
package/dist/index.mjs
CHANGED
|
@@ -96,8 +96,8 @@ var RobotURI = class _RobotURI {
|
|
|
96
96
|
};
|
|
97
97
|
|
|
98
98
|
// src/version.ts
|
|
99
|
-
var SPEC_VERSION = "2.2.
|
|
100
|
-
var SDK_VERSION = "1.2.
|
|
99
|
+
var SPEC_VERSION = "2.2.1";
|
|
100
|
+
var SDK_VERSION = "1.2.2";
|
|
101
101
|
function validateVersionCompat(incomingVersion, localVersion = SPEC_VERSION) {
|
|
102
102
|
const parseParts = (v) => {
|
|
103
103
|
const parts = v.split(".");
|
|
@@ -276,7 +276,6 @@ var RCANMessage = class _RCANMessage {
|
|
|
276
276
|
if (this.mediaChunks !== void 0) obj.mediaChunks = this.mediaChunks;
|
|
277
277
|
if (this.firmwareHash !== void 0) obj.firmwareHash = this.firmwareHash;
|
|
278
278
|
if (this.attestationRef !== void 0) obj.attestationRef = this.attestationRef;
|
|
279
|
-
if (this.pqSig !== void 0) obj.pqSig = this.pqSig;
|
|
280
279
|
return obj;
|
|
281
280
|
}
|
|
282
281
|
/** Serialize to JSON string */
|
|
@@ -3103,21 +3102,15 @@ function fromBase64url(b64) {
|
|
|
3103
3102
|
return bytes;
|
|
3104
3103
|
}
|
|
3105
3104
|
async function sha256hex(data) {
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
const subtle = webcrypto?.subtle;
|
|
3109
|
-
if (subtle) {
|
|
3110
|
-
const buf = await subtle.digest("SHA-256", data);
|
|
3105
|
+
if (typeof crypto !== "undefined" && crypto.subtle) {
|
|
3106
|
+
const buf = await crypto.subtle.digest("SHA-256", data.buffer);
|
|
3111
3107
|
return Array.from(new Uint8Array(buf)).map((b) => b.toString(16).padStart(2, "0")).join("").slice(0, 8);
|
|
3112
3108
|
}
|
|
3113
|
-
const
|
|
3114
|
-
|
|
3115
|
-
return nodeCrypto.createHash("sha256").update(data).digest("hex").slice(0, 8);
|
|
3116
|
-
}
|
|
3117
|
-
throw new Error("No SHA-256 implementation available");
|
|
3109
|
+
const { createHash } = __require("crypto");
|
|
3110
|
+
return createHash("sha256").update(data).digest("hex").slice(0, 8);
|
|
3118
3111
|
}
|
|
3119
3112
|
var _mlDsaModule;
|
|
3120
|
-
async function
|
|
3113
|
+
async function requireMlDsa() {
|
|
3121
3114
|
if (_mlDsaModule) return _mlDsaModule;
|
|
3122
3115
|
if (typeof __require !== "undefined") {
|
|
3123
3116
|
try {
|
|
@@ -3131,7 +3124,7 @@ async function requireNoblePostQuantum() {
|
|
|
3131
3124
|
return _mlDsaModule;
|
|
3132
3125
|
} catch {
|
|
3133
3126
|
throw new Error(
|
|
3134
|
-
"ML-DSA signing requires @noble/post-quantum. Install with: npm install @noble/post-quantum"
|
|
3127
|
+
"ML-DSA-65 signing requires @noble/post-quantum. Install with: npm install @noble/post-quantum"
|
|
3135
3128
|
);
|
|
3136
3129
|
}
|
|
3137
3130
|
}
|
|
@@ -3144,23 +3137,16 @@ var MLDSAKeyPair = class _MLDSAKeyPair {
|
|
|
3144
3137
|
this.publicKey = data.publicKey;
|
|
3145
3138
|
this.secretKey = data.secretKey;
|
|
3146
3139
|
}
|
|
3147
|
-
/** Generate a new ML-DSA-65 key pair. */
|
|
3148
3140
|
static async generate() {
|
|
3149
|
-
const
|
|
3150
|
-
const kp = ml_dsa65.keygen();
|
|
3141
|
+
const m = await requireMlDsa();
|
|
3142
|
+
const kp = m.ml_dsa65.keygen();
|
|
3151
3143
|
const keyId = await sha256hex(kp.publicKey);
|
|
3152
|
-
return new _MLDSAKeyPair({
|
|
3153
|
-
publicKey: kp.publicKey,
|
|
3154
|
-
secretKey: kp.secretKey,
|
|
3155
|
-
keyId
|
|
3156
|
-
});
|
|
3144
|
+
return new _MLDSAKeyPair({ publicKey: kp.publicKey, secretKey: kp.secretKey, keyId });
|
|
3157
3145
|
}
|
|
3158
|
-
/** Build a verify-only key pair from raw public key bytes. */
|
|
3159
3146
|
static async fromPublicKey(publicKey) {
|
|
3160
3147
|
const keyId = await sha256hex(publicKey);
|
|
3161
3148
|
return new _MLDSAKeyPair({ publicKey, keyId });
|
|
3162
3149
|
}
|
|
3163
|
-
/** Build a full key pair from saved bytes (public + secret). */
|
|
3164
3150
|
static async fromKeyMaterial(publicKey, secretKey) {
|
|
3165
3151
|
const keyId = await sha256hex(publicKey);
|
|
3166
3152
|
return new _MLDSAKeyPair({ publicKey, secretKey, keyId });
|
|
@@ -3168,82 +3154,68 @@ var MLDSAKeyPair = class _MLDSAKeyPair {
|
|
|
3168
3154
|
get hasPrivateKey() {
|
|
3169
3155
|
return this.secretKey !== void 0;
|
|
3170
3156
|
}
|
|
3171
|
-
/** Sign raw bytes; returns ML-DSA-65 signature (3309 bytes). */
|
|
3172
3157
|
async signBytes(data) {
|
|
3173
|
-
if (!this.secretKey)
|
|
3174
|
-
|
|
3175
|
-
|
|
3176
|
-
const { ml_dsa65 } = await requireNoblePostQuantum();
|
|
3177
|
-
return ml_dsa65.sign(data, this.secretKey);
|
|
3158
|
+
if (!this.secretKey) throw new Error("Cannot sign: MLDSAKeyPair has no private key (verify-only)");
|
|
3159
|
+
const m = await requireMlDsa();
|
|
3160
|
+
return m.ml_dsa65.sign(data, this.secretKey);
|
|
3178
3161
|
}
|
|
3179
|
-
/**
|
|
3180
|
-
* Verify an ML-DSA-65 signature.
|
|
3181
|
-
* @returns true if valid
|
|
3182
|
-
* @throws {Error} on invalid signature
|
|
3183
|
-
*/
|
|
3184
3162
|
async verifyBytes(data, signature) {
|
|
3185
|
-
const
|
|
3186
|
-
const ok = ml_dsa65.verify(signature, data, this.publicKey);
|
|
3187
|
-
if (!ok) throw new Error("ML-DSA signature verification failed");
|
|
3163
|
+
const m = await requireMlDsa();
|
|
3164
|
+
const ok = m.ml_dsa65.verify(signature, data, this.publicKey);
|
|
3165
|
+
if (!ok) throw new Error("ML-DSA-65 signature verification failed");
|
|
3188
3166
|
}
|
|
3189
3167
|
toString() {
|
|
3190
|
-
|
|
3191
|
-
return `MLDSAKeyPair(keyId=${this.keyId}, alg=ML-DSA-65, ${mode})`;
|
|
3168
|
+
return `MLDSAKeyPair(keyId=${this.keyId}, alg=ML-DSA-65, ${this.hasPrivateKey ? "private+public" : "public-only"})`;
|
|
3192
3169
|
}
|
|
3193
3170
|
};
|
|
3194
3171
|
function canonicalMessageBytes(msg) {
|
|
3195
|
-
const m = msg;
|
|
3196
3172
|
const payload = {
|
|
3197
3173
|
rcan: msg.rcan,
|
|
3198
|
-
msg_id:
|
|
3174
|
+
msg_id: msg["msgId"] ?? "",
|
|
3199
3175
|
timestamp: msg.timestamp,
|
|
3200
3176
|
cmd: msg.cmd,
|
|
3201
3177
|
target: msg.target,
|
|
3202
3178
|
params: msg.params
|
|
3203
3179
|
};
|
|
3204
|
-
|
|
3205
|
-
Object.fromEntries(Object.entries(payload).sort())
|
|
3206
|
-
null,
|
|
3207
|
-
void 0
|
|
3180
|
+
return new TextEncoder().encode(
|
|
3181
|
+
JSON.stringify(Object.fromEntries(Object.entries(payload).sort()))
|
|
3208
3182
|
);
|
|
3209
|
-
return new TextEncoder().encode(sorted);
|
|
3210
3183
|
}
|
|
3211
|
-
async function
|
|
3184
|
+
async function signMessage(msg, keypair) {
|
|
3212
3185
|
const payload = canonicalMessageBytes(msg);
|
|
3213
3186
|
const rawSig = await keypair.signBytes(payload);
|
|
3214
|
-
|
|
3187
|
+
msg["signature"] = {
|
|
3215
3188
|
alg: "ml-dsa-65",
|
|
3216
3189
|
kid: keypair.keyId,
|
|
3217
3190
|
sig: toBase64url(rawSig)
|
|
3218
3191
|
};
|
|
3219
|
-
msg["pqSig"] = block;
|
|
3220
3192
|
return msg;
|
|
3221
3193
|
}
|
|
3222
|
-
async function
|
|
3223
|
-
const
|
|
3224
|
-
if (!
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
}
|
|
3230
|
-
if (pqSig.alg !== "ml-dsa-65") {
|
|
3231
|
-
throw new Error(`Unsupported PQ signature algorithm: ${pqSig.alg}`);
|
|
3194
|
+
async function verifyMessage(msg, trustedKeys) {
|
|
3195
|
+
const sig = msg.signature;
|
|
3196
|
+
if (!sig) throw new Error("Message is unsigned \u2014 signature field missing");
|
|
3197
|
+
if (sig.alg !== "ml-dsa-65") {
|
|
3198
|
+
throw new Error(
|
|
3199
|
+
`Unsupported signature algorithm: ${sig.alg}. RCAN v2.2 requires ml-dsa-65 (Ed25519 is deprecated).`
|
|
3200
|
+
);
|
|
3232
3201
|
}
|
|
3233
|
-
const matched = trustedKeys.find((k) => k.keyId ===
|
|
3202
|
+
const matched = trustedKeys.find((k) => k.keyId === sig.kid);
|
|
3234
3203
|
if (!matched) {
|
|
3235
3204
|
throw new Error(
|
|
3236
|
-
`No trusted ML-DSA key with kid=${
|
|
3205
|
+
`No trusted ML-DSA-65 key with kid=${sig.kid}. Known kids: [${trustedKeys.map((k) => k.keyId).join(", ")}]`
|
|
3237
3206
|
);
|
|
3238
3207
|
}
|
|
3239
3208
|
let rawSig;
|
|
3240
3209
|
try {
|
|
3241
|
-
rawSig = fromBase64url(
|
|
3210
|
+
rawSig = fromBase64url(sig.sig);
|
|
3242
3211
|
} catch (e) {
|
|
3243
|
-
throw new Error(`Invalid base64url
|
|
3212
|
+
throw new Error(`Invalid base64url sig: ${e}`);
|
|
3244
3213
|
}
|
|
3245
|
-
|
|
3246
|
-
|
|
3214
|
+
await matched.verifyBytes(canonicalMessageBytes(msg), rawSig);
|
|
3215
|
+
}
|
|
3216
|
+
var addPQSignature = signMessage;
|
|
3217
|
+
async function verifyPQSignature(msg, trustedKeys, _requirePQ = true) {
|
|
3218
|
+
return verifyMessage(msg, trustedKeys);
|
|
3247
3219
|
}
|
|
3248
3220
|
|
|
3249
3221
|
// src/index.ts
|
|
@@ -3371,6 +3343,7 @@ export {
|
|
|
3371
3343
|
parseM2mTrustedToken,
|
|
3372
3344
|
roleFromJwtLevel,
|
|
3373
3345
|
selectTransport,
|
|
3346
|
+
signMessage,
|
|
3374
3347
|
validateAuthorityAccess,
|
|
3375
3348
|
validateCompetitionScope,
|
|
3376
3349
|
validateConfig,
|
|
@@ -3393,6 +3366,7 @@ export {
|
|
|
3393
3366
|
validateVersionCompat,
|
|
3394
3367
|
verifyM2mTrustedToken,
|
|
3395
3368
|
verifyM2mTrustedTokenClaims,
|
|
3369
|
+
verifyMessage,
|
|
3396
3370
|
verifyPQSignature
|
|
3397
3371
|
};
|
|
3398
3372
|
//# sourceMappingURL=index.mjs.map
|