@adapt-toolkit/mufl 0.10.2 → 0.10.4
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/meta/meta.mm +14 -2
- package/mufl_stdlib/config.mufl +2 -0
- package/mufl_stdlib/cryptography/address_document.mm +17 -8
- package/mufl_stdlib/cryptography/address_document_types.mm +30 -5
- package/mufl_stdlib/cryptography/e2e.mm +304 -0
- package/mufl_stdlib/cryptography/key_storage.mm +9 -1
- package/package.json +1 -1
- package/prebuilds/linux-x64/mufl +0 -0
- package/prebuilds/linux-x64/mufl-compile +0 -0
- package/transactions/__t_wrapper.mm +79 -16
- package/transactions/config.mufl +2 -1
package/meta/meta.mm
CHANGED
|
@@ -2496,8 +2496,20 @@ library meta {
|
|
|
2496
2496
|
names_root "::_crypto_verify_hash_signature" -> function [hash_code,crypto_signature,publickey_sign] truenil.
|
|
2497
2497
|
names_root "::_crypto_encrypt_message" -> function [secretkey_encrypt,publickey_encrypt,bin] crypto_message.
|
|
2498
2498
|
names_root "::_crypto_decrypt_message" -> function [secretkey_encrypt,publickey_encrypt,crypto_message] bin.
|
|
2499
|
-
names_root "::_crypto_get_key_id" -> function [ crypto ] bin.
|
|
2500
|
-
names_root "::_crypto_get_key_id_target" -> function [ crypto_message ] bin.
|
|
2499
|
+
names_root "::_crypto_get_key_id" -> function [ crypto ] bin.
|
|
2500
|
+
names_root "::_crypto_get_key_id_target" -> function [ crypto_message ] bin.
|
|
2501
|
+
// adapt-e2e-core (Olm / X3DH + Double-Ratchet) engine primitives (domain_e2e).
|
|
2502
|
+
// All pickle-in/pickle-out; the trailing bin is the 32-byte pickle_key.
|
|
2503
|
+
names_root "::_e2e_account_create" -> function [bin] bin.
|
|
2504
|
+
names_root "::_e2e_account_gen_otks" -> function [bin, int, bin] bin.
|
|
2505
|
+
names_root "::_e2e_account_gen_fallback" -> function [bin, bin] bin.
|
|
2506
|
+
names_root "::_e2e_account_bundle" -> function [bin, bin] bin.
|
|
2507
|
+
names_root "::_e2e_session_outbound" -> function [bin, bin, bin, bin] (record::type ($session->bin, $account->bin)).
|
|
2508
|
+
names_root "::_e2e_session_inbound" -> function [bin, bin, bin, bin] (record::type ($session->bin, $account->bin, $plaintext->bin)).
|
|
2509
|
+
names_root "::_e2e_encrypt" -> function [bin, bin, bin] (record::type ($msg->bin, $msg_type->int, $session->bin)).
|
|
2510
|
+
names_root "::_e2e_decrypt" -> function [bin, int, bin, bin] (record::type ($plaintext->bin, $session->bin)).
|
|
2511
|
+
names_root "::_e2e_session_id" -> function [bin, bin] bin.
|
|
2512
|
+
names_root "::_e2e_matches_inbound" -> function [bin, bin, bin] int.
|
|
2501
2513
|
names_root "::_construct_blinded_dictionary" -> functor::make_any_x_ret_blinded_0 (immutable::set (immutable::array basics::any) ).
|
|
2502
2514
|
names_root "::_construct_blinded_dictionary_custom" -> functor::make_any_x_y_ret_blinded_0 (immutable::set (immutable::array basics::any) ) (immutable::type str any).
|
|
2503
2515
|
names_root "::_construct_blinded_with_absence" -> functor::make_any_x_y_ret_blinded_0 (immutable::set (immutable::array basics::any) ) (immutable::set (immutable::array basics::any) ).
|
package/mufl_stdlib/config.mufl
CHANGED
|
@@ -19,6 +19,7 @@ config script {
|
|
|
19
19
|
$key_storage_api->#"cryptography/key_storage_api.mm",
|
|
20
20
|
$key_storage->#"cryptography/key_storage.mm",
|
|
21
21
|
$key_utils->#"cryptography/key_utils.mm",
|
|
22
|
+
$e2e->#"cryptography/e2e.mm",
|
|
22
23
|
$registration_proof->#"cryptography/registration_proof.mm",
|
|
23
24
|
$attestation_document->#"cryptography/attestation/attestation_document.mm",
|
|
24
25
|
$container_associated_data->#"cryptography/attestation/container_associated_data.mm",
|
|
@@ -63,6 +64,7 @@ config script {
|
|
|
63
64
|
$key_storage_api->#"cryptography/key_storage_api.mm",
|
|
64
65
|
$key_storage->#"cryptography/key_storage.mm",
|
|
65
66
|
$key_utils->#"cryptography/key_utils.mm",
|
|
67
|
+
$e2e->#"cryptography/e2e.mm",
|
|
66
68
|
$registration_proof->#"cryptography/registration_proof.mm",
|
|
67
69
|
$attestation_document->#"cryptography/attestation/attestation_document.mm",
|
|
68
70
|
$container_associated_data->#"cryptography/attestation/container_associated_data.mm",
|
|
@@ -23,7 +23,7 @@ and address document is a record of keys that are attested to belong to a certai
|
|
|
23
23
|
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
|
-
library address_document loads libraries address_document_types, key_storage, key_utils, transaction_queue, current_transaction_info uses transactions
|
|
26
|
+
library address_document loads libraries address_document_types, key_storage, key_utils, transaction_queue, current_transaction_info, e2e uses transactions
|
|
27
27
|
{
|
|
28
28
|
using key_utils.
|
|
29
29
|
using address_document_types.
|
|
@@ -43,11 +43,11 @@ library address_document loads libraries address_document_types, key_storage, ke
|
|
|
43
43
|
*/
|
|
44
44
|
|
|
45
45
|
hidden {
|
|
46
|
-
m_current_version IS int =
|
|
47
|
-
m_authorizing_containers IS t_container_id(,) = (,).
|
|
46
|
+
m_current_version IS int = 2. // AD-v2: identity embeds the E2E prekey bundle ($e2e_bundle)
|
|
47
|
+
m_authorizing_containers IS t_container_id(,) = (,).
|
|
48
48
|
m_ad_signatures IS (t_container_id ->> crypto_signature) = (,).
|
|
49
|
-
m_max_allowed_number_of_authorizations = 10.
|
|
50
|
-
m_valid_versions = (1,).
|
|
49
|
+
m_max_allowed_number_of_authorizations = 10.
|
|
50
|
+
m_valid_versions = (1, 2). // accept v1 (no bundle) and v2 (bundle) peers — dual-support
|
|
51
51
|
m_my_address_document IS t_address_document+ = NIL.
|
|
52
52
|
}
|
|
53
53
|
|
|
@@ -146,9 +146,18 @@ library address_document loads libraries address_document_types, key_storage, ke
|
|
|
146
146
|
|
|
147
147
|
produce_my_address_document = fn (_) -> t_address_document
|
|
148
148
|
{
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
149
|
+
base_identity = key_storage::get_my_identity().
|
|
150
|
+
// AD-v2: embed our E2E prekey bundle into the identity so peers can open outbound
|
|
151
|
+
// sessions to us and the far-side decode hook can read our identity key. Rebuild the
|
|
152
|
+
// record explicitly (no in-place mutation) and compute the hash AFTER embedding, so
|
|
153
|
+
// the self-signature — and every authorizing-container signature over _value_id
|
|
154
|
+
// identity — commits to the bundle (belt-and-suspenders with each per-key $sig).
|
|
155
|
+
my_identity = ( $key_list -> (base_identity $key_list),
|
|
156
|
+
$default_keys -> (base_identity $default_keys),
|
|
157
|
+
$container_id -> (base_identity $container_id),
|
|
158
|
+
$e2e_bundle -> e2e::my_public_bundle() ).
|
|
159
|
+
signatures IS crypto_signature(,) = (,).
|
|
160
|
+
hash = _value_id my_identity.
|
|
152
161
|
|
|
153
162
|
sc my_identity $key_list -- (key -> ) ?? key_get_function key == $SIGN {
|
|
154
163
|
sig = key_storage::sign hash (_crypto_get_key_id key).
|
|
@@ -1,16 +1,41 @@
|
|
|
1
1
|
library address_document_types loads library key_utils
|
|
2
2
|
{
|
|
3
3
|
using key_utils.
|
|
4
|
-
|
|
5
|
-
(
|
|
4
|
+
|
|
5
|
+
// AD-v2 E2E prekey bundle (double-ratchet / Olm handshake material), published inside
|
|
6
|
+
// the container identity so a peer can open an outbound session to us and so the
|
|
7
|
+
// decode-level receive hook can read the sender's identity key. adapt OWNS this type;
|
|
8
|
+
// the ours-mufl-core companion registers it in a2a_versions (class-B versioned, $pv
|
|
9
|
+
// discriminator + try_narrow). Each prekey carries a host Ed25519 signature over the
|
|
10
|
+
// key bytes — belt-and-suspenders alongside the AD root signature that already binds
|
|
11
|
+
// the whole identity (including this bundle).
|
|
12
|
+
//
|
|
13
|
+
// v1 posture (empty one-time keys): the underlying engine (vodozemac) exposes an
|
|
14
|
+
// identity keypair + a reusable *fallback* key + one-time keys — there is NO distinct
|
|
15
|
+
// signed prekey. So v1 publishes $fallback (the establishment prekey) with an EMPTY
|
|
16
|
+
// $one_time_keys list, and leaves $signed_prekey NIL. The field is kept optional for
|
|
17
|
+
// forward-compat (a real rotated SPK can be added later with no re-shape).
|
|
18
|
+
metadef t_e2e_prekey: ( $id -> int, $key -> bin, $sig -> crypto_signature ).
|
|
19
|
+
metadef t_e2e_bundle:
|
|
20
|
+
(
|
|
21
|
+
$ik_curve -> bin, // Curve25519 identity key (the sender_ik peers establish against)
|
|
22
|
+
$ik_ed -> bin, // Ed25519 identity key
|
|
23
|
+
$signed_prekey -> t_e2e_prekey+, // NIL under empty-OTK v1 (no distinct SPK in the engine)
|
|
24
|
+
$one_time_keys -> t_e2e_prekey[], // EMPTY [] in v1 (one-time-key reuse impossible)
|
|
25
|
+
$fallback -> t_e2e_prekey // reusable establishment prekey, host-signed
|
|
26
|
+
).
|
|
27
|
+
|
|
28
|
+
metadef t_address_document:
|
|
29
|
+
(
|
|
6
30
|
$version->int, // version of the address document
|
|
7
31
|
$identity -> t_container_identity: // specifies the identity of the container
|
|
8
32
|
(
|
|
9
33
|
$key_list->t_publickey(,), // list of keys active with this container
|
|
10
34
|
$default_keys->t_function->>t_key_id, // exactly two keys, one for signing, one for encryption, that are the default keys for this container
|
|
11
|
-
$container_id->t_container_id // id of the container
|
|
12
|
-
|
|
13
|
-
|
|
35
|
+
$container_id->t_container_id, // id of the container
|
|
36
|
+
$e2e_bundle->t_e2e_bundle+ // AD-v2: E2E double-ratchet prekey bundle (NIL for v1 identities)
|
|
37
|
+
/* $code_id->hash_code */
|
|
38
|
+
),
|
|
14
39
|
$authorizations -> crypto_signature(,)
|
|
15
40
|
).
|
|
16
41
|
}
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
/*
|
|
2
|
+
** e2e — the end-to-end (Double-Ratchet / Olm) encryption library.
|
|
3
|
+
**
|
|
4
|
+
** Wraps the domain_e2e engine primitives (_e2e_*, over the adapt-e2e-core C-ABI)
|
|
5
|
+
** behind ergonomic functions that return a CLEAN VALUE or a TYPED ERROR — a caller
|
|
6
|
+
** never sees a raw crate return code. The recoverable failures (a tampered or
|
|
7
|
+
** wrong-session ciphertext, an exhausted one-time key, a corrupt inbound pickle)
|
|
8
|
+
** arrive from the primitives as PROVISIONAL aborts carrying a stable "e2e:*" token;
|
|
9
|
+
** we catch them here and fold them into an `e2e_result`. Fatal faults (a
|
|
10
|
+
** length/version/seed contract breach, a crate-internal error or panic) stay
|
|
11
|
+
** provisional-uncatchable and abort the transaction — they are never returned as data.
|
|
12
|
+
**
|
|
13
|
+
** This module is the STATELESS transform core (pickle-in / pickle-out): every fn
|
|
14
|
+
** takes the opaque state blob(s) and returns the new one(s). The adapt-owned,
|
|
15
|
+
** packet-state SEND/RECEIVE facade (single Olm-session owner; encrypt_to / the
|
|
16
|
+
** __t_wrapper decode-receive) is built on top of these transforms.
|
|
17
|
+
**
|
|
18
|
+
** pickle_key is derived INTERNALLY from this packet's root SIGN secret — never a
|
|
19
|
+
** caller concern, never persisted, never exported.
|
|
20
|
+
*/
|
|
21
|
+
library e2e
|
|
22
|
+
loads libraries key_storage, address_document_types
|
|
23
|
+
{
|
|
24
|
+
using key_storage.
|
|
25
|
+
using address_document_types.
|
|
26
|
+
|
|
27
|
+
// A typed, caller-facing error. $code is one of the stable strings below;
|
|
28
|
+
// $detail carries the underlying engine text for logs (never parsed by callers).
|
|
29
|
+
// "no_session" — a normal (olm_type=1) message but no session for the sender
|
|
30
|
+
// "tampered" — ciphertext failed authentication (flipped bits / wrong key)
|
|
31
|
+
// "session_mismatch" — a pre-key message for a different session than we hold
|
|
32
|
+
// "otk_exhausted" — the peer's one-time key was already consumed
|
|
33
|
+
// "internal" — an unexpected provisional token (should-never-happen)
|
|
34
|
+
// (A malformed/corrupt pickle is NOT recoverable — it aborts the tx fatally, since it
|
|
35
|
+
// can indicate local state corruption, not just a bad inbound message. See e2e_core.cpp.)
|
|
36
|
+
metadef e2e_error: ( $code -> str, $detail -> str ).
|
|
37
|
+
|
|
38
|
+
// The public prekey material a peer needs to open an outbound session to us is the
|
|
39
|
+
// AD-v2 t_e2e_bundle (address_document_types) — identity keys + a host-signed fallback
|
|
40
|
+
// prekey, empty one-time-key list under v1. A peer establishes via $fallback $key.
|
|
41
|
+
|
|
42
|
+
// The unified inbound result. On $ok=TRUE, $plaintext + $session are present, and
|
|
43
|
+
// $account is present ONLY on the establish path (a pre-key message rotates the
|
|
44
|
+
// account by consuming a prekey) — a normal-message decrypt leaves $account NIL, so
|
|
45
|
+
// the caller must overwrite its stored account ONLY when this field is non-NIL.
|
|
46
|
+
// On $ok=FALSE, $error is present and the message should be DROPPED (session lives).
|
|
47
|
+
metadef e2e_result:
|
|
48
|
+
(
|
|
49
|
+
$ok -> bool,
|
|
50
|
+
$plaintext -> bin+,
|
|
51
|
+
$session -> bin+,
|
|
52
|
+
$account -> bin+,
|
|
53
|
+
$error -> e2e_error+
|
|
54
|
+
).
|
|
55
|
+
|
|
56
|
+
hidden
|
|
57
|
+
{
|
|
58
|
+
// The 32-byte AEAD pickle key that seals every account/session blob. Derived
|
|
59
|
+
// ONCE per boot from the packet's stable root SIGN secret (survives key roll /
|
|
60
|
+
// reseed) with domain separation, then memoised. _value_id yields a HashCode;
|
|
61
|
+
// _hash_code_to_binary lowers it to the 32-byte binary the primitives require.
|
|
62
|
+
// NEVER persisted, NEVER exported — re-derived from the identity on each boot.
|
|
63
|
+
m_pickle_key IS bin+ = NIL.
|
|
64
|
+
|
|
65
|
+
fn pickle_key (_) -> bin
|
|
66
|
+
{
|
|
67
|
+
if m_pickle_key == NIL
|
|
68
|
+
{
|
|
69
|
+
secret = key_storage::export_identity_signing_secret().
|
|
70
|
+
m_pickle_key -> _hash_code_to_binary
|
|
71
|
+
(_value_id ($purpose -> "adapt/e2e/pickle/v1", $seed -> secret)).
|
|
72
|
+
}
|
|
73
|
+
return m_pickle_key as bin. // narrow bin+ -> bin (guaranteed set above)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Map a caught provisional token (a string that CONTAINS one of the primitives'
|
|
77
|
+
// "e2e:*" markers, wrapped in the "EVAL_ERROR: " prefix + best-effort frame
|
|
78
|
+
// trace) to a typed error. Substring match is robust to that wrapping.
|
|
79
|
+
fn has_token (haystack: str, needle: str) -> bool
|
|
80
|
+
{
|
|
81
|
+
return (_str_find haystack needle 0) >= 0.
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
fn map_error (err: str+) -> e2e_error
|
|
85
|
+
{
|
|
86
|
+
text = err as str. // the caught message (guaranteed non-NIL: success paths RETURN)
|
|
87
|
+
if has_token text "e2e:tampered" { return ( $code -> "tampered", $detail -> text ). }
|
|
88
|
+
if has_token text "e2e:session_mismatch" { return ( $code -> "session_mismatch", $detail -> text ). }
|
|
89
|
+
if has_token text "e2e:otk_exhausted" { return ( $code -> "otk_exhausted", $detail -> text ). }
|
|
90
|
+
// A provisional abort with no known e2e marker should be impossible (only the
|
|
91
|
+
// e2e primitives raise provisionals inside decrypt's body). Surface it as
|
|
92
|
+
// "internal" WITH the full text, so it is typed but never silently lost.
|
|
93
|
+
return ( $code -> "internal", $detail -> text ).
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Create a fresh identity account: identity keys + a reusable fallback key, and
|
|
98
|
+
// (empty-OTK v1 posture) NO one-time keys — so a pre-key handshake rests on the
|
|
99
|
+
// reusable fallback key. This makes one-time-key reuse *impossible* (none are
|
|
100
|
+
// published), but the tradeoff is real: initial-message forward secrecy now rests
|
|
101
|
+
// on the multi-use fallback rather than single-use one-time keys, so it is WEAKER
|
|
102
|
+
// for the first message(s) until the Double Ratchet advances. Steady-state FS/PCS
|
|
103
|
+
// after the first round-trip is unaffected. (See /tmp/e2e-otk-options.md.) The app
|
|
104
|
+
// persists the returned blob.
|
|
105
|
+
fn create_account (_) -> bin
|
|
106
|
+
{
|
|
107
|
+
a = _e2e_account_create (pickle_key()).
|
|
108
|
+
return _e2e_account_gen_fallback a (pickle_key()).
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Our published AD-v2 prekey bundle, for a peer to open an outbound session to us and
|
|
112
|
+
// for the decode-level receive hook on the far side to read our identity key. Built
|
|
113
|
+
// FRESH from the account's raw public bundle (never mutates the immutable engine blob),
|
|
114
|
+
// with a host Ed25519 signature over the fallback key bytes (belt-and-suspenders with
|
|
115
|
+
// the AD root signature). Under empty-OTK v1 there are no one-time keys and no distinct
|
|
116
|
+
// signed prekey, so a peer establishes via $fallback $key.
|
|
117
|
+
fn my_bundle (account: bin) -> t_e2e_bundle
|
|
118
|
+
{
|
|
119
|
+
b = _e2e_account_bundle account (pickle_key()).
|
|
120
|
+
// Raw bundle layout (crate src/mgmt/bundle.rs):
|
|
121
|
+
// ik_curve[0:32] ik_ed[32:64] fb_count(u8)@64 fb_id(u32 LE)[65:69] fb_key[69:101]
|
|
122
|
+
// otk_count(u32 LE)@101 ... (empty-OTK v1 => otk_count 0, no otk entries).
|
|
123
|
+
// Offset 69 is the fallback key ONLY when a fallback is present (fb_count@64 == 1).
|
|
124
|
+
// Guard it: without this, an account with no fallback would slice the wrong bytes
|
|
125
|
+
// (or overrun). Accounts made by create_account always carry one, but my_bundle is
|
|
126
|
+
// public and must not emit a garbage "fallback" key for an arbitrary account blob.
|
|
127
|
+
abort "e2e::my_bundle: account has no fallback key (empty-OTK v1 requires one)" WHEN (b 64) != 1.
|
|
128
|
+
fb_key = _binary_slice b 69 32.
|
|
129
|
+
return
|
|
130
|
+
(
|
|
131
|
+
$ik_curve -> (_binary_slice b 0 32),
|
|
132
|
+
$ik_ed -> (_binary_slice b 32 32),
|
|
133
|
+
$signed_prekey -> NIL, // v1: engine exposes no distinct signed prekey
|
|
134
|
+
$one_time_keys -> [], // empty-OTK v1 (one-time-key reuse impossible)
|
|
135
|
+
// vodozemac fallback key-id is not surfaced in v1 ($id -> 0): the receiver
|
|
136
|
+
// establishes from material inside the pre-key ciphertext and the sender uses
|
|
137
|
+
// only the key bytes, so the id is functionally unused here.
|
|
138
|
+
$fallback -> ( $id -> 0,
|
|
139
|
+
$key -> fb_key,
|
|
140
|
+
$sig -> (key_storage::default_sign (_value_id fb_key)) )
|
|
141
|
+
).
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Establish an OUTBOUND session to a peer from the peer's published AD-v2 bundle (picks
|
|
145
|
+
// the fallback prekey under empty-OTK v1). Returns the new session and the
|
|
146
|
+
// (possibly rotated) account — the app persists BOTH.
|
|
147
|
+
fn create_outbound_session (account: bin, peer: t_e2e_bundle) -> ( $session -> bin, $account -> bin )
|
|
148
|
+
{
|
|
149
|
+
r = _e2e_session_outbound account (peer $ik_curve) (peer $fallback $key) (pickle_key()).
|
|
150
|
+
return ( $session -> r $session, $account -> r $account ).
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Encrypt a plaintext on an established session. Returns the ciphertext, its
|
|
154
|
+
// olm_type (0=pre-key until the peer replies, 1=normal thereafter), and the
|
|
155
|
+
// advanced session — the app persists $session (the ratchet step).
|
|
156
|
+
fn encrypt (session: bin, plaintext: bin) -> ( $ciphertext -> bin, $olm_type -> int, $session -> bin )
|
|
157
|
+
{
|
|
158
|
+
r = _e2e_encrypt session plaintext (pickle_key()).
|
|
159
|
+
return ( $ciphertext -> r $msg, $olm_type -> r $msg_type, $session -> r $session ).
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// The ONE unified inbound call. The library decides establish-vs-decrypt from
|
|
163
|
+
// olm_type + session presence and maps every recoverable failure to a typed
|
|
164
|
+
// e2e_result — the caller NEVER branches on olm_type or handles a return code.
|
|
165
|
+
// olm_type=0 -> (re)establish an inbound session from the pre-key message;
|
|
166
|
+
// yields plaintext + new session + rotated account.
|
|
167
|
+
// olm_type=1, session -> decrypt on the existing session; yields plaintext + session.
|
|
168
|
+
// olm_type=1, NO session-> typed "no_session" (drop; the sender's pre-key repeat self-heals).
|
|
169
|
+
fn decrypt (account: bin, session: bin+, sender_ik: bin, olm_type: int, ciphertext: bin) -> e2e_result
|
|
170
|
+
{
|
|
171
|
+
if olm_type == 1 && session == NIL
|
|
172
|
+
{
|
|
173
|
+
return ( $ok -> FALSE, $plaintext -> NIL, $session -> NIL, $account -> NIL,
|
|
174
|
+
$error -> ( $code -> "no_session",
|
|
175
|
+
$detail -> "normal (olm_type=1) message but no established session for sender" ) ).
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
WHEN provisional abort err
|
|
179
|
+
{
|
|
180
|
+
if olm_type == 0
|
|
181
|
+
{
|
|
182
|
+
r = _e2e_session_inbound account sender_ik ciphertext (pickle_key()).
|
|
183
|
+
return ( $ok -> TRUE, $plaintext -> r $plaintext, $session -> r $session,
|
|
184
|
+
$account -> r $account, $error -> NIL ).
|
|
185
|
+
}
|
|
186
|
+
r = _e2e_decrypt (session as bin) 1 ciphertext (pickle_key()).
|
|
187
|
+
return ( $ok -> TRUE, $plaintext -> r $plaintext, $session -> r $session,
|
|
188
|
+
$account -> NIL, $error -> NIL ).
|
|
189
|
+
}
|
|
190
|
+
// Reached ONLY when the body provisional-aborted (both success branches RETURN):
|
|
191
|
+
// `err` holds the token text → typed drop, the session survives.
|
|
192
|
+
return ( $ok -> FALSE, $plaintext -> NIL, $session -> NIL, $account -> NIL,
|
|
193
|
+
$error -> (map_error err) ).
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Idempotent re-delivery check: TRUE iff `session` is the channel that this pre-key
|
|
197
|
+
// ciphertext would establish (so a re-sent handshake can be recognised and dropped
|
|
198
|
+
// by the stateful facade before re-establishing). Pure read.
|
|
199
|
+
fn session_matches (session: bin, ciphertext: bin) -> bool
|
|
200
|
+
{
|
|
201
|
+
return (_e2e_matches_inbound session ciphertext (pickle_key())) == 1.
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// The session's stable 32-byte id (shared by both ends of a channel). Pure read.
|
|
205
|
+
fn session_id (session: bin) -> bin
|
|
206
|
+
{
|
|
207
|
+
return _e2e_session_id session (pickle_key()).
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// =====================================================================================
|
|
211
|
+
// STATEFUL FACADE — adapt is the SINGLE owner of the Olm account + per-peer sessions.
|
|
212
|
+
// Because RECEIVE happens transparently at the __t_wrapper decode seam (adapt-owned
|
|
213
|
+
// packet-state, committed in the guaranteed-mutating decode tx), SEND must be adapt-owned
|
|
214
|
+
// too — one Olm session cannot have two owners or the ratchet desyncs. So the ours-mcp
|
|
215
|
+
// layer never holds session state: it calls encrypt_to to send and the decode hook calls
|
|
216
|
+
// decrypt_and_commit to receive. Both mutate this library's packet-state.
|
|
217
|
+
// =====================================================================================
|
|
218
|
+
|
|
219
|
+
// The E2E message envelope (opaque ciphertext + routing stamp) and its outer
|
|
220
|
+
// sender-authentication signature. NEW additive variant — the existing plain/signed/
|
|
221
|
+
// encrypted envelopes are untouched. The $e2e_envelope field is the decode MARKER.
|
|
222
|
+
metadef t_e2e_envelope: ( $session_id -> bin, $olm_type -> int, $ciphertext -> bin, $pv -> int ).
|
|
223
|
+
metadef e2e_signed_message: ( $e2e_envelope -> t_e2e_envelope, $emsignature -> crypto_signature ).
|
|
224
|
+
|
|
225
|
+
hidden
|
|
226
|
+
{
|
|
227
|
+
m_account IS bin+ = NIL. // this packet's Olm account (lazy first-use)
|
|
228
|
+
m_sessions IS (global_id ->> bin) = (,). // per-peer session pickle, keyed by contact id
|
|
229
|
+
m_wire_pv = 8. // wire dialect stamp ($pv) for v1 (matches wire8)
|
|
230
|
+
|
|
231
|
+
fn account (_) -> bin
|
|
232
|
+
{
|
|
233
|
+
if m_account == NIL { m_account -> create_account(). }
|
|
234
|
+
return m_account as bin.
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Our own published AD-v2 prekey bundle (from our lazily-created account) — embedded in
|
|
239
|
+
// our address document ($identity.$e2e_bundle) so a peer can open an outbound session to
|
|
240
|
+
// us and the far-side decode hook can read our identity key.
|
|
241
|
+
fn my_public_bundle (_) -> t_e2e_bundle
|
|
242
|
+
{
|
|
243
|
+
return my_bundle (account()).
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// SEND: encrypt tx_body (already serialized to bin by the caller) to a peer, lazily
|
|
247
|
+
// establishing the outbound session from the peer's bundle on first send, ADVANCING +
|
|
248
|
+
// COMMITTING our session, and returning the fully-built, sender-signed envelope ready to
|
|
249
|
+
// relay. ours-mcp send collapses to: action::send cid (e2e::encrypt_to cid body peer).
|
|
250
|
+
fn encrypt_to (cid: global_id, plaintext: bin, peer: t_e2e_bundle) -> e2e_signed_message
|
|
251
|
+
{
|
|
252
|
+
if (m_sessions cid) == NIL
|
|
253
|
+
{
|
|
254
|
+
est = create_outbound_session (account()) peer.
|
|
255
|
+
m_account -> est $account.
|
|
256
|
+
m_sessions cid -> est $session.
|
|
257
|
+
}
|
|
258
|
+
e = encrypt ((m_sessions cid) as bin) plaintext.
|
|
259
|
+
m_sessions cid -> e $session. // commit the ratchet advance
|
|
260
|
+
env = ( $session_id -> (session_id (e $session)), $olm_type -> (e $olm_type),
|
|
261
|
+
$ciphertext -> (e $ciphertext), $pv -> m_wire_pv ).
|
|
262
|
+
// S1: bind the SENDER ($from = us) and RECIPIENT ($to = cid) into the emsig pre-image,
|
|
263
|
+
// alongside the wire-versioned $env (which carries $pv). This authenticates the routing,
|
|
264
|
+
// so a captured envelope cannot be reflected to a third party or re-targeted. The envelope
|
|
265
|
+
// STRUCTURE is unchanged (adapt signs here; the adapt decode hook verifies over the same
|
|
266
|
+
// reconstructed context) — no companion/wire-corpus change.
|
|
267
|
+
sig_context = ( $from -> _get_container_id(), $to -> cid, $envelope -> env ).
|
|
268
|
+
return ( $e2e_envelope -> env,
|
|
269
|
+
$emsignature -> (key_storage::default_sign (_value_id sig_context)) ).
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// The supported wire dialect stamp ($pv) — the receiver validates an inbound envelope's $pv
|
|
273
|
+
// against this to reject an unknown/downgraded dialect (S2). Pure read.
|
|
274
|
+
fn wire_pv (_) -> int { return m_wire_pv. }
|
|
275
|
+
|
|
276
|
+
// RECEIVE: decrypt an inbound envelope for a known sender and COMMIT the advanced session
|
|
277
|
+
// (and, on the establish path only, the rotated account). Returns the typed e2e_result.
|
|
278
|
+
// The caller (the __t_wrapper decode hook) authenticates $emsignature FIRST and supplies
|
|
279
|
+
// the sender's e2e identity key (read from the same AD identity it origin-binds against).
|
|
280
|
+
fn decrypt_and_commit (from_cid: global_id, sender_ik: bin, olm_type: int, ciphertext: bin) -> e2e_result
|
|
281
|
+
{
|
|
282
|
+
// Pre-key (establish) REPLAY guard. v1 publishes a MULTI-USE fallback key (empty OTK
|
|
283
|
+
// list), so a replayed olm_type=0 pre-key would re-establish successfully — re-executing
|
|
284
|
+
// the inner transaction AND overwriting the live (possibly advanced) session. If we
|
|
285
|
+
// already hold a session for this sender and THIS pre-key is the same handshake that
|
|
286
|
+
// established it, it is an idempotent re-delivery: drop it recoverably (no re-establish,
|
|
287
|
+
// no re-deliver, session untouched). session_matches is a pure read on the existing
|
|
288
|
+
// session; a genuine NEW handshake (different ephemeral) does not match and re-establishes.
|
|
289
|
+
existing IS bin+ = m_sessions from_cid.
|
|
290
|
+
if olm_type == 0 && existing != NIL && (session_matches (existing as bin) ciphertext)
|
|
291
|
+
{
|
|
292
|
+
return ( $ok -> FALSE, $plaintext -> NIL, $session -> NIL, $account -> NIL,
|
|
293
|
+
$error -> ( $code -> "replayed_handshake",
|
|
294
|
+
$detail -> "pre-key re-delivers an already-established session" ) ).
|
|
295
|
+
}
|
|
296
|
+
r = decrypt (account()) (m_sessions from_cid) sender_ik olm_type ciphertext.
|
|
297
|
+
if r $ok
|
|
298
|
+
{
|
|
299
|
+
m_sessions from_cid -> (r $session) as bin. // commit the ratchet advance
|
|
300
|
+
if (r $account) != NIL { m_account -> (r $account) as bin. } // establish path rotates the account
|
|
301
|
+
}
|
|
302
|
+
return r.
|
|
303
|
+
}
|
|
304
|
+
}
|
|
@@ -10,7 +10,15 @@ library key_storage loads library key_utils {
|
|
|
10
10
|
_read_or_abort -> read.
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
// Deserialize untrusted external bytes (a decrypted peer payload) through the injected
|
|
14
|
+
// D_Code-FILTERED reader — the same defence decrypt_message applies to peer crypto_messages.
|
|
15
|
+
// Used by the E2E decode hook to turn a decrypted Olm plaintext back into an inner t_trn.
|
|
16
|
+
read_external = fn (data: bin) -> any
|
|
17
|
+
{
|
|
18
|
+
return _read_or_abort data.
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
using key_utils.
|
|
14
22
|
metadef t_keypair_info:
|
|
15
23
|
(
|
|
16
24
|
$function->t_function,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adapt-toolkit/mufl",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.4",
|
|
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.",
|
package/prebuilds/linux-x64/mufl
CHANGED
|
Binary file
|
|
Binary file
|
|
@@ -3,16 +3,21 @@
|
|
|
3
3
|
// to create a function that takes a string that contains JSON that encodes the value of that type
|
|
4
4
|
// and performs all the necessary runtime checks.
|
|
5
5
|
|
|
6
|
-
library __t_wrapper loads libraries transaction, key_storage, transaction_queue, identity_proof_document_types {
|
|
6
|
+
library __t_wrapper loads libraries transaction, key_storage, transaction_queue, identity_proof_document_types, e2e {
|
|
7
7
|
|
|
8
8
|
metadef t_trn: ($name->str, $targ->any).
|
|
9
|
-
metadef t_trn_extended: ($trn->t_trn, $is_signed->bool, $is_encrypted->bool).
|
|
9
|
+
metadef t_trn_extended: ($trn->t_trn, $is_signed->bool, $is_encrypted->bool, $is_e2e->bool+).
|
|
10
10
|
metadef t_trn_message_info: (
|
|
11
11
|
$decoded_envelope -> transaction::envelope::external::decoded_type || t_trn,
|
|
12
12
|
$decoded_message -> t_trn,
|
|
13
13
|
$is_signed -> bool,
|
|
14
14
|
$is_encrypted -> bool,
|
|
15
|
-
$has_envelope -> bool
|
|
15
|
+
$has_envelope -> bool,
|
|
16
|
+
// TRUE only for a decoded E2E (double-ratchet) envelope. The C++ transaction driver
|
|
17
|
+
// uses it to enforce the reject-readonly-E2E invariant (an E2E tx is never readonly,
|
|
18
|
+
// so the committing ratchet-advance always seals) and to drop a benign recoverable
|
|
19
|
+
// failure (empty $name) without poisoning the channel.
|
|
20
|
+
$is_e2e -> bool+
|
|
16
21
|
).
|
|
17
22
|
|
|
18
23
|
hidden {
|
|
@@ -25,7 +30,11 @@ library __t_wrapper loads libraries transaction, key_storage, transaction_queue,
|
|
|
25
30
|
return key_storage::check_signature hash signature.
|
|
26
31
|
}
|
|
27
32
|
|
|
28
|
-
|
|
33
|
+
// Verify the enveloped sender's identity proof commits to $from and returns BOTH the
|
|
34
|
+
// origin-bound SIGN key (for signature checks) AND the parsed identity — the latter so
|
|
35
|
+
// the E2E decode hook can read the sender's e2e identity key ($e2e_bundle $ik_curve)
|
|
36
|
+
// off the SAME origin-bound identity, with no separate peer store.
|
|
37
|
+
check_origin_binding = fn (envelope: any) -> ( $sign_key -> publickey_sign, $identity -> any ) {
|
|
29
38
|
ip_raw = envelope $ip_document.
|
|
30
39
|
abort "Missing identity document on external message" WHEN ip_raw == NIL.
|
|
31
40
|
ip = ip_raw SAFE(identity_proof_document_types::t_identity_proof_document) abort "Invalid identity proof document" WHEN IS NIL.
|
|
@@ -41,10 +50,13 @@ library __t_wrapper loads libraries transaction, key_storage, transaction_queue,
|
|
|
41
50
|
}
|
|
42
51
|
abort "SIGN key not found in identity key_list" WHEN sign_pub == NIL.
|
|
43
52
|
abort "Origin address does not commit to signing key" WHEN (key_storage::address_of_key sign_pub?) != (envelope $from).
|
|
44
|
-
return sign_pub
|
|
53
|
+
return ( $sign_key -> sign_pub?, $identity -> identity ).
|
|
45
54
|
}
|
|
46
55
|
|
|
47
|
-
|
|
56
|
+
// origin_sign_key / from_cid / sender_ik are the origin-bound sender key, the sender's
|
|
57
|
+
// container id ($from), and the sender's e2e identity key ($e2e_bundle $ik_curve) — all
|
|
58
|
+
// NIL for non-enveloped (internal) messages, which never carry $e2e_envelope.
|
|
59
|
+
decode_message_with_keys = fn (message: any, origin_sign_key: publickey_sign+, from_cid: global_id+, to_cid: global_id+, sender_ik: bin+) -> t_trn_extended {
|
|
48
60
|
if message $encrypted_message {
|
|
49
61
|
is_ptsignature IS bool = TRUE.
|
|
50
62
|
signature IS crypto_signature+ = NIL.
|
|
@@ -58,12 +70,52 @@ library __t_wrapper loads libraries transaction, key_storage, transaction_queue,
|
|
|
58
70
|
decrypted_message = decrypt_message encrypted_message.
|
|
59
71
|
res = decrypted_message SAFE(t_trn).
|
|
60
72
|
if is_ptsignature && (check_signature signature? decrypted_message) {
|
|
61
|
-
return ($trn -> res, $is_signed -> TRUE, $is_encrypted -> TRUE).
|
|
73
|
+
return ($trn -> res, $is_signed -> TRUE, $is_encrypted -> TRUE, $is_e2e -> FALSE).
|
|
62
74
|
} elif (!is_ptsignature && check_signature signature? encrypted_message) {
|
|
63
|
-
return ($trn -> res, $is_signed -> TRUE, $is_encrypted -> TRUE).
|
|
75
|
+
return ($trn -> res, $is_signed -> TRUE, $is_encrypted -> TRUE, $is_e2e -> FALSE).
|
|
64
76
|
} else {
|
|
65
77
|
abort "Signature verification failed." WHEN TRUE.
|
|
66
|
-
return ($trn -> ($name -> "", $targ -> NIL), $is_signed -> FALSE, $is_encrypted -> FALSE).
|
|
78
|
+
return ($trn -> ($name -> "", $targ -> NIL), $is_signed -> FALSE, $is_encrypted -> FALSE, $is_e2e -> FALSE).
|
|
79
|
+
}
|
|
80
|
+
} elif message $e2e_envelope {
|
|
81
|
+
// === E2E (double-ratchet) decode hook — transparent decrypt at the decode seam ===
|
|
82
|
+
// The inner transaction rides as opaque Olm ciphertext; here we authenticate the
|
|
83
|
+
// sender, decrypt+commit the ratchet in this (guaranteed-mutating) decode tx, and
|
|
84
|
+
// hand the plaintext inner t_trn back to the driver. $is_e2e=TRUE lets the C++
|
|
85
|
+
// driver enforce reject-readonly-E2E (post-findTransaction, where the inner tx's
|
|
86
|
+
// readonly flag is known) and drop a benign failure (empty $name) cleanly.
|
|
87
|
+
env = message $e2e_envelope.
|
|
88
|
+
// Outer sender authentication: $emsignature over the SENDER+RECIPIENT+envelope
|
|
89
|
+
// context (S1) by the SAME SIGN key check_origin_binding tied to $from. E2E is
|
|
90
|
+
// enveloped-only, so origin_sign_key, $from, $to and the sender's e2e key must all
|
|
91
|
+
// be present. from_cid/to_cid are the outer envelope's $from/$to.
|
|
92
|
+
abort "E2E envelope requires an origin-bound sender key" WHEN origin_sign_key == NIL.
|
|
93
|
+
abort "E2E envelope missing sender container id" WHEN from_cid == NIL.
|
|
94
|
+
abort "E2E envelope missing recipient container id" WHEN to_cid == NIL.
|
|
95
|
+
abort "E2E sender published no e2e bundle" WHEN sender_ik == NIL.
|
|
96
|
+
// S2: reject an unknown/downgraded wire dialect before any decrypt/commit.
|
|
97
|
+
abort "E2E envelope wire-version mismatch" WHEN ((env $pv) as int) != (e2e::wire_pv()).
|
|
98
|
+
emsig = (message $emsignature) SAFE(crypto_signature) abort "Missing E2E envelope signature" WHEN IS NIL.
|
|
99
|
+
// S1: recompute the signed context from the ACTUAL outer $from/$to + envelope, so a
|
|
100
|
+
// captured envelope cannot be reflected to a third party or re-targeted.
|
|
101
|
+
sig_context = ( $from -> from_cid?, $to -> to_cid?, $envelope -> env ).
|
|
102
|
+
abort "E2E envelope signature verification failed"
|
|
103
|
+
WHEN (_crypto_verify_hash_signature (_value_id sig_context |) emsig? origin_sign_key?) != TRUE.
|
|
104
|
+
r = e2e::decrypt_and_commit from_cid? sender_ik? ((env $olm_type) as int) ((env $ciphertext) as bin).
|
|
105
|
+
if r $ok {
|
|
106
|
+
// The plaintext is the peer's inner t_trn serialized with _write. Deserialize
|
|
107
|
+
// with the D_Code-FILTERED external reader (untrusted ingress) that key_storage
|
|
108
|
+
// owns — the same defence the encrypted_message path relies on.
|
|
109
|
+
inner = key_storage::read_external (r $plaintext |).
|
|
110
|
+
inner_trn = (inner SAFE(t_trn)) abort "E2E: decrypted payload is not a transaction" WHEN IS NIL.
|
|
111
|
+
return ($trn -> inner_trn?, $is_signed -> TRUE, $is_encrypted -> TRUE, $is_e2e -> TRUE).
|
|
112
|
+
} else {
|
|
113
|
+
// Recoverable failure (no_session | tampered | session_mismatch): deliver a
|
|
114
|
+
// benign no-op. An empty $name + $is_e2e signals the driver to drop silently
|
|
115
|
+
// without poisoning the channel (a failed decrypt commits no state, so the
|
|
116
|
+
// session is untouched). $is_signed stays TRUE (the emsig verified) so the
|
|
117
|
+
// enveloped-must-be-signed guard below does not abort.
|
|
118
|
+
return ($trn -> ($name -> "", $targ -> NIL), $is_signed -> TRUE, $is_encrypted -> TRUE, $is_e2e -> TRUE).
|
|
67
119
|
}
|
|
68
120
|
} elif message $trn {
|
|
69
121
|
tn = message $trn.
|
|
@@ -73,16 +125,16 @@ library __t_wrapper loads libraries transaction, key_storage, transaction_queue,
|
|
|
73
125
|
} else {
|
|
74
126
|
abort "Signature verification failed for message." WHEN ((check_signature signature? tn) == NIL).
|
|
75
127
|
}
|
|
76
|
-
return ($trn -> (tn safe(t_trn)), $is_signed -> TRUE, $is_encrypted -> FALSE).
|
|
128
|
+
return ($trn -> (tn safe(t_trn)), $is_signed -> TRUE, $is_encrypted -> FALSE, $is_e2e -> FALSE).
|
|
77
129
|
} else {
|
|
78
|
-
return ($trn -> (message safe(t_trn)), $is_signed -> FALSE, $is_encrypted -> FALSE).
|
|
130
|
+
return ($trn -> (message safe(t_trn)), $is_signed -> FALSE, $is_encrypted -> FALSE, $is_e2e -> FALSE).
|
|
79
131
|
}
|
|
80
132
|
}
|
|
81
133
|
}
|
|
82
134
|
|
|
83
135
|
fn decode_message (message: any) -> t_trn_extended
|
|
84
136
|
{
|
|
85
|
-
return decode_message_with_keys message NIL.
|
|
137
|
+
return decode_message_with_keys message NIL NIL NIL NIL.
|
|
86
138
|
}
|
|
87
139
|
|
|
88
140
|
/*
|
|
@@ -112,10 +164,19 @@ library __t_wrapper loads libraries transaction, key_storage, transaction_queue,
|
|
|
112
164
|
has_envelope is bool = FALSE.
|
|
113
165
|
message is any = NIL.
|
|
114
166
|
origin_sign_key IS publickey_sign+ = NIL.
|
|
167
|
+
from_cid IS global_id+ = NIL. // the sender's container id ($from), enveloped only
|
|
168
|
+
to_cid IS global_id+ = NIL. // the recipient's container id ($to), enveloped only (S1)
|
|
169
|
+
sender_ik IS bin+ = NIL. // the sender's e2e identity key, read off the origin-bound identity
|
|
115
170
|
|
|
116
171
|
// This is the case of external message packed into the transaction envelope
|
|
117
172
|
if envelope $body != NIL {
|
|
118
|
-
|
|
173
|
+
check_origin_binding envelope => ($sign_key -> ob_key, $identity -> ob_identity).
|
|
174
|
+
origin_sign_key -> ob_key.
|
|
175
|
+
from_cid -> (envelope $from) as global_id.
|
|
176
|
+
if (envelope $to) { to_cid -> (envelope $to) as global_id. }
|
|
177
|
+
// AD-v2 peers publish an $e2e_bundle in their identity; a v1 peer has none (and
|
|
178
|
+
// cannot send E2E). Read the sender's identity key for the E2E establish path.
|
|
179
|
+
if (ob_identity $e2e_bundle) { sender_ik -> ((ob_identity $e2e_bundle $ik_curve) as bin). }
|
|
119
180
|
message -> envelope $body.
|
|
120
181
|
has_envelope -> TRUE.
|
|
121
182
|
} else {
|
|
@@ -123,7 +184,7 @@ library __t_wrapper loads libraries transaction, key_storage, transaction_queue,
|
|
|
123
184
|
has_envelope -> FALSE.
|
|
124
185
|
}
|
|
125
186
|
|
|
126
|
-
decode_message_with_keys message origin_sign_key => ($trn -> decoded_message, $is_signed -> is_signed, $is_encrypted -> is_encrypted).
|
|
187
|
+
decode_message_with_keys message origin_sign_key from_cid to_cid sender_ik => ($trn -> decoded_message, $is_signed -> is_signed, $is_encrypted -> is_encrypted, $is_e2e -> is_e2e).
|
|
127
188
|
|
|
128
189
|
abort "Unsigned external message rejected: enveloped messages must be signed." WHEN has_envelope && (!is_signed).
|
|
129
190
|
|
|
@@ -134,7 +195,8 @@ library __t_wrapper loads libraries transaction, key_storage, transaction_queue,
|
|
|
134
195
|
$decoded_message -> decoded_message,
|
|
135
196
|
$is_signed -> is_signed,
|
|
136
197
|
$is_encrypted -> is_encrypted,
|
|
137
|
-
$has_envelope -> has_envelope
|
|
198
|
+
$has_envelope -> has_envelope,
|
|
199
|
+
$is_e2e -> is_e2e
|
|
138
200
|
) safe t_trn_message_info.
|
|
139
201
|
} else {
|
|
140
202
|
return (
|
|
@@ -142,7 +204,8 @@ library __t_wrapper loads libraries transaction, key_storage, transaction_queue,
|
|
|
142
204
|
$decoded_message -> decoded_message,
|
|
143
205
|
$is_signed -> is_signed,
|
|
144
206
|
$is_encrypted -> is_encrypted,
|
|
145
|
-
$has_envelope -> has_envelope
|
|
207
|
+
$has_envelope -> has_envelope,
|
|
208
|
+
$is_e2e -> is_e2e
|
|
146
209
|
) safe t_trn_message_info.
|
|
147
210
|
}
|
|
148
211
|
}
|
package/transactions/config.mufl
CHANGED
|
@@ -14,7 +14,8 @@ config script {
|
|
|
14
14
|
$key_storage->#"../mufl_stdlib",
|
|
15
15
|
$transaction_queue->#"../mufl_stdlib",
|
|
16
16
|
$key_utils->#"../mufl_stdlib",
|
|
17
|
-
$identity_proof_document_types->#"../mufl_stdlib"
|
|
17
|
+
$identity_proof_document_types->#"../mufl_stdlib",
|
|
18
|
+
$e2e->#"../mufl_stdlib"
|
|
18
19
|
),
|
|
19
20
|
$applications->(,)
|
|
20
21
|
)
|