mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-10-09 02:54:14 +00:00
Merge branch 'hotfix' into dev
This commit is contained in:
commit
856a67edaa
6 changed files with 115 additions and 6 deletions
|
|
@ -1,5 +1,14 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 0.5.4
|
||||||
|
|
||||||
|
- Fix: Recover broken sessions more reliably
|
||||||
|
|
||||||
|
## 0.5.3
|
||||||
|
|
||||||
|
- Hotfix: Fixed stuck Signal sessions
|
||||||
|
- Improve: Gracefully upgrade all sessions to PQXDH
|
||||||
|
|
||||||
## 0.5.2
|
## 0.5.2
|
||||||
|
|
||||||
- Hotfix: Fixed an infinite loop involving delivery receipts
|
- Hotfix: Fixed an infinite loop involving delivery receipts
|
||||||
|
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
ce49e7d90cd902197f9a9cbc84219d23
|
|
||||||
|
|
@ -136,8 +136,10 @@ class MainCameraController {
|
||||||
try {
|
try {
|
||||||
await cameraControllerTemp?.stopImageStream();
|
await cameraControllerTemp?.stopImageStream();
|
||||||
// ignore: empty_catches
|
// ignore: empty_catches
|
||||||
} catch (e) {}
|
} catch (e) {
|
||||||
|
} finally {
|
||||||
await cameraControllerTemp?.dispose();
|
await cameraControllerTemp?.dispose();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
initCameraStarted = false;
|
initCameraStarted = false;
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ description: "twonly, a privacy-friendly way to connect with friends through sec
|
||||||
|
|
||||||
publish_to: 'none'
|
publish_to: 'none'
|
||||||
|
|
||||||
version: 0.6.0+173
|
version: 0.6.0+175
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ^3.11.0
|
sdk: ^3.11.0
|
||||||
|
|
|
||||||
|
|
@ -420,9 +420,26 @@ impl ApiClient {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// Tungstenite message
|
// Tungstenite message
|
||||||
if let stream_tungstenite::tokio_tungstenite::tungstenite::Message::Binary(bytes) = &*msg {
|
use stream_tungstenite::tokio_tungstenite::tungstenite::Message;
|
||||||
|
match &*msg {
|
||||||
|
Message::Binary(bytes) => {
|
||||||
self_clone.handle_incoming(&connection, bytes).await;
|
self_clone.handle_incoming(&connection, bytes).await;
|
||||||
}
|
}
|
||||||
|
// The server's close code tells why it dropped the
|
||||||
|
// connection, e.g. 1013 (outbound queue full) or 4003
|
||||||
|
// (ack timeouts). The disconnect event does not carry it.
|
||||||
|
Message::Close(frame) => match frame {
|
||||||
|
Some(frame) => tracing::info!(
|
||||||
|
"websocket closed by the server with code {}: {}",
|
||||||
|
u16::from(frame.code),
|
||||||
|
frame.reason
|
||||||
|
),
|
||||||
|
None => tracing::info!(
|
||||||
|
"websocket closed by the server without a code"
|
||||||
|
),
|
||||||
|
},
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Err(_) => break,
|
Err(_) => break,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -972,6 +972,88 @@ mod tests {
|
||||||
assert!(!bob.reset_session("carol", 1, false).await.unwrap());
|
assert!(!bob.reset_session("carol", 1, false).await.unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The user-initiated reset: the record is gone, nothing encrypts for the
|
||||||
|
/// peer until a fresh bundle builds a new session, and that one works.
|
||||||
|
#[tokio::test]
|
||||||
|
async fn hard_reset_deletes_and_allows_rebuilding_a_contact_session() {
|
||||||
|
let (alice, _alice_dir) = create_test_engine("alice").await;
|
||||||
|
let (bob, _bob_dir) = create_test_engine("bob").await;
|
||||||
|
|
||||||
|
alice
|
||||||
|
.process_prekey_bundle("bob".to_string(), 1, bob.generate_bundle().await.unwrap())
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert!(alice.reset_session("bob", 1, true).await.unwrap());
|
||||||
|
assert!(!alice.reset_session("bob", 1, true).await.unwrap());
|
||||||
|
assert!(alice
|
||||||
|
.encrypt_message("bob".to_string(), 1, b"missing".to_vec())
|
||||||
|
.await
|
||||||
|
.is_err());
|
||||||
|
|
||||||
|
alice
|
||||||
|
.process_prekey_bundle("bob".to_string(), 1, bob.generate_bundle().await.unwrap())
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(ping(&alice, &bob, "rebuilt").await.unwrap(), b"rebuilt");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A message decrypted twice is a duplicate, for prekey and whisper
|
||||||
|
/// messages alike, and never reported as an unusable session.
|
||||||
|
#[tokio::test]
|
||||||
|
async fn decrypting_a_message_twice_reports_a_duplicate() {
|
||||||
|
let (alice, _alice_dir) = create_test_engine("alice").await;
|
||||||
|
let (bob, _bob_dir) = create_test_engine("bob").await;
|
||||||
|
|
||||||
|
alice
|
||||||
|
.process_prekey_bundle("bob".to_string(), 1, bob.generate_bundle().await.unwrap())
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let prekey_message = alice
|
||||||
|
.encrypt_message("bob".to_string(), 1, b"first".to_vec())
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
prekey_message.last(),
|
||||||
|
Some(&(CiphertextMessageType::PreKey as u8))
|
||||||
|
);
|
||||||
|
bob.decrypt_message("alice".to_string(), 1, prekey_message.clone())
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let error = bob
|
||||||
|
.decrypt_message("alice".to_string(), 1, prekey_message)
|
||||||
|
.await
|
||||||
|
.unwrap_err();
|
||||||
|
assert!(
|
||||||
|
matches!(error, TwonlyError::SignalDuplicateMessage(_)),
|
||||||
|
"{error}"
|
||||||
|
);
|
||||||
|
|
||||||
|
// After Bob's reply Alice no longer needs a PreKey message.
|
||||||
|
ping(&bob, &alice, "reply").await.unwrap();
|
||||||
|
let whisper_message = alice
|
||||||
|
.encrypt_message("bob".to_string(), 1, b"second".to_vec())
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
whisper_message.last(),
|
||||||
|
Some(&(CiphertextMessageType::Whisper as u8))
|
||||||
|
);
|
||||||
|
bob.decrypt_message("alice".to_string(), 1, whisper_message.clone())
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let error = bob
|
||||||
|
.decrypt_message("alice".to_string(), 1, whisper_message)
|
||||||
|
.await
|
||||||
|
.unwrap_err();
|
||||||
|
assert!(
|
||||||
|
matches!(error, TwonlyError::SignalDuplicateMessage(_)),
|
||||||
|
"{error}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_twonly_api_100_messages() -> std::result::Result<(), Box<dyn std::error::Error>> {
|
async fn test_twonly_api_100_messages() -> std::result::Result<(), Box<dyn std::error::Error>> {
|
||||||
use crate::database::signal::Database;
|
use crate::database::signal::Database;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue