Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Swap libsignal-protocol dependency with libsignal-client #83

Merged
merged 24 commits into from
Apr 30, 2021
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
76758f0
Swap libsignal-protocol dependency with libsignal-client
rubdos Apr 20, 2021
c4110d2
part 1
rubdos Apr 20, 2021
f4615cc
rand 0.7 because of compatibility
rubdos Apr 20, 2021
9e61ecf
part 2
rubdos Apr 20, 2021
adc795d
Merge remote-tracking branch 'upstream/master' into libsignal-client
gferon Apr 26, 2021
04788d8
Progress towards using the new libsignal-protocol in Rust
gferon Apr 26, 2021
5a3531e
Fix HKDF usage
gferon Apr 27, 2021
839baaa
Use dynamic dispatch and fix tests
gferon Apr 27, 2021
3419aa9
Adjust usage of verify_signature
gferon Apr 27, 2021
66b8969
Remove FIXME
gferon Apr 27, 2021
926d062
Last changes before shipping\!
gferon Apr 27, 2021
53dddee
Last changes before shipping!
gferon Apr 27, 2021
c867547
Merge remote-tracking branch 'upstream/master' into libsignal-client
gferon Apr 27, 2021
f1ae701
Use upstream libsignal-client
gferon Apr 27, 2021
1829246
Address review comments
gferon Apr 27, 2021
6e00342
Merge remote-tracking branch 'upstream/master' into libsignal-client
boxdot Apr 29, 2021
0ab27c5
Merge pull request #1 from boxdot/libsignal-client
gferon Apr 29, 2021
4436018
Merge remote-tracking branch 'fork/libsignal-client' into libsignal-c…
gferon Apr 29, 2021
1365782
Use static dispatch
gferon Apr 29, 2021
f6c4df7
Derive clone on ciphers and sender/receiver
gferon Apr 30, 2021
797dead
Fix clippy warnings
gferon Apr 30, 2021
a3be3d5
Add get_sub_device_sessions method to ServiceAddress
gferon Apr 30, 2021
4a5cfa2
Change license to AGPLv3
rubdos Apr 30, 2021
ead122e
Reflect license and copyright changes in README
rubdos Apr 30, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 2 additions & 5 deletions libsignal-service-actix/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,8 @@ version = "0.1.0"
authors = ["Ruben De Smet <[email protected]>"]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
libsignal-service = { path = "../libsignal-service" }
libsignal-protocol = { git = "https://github.com/Michael-F-Bryan/libsignal-protocol-rs" }

awc = { version = "3.0.0-beta.5", features=["rustls"] }
actix = "0.11.1"
Expand All @@ -22,7 +19,7 @@ rustls = "0.19"
url = "2.1"
serde = "1.0"
log = "0.4"
rand = "0.8"
rand = "0.7"

failure = "0.1.5"
thiserror = "1.0"
Expand All @@ -36,6 +33,6 @@ env_logger = "0.8"
image = { version = "0.23", default-features = false, features = ["png"] }
opener = "0.4"
qrcode = "0.12"
rand = "0.8"
rand = "0.7"
structopt = "0.3"
tokio = { version = "1", features=["macros"] }
23 changes: 9 additions & 14 deletions libsignal-service-actix/examples/link.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,16 @@
use failure::Error;
use futures::{channel::mpsc::channel, future, StreamExt};
use image::Luma;
use libsignal_service::configuration::SignalServers;
use libsignal_service::{
configuration::SignalServers, provisioning::LinkingManager,
provisioning::SecondaryDeviceProvisioning, USER_AGENT,
};
use libsignal_service_actix::prelude::AwcPushService;
use log::LevelFilter;
use qrcode::QrCode;
use rand::{distributions::Alphanumeric, Rng, RngCore};
use structopt::StructOpt;

use libsignal_protocol::Context;

use libsignal_service::{
provisioning::LinkingManager, provisioning::SecondaryDeviceProvisioning,
USER_AGENT,
};
use libsignal_service_actix::prelude::AwcPushService;

