@@ -1455,6 +1455,140 @@ final class SessionTests: XCTestCase {
14551455 XCTFail ( " Invalid error type: \( error) " )
14561456 }
14571457 }
1458+
1459+ func testInteractionWhenLostSessionChanges( ) throws {
1460+ let senderClient = try TestClient ( userId: UUID ( ) ) // Alice
1461+ let recipientClient = try TestClient ( userId: UUID ( ) ) // Bob
1462+ try initializeSession ( senderClient: senderClient, recipientClient: recipientClient)
1463+
1464+ let aliceClient = senderClient
1465+ let bobClient = recipientClient
1466+
1467+ // Archive session state so that we have a non-empty list of previous states
1468+ var session = try bobClient. sessionStore. loadSession ( for: aliceClient. protocolAddress)
1469+ session? . archiveCurrentState ( )
1470+ try bobClient. sessionStore. storeSession (
1471+ try XCTUnwrap ( session) ,
1472+ for: aliceClient. protocolAddress)
1473+
1474+ // The code below will do the next ratchet step
1475+ // {
1476+ // Alice sends one more message
1477+ let data = try XCTUnwrap ( " From Alice " . data ( using: . utf8) )
1478+ let message = try Session . encrypt (
1479+ data: data,
1480+ for: bobClient. protocolAddress,
1481+ sessionStore: aliceClient. sessionStore,
1482+ identityStore: aliceClient. identityKeyStore
1483+ )
1484+ let result = try Session . decrypt (
1485+ message: message,
1486+ from: aliceClient. protocolAddress,
1487+ sessionStore: bobClient. sessionStore,
1488+ identityStore: bobClient. identityKeyStore,
1489+ preKeyStore: bobClient. preKeyStore,
1490+ signedPreKeyStore: bobClient. signedPreKeyStore
1491+ )
1492+ XCTAssertEqual ( result, data)
1493+
1494+ // Bob sends reply to Alice
1495+ let replyData = try XCTUnwrap ( " From Bob " . data ( using: . utf8) )
1496+ let replyMessage = try Session . encrypt (
1497+ data: replyData,
1498+ for: aliceClient. protocolAddress,
1499+ sessionStore: bobClient. sessionStore,
1500+ identityStore: bobClient. identityKeyStore)
1501+
1502+ let decryptReplyMessage = try Session . decrypt (
1503+ message: replyMessage,
1504+ from: bobClient. protocolAddress,
1505+ sessionStore: aliceClient. sessionStore,
1506+ identityStore: aliceClient. identityKeyStore,
1507+ preKeyStore: aliceClient. preKeyStore,
1508+ signedPreKeyStore: aliceClient. signedPreKeyStore)
1509+ XCTAssertEqual ( decryptReplyMessage, replyData)
1510+ // }
1511+
1512+ // We will use this session to imitate loosing of session's changes
1513+ let outdatedSession = try bobClient. sessionStore. loadSession ( for: aliceClient. protocolAddress)
1514+
1515+ // The code below will do further ratchet step (session's changes)
1516+ // {
1517+ let secondData = try XCTUnwrap ( " Alice second message " . data ( using: . utf8) )
1518+ let secondMessage = try Session . encrypt (
1519+ data: secondData,
1520+ for: bobClient. protocolAddress,
1521+ sessionStore: aliceClient. sessionStore,
1522+ identityStore: aliceClient. identityKeyStore)
1523+ let decryptSecondResult = try Session . decrypt (
1524+ message: secondMessage,
1525+ from: aliceClient. protocolAddress,
1526+ sessionStore: bobClient. sessionStore,
1527+ identityStore: bobClient. identityKeyStore,
1528+ preKeyStore: bobClient. preKeyStore,
1529+ signedPreKeyStore: bobClient. signedPreKeyStore)
1530+ XCTAssertEqual ( decryptSecondResult, secondData)
1531+
1532+ let replySecondData = try XCTUnwrap ( " Bob reply on second message " . data ( using: . utf8) )
1533+ let replySecondMessage = try Session . encrypt (
1534+ data: replySecondData,
1535+ for: aliceClient. protocolAddress,
1536+ sessionStore: bobClient. sessionStore,
1537+ identityStore: bobClient. identityKeyStore)
1538+ let decryptReplySecondResult = try Session . decrypt (
1539+ message: replySecondMessage,
1540+ from: bobClient. protocolAddress,
1541+ sessionStore: aliceClient. sessionStore,
1542+ identityStore: aliceClient. identityKeyStore,
1543+ preKeyStore: aliceClient. preKeyStore,
1544+ signedPreKeyStore: aliceClient. signedPreKeyStore)
1545+ XCTAssertEqual ( decryptReplySecondResult, replySecondData)
1546+
1547+ // Actually test
1548+
1549+ // Imitate like we lost last changes made to session
1550+ try bobClient. sessionStore. storeSession (
1551+ try XCTUnwrap ( outdatedSession) ,
1552+ for: aliceClient. protocolAddress)
1553+
1554+ // Test Bob can send message to Alice after lost session changes
1555+ let bobDataAfterLost = try XCTUnwrap ( " Message From Bob after lost " . data ( using: . utf8) )
1556+ let bobMessageAfterLost = try Session . encrypt (
1557+ data: bobDataAfterLost,
1558+ for: aliceClient. protocolAddress,
1559+ sessionStore: bobClient. sessionStore,
1560+ identityStore: bobClient. identityKeyStore)
1561+ let aliceDecryptAfterLost = try Session . decrypt (
1562+ message: bobMessageAfterLost,
1563+ from: bobClient. protocolAddress,
1564+ sessionStore: aliceClient. sessionStore,
1565+ identityStore: aliceClient. identityKeyStore,
1566+ preKeyStore: aliceClient. preKeyStore,
1567+ signedPreKeyStore: aliceClient. signedPreKeyStore)
1568+ XCTAssertEqual ( aliceDecryptAfterLost, bobDataAfterLost)
1569+
1570+ // Test Bob can't receive message from Alice
1571+ let aliceThirdMessageData = try XCTUnwrap ( " Alice third message " . data ( using: . utf8) )
1572+ let aliceThirdMessage = try Session . encrypt (
1573+ data: aliceThirdMessageData,
1574+ for: bobClient. protocolAddress,
1575+ sessionStore: aliceClient. sessionStore,
1576+ identityStore: aliceClient. identityKeyStore)
1577+ do {
1578+ let decryptThirdMessageResult = try Session . decrypt (
1579+ message: aliceThirdMessage,
1580+ from: aliceClient. protocolAddress,
1581+ sessionStore: bobClient. sessionStore,
1582+ identityStore: bobClient. identityKeyStore,
1583+ preKeyStore: bobClient. preKeyStore,
1584+ signedPreKeyStore: bobClient. signedPreKeyStore)
1585+ XCTFail ( " Third message should not be decrypted due loosing session changes " )
1586+ XCTAssertEqual ( decryptThirdMessageResult, aliceThirdMessageData)
1587+ } catch DXError . messageVerificationFailed {
1588+ } catch {
1589+ XCTFail ( " Incorrect error thrown " )
1590+ }
1591+ }
14581592}
14591593
14601594extension SessionTests {
0 commit comments