diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 33e6bb751..f1224df53 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -26,12 +26,12 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v2 - + - name: Setup Rust toolchain uses: actions-rs/toolchain@v1 with: toolchain: stable - + - name: Configure CI cache uses: Swatinem/rust-cache@v2 @@ -40,13 +40,19 @@ jobs: with: command: build args: --all-targets ${{ matrix.cargo_flags }} - + - name: Run tests uses: actions-rs/cargo@v1 with: command: test args: --all-targets ${{ matrix.cargo_flags }} + - name: Run doc tests + uses: actions-rs/cargo@v1 + with: + command: test + args: --doc ${{ matrix.cargo_flags }} + rustfmt: name: rustfmt runs-on: ubuntu-latest @@ -60,7 +66,7 @@ jobs: profile: minimal toolchain: stable components: rustfmt - + - name: Check code format uses: actions-rs/cargo@v1 with: @@ -73,14 +79,14 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v2 - + - name: Setup Rust toolchain uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: stable components: clippy - + - name: Setup CI cache uses: Swatinem/rust-cache@v2 diff --git a/presage/Cargo.toml b/presage/Cargo.toml index ae22f2cb2..bea3bc85e 100644 --- a/presage/Cargo.toml +++ b/presage/Cargo.toml @@ -9,7 +9,7 @@ edition = "2021" libsignal-service = { git = "https://github.com/whisperfish/libsignal-service-rs", rev = "16695d0" } libsignal-service-hyper = { git = "https://github.com/whisperfish/libsignal-service-rs", rev = "16695d0" } -base64 = "0.12" +base64 = "0.21" futures = "0.3" log = "0.4.8" rand = "0.8" @@ -23,3 +23,4 @@ tokio = { version = "1.0", default-features = false, features = ["time"] } [dev-dependencies] quickcheck = "1.0.3" quickcheck_async = "0.1" +presage-store-sled = { path = "../presage-store-sled" } diff --git a/presage/src/manager.rs b/presage/src/manager.rs index 40dbb4cef..b60ce1c8f 100644 --- a/presage/src/manager.rs +++ b/presage/src/manager.rs @@ -153,14 +153,16 @@ impl Manager { /// have to use to send the confirmation code. /// /// ```no_run - /// #[tokio::main] - /// async fn main() -> anyhow::Result<()> { - /// use std::str::FromStr; + /// use std::str::FromStr; + /// + /// use presage::{ + /// prelude::{phonenumber::PhoneNumber, SignalServers}, + /// Manager, RegistrationOptions, + /// }; + /// use presage_store_sled::{MigrationConflictStrategy, SledStore}; /// - /// use presage::{ - /// prelude::{phonenumber::PhoneNumber, SignalServers}, - /// Manager, MigrationConflictStrategy, RegistrationOptions, SledStore, - /// }; + /// #[tokio::main] + /// async fn main() -> Result<(), Box> { /// /// let config_store = /// SledStore::open("/tmp/presage-example", MigrationConflictStrategy::Drop)?; @@ -268,10 +270,11 @@ impl Manager { /// /// ```no_run /// use futures::{channel::oneshot, future, StreamExt}; - /// use presage::{prelude::SignalServers, Manager, MigrationConflictStrategy, SledStore}; + /// use presage::{prelude::SignalServers, Manager}; + /// use presage_store_sled::{MigrationConflictStrategy, SledStore}; /// /// #[tokio::main] - /// async fn main() -> anyhow::Result<()> { + /// async fn main() -> Result<(), Box> { /// let config_store = /// SledStore::open("/tmp/presage-example", MigrationConflictStrategy::Drop)?; /// @@ -867,7 +870,7 @@ impl Manager { /// Starts receiving and storing messages. /// - /// Returns a [Stream] of messages to consume. Messages will also be stored by the implementation of the [MessageStore]. + /// Returns a [futures::Stream] of messages to consume. Messages will also be stored by the implementation of the [Store]. pub async fn receive_messages( &mut self, ) -> Result, Error> { @@ -1028,7 +1031,7 @@ impl Manager { /// The timestamp should be set to now and is used by Signal mobile apps /// to order messages later, and apply reactions. /// - /// This method will automatically update the [DataMessage::expiration_timer] if it is set to + /// This method will automatically update the [DataMessage::expire_timer] if it is set to /// [None] such that the chat will keep the current expire timer. pub async fn send_message( &mut self, @@ -1116,7 +1119,7 @@ impl Manager { /// Sends one message in a group (v2). The `master_key_bytes` is required to have 32 elements. /// - /// This method will automatically update the [DataMessage::expiration_timer] if it is set to + /// This method will automatically update the [DataMessage::expire_timer] if it is set to /// [None] such that the chat will keep the current expire timer. pub async fn send_message_to_group( &mut self, @@ -1512,6 +1515,8 @@ fn save_message(config_store: &mut C, message: Content) -> Result<(), #[cfg(test)] mod tests { + use base64::engine::general_purpose; + use base64::Engine; use libsignal_service::prelude::ProfileKey; use libsignal_service::protocol::KeyPair; use rand::RngCore; @@ -1548,12 +1553,12 @@ mod tests { }, "uuid": "ff9a89d9-8052-4af0-a91d-2a0dfa0c6b95", "password": "HelloWorldOfPasswords", - "signaling_key": base64::encode(signaling_key), + "signaling_key": general_purpose::STANDARD.encode(signaling_key), "device_id": 42, "registration_id": 64, - "private_key": base64::encode(key_pair.private_key.serialize()), - "public_key": base64::encode(key_pair.public_key.serialize()), - "profile_key": base64::encode(profile_key.get_bytes()), + "private_key": general_purpose::STANDARD.encode(key_pair.private_key.serialize()), + "public_key": general_purpose::STANDARD.encode(key_pair.public_key.serialize()), + "profile_key": general_purpose::STANDARD.encode(profile_key.get_bytes()), }); let state: Registered = serde_json::from_value(previous_state).expect("should deserialize"); diff --git a/presage/src/serde.rs b/presage/src/serde.rs index b4df46f16..4beed7371 100644 --- a/presage/src/serde.rs +++ b/presage/src/serde.rs @@ -1,22 +1,45 @@ -pub mod serde_profile_key { +pub(crate) mod serde_profile_key { + + use base64::{engine::general_purpose, Engine}; use libsignal_service::prelude::ProfileKey; use serde::{Deserialize, Deserializer, Serializer}; - pub fn serialize(profile_key: &ProfileKey, serializer: S) -> Result + pub(crate) fn serialize(profile_key: &ProfileKey, serializer: S) -> Result where S: Serializer, { - serializer.serialize_str(&base64::encode(profile_key.bytes)) + serializer.serialize_str(&general_purpose::STANDARD.encode(profile_key.bytes)) } - pub fn deserialize<'de, D>(deserializer: D) -> Result + pub(crate) fn deserialize<'de, D>(deserializer: D) -> Result where D: Deserializer<'de>, { - let bytes: [u8; 32] = base64::decode(String::deserialize(deserializer)?) + let bytes: [u8; 32] = general_purpose::STANDARD + .decode(String::deserialize(deserializer)?) .map_err(serde::de::Error::custom)? .try_into() .map_err(|e: Vec| serde::de::Error::invalid_length(e.len(), &"32 bytes"))?; Ok(ProfileKey::create(bytes)) } + + #[cfg(test)] + mod tests { + use super::*; + + #[test] + fn test_serialize_deserialize() { + let profile_key = ProfileKey { + bytes: *b"kaijpqxdvaiaeaulmsrozckjkgbpjowc", + }; + let mut serializer = serde_json::Serializer::new(Vec::new()); + serialize(&profile_key, &mut serializer).unwrap(); + let json = String::from_utf8(serializer.into_inner()).unwrap(); + assert_eq!(json, "\"a2FpanBxeGR2YWlhZWF1bG1zcm96Y2tqa2dicGpvd2M=\""); + + let mut deserializer = serde_json::Deserializer::from_slice(json.as_bytes()); + let profile_key2: ProfileKey = deserialize(&mut deserializer).unwrap(); + assert_eq!(profile_key.bytes, profile_key2.bytes); + } + } }