#[derive(Debug, StructOpt)]
struct Args {
#[structopt(long = "servers", short = "s", default_value = "staging")]
Expand All @@ -36,8 +32,7 @@ async fn main() -> Result<(), Error> {

// generate a random 16 bytes password
let mut rng = rand::rngs::OsRng::default();
let password: Vec<u8> = rng.sample_iter(&Alphanumeric).take(24).collect();
let password = String::from_utf8(password)?;
let password: String = rng.sample_iter(&Alphanumeric).take(24).collect();

// generate a 52 bytes signaling key
let mut signaling_key = [0u8; 52];
Expand All @@ -47,16 +42,16 @@ async fn main() -> Result<(), Error> {
base64::encode(&signaling_key.to_vec())
);

let signal_context = Context::default();

let mut provision_manager: LinkingManager<AwcPushService> =
LinkingManager::new(args.servers, USER_AGENT.into(), password);

let (tx, mut rx) = channel(1);

let mut csprng = rand::thread_rng();

let (fut1, fut2) = future::join(
provision_manager.provision_secondary_device(
&signal_context,
&mut csprng,
signaling_key,
&args.device_name,
tx,
Expand Down
3 changes: 0 additions & 3 deletions libsignal-service-hyper/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,8 @@ version = "0.1.0"
authors = ["Gabriel Féron <[email protected]>"]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
libsignal-service = { path = "../libsignal-service" }
libsignal-protocol = { git = "https://github.com/Michael-F-Bryan/libsignal-protocol-rs" }

async-trait = "0.1"
base64 = "0.13"
Expand Down
7 changes: 4 additions & 3 deletions libsignal-service/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ license = "GPLv3"
readme = "../README.md"

[dependencies]
libsignal-protocol = { git = "https://github.com/Michael-F-Bryan/libsignal-protocol-rs" }
zkgroup = { git = "https://github.com/signalapp/zkgroup" }
libsignal-protocol = { git = "https://github.com/signalapp/libsignal-client" }
rubdos marked this conversation as resolved.
Show resolved Hide resolved
zkgroup = { git = "https://github.com/signalapp/zkgroup", tag = "v0.7.2" }
async-trait = "0.1.30"
url = { version = "2.1.1", features = ["serde"] }
base64 = "0.13"
Expand All @@ -30,7 +30,7 @@ aes = "0.6.0"
aes-gcm = "0.8.0"
aes-ctr = "0.6.0"
block-modes = "0.7.0"
rand = "0.8.0"
rand = "0.7"

uuid = { version = "0.8", features = [ "serde" ] }
phonenumber = "0.3"
Expand All @@ -40,6 +40,7 @@ prost-build = "0.7"

[dev-dependencies]
anyhow = "1.0"
tokio = { version = "1.0", features = [ "macros" ] }

[features]
prefer-e164 = []
110 changes: 65 additions & 45 deletions libsignal-service/src/account_manager.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
use std::collections::HashMap;
use std::convert::{TryFrom, TryInto};
use std::time::SystemTime;

use libsignal_protocol::{
IdentityKeyStore, KeyPair, PreKeyRecord, PreKeyStore, PublicKey,
SignalProtocolError, SignedPreKeyRecord, SignedPreKeyStore,
};
use zkgroup::profiles::ProfileKey;

use crate::{
configuration::{Endpoint, ServiceCredentials},
pre_keys::{PreKeyEntity, PreKeyState},
Expand All @@ -11,16 +21,7 @@ use crate::{
},
};

use std::collections::HashMap;
use std::convert::TryFrom;
use std::time::SystemTime;

use libsignal_protocol::keys::PublicKey;
use libsignal_protocol::{Context, StoreContext};
use zkgroup::profiles::ProfileKey;

pub struct AccountManager<Service> {
context: Context,
service: Service,
profile_key: Option<[u8; 32]>,
}
Expand All @@ -42,7 +43,7 @@ pub enum LinkError {
#[error("TsUrl has an invalid pub_key field")]
InvalidPublicKey,
#[error("Protocol error {0}")]
ProtocolError(#[from] libsignal_protocol::Error),
ProtocolError(#[from] SignalProtocolError),
#[error(transparent)]
ProvisioningError(#[from] ProvisioningError),
}
Expand All @@ -56,15 +57,11 @@ pub struct Profile {

const PRE_KEY_MINIMUM: u32 = 10;
const PRE_KEY_BATCH_SIZE: u32 = 100;
const PRE_KEY_MEDIUM_MAX_VALUE: u32 = 0xFFFFFF;

impl<Service: PushService> AccountManager<Service> {
pub fn new(
context: Context,
service: Service,
profile_key: Option<[u8; 32]>,
) -> Self {
pub fn new(service: Service, profile_key: Option<[u8; 32]>) -> Self {
Self {
context,
service,
profile_key,
}
Expand All @@ -78,9 +75,12 @@ impl<Service: PushService> AccountManager<Service> {
/// Equivalent to Java's RefreshPreKeysJob
///
/// Returns the next pre-key offset and next signed pre-key offset as a tuple.
pub async fn update_pre_key_bundle(
pub async fn update_pre_key_bundle<R: rand::Rng + rand::CryptoRng>(
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
pub async fn update_pre_key_bundle<R: rand::Rng + rand::CryptoRng>(
#[allow(clippy::too_many_arguments)] // See issue https://github.com/Michael-F-Bryan/libsignal-service-rs/issues/92
pub async fn update_pre_key_bundle<R: rand::Rng + rand::CryptoRng>(

&mut self,
store_context: StoreContext,
identity_store: &dyn IdentityKeyStore,
pre_key_store: &mut dyn PreKeyStore,
signed_pre_key_store: &mut dyn SignedPreKeyStore,
rubdos marked this conversation as resolved.
Show resolved Hide resolved
rubdos marked this conversation as resolved.
Show resolved Hide resolved
csprng: &mut R,
pre_keys_offset_id: u32,
next_signed_pre_key_id: u32,
use_last_resort_key: bool,
Expand All @@ -102,31 +102,51 @@ impl<Service: PushService> AccountManager<Service> {
return Ok((pre_keys_offset_id, next_signed_pre_key_id));
}

let pre_keys = libsignal_protocol::generate_pre_keys(
&self.context,
pre_keys_offset_id,
PRE_KEY_BATCH_SIZE,
)?;
let identity_key_pair = store_context.identity_key_pair()?;
let signed_pre_key = libsignal_protocol::generate_signed_pre_key(
&self.context,
&identity_key_pair,
next_signed_pre_key_id,
SystemTime::now(),
)?;

store_context.store_signed_pre_key(&signed_pre_key)?;

let mut pre_key_entities = vec![];
for pre_key in pre_keys {
store_context.store_pre_key(&pre_key)?;
pre_key_entities.push(PreKeyEntity::try_from(pre_key)?);
for i in 0..PRE_KEY_BATCH_SIZE {
let key_pair = KeyPair::generate(csprng);
let pre_key_id =
((pre_keys_offset_id + i) % (PRE_KEY_MEDIUM_MAX_VALUE - 1)) + 1;
let pre_key_record = PreKeyRecord::new(pre_key_id, &key_pair);
pre_key_store
.save_pre_key(pre_key_id, &pre_key_record, None)
.await?;

pre_key_entities.push(PreKeyEntity::try_from(pre_key_record)?);
}

// Generate and store the next signed prekey
let identity_key_pair =
identity_store.get_identity_key_pair(None).await?;
let signed_pre_key_pair = KeyPair::generate(csprng);
let signed_pre_key_public = signed_pre_key_pair.public_key;
let signed_pre_key_signature = identity_key_pair
.private_key()
.calculate_signature(&signed_pre_key_public.serialize(), csprng)?;

let unix_time = SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap();

let signed_prekey_record = SignedPreKeyRecord::new(
next_signed_pre_key_id,
unix_time.as_millis() as u64,
&signed_pre_key_pair,
&signed_pre_key_signature,
);

signed_pre_key_store
.save_signed_pre_key(
next_signed_pre_key_id,
&signed_prekey_record,
None,
)
.await?;

let pre_key_state = PreKeyState {
pre_keys: pre_key_entities,
signed_pre_key: signed_pre_key.into(),
identity_key: identity_key_pair.public(),
signed_pre_key: signed_prekey_record.try_into()?,
identity_key: identity_key_pair.public_key().clone(),
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
identity_key: identity_key_pair.public_key().clone(),
identity_key: identity_key_pair.public_key(),

last_resort_key: if use_last_resort_key {
Some(PreKeyEntity {
key_id: 0x7fffffff,
Expand Down Expand Up @@ -209,7 +229,7 @@ impl<Service: PushService> AccountManager<Service> {
pub async fn link_device(
&mut self,
url: url::Url,
store_context: StoreContext,
identity_store: &dyn IdentityKeyStore,
credentials: ServiceCredentials,
) -> Result<(), LinkError> {
let query: HashMap<_, _> = url.query_pairs().collect();
Expand All @@ -218,10 +238,11 @@ impl<Service: PushService> AccountManager<Service> {
query.get("pub_key").ok_or(LinkError::InvalidPublicKey)?;
let pub_key = base64::decode(&**pub_key)
.map_err(|_e| LinkError::InvalidPublicKey)?;
let pub_key = PublicKey::decode_point(&self.context, &pub_key)
let pub_key = PublicKey::deserialize(&pub_key)
.map_err(|_e| LinkError::InvalidPublicKey)?;

let identity_key_pair = store_context.identity_key_pair()?;
let identity_key_pair =
identity_store.get_identity_key_pair(None).await?;

if credentials.uuid.is_none() {
log::warn!("No local UUID set");
Expand All @@ -231,10 +252,10 @@ impl<Service: PushService> AccountManager<Service> {

let msg = ProvisionMessage {
identity_key_public: Some(
identity_key_pair.public().to_bytes()?.as_slice().to_vec(),
identity_key_pair.public_key().serialize().into_vec(),
),
identity_key_private: Some(
identity_key_pair.private().to_bytes()?.as_slice().to_vec(),
identity_key_pair.private_key().serialize(),
),
number: Some(credentials.e164()),
uuid: credentials.uuid.as_ref().map(|u| u.to_string()),
Expand All @@ -248,8 +269,7 @@ impl<Service: PushService> AccountManager<Service> {
user_agent: None,
};

let cipher =
ProvisioningCipher::from_public(self.context.clone(), pub_key);
let cipher = ProvisioningCipher::from_public(pub_key);

let encrypted = cipher.encrypt(msg)?;
self.send_provisioning_message(ephemeral_id, encrypted)
Expand Down
Loading