@adapt-toolkit/mufl 0.10.7 → 0.10.9

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.
@@ -201,6 +201,40 @@ library address_document loads libraries address_document_types, key_storage, ke
201
201
  RETURN ($version->1, $identity->my_identity, $authorizations->signatures).
202
202
  }
203
203
 
204
+ // The address document for the TRANSPORT identity-proof (node_description) —
205
+ // the self-attestation attached to EVERY outgoing external transaction. The
206
+ // identity is the full v2 record (the $e2e_bundle stays: receivers read the
207
+ // sender's e2e identity key off it for the inbound E2E decode hook), but
208
+ // $authorizations carries TWO self-signatures per SIGN key: one over
209
+ // _value_id(v2 identity) and one over _value_id(v1 bundle-less identity).
210
+ // A pre-e2e_bundle validator (SDK <= 0.9.x schema, e.g. the deployed
211
+ // ours-mcp 0.10.1) re-hashes the identity WITHOUT $e2e_bundle and finds the
212
+ // v1-hash signature in its any-match loop; a current validator finds the
213
+ // v2-hash signature. NOT memoised and used ONLY by node_description::create,
214
+ // so the main address document (invites, delegation certs, readvertise
215
+ // payloads — and every cert ad-hash binding) stays strict v2-sig-only.
216
+ produce_transport_address_document = fn (_) -> t_address_document
217
+ {
218
+ base_identity = key_storage::get_my_identity().
219
+ my_identity = ( $key_list -> (base_identity $key_list),
220
+ $default_keys -> (base_identity $default_keys),
221
+ $container_id -> (base_identity $container_id),
222
+ $e2e_bundle -> e2e::my_public_bundle() ).
223
+ v1_identity = ( $key_list -> (base_identity $key_list),
224
+ $default_keys -> (base_identity $default_keys),
225
+ $container_id -> (base_identity $container_id) ).
226
+ signatures IS crypto_signature(,) = (,).
227
+ hash_v2 = _value_id my_identity.
228
+ hash_v1 = _value_id v1_identity.
229
+
230
+ sc my_identity $key_list -- (key -> ) ?? key_get_function key == $SIGN {
231
+ signatures (key_storage::sign hash_v2 (_crypto_get_key_id key)) -> TRUE.
232
+ signatures (key_storage::sign hash_v1 (_crypto_get_key_id key)) -> TRUE.
233
+ }
234
+
235
+ RETURN ($version->m_current_version, $identity->my_identity, $authorizations->signatures).
236
+ }
237
+
204
238
  // The address document to hand a peer, down-levelled when the peer is a v1
205
239
  // (pre-E2E) node. peer_is_v1 NIL/FALSE => the current (v2) document (memoised as
206
240
  // usual); TRUE => a freshly built v1 document (not memoised). This is the single
@@ -2,7 +2,9 @@ library node_description loads libraries address_document, container_associated_
2
2
  metadef t_node_description: identity_proof_document_types::t_node_description.
3
3
 
4
4
  create = fn (_) -> t_node_description {
5
- ad = address_document::get_my_address_document().
5
+ // Transport self-attestation: dual-signed (v2-hash + v1-hash) so both
6
+ // current and pre-e2e_bundle-schema peers can verify the self-signature.
7
+ ad = address_document::produce_transport_address_document().
6
8
  associated_data = container_associated_data::create().
7
9
  return ($address_document -> ad, $container_associated_data -> associated_data).
8
10
  }
@@ -394,6 +394,25 @@ loads libraries key_storage, address_document_types
394
394
  return session_id (s as bin).
395
395
  }
396
396
 
397
+ // Decrypt an olm_type=0 (pre-key) message ON the EXISTING session it matches — the
398
+ // born-DR pre-key phase, where the sender keeps prefixing the pre-key until the peer
399
+ // replies, so genuine 2nd+ messages are pre-keys on the live session. Decrypts on the
400
+ // given session (advancing its ratchet); a TRUE replay (already-consumed index)
401
+ // provisional-aborts in the crate -> $ok FALSE. DISTINCT from `decrypt` olm_type=0,
402
+ // which ESTABLISHES a fresh inbound session (first contact / self-heal) — this one
403
+ // never re-establishes, so it cannot double-deliver or reset the ratchet.
404
+ fn decrypt_prekey_on_existing (session: bin, ciphertext: bin) -> e2e_result
405
+ {
406
+ WHEN provisional abort err
407
+ {
408
+ r = _e2e_decrypt session 0 ciphertext (pickle_key()).
409
+ return ( $ok -> TRUE, $plaintext -> r $plaintext, $session -> r $session,
410
+ $account -> NIL, $error -> NIL ).
411
+ }
412
+ return ( $ok -> FALSE, $plaintext -> NIL, $session -> NIL, $account -> NIL,
413
+ $error -> (map_error err) ).
414
+ }
415
+
397
416
  // RECEIVE: decrypt an inbound envelope for a known sender and COMMIT the advanced session
398
417
  // (and, on the establish path only, the rotated account). Returns the typed e2e_result.
399
418
  // The caller (the __t_wrapper decode hook) authenticates $emsignature FIRST and supplies
@@ -409,9 +428,20 @@ loads libraries key_storage, address_document_types
409
428
  // list), so a replayed olm_type=0 pre-key would re-establish successfully — re-running
410
429
  // the inner tx AND overwriting the (possibly advanced) session. session_matches is a
411
430
  // pure read; a genuine NEW handshake (different ephemeral) does not match.
412
- // (a) matches the LIVE session idempotent re-delivery, drop recoverably.
431
+ // (a) matches the LIVE session. This is the born-DR pre-key phase: until the
432
+ // peer REPLIES, the sender keeps prefixing the pre-key preamble, so the
433
+ // 2nd, 3rd, ... GENUINE message is an olm_type=0 pre-key on the SAME live
434
+ // session (session_matches TRUE). DECRYPT it on the existing session
435
+ // (advancing the ratchet) instead of dropping it as a replay. A TRUE replay
436
+ // (an already-consumed message index) fails the crate decrypt — vodozemac
437
+ // tracks the index — surfacing $ok=FALSE, which we drop recoverably as
438
+ // "replayed", so genuine replays still cannot double-deliver.
413
439
  if existing != NIL && (session_matches (existing as bin) ciphertext)
414
- { return replayed(). }
440
+ {
441
+ rd = decrypt_prekey_on_existing (existing as bin) ciphertext.
442
+ if rd $ok { m_sessions from_cid -> (rd $session) as bin. return rd. }
443
+ return replayed().
444
+ }
415
445
  // (b) matches the STAGED session — a redelivered migration COMMIT after we already
416
446
  // staged it (the lost-confirm case, spec §5.5). The wrapper turns this typed
417
447
  // error into a re-confirm via core mig_handle_replayed_commit; NO inner
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adapt-toolkit/mufl",
3
- "version": "0.10.7",
3
+ "version": "0.10.9",
4
4
  "type": "module",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "description": "The MUFL toolchain: prebuilt `mufl` (REPL) and `mufl-compile` (compiler) engine binaries plus the MUFL stdlib, meta, and transactions trees they need at run time. Linux x64 only; the binaries are proprietary (AEFL) — see LICENSE and NOTICE.",