@@ -11,6 +11,74 @@ describe('message encryption', () => {
1111 const plaintext = await messageCrypto . decryptText ( ciphertext , iv , key )
1212 expect ( plaintext ) . toBe ( 'hello world' )
1313 } )
14+
15+ it ( 'produces different ciphertext for same plaintext with different keys' , async ( ) => {
16+ const key1 = await messageCrypto . deriveKey ( 'secret-1' , 'thread-1' )
17+ const key2 = await messageCrypto . deriveKey ( 'secret-2' , 'thread-1' )
18+ const { ciphertext : ct1 } = await messageCrypto . encryptText ( 'same message' , key1 )
19+ const { ciphertext : ct2 } = await messageCrypto . encryptText ( 'same message' , key2 )
20+ expect ( ct1 ) . not . toEqual ( ct2 )
21+ } )
22+
23+ it ( 'fails to decrypt with wrong key' , async ( ) => {
24+ const correctKey = await messageCrypto . deriveKey ( 'correct' , 'thread-1' )
25+ const wrongKey = await messageCrypto . deriveKey ( 'wrong' , 'thread-1' )
26+ const { ciphertext, iv } = await messageCrypto . encryptText ( 'secret data' , correctKey )
27+ await expect ( messageCrypto . decryptText ( ciphertext , iv , wrongKey ) ) . rejects . toThrow ( )
28+ } )
29+ } )
30+
31+ describe ( 'E2E encryption — key exchange and session' , ( ) => {
32+ it ( 'key bundle contains all required fields' , ( ) => {
33+ const bundle = {
34+ identityKey : new Uint8Array ( 33 ) ,
35+ signedPreKey : { keyId : 1 , publicKey : new Uint8Array ( 33 ) , signature : new Uint8Array ( 64 ) } ,
36+ oneTimePreKeys : [ { keyId : 1 , publicKey : new Uint8Array ( 33 ) } ] ,
37+ }
38+ expect ( bundle . identityKey ) . toHaveLength ( 33 )
39+ expect ( bundle . signedPreKey . keyId ) . toBe ( 1 )
40+ expect ( bundle . signedPreKey . publicKey ) . toHaveLength ( 33 )
41+ expect ( bundle . signedPreKey . signature ) . toHaveLength ( 64 )
42+ expect ( bundle . oneTimePreKeys ) . toHaveLength ( 1 )
43+ } )
44+
45+ it ( 'encrypted message has correct structure' , ( ) => {
46+ const msg = {
47+ id : 'msg-1' ,
48+ senderId : 'user-a' ,
49+ recipientId : 'user-b' ,
50+ ciphertext : new Uint8Array ( [ 0x01 , 0x02 , 0x03 ] ) ,
51+ messageType : 3 as const ,
52+ timestamp : Date . now ( ) ,
53+ }
54+ expect ( msg . senderId ) . not . toBe ( msg . recipientId )
55+ expect ( msg . ciphertext . length ) . toBeGreaterThan ( 0 )
56+ expect ( [ 1 , 3 ] ) . toContain ( msg . messageType )
57+ } )
58+
59+ it ( 'delivery receipt tracks message status' , ( ) => {
60+ const receipt = {
61+ messageId : 'msg-1' ,
62+ recipientId : 'user-b' ,
63+ status : 'delivered' as const ,
64+ timestamp : Date . now ( ) ,
65+ }
66+ expect ( receipt . status ) . toBe ( 'delivered' )
67+ expect ( receipt . messageId ) . toBe ( 'msg-1' )
68+ } )
69+
70+ it ( 'sealed sender envelope contains version byte and data' , ( ) => {
71+ const sealedMsg = {
72+ id : 'sealed-1' ,
73+ envelope : new Uint8Array ( [ 0x01 , ...new Array ( 33 ) . fill ( 0 ) , ...new Array ( 20 ) . fill ( 0xFF ) ] ) ,
74+ signature : new Uint8Array ( 64 ) ,
75+ messageType : 1 as const ,
76+ timestamp : Date . now ( ) ,
77+ }
78+ expect ( sealedMsg . envelope [ 0 ] ) . toBe ( 0x01 )
79+ expect ( sealedMsg . envelope . length ) . toBeGreaterThan ( 34 )
80+ expect ( sealedMsg . signature ) . toHaveLength ( 64 )
81+ } )
1482} )
1583
1684describe ( 'websocket wrapper' , ( ) => {
0 commit comments