Skip to content

Commit b66d201

Browse files
committed
Adding JSON handling as fallback of EBTF decode failure
The Cryptic engine is using Erlang Binary Term Format for encoding, for example, metadata in X3DH messages. However, with the introduction of a Mobila App written in Dart it is easer to just encode it as JSON. Hence, we introduce this fallback mechanism, so that if the term_to_binary fail, we will try to decode the term as JSON.
1 parent 00b24ab commit b66d201

File tree

2 files changed

+51
-4
lines changed

2 files changed

+51
-4
lines changed

src/cryptic_engine.erl

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2217,8 +2217,10 @@ initialize_receiver_session_from_x3dh(FromUsername, MessagePayload, State) ->
22172217
maps:get(<<"metadata">>, MessagePayload)
22182218
),
22192219

2220-
% Decode metadata to extract sender identity and other info
2221-
Metadata = erlang:binary_to_term(EncodedMetadata),
2220+
% Decode metadata - try ETF first, then JSON for mobile clients
2221+
{Metadata, MetadataFormat} = decode_x3dh_metadata(EncodedMetadata),
2222+
?dbg("Decoded X3DH metadata (format: ~p): ~p~n", [MetadataFormat, Metadata]),
2223+
22222224
SenderIdPub = maps:get(sender_identity_sign_public, Metadata),
22232225

22242226
% Extract receiver keys from state
@@ -2262,6 +2264,8 @@ initialize_receiver_session_from_x3dh(FromUsername, MessagePayload, State) ->
22622264

22632265
MessageBlob = #{
22642266
metadata => Metadata,
2267+
metadata_bytes => EncodedMetadata, % Original bytes for signature verification
2268+
metadata_format => MetadataFormat, % 'etf' or 'json'
22652269
signature => Signature,
22662270
ciphertext => Ciphertext,
22672271
nonce => Nonce
@@ -2342,6 +2346,41 @@ initialize_receiver_session_from_x3dh(FromUsername, MessagePayload, State) ->
23422346
{error, {exception, ErrorClass, ErrorReason}}
23432347
end.
23442348

2349+
%% @private
2350+
%% @doc Decode X3DH metadata from either ETF (Erlang clients) or JSON (mobile clients)
2351+
%% Returns {Metadata, Format} where Format is 'etf' or 'json'
2352+
decode_x3dh_metadata(EncodedMetadata) ->
2353+
% Try ETF first (Erlang native format)
2354+
try
2355+
EtfMetadata = erlang:binary_to_term(EncodedMetadata),
2356+
{EtfMetadata, etf}
2357+
catch
2358+
error:badarg ->
2359+
% Try JSON (mobile client format)
2360+
JsonMap = jsx:decode(EncodedMetadata, [return_maps]),
2361+
JsonMetadata = convert_json_metadata_to_erlang(JsonMap),
2362+
{JsonMetadata, json}
2363+
end.
2364+
2365+
%% @private
2366+
%% @doc Convert JSON metadata map with binary keys to Erlang metadata with atom keys
2367+
convert_json_metadata_to_erlang(JsonMap) ->
2368+
#{
2369+
version => maps:get(<<"version">>, JsonMap),
2370+
type => maps:get(<<"type">>, JsonMap),
2371+
sender_id => base64:decode(maps:get(<<"sender_id">>, JsonMap)),
2372+
sender_identity_dh_public => base64:decode(maps:get(<<"sender_identity_dh_public">>, JsonMap)),
2373+
sender_identity_sign_public => base64:decode(maps:get(<<"sender_identity_sign_public">>, JsonMap)),
2374+
recipient_id => base64:decode(maps:get(<<"recipient_id">>, JsonMap)),
2375+
ephemeral_public => base64:decode(maps:get(<<"ephemeral_public">>, JsonMap)),
2376+
otpk_id => case maps:get(<<"otpk_id">>, JsonMap, null) of
2377+
null -> undefined;
2378+
OtpkIdB64 -> base64:decode(OtpkIdB64)
2379+
end,
2380+
message_id => base64:decode(maps:get(<<"message_id">>, JsonMap)),
2381+
timestamp => maps:get(<<"timestamp">>, JsonMap)
2382+
}.
2383+
23452384
%% @private
23462385
%% @doc Initialize ratchet session as receiver after X3DH
23472386
%% If SenderRatchetDHPub is provided, the receiver can immediately send (bidirectional)

src/cryptic_lib.erl

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1460,7 +1460,11 @@ x3dh_receiver_decrypt(
14601460
%% This ensures we verify against exactly what Alice signed
14611461

14621462
%% Verify message signature using complete metadata
1463-
MetadataBin = erlang:term_to_binary(CompleteMetadata),
1463+
%% Use original metadata bytes if available (for mobile clients with JSON format)
1464+
MetadataBin = case maps:get(metadata_bytes, MessageBlob, undefined) of
1465+
undefined -> erlang:term_to_binary(CompleteMetadata);
1466+
OriginalBytes -> OriginalBytes
1467+
end,
14641468
?dbg("X3DH signature verification - Complete Metadata map: ~p", [
14651469
CompleteMetadata
14661470
]),
@@ -1580,7 +1584,11 @@ x3dh_receiver_decrypt_with_session_key(
15801584
} = Metadata,
15811585

15821586
%% Verify message signature (use provided SenderIdPub parameter)
1583-
MetadataBin = erlang:term_to_binary(Metadata),
1587+
%% Use original metadata bytes if available (for mobile clients with JSON format)
1588+
MetadataBin = case maps:get(metadata_bytes, MessageBlob, undefined) of
1589+
undefined -> erlang:term_to_binary(Metadata);
1590+
OriginalBytes -> OriginalBytes
1591+
end,
15841592
case verify_signature(MetadataBin, Signature, SenderIdPub) of
15851593
false ->
15861594
{error, invalid_message_signature};

0 commit comments

Comments
 (0)