Skip to content

Commit

Permalink
Fix linking as secondary device (#271)
Browse files Browse the repository at this point in the history
Co-authored-by: Boxdot <[email protected]>
  • Loading branch information
gferon and boxdot authored Dec 17, 2023
1 parent 9f81c52 commit 9af1f42
Show file tree
Hide file tree
Showing 7 changed files with 430 additions and 451 deletions.
98 changes: 0 additions & 98 deletions libsignal-service-actix/examples/link.rs

This file was deleted.

76 changes: 28 additions & 48 deletions libsignal-service/src/account_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@ use libsignal_protocol::{
PreKeyRecord, PrivateKey, ProtocolStore, PublicKey, SignalProtocolError,
SignedPreKeyRecord,
};
use prost::Message;
use serde::{Deserialize, Serialize};
use sha2::Sha256;
use zkgroup::profiles::ProfileKey;

use crate::pre_keys::KyberPreKeyEntity;
use crate::proto::DeviceName;
use crate::push_service::{AvatarWrite, RecaptchaAttributes, ServiceIdType};
use crate::ServiceAddress;
use crate::{
Expand All @@ -28,7 +30,7 @@ use crate::{
push_service::{
AccountAttributes, HttpAuthOverride, PushService, ServiceError,
},
utils::{serde_base64, serde_public_key},
utils::serde_base64,
};

pub struct AccountManager<Service> {
Expand Down Expand Up @@ -259,8 +261,6 @@ impl<Service: PushService> AccountManager<Service> {
destination: &str,
env: ProvisionEnvelope,
) -> Result<(), ServiceError> {
use prost::Message;

#[derive(serde::Serialize)]
struct ProvisioningMessage {
body: String,
Expand Down Expand Up @@ -465,15 +465,12 @@ impl<Service: PushService> AccountManager<Service> {
device_name: &str,
public_key: &PublicKey,
) -> Result<(), ServiceError> {
let encrypted_device_name: DeviceName = encrypt_device_name(
let encrypted_device_name = encrypt_device_name(
&mut rand::thread_rng(),
device_name,
public_key,
)?;

let encrypted_device_name_proto: crate::proto::DeviceName =
encrypted_device_name.clone().into_proto()?;

#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
struct Data {
Expand All @@ -488,9 +485,7 @@ impl<Service: PushService> AccountManager<Service> {
&[],
HttpAuthOverride::NoOverride,
Data {
device_name: prost::Message::encode_to_vec(
&encrypted_device_name_proto,
),
device_name: encrypted_device_name.encode_to_vec(),
},
)
.await?;
Expand Down Expand Up @@ -526,30 +521,6 @@ impl<Service: PushService> AccountManager<Service> {
}
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DeviceName {
#[serde(with = "serde_public_key")]
ephemeral_public: PublicKey,
#[serde(with = "serde_base64")]
synthetic_iv: Vec<u8>,
#[serde(with = "serde_base64")]
ciphertext: Vec<u8>,
}

impl DeviceName {
pub(crate) fn into_proto(
self,
) -> Result<crate::proto::DeviceName, SignalProtocolError> {
Ok(crate::proto::DeviceName {
ephemeral_public: Some(
self.ephemeral_public.public_key_bytes()?.to_vec(),
),
synthetic_iv: Some(self.synthetic_iv.to_vec()),
ciphertext: Some(self.ciphertext.clone()),
})
}
}

fn calculate_hmac256(
mac_key: &[u8],
ciphertext: &[u8],
Expand Down Expand Up @@ -586,24 +557,33 @@ pub fn encrypt_device_name<R: rand::Rng + rand::CryptoRng>(
);
cipher.apply_keystream(&mut ciphertext);

Ok(DeviceName {
ephemeral_public: ephemeral_key_pair.public_key,
synthetic_iv,
ciphertext,
})
let device_name = DeviceName {
ephemeral_public: Some(
ephemeral_key_pair.public_key.serialize().to_vec(),
),
synthetic_iv: Some(synthetic_iv.to_vec()),
ciphertext: Some(ciphertext),
};

Ok(device_name)
}

pub fn decrypt_device_name(
private_key: &PrivateKey,
device_name: &DeviceName,
) -> Result<String, ServiceError> {
let DeviceName {
ephemeral_public,
synthetic_iv,
ciphertext,
} = device_name;
ephemeral_public: Some(ephemeral_public),
synthetic_iv: Some(synthetic_iv),
ciphertext: Some(ciphertext),
} = device_name
else {
return Err(ServiceError::InvalidDeviceName);
};

let ephemeral_public = PublicKey::deserialize(ephemeral_public)?;

let master_secret = private_key.calculate_agreement(ephemeral_public)?;
let master_secret = private_key.calculate_agreement(&ephemeral_public)?;
let key2 = calculate_hmac256(&master_secret, b"cipher")?;
let cipher_key = calculate_hmac256(&key2, synthetic_iv)?;

Expand Down Expand Up @@ -661,11 +641,11 @@ mod tests {
)?)?;

let device_name = DeviceName {
ephemeral_public: ephemeral_public_key,
synthetic_iv: base64::decode("86gekHGmltnnZ9QARhiFcg==")?,
ciphertext: base64::decode(
ephemeral_public: Some(ephemeral_public_key.serialize().to_vec()),
synthetic_iv: Some(base64::decode("86gekHGmltnnZ9QARhiFcg==")?),
ciphertext: Some(base64::decode(
"MtJ9/9KBWLBVAxfZJD4pLKzP4q+iodRJeCc+/A==",
)?,
)?),
};

let decrypted_device_name =
Expand Down
89 changes: 86 additions & 3 deletions libsignal-service/src/pre_keys.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,48 @@
use std::convert::TryFrom;
use std::{convert::TryFrom, time::SystemTime};

use crate::utils::{serde_base64, serde_public_key};
use libsignal_protocol::{
error::SignalProtocolError, GenericSignedPreKey, KyberPreKeyRecord,
PreKeyRecord, PublicKey, SignedPreKeyRecord,
error::SignalProtocolError, kem, GenericSignedPreKey, KeyPair,
KyberPreKeyRecord, KyberPreKeyStore, PreKeyRecord, PreKeyStore, PublicKey,
SignedPreKeyRecord, SignedPreKeyStore,
};

use serde::{Deserialize, Serialize};

/// Stores the ID of keys published ahead of time
///
/// <https://signal.org/docs/specifications/x3dh/>
pub trait PreKeysStore:
PreKeyStore + SignedPreKeyStore + KyberPreKeyStore
{
/// ID of the next pre key
fn pre_keys_offset_id(&self) -> Result<u32, SignalProtocolError>;

/// ID of the next signed pre key
fn next_signed_pre_key_id(&self) -> Result<u32, SignalProtocolError>;

/// ID of the next PQ pre key
fn next_pq_pre_key_id(&self) -> Result<u32, SignalProtocolError>;

/// set the ID of the next pre key
fn set_pre_keys_offset_id(
&mut self,
id: u32,
) -> Result<(), SignalProtocolError>;

/// set the ID of the next signed pre key
fn set_next_signed_pre_key_id(
&mut self,
id: u32,
) -> Result<(), SignalProtocolError>;

/// set the ID of the next PQ pre key
fn set_next_pq_pre_key_id(
&mut self,
id: u32,
) -> Result<(), SignalProtocolError>;
}

#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct PreKeyEntity {
Expand Down Expand Up @@ -92,3 +127,51 @@ pub struct PreKeyState {
pub pq_last_resort_key: Option<KyberPreKeyEntity>,
pub pq_pre_keys: Vec<KyberPreKeyEntity>,
}

pub(crate) async fn generate_last_resort_kyber_key<S: PreKeysStore>(
store: &mut S,
identity_key: &KeyPair,
) -> Result<KyberPreKeyRecord, SignalProtocolError> {
let id = store.next_pq_pre_key_id()?;
let id = id.max(1); // TODO: Hack, keys start with 1

let record = KyberPreKeyRecord::generate(
kem::KeyType::Kyber1024,
id.into(),
&identity_key.private_key,
)?;

store.save_kyber_pre_key(id.into(), &record).await?;
store.set_next_pq_pre_key_id(id + 1)?;

Ok(record)
}

pub(crate) async fn generate_signed_pre_key<
S: PreKeysStore,
R: rand::Rng + rand::CryptoRng,
>(
store: &mut S,
csprng: &mut R,
identity_key: &KeyPair,
) -> Result<SignedPreKeyRecord, SignalProtocolError> {
let id = store.next_signed_pre_key_id()?;
let id = id.max(1); // TODO: Hack, keys start with 1

let key_pair = KeyPair::generate(csprng);
let signature = identity_key
.private_key
.calculate_signature(&key_pair.public_key.serialize(), csprng)?;

let unix_time = SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap()
.as_millis() as u64;
let record =
SignedPreKeyRecord::new(id.into(), unix_time, &key_pair, &signature);

store.save_signed_pre_key(id.into(), &record).await?;
store.set_next_signed_pre_key_id(id + 1)?;

Ok(record)
}
2 changes: 1 addition & 1 deletion libsignal-service/src/provisioning/cipher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ impl ProvisioningCipher {
.expect("initalization of CBC/AES/PKCS7");
let input = cipher.decrypt_vec(cipher_text).map_err(|e| {
ProvisioningError::InvalidData {
reason: format!("CBC/Padding error: {:?}", e),
reason: format!("CBC/Padding error: {:?}", e).into(),
}
})?;

Expand Down
Loading

0 comments on commit 9af1f42

Please sign in to comment.