Skip to content

Commit 1c8c939

Browse files
committed
Add Argon2id support. Rename generic agreement and signing key derivation functions.
1 parent 78a0975 commit 1c8c939

File tree

4 files changed

+106
-25
lines changed

4 files changed

+106
-25
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ anyhow = "^1.0.0"
2727
hex = "^0.4.3"
2828
ed25519-dalek = { version = "2.1.1", features = ["rand_core"] }
2929
scrypt = { version = "0.11.0", default-features = false }
30+
argon2 = "0.5.3"
3031

3132
[dev-dependencies]
3233
hex-literal = "^0.4.1"

src/argon.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
use argon2::Argon2;
2+
3+
pub fn argon2id(pass: impl AsRef<[u8]>, salt: impl AsRef<[u8]>, output_len: usize) -> Vec<u8> {
4+
let mut output = vec![0u8; output_len];
5+
Argon2::default()
6+
.hash_password_into(pass.as_ref(), salt.as_ref(), &mut output)
7+
.expect("argon2 failed");
8+
output
9+
}
10+
11+
#[cfg(test)]
12+
mod tests {
13+
use super::*;
14+
15+
#[test]
16+
fn test_argon2id_basic() {
17+
let pass = b"password";
18+
let salt = b"example salt";
19+
let output = argon2id(pass, salt, 32);
20+
assert_eq!(output.len(), 32);
21+
// Argon2 should be deterministic for same input
22+
let output2 = argon2id(pass, salt, 32);
23+
assert_eq!(output, output2);
24+
}
25+
26+
#[test]
27+
fn test_argon2id_different_salt() {
28+
let pass = b"password";
29+
let salt1 = b"example salt";
30+
let salt2 = b"example salt2";
31+
let out1 = argon2id(pass, salt1, 32);
32+
let out2 = argon2id(pass, salt2, 32);
33+
assert_ne!(out1, out2);
34+
}
35+
}

