diff --git a/rust/device-transfer/src/lib.rs b/rust/device-transfer/src/lib.rs index c24c54953b..6251152c06 100644 --- a/rust/device-transfer/src/lib.rs +++ b/rust/device-transfer/src/lib.rs @@ -3,7 +3,10 @@ // SPDX-License-Identifier: AGPL-3.0-only // +//! Support logic for Signal's device-to-device transfer feature. + #![deny(unsafe_code)] +#![warn(missing_docs)] use chrono::{Datelike, Duration, Utc}; use picky::key::PrivateKey; @@ -12,9 +15,12 @@ use picky::x509::{certificate::CertificateBuilder, date::UTCDate}; use picky::{hash::HashAlgorithm, signature::SignatureAlgorithm}; use std::fmt; +/// Error types for device transfer. #[derive(Copy, Clone, Debug)] pub enum Error { + /// Failure to decode some provided RSA private key. KeyDecodingFailed, + /// Internal error in device transfer. InternalError(&'static str), } @@ -22,11 +28,12 @@ impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Error::KeyDecodingFailed => write!(f, "Decoding provided RSA private key failed"), - Error::InternalError(s) => write!(f, "Internal error in device tranfer ({})", s), + Error::InternalError(s) => write!(f, "Internal error in device transfer ({})", s), } } } +/// Generate a private key of size `bits` and export to PKCS8 format. pub fn create_rsa_private_key(bits: usize) -> Result, Error> { let key = PrivateKey::generate_rsa(bits) .map_err(|_| Error::InternalError("RSA key generation failed"))?; @@ -35,6 +42,9 @@ pub fn create_rsa_private_key(bits: usize) -> Result, Error> { .map_err(|_| Error::InternalError("Exporting to PKCS8 failed"))?) } +/// Generate a self-signed certificate of name `name`, expiring in `days_to_expire`. +/// +/// `rsa_key_pkcs8` should be the output of [create_rsa_private_key]. pub fn create_self_signed_cert( rsa_key_pkcs8: &[u8], name: &str, diff --git a/rust/protocol/src/address.rs b/rust/protocol/src/address.rs index 38d2fd94d8..18aa59f776 100644 --- a/rust/protocol/src/address.rs +++ b/rust/protocol/src/address.rs @@ -1,26 +1,67 @@ // -// Copyright 2020 Signal Messenger, LLC. +// Copyright 2020-2021 Signal Messenger, LLC. // SPDX-License-Identifier: AGPL-3.0-only // +#![warn(missing_docs)] + +//! A normalized representation of an individual Signal client instance. + +#[cfg(doc)] +use crate::SignalMessage; + use std::fmt; +/// The type used in memory to represent a *device*, i.e. a particular Signal client instance which +/// represents some user. +/// +/// Used in [ProtocolAddress]. +pub type DeviceId = u32; + +/// Represents a unique Signal client instance as `(, )` pair. #[derive(Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)] pub struct ProtocolAddress { name: String, - device_id: u32, + device_id: DeviceId, } impl ProtocolAddress { - pub fn new(name: String, device_id: u32) -> Self { + /// Create a new address. + /// + /// - `name` defines a user's public identity, and therefore must be globally unique to that + /// user. + /// - Each Signal client instance then has its own `device_id`, which must be unique among + /// all clients for that user. + /// + ///``` + /// use libsignal_protocol::{DeviceId, ProtocolAddress}; + /// + /// // This is a unique id for some user, typically a UUID. + /// let user_id: String = "04899A85-4C9E-44CC-8428-A02AB69335F1".to_string(); + /// // Each client instance representing that user has a unique device id. + /// let device_id: DeviceId = 2_u32.into(); + /// let address = ProtocolAddress::new(user_id.clone(), device_id); + /// + /// assert!(address.name() == &user_id); + /// assert!(address.device_id() == device_id); + ///``` + pub fn new(name: String, device_id: DeviceId) -> Self { ProtocolAddress { name, device_id } } + /// A unique identifier for the target user. This is usually a UUID. + #[inline] pub fn name(&self) -> &str { &self.name } - pub fn device_id(&self) -> u32 { + /// An identifier representing a particular Signal client instance to send to. + /// + /// For example, if a user has set up Signal on both their phone and laptop, any [SignalMessage] + /// sent to the user will still only go to a single device. So when a user sends a message to + /// another user at all, they're actually sending a message to *every* device. + #[inline] + pub fn device_id(&self) -> DeviceId { self.device_id } } diff --git a/rust/protocol/src/lib.rs b/rust/protocol/src/lib.rs index e4a0f43917..8d56c0ef8e 100644 --- a/rust/protocol/src/lib.rs +++ b/rust/protocol/src/lib.rs @@ -3,9 +3,25 @@ // SPDX-License-Identifier: AGPL-3.0-only // +//! Rust implementation of the **[Signal Protocol]** for asynchronous +//! forward-secret public-key cryptography. +//! +//! In particular, this library implements operations conforming to the following specifications: +//! - the **[X3DH]** key agreement protocol, +//! - the **[Double Ratchet]** *(Axolotl)* messaging protocol, +//! +//! [Signal Protocol]: https://signal.org/ +//! [X3DH]: https://signal.org/docs/specifications/x3dh/ +//! [Double Ratchet]: https://signal.org/docs/specifications/doubleratchet/ + #![warn(clippy::unwrap_used)] #![deny(unsafe_code)] +// TODO(https://github.com/signalapp/libsignal-client/issues/285): it should be an aspiration to +// eventually warn and then error for public members without docstrings. Also see +// https://doc.rust-lang.org/rustdoc/what-to-include.html for background. +// #![warn(missing_docs)] + mod address; mod consts; mod crypto; @@ -29,7 +45,7 @@ mod utils; use error::Result; pub use { - address::ProtocolAddress, + address::{DeviceId, ProtocolAddress}, curve::{KeyPair, PrivateKey, PublicKey}, error::SignalProtocolError, fingerprint::{DisplayableFingerprint, Fingerprint, ScannableFingerprint},