@@ -178,16 +178,14 @@ module Crypto =
178178 let info = Encoding.UTF8.GetBytes(Constants.hkdfInfo)
179179 HKDF.DeriveKey(HashAlgorithmName.SHA256, rawSecret, 32, salt, info)
180180
181− // Blob layout v2: [ephPub(32) | nonce(24) | ciphertext]
181+ // Blob layout: [ephPub(32) | nonce(24) | ciphertext]
182182 // encrypted payload: [senderPub(32) | signature(64) | compressed]
183183 // signature signs (recipientPub ++ compressed) — prevents forwarding
184− // Legacy layout: [ephPub(32) | nonce(24) | ciphertext | senderPub(32) | signature(64)]
185184 let private ephPubLen = 32
186185 let private nonceLen = 24
187186 let private pubLen = 32
188187 let private sigLen = 64
189188 let private headerLen = ephPubLen + nonceLen
190− let private legacyTrailerLen = pubLen + sigLen
191189 let private minBlobLen = headerLen + pubLen + sigLen + 16 // inner fields + AEAD tag
192190
193191 let private deriveSymKey recipientPrivKey ephPub =
@@ -226,50 +224,22 @@ module Crypto =
226224 else
227225 let ephPub = blob.[.. ephPubLen - 1]
228226 let nonce = blob.[ephPubLen .. headerLen - 1]
227+ let ciphertext = blob.[headerLen ..]
228+ let key = deriveSymKey recipientPrivKey ephPub
229+ let decrypted = aeadDecrypt ciphertext nonce key
229230
230− // Try v2 format: entire payload after header is ciphertext
231− let v2Result =
232− try
233− let ciphertext = blob.[headerLen ..]
234− let key = deriveSymKey recipientPrivKey ephPub
235− let decrypted = aeadDecrypt ciphertext nonce key
236−
237− if decrypted.Length < pubLen + sigLen then
238− None
239− else
240− let senderPub = decrypted.[.. pubLen - 1]
241− let signature = decrypted.[pubLen .. pubLen + sigLen - 1]
242− let compressed = decrypted.[pubLen + sigLen ..]
243−
244− let recipientPub = recipientPrivKey.[32..63]
245− let signedData = Array.append recipientPub compressed
246−
247− if verifyDetached signature signedData senderPub then
248− let plaintext = LZ4Pickler.Unpickle(compressed)
249− Some(Encoding.UTF8.GetString(plaintext), toHex senderPub)
250− else
251− None
252− with _ -> None
253−
254− match v2Result with
255− | Some _ -> v2Result
256− | None ->
257− // Fall back to legacy format: trailer has senderPub + signature in plaintext
258− try
259− if blob.Length < headerLen + 16 + legacyTrailerLen then
260− None
261− else
262− let ciphertext = blob.[headerLen .. blob.Length - legacyTrailerLen - 1]
263− let senderPub = blob.[blob.Length - legacyTrailerLen .. blob.Length - sigLen - 1]
264− let signature = blob.[blob.Length - sigLen ..]
265−
266− if not (verifyDetached signature ciphertext senderPub) then
267− None
268− else
269− let key = deriveSymKey recipientPrivKey ephPub
270− let decrypted = aeadDecrypt ciphertext nonce key
271− let plaintext = LZ4Pickler.Unpickle(decrypted)
272− Some(Encoding.UTF8.GetString(plaintext), toHex senderPub)
273− with ex ->
274− log $"decrypt error: {ex.Message}"
231+ if decrypted.Length < pubLen + sigLen then
232+ None
233+ else
234+ let senderPub = decrypted.[.. pubLen - 1]
235+ let signature = decrypted.[pubLen .. pubLen + sigLen - 1]
236+ let compressed = decrypted.[pubLen + sigLen ..]
237+
238+ let recipientPub = recipientPrivKey.[32..63]
239+ let signedData = Array.append recipientPub compressed
240+
241+ if verifyDetached signature signedData senderPub then
242+ let plaintext = LZ4Pickler.Unpickle(compressed)
243+ Some(Encoding.UTF8.GetString(plaintext), toHex senderPub)
244+ else
275245 None