src/lib.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,8 @@ mod public_key_encryption;
6060
pub use public_key_encryption::{
6161
x25519_new_private_key_using,
6262
x25519_public_key_from_private_key,
63-
x25519_derive_private_key,
64-
x25519_derive_signing_private_key,
63+
derive_agreement_private_key,
64+
derive_signing_private_key,
6565
x25519_shared_key,
6666
X25519_PRIVATE_KEY_SIZE,
6767
X25519_PUBLIC_KEY_SIZE,
@@ -109,6 +109,9 @@ pub use ed25519_signing::{
109109
mod scrypt;
110110
pub use scrypt::{ scrypt, scrypt_opt };
111111

112+
mod argon;
113+
pub use argon::argon2id;
114+
112115
#[cfg(test)]
113116
mod tests {
114117
#[test]

src/public_key_encryption.rs

Lines changed: 65 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,55 @@
1-
use crate::{hash::hkdf_hmac_sha256, SYMMETRIC_KEY_SIZE};
1+
use crate::{ hash::hkdf_hmac_sha256, SYMMETRIC_KEY_SIZE };
22
use bc_rand::RandomNumberGenerator;
3-
use x25519_dalek::{PublicKey, StaticSecret};
3+
use x25519_dalek::{ PublicKey, StaticSecret };
44

5+
pub const GENERIC_PRIVATE_KEY_SIZE: usize = 32;
6+
pub const GENERIC_PUBLIC_KEY_SIZE: usize = 32;
57
pub const X25519_PRIVATE_KEY_SIZE: usize = 32;
68
pub const X25519_PUBLIC_KEY_SIZE: usize = 32;
79

10+
/// Derive a 32-byte agreement private key from the given key material.
11+
///
12+
/// May be used for key agreement or key encapsulation.
13+
///
14+
/// Enforces domain separation from signing keys by using the "agreement" salt.
15+
pub fn derive_agreement_private_key(
16+
key_material: impl AsRef<[u8]>
17+
) -> [u8; GENERIC_PRIVATE_KEY_SIZE] {
18+
hkdf_hmac_sha256(key_material, "agreement".as_bytes(), GENERIC_PRIVATE_KEY_SIZE)
19+
.try_into()
20+
.unwrap()
21+
}
22+
23+
/// Derive a 32-byte signing private key from the given key material.
24+
///
25+
/// Enforces domain separation from agreement keys by using the "signing" salt.
26+
pub fn derive_signing_private_key(key_material: impl AsRef<[u8]>) -> [u8; GENERIC_PUBLIC_KEY_SIZE] {
27+
hkdf_hmac_sha256(key_material, "signing".as_bytes(), GENERIC_PUBLIC_KEY_SIZE)
28+
.try_into()
29+
.unwrap()
30+
}
31+
832
/// Create a new X25519 private key using the given random number generator.
9-
pub fn x25519_new_private_key_using(rng: &mut impl RandomNumberGenerator) -> [u8; X25519_PRIVATE_KEY_SIZE] {
33+
pub fn x25519_new_private_key_using(
34+
rng: &mut impl RandomNumberGenerator
35+
) -> [u8; X25519_PRIVATE_KEY_SIZE] {
1036
rng.random_data(X25519_PRIVATE_KEY_SIZE).try_into().unwrap()
1137
}
1238

1339
/// Derive a X25519 public key from a private key.
14-
pub fn x25519_public_key_from_private_key(x25519_private_key: &[u8; X25519_PRIVATE_KEY_SIZE]) -> [u8; X25519_PUBLIC_KEY_SIZE] {
40+
pub fn x25519_public_key_from_private_key(
41+
x25519_private_key: &[u8; X25519_PRIVATE_KEY_SIZE]
42+
) -> [u8; X25519_PUBLIC_KEY_SIZE] {
1543
let sk = StaticSecret::from(*x25519_private_key);
1644
let pk = PublicKey::from(&sk);
1745
pk.as_bytes().to_owned()
1846
}
1947

20-
/// Derive an X25519 private key from the given key material.
21-
pub fn x25519_derive_private_key(key_material: impl AsRef<[u8]>) -> [u8; X25519_PRIVATE_KEY_SIZE] {
22-
hkdf_hmac_sha256(key_material, "agreement".as_bytes(), X25519_PRIVATE_KEY_SIZE).try_into().unwrap()
23-
}
24-
25-
/// Derive an X25519 signing private key from the given key material.
26-
pub fn x25519_derive_signing_private_key(key_material: impl AsRef<[u8]>) -> [u8; X25519_PRIVATE_KEY_SIZE] {
27-
hkdf_hmac_sha256(key_material, "signing".as_bytes(), X25519_PRIVATE_KEY_SIZE).try_into().unwrap()
28-
}
29-
3048
/// Compute the shared symmetric key from the given X25519 private and public keys.
31-
pub fn x25519_shared_key(x25519_private_key: &[u8; X25519_PRIVATE_KEY_SIZE], x25519_public_key: &[u8; X25519_PUBLIC_KEY_SIZE]) -> [u8; SYMMETRIC_KEY_SIZE] {
49+
pub fn x25519_shared_key(
50+
x25519_private_key: &[u8; X25519_PRIVATE_KEY_SIZE],
51+
x25519_public_key: &[u8; X25519_PUBLIC_KEY_SIZE]
52+
) -> [u8; SYMMETRIC_KEY_SIZE] {
3253
let sk = StaticSecret::from(*x25519_private_key);
3354
let pk = PublicKey::from(*x25519_public_key);
3455
let shared_secret = sk.diffie_hellman(&pk);
@@ -40,21 +61,39 @@ mod tests {
4061
use bc_rand::make_fake_random_number_generator;
4162
use hex_literal::hex;
4263

43-
use crate::{x25519_new_private_key_using, x25519_public_key_from_private_key, x25519_derive_private_key, x25519_derive_signing_private_key, x25519_shared_key};
64+
use crate::{
65+
derive_agreement_private_key,
66+
derive_signing_private_key,
67+
x25519_new_private_key_using,
68+
x25519_public_key_from_private_key,
69+
x25519_shared_key,
70+
};
4471

4572
#[test]
4673
fn test_x25519_keys() {
4774
let mut rng = make_fake_random_number_generator();
4875
let private_key = x25519_new_private_key_using(&mut rng);
49-
assert_eq!(private_key, hex!("7eb559bbbf6cce2632cf9f194aeb50943de7e1cbad54dcfab27a42759f5e2fed"));
76+
assert_eq!(
77+
private_key,
78+
hex!("7eb559bbbf6cce2632cf9f194aeb50943de7e1cbad54dcfab27a42759f5e2fed")
79+
);
5080
let public_key = x25519_public_key_from_private_key(&private_key);
51-
assert_eq!(public_key, hex!("f1bd7a7e118ea461eba95126a3efef543ebb78439d1574bedcbe7d89174cf025"));
81+
assert_eq!(
82+
public_key,
83+
hex!("f1bd7a7e118ea461eba95126a3efef543ebb78439d1574bedcbe7d89174cf025")
84+
);
5285

53-
let derived_x25519_private_key = x25519_derive_private_key(b"password");
54-
assert_eq!(derived_x25519_private_key, hex!("7b19769132648ff43ae60cbaa696d5be3f6d53e6645db72e2d37516f0729619f"));
86+
let derived_x25519_private_key = derive_agreement_private_key(b"password");
87+
assert_eq!(
88+
derived_x25519_private_key,
89+
hex!("7b19769132648ff43ae60cbaa696d5be3f6d53e6645db72e2d37516f0729619f")
90+
);
5591

56-
let derived_signing_private_key = x25519_derive_signing_private_key(b"password");
57-
assert_eq!(derived_signing_private_key, hex!("05cc550daa75058e613e606d9898fedf029e395911c43273a208b7e0e88e271b"));
92+
let derived_signing_private_key = derive_signing_private_key(b"password");
93+
assert_eq!(
94+
derived_signing_private_key,
95+
hex!("05cc550daa75058e613e606d9898fedf029e395911c43273a208b7e0e88e271b")
96+
);
5897
}
5998

6099
#[test]
@@ -67,6 +106,9 @@ mod tests {
67106
let alice_shared_key = x25519_shared_key(&alice_private_key, &bob_public_key);
68107
let bob_shared_key = x25519_shared_key(&bob_private_key, &alice_public_key);
69108
assert_eq!(alice_shared_key, bob_shared_key);
70-
assert_eq!(alice_shared_key, hex!("1e9040d1ff45df4bfca7ef2b4dd2b11101b40d91bf5bf83f8c83d53f0fbb6c23"));
109+
assert_eq!(
110+
alice_shared_key,
111+
hex!("1e9040d1ff45df4bfca7ef2b4dd2b11101b40d91bf5bf83f8c83d53f0fbb6c23")
112+
);
71113
}
72114
}

0 commit comments

Comments
 (0)