Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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: 5 additions & 2 deletions .github/workflows/srp.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ jobs:
with:
toolchain: ${{ matrix.rust }}
targets: ${{ matrix.target }}
- run: cargo build --target ${{ matrix.target }} --release --no-default-features
- run: cargo build --target ${{ matrix.target }} --no-default-features

test:
runs-on: ubuntu-latest
Expand All @@ -49,4 +49,7 @@ jobs:
- uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ matrix.rust }}
- run: cargo test --release
- run: cargo test --no-default-features
- run: cargo test
- run: cargo test --all-features
- run: cargo test --all-features --release
4 changes: 4 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 10 additions & 1 deletion srp/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ edition = "2024"
rust-version = "1.85"

[dependencies]
crypto-bigint = { version = "0.7.0-rc.17", features = ["alloc"] }
bigint = { package = "crypto-bigint", version = "0.7.0-rc.17", features = ["alloc"] }
common = { package = "crypto-common", version = "0.2.0-rc.9" }
digest = "0.11.0-rc.5"
subtle = { version = "2.4", default-features = false }

Expand All @@ -26,3 +27,11 @@ getrandom = { version = "0.4.0-rc.0", features = ["sys_rng"] }
hex-literal = "1"
sha1 = "0.11.0-rc.3"
sha2 = "0.11.0-rc.3"

[features]
default = ["getrandom"]
getrandom = ["rand_core", "bigint/getrandom", "common/getrandom"]
rand_core = ["common/rand_core"]

[package.metadata.docs.rs]
all-features = true
45 changes: 12 additions & 33 deletions srp/src/client.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::{errors::AuthError, groups::*, utils::*};
use alloc::vec::Vec;
use bigint::{BoxedUint, ConcatenatingMul, Odd, Resize, modular::BoxedMontyForm};
use core::marker::PhantomData;
use crypto_bigint::{BoxedUint, ConcatenatingMul, Odd, Resize, modular::BoxedMontyForm};
use digest::{Digest, Output};
use subtle::ConstantTimeEq;

Expand Down Expand Up @@ -38,12 +38,15 @@ pub type ClientG4096<D> = Client<G4096, D>;
/// Next send handshake data (username and `a_pub`) to the server and receive
/// `salt` and `b_pub`:
///
/// ```rust
#[cfg_attr(feature = "getrandom", doc = "```")]
#[cfg_attr(not(feature = "getrandom"), doc = "```ignore")]
/// # let client = srp::ClientG2048::<sha2::Sha256>::new();
/// # fn server_response()-> (Vec<u8>, Vec<u8>) { (vec![], vec![]) }
/// // NOTE: requires `getrandom` crate feature is enabled
///
/// use srp::{EphemeralSecret, Generate};
///
/// let mut a = [0u8; 64];
/// // rng.fill_bytes(&mut a);
/// let mut a = EphemeralSecret::generate();
/// let a_pub = client.compute_public_ephemeral(&a);
/// let (salt, b_pub) = server_response();
/// ```
Expand All @@ -58,9 +61,8 @@ pub type ClientG4096<D> = Client<G4096, D>;
/// # let password = b"password";
/// # let salt = b"salt";
/// # let b_pub = b"b_pub";
///
/// let private_key = (username, password, salt);
/// let verifier = client.process_reply_legacy(&a, username, password, salt, b_pub);
/// let verifier = client.process_reply(&a, username, password, salt, b_pub);
/// ```
///
/// Finally verify the server: first generate user proof,
Expand All @@ -71,7 +73,6 @@ pub type ClientG4096<D> = Client<G4096, D>;
/// # let client = srp::ClientG2048::<sha2::Sha256>::new();
/// # let verifier = client.process_reply(b"", b"", b"", b"", b"1").unwrap();
/// # fn send_proof(_: &[u8]) -> Vec<u8> { vec![173, 202, 13, 26, 207, 73, 0, 46, 121, 238, 48, 170, 96, 146, 60, 49, 88, 76, 12, 184, 152, 76, 207, 220, 140, 205, 190, 189, 117, 6, 131, 63] }
///
/// let client_proof = verifier.proof();
/// let server_proof = send_proof(client_proof);
/// verifier.verify_server(&server_proof).unwrap();
Expand All @@ -81,40 +82,21 @@ pub type ClientG4096<D> = Client<G4096, D>;
/// key using `key()` method.
/// ```rust
/// # let client = srp::ClientG2048::<sha2::Sha256>::new();
/// # let verifier = client.process_reply_legacy(b"", b"", b"", b"", b"1").unwrap();
///
/// # let verifier = client.process_reply(b"", b"", b"", b"", b"1").unwrap();
/// verifier.key();
/// ```
///
///
/// Alternatively, you can use `process_reply_rfc5054` method to process parameters
/// according to RFC 5054 if the server is using it. This way, it generates M1 and
/// M2 differently and also the `verify_server` method will return a shared session
/// key in case of correct server data.
///
/// ```ignore
/// # let client = srp::ClientG2048::<sha2::Sha256>::new();
/// # let verifier = client.process_reply_rfc5054(b"", b"", b"", b"", b"1").unwrap();
/// # fn send_proof(_: &[u8]) -> Vec<u8> { vec![10, 215, 214, 40, 136, 200, 122, 121, 68, 160, 38, 32, 85, 82, 128, 30, 199, 194, 126, 222, 61, 55, 2, 28, 120, 181, 155, 102, 141, 65, 17, 64] }
///
/// let client_proof = verifier.proof();
/// let server_proof = send_proof(client_proof);
/// let session_key = verifier.verify_server(&server_proof).unwrap();
/// ```
///
///
/// For user registration on the server first generate salt (e.g. 32 bytes long)
/// and get password verifier which depends on private key. Send username, salt
/// and password verifier over protected channel to protect against
/// Man-in-the-middle (MITM) attack for registration.
/// and password verifier over a protected channel to protect against Man-in-the-middle
/// (MITM) attack for registration.
///
/// ```rust
/// # let client = srp::ClientG2048::<sha2::Sha256>::new();
/// # let username = b"username";
/// # let password = b"password";
/// # let salt = b"salt";
/// # fn send_registration_data(_: &[u8], _: &[u8], _: &[u8]) {}
///
/// let pwd_verifier = client.compute_verifier(username, password, salt);
/// send_registration_data(username, salt, &pwd_verifier);
/// ```
Expand Down Expand Up @@ -272,10 +254,7 @@ impl<G: Group, D: Digest> Client<G, D> {
/// - `a` is a random value,
/// - `username`, `password` is supplied by the user
/// - `salt` and `b_pub` come from the server
#[deprecated(
since = "0.7.0",
note = "please switch to `Client::process_reply_rfc5054`"
)]
#[deprecated(since = "0.7.0", note = "please use `Client::process_reply` (RFC5054)")]
#[allow(deprecated)]
pub fn process_reply_legacy(
&self,
Expand Down
8 changes: 4 additions & 4 deletions srp/src/groups.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@
//!
//! [RFC5054]: https://tools.ietf.org/html/rfc5054

use bigint::{
BoxedUint, Odd, Resize,
modular::{BoxedMontyForm, BoxedMontyParams},
};
use core::{
any,
fmt::{self, Debug},
};
use crypto_bigint::{
BoxedUint, Odd, Resize,
modular::{BoxedMontyForm, BoxedMontyParams},
};

/// Group used for SRP computations.
pub trait Group {
Expand Down
16 changes: 16 additions & 0 deletions srp/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#![no_std]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![doc = include_str!("../README.md")]
#![doc(
html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg",
Expand Down Expand Up @@ -78,7 +79,9 @@ mod client;
mod errors;
mod server;

pub use bigint;
pub use client::{Client, ClientG2048, ClientG3072, ClientG4096, ClientVerifier};
pub use common;
pub use errors::AuthError;
pub use groups::Group;
pub use server::{Server, ServerG2048, ServerG3072, ServerG4096, ServerVerifier};
Expand All @@ -88,3 +91,16 @@ pub use {
client::{ClientG1024, ClientG1536, LegacyClientVerifier},
server::{LegacyServerVerifier, ServerG1024, ServerG1536},
};

#[cfg(feature = "rand_core")]
pub use common::Generate;
#[cfg(feature = "rand_core")]
pub use common::rand_core;

/// 384-bit ephemeral secret (usable as `a` or `b`).
///
/// Should be large enough for use with any of the groups defined in this crate.
pub type EphemeralSecret = [u8; 48];

/// 256-bit salt value.
pub type Salt = [u8; 16];
50 changes: 15 additions & 35 deletions srp/src/server.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::{errors::AuthError, groups::*, utils::*};
use alloc::vec::Vec;
use bigint::{BoxedUint, Odd, Resize, modular::BoxedMontyForm};
use core::marker::PhantomData;
use crypto_bigint::{BoxedUint, Odd, Resize, modular::BoxedMontyForm};
use digest::{Digest, Output};
use subtle::ConstantTimeEq;

Expand All @@ -25,18 +25,20 @@ pub type ServerG4096<D> = Server<G4096, D>;
/// # Usage
/// First receive user's username and public value `a_pub`, retrieve from a
/// database the salt and verifier for a given username. Generate `b` and public value `b_pub`.
//
#[cfg_attr(feature = "getrandom", doc = "```")]
#[cfg_attr(not(feature = "getrandom"), doc = "```ignore")]
/// // NOTE: requires `getrandom` crate feature is enabled
///
///
/// ```rust
/// use sha2::Sha256;
/// use srp::{EphemeralSecret, Generate};
/// # fn get_client_request()-> (Vec<u8>, Vec<u8>) { (vec![], vec![])}
/// # fn get_user(_: &[u8])-> (Vec<u8>, Vec<u8>) { (vec![], vec![])}
///
/// let server = srp::ServerG2048::<Sha256>::new();
/// let (username, a_pub) = get_client_request();
/// let (salt, v) = get_user(&username);
/// let mut b = [0u8; 64];
/// // rng.fill_bytes(&mut b);
/// let mut b = EphemeralSecret::generate();
/// let b_pub = server.compute_public_ephemeral(&b, &v);
/// ```
///
Expand All @@ -49,53 +51,34 @@ pub type ServerG4096<D> = Server<G4096, D>;
/// # fn get_client_response() -> Vec<u8> { vec![1] }
/// # let b = [0u8; 64];
/// # let v = b"";
///
/// # let username = b"user";
/// # let salt = b"nacl";
/// let a_pub = get_client_response();
/// let verifier = server.process_reply_legacy(&b, v, &a_pub).unwrap();
/// let verifier = server.process_reply(username, salt, &b, v, &a_pub).unwrap();
/// ```
///
///
/// And finally receive user proof, verify it and send server proof in the
/// reply:
///
/// ```rust
/// ```ignore
/// # let server = srp::ServerG2048::<sha2::Sha256>::new();
/// # let verifier = server.process_reply_legacy(b"", b"", b"1").unwrap();
/// # let verifier = server.process_reply(b"", b"", b"", b"", b"1").unwrap();
/// # fn get_client_proof()-> Vec<u8> { vec![26, 80, 8, 243, 111, 162, 238, 171, 208, 237, 207, 46, 46, 137, 44, 213, 105, 208, 84, 224, 244, 216, 103, 145, 14, 103, 182, 56, 242, 4, 179, 57] };
/// # fn send_proof(_: &[u8]) { };
///
/// let client_proof = get_client_proof();
/// verifier.verify_client(&client_proof).unwrap();
/// send_proof(verifier.proof());
/// ```
///
///
/// `key` contains shared secret key between user and the server. You can extract shared secret
/// key using `key()` method.
/// ```rust
/// # let server = srp::ServerG2048::<sha2::Sha256>::new();
/// # let verifier = server.process_reply_legacy(b"", b"", b"1").unwrap();
///
/// # let verifier = server.process_reply(b"", b"", b"", b"", b"1").unwrap();
/// verifier.key();
/// ```
///
///
/// Alternatively, you can use `process_reply_rfc5054` method to process parameters
/// according to RFC 5054 if the client is using it. You need to pass `username` and
/// `salt` in addition to other parameters to this method. This way, it generates M1
/// and M2 differently and also the `verify_client` method will return a shared session
/// key in case of correct server data.
///
/// ```ident
/// # let server = srp::ServerG2048::<sha2::Sha256>::new();
/// # let verifier = server.process_reply_rfc5054(b"", b"", b"", b"", b"1").unwrap();
/// # fn get_client_proof()-> Vec<u8> { vec![53, 91, 252, 129, 223, 201, 97, 145, 208, 243, 229, 232, 20, 118, 108, 126, 244, 68, 237, 38, 121, 24, 181, 53, 155, 103, 134, 44, 107, 204, 56, 50] };
/// # fn send_proof(_: &[u8]) { };
///
/// let client_proof = get_client_proof();
/// let session_key = verifier.verify_client(&client_proof).unwrap();
/// send_proof(verifier.proof());
/// ```
#[derive(Debug)]
pub struct Server<G: Group, D: Digest> {
g: BoxedMontyForm,
Expand Down Expand Up @@ -208,10 +191,7 @@ impl<G: Group, D: Digest> Server<G, D> {
/// # Params
/// - `b` is a random value,
/// - `v` is the provided during initial user registration
#[deprecated(
since = "0.7.0",
note = "please switch to `Server::process_reply_rfc5054`"
)]
#[deprecated(since = "0.7.0", note = "please use `Server::process_reply` (RFC5054)")]
#[allow(deprecated)]
pub fn process_reply_legacy(
&self,
Expand Down
2 changes: 1 addition & 1 deletion srp/src/utils.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use alloc::vec::Vec;
use crypto_bigint::{
use bigint::{
BoxedUint, Resize,
modular::{BoxedMontyForm, BoxedMontyParams},
};
Expand Down
2 changes: 1 addition & 1 deletion srp/tests/bad_public.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crypto_bigint::BoxedUint;
use bigint::BoxedUint;
use sha1::Sha1;
use srp::{ClientG2048, ServerG2048};

Expand Down
2 changes: 1 addition & 1 deletion srp/tests/rfc5054.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crypto_bigint::BoxedUint;
use bigint::BoxedUint;
use hex_literal::hex;
use sha1::Sha1;
use srp::utils::{compute_k, compute_u};
Expand Down