SIGMA-I Key Exchange (#378)
* Move `KeGroup` to `KeyExchange::Group` - Introduce `KeyExchange::Hash`, which separates the OPRF hash from the one used in `KeyExchange`. - Remove `De/Serialize` requirement on key exchange messages and states, which forced a lot of where bounds on downstream users. - Rename `KeGroup` to `Group`. - Replace `D` generic for hash with `H`. * Use `voprf::derive_key()` directly * Implement SIGMA-I key exchange * Improve `KeyExchange` for SIGMA-I and Ed25519 * Implement EdDSA * Un-qualify some method calls * SIGMA-I: only include client identity in client mac * SIGMA-I: include server mac in client signature * Expose key exchange types in `crate` & move modules * Implement Ed25519ph * Document `ed25519` crate feature * Remove `ristretto255-voprf` crate feature * Adjust CI crate feature testing * Fix Rustdoc * Remove unnecessary generic parameters from SIGMA-I * Properly mark to-do's with TODO * Assorted fixes * SIGMA-I: include context in signature * SIGMA-I: include identifiers in signature * Merge `ServerLoginStart/FinishParameters` * Re-export more necessary types * More carefully expose types * Add ECDSA test * SIGMA-I: share context hashing * De-duplicate client static public key storage * Hide `KeyExchange` better * Use the correct hash in the root documentation * Bump `derive-where` * Format documentation examples a bit further * Add remote OPRF seed documentation * Rename `deserialize_key_pair` to `deserialize_take_key_pair` * Add more key tests * Remove `SharedSecret` trait * SIGMA-I refactor message API * Share more implementation between 3DH and SIGMA-I * Remove unnecessary zero scalar check for Curve25519 * Use correct hash in test * Add some more TODOs * Exclude `tests` folder from Cargo publishing * Enable missing dependencies * Use right crate for testing Ed25519 * Remove unnecessary `Sized` constraints * Remove unnecessary `ecdsa` crate features * Move signature de/serialization to trait methods * Nit: move import to appropriate location * Add warning to SIGMA-I
This commit is contained in:
+16
-12
@@ -13,10 +13,12 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
backend_feature:
|
backend_feature:
|
||||||
- --features ristretto255-voprf
|
|
||||||
-
|
-
|
||||||
- --features curve25519,ristretto255-voprf
|
- --features ristretto255
|
||||||
- --features curve25519
|
- --features curve25519
|
||||||
|
- --features ecdsa
|
||||||
|
- --features ed25519
|
||||||
|
- --features ristretto255,curve25519,ecdsa,ed25519
|
||||||
frontend_feature:
|
frontend_feature:
|
||||||
-
|
-
|
||||||
- --features argon2
|
- --features argon2
|
||||||
@@ -69,10 +71,12 @@ jobs:
|
|||||||
# 32-bit x86
|
# 32-bit x86
|
||||||
- i686-unknown-linux-gnu
|
- i686-unknown-linux-gnu
|
||||||
backend_feature:
|
backend_feature:
|
||||||
- --features ristretto255-voprf
|
|
||||||
-
|
-
|
||||||
- curve25519,ristretto255-voprf
|
- --features ristretto255
|
||||||
- curve25519
|
- --features curve25519
|
||||||
|
- --features ecdsa
|
||||||
|
- --features ed25519
|
||||||
|
- --features ristretto255,curve25519,ecdsa,ed25519
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@main
|
- uses: actions/checkout@main
|
||||||
- uses: hecrj/setup-rust-action@v2
|
- uses: hecrj/setup-rust-action@v2
|
||||||
@@ -140,10 +144,12 @@ jobs:
|
|||||||
# for any no_std target
|
# for any no_std target
|
||||||
- thumbv6m-none-eabi
|
- thumbv6m-none-eabi
|
||||||
backend_feature:
|
backend_feature:
|
||||||
- ristretto255-voprf
|
|
||||||
-
|
-
|
||||||
- curve25519,ristretto255-voprf
|
- ristretto255
|
||||||
- curve25519
|
- curve25519
|
||||||
|
- ecdsa
|
||||||
|
- ed25519
|
||||||
|
- ristretto255,curve25519,ecdsa,ed25519
|
||||||
frontend_feature:
|
frontend_feature:
|
||||||
- argon2
|
- argon2
|
||||||
- serde
|
- serde
|
||||||
@@ -160,10 +166,8 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
backend_feature:
|
backend_feature:
|
||||||
- --features ristretto255-voprf
|
- --features ristretto255
|
||||||
-
|
-
|
||||||
- --features curve25519,ristretto255-voprf
|
|
||||||
- --features curve25519
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout sources
|
- name: Checkout sources
|
||||||
uses: actions/checkout@main
|
uses: actions/checkout@main
|
||||||
@@ -200,7 +204,7 @@ jobs:
|
|||||||
uses: actions-rs/cargo@v1
|
uses: actions-rs/cargo@v1
|
||||||
with:
|
with:
|
||||||
command: clippy
|
command: clippy
|
||||||
args: --all-targets --features argon2,std,curve25519 -- -D warnings
|
args: --all-targets --features argon2,std,curve25519,ecdsa,ed25519 -- -D warnings
|
||||||
|
|
||||||
- name: Run cargo doc
|
- name: Run cargo doc
|
||||||
uses: actions-rs/cargo@v1
|
uses: actions-rs/cargo@v1
|
||||||
@@ -208,7 +212,7 @@ jobs:
|
|||||||
RUSTDOCFLAGS: -D warnings
|
RUSTDOCFLAGS: -D warnings
|
||||||
with:
|
with:
|
||||||
command: doc
|
command: doc
|
||||||
args: --no-deps --document-private-items --features argon2,std,curve25519
|
args: --no-deps --document-private-items --features argon2,std,curve25519,ecdsa,ed25519
|
||||||
|
|
||||||
format:
|
format:
|
||||||
name: cargo fmt
|
name: cargo fmt
|
||||||
|
|||||||
+20
-4
@@ -3,6 +3,7 @@ authors = ["Kevin Lewi <[email protected]>", "François Garillot <[email protected]>"]
|
|||||||
categories = ["no-std"]
|
categories = ["no-std"]
|
||||||
description = "An implementation of the OPAQUE password-authenticated key exchange protocol"
|
description = "An implementation of the OPAQUE password-authenticated key exchange protocol"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
exclude = ["/src/tests/"]
|
||||||
keywords = ["cryptography", "crypto", "opaque", "passwords", "authentication"]
|
keywords = ["cryptography", "crypto", "opaque", "passwords", "authentication"]
|
||||||
license = "Apache-2.0 OR MIT"
|
license = "Apache-2.0 OR MIT"
|
||||||
name = "opaque-ke"
|
name = "opaque-ke"
|
||||||
@@ -14,12 +15,15 @@ version = "3.0.0"
|
|||||||
[features]
|
[features]
|
||||||
argon2 = ["dep:argon2"]
|
argon2 = ["dep:argon2"]
|
||||||
curve25519 = ["dep:curve25519-dalek"]
|
curve25519 = ["dep:curve25519-dalek"]
|
||||||
default = ["ristretto255-voprf", "serde"]
|
default = ["ristretto255", "serde"]
|
||||||
ristretto255 = ["dep:curve25519-dalek", "voprf/ristretto255"]
|
ecdsa = ["dep:ecdsa", "dep:rfc6979"]
|
||||||
ristretto255-voprf = ["ristretto255", "voprf/ristretto255-ciphersuite"]
|
ed25519 = ["dep:curve25519-dalek", "dep:ed25519-dalek"]
|
||||||
|
ristretto255 = ["dep:curve25519-dalek", "voprf/ristretto255-ciphersuite"]
|
||||||
serde = [
|
serde = [
|
||||||
"dep:serde",
|
"dep:serde",
|
||||||
"curve25519-dalek?/serde",
|
"curve25519-dalek?/serde",
|
||||||
|
"ecdsa?/serde",
|
||||||
|
"ed25519-dalek?/serde",
|
||||||
"generic-array/serde",
|
"generic-array/serde",
|
||||||
"voprf/serde",
|
"voprf/serde",
|
||||||
"zeroize/serde",
|
"zeroize/serde",
|
||||||
@@ -33,14 +37,23 @@ argon2 = { version = "0.5", default-features = false, features = [
|
|||||||
curve25519-dalek = { version = "4", default-features = false, features = [
|
curve25519-dalek = { version = "4", default-features = false, features = [
|
||||||
"zeroize",
|
"zeroize",
|
||||||
], optional = true }
|
], optional = true }
|
||||||
derive-where = { version = "1.3", features = ["zeroize-on-drop"] }
|
derive-where = { version = "1.4", features = ["zeroize-on-drop"] }
|
||||||
digest = "0.10"
|
digest = "0.10"
|
||||||
displaydoc = { version = "0.2", default-features = false }
|
displaydoc = { version = "0.2", default-features = false }
|
||||||
|
ecdsa = { version = "0.16", default-features = false, features = [
|
||||||
|
"arithmetic",
|
||||||
|
"hazmat",
|
||||||
|
], optional = true }
|
||||||
|
ed25519-dalek = { version = "2", default-features = false, features = [
|
||||||
|
"digest",
|
||||||
|
"hazmat",
|
||||||
|
], optional = true }
|
||||||
elliptic-curve = { version = "0.13", features = ["hash2curve", "sec1"] }
|
elliptic-curve = { version = "0.13", features = ["hash2curve", "sec1"] }
|
||||||
generic-array = "0.14"
|
generic-array = "0.14"
|
||||||
hkdf = "0.12"
|
hkdf = "0.12"
|
||||||
hmac = "0.12"
|
hmac = "0.12"
|
||||||
rand = { version = "0.8", default-features = false }
|
rand = { version = "0.8", default-features = false }
|
||||||
|
rfc6979 = { version = "0.4", optional = true }
|
||||||
serde = { version = "1", default-features = false, features = [
|
serde = { version = "1", default-features = false, features = [
|
||||||
"derive",
|
"derive",
|
||||||
], optional = true }
|
], optional = true }
|
||||||
@@ -60,6 +73,7 @@ cryptoki = "0.9"
|
|||||||
elliptic-curve = { version = "0.13", features = ["alloc", "pkcs8"] }
|
elliptic-curve = { version = "0.13", features = ["alloc", "pkcs8"] }
|
||||||
hex = "0.4"
|
hex = "0.4"
|
||||||
p256 = { version = "0.13", default-features = false, features = [
|
p256 = { version = "0.13", default-features = false, features = [
|
||||||
|
"ecdsa",
|
||||||
"hash2curve",
|
"hash2curve",
|
||||||
"pkcs8",
|
"pkcs8",
|
||||||
"voprf",
|
"voprf",
|
||||||
@@ -74,9 +88,11 @@ p521 = { version = "0.13.3", default-features = false, features = [
|
|||||||
"pkcs8",
|
"pkcs8",
|
||||||
"voprf",
|
"voprf",
|
||||||
] }
|
] }
|
||||||
|
paste = "1"
|
||||||
proptest = "1"
|
proptest = "1"
|
||||||
rand = "0.8"
|
rand = "0.8"
|
||||||
regex = "1"
|
regex = "1"
|
||||||
|
sha2 = { version = "0.10", default-features = false }
|
||||||
thiserror = "2"
|
thiserror = "2"
|
||||||
# MSRV
|
# MSRV
|
||||||
rustyline = "15"
|
rustyline = "15"
|
||||||
|
|||||||
+12
-9
@@ -23,16 +23,14 @@ struct Default;
|
|||||||
#[cfg(feature = "ristretto255")]
|
#[cfg(feature = "ristretto255")]
|
||||||
impl CipherSuite for Default {
|
impl CipherSuite for Default {
|
||||||
type OprfCs = opaque_ke::Ristretto255;
|
type OprfCs = opaque_ke::Ristretto255;
|
||||||
type KeGroup = opaque_ke::Ristretto255;
|
type KeyExchange = opaque_ke::TripleDh<opaque_ke::Ristretto255, sha2::Sha512>;
|
||||||
type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDh;
|
|
||||||
type Ksf = opaque_ke::ksf::Identity;
|
type Ksf = opaque_ke::ksf::Identity;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "ristretto255"))]
|
#[cfg(not(feature = "ristretto255"))]
|
||||||
impl CipherSuite for Default {
|
impl CipherSuite for Default {
|
||||||
type OprfCs = p256::NistP256;
|
type OprfCs = p256::NistP256;
|
||||||
type KeGroup = p256::NistP256;
|
type KeyExchange = opaque_ke::TripleDh<p256::NistP256, sha2::Sha256>;
|
||||||
type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDh;
|
|
||||||
type Ksf = opaque_ke::ksf::Identity;
|
type Ksf = opaque_ke::ksf::Identity;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,7 +185,7 @@ fn server_login_start_real(c: &mut Criterion) {
|
|||||||
Some(password_file.clone()),
|
Some(password_file.clone()),
|
||||||
client_login_start_result.clone().message,
|
client_login_start_result.clone().message,
|
||||||
username,
|
username,
|
||||||
ServerLoginStartParameters::default(),
|
ServerLoginParameters::default(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
})
|
})
|
||||||
@@ -209,7 +207,7 @@ fn server_login_start_fake(c: &mut Criterion) {
|
|||||||
None,
|
None,
|
||||||
client_login_start_result.clone().message,
|
client_login_start_result.clone().message,
|
||||||
username,
|
username,
|
||||||
ServerLoginStartParameters::default(),
|
ServerLoginParameters::default(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
})
|
})
|
||||||
@@ -246,7 +244,7 @@ fn client_login_finish(c: &mut Criterion) {
|
|||||||
Some(password_file),
|
Some(password_file),
|
||||||
client_login_start_result.clone().message,
|
client_login_start_result.clone().message,
|
||||||
username,
|
username,
|
||||||
ServerLoginStartParameters::default(),
|
ServerLoginParameters::default(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -256,6 +254,7 @@ fn client_login_finish(c: &mut Criterion) {
|
|||||||
.clone()
|
.clone()
|
||||||
.state
|
.state
|
||||||
.finish(
|
.finish(
|
||||||
|
&mut rng,
|
||||||
password,
|
password,
|
||||||
server_login_start.clone().message,
|
server_login_start.clone().message,
|
||||||
ClientLoginFinishParameters::default(),
|
ClientLoginFinishParameters::default(),
|
||||||
@@ -295,12 +294,13 @@ fn server_login_finish(c: &mut Criterion) {
|
|||||||
Some(password_file),
|
Some(password_file),
|
||||||
client_login_start_result.clone().message,
|
client_login_start_result.clone().message,
|
||||||
username,
|
username,
|
||||||
ServerLoginStartParameters::default(),
|
ServerLoginParameters::default(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let client_login_finish_result = client_login_start_result
|
let client_login_finish_result = client_login_start_result
|
||||||
.state
|
.state
|
||||||
.finish(
|
.finish(
|
||||||
|
&mut rng,
|
||||||
password,
|
password,
|
||||||
server_login_start_result.clone().message,
|
server_login_start_result.clone().message,
|
||||||
ClientLoginFinishParameters::default(),
|
ClientLoginFinishParameters::default(),
|
||||||
@@ -312,7 +312,10 @@ fn server_login_finish(c: &mut Criterion) {
|
|||||||
server_login_start_result
|
server_login_start_result
|
||||||
.clone()
|
.clone()
|
||||||
.state
|
.state
|
||||||
.finish(client_login_finish_result.clone().message)
|
.finish(
|
||||||
|
client_login_finish_result.clone().message,
|
||||||
|
ServerLoginParameters::default(),
|
||||||
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
doc-valid-idents = ["HashEdDSA", "PureEdDSA", ".."]
|
||||||
@@ -38,7 +38,7 @@ use opaque_ke::{
|
|||||||
ClientLogin, ClientLoginFinishParameters, ClientRegistration,
|
ClientLogin, ClientLoginFinishParameters, ClientRegistration,
|
||||||
ClientRegistrationFinishParameters, CredentialFinalization, CredentialRequest,
|
ClientRegistrationFinishParameters, CredentialFinalization, CredentialRequest,
|
||||||
CredentialResponse, RegistrationRequest, RegistrationResponse, RegistrationUpload, ServerLogin,
|
CredentialResponse, RegistrationRequest, RegistrationResponse, RegistrationUpload, ServerLogin,
|
||||||
ServerLoginStartParameters, ServerRegistration, ServerRegistrationLen, ServerSetup,
|
ServerLoginParameters, ServerRegistration, ServerRegistrationLen, ServerSetup,
|
||||||
};
|
};
|
||||||
use rustyline::error::ReadlineError;
|
use rustyline::error::ReadlineError;
|
||||||
use rustyline::history::DefaultHistory;
|
use rustyline::history::DefaultHistory;
|
||||||
@@ -52,16 +52,14 @@ struct DefaultCipherSuite;
|
|||||||
#[cfg(feature = "ristretto255")]
|
#[cfg(feature = "ristretto255")]
|
||||||
impl CipherSuite for DefaultCipherSuite {
|
impl CipherSuite for DefaultCipherSuite {
|
||||||
type OprfCs = opaque_ke::Ristretto255;
|
type OprfCs = opaque_ke::Ristretto255;
|
||||||
type KeGroup = opaque_ke::Ristretto255;
|
type KeyExchange = opaque_ke::TripleDh<opaque_ke::Ristretto255, sha2::Sha512>;
|
||||||
type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDh;
|
|
||||||
type Ksf = opaque_ke::ksf::Identity;
|
type Ksf = opaque_ke::ksf::Identity;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "ristretto255"))]
|
#[cfg(not(feature = "ristretto255"))]
|
||||||
impl CipherSuite for DefaultCipherSuite {
|
impl CipherSuite for DefaultCipherSuite {
|
||||||
type OprfCs = p256::NistP256;
|
type OprfCs = p256::NistP256;
|
||||||
type KeGroup = p256::NistP256;
|
type KeyExchange = opaque_ke::TripleDh<p256::NistP256, sha2::Sha256>;
|
||||||
type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDh;
|
|
||||||
type Ksf = opaque_ke::ksf::Identity;
|
type Ksf = opaque_ke::ksf::Identity;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,7 +170,7 @@ fn open_locker(
|
|||||||
Some(password_file),
|
Some(password_file),
|
||||||
CredentialRequest::deserialize(&credential_request_bytes).unwrap(),
|
CredentialRequest::deserialize(&credential_request_bytes).unwrap(),
|
||||||
&locker_id.to_be_bytes(),
|
&locker_id.to_be_bytes(),
|
||||||
ServerLoginStartParameters::default(),
|
ServerLoginParameters::default(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let credential_response_bytes = server_login_start_result.message.serialize();
|
let credential_response_bytes = server_login_start_result.message.serialize();
|
||||||
@@ -180,6 +178,7 @@ fn open_locker(
|
|||||||
// Server sends credential_response_bytes to client
|
// Server sends credential_response_bytes to client
|
||||||
|
|
||||||
let result = client_login_start_result.state.finish(
|
let result = client_login_start_result.state.finish(
|
||||||
|
&mut client_rng,
|
||||||
password.as_bytes(),
|
password.as_bytes(),
|
||||||
CredentialResponse::deserialize(&credential_response_bytes).unwrap(),
|
CredentialResponse::deserialize(&credential_response_bytes).unwrap(),
|
||||||
ClientLoginFinishParameters::default(),
|
ClientLoginFinishParameters::default(),
|
||||||
@@ -196,7 +195,10 @@ fn open_locker(
|
|||||||
|
|
||||||
let server_login_finish_result = server_login_start_result
|
let server_login_finish_result = server_login_start_result
|
||||||
.state
|
.state
|
||||||
.finish(CredentialFinalization::deserialize(&credential_finalization_bytes).unwrap())
|
.finish(
|
||||||
|
CredentialFinalization::deserialize(&credential_finalization_bytes).unwrap(),
|
||||||
|
ServerLoginParameters::default(),
|
||||||
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
// Server sends locker contents, encrypted under the session key, to the client
|
// Server sends locker contents, encrypted under the session key, to the client
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ use opaque_ke::{
|
|||||||
ClientLogin, ClientLoginFinishParameters, ClientRegistration,
|
ClientLogin, ClientLoginFinishParameters, ClientRegistration,
|
||||||
ClientRegistrationFinishParameters, CredentialFinalization, CredentialRequest,
|
ClientRegistrationFinishParameters, CredentialFinalization, CredentialRequest,
|
||||||
CredentialResponse, RegistrationRequest, RegistrationResponse, RegistrationUpload, ServerLogin,
|
CredentialResponse, RegistrationRequest, RegistrationResponse, RegistrationUpload, ServerLogin,
|
||||||
ServerLoginStartParameters, ServerRegistration, ServerRegistrationLen, ServerSetup,
|
ServerLoginParameters, ServerRegistration, ServerRegistrationLen, ServerSetup,
|
||||||
};
|
};
|
||||||
use rustyline::error::ReadlineError;
|
use rustyline::error::ReadlineError;
|
||||||
use rustyline::history::DefaultHistory;
|
use rustyline::history::DefaultHistory;
|
||||||
@@ -47,8 +47,7 @@ struct DefaultCipherSuite;
|
|||||||
#[cfg(feature = "ristretto255")]
|
#[cfg(feature = "ristretto255")]
|
||||||
impl CipherSuite for DefaultCipherSuite {
|
impl CipherSuite for DefaultCipherSuite {
|
||||||
type OprfCs = opaque_ke::Ristretto255;
|
type OprfCs = opaque_ke::Ristretto255;
|
||||||
type KeGroup = opaque_ke::Ristretto255;
|
type KeyExchange = opaque_ke::TripleDh<opaque_ke::Ristretto255, sha2::Sha512>;
|
||||||
type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDh;
|
|
||||||
|
|
||||||
type Ksf = Argon2<'static>;
|
type Ksf = Argon2<'static>;
|
||||||
}
|
}
|
||||||
@@ -56,8 +55,7 @@ impl CipherSuite for DefaultCipherSuite {
|
|||||||
#[cfg(not(feature = "ristretto255"))]
|
#[cfg(not(feature = "ristretto255"))]
|
||||||
impl CipherSuite for DefaultCipherSuite {
|
impl CipherSuite for DefaultCipherSuite {
|
||||||
type OprfCs = p256::NistP256;
|
type OprfCs = p256::NistP256;
|
||||||
type KeGroup = p256::NistP256;
|
type KeyExchange = opaque_ke::TripleDh<p256::NistP256, sha2::Sha256>;
|
||||||
type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDh;
|
|
||||||
|
|
||||||
type Ksf = Argon2<'static>;
|
type Ksf = Argon2<'static>;
|
||||||
}
|
}
|
||||||
@@ -128,7 +126,7 @@ fn account_login(
|
|||||||
Some(password_file),
|
Some(password_file),
|
||||||
CredentialRequest::deserialize(&credential_request_bytes).unwrap(),
|
CredentialRequest::deserialize(&credential_request_bytes).unwrap(),
|
||||||
username.as_bytes(),
|
username.as_bytes(),
|
||||||
ServerLoginStartParameters::default(),
|
ServerLoginParameters::default(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let credential_response_bytes = server_login_start_result.message.serialize();
|
let credential_response_bytes = server_login_start_result.message.serialize();
|
||||||
@@ -136,6 +134,7 @@ fn account_login(
|
|||||||
// Server sends credential_response_bytes to client
|
// Server sends credential_response_bytes to client
|
||||||
|
|
||||||
let result = client_login_start_result.state.finish(
|
let result = client_login_start_result.state.finish(
|
||||||
|
&mut client_rng,
|
||||||
password.as_bytes(),
|
password.as_bytes(),
|
||||||
CredentialResponse::deserialize(&credential_response_bytes).unwrap(),
|
CredentialResponse::deserialize(&credential_response_bytes).unwrap(),
|
||||||
ClientLoginFinishParameters::default(),
|
ClientLoginFinishParameters::default(),
|
||||||
@@ -152,7 +151,10 @@ fn account_login(
|
|||||||
|
|
||||||
let server_login_finish_result = server_login_start_result
|
let server_login_finish_result = server_login_start_result
|
||||||
.state
|
.state
|
||||||
.finish(CredentialFinalization::deserialize(&credential_finalization_bytes).unwrap())
|
.finish(
|
||||||
|
CredentialFinalization::deserialize(&credential_finalization_bytes).unwrap(),
|
||||||
|
ServerLoginParameters::default(),
|
||||||
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
client_login_finish_result.session_key == server_login_finish_result.session_key
|
client_login_finish_result.session_key == server_login_finish_result.session_key
|
||||||
|
|||||||
+18
-10
@@ -9,14 +9,18 @@
|
|||||||
//! Defines the [`CipherSuite`] trait to specify the underlying primitives for
|
//! Defines the [`CipherSuite`] trait to specify the underlying primitives for
|
||||||
//! OPAQUE
|
//! OPAQUE
|
||||||
|
|
||||||
use digest::core_api::{BlockSizeUser, CoreProxy};
|
use core::ops::Add;
|
||||||
use digest::OutputSizeUser;
|
|
||||||
use generic_array::typenum::{IsLess, IsLessOrEqual, Le, NonZero, U256};
|
|
||||||
|
|
||||||
use crate::hash::{Hash, ProxyHash};
|
use digest::core_api::{BlockSizeUser, CoreProxy};
|
||||||
use crate::key_exchange::group::KeGroup;
|
use generic_array::typenum::{IsLess, Le, NonZero, Sum, U256};
|
||||||
|
use generic_array::ArrayLength;
|
||||||
|
|
||||||
|
use crate::envelope::NonceLen;
|
||||||
|
use crate::hash::{Hash, OutputSize, ProxyHash};
|
||||||
|
use crate::key_exchange::group::Group;
|
||||||
use crate::key_exchange::traits::KeyExchange;
|
use crate::key_exchange::traits::KeyExchange;
|
||||||
use crate::ksf::Ksf;
|
use crate::ksf::Ksf;
|
||||||
|
use crate::opaque::MaskedResponseLen;
|
||||||
|
|
||||||
/// Configures the underlying primitives used in OPAQUE
|
/// Configures the underlying primitives used in OPAQUE
|
||||||
/// * `OprfCs`: A VOPRF ciphersuite, see [`voprf::CipherSuite`].
|
/// * `OprfCs`: A VOPRF ciphersuite, see [`voprf::CipherSuite`].
|
||||||
@@ -26,22 +30,26 @@ use crate::ksf::Ksf;
|
|||||||
/// * `Ksf`: A key stretching function, typically used for password hashing
|
/// * `Ksf`: A key stretching function, typically used for password hashing
|
||||||
pub trait CipherSuite
|
pub trait CipherSuite
|
||||||
where
|
where
|
||||||
<OprfHash<Self> as OutputSizeUser>::OutputSize:
|
|
||||||
IsLess<U256> + IsLessOrEqual<<OprfHash<Self> as BlockSizeUser>::BlockSize>,
|
|
||||||
OprfHash<Self>: Hash,
|
OprfHash<Self>: Hash,
|
||||||
<OprfHash<Self> as CoreProxy>::Core: ProxyHash,
|
<OprfHash<Self> as CoreProxy>::Core: ProxyHash,
|
||||||
<<OprfHash<Self> as CoreProxy>::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
|
<<OprfHash<Self> as CoreProxy>::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
|
||||||
Le<<<OprfHash<Self> as CoreProxy>::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
|
Le<<<OprfHash<Self> as CoreProxy>::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
|
||||||
|
// Envelope: Nonce + Hash
|
||||||
|
// MaskedResponse: (Nonce + Hash) + KePk
|
||||||
|
OutputSize<OprfHash<Self>>: Add<NonceLen>,
|
||||||
|
Sum<OutputSize<OprfHash<Self>>, NonceLen>:
|
||||||
|
ArrayLength<u8> + Add<<KeGroup<Self> as Group>::PkLen>,
|
||||||
|
MaskedResponseLen<Self>: ArrayLength<u8>,
|
||||||
{
|
{
|
||||||
/// A VOPRF ciphersuite, see [`voprf::CipherSuite`].
|
/// A VOPRF ciphersuite, see [`voprf::CipherSuite`].
|
||||||
type OprfCs: voprf::CipherSuite;
|
type OprfCs: voprf::CipherSuite;
|
||||||
/// A `Group` used for the `KeyExchange`.
|
|
||||||
type KeGroup: 'static + KeGroup;
|
|
||||||
/// A key exchange protocol
|
/// A key exchange protocol
|
||||||
type KeyExchange: KeyExchange<OprfHash<Self>, Self::KeGroup>;
|
type KeyExchange: KeyExchange;
|
||||||
/// A key stretching function, typically used for password hashing
|
/// A key stretching function, typically used for password hashing
|
||||||
type Ksf: Ksf;
|
type Ksf: Ksf;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) type OprfGroup<CS: CipherSuite> = <CS::OprfCs as voprf::CipherSuite>::Group;
|
pub(crate) type OprfGroup<CS: CipherSuite> = <CS::OprfCs as voprf::CipherSuite>::Group;
|
||||||
pub(crate) type OprfHash<CS: CipherSuite> = <CS::OprfCs as voprf::CipherSuite>::Hash;
|
pub(crate) type OprfHash<CS: CipherSuite> = <CS::OprfCs as voprf::CipherSuite>::Hash;
|
||||||
|
pub(crate) type KeGroup<CS: CipherSuite> = <CS::KeyExchange as KeyExchange>::Group;
|
||||||
|
pub(crate) type KeHash<CS: CipherSuite> = <CS::KeyExchange as KeyExchange>::Hash;
|
||||||
|
|||||||
+67
-69
@@ -7,26 +7,25 @@
|
|||||||
// licenses.
|
// licenses.
|
||||||
|
|
||||||
use core::convert::TryFrom;
|
use core::convert::TryFrom;
|
||||||
use core::ops::Add;
|
|
||||||
|
|
||||||
use derive_where::derive_where;
|
use derive_where::derive_where;
|
||||||
use digest::Output;
|
use digest::Output;
|
||||||
use generic_array::sequence::Concat;
|
use generic_array::sequence::Concat;
|
||||||
use generic_array::typenum::{Sum, Unsigned, U2, U32};
|
use generic_array::typenum::{Sum, U32};
|
||||||
use generic_array::{ArrayLength, GenericArray};
|
use generic_array::GenericArray;
|
||||||
use hkdf::Hkdf;
|
use hkdf::Hkdf;
|
||||||
use hmac::{Hmac, Mac};
|
use hmac::{Hmac, Mac};
|
||||||
use rand::{CryptoRng, RngCore};
|
use rand::{CryptoRng, RngCore};
|
||||||
use zeroize::{Zeroize, ZeroizeOnDrop};
|
use zeroize::{Zeroize, ZeroizeOnDrop};
|
||||||
|
|
||||||
use crate::ciphersuite::{CipherSuite, OprfHash};
|
use crate::ciphersuite::{CipherSuite, KeGroup, OprfHash};
|
||||||
use crate::errors::utils::check_slice_size;
|
|
||||||
use crate::errors::{InternalError, ProtocolError};
|
use crate::errors::{InternalError, ProtocolError};
|
||||||
use crate::hash::OutputSize;
|
use crate::hash::OutputSize;
|
||||||
use crate::key_exchange::group::KeGroup;
|
use crate::key_exchange::group::Group;
|
||||||
use crate::keypair::{KeyPair, PrivateKey, PrivateKeySerialization, PublicKey};
|
use crate::key_exchange::traits::SerializedIdentifiers;
|
||||||
use crate::opaque::{bytestrings_from_identifiers, Identifiers};
|
use crate::keypair::{KeyPair, PrivateKey, PublicKey};
|
||||||
use crate::serialization::{Input, MacExt};
|
use crate::opaque::Identifiers;
|
||||||
|
use crate::serialization::{GenericArrayExt, SliceExt, UpdateExt};
|
||||||
|
|
||||||
// Constant string used as salt for HKDF computation
|
// Constant string used as salt for HKDF computation
|
||||||
const STR_AUTH_KEY: [u8; 7] = *b"AuthKey";
|
const STR_AUTH_KEY: [u8; 7] = *b"AuthKey";
|
||||||
@@ -82,10 +81,9 @@ pub(crate) struct Envelope<CS: CipherSuite> {
|
|||||||
// which is technically unrelated to the envelope's encrypted and authenticated
|
// which is technically unrelated to the envelope's encrypted and authenticated
|
||||||
// contents.
|
// contents.
|
||||||
pub(crate) struct OpenedEnvelope<'a, CS: CipherSuite> {
|
pub(crate) struct OpenedEnvelope<'a, CS: CipherSuite> {
|
||||||
pub(crate) client_static_keypair: KeyPair<CS::KeGroup>,
|
pub(crate) client_static_keypair: KeyPair<KeGroup<CS>>,
|
||||||
pub(crate) export_key: Output<OprfHash<CS>>,
|
pub(crate) export_key: Output<OprfHash<CS>>,
|
||||||
pub(crate) id_u: Input<'a, U2, <CS::KeGroup as KeGroup>::PkLen>,
|
pub(crate) identifiers: SerializedIdentifiers<'a, KeGroup<CS>>,
|
||||||
pub(crate) id_s: Input<'a, U2, <CS::KeGroup as KeGroup>::PkLen>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct OpenedInnerEnvelope<CS: CipherSuite> {
|
pub(crate) struct OpenedInnerEnvelope<CS: CipherSuite> {
|
||||||
@@ -97,23 +95,23 @@ type SealRawResult<CS: CipherSuite> = (Envelope<CS>, Output<OprfHash<CS>>);
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
type SealRawResult<CS: CipherSuite> = (Envelope<CS>, Output<OprfHash<CS>>, Output<OprfHash<CS>>);
|
type SealRawResult<CS: CipherSuite> = (Envelope<CS>, Output<OprfHash<CS>>, Output<OprfHash<CS>>);
|
||||||
#[cfg(not(test))]
|
#[cfg(not(test))]
|
||||||
type SealResult<CS: CipherSuite> = (Envelope<CS>, PublicKey<CS::KeGroup>, Output<OprfHash<CS>>);
|
type SealResult<CS: CipherSuite> = (Envelope<CS>, PublicKey<KeGroup<CS>>, Output<OprfHash<CS>>);
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
type SealResult<CS: CipherSuite> = (
|
type SealResult<CS: CipherSuite> = (
|
||||||
Envelope<CS>,
|
Envelope<CS>,
|
||||||
PublicKey<CS::KeGroup>,
|
PublicKey<KeGroup<CS>>,
|
||||||
Output<OprfHash<CS>>,
|
Output<OprfHash<CS>>,
|
||||||
Output<OprfHash<CS>>,
|
Output<OprfHash<CS>>,
|
||||||
);
|
);
|
||||||
|
|
||||||
pub(crate) type EnvelopeLen<CS: CipherSuite> = Sum<NonceLen, OutputSize<OprfHash<CS>>>;
|
pub(crate) type EnvelopeLen<CS: CipherSuite> = Sum<OutputSize<OprfHash<CS>>, NonceLen>;
|
||||||
|
|
||||||
impl<CS: CipherSuite> Envelope<CS> {
|
impl<CS: CipherSuite> Envelope<CS> {
|
||||||
#[allow(clippy::type_complexity)]
|
#[allow(clippy::type_complexity)]
|
||||||
pub(crate) fn seal<R: RngCore + CryptoRng>(
|
pub(crate) fn seal<R: RngCore + CryptoRng>(
|
||||||
rng: &mut R,
|
rng: &mut R,
|
||||||
randomized_pwd_hasher: Hkdf<OprfHash<CS>>,
|
randomized_pwd_hasher: Hkdf<OprfHash<CS>>,
|
||||||
server_s_pk: &PublicKey<CS::KeGroup>,
|
server_s_pk: &PublicKey<KeGroup<CS>>,
|
||||||
ids: Identifiers,
|
ids: Identifiers,
|
||||||
) -> Result<SealResult<CS>, ProtocolError> {
|
) -> Result<SealResult<CS>, ProtocolError> {
|
||||||
let mut nonce = GenericArray::default();
|
let mut nonce = GenericArray::default();
|
||||||
@@ -125,12 +123,16 @@ impl<CS: CipherSuite> Envelope<CS> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let server_s_pk_bytes = server_s_pk.serialize();
|
let server_s_pk_bytes = server_s_pk.serialize();
|
||||||
let (id_u, id_s) = bytestrings_from_identifiers::<CS::KeGroup>(
|
let identifiers = SerializedIdentifiers::<KeGroup<CS>>::from_identifiers(
|
||||||
ids,
|
ids,
|
||||||
client_s_pk.serialize(),
|
client_s_pk.serialize(),
|
||||||
server_s_pk_bytes.clone(),
|
server_s_pk_bytes.clone(),
|
||||||
)?;
|
)?;
|
||||||
let aad = construct_aad(id_u.iter(), id_s.iter(), &server_s_pk_bytes);
|
let aad = construct_aad(
|
||||||
|
identifiers.client.iter(),
|
||||||
|
identifiers.server.iter(),
|
||||||
|
&server_s_pk_bytes,
|
||||||
|
);
|
||||||
|
|
||||||
let result = Self::seal_raw(randomized_pwd_hasher, nonce, aad, mode)?;
|
let result = Self::seal_raw(randomized_pwd_hasher, nonce, aad, mode)?;
|
||||||
Ok((
|
Ok((
|
||||||
@@ -183,7 +185,7 @@ impl<CS: CipherSuite> Envelope<CS> {
|
|||||||
pub(crate) fn open<'a>(
|
pub(crate) fn open<'a>(
|
||||||
&self,
|
&self,
|
||||||
randomized_pwd_hasher: Hkdf<OprfHash<CS>>,
|
randomized_pwd_hasher: Hkdf<OprfHash<CS>>,
|
||||||
server_s_pk: PublicKey<CS::KeGroup>,
|
server_s_pk: PublicKey<KeGroup<CS>>,
|
||||||
optional_ids: Identifiers<'a>,
|
optional_ids: Identifiers<'a>,
|
||||||
) -> Result<OpenedEnvelope<'a, CS>, ProtocolError> {
|
) -> Result<OpenedEnvelope<'a, CS>, ProtocolError> {
|
||||||
let client_static_keypair = match self.mode {
|
let client_static_keypair = match self.mode {
|
||||||
@@ -196,20 +198,23 @@ impl<CS: CipherSuite> Envelope<CS> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let server_s_pk_bytes = server_s_pk.serialize();
|
let server_s_pk_bytes = server_s_pk.serialize();
|
||||||
let (id_u, id_s) = bytestrings_from_identifiers::<CS::KeGroup>(
|
let identifiers = SerializedIdentifiers::<KeGroup<CS>>::from_identifiers(
|
||||||
optional_ids,
|
optional_ids,
|
||||||
client_static_keypair.public().serialize(),
|
client_static_keypair.public().serialize(),
|
||||||
server_s_pk_bytes.clone(),
|
server_s_pk_bytes.clone(),
|
||||||
)?;
|
)?;
|
||||||
let aad = construct_aad(id_u.iter(), id_s.iter(), &server_s_pk_bytes);
|
let aad = construct_aad(
|
||||||
|
identifiers.client.iter(),
|
||||||
|
identifiers.server.iter(),
|
||||||
|
&server_s_pk_bytes,
|
||||||
|
);
|
||||||
|
|
||||||
let opened = self.open_raw(randomized_pwd_hasher, aad)?;
|
let opened = self.open_raw(randomized_pwd_hasher, aad)?;
|
||||||
|
|
||||||
Ok(OpenedEnvelope {
|
Ok(OpenedEnvelope {
|
||||||
client_static_keypair,
|
client_static_keypair,
|
||||||
export_key: opened.export_key,
|
export_key: opened.export_key,
|
||||||
id_u,
|
identifiers,
|
||||||
id_s,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -249,45 +254,22 @@ impl<CS: CipherSuite> Envelope<CS> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn hmac_key_size() -> usize {
|
#[cfg(test)]
|
||||||
OutputSize::<OprfHash<CS>>::USIZE
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn len() -> usize {
|
pub(crate) fn len() -> usize {
|
||||||
|
use generic_array::typenum::Unsigned;
|
||||||
|
|
||||||
OutputSize::<OprfHash<CS>>::USIZE + NonceLen::USIZE
|
OutputSize::<OprfHash<CS>>::USIZE + NonceLen::USIZE
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn serialize(&self) -> GenericArray<u8, EnvelopeLen<CS>>
|
pub(crate) fn serialize(&self) -> GenericArray<u8, EnvelopeLen<CS>> {
|
||||||
where
|
self.nonce.concat_ext(&self.hmac)
|
||||||
// Envelope: Nonce + Hash
|
|
||||||
NonceLen: Add<OutputSize<OprfHash<CS>>>,
|
|
||||||
EnvelopeLen<CS>: ArrayLength<u8>,
|
|
||||||
{
|
|
||||||
self.nonce.concat(self.hmac.clone())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn deserialize(bytes: &[u8]) -> Result<Self, ProtocolError> {
|
pub(crate) fn deserialize_take(bytes: &mut &[u8]) -> Result<Self, ProtocolError> {
|
||||||
let mode = InnerEnvelopeMode::Internal; // Better way to hard-code this?
|
|
||||||
|
|
||||||
if bytes.len() < NonceLen::USIZE {
|
|
||||||
return Err(ProtocolError::SerializationError);
|
|
||||||
}
|
|
||||||
let nonce = GenericArray::clone_from_slice(&bytes[..NonceLen::USIZE]);
|
|
||||||
|
|
||||||
let remainder = match mode {
|
|
||||||
InnerEnvelopeMode::Zero => {
|
|
||||||
return Err(InternalError::IncompatibleEnvelopeModeError.into())
|
|
||||||
}
|
|
||||||
InnerEnvelopeMode::Internal => &bytes[NonceLen::USIZE..],
|
|
||||||
};
|
|
||||||
|
|
||||||
let hmac_key_size = Self::hmac_key_size();
|
|
||||||
let hmac = check_slice_size(remainder, hmac_key_size, "hmac_key_size")?;
|
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
mode,
|
mode: InnerEnvelopeMode::Internal,
|
||||||
nonce,
|
nonce: bytes.take_array("nonce")?,
|
||||||
hmac: GenericArray::clone_from_slice(hmac),
|
hmac: bytes.take_array("hmac")?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -297,33 +279,28 @@ impl<CS: CipherSuite> Envelope<CS> {
|
|||||||
fn build_inner_envelope_internal<CS: CipherSuite>(
|
fn build_inner_envelope_internal<CS: CipherSuite>(
|
||||||
randomized_pwd_hasher: Hkdf<OprfHash<CS>>,
|
randomized_pwd_hasher: Hkdf<OprfHash<CS>>,
|
||||||
nonce: GenericArray<u8, NonceLen>,
|
nonce: GenericArray<u8, NonceLen>,
|
||||||
) -> Result<PublicKey<CS::KeGroup>, ProtocolError> {
|
) -> Result<PublicKey<KeGroup<CS>>, ProtocolError> {
|
||||||
let mut keypair_seed = GenericArray::<_, <CS::KeGroup as KeGroup>::SkLen>::default();
|
let mut keypair_seed = GenericArray::<_, <KeGroup<CS> as Group>::SkLen>::default();
|
||||||
randomized_pwd_hasher
|
randomized_pwd_hasher
|
||||||
.expand(&nonce.concat(STR_PRIVATE_KEY.into()), &mut keypair_seed)
|
.expand(&nonce.concat(STR_PRIVATE_KEY.into()), &mut keypair_seed)
|
||||||
.map_err(|_| InternalError::HkdfError)?;
|
.map_err(|_| InternalError::HkdfError)?;
|
||||||
let client_static_keypair =
|
let client_s_sk = PrivateKey::new(KeGroup::<CS>::derive_scalar(keypair_seed)?);
|
||||||
PrivateKey::<CS::KeGroup>::deserialize_key_pair(&CS::KeGroup::serialize_sk(
|
|
||||||
CS::KeGroup::derive_auth_keypair::<CS::OprfCs>(keypair_seed)?,
|
|
||||||
))?;
|
|
||||||
|
|
||||||
Ok(client_static_keypair.public().clone())
|
Ok(client_s_sk.public_key())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn recover_keys_internal<CS: CipherSuite>(
|
fn recover_keys_internal<CS: CipherSuite>(
|
||||||
randomized_pwd_hasher: Hkdf<OprfHash<CS>>,
|
randomized_pwd_hasher: Hkdf<OprfHash<CS>>,
|
||||||
nonce: GenericArray<u8, NonceLen>,
|
nonce: GenericArray<u8, NonceLen>,
|
||||||
) -> Result<KeyPair<CS::KeGroup>, ProtocolError> {
|
) -> Result<KeyPair<KeGroup<CS>>, ProtocolError> {
|
||||||
let mut keypair_seed = GenericArray::<_, <CS::KeGroup as KeGroup>::SkLen>::default();
|
let mut keypair_seed = GenericArray::<_, <KeGroup<CS> as Group>::SkLen>::default();
|
||||||
randomized_pwd_hasher
|
randomized_pwd_hasher
|
||||||
.expand(&nonce.concat(STR_PRIVATE_KEY.into()), &mut keypair_seed)
|
.expand(&nonce.concat(STR_PRIVATE_KEY.into()), &mut keypair_seed)
|
||||||
.map_err(|_| InternalError::HkdfError)?;
|
.map_err(|_| InternalError::HkdfError)?;
|
||||||
let client_static_keypair =
|
let client_s_sk = PrivateKey::new(KeGroup::<CS>::derive_scalar(keypair_seed)?);
|
||||||
PrivateKey::<CS::KeGroup>::deserialize_key_pair(&CS::KeGroup::serialize_sk(
|
let client_s_pk = client_s_sk.public_key();
|
||||||
CS::KeGroup::derive_auth_keypair::<CS::OprfCs>(keypair_seed)?,
|
|
||||||
))?;
|
|
||||||
|
|
||||||
Ok(client_static_keypair)
|
Ok(KeyPair::new(client_s_sk, client_s_pk))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn construct_aad<'a>(
|
fn construct_aad<'a>(
|
||||||
@@ -333,3 +310,24 @@ fn construct_aad<'a>(
|
|||||||
) -> impl Iterator<Item = &'a [u8]> {
|
) -> impl Iterator<Item = &'a [u8]> {
|
||||||
[server_s_pk].into_iter().chain(id_s).chain(id_u)
|
[server_s_pk].into_iter().chain(id_s).chain(id_u)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//////////////////////////
|
||||||
|
// Test Implementations //
|
||||||
|
//===================== //
|
||||||
|
//////////////////////////
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
use crate::serialization::AssertZeroized;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
impl<CS: CipherSuite> AssertZeroized for Envelope<CS> {
|
||||||
|
fn assert_zeroized(&self) {
|
||||||
|
let Self { mode, nonce, hmac } = self;
|
||||||
|
|
||||||
|
assert_eq!(mode, &InnerEnvelopeMode::Zero);
|
||||||
|
|
||||||
|
for byte in nonce.iter().chain(hmac) {
|
||||||
|
assert_eq!(byte, &0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -96,9 +96,6 @@ pub enum ProtocolError<T = Infallible> {
|
|||||||
/** This error occurs when the client detects that the server has
|
/** This error occurs when the client detects that the server has
|
||||||
reflected the OPRF value (beta == alpha) */
|
reflected the OPRF value (beta == alpha) */
|
||||||
ReflectedValueError,
|
ReflectedValueError,
|
||||||
/** Identity group element was encountered during deserialization, which is
|
|
||||||
invalid */
|
|
||||||
IdentityGroupElementError,
|
|
||||||
/// Custom [`SecretKey`](crate::keypair::PrivateKeySerialization) error type
|
/// Custom [`SecretKey`](crate::keypair::PrivateKeySerialization) error type
|
||||||
Custom(T),
|
Custom(T),
|
||||||
}
|
}
|
||||||
@@ -122,7 +119,6 @@ impl<T: Debug> Debug for ProtocolError<T> {
|
|||||||
.field("actual_len", actual_len)
|
.field("actual_len", actual_len)
|
||||||
.finish(),
|
.finish(),
|
||||||
Self::ReflectedValueError => f.debug_tuple("ReflectedValueError").finish(),
|
Self::ReflectedValueError => f.debug_tuple("ReflectedValueError").finish(),
|
||||||
Self::IdentityGroupElementError => f.debug_tuple("IdentityGroupElementError").finish(),
|
|
||||||
Self::Custom(custom) => f.debug_tuple("Custom").field(custom).finish(),
|
Self::Custom(custom) => f.debug_tuple("Custom").field(custom).finish(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -164,41 +160,6 @@ impl ProtocolError {
|
|||||||
actual_len,
|
actual_len,
|
||||||
},
|
},
|
||||||
Self::ReflectedValueError => ProtocolError::ReflectedValueError,
|
Self::ReflectedValueError => ProtocolError::ReflectedValueError,
|
||||||
Self::IdentityGroupElementError => ProtocolError::IdentityGroupElementError,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) mod utils {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
pub fn check_slice_size<'a>(
|
|
||||||
slice: &'a [u8],
|
|
||||||
expected_len: usize,
|
|
||||||
arg_name: &'static str,
|
|
||||||
) -> Result<&'a [u8], ProtocolError> {
|
|
||||||
if slice.len() != expected_len {
|
|
||||||
return Err(ProtocolError::SizeError {
|
|
||||||
name: arg_name,
|
|
||||||
len: expected_len,
|
|
||||||
actual_len: slice.len(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Ok(slice)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn check_slice_size_atleast<'a>(
|
|
||||||
slice: &'a [u8],
|
|
||||||
expected_len: usize,
|
|
||||||
arg_name: &'static str,
|
|
||||||
) -> Result<&'a [u8], ProtocolError> {
|
|
||||||
if slice.len() < expected_len {
|
|
||||||
return Err(ProtocolError::SizeError {
|
|
||||||
name: arg_name,
|
|
||||||
len: expected_len,
|
|
||||||
actual_len: slice.len(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Ok(slice)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
+1
-1
@@ -13,7 +13,7 @@ use digest::core_api::{BlockSizeUser, BufferKindUser, CoreProxy, FixedOutputCore
|
|||||||
use digest::{FixedOutputReset, HashMarker, OutputSizeUser};
|
use digest::{FixedOutputReset, HashMarker, OutputSizeUser};
|
||||||
use generic_array::typenum::{IsLess, Le, NonZero, U256};
|
use generic_array::typenum::{IsLess, Le, NonZero, U256};
|
||||||
|
|
||||||
pub(crate) type OutputSize<D> = <<D as CoreProxy>::Core as OutputSizeUser>::OutputSize;
|
pub(crate) type OutputSize<H> = <<H as CoreProxy>::Core as OutputSizeUser>::OutputSize;
|
||||||
|
|
||||||
/// Trait to simplify requirements for [`Hash`].
|
/// Trait to simplify requirements for [`Hash`].
|
||||||
pub trait ProxyHash:
|
pub trait ProxyHash:
|
||||||
|
|||||||
@@ -8,26 +8,25 @@
|
|||||||
|
|
||||||
//! Key Exchange group implementation for Curve25519
|
//! Key Exchange group implementation for Curve25519
|
||||||
|
|
||||||
|
pub use curve25519_dalek;
|
||||||
use curve25519_dalek::montgomery::MontgomeryPoint;
|
use curve25519_dalek::montgomery::MontgomeryPoint;
|
||||||
use curve25519_dalek::scalar;
|
use curve25519_dalek::scalar;
|
||||||
use curve25519_dalek::traits::Identity;
|
use curve25519_dalek::traits::Identity;
|
||||||
use digest::core_api::BlockSizeUser;
|
use generic_array::typenum::U32;
|
||||||
use digest::{FixedOutput, HashMarker, OutputSizeUser};
|
|
||||||
use generic_array::typenum::{IsLess, IsLessOrEqual, U256, U32};
|
|
||||||
use generic_array::GenericArray;
|
use generic_array::GenericArray;
|
||||||
use rand::{CryptoRng, RngCore};
|
use rand::{CryptoRng, RngCore};
|
||||||
use subtle::ConstantTimeEq;
|
|
||||||
use zeroize::Zeroize;
|
use zeroize::Zeroize;
|
||||||
|
|
||||||
use super::KeGroup;
|
use super::Group;
|
||||||
use crate::errors::{InternalError, ProtocolError};
|
use crate::errors::{InternalError, ProtocolError};
|
||||||
use crate::key_exchange::tripledh::DiffieHellman;
|
use crate::key_exchange::shared::DiffieHellman;
|
||||||
|
use crate::serialization::SliceExt;
|
||||||
|
|
||||||
/// Implementation for Curve25519.
|
/// Implementation for Curve25519.
|
||||||
pub struct Curve25519;
|
pub struct Curve25519;
|
||||||
|
|
||||||
/// The implementation of such a subgroup for Curve25519
|
/// The implementation of such a subgroup for Curve25519
|
||||||
impl KeGroup for Curve25519 {
|
impl Group for Curve25519 {
|
||||||
type Pk = MontgomeryPoint;
|
type Pk = MontgomeryPoint;
|
||||||
type PkLen = U32;
|
type PkLen = U32;
|
||||||
type Sk = Scalar;
|
type Sk = Scalar;
|
||||||
@@ -37,50 +36,28 @@ impl KeGroup for Curve25519 {
|
|||||||
pk.to_bytes().into()
|
pk.to_bytes().into()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deserialize_pk(bytes: &[u8]) -> Result<Self::Pk, ProtocolError> {
|
fn deserialize_take_pk(bytes: &mut &[u8]) -> Result<Self::Pk, ProtocolError> {
|
||||||
bytes
|
bytes
|
||||||
.try_into()
|
.take_array::<U32>("public key")
|
||||||
.ok()
|
.ok()
|
||||||
.map(MontgomeryPoint)
|
.map(|array| MontgomeryPoint(array.into()))
|
||||||
.filter(|pk| pk != &MontgomeryPoint::identity())
|
.filter(|pk| pk != &MontgomeryPoint::identity())
|
||||||
.ok_or(ProtocolError::SerializationError)
|
.ok_or(ProtocolError::SerializationError)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn random_sk<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Sk {
|
fn random_sk<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Sk {
|
||||||
loop {
|
// Sample 32 random bytes and then clamp, as described in https://cr.yp.to/ecdh.html
|
||||||
// Sample 32 random bytes and then clamp, as described in https://cr.yp.to/ecdh.html
|
let mut scalar_bytes = [0u8; 32];
|
||||||
let mut scalar_bytes = [0u8; 32];
|
rng.fill_bytes(&mut scalar_bytes);
|
||||||
rng.fill_bytes(&mut scalar_bytes);
|
let scalar = scalar::clamp_integer(scalar_bytes);
|
||||||
let scalar = scalar::clamp_integer(scalar_bytes);
|
|
||||||
|
|
||||||
if scalar != curve25519_dalek::Scalar::ZERO.to_bytes() {
|
Scalar(scalar)
|
||||||
break Scalar(scalar);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn hash_to_scalar<'a, H>(_input: &[&[u8]], _dst: &[&[u8]]) -> Result<Self::Sk, InternalError>
|
fn derive_scalar(seed: GenericArray<u8, Self::SkLen>) -> Result<Self::Sk, InternalError> {
|
||||||
where
|
|
||||||
H: BlockSizeUser + Default + FixedOutput + HashMarker,
|
|
||||||
H::OutputSize: IsLess<U256> + IsLessOrEqual<H::BlockSize>,
|
|
||||||
{
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn derive_auth_keypair<CS: voprf::CipherSuite>(
|
|
||||||
seed: GenericArray<u8, Self::SkLen>,
|
|
||||||
) -> Result<Self::Sk, InternalError>
|
|
||||||
where
|
|
||||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
|
||||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
|
||||||
{
|
|
||||||
Ok(Scalar(scalar::clamp_integer(seed.into())))
|
Ok(Scalar(scalar::clamp_integer(seed.into())))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_zero_scalar(scalar: Self::Sk) -> subtle::Choice {
|
|
||||||
scalar.0.ct_eq(&curve25519_dalek::Scalar::ZERO.to_bytes())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn public_key(sk: Self::Sk) -> Self::Pk {
|
fn public_key(sk: Self::Sk) -> Self::Pk {
|
||||||
MontgomeryPoint::mul_base_clamped(sk.0)
|
MontgomeryPoint::mul_base_clamped(sk.0)
|
||||||
}
|
}
|
||||||
@@ -89,22 +66,21 @@ impl KeGroup for Curve25519 {
|
|||||||
sk.0.into()
|
sk.0.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deserialize_sk(bytes: &[u8]) -> Result<Self::Sk, ProtocolError> {
|
fn deserialize_take_sk(bytes: &mut &[u8]) -> Result<Self::Sk, ProtocolError> {
|
||||||
bytes
|
bytes
|
||||||
.try_into()
|
.take_array::<U32>("secret key")
|
||||||
.ok()
|
.ok()
|
||||||
.and_then(|bytes| {
|
.and_then(|bytes| {
|
||||||
let scalar = scalar::clamp_integer(bytes);
|
let scalar = scalar::clamp_integer(bytes.into());
|
||||||
(scalar == bytes).then_some(scalar)
|
(scalar == *bytes).then_some(scalar)
|
||||||
})
|
})
|
||||||
.filter(|scalar| scalar != &curve25519_dalek::Scalar::ZERO.to_bytes())
|
|
||||||
.map(Scalar)
|
.map(Scalar)
|
||||||
.ok_or(ProtocolError::SerializationError)
|
.ok_or(ProtocolError::SerializationError)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Curve25519 scalar.
|
/// Curve25519 scalar.
|
||||||
#[derive(Clone, Copy, Zeroize)]
|
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Zeroize)]
|
||||||
pub struct Scalar([u8; 32]);
|
pub struct Scalar([u8; 32]);
|
||||||
|
|
||||||
impl DiffieHellman<Curve25519> for Scalar {
|
impl DiffieHellman<Curve25519> for Scalar {
|
||||||
@@ -112,3 +88,36 @@ impl DiffieHellman<Curve25519> for Scalar {
|
|||||||
Curve25519::serialize_pk(pk.mul_clamped(self.0))
|
Curve25519::serialize_pk(pk.mul_clamped(self.0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//////////////////////////
|
||||||
|
// Test Implementations //
|
||||||
|
//===================== //
|
||||||
|
//////////////////////////
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
use crate::serialization::AssertZeroized;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
impl AssertZeroized for MontgomeryPoint {
|
||||||
|
fn assert_zeroized(&self) {
|
||||||
|
assert_eq!(*self, MontgomeryPoint::default());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
impl AssertZeroized for Scalar {
|
||||||
|
fn assert_zeroized(&self) {
|
||||||
|
assert_eq!(*self, Scalar(<_>::default()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn non_zero_scalar() {
|
||||||
|
use std::vec;
|
||||||
|
|
||||||
|
use crate::tests::mock_rng::CycleRng;
|
||||||
|
|
||||||
|
let mut rng = CycleRng::new(vec![0]);
|
||||||
|
let sk = Curve25519::random_sk(&mut rng);
|
||||||
|
assert_ne!(sk.0, curve25519_dalek::Scalar::ZERO.to_bytes());
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,459 @@
|
|||||||
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||||
|
//
|
||||||
|
// This source code is dual-licensed under either the MIT license found in the
|
||||||
|
// LICENSE-MIT file in the root directory of this source tree or the Apache
|
||||||
|
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||||
|
// of this source tree. You may select, at your option, one of the above-listed
|
||||||
|
// licenses.
|
||||||
|
|
||||||
|
//! Key Exchange group implementation for Ed25519
|
||||||
|
|
||||||
|
use core::iter;
|
||||||
|
|
||||||
|
use curve25519_dalek::edwards::CompressedEdwardsY;
|
||||||
|
use curve25519_dalek::traits::IsIdentity;
|
||||||
|
use curve25519_dalek::{EdwardsPoint, Scalar};
|
||||||
|
use digest::Digest;
|
||||||
|
pub use ed25519_dalek;
|
||||||
|
use ed25519_dalek::hazmat::ExpandedSecretKey;
|
||||||
|
use ed25519_dalek::{SecretKey, Sha512};
|
||||||
|
use generic_array::sequence::Concat;
|
||||||
|
use generic_array::typenum::{U32, U64};
|
||||||
|
use generic_array::GenericArray;
|
||||||
|
use rand::{CryptoRng, RngCore};
|
||||||
|
use zeroize::Zeroize;
|
||||||
|
|
||||||
|
use super::Group;
|
||||||
|
use crate::ciphersuite::CipherSuite;
|
||||||
|
use crate::errors::{InternalError, ProtocolError};
|
||||||
|
use crate::key_exchange::sigma_i::hash_eddsa::implementation::HashEddsaImpl;
|
||||||
|
use crate::key_exchange::sigma_i::pure_eddsa::implementation::PureEddsaImpl;
|
||||||
|
pub use crate::key_exchange::sigma_i::shared::PreHash;
|
||||||
|
use crate::key_exchange::sigma_i::{CachedMessage, Message, MessageBuilder};
|
||||||
|
use crate::serialization::{SliceExt, UpdateExt};
|
||||||
|
|
||||||
|
/// Implementation for Ed25519.
|
||||||
|
pub struct Ed25519;
|
||||||
|
|
||||||
|
impl Group for Ed25519 {
|
||||||
|
type Pk = VerifyingKey;
|
||||||
|
type PkLen = U32;
|
||||||
|
type Sk = SigningKey;
|
||||||
|
type SkLen = U32;
|
||||||
|
|
||||||
|
fn serialize_pk(pk: Self::Pk) -> GenericArray<u8, Self::PkLen> {
|
||||||
|
pk.compressed.0.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_take_pk(bytes: &mut &[u8]) -> Result<Self::Pk, ProtocolError> {
|
||||||
|
let compressed = bytes
|
||||||
|
.take_array("public key")
|
||||||
|
.map(|bytes| CompressedEdwardsY(bytes.into()))?;
|
||||||
|
|
||||||
|
if let Some(point) = compressed.decompress().filter(|point| !point.is_identity()) {
|
||||||
|
Ok(VerifyingKey { point, compressed })
|
||||||
|
} else {
|
||||||
|
Err(ProtocolError::SerializationError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn random_sk<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Sk {
|
||||||
|
let mut sk = <[u8; 32]>::default();
|
||||||
|
rng.fill_bytes(&mut sk);
|
||||||
|
|
||||||
|
SigningKey::from_bytes(sk)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn derive_scalar(seed: GenericArray<u8, Self::SkLen>) -> Result<Self::Sk, InternalError> {
|
||||||
|
Ok(SigningKey::from_bytes(seed.into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn public_key(sk: Self::Sk) -> Self::Pk {
|
||||||
|
sk.verifying_key
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_sk(sk: Self::Sk) -> GenericArray<u8, Self::SkLen> {
|
||||||
|
sk.sk.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_take_sk(bytes: &mut &[u8]) -> Result<Self::Sk, ProtocolError> {
|
||||||
|
Ok(SigningKey::from_bytes(
|
||||||
|
bytes.take_array("secret key")?.into(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Ed25519 verifying key.
|
||||||
|
// `ed25519_dalek::VerifyingKey` doesn't implement `Zeroize`.
|
||||||
|
// TODO: remove after https://github.com/dalek-cryptography/curve25519-dalek/pull/747.
|
||||||
|
// Required for manual implementation of EdDSA.
|
||||||
|
// TODO: remove after https://github.com/dalek-cryptography/curve25519-dalek/pull/556.
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, PartialEq, Zeroize)]
|
||||||
|
pub struct VerifyingKey {
|
||||||
|
point: EdwardsPoint,
|
||||||
|
compressed: CompressedEdwardsY,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Ed25519 siging key.
|
||||||
|
// We store the `ExpandedSecret` in memory to avoid computing it on demand and then discarding it
|
||||||
|
// again.
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, PartialEq, Zeroize)]
|
||||||
|
pub struct SigningKey {
|
||||||
|
// `ed25519_dalek::SigningKey` doesn't implement `Zeroize`. See
|
||||||
|
// https://github.com/dalek-cryptography/curve25519-dalek/pull/747
|
||||||
|
// Required for manual implementation of EdDSA.
|
||||||
|
// TODO: remove after https://github.com/dalek-cryptography/curve25519-dalek/pull/556.
|
||||||
|
sk: SecretKey,
|
||||||
|
verifying_key: VerifyingKey,
|
||||||
|
// `ed25519_dalek::ExpandedSecret` doesn't implement traits we need. See
|
||||||
|
// TODO: remove after https://github.com/dalek-cryptography/curve25519-dalek/pull/748 and
|
||||||
|
// https://github.com/dalek-cryptography/curve25519-dalek/pull/747.
|
||||||
|
scalar: Scalar,
|
||||||
|
hash_prefix: [u8; 32],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SigningKey {
|
||||||
|
fn from_bytes(sk: [u8; 32]) -> Self {
|
||||||
|
let ExpandedSecretKey {
|
||||||
|
scalar,
|
||||||
|
hash_prefix,
|
||||||
|
} = ExpandedSecretKey::from(&sk);
|
||||||
|
let point = EdwardsPoint::mul_base(&scalar);
|
||||||
|
let verifying_key = VerifyingKey {
|
||||||
|
point,
|
||||||
|
compressed: point.compress(),
|
||||||
|
};
|
||||||
|
|
||||||
|
SigningKey {
|
||||||
|
sk,
|
||||||
|
verifying_key,
|
||||||
|
scalar,
|
||||||
|
hash_prefix,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PureEddsaImpl for Ed25519 {
|
||||||
|
type Signature = Signature;
|
||||||
|
type SignatureLen = U64;
|
||||||
|
|
||||||
|
fn sign<CS: CipherSuite, KE: Group>(
|
||||||
|
sk: &Self::Sk,
|
||||||
|
message: &Message<CS, KE>,
|
||||||
|
) -> (Self::Signature, CachedMessage<CS, KE>) {
|
||||||
|
(sign(sk, false, message.sign_message()), message.to_cached())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Validates that the signature was created by signing the given message
|
||||||
|
/// with the corresponding private key.
|
||||||
|
fn verify<CS: CipherSuite, KE: Group>(
|
||||||
|
pk: &Self::Pk,
|
||||||
|
message_builder: MessageBuilder<'_, CS>,
|
||||||
|
state: CachedMessage<CS, KE>,
|
||||||
|
signature: &Self::Signature,
|
||||||
|
) -> Result<(), ProtocolError> {
|
||||||
|
verify(
|
||||||
|
pk,
|
||||||
|
false,
|
||||||
|
message_builder.build::<KE>(state).verify_message(),
|
||||||
|
signature,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_take_signature(bytes: &mut &[u8]) -> Result<Self::Signature, ProtocolError> {
|
||||||
|
Signature::deserialize_take(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_signature(signature: &Self::Signature) -> GenericArray<u8, Self::SignatureLen> {
|
||||||
|
signature.serialize()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HashEddsaImpl for Ed25519 {
|
||||||
|
type Signature = Signature;
|
||||||
|
type SignatureLen = U64;
|
||||||
|
type VerifyState<CS: CipherSuite, KE: Group> = PreHash<Sha512>;
|
||||||
|
|
||||||
|
fn sign<CS: CipherSuite, KE: Group>(
|
||||||
|
sk: &Self::Sk,
|
||||||
|
message: &Message<CS, KE>,
|
||||||
|
) -> (Self::Signature, Self::VerifyState<CS, KE>) {
|
||||||
|
let hash = message.hash::<Sha512>();
|
||||||
|
|
||||||
|
(
|
||||||
|
sign(sk, true, iter::once(hash.sign.finalize().as_slice())),
|
||||||
|
PreHash(hash.verify.finalize()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Validates that the signature was created by signing the given message
|
||||||
|
/// with the corresponding private key.
|
||||||
|
fn verify<CS: CipherSuite, KE: Group>(
|
||||||
|
pk: &Self::Pk,
|
||||||
|
state: Self::VerifyState<CS, KE>,
|
||||||
|
signature: &Self::Signature,
|
||||||
|
) -> Result<(), ProtocolError> {
|
||||||
|
verify(pk, true, iter::once(state.0.as_slice()), signature)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_take_signature(bytes: &mut &[u8]) -> Result<Self::Signature, ProtocolError> {
|
||||||
|
Signature::deserialize_take(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_signature(signature: &Self::Signature) -> GenericArray<u8, Self::SignatureLen> {
|
||||||
|
signature.serialize()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This contains a manual implementation of EdDSA because `ed25519-dalek`
|
||||||
|
// doesn't support message streaming. See
|
||||||
|
// TODO: remove after https://github.com/dalek-cryptography/curve25519-dalek/pull/556.
|
||||||
|
fn sign<'a>(
|
||||||
|
sk: &SigningKey,
|
||||||
|
pre_hash: bool,
|
||||||
|
message: impl Clone + Iterator<Item = &'a [u8]>,
|
||||||
|
) -> Signature {
|
||||||
|
let mut h = Sha512::new();
|
||||||
|
|
||||||
|
if pre_hash {
|
||||||
|
h.update(b"SigEd25519 no Ed25519 collisions");
|
||||||
|
h.update([1]); // Ed25519ph
|
||||||
|
h.update([0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
h.update(sk.hash_prefix);
|
||||||
|
h.update_iter(message.clone());
|
||||||
|
|
||||||
|
let r = Scalar::from_hash(h);
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
let R = EdwardsPoint::mul_base(&r).compress();
|
||||||
|
|
||||||
|
h = Sha512::new();
|
||||||
|
|
||||||
|
if pre_hash {
|
||||||
|
h.update(b"SigEd25519 no Ed25519 collisions");
|
||||||
|
h.update([1]); // Ed25519ph
|
||||||
|
h.update([0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
h.update(R.as_bytes());
|
||||||
|
h.update(sk.verifying_key.compressed.0);
|
||||||
|
h.update_iter(message);
|
||||||
|
|
||||||
|
let k = Scalar::from_hash(h);
|
||||||
|
let s: Scalar = (k * sk.scalar) + r;
|
||||||
|
|
||||||
|
Signature { R, s }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn verify<'a>(
|
||||||
|
pk: &VerifyingKey,
|
||||||
|
pre_hash: bool,
|
||||||
|
message: impl Iterator<Item = &'a [u8]>,
|
||||||
|
signature: &Signature,
|
||||||
|
) -> Result<(), ProtocolError> {
|
||||||
|
let mut h = Sha512::new();
|
||||||
|
|
||||||
|
if pre_hash {
|
||||||
|
h.update(b"SigEd25519 no Ed25519 collisions");
|
||||||
|
h.update([1]); // Ed25519ph
|
||||||
|
h.update([0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
h.update(signature.R.as_bytes());
|
||||||
|
h.update(pk.compressed.as_bytes());
|
||||||
|
h.update_iter(message);
|
||||||
|
let k = Scalar::from_hash(h);
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
let minus_A: EdwardsPoint = -pk.point;
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
let expected_R =
|
||||||
|
EdwardsPoint::vartime_double_scalar_mul_basepoint(&k, &(minus_A), &signature.s).compress();
|
||||||
|
|
||||||
|
if expected_R == signature.R {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(ProtocolError::InvalidLoginError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Ed25519 Signature.
|
||||||
|
// `ed25519_dalek::Signature` doesn't implement validation with Serde de/serialization.
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
pub struct Signature {
|
||||||
|
R: CompressedEdwardsY,
|
||||||
|
s: Scalar,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Signature {
|
||||||
|
/// Expects the `R` and `s` components of a Ed25519 signature with no added
|
||||||
|
/// framing.
|
||||||
|
pub fn from_slice(mut bytes: &[u8]) -> Result<Self, ProtocolError> {
|
||||||
|
Self::deserialize_take(&mut bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_take(bytes: &mut &[u8]) -> Result<Self, ProtocolError> {
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
let R = CompressedEdwardsY(bytes.take_array("signature R")?.into());
|
||||||
|
|
||||||
|
let s = Scalar::from_canonical_bytes(bytes.take_array("signature s")?.into())
|
||||||
|
.into_option()
|
||||||
|
.ok_or(ProtocolError::SerializationError)?;
|
||||||
|
|
||||||
|
Ok(Self { R, s })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize(&self) -> GenericArray<u8, U64> {
|
||||||
|
GenericArray::from(self.R.0).concat(GenericArray::from(self.s.to_bytes()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
impl<'de> serde::Deserialize<'de> for Signature {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
use serde::de::Error;
|
||||||
|
|
||||||
|
Signature::deserialize_take(
|
||||||
|
&mut (GenericArray::<_, U64>::deserialize(deserializer)?.as_slice()),
|
||||||
|
)
|
||||||
|
.map_err(D::Error::custom)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
impl serde::Serialize for Signature {
|
||||||
|
fn serialize<SK>(&self, serializer: SK) -> Result<SK::Ok, SK::Error>
|
||||||
|
where
|
||||||
|
SK: serde::Serializer,
|
||||||
|
{
|
||||||
|
self.serialize().serialize(serializer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Zeroize for Signature {
|
||||||
|
fn zeroize(&mut self) {
|
||||||
|
self.R.0 = [0; 32];
|
||||||
|
self.s = Scalar::default();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////
|
||||||
|
// Test Implementations //
|
||||||
|
//===================== //
|
||||||
|
//////////////////////////
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
use crate::serialization::AssertZeroized;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
impl AssertZeroized for VerifyingKey {
|
||||||
|
fn assert_zeroized(&self) {
|
||||||
|
use curve25519_dalek::traits::Identity;
|
||||||
|
|
||||||
|
let Self { point, compressed } = self;
|
||||||
|
|
||||||
|
assert_eq!(point, &EdwardsPoint::identity());
|
||||||
|
assert_eq!(compressed, &EdwardsPoint::identity().compress());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
impl AssertZeroized for SigningKey {
|
||||||
|
fn assert_zeroized(&self) {
|
||||||
|
let Self {
|
||||||
|
sk,
|
||||||
|
verifying_key,
|
||||||
|
scalar,
|
||||||
|
hash_prefix,
|
||||||
|
} = self;
|
||||||
|
|
||||||
|
verifying_key.assert_zeroized();
|
||||||
|
|
||||||
|
for byte in sk.iter().chain(scalar.to_bytes().iter()).chain(hash_prefix) {
|
||||||
|
assert_eq!(byte, &0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use std::iter;
|
||||||
|
|
||||||
|
use ed25519_dalek::{Signer, SigningKey, Verifier, VerifyingKey};
|
||||||
|
use rand::rngs::OsRng;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn pure_eddsa() {
|
||||||
|
let mut message = [0; 1024];
|
||||||
|
OsRng.fill_bytes(&mut message);
|
||||||
|
|
||||||
|
let mut sk = SecretKey::default();
|
||||||
|
OsRng.fill_bytes(&mut sk);
|
||||||
|
let signing_key = SigningKey::from_bytes(&sk);
|
||||||
|
|
||||||
|
let signature = signing_key.sign(&message);
|
||||||
|
|
||||||
|
let custom_sk = Ed25519::deserialize_take_sk(&mut sk.as_slice()).unwrap();
|
||||||
|
let custom_signature = sign(&custom_sk, false, iter::once(message.as_slice()));
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
signature.to_bytes(),
|
||||||
|
custom_signature.serialize().as_slice()
|
||||||
|
);
|
||||||
|
|
||||||
|
let verifying_key = VerifyingKey::from(&signing_key);
|
||||||
|
verifying_key.verify(&message, &signature).unwrap();
|
||||||
|
|
||||||
|
let custom_pk = Ed25519::public_key(custom_sk);
|
||||||
|
verify(
|
||||||
|
&custom_pk,
|
||||||
|
false,
|
||||||
|
iter::once(message.as_slice()),
|
||||||
|
&custom_signature,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn hash_eddsa() {
|
||||||
|
let mut message = [0; 1024];
|
||||||
|
OsRng.fill_bytes(&mut message);
|
||||||
|
let message = Sha512::new_with_prefix(message);
|
||||||
|
let pre_hash = message.clone().finalize();
|
||||||
|
|
||||||
|
let mut sk = SecretKey::default();
|
||||||
|
OsRng.fill_bytes(&mut sk);
|
||||||
|
let signing_key = SigningKey::from_bytes(&sk);
|
||||||
|
|
||||||
|
let signature = signing_key.sign_prehashed(message.clone(), None).unwrap();
|
||||||
|
|
||||||
|
let custom_sk = Ed25519::deserialize_take_sk(&mut sk.as_slice()).unwrap();
|
||||||
|
let custom_signature = sign(&custom_sk, true, iter::once(pre_hash.as_slice()));
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
signature.to_bytes(),
|
||||||
|
custom_signature.serialize().as_slice()
|
||||||
|
);
|
||||||
|
|
||||||
|
let verifying_key = VerifyingKey::from(&signing_key);
|
||||||
|
verifying_key
|
||||||
|
.verify_prehashed(message, None, &signature)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let custom_pk = Ed25519::public_key(custom_sk);
|
||||||
|
verify(
|
||||||
|
&custom_pk,
|
||||||
|
true,
|
||||||
|
iter::once(pre_hash.as_slice()),
|
||||||
|
&custom_signature,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,101 +6,157 @@
|
|||||||
// of this source tree. You may select, at your option, one of the above-listed
|
// of this source tree. You may select, at your option, one of the above-listed
|
||||||
// licenses.
|
// licenses.
|
||||||
|
|
||||||
use digest::core_api::BlockSizeUser;
|
//! Implementation for EC curves via [`elliptic_curve`] traits.
|
||||||
use digest::{FixedOutput, HashMarker};
|
|
||||||
use elliptic_curve::group::cofactor::CofactorGroup;
|
use core::fmt::{self, Debug, Formatter};
|
||||||
use elliptic_curve::hash2curve::{ExpandMsgXmd, FromOkm, GroupDigest};
|
|
||||||
use elliptic_curve::sec1::{FromEncodedPoint, ModulusSize, ToEncodedPoint};
|
use derive_where::derive_where;
|
||||||
|
use elliptic_curve::group::GroupEncoding;
|
||||||
|
use elliptic_curve::ops::MulByGenerator;
|
||||||
|
use elliptic_curve::sec1::{ModulusSize, ToEncodedPoint};
|
||||||
use elliptic_curve::{
|
use elliptic_curve::{
|
||||||
AffinePoint, Field, FieldBytesSize, Group, ProjectivePoint, PublicKey, Scalar, SecretKey,
|
point, CurveArithmetic, FieldBytesSize, Group as _, NonZeroScalar, ProjectivePoint, Scalar,
|
||||||
|
SecretKey,
|
||||||
};
|
};
|
||||||
use generic_array::typenum::{IsLess, IsLessOrEqual, U256};
|
|
||||||
use generic_array::GenericArray;
|
use generic_array::GenericArray;
|
||||||
use rand::{CryptoRng, RngCore};
|
use rand::{CryptoRng, RngCore};
|
||||||
|
use voprf::Mode;
|
||||||
|
use zeroize::Zeroize;
|
||||||
|
|
||||||
use super::KeGroup;
|
use super::{Group, STR_OPAQUE_DERIVE_AUTH_KEY_PAIR};
|
||||||
use crate::errors::{InternalError, ProtocolError};
|
use crate::errors::{InternalError, ProtocolError};
|
||||||
use crate::key_exchange::tripledh::DiffieHellman;
|
use crate::key_exchange::shared::DiffieHellman;
|
||||||
|
use crate::serialization::SliceExt;
|
||||||
|
|
||||||
impl<G> KeGroup for G
|
impl<G> Group for G
|
||||||
where
|
where
|
||||||
G: GroupDigest,
|
Self: CurveArithmetic + voprf::CipherSuite<Group = Self> + voprf::Group<Scalar = Scalar<Self>>,
|
||||||
FieldBytesSize<Self>: ModulusSize,
|
FieldBytesSize<Self>: ModulusSize,
|
||||||
AffinePoint<Self>: FromEncodedPoint<Self> + ToEncodedPoint<Self>,
|
ProjectivePoint<Self>: GroupEncoding<
|
||||||
ProjectivePoint<Self>: CofactorGroup + ToEncodedPoint<Self>,
|
Repr = GenericArray<u8, <FieldBytesSize<Self> as ModulusSize>::CompressedPointSize>,
|
||||||
Scalar<Self>: FromOkm,
|
> + ToEncodedPoint<Self>,
|
||||||
{
|
{
|
||||||
type Pk = ProjectivePoint<Self>;
|
type Pk = NonIdentity<Self>;
|
||||||
|
|
||||||
type PkLen = <FieldBytesSize<Self> as ModulusSize>::CompressedPointSize;
|
type PkLen = <FieldBytesSize<Self> as ModulusSize>::CompressedPointSize;
|
||||||
|
|
||||||
type Sk = Scalar<Self>;
|
type Sk = NonZeroScalar<Self>;
|
||||||
|
|
||||||
type SkLen = FieldBytesSize<Self>;
|
type SkLen = FieldBytesSize<Self>;
|
||||||
|
|
||||||
fn serialize_pk(pk: Self::Pk) -> GenericArray<u8, Self::PkLen> {
|
fn serialize_pk(pk: Self::Pk) -> GenericArray<u8, Self::PkLen> {
|
||||||
GenericArray::clone_from_slice(pk.to_encoded_point(true).as_bytes())
|
GenericArray::clone_from_slice(pk.0.to_encoded_point(true).as_bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deserialize_pk(bytes: &[u8]) -> Result<Self::Pk, ProtocolError> {
|
fn deserialize_take_pk(bytes: &mut &[u8]) -> Result<Self::Pk, ProtocolError> {
|
||||||
PublicKey::<Self>::from_sec1_bytes(bytes)
|
point::NonIdentity::<ProjectivePoint<Self>>::from_bytes(&bytes.take_array("public key")?)
|
||||||
.map(|public_key| public_key.to_projective())
|
.into_option()
|
||||||
.map_err(|_| ProtocolError::SerializationError)
|
.map(NonIdentity)
|
||||||
|
.ok_or(ProtocolError::SerializationError)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn random_sk<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Sk {
|
fn random_sk<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Sk {
|
||||||
*SecretKey::<Self>::random(rng).to_nonzero_scalar()
|
SecretKey::<Self>::random(rng).to_nonzero_scalar()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implements the `HashToScalar()` function from
|
fn derive_scalar(seed: GenericArray<u8, Self::SkLen>) -> Result<Self::Sk, InternalError> {
|
||||||
// <https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-19.html#section-4>
|
voprf::derive_key::<Self>(&seed, &STR_OPAQUE_DERIVE_AUTH_KEY_PAIR, Mode::Oprf)
|
||||||
fn hash_to_scalar<H>(input: &[&[u8]], dst: &[&[u8]]) -> Result<Self::Sk, InternalError>
|
.map(|scalar| {
|
||||||
where
|
NonZeroScalar::new(scalar).expect("`voprf::derive_key()` returned a zero scalar")
|
||||||
H: BlockSizeUser + Default + FixedOutput + HashMarker,
|
|
||||||
H::OutputSize: IsLess<U256> + IsLessOrEqual<H::BlockSize>,
|
|
||||||
{
|
|
||||||
Self::hash_to_scalar::<ExpandMsgXmd<H>>(input, dst)
|
|
||||||
.map_err(|_| InternalError::HashToScalar)
|
|
||||||
.and_then(|scalar| {
|
|
||||||
if bool::from(scalar.is_zero()) {
|
|
||||||
Err(InternalError::HashToScalar)
|
|
||||||
} else {
|
|
||||||
Ok(scalar)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
.map_err(InternalError::from)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn public_key(sk: Self::Sk) -> Self::Pk {
|
fn public_key(sk: Self::Sk) -> Self::Pk {
|
||||||
ProjectivePoint::<Self>::generator() * sk
|
// Non-panicking version in https://github.com/RustCrypto/traits/pull/1833.
|
||||||
}
|
NonIdentity(
|
||||||
|
point::NonIdentity::new(ProjectivePoint::<Self>::mul_by_generator(&*sk))
|
||||||
fn is_zero_scalar(scalar: Self::Sk) -> subtle::Choice {
|
.expect("multiplying with a non-zero scalar can never yield the identity element"),
|
||||||
scalar.is_zero()
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn serialize_sk(sk: Self::Sk) -> GenericArray<u8, Self::SkLen> {
|
fn serialize_sk(sk: Self::Sk) -> GenericArray<u8, Self::SkLen> {
|
||||||
sk.into()
|
sk.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deserialize_sk(bytes: &[u8]) -> Result<Self::Sk, ProtocolError> {
|
fn deserialize_take_sk(bytes: &mut &[u8]) -> Result<Self::Sk, ProtocolError> {
|
||||||
SecretKey::<Self>::from_slice(bytes)
|
SecretKey::<Self>::from_bytes(&bytes.take_array("secret key")?)
|
||||||
.map(|secret_key| *secret_key.to_nonzero_scalar())
|
.map(|secret_key| secret_key.to_nonzero_scalar())
|
||||||
.map_err(|_| ProtocolError::SerializationError)
|
.map_err(|_| ProtocolError::SerializationError)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<G> DiffieHellman<G> for Scalar<G>
|
/// Wrapper around [`NonIdentity`](point::NonIdentity) to implement [`Zeroize`].
|
||||||
|
// TODO: remove after https://github.com/RustCrypto/traits/pull/1832.
|
||||||
|
#[derive_where(Clone, Copy)]
|
||||||
|
#[cfg_attr(
|
||||||
|
feature = "serde",
|
||||||
|
derive(serde::Deserialize, serde::Serialize),
|
||||||
|
serde(bound(
|
||||||
|
deserialize = "point::NonIdentity<ProjectivePoint<G>>: serde::Deserialize<'de>",
|
||||||
|
serialize = "point::NonIdentity<ProjectivePoint<G>>: serde::Serialize"
|
||||||
|
))
|
||||||
|
)]
|
||||||
|
pub struct NonIdentity<G: CurveArithmetic>(pub point::NonIdentity<ProjectivePoint<G>>);
|
||||||
|
|
||||||
|
impl<G: CurveArithmetic> Debug for NonIdentity<G> {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||||
|
f.debug_tuple("NonIdentity")
|
||||||
|
.field(&self.0.to_point())
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<G: CurveArithmetic> PartialEq for NonIdentity<G> {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.0.to_point().eq(&other.0.to_point())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<G: CurveArithmetic> Eq for NonIdentity<G> {}
|
||||||
|
|
||||||
|
impl<G: CurveArithmetic> Zeroize for NonIdentity<G> {
|
||||||
|
fn zeroize(&mut self) {
|
||||||
|
self.0 = point::NonIdentity::new(ProjectivePoint::<G>::generator()).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<G> DiffieHellman<G> for NonZeroScalar<G>
|
||||||
where
|
where
|
||||||
G: GroupDigest,
|
G: CurveArithmetic + voprf::CipherSuite<Group = G> + voprf::Group<Scalar = Scalar<G>>,
|
||||||
FieldBytesSize<G>: ModulusSize,
|
FieldBytesSize<G>: ModulusSize,
|
||||||
AffinePoint<G>: FromEncodedPoint<G> + ToEncodedPoint<G>,
|
ProjectivePoint<G>: GroupEncoding<
|
||||||
ProjectivePoint<G>: CofactorGroup + ToEncodedPoint<G>,
|
Repr = GenericArray<u8, <FieldBytesSize<G> as ModulusSize>::CompressedPointSize>,
|
||||||
Scalar<G>: FromOkm,
|
> + ToEncodedPoint<G>,
|
||||||
{
|
{
|
||||||
fn diffie_hellman(
|
fn diffie_hellman(
|
||||||
self,
|
self,
|
||||||
pk: ProjectivePoint<G>,
|
pk: NonIdentity<G>,
|
||||||
) -> GenericArray<u8, <FieldBytesSize<G> as ModulusSize>::CompressedPointSize> {
|
) -> GenericArray<u8, <FieldBytesSize<G> as ModulusSize>::CompressedPointSize> {
|
||||||
GenericArray::clone_from_slice((pk * self).to_encoded_point(true).as_bytes())
|
GenericArray::clone_from_slice((pk.0 * self).to_encoded_point(true).as_bytes())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////
|
||||||
|
// Test Implementations //
|
||||||
|
//===================== //
|
||||||
|
//////////////////////////
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
use crate::serialization::AssertZeroized;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
impl<G: CurveArithmetic> AssertZeroized for NonIdentity<G> {
|
||||||
|
fn assert_zeroized(&self) {
|
||||||
|
assert_eq!(self.0.to_point(), ProjectivePoint::<G>::generator());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
impl<G: CurveArithmetic> AssertZeroized for NonZeroScalar<G> {
|
||||||
|
fn assert_zeroized(&self) {
|
||||||
|
use elliptic_curve::Field;
|
||||||
|
|
||||||
|
assert_eq!(**self, Scalar::<G>::ONE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,18 +6,16 @@
|
|||||||
// of this source tree. You may select, at your option, one of the above-listed
|
// of this source tree. You may select, at your option, one of the above-listed
|
||||||
// licenses.
|
// licenses.
|
||||||
|
|
||||||
//! Includes the [`KeGroup`] trait and definitions for the key exchange groups
|
//! Includes the [`Group`] trait and definitions for the key exchange groups
|
||||||
|
|
||||||
#[cfg(feature = "curve25519")]
|
#[cfg(feature = "curve25519")]
|
||||||
pub mod curve25519;
|
pub mod curve25519;
|
||||||
mod elliptic_curve;
|
#[cfg(feature = "ed25519")]
|
||||||
|
pub mod ed25519;
|
||||||
|
pub mod elliptic_curve;
|
||||||
#[cfg(feature = "ristretto255")]
|
#[cfg(feature = "ristretto255")]
|
||||||
pub mod ristretto255;
|
pub mod ristretto255;
|
||||||
|
|
||||||
use digest::core_api::BlockSizeUser;
|
|
||||||
use digest::{FixedOutput, HashMarker, OutputSizeUser};
|
|
||||||
use generic_array::sequence::Concat;
|
|
||||||
use generic_array::typenum::{IsLess, IsLessOrEqual, U256};
|
|
||||||
use generic_array::{ArrayLength, GenericArray};
|
use generic_array::{ArrayLength, GenericArray};
|
||||||
use rand::{CryptoRng, RngCore};
|
use rand::{CryptoRng, RngCore};
|
||||||
use zeroize::Zeroize;
|
use zeroize::Zeroize;
|
||||||
@@ -27,7 +25,7 @@ use crate::errors::{InternalError, ProtocolError};
|
|||||||
const STR_OPAQUE_DERIVE_AUTH_KEY_PAIR: [u8; 33] = *b"OPAQUE-DeriveDiffieHellmanKeyPair";
|
const STR_OPAQUE_DERIVE_AUTH_KEY_PAIR: [u8; 33] = *b"OPAQUE-DeriveDiffieHellmanKeyPair";
|
||||||
|
|
||||||
/// A group representation for use in the key exchange
|
/// A group representation for use in the key exchange
|
||||||
pub trait KeGroup {
|
pub trait Group {
|
||||||
/// Public key
|
/// Public key
|
||||||
type Pk: Copy + Zeroize;
|
type Pk: Copy + Zeroize;
|
||||||
/// Length of the public key
|
/// Length of the public key
|
||||||
@@ -41,65 +39,15 @@ pub trait KeGroup {
|
|||||||
fn serialize_pk(pk: Self::Pk) -> GenericArray<u8, Self::PkLen>;
|
fn serialize_pk(pk: Self::Pk) -> GenericArray<u8, Self::PkLen>;
|
||||||
|
|
||||||
/// Return a public key from its fixed-length bytes representation
|
/// Return a public key from its fixed-length bytes representation
|
||||||
fn deserialize_pk(bytes: &[u8]) -> Result<Self::Pk, ProtocolError>;
|
///
|
||||||
|
/// The deserialized bytes must be taken from `bytes`.
|
||||||
|
fn deserialize_take_pk(bytes: &mut &[u8]) -> Result<Self::Pk, ProtocolError>;
|
||||||
|
|
||||||
/// Generate a random secret key
|
/// Generate a random secret key
|
||||||
fn random_sk<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Sk;
|
fn random_sk<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Sk;
|
||||||
|
|
||||||
/// Hashes a slice of pseudo-random bytes to a scalar
|
/// Deterministically derive a [`Self::Sk`] from `seed`.
|
||||||
///
|
fn derive_scalar(seed: GenericArray<u8, Self::SkLen>) -> Result<Self::Sk, InternalError>;
|
||||||
/// # Errors
|
|
||||||
/// [`InternalError::HashToScalar`] if the `input` is empty or longer then
|
|
||||||
/// [`u16::MAX`].
|
|
||||||
fn hash_to_scalar<H>(input: &[&[u8]], dst: &[&[u8]]) -> Result<Self::Sk, InternalError>
|
|
||||||
where
|
|
||||||
H: BlockSizeUser + Default + FixedOutput + HashMarker,
|
|
||||||
H::OutputSize: IsLess<U256> + IsLessOrEqual<H::BlockSize>;
|
|
||||||
|
|
||||||
/// Corresponds to the `DeriveAuthKeyPair()` function defined in
|
|
||||||
/// <https://www.ietf.org/archive/id/draft-irtf-cfrg-opaque-08.html#section-6.4.2>
|
|
||||||
///
|
|
||||||
/// Note that we cannot call the voprf crate directly since we need to
|
|
||||||
/// ensure that the [`KeGroup`] is used for the
|
|
||||||
/// [`hash_to_scalar`](Self::hash_to_scalar) operation (as opposed to
|
|
||||||
/// the [`OprfGroup`](voprf::Group)).
|
|
||||||
fn derive_auth_keypair<CS: voprf::CipherSuite>(
|
|
||||||
seed: GenericArray<u8, Self::SkLen>,
|
|
||||||
) -> Result<Self::Sk, InternalError>
|
|
||||||
where
|
|
||||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
|
||||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
|
||||||
{
|
|
||||||
let info = &STR_OPAQUE_DERIVE_AUTH_KEY_PAIR;
|
|
||||||
let dst_1 = GenericArray::from(STR_DERIVE_KEYPAIR)
|
|
||||||
.concat(STR_OPRF.into())
|
|
||||||
.concat([voprf::Mode::Oprf.to_u8()].into())
|
|
||||||
.concat([b'-'].into());
|
|
||||||
let dst_2 = CS::ID.as_bytes();
|
|
||||||
|
|
||||||
let info_len = i2osp_2(info.len())
|
|
||||||
.map_err(|_| InternalError::OprfError(voprf::Error::DeriveKeyPair))?;
|
|
||||||
|
|
||||||
for counter in 0_u8..=u8::MAX {
|
|
||||||
// deriveInput = seed || I2OSP(len(info), 2) || info
|
|
||||||
// skS = G.HashToScalar(deriveInput || I2OSP(counter, 1), DST = "DeriveKeyPair"
|
|
||||||
// || contextString)
|
|
||||||
let sk_s = Self::hash_to_scalar::<CS::Hash>(
|
|
||||||
&[&seed, &info_len, info, &counter.to_be_bytes()],
|
|
||||||
&[&dst_1, dst_2],
|
|
||||||
)
|
|
||||||
.map_err(|_| InternalError::OprfError(voprf::Error::DeriveKeyPair))?;
|
|
||||||
|
|
||||||
if !bool::from(Self::is_zero_scalar(sk_s)) {
|
|
||||||
return Ok(sk_s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Err(InternalError::OprfError(voprf::Error::DeriveKeyPair))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns `true` if the scalar is zero.
|
|
||||||
fn is_zero_scalar(scalar: Self::Sk) -> subtle::Choice;
|
|
||||||
|
|
||||||
/// Return a public key from its secret key
|
/// Return a public key from its secret key
|
||||||
fn public_key(sk: Self::Sk) -> Self::Pk;
|
fn public_key(sk: Self::Sk) -> Self::Pk;
|
||||||
@@ -108,17 +56,7 @@ pub trait KeGroup {
|
|||||||
fn serialize_sk(sk: Self::Sk) -> GenericArray<u8, Self::SkLen>;
|
fn serialize_sk(sk: Self::Sk) -> GenericArray<u8, Self::SkLen>;
|
||||||
|
|
||||||
/// Return a public key from its fixed-length bytes representation
|
/// Return a public key from its fixed-length bytes representation
|
||||||
fn deserialize_sk(bytes: &[u8]) -> Result<Self::Sk, ProtocolError>;
|
///
|
||||||
}
|
/// The deserialized bytes must be taken from `bytes`.
|
||||||
|
fn deserialize_take_sk(bytes: &mut &[u8]) -> Result<Self::Sk, ProtocolError>;
|
||||||
// Helper functions used to compute DeriveAuthKeyPair() (taken from the voprf
|
|
||||||
// crate)
|
|
||||||
|
|
||||||
const STR_OPRF: [u8; 7] = *b"OPRFV1-";
|
|
||||||
const STR_DERIVE_KEYPAIR: [u8; 13] = *b"DeriveKeyPair";
|
|
||||||
|
|
||||||
fn i2osp_2(input: usize) -> Result<[u8; 2], InternalError> {
|
|
||||||
u16::try_from(input)
|
|
||||||
.map(|input| input.to_be_bytes())
|
|
||||||
.map_err(|_| InternalError::OprfInternalError(voprf::InternalError::I2osp))
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
//! Key Exchange group implementation for ristretto255
|
//! Key Exchange group implementation for ristretto255
|
||||||
|
|
||||||
|
pub use curve25519_dalek;
|
||||||
use curve25519_dalek::constants::RISTRETTO_BASEPOINT_POINT;
|
use curve25519_dalek::constants::RISTRETTO_BASEPOINT_POINT;
|
||||||
use curve25519_dalek::ristretto::{CompressedRistretto, RistrettoPoint};
|
use curve25519_dalek::ristretto::{CompressedRistretto, RistrettoPoint};
|
||||||
use curve25519_dalek::scalar::Scalar;
|
use curve25519_dalek::scalar::Scalar;
|
||||||
@@ -17,19 +18,19 @@ use digest::{FixedOutput, HashMarker};
|
|||||||
use generic_array::typenum::{IsLess, IsLessOrEqual, U256, U32};
|
use generic_array::typenum::{IsLess, IsLessOrEqual, U256, U32};
|
||||||
use generic_array::GenericArray;
|
use generic_array::GenericArray;
|
||||||
use rand::{CryptoRng, RngCore};
|
use rand::{CryptoRng, RngCore};
|
||||||
use subtle::ConstantTimeEq;
|
use voprf::Mode;
|
||||||
use voprf::Group;
|
|
||||||
|
|
||||||
use super::KeGroup;
|
use super::{Group, STR_OPAQUE_DERIVE_AUTH_KEY_PAIR};
|
||||||
use crate::errors::{InternalError, ProtocolError};
|
use crate::errors::{InternalError, ProtocolError};
|
||||||
use crate::key_exchange::tripledh::DiffieHellman;
|
use crate::key_exchange::shared::DiffieHellman;
|
||||||
|
use crate::serialization::SliceExt;
|
||||||
|
|
||||||
/// Implementation for Ristretto255.
|
/// Implementation for Ristretto255.
|
||||||
// This is necessary because Rust lacks specialization, otherwise we could
|
// This is necessary because Rust lacks specialization, otherwise we could
|
||||||
// implement `KeGroup` for `voprf::Ristretto255`.
|
// implement `KeGroup` for `voprf::Ristretto255`.
|
||||||
pub struct Ristretto255;
|
pub struct Ristretto255;
|
||||||
|
|
||||||
impl KeGroup for Ristretto255 {
|
impl Group for Ristretto255 {
|
||||||
type Pk = RistrettoPoint;
|
type Pk = RistrettoPoint;
|
||||||
type PkLen = U32;
|
type PkLen = U32;
|
||||||
type Sk = Scalar;
|
type Sk = Scalar;
|
||||||
@@ -39,8 +40,8 @@ impl KeGroup for Ristretto255 {
|
|||||||
pk.compress().to_bytes().into()
|
pk.compress().to_bytes().into()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deserialize_pk(bytes: &[u8]) -> Result<Self::Pk, ProtocolError> {
|
fn deserialize_take_pk(bytes: &mut &[u8]) -> Result<Self::Pk, ProtocolError> {
|
||||||
CompressedRistretto::from_slice(bytes)
|
CompressedRistretto::from_slice(&bytes.take_array::<U32>("public key")?)
|
||||||
.map_err(|_| ProtocolError::SerializationError)?
|
.map_err(|_| ProtocolError::SerializationError)?
|
||||||
.decompress()
|
.decompress()
|
||||||
.filter(|point| point != &RistrettoPoint::identity())
|
.filter(|point| point != &RistrettoPoint::identity())
|
||||||
@@ -49,21 +50,7 @@ impl KeGroup for Ristretto255 {
|
|||||||
|
|
||||||
fn random_sk<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Sk {
|
fn random_sk<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Sk {
|
||||||
loop {
|
loop {
|
||||||
let scalar = {
|
let scalar = Scalar::random(rng);
|
||||||
#[cfg(not(test))]
|
|
||||||
{
|
|
||||||
Scalar::random(rng)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tests need an exact conversion from bytes to scalar, sampling only 32 bytes
|
|
||||||
// from rng
|
|
||||||
#[cfg(test)]
|
|
||||||
{
|
|
||||||
let mut scalar_bytes = [0u8; 32];
|
|
||||||
rng.fill_bytes(&mut scalar_bytes);
|
|
||||||
Scalar::from_bytes_mod_order(scalar_bytes)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if scalar != Scalar::ZERO {
|
if scalar != Scalar::ZERO {
|
||||||
break scalar;
|
break scalar;
|
||||||
@@ -71,19 +58,9 @@ impl KeGroup for Ristretto255 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implements the `HashToScalar()` function from
|
fn derive_scalar(seed: GenericArray<u8, Self::SkLen>) -> Result<Self::Sk, InternalError> {
|
||||||
// <https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-19.html#section-4>
|
voprf::derive_key::<Self>(&seed, &STR_OPAQUE_DERIVE_AUTH_KEY_PAIR, Mode::Oprf)
|
||||||
fn hash_to_scalar<'a, H>(input: &[&[u8]], dst: &[&[u8]]) -> Result<Self::Sk, InternalError>
|
.map_err(InternalError::from)
|
||||||
where
|
|
||||||
H: BlockSizeUser + Default + FixedOutput + HashMarker,
|
|
||||||
H::OutputSize: IsLess<U256> + IsLessOrEqual<H::BlockSize>,
|
|
||||||
{
|
|
||||||
<voprf::Ristretto255 as Group>::hash_to_scalar::<H>(input, dst)
|
|
||||||
.map_err(InternalError::OprfInternalError)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_zero_scalar(scalar: Self::Sk) -> subtle::Choice {
|
|
||||||
scalar.ct_eq(&Scalar::ZERO)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn public_key(sk: Self::Sk) -> Self::Pk {
|
fn public_key(sk: Self::Sk) -> Self::Pk {
|
||||||
@@ -94,17 +71,16 @@ impl KeGroup for Ristretto255 {
|
|||||||
sk.to_bytes().into()
|
sk.to_bytes().into()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deserialize_sk(bytes: &[u8]) -> Result<Self::Sk, ProtocolError> {
|
fn deserialize_take_sk(bytes: &mut &[u8]) -> Result<Self::Sk, ProtocolError> {
|
||||||
bytes
|
bytes
|
||||||
.try_into()
|
.take_array::<U32>("secret key")
|
||||||
.ok()
|
.ok()
|
||||||
.and_then(|bytes| Scalar::from_canonical_bytes(bytes).into())
|
.and_then(|bytes| Scalar::from_canonical_bytes(bytes.into()).into())
|
||||||
.filter(|scalar| scalar != &Scalar::ZERO)
|
.filter(|scalar| scalar != &Scalar::ZERO)
|
||||||
.ok_or(ProtocolError::SerializationError)
|
.ok_or(ProtocolError::SerializationError)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "ristretto255-voprf")]
|
|
||||||
impl voprf::CipherSuite for Ristretto255 {
|
impl voprf::CipherSuite for Ristretto255 {
|
||||||
const ID: &'static str = voprf::Ristretto255::ID;
|
const ID: &'static str = voprf::Ristretto255::ID;
|
||||||
|
|
||||||
@@ -113,14 +89,14 @@ impl voprf::CipherSuite for Ristretto255 {
|
|||||||
type Hash = <voprf::Ristretto255 as voprf::CipherSuite>::Hash;
|
type Hash = <voprf::Ristretto255 as voprf::CipherSuite>::Hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Group for Ristretto255 {
|
impl voprf::Group for Ristretto255 {
|
||||||
type Elem = <voprf::Ristretto255 as Group>::Elem;
|
type Elem = <voprf::Ristretto255 as voprf::Group>::Elem;
|
||||||
|
|
||||||
type ElemLen = <voprf::Ristretto255 as Group>::ElemLen;
|
type ElemLen = <voprf::Ristretto255 as voprf::Group>::ElemLen;
|
||||||
|
|
||||||
type Scalar = <voprf::Ristretto255 as Group>::Scalar;
|
type Scalar = <voprf::Ristretto255 as voprf::Group>::Scalar;
|
||||||
|
|
||||||
type ScalarLen = <voprf::Ristretto255 as Group>::ScalarLen;
|
type ScalarLen = <voprf::Ristretto255 as voprf::Group>::ScalarLen;
|
||||||
|
|
||||||
fn hash_to_curve<H>(
|
fn hash_to_curve<H>(
|
||||||
input: &[&[u8]],
|
input: &[&[u8]],
|
||||||
@@ -130,7 +106,7 @@ impl Group for Ristretto255 {
|
|||||||
H: BlockSizeUser + Default + FixedOutput + HashMarker,
|
H: BlockSizeUser + Default + FixedOutput + HashMarker,
|
||||||
H::OutputSize: IsLess<U256> + IsLessOrEqual<H::BlockSize>,
|
H::OutputSize: IsLess<U256> + IsLessOrEqual<H::BlockSize>,
|
||||||
{
|
{
|
||||||
<voprf::Ristretto255 as Group>::hash_to_curve::<H>(input, dst)
|
<voprf::Ristretto255 as voprf::Group>::hash_to_curve::<H>(input, dst)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn hash_to_scalar<H>(
|
fn hash_to_scalar<H>(
|
||||||
@@ -141,43 +117,43 @@ impl Group for Ristretto255 {
|
|||||||
H: BlockSizeUser + Default + FixedOutput + HashMarker,
|
H: BlockSizeUser + Default + FixedOutput + HashMarker,
|
||||||
H::OutputSize: IsLess<U256> + IsLessOrEqual<H::BlockSize>,
|
H::OutputSize: IsLess<U256> + IsLessOrEqual<H::BlockSize>,
|
||||||
{
|
{
|
||||||
<voprf::Ristretto255 as Group>::hash_to_scalar::<H>(input, dst)
|
<voprf::Ristretto255 as voprf::Group>::hash_to_scalar::<H>(input, dst)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn base_elem() -> Self::Elem {
|
fn base_elem() -> Self::Elem {
|
||||||
<voprf::Ristretto255 as Group>::base_elem()
|
<voprf::Ristretto255 as voprf::Group>::base_elem()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn identity_elem() -> Self::Elem {
|
fn identity_elem() -> Self::Elem {
|
||||||
<voprf::Ristretto255 as Group>::identity_elem()
|
<voprf::Ristretto255 as voprf::Group>::identity_elem()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn serialize_elem(elem: Self::Elem) -> GenericArray<u8, Self::ElemLen> {
|
fn serialize_elem(elem: Self::Elem) -> GenericArray<u8, Self::ElemLen> {
|
||||||
<voprf::Ristretto255 as Group>::serialize_elem(elem)
|
<voprf::Ristretto255 as voprf::Group>::serialize_elem(elem)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deserialize_elem(element_bits: &[u8]) -> voprf::Result<Self::Elem> {
|
fn deserialize_elem(element_bits: &[u8]) -> voprf::Result<Self::Elem> {
|
||||||
<voprf::Ristretto255 as Group>::deserialize_elem(element_bits)
|
<voprf::Ristretto255 as voprf::Group>::deserialize_elem(element_bits)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn random_scalar<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Scalar {
|
fn random_scalar<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Scalar {
|
||||||
<voprf::Ristretto255 as Group>::random_scalar(rng)
|
<voprf::Ristretto255 as voprf::Group>::random_scalar(rng)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn invert_scalar(scalar: Self::Scalar) -> Self::Scalar {
|
fn invert_scalar(scalar: Self::Scalar) -> Self::Scalar {
|
||||||
<voprf::Ristretto255 as Group>::invert_scalar(scalar)
|
<voprf::Ristretto255 as voprf::Group>::invert_scalar(scalar)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_zero_scalar(scalar: Self::Scalar) -> subtle::Choice {
|
fn is_zero_scalar(scalar: Self::Scalar) -> subtle::Choice {
|
||||||
<voprf::Ristretto255 as Group>::is_zero_scalar(scalar)
|
<voprf::Ristretto255 as voprf::Group>::is_zero_scalar(scalar)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn serialize_scalar(scalar: Self::Scalar) -> GenericArray<u8, Self::ScalarLen> {
|
fn serialize_scalar(scalar: Self::Scalar) -> GenericArray<u8, Self::ScalarLen> {
|
||||||
<voprf::Ristretto255 as Group>::serialize_scalar(scalar)
|
<voprf::Ristretto255 as voprf::Group>::serialize_scalar(scalar)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deserialize_scalar(scalar_bits: &[u8]) -> voprf::Result<Self::Scalar> {
|
fn deserialize_scalar(scalar_bits: &[u8]) -> voprf::Result<Self::Scalar> {
|
||||||
<voprf::Ristretto255 as Group>::deserialize_scalar(scalar_bits)
|
<voprf::Ristretto255 as voprf::Group>::deserialize_scalar(scalar_bits)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -186,3 +162,25 @@ impl DiffieHellman<Ristretto255> for Scalar {
|
|||||||
Ristretto255::serialize_pk(pk * self)
|
Ristretto255::serialize_pk(pk * self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//////////////////////////
|
||||||
|
// Test Implementations //
|
||||||
|
//===================== //
|
||||||
|
//////////////////////////
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
use crate::serialization::AssertZeroized;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
impl AssertZeroized for RistrettoPoint {
|
||||||
|
fn assert_zeroized(&self) {
|
||||||
|
assert_eq!(*self, RistrettoPoint::default());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
impl AssertZeroized for Scalar {
|
||||||
|
fn assert_zeroized(&self) {
|
||||||
|
assert_eq!(*self, Scalar::default());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -10,5 +10,9 @@
|
|||||||
//! OPAQUE
|
//! OPAQUE
|
||||||
|
|
||||||
pub mod group;
|
pub mod group;
|
||||||
|
pub(crate) mod shared;
|
||||||
|
pub mod sigma_i;
|
||||||
pub(crate) mod traits;
|
pub(crate) mod traits;
|
||||||
pub mod tripledh;
|
pub mod tripledh;
|
||||||
|
|
||||||
|
pub use crate::key_exchange::traits::KeyExchange;
|
||||||
|
|||||||
@@ -0,0 +1,391 @@
|
|||||||
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||||
|
//
|
||||||
|
// This source code is dual-licensed under either the MIT license found in the
|
||||||
|
// LICENSE-MIT file in the root directory of this source tree or the Apache
|
||||||
|
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||||
|
// of this source tree. You may select, at your option, one of the above-listed
|
||||||
|
// licenses.
|
||||||
|
|
||||||
|
use core::ops::Add;
|
||||||
|
|
||||||
|
use derive_where::derive_where;
|
||||||
|
use digest::core_api::BlockSizeUser;
|
||||||
|
use digest::{Digest, Output, OutputSizeUser, Update};
|
||||||
|
use generic_array::sequence::Concat;
|
||||||
|
use generic_array::typenum::{IsLess, Le, NonZero, Sum, Unsigned, U1, U2, U256, U32};
|
||||||
|
use generic_array::{ArrayLength, GenericArray};
|
||||||
|
use hkdf::{Hkdf, HkdfExtract};
|
||||||
|
use rand::{CryptoRng, RngCore};
|
||||||
|
|
||||||
|
use crate::ciphersuite::{CipherSuite, KeGroup, KeHash};
|
||||||
|
use crate::errors::{InternalError, ProtocolError};
|
||||||
|
use crate::hash::{Hash, OutputSize, ProxyHash};
|
||||||
|
use crate::key_exchange::group::Group;
|
||||||
|
use crate::key_exchange::traits::{
|
||||||
|
CredentialRequestParts, CredentialResponseParts, Deserialize, Serialize, SerializedContext,
|
||||||
|
SerializedIdentifiers,
|
||||||
|
};
|
||||||
|
use crate::keypair::{KeyPair, PrivateKey, PublicKey};
|
||||||
|
use crate::serialization::{i2osp, SliceExt, UpdateExt};
|
||||||
|
|
||||||
|
///////////////
|
||||||
|
// Constants //
|
||||||
|
// ========= //
|
||||||
|
///////////////
|
||||||
|
|
||||||
|
pub(crate) type NonceLen = U32;
|
||||||
|
pub(super) static STR_CONTEXT: &[u8] = b"OPAQUEv1-";
|
||||||
|
static STR_CLIENT_MAC: &[u8] = b"ClientMAC";
|
||||||
|
static STR_HANDSHAKE_SECRET: &[u8] = b"HandshakeSecret";
|
||||||
|
static STR_SERVER_MAC: &[u8] = b"ServerMAC";
|
||||||
|
static STR_SESSION_KEY: &[u8] = b"SessionKey";
|
||||||
|
static STR_OPAQUE: &[u8] = b"OPAQUE-";
|
||||||
|
|
||||||
|
////////////////////////////
|
||||||
|
// High-level API Structs //
|
||||||
|
// ====================== //
|
||||||
|
////////////////////////////
|
||||||
|
|
||||||
|
/// Trait required by [`Group::Sk`] to be compatible with
|
||||||
|
/// [`TripleDh`](crate::TripleDh) and [`SigmaI`](crate::SigmaI).
|
||||||
|
pub trait DiffieHellman<G: Group> {
|
||||||
|
/// Diffie-Hellman key exchange.
|
||||||
|
fn diffie_hellman(self, pk: G::Pk) -> GenericArray<u8, G::PkLen>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The client state produced after the first key exchange message
|
||||||
|
#[cfg_attr(
|
||||||
|
feature = "serde",
|
||||||
|
derive(serde::Deserialize, serde::Serialize),
|
||||||
|
serde(bound = "")
|
||||||
|
)]
|
||||||
|
#[derive_where(Clone, ZeroizeOnDrop)]
|
||||||
|
#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; G::Sk)]
|
||||||
|
pub struct Ke1State<G: Group> {
|
||||||
|
pub(super) client_e_sk: PrivateKey<G>,
|
||||||
|
pub(super) client_nonce: GenericArray<u8, NonceLen>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The first key exchange message
|
||||||
|
#[cfg_attr(
|
||||||
|
feature = "serde",
|
||||||
|
derive(serde::Deserialize, serde::Serialize),
|
||||||
|
serde(bound = "")
|
||||||
|
)]
|
||||||
|
#[derive_where(Clone, ZeroizeOnDrop)]
|
||||||
|
#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; G::Pk)]
|
||||||
|
pub struct Ke1Message<G: Group> {
|
||||||
|
pub(super) client_nonce: GenericArray<u8, NonceLen>,
|
||||||
|
pub(super) client_e_pk: PublicKey<G>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////
|
||||||
|
// Convenience Structs //
|
||||||
|
//==================== //
|
||||||
|
/////////////////////////
|
||||||
|
|
||||||
|
// Consists of a session key, followed by two mac keys: (session_key, km2, km3)
|
||||||
|
pub(super) struct DerivedKeys<H: OutputSizeUser> {
|
||||||
|
pub(super) session_key: Output<H>,
|
||||||
|
pub(super) km2: Output<H>,
|
||||||
|
pub(super) km3: Output<H>,
|
||||||
|
#[cfg(test)]
|
||||||
|
pub(super) handshake_secret: Output<H>,
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////
|
||||||
|
// Helper functions and Trait Implementations //
|
||||||
|
// ========================================== //
|
||||||
|
////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Helper functions
|
||||||
|
|
||||||
|
pub(super) fn generate_ke1<R: RngCore + CryptoRng, G: Group>(
|
||||||
|
rng: &mut R,
|
||||||
|
) -> Result<(Ke1State<G>, Ke1Message<G>), ProtocolError> {
|
||||||
|
let client_e_kp = KeyPair::<G>::derive_random(rng);
|
||||||
|
let client_nonce = generate_nonce::<R>(rng);
|
||||||
|
|
||||||
|
let ke1_message = Ke1Message {
|
||||||
|
client_nonce,
|
||||||
|
client_e_pk: client_e_kp.public().clone(),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
Ke1State {
|
||||||
|
client_e_sk: client_e_kp.private().clone(),
|
||||||
|
client_nonce,
|
||||||
|
},
|
||||||
|
ke1_message,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate a random nonce up to NonceLen::USIZE bytes.
|
||||||
|
pub(super) fn generate_nonce<R: RngCore + CryptoRng>(rng: &mut R) -> GenericArray<u8, NonceLen> {
|
||||||
|
let mut nonce_bytes = GenericArray::default();
|
||||||
|
rng.fill_bytes(&mut nonce_bytes);
|
||||||
|
nonce_bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn transcript<CS: CipherSuite, KE: Group>(
|
||||||
|
context: &SerializedContext<'_>,
|
||||||
|
identifiers: &SerializedIdentifiers<'_, KeGroup<CS>>,
|
||||||
|
credential_request: &CredentialRequestParts<CS>,
|
||||||
|
ke1_message: &Ke1MessageIter<KE>,
|
||||||
|
credential_response: &CredentialResponseParts<CS>,
|
||||||
|
server_nonce: GenericArray<u8, NonceLen>,
|
||||||
|
server_e_pk: &GenericArray<u8, KE::PkLen>,
|
||||||
|
) -> KeHash<CS> {
|
||||||
|
KeHash::<CS>::new()
|
||||||
|
.chain_iter(context.iter())
|
||||||
|
.chain_iter(identifiers.client.iter())
|
||||||
|
.chain_iter(credential_request.iter())
|
||||||
|
.chain_iter(ke1_message.iter())
|
||||||
|
.chain_iter(identifiers.server.iter())
|
||||||
|
.chain_iter(credential_response.iter())
|
||||||
|
.chain(server_nonce)
|
||||||
|
.chain(server_e_pk)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Internal function which takes computed shared secrets, along with some
|
||||||
|
// auxiliary metadata, to produce the session key and two MAC keys
|
||||||
|
pub(super) fn derive_keys<'a, H: Hash>(
|
||||||
|
ikms: impl Iterator<Item = &'a [u8]>,
|
||||||
|
hashed_derivation_transcript: &[u8],
|
||||||
|
) -> Result<DerivedKeys<H>, ProtocolError>
|
||||||
|
where
|
||||||
|
H::Core: ProxyHash,
|
||||||
|
<H::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
|
||||||
|
Le<<H::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
|
||||||
|
{
|
||||||
|
let mut hkdf = HkdfExtract::<H>::new(None);
|
||||||
|
|
||||||
|
for ikm in ikms {
|
||||||
|
hkdf.input_ikm(ikm);
|
||||||
|
}
|
||||||
|
|
||||||
|
let (_, extracted_ikm) = hkdf.finalize();
|
||||||
|
let handshake_secret = derive_secrets::<H>(
|
||||||
|
&extracted_ikm,
|
||||||
|
STR_HANDSHAKE_SECRET,
|
||||||
|
hashed_derivation_transcript,
|
||||||
|
)?;
|
||||||
|
let session_key = derive_secrets::<H>(
|
||||||
|
&extracted_ikm,
|
||||||
|
STR_SESSION_KEY,
|
||||||
|
hashed_derivation_transcript,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let km2 = hkdf_expand_label::<H>(&handshake_secret, STR_SERVER_MAC, b"")?;
|
||||||
|
let km3 = hkdf_expand_label::<H>(&handshake_secret, STR_CLIENT_MAC, b"")?;
|
||||||
|
|
||||||
|
Ok(DerivedKeys {
|
||||||
|
session_key,
|
||||||
|
km2,
|
||||||
|
km3,
|
||||||
|
#[cfg(test)]
|
||||||
|
handshake_secret,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hkdf_expand_label<H: Hash>(
|
||||||
|
secret: &[u8],
|
||||||
|
label: &[u8],
|
||||||
|
context: &[u8],
|
||||||
|
) -> Result<Output<H>, ProtocolError>
|
||||||
|
where
|
||||||
|
H::Core: ProxyHash,
|
||||||
|
<H::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
|
||||||
|
Le<<H::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
|
||||||
|
{
|
||||||
|
let h = Hkdf::<H>::from_prk(secret).map_err(|_| InternalError::HkdfError)?;
|
||||||
|
hkdf_expand_label_extracted(&h, label, context)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hkdf_expand_label_extracted<H: Hash>(
|
||||||
|
hkdf: &Hkdf<H>,
|
||||||
|
label: &[u8],
|
||||||
|
context: &[u8],
|
||||||
|
) -> Result<Output<H>, ProtocolError>
|
||||||
|
where
|
||||||
|
H::Core: ProxyHash,
|
||||||
|
<H::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
|
||||||
|
Le<<H::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
|
||||||
|
{
|
||||||
|
let mut okm = GenericArray::default();
|
||||||
|
|
||||||
|
let length = i2osp::<U2>(OutputSize::<H>::USIZE)?;
|
||||||
|
let label_length = i2osp::<U1>(STR_OPAQUE.len() + label.len())?;
|
||||||
|
let context_len = i2osp::<U1>(context.len())?;
|
||||||
|
|
||||||
|
let hkdf_label = [
|
||||||
|
length.as_slice(),
|
||||||
|
&label_length,
|
||||||
|
STR_OPAQUE,
|
||||||
|
label,
|
||||||
|
&context_len,
|
||||||
|
context,
|
||||||
|
];
|
||||||
|
|
||||||
|
hkdf.expand_multi_info(&hkdf_label, &mut okm)
|
||||||
|
.map_err(|_| InternalError::HkdfError)?;
|
||||||
|
Ok(okm)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn derive_secrets<H: Hash>(
|
||||||
|
hkdf: &Hkdf<H>,
|
||||||
|
label: &[u8],
|
||||||
|
hashed_derivation_transcript: &[u8],
|
||||||
|
) -> Result<Output<H>, ProtocolError>
|
||||||
|
where
|
||||||
|
H::Core: ProxyHash,
|
||||||
|
<H::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
|
||||||
|
Le<<H::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
|
||||||
|
{
|
||||||
|
hkdf_expand_label_extracted::<H>(hkdf, label, hashed_derivation_transcript)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serialization and deserialization implementations
|
||||||
|
|
||||||
|
impl<G: Group> Deserialize for Ke1State<G> {
|
||||||
|
fn deserialize_take(bytes: &mut &[u8]) -> Result<Self, ProtocolError> {
|
||||||
|
Ok(Self {
|
||||||
|
client_e_sk: PrivateKey::deserialize_take(bytes)?,
|
||||||
|
client_nonce: bytes.take_array("client nonce")?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<G: Group> Serialize for Ke1State<G>
|
||||||
|
where
|
||||||
|
// Ke1State: KeSk + Nonce
|
||||||
|
G::SkLen: Add<NonceLen>,
|
||||||
|
Sum<G::SkLen, NonceLen>: ArrayLength<u8>,
|
||||||
|
{
|
||||||
|
type Len = Sum<G::SkLen, NonceLen>;
|
||||||
|
|
||||||
|
fn serialize(&self) -> GenericArray<u8, Self::Len> {
|
||||||
|
self.client_e_sk.serialize().concat(self.client_nonce)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<G: Group> Deserialize for Ke1Message<G> {
|
||||||
|
fn deserialize_take(input: &mut &[u8]) -> Result<Self, ProtocolError> {
|
||||||
|
Ok(Self {
|
||||||
|
client_nonce: input.take_array("client nonce")?,
|
||||||
|
client_e_pk: PublicKey::deserialize_take(input)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<G: Group> Serialize for Ke1Message<G>
|
||||||
|
where
|
||||||
|
// Ke1Message: Nonce + KePk
|
||||||
|
NonceLen: Add<G::PkLen>,
|
||||||
|
Sum<NonceLen, G::PkLen>: ArrayLength<u8>,
|
||||||
|
{
|
||||||
|
type Len = Sum<NonceLen, G::PkLen>;
|
||||||
|
|
||||||
|
fn serialize(&self) -> GenericArray<u8, Self::Len> {
|
||||||
|
self.client_nonce.concat(self.client_e_pk.serialize())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<G: Group> Ke1Message<G> {
|
||||||
|
pub(crate) fn to_iter(&self) -> Ke1MessageIter<G> {
|
||||||
|
Ke1MessageIter {
|
||||||
|
client_nonce: self.client_nonce,
|
||||||
|
client_e_pk: self.client_e_pk.serialize(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(
|
||||||
|
feature = "serde",
|
||||||
|
derive(serde::Deserialize, serde::Serialize),
|
||||||
|
serde(bound = "")
|
||||||
|
)]
|
||||||
|
#[derive_where(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Zeroize)]
|
||||||
|
pub(crate) struct Ke1MessageIter<G: Group> {
|
||||||
|
client_nonce: GenericArray<u8, NonceLen>,
|
||||||
|
client_e_pk: GenericArray<u8, G::PkLen>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) type Ke1MessageIterLen<G: Group> = Sum<NonceLen, G::PkLen>;
|
||||||
|
|
||||||
|
impl<G: Group> Ke1MessageIter<G> {
|
||||||
|
pub(crate) fn iter(&self) -> impl Clone + Iterator<Item = &[u8]> {
|
||||||
|
[self.client_nonce.as_slice(), self.client_e_pk.as_slice()].into_iter()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn deserialize_take(input: &mut &[u8]) -> Result<Self, ProtocolError> {
|
||||||
|
Ok(Ke1MessageIter {
|
||||||
|
client_nonce: input.take_array("client nonce")?,
|
||||||
|
client_e_pk: input.take_array("client ephemeral public key")?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<G: Group> Ke1MessageIter<G>
|
||||||
|
where
|
||||||
|
NonceLen: Add<G::PkLen>,
|
||||||
|
Ke1MessageIterLen<G>: ArrayLength<u8>,
|
||||||
|
{
|
||||||
|
pub(crate) fn serialize(&self) -> GenericArray<u8, Ke1MessageIterLen<G>> {
|
||||||
|
self.client_nonce.concat(self.client_e_pk.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////
|
||||||
|
// Test Implementations //
|
||||||
|
//===================== //
|
||||||
|
//////////////////////////
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
use crate::serialization::AssertZeroized;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
impl<G: Group> AssertZeroized for Ke1State<G>
|
||||||
|
where
|
||||||
|
G::Sk: AssertZeroized,
|
||||||
|
{
|
||||||
|
fn assert_zeroized(&self) {
|
||||||
|
let Self {
|
||||||
|
client_e_sk,
|
||||||
|
client_nonce,
|
||||||
|
} = self;
|
||||||
|
|
||||||
|
client_e_sk.assert_zeroized();
|
||||||
|
assert_eq!(client_nonce, &GenericArray::default());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
impl<G: Group> AssertZeroized for Ke1Message<G>
|
||||||
|
where
|
||||||
|
G::Pk: AssertZeroized,
|
||||||
|
{
|
||||||
|
fn assert_zeroized(&self) {
|
||||||
|
let Self {
|
||||||
|
client_nonce,
|
||||||
|
client_e_pk,
|
||||||
|
} = self;
|
||||||
|
|
||||||
|
assert_eq!(client_nonce, &GenericArray::default());
|
||||||
|
client_e_pk.assert_zeroized();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
impl<G: Group> AssertZeroized for Ke1MessageIter<G> {
|
||||||
|
fn assert_zeroized(&self) {
|
||||||
|
let Self {
|
||||||
|
client_nonce,
|
||||||
|
client_e_pk,
|
||||||
|
} = self;
|
||||||
|
|
||||||
|
for byte in client_nonce.iter().chain(client_e_pk) {
|
||||||
|
assert_eq!(byte, &0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,192 @@
|
|||||||
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||||
|
//
|
||||||
|
// This source code is dual-licensed under either the MIT license found in the
|
||||||
|
// LICENSE-MIT file in the root directory of this source tree or the Apache
|
||||||
|
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||||
|
// of this source tree. You may select, at your option, one of the above-listed
|
||||||
|
// licenses.
|
||||||
|
|
||||||
|
//! ECDSA implementation for [`elliptic_curve`] [`Group`] implementations to
|
||||||
|
//! support [`SigmaI`](crate::SigmaI).
|
||||||
|
|
||||||
|
use core::marker::PhantomData;
|
||||||
|
|
||||||
|
use derive_where::derive_where;
|
||||||
|
use digest::core_api::BlockSizeUser;
|
||||||
|
use digest::{FixedOutputReset, HashMarker};
|
||||||
|
use ecdsa::{hazmat, PrimeCurve, SignatureSize};
|
||||||
|
use elliptic_curve::{
|
||||||
|
CurveArithmetic, Field, FieldBytes, FieldBytesEncoding, FieldBytesSize, NonZeroScalar,
|
||||||
|
PrimeField, Scalar,
|
||||||
|
};
|
||||||
|
use generic_array::{ArrayLength, GenericArray};
|
||||||
|
use rand::{CryptoRng, RngCore};
|
||||||
|
use zeroize::Zeroize;
|
||||||
|
|
||||||
|
use super::{Message, MessageBuilder, SignatureProtocol};
|
||||||
|
use crate::ciphersuite::CipherSuite;
|
||||||
|
use crate::errors::ProtocolError;
|
||||||
|
use crate::key_exchange::group::elliptic_curve::NonIdentity;
|
||||||
|
use crate::key_exchange::group::Group;
|
||||||
|
pub use crate::key_exchange::sigma_i::shared::PreHash;
|
||||||
|
use crate::serialization::SliceExt;
|
||||||
|
|
||||||
|
/// ECDSA for [`SigmaI`](crate::SigmaI).
|
||||||
|
///
|
||||||
|
/// The ["verification state"](Self::VerifyState) is the pre-hash for the
|
||||||
|
/// message to be verified.
|
||||||
|
pub struct Ecdsa<G, H>(PhantomData<(G, H)>);
|
||||||
|
|
||||||
|
impl<G, H> SignatureProtocol for Ecdsa<G, H>
|
||||||
|
where
|
||||||
|
G: CurveArithmetic + Group<Sk = NonZeroScalar<G>, Pk = NonIdentity<G>> + PrimeCurve,
|
||||||
|
SignatureSize<G>: ArrayLength<u8>,
|
||||||
|
H: Clone
|
||||||
|
+ Default
|
||||||
|
+ BlockSizeUser
|
||||||
|
+ FixedOutputReset<OutputSize = FieldBytesSize<G>>
|
||||||
|
+ HashMarker,
|
||||||
|
{
|
||||||
|
type Group = G;
|
||||||
|
type Signature = Signature<G>;
|
||||||
|
type SignatureLen = SignatureSize<G>;
|
||||||
|
type VerifyState<CS: CipherSuite, KE: Group> = PreHash<H>;
|
||||||
|
|
||||||
|
// We use a manual implementation of `RandomizedPrehashSigner` to use the same
|
||||||
|
// hash for the message as for generating `k`. See
|
||||||
|
// https://github.com/RustCrypto/signatures/issues/949.
|
||||||
|
fn sign<'a, R: CryptoRng + RngCore, CS: CipherSuite, KE: Group>(
|
||||||
|
sk: &<Self::Group as Group>::Sk,
|
||||||
|
rng: &mut R,
|
||||||
|
message: &Message<CS, KE>,
|
||||||
|
) -> (Self::Signature, Self::VerifyState<CS, KE>) {
|
||||||
|
let hash = message.hash::<H>();
|
||||||
|
|
||||||
|
(
|
||||||
|
Signature(sign::<_, G, H>(sk, rng, &hash.sign.finalize_fixed())),
|
||||||
|
PreHash(hash.verify.finalize_fixed()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn verify<CS: CipherSuite, KE: Group>(
|
||||||
|
pk: &<Self::Group as Group>::Pk,
|
||||||
|
_: MessageBuilder<'_, CS>,
|
||||||
|
state: Self::VerifyState<CS, KE>,
|
||||||
|
signature: &Self::Signature,
|
||||||
|
) -> Result<(), ProtocolError> {
|
||||||
|
verify(pk, &state.0, &signature.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_signature(signature: &Self::Signature) -> GenericArray<u8, Self::SignatureLen> {
|
||||||
|
signature.0.to_bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_take_signature(bytes: &mut &[u8]) -> Result<Self::Signature, ProtocolError> {
|
||||||
|
ecdsa::Signature::from_bytes(&bytes.take_array("signature")?)
|
||||||
|
.map(Signature)
|
||||||
|
.map_err(|_| ProtocolError::SerializationError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sign<R, C, H>(sk: &NonZeroScalar<C>, rng: &mut R, pre_hash: &[u8]) -> ecdsa::Signature<C>
|
||||||
|
where
|
||||||
|
R: CryptoRng + RngCore,
|
||||||
|
C: CurveArithmetic + PrimeCurve,
|
||||||
|
SignatureSize<C>: ArrayLength<u8>,
|
||||||
|
H: Default + BlockSizeUser + FixedOutputReset<OutputSize = FieldBytesSize<C>> + HashMarker,
|
||||||
|
{
|
||||||
|
let repr = sk.to_repr();
|
||||||
|
let order = C::ORDER.encode_field_bytes();
|
||||||
|
let z =
|
||||||
|
hazmat::bits2field::<C>(pre_hash).expect("hash output can not be shorter than a scalar");
|
||||||
|
|
||||||
|
// This can only fail if the computed `r` or `s` are zero, in which case we just
|
||||||
|
// retry with a new `k`. See https://github.com/RustCrypto/signatures/pull/951.
|
||||||
|
loop {
|
||||||
|
let mut ad = FieldBytes::<C>::default();
|
||||||
|
rng.fill_bytes(&mut ad);
|
||||||
|
|
||||||
|
let k =
|
||||||
|
Scalar::<C>::from_repr(rfc6979::generate_k::<H, _>(&repr, &order, &z, &ad)).unwrap();
|
||||||
|
|
||||||
|
if let Ok((signature, _)) = hazmat::sign_prehashed::<C, _>(sk, k, &z) {
|
||||||
|
break signature;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn verify<C>(
|
||||||
|
pk: &NonIdentity<C>,
|
||||||
|
pre_hash: &[u8],
|
||||||
|
signature: &ecdsa::Signature<C>,
|
||||||
|
) -> Result<(), ProtocolError>
|
||||||
|
where
|
||||||
|
C: CurveArithmetic + PrimeCurve,
|
||||||
|
SignatureSize<C>: ArrayLength<u8>,
|
||||||
|
{
|
||||||
|
let z =
|
||||||
|
hazmat::bits2field::<C>(pre_hash).expect("hash output can not be shorter than a scalar");
|
||||||
|
hazmat::verify_prehashed(&pk.0.to_point(), &z, signature)
|
||||||
|
.map_err(|_| ProtocolError::InvalidLoginError)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Wrapper around [`ecdsa::Signature`] to implement [`Zeroize`].
|
||||||
|
// TODO: remove after https://github.com/RustCrypto/signatures/pull/948.
|
||||||
|
#[derive_where(Clone, Debug, Eq, PartialEq)]
|
||||||
|
#[cfg_attr(
|
||||||
|
feature = "serde",
|
||||||
|
derive(serde::Deserialize, serde::Serialize),
|
||||||
|
serde(bound = "")
|
||||||
|
)]
|
||||||
|
pub struct Signature<G: CurveArithmetic + PrimeCurve>(pub ecdsa::Signature<G>)
|
||||||
|
where
|
||||||
|
SignatureSize<G>: ArrayLength<u8>;
|
||||||
|
|
||||||
|
impl<G: CurveArithmetic + PrimeCurve> Zeroize for Signature<G>
|
||||||
|
where
|
||||||
|
SignatureSize<G>: ArrayLength<u8>,
|
||||||
|
{
|
||||||
|
fn zeroize(&mut self) {
|
||||||
|
self.0 = ecdsa::Signature::from_scalars(
|
||||||
|
Into::<FieldBytes<G>>::into(Scalar::<G>::ONE),
|
||||||
|
Into::<FieldBytes<G>>::into(Scalar::<G>::ONE),
|
||||||
|
)
|
||||||
|
.expect("failed to create `Signature` with non-zero `Scalar`s");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn ecdsa() {
|
||||||
|
use std::vec;
|
||||||
|
|
||||||
|
use digest::Digest;
|
||||||
|
use p256::ecdsa::signature::{DigestVerifier, RandomizedDigestSigner};
|
||||||
|
use p256::ecdsa::{Signature, SigningKey, VerifyingKey};
|
||||||
|
use p256::{NistP256, PublicKey};
|
||||||
|
use rand::rngs::OsRng;
|
||||||
|
use sha2::Sha256;
|
||||||
|
|
||||||
|
use crate::tests::mock_rng::CycleRng;
|
||||||
|
|
||||||
|
let mut rng = CycleRng::new(vec![1; 32]);
|
||||||
|
|
||||||
|
let mut message = [0; 1024];
|
||||||
|
OsRng.fill_bytes(&mut message);
|
||||||
|
let hash = Sha256::new_with_prefix(message);
|
||||||
|
|
||||||
|
let sk = NistP256::random_sk(&mut OsRng);
|
||||||
|
let signing_key = SigningKey::from(sk);
|
||||||
|
|
||||||
|
let signature: Signature = signing_key.sign_digest_with_rng(&mut rng, hash.clone());
|
||||||
|
let custom_signature = sign::<_, _, Sha256>(&sk, &mut rng, &hash.clone().finalize());
|
||||||
|
|
||||||
|
assert_eq!(signature, custom_signature);
|
||||||
|
|
||||||
|
let pk = NistP256::public_key(sk);
|
||||||
|
let verifying_key = VerifyingKey::from(PublicKey::from(pk.0));
|
||||||
|
|
||||||
|
verifying_key
|
||||||
|
.verify_digest(hash.clone(), &signature)
|
||||||
|
.unwrap();
|
||||||
|
verify(&pk, &hash.finalize(), &custom_signature).unwrap();
|
||||||
|
}
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||||
|
//
|
||||||
|
// This source code is dual-licensed under either the MIT license found in the
|
||||||
|
// LICENSE-MIT file in the root directory of this source tree or the Apache
|
||||||
|
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||||
|
// of this source tree. You may select, at your option, one of the above-listed
|
||||||
|
// licenses.
|
||||||
|
|
||||||
|
//! HashEdDSA implementation for [`SigmaI`](crate::SigmaI). Currently only
|
||||||
|
//! supports [`Ed25519`](crate::Ed25519).
|
||||||
|
|
||||||
|
use core::marker::PhantomData;
|
||||||
|
|
||||||
|
use generic_array::GenericArray;
|
||||||
|
use rand::{CryptoRng, RngCore};
|
||||||
|
use zeroize::Zeroize;
|
||||||
|
|
||||||
|
use self::implementation::HashEddsaImpl;
|
||||||
|
use super::{Message, MessageBuilder, SignatureProtocol};
|
||||||
|
use crate::ciphersuite::CipherSuite;
|
||||||
|
use crate::errors::ProtocolError;
|
||||||
|
use crate::key_exchange::group::Group;
|
||||||
|
|
||||||
|
/// HashEdDSA for [`SigmaI`](crate::SigmaI).
|
||||||
|
///
|
||||||
|
/// The ["verification state"](Self::VerifyState) is the pre-hash for the
|
||||||
|
/// message to be verified.
|
||||||
|
pub struct HashEddsa<G>(PhantomData<G>);
|
||||||
|
|
||||||
|
impl<G: HashEddsaImpl> SignatureProtocol for HashEddsa<G> {
|
||||||
|
type Group = G;
|
||||||
|
type Signature = G::Signature;
|
||||||
|
type SignatureLen = G::SignatureLen;
|
||||||
|
type VerifyState<CS: CipherSuite, KE: Group> = G::VerifyState<CS, KE>;
|
||||||
|
|
||||||
|
fn sign<'a, R: CryptoRng + RngCore, CS: CipherSuite, KE: Group>(
|
||||||
|
sk: &<Self::Group as Group>::Sk,
|
||||||
|
_: &mut R,
|
||||||
|
message: &Message<CS, KE>,
|
||||||
|
) -> (Self::Signature, Self::VerifyState<CS, KE>) {
|
||||||
|
G::sign(sk, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn verify<CS: CipherSuite, KE: Group>(
|
||||||
|
pk: &<Self::Group as Group>::Pk,
|
||||||
|
_: MessageBuilder<'_, CS>,
|
||||||
|
state: Self::VerifyState<CS, KE>,
|
||||||
|
signature: &Self::Signature,
|
||||||
|
) -> Result<(), ProtocolError> {
|
||||||
|
G::verify(pk, state, signature)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_signature(signature: &Self::Signature) -> GenericArray<u8, Self::SignatureLen> {
|
||||||
|
G::serialize_signature(signature)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_take_signature(bytes: &mut &[u8]) -> Result<Self::Signature, ProtocolError> {
|
||||||
|
G::deserialize_take_signature(bytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(in super::super) mod implementation {
|
||||||
|
use generic_array::ArrayLength;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub trait HashEddsaImpl: Group {
|
||||||
|
type Signature: Clone + Zeroize;
|
||||||
|
type SignatureLen: ArrayLength<u8>;
|
||||||
|
type VerifyState<CS: CipherSuite, KE: Group>: Clone + Zeroize;
|
||||||
|
|
||||||
|
fn sign<CS: CipherSuite, KE: Group>(
|
||||||
|
sk: &Self::Sk,
|
||||||
|
message: &Message<CS, KE>,
|
||||||
|
) -> (Self::Signature, Self::VerifyState<CS, KE>);
|
||||||
|
|
||||||
|
fn verify<CS: CipherSuite, KE: Group>(
|
||||||
|
pk: &Self::Pk,
|
||||||
|
state: Self::VerifyState<CS, KE>,
|
||||||
|
signature: &Self::Signature,
|
||||||
|
) -> Result<(), ProtocolError>;
|
||||||
|
|
||||||
|
fn deserialize_take_signature(bytes: &mut &[u8]) -> Result<Self::Signature, ProtocolError>;
|
||||||
|
|
||||||
|
fn serialize_signature(signature: &Self::Signature)
|
||||||
|
-> GenericArray<u8, Self::SignatureLen>;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,296 @@
|
|||||||
|
use core::ops::Add;
|
||||||
|
|
||||||
|
use derive_where::derive_where;
|
||||||
|
use digest::{FixedOutput, Output, Update};
|
||||||
|
use generic_array::sequence::Concat;
|
||||||
|
use generic_array::typenum::Sum;
|
||||||
|
use generic_array::{ArrayLength, GenericArray};
|
||||||
|
use zeroize::{Zeroize, ZeroizeOnDrop};
|
||||||
|
|
||||||
|
use crate::ciphersuite::{CipherSuite, KeGroup, KeHash, OprfGroup};
|
||||||
|
use crate::errors::ProtocolError;
|
||||||
|
use crate::hash::OutputSize;
|
||||||
|
use crate::key_exchange::group::Group;
|
||||||
|
use crate::key_exchange::shared::{Ke1MessageIter, Ke1MessageIterLen, NonceLen};
|
||||||
|
use crate::key_exchange::traits::{
|
||||||
|
CredentialRequestParts, CredentialRequestPartsLen, CredentialResponseParts,
|
||||||
|
CredentialResponsePartsLen, Deserialize, Serialize, SerializedContext, SerializedIdentifier,
|
||||||
|
SerializedIdentifiers,
|
||||||
|
};
|
||||||
|
use crate::opaque::MaskedResponseLen;
|
||||||
|
use crate::serialization::{SliceExt, UpdateExt};
|
||||||
|
|
||||||
|
/// This holds the message to be signed and the message to be verified.
|
||||||
|
///
|
||||||
|
/// If your signature protocol requires pre-hashes, you can call [`hash()`].
|
||||||
|
///
|
||||||
|
/// If you require the actual message, call [`sign_message()`]. To get the
|
||||||
|
/// message to verify, call [`to_cached()`] to create a [`CachedMessage`] and
|
||||||
|
/// save it in [`SignatureProtocol::VerifyState`], which you can then use in
|
||||||
|
/// [`SignatureProtocol::verify()`] with [`MessageBuilder`] to create
|
||||||
|
/// [`VerifyMessage`].
|
||||||
|
///
|
||||||
|
/// [`hash()`]: super::Message::hash
|
||||||
|
/// [`sign_message()`]: super::Message::sign_message
|
||||||
|
/// [`to_cached()`]: super::Message::to_cached
|
||||||
|
/// [`SignatureProtocol::sign()`]: super::SignatureProtocol::sign
|
||||||
|
/// [`SignatureProtocol::verify()`]: super::SignatureProtocol::verify
|
||||||
|
/// [`SignatureProtocol::VerifyState`]: super::SignatureProtocol::VerifyState
|
||||||
|
#[cfg_attr(
|
||||||
|
feature = "serde",
|
||||||
|
derive(serde::Deserialize, serde::Serialize),
|
||||||
|
serde(bound(deserialize = "'de: 'a", serialize = ""))
|
||||||
|
)]
|
||||||
|
#[derive_where(Clone, Debug, Eq, Hash, PartialEq, ZeroizeOnDrop)]
|
||||||
|
pub struct Message<'a, CS: CipherSuite, KE: Group> {
|
||||||
|
pub(super) role: Role,
|
||||||
|
pub(super) context: SerializedContext<'a>,
|
||||||
|
pub(super) identifiers: SerializedIdentifiers<'a, KeGroup<CS>>,
|
||||||
|
pub(super) cache: CachedMessage<CS, KE>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This holds the message to be verified.
|
||||||
|
///
|
||||||
|
/// Create it by using [`MessageBuilder::build()`] with [`CachedMessage`].
|
||||||
|
#[cfg_attr(
|
||||||
|
feature = "serde",
|
||||||
|
derive(serde::Deserialize, serde::Serialize),
|
||||||
|
serde(bound(deserialize = "'de: 'a", serialize = ""))
|
||||||
|
)]
|
||||||
|
#[derive_where(Clone, Debug, Eq, Hash, PartialEq, ZeroizeOnDrop)]
|
||||||
|
pub struct VerifyMessage<'a, CS: CipherSuite, KE: Group> {
|
||||||
|
role: Role,
|
||||||
|
context: SerializedContext<'a>,
|
||||||
|
identifier: SerializedIdentifier<'a, KeGroup<CS>>,
|
||||||
|
pub(super) cache: CachedMessage<CS, KE>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Used to build [`VerifyMessage`]. It is only available in
|
||||||
|
/// [`SignatureProtocol::verify()`].
|
||||||
|
///
|
||||||
|
/// [`SignatureProtocol::verify()`]: super::SignatureProtocol::verify
|
||||||
|
#[derive(Debug, Eq, Hash, PartialEq, ZeroizeOnDrop)]
|
||||||
|
pub struct MessageBuilder<'a, CS: CipherSuite> {
|
||||||
|
pub(super) role: Role,
|
||||||
|
pub(super) context: SerializedContext<'a>,
|
||||||
|
pub(super) identifier: SerializedIdentifier<'a, KeGroup<CS>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Created by [`Message::to_cached()`]. This is used to save the message to be
|
||||||
|
/// verified in [`SignatureProtocol::VerifyState`].
|
||||||
|
///
|
||||||
|
/// Use [`MessageBuilder::build()`] to create [`VerifyMessage`] in
|
||||||
|
/// [`SignatureProtocol::verify()`].
|
||||||
|
///
|
||||||
|
/// [`SignatureProtocol::verify()`]: super::SignatureProtocol::verify
|
||||||
|
/// [`SignatureProtocol::VerifyState`]: super::SignatureProtocol::VerifyState
|
||||||
|
#[cfg_attr(
|
||||||
|
feature = "serde",
|
||||||
|
derive(serde::Deserialize, serde::Serialize),
|
||||||
|
serde(bound = "")
|
||||||
|
)]
|
||||||
|
#[derive_where(Clone, Debug, Eq, Hash, PartialEq, Zeroize, ZeroizeOnDrop)]
|
||||||
|
pub struct CachedMessage<CS: CipherSuite, KE: Group> {
|
||||||
|
pub(super) credential_request: CredentialRequestParts<CS>,
|
||||||
|
pub(super) ke1_message: Ke1MessageIter<KE>,
|
||||||
|
pub(super) credential_response: CredentialResponseParts<CS>,
|
||||||
|
pub(super) server_nonce: GenericArray<u8, NonceLen>,
|
||||||
|
pub(super) server_e_pk: GenericArray<u8, KE::PkLen>,
|
||||||
|
pub(super) server_mac: Output<KeHash<CS>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<CS: CipherSuite, KE: Group> Message<'_, CS, KE> {
|
||||||
|
/// Returns the message to be signed.
|
||||||
|
pub fn sign_message(&self) -> impl Clone + Iterator<Item = &[u8]> {
|
||||||
|
self.context.iter().chain(self.post_message(Stage::Sign))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the hash of both messages.
|
||||||
|
pub fn hash<KEH: Default + Clone + FixedOutput + Update>(&self) -> HashOutput<KEH> {
|
||||||
|
let mut context = KEH::default();
|
||||||
|
context.update_iter(self.context.iter());
|
||||||
|
|
||||||
|
let sign = context.clone().chain_iter(self.post_message(Stage::Sign));
|
||||||
|
let verify = context.chain_iter(self.post_message(Stage::Verify));
|
||||||
|
|
||||||
|
HashOutput { sign, verify }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn post_message(&self, stage: Stage) -> impl Clone + Iterator<Item = &[u8]> {
|
||||||
|
let transcript = match (self.role, stage) {
|
||||||
|
(Role::Server, Stage::Sign) => Role::Server,
|
||||||
|
(Role::Server, Stage::Verify) => Role::Client,
|
||||||
|
(Role::Client, Stage::Sign) => Role::Client,
|
||||||
|
(Role::Client, Stage::Verify) => Role::Server,
|
||||||
|
};
|
||||||
|
let identifier = match transcript {
|
||||||
|
Role::Server => &self.identifiers.server,
|
||||||
|
Role::Client => &self.identifiers.client,
|
||||||
|
};
|
||||||
|
|
||||||
|
self.cache.post_message(transcript, identifier)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a [`CachedMessage`], which can be saved in
|
||||||
|
/// [`SignatureProtocol::VerifyState`] and create a [`VerifyMessage`] with
|
||||||
|
/// [`MessageBuilder::build()`].
|
||||||
|
///
|
||||||
|
/// [`SignatureProtocol::VerifyState`]: super::SignatureProtocol::VerifyState
|
||||||
|
pub fn to_cached(&self) -> CachedMessage<CS, KE> {
|
||||||
|
self.cache.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<CS: CipherSuite, KE: Group> VerifyMessage<'_, CS, KE> {
|
||||||
|
/// Returns the message to be verified.
|
||||||
|
pub fn verify_message(&self) -> impl Clone + Iterator<Item = &[u8]> {
|
||||||
|
let transcript = match self.role {
|
||||||
|
Role::Server => Role::Client,
|
||||||
|
Role::Client => Role::Server,
|
||||||
|
};
|
||||||
|
|
||||||
|
self.context
|
||||||
|
.iter()
|
||||||
|
.chain(self.cache.post_message(transcript, &self.identifier))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<CS: CipherSuite, KE: Group> CachedMessage<CS, KE> {
|
||||||
|
fn post_message<'a>(
|
||||||
|
&'a self,
|
||||||
|
transcript: Role,
|
||||||
|
identifier: &'a SerializedIdentifier<'_, KeGroup<CS>>,
|
||||||
|
) -> impl Clone + Iterator<Item = &'a [u8]> {
|
||||||
|
Some(identifier.iter())
|
||||||
|
.filter(|_| matches!(transcript, Role::Client))
|
||||||
|
.into_iter()
|
||||||
|
.flatten()
|
||||||
|
.chain(self.credential_request.iter())
|
||||||
|
.chain(self.ke1_message.iter())
|
||||||
|
.chain(
|
||||||
|
Some(identifier.iter())
|
||||||
|
.filter(|_| matches!(transcript, Role::Server))
|
||||||
|
.into_iter()
|
||||||
|
.flatten(),
|
||||||
|
)
|
||||||
|
.chain(self.credential_response.iter())
|
||||||
|
.chain([self.server_nonce.as_slice(), &self.server_e_pk])
|
||||||
|
.chain(Some(self.server_mac.as_slice()).filter(|_| matches!(transcript, Role::Client)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(
|
||||||
|
feature = "serde",
|
||||||
|
derive(serde::Deserialize, serde::Serialize),
|
||||||
|
serde(bound = "")
|
||||||
|
)]
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Zeroize)]
|
||||||
|
pub(super) enum Role {
|
||||||
|
Server,
|
||||||
|
Client,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Stage {
|
||||||
|
Sign,
|
||||||
|
Verify,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returned by [`Message::hash()`] containing the hash of the message to be
|
||||||
|
/// signed and the message to be verified.
|
||||||
|
pub struct HashOutput<H> {
|
||||||
|
/// The hash of the message to be signed.
|
||||||
|
pub sign: H,
|
||||||
|
/// The hash of the message to be verified.
|
||||||
|
pub verify: H,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, CS: CipherSuite> MessageBuilder<'a, CS> {
|
||||||
|
/// Creates a [`VerifyMessage`]. [`CachedMessage`] can be created by
|
||||||
|
/// [`Message::to_cached()`] and stored in
|
||||||
|
/// [`SignatureProtocol::VerifyState`].
|
||||||
|
///
|
||||||
|
/// [`SignatureProtocol::VerifyState`]: super::SignatureProtocol::VerifyState
|
||||||
|
pub fn build<KE: Group>(self, cache: CachedMessage<CS, KE>) -> VerifyMessage<'a, CS, KE> {
|
||||||
|
VerifyMessage {
|
||||||
|
role: self.role,
|
||||||
|
context: self.context.clone(),
|
||||||
|
identifier: self.identifier.clone(),
|
||||||
|
cache,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<CS: CipherSuite, KE: Group> Deserialize for CachedMessage<CS, KE> {
|
||||||
|
fn deserialize_take(input: &mut &[u8]) -> Result<Self, ProtocolError> {
|
||||||
|
Ok(Self {
|
||||||
|
credential_request: CredentialRequestParts::deserialize_take(input)?,
|
||||||
|
ke1_message: Ke1MessageIter::deserialize_take(input)?,
|
||||||
|
credential_response: CredentialResponseParts::deserialize_take(input)?,
|
||||||
|
server_nonce: input.take_array("server nonce")?,
|
||||||
|
server_e_pk: input.take_array("serialized server ephemeral key")?,
|
||||||
|
server_mac: input.take_array("server mac")?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Length of [`CachedMessage`].
|
||||||
|
type CachedMessageLen<CS: CipherSuite, KE: Group> = Sum<
|
||||||
|
Sum<
|
||||||
|
Sum<
|
||||||
|
Sum<
|
||||||
|
Sum<CredentialRequestPartsLen<CS>, Ke1MessageIterLen<KE>>,
|
||||||
|
CredentialResponsePartsLen<CS>,
|
||||||
|
>,
|
||||||
|
NonceLen,
|
||||||
|
>,
|
||||||
|
KE::PkLen,
|
||||||
|
>,
|
||||||
|
OutputSize<KeHash<CS>>,
|
||||||
|
>;
|
||||||
|
|
||||||
|
impl<CS: CipherSuite, KE: Group> Serialize for CachedMessage<CS, KE>
|
||||||
|
where
|
||||||
|
CredentialRequestPartsLen<CS>: ArrayLength<u8> + Add<Ke1MessageIterLen<KE>>,
|
||||||
|
Sum<CredentialRequestPartsLen<CS>, Ke1MessageIterLen<KE>>:
|
||||||
|
ArrayLength<u8> + Add<CredentialResponsePartsLen<CS>>,
|
||||||
|
Sum<Sum<CredentialRequestPartsLen<CS>, Ke1MessageIterLen<KE>>, CredentialResponsePartsLen<CS>>:
|
||||||
|
ArrayLength<u8> + Add<NonceLen>,
|
||||||
|
Sum<
|
||||||
|
Sum<
|
||||||
|
Sum<CredentialRequestPartsLen<CS>, Ke1MessageIterLen<KE>>,
|
||||||
|
CredentialResponsePartsLen<CS>,
|
||||||
|
>,
|
||||||
|
NonceLen,
|
||||||
|
>: ArrayLength<u8> + Add<KE::PkLen>,
|
||||||
|
Sum<
|
||||||
|
Sum<
|
||||||
|
Sum<
|
||||||
|
Sum<CredentialRequestPartsLen<CS>, Ke1MessageIterLen<KE>>,
|
||||||
|
CredentialResponsePartsLen<CS>,
|
||||||
|
>,
|
||||||
|
NonceLen,
|
||||||
|
>,
|
||||||
|
KE::PkLen,
|
||||||
|
>: ArrayLength<u8> + Add<OutputSize<KeHash<CS>>>,
|
||||||
|
CachedMessageLen<CS, KE>: ArrayLength<u8>,
|
||||||
|
// Ke1MessageIter
|
||||||
|
NonceLen: Add<KE::PkLen>,
|
||||||
|
Ke1MessageIterLen<KE>: ArrayLength<u8>,
|
||||||
|
// CredentialResponseParts
|
||||||
|
<OprfGroup<CS> as voprf::Group>::ElemLen: Add<NonceLen>,
|
||||||
|
Sum<<OprfGroup<CS> as voprf::Group>::ElemLen, NonceLen>:
|
||||||
|
ArrayLength<u8> + Add<MaskedResponseLen<CS>>,
|
||||||
|
CredentialResponsePartsLen<CS>: ArrayLength<u8>,
|
||||||
|
{
|
||||||
|
type Len = CachedMessageLen<CS, KE>;
|
||||||
|
|
||||||
|
fn serialize(&self) -> GenericArray<u8, Self::Len> {
|
||||||
|
self.credential_request
|
||||||
|
.serialize()
|
||||||
|
.concat(self.ke1_message.serialize())
|
||||||
|
.concat(self.credential_response.serialize())
|
||||||
|
.concat(self.server_nonce)
|
||||||
|
.concat(self.server_e_pk.clone())
|
||||||
|
.concat(self.server_mac.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,645 @@
|
|||||||
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||||
|
//
|
||||||
|
// This source code is dual-licensed under either the MIT license found in the
|
||||||
|
// LICENSE-MIT file in the root directory of this source tree or the Apache
|
||||||
|
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||||
|
// of this source tree. You may select, at your option, one of the above-listed
|
||||||
|
// licenses.
|
||||||
|
|
||||||
|
//! An implementation of the SIGMA-I key exchange protocol
|
||||||
|
//!
|
||||||
|
//! ⚠️ **Warning**: This implementation has not been audited. Use at your own
|
||||||
|
//! risk!
|
||||||
|
|
||||||
|
#[cfg(feature = "ecdsa")]
|
||||||
|
pub mod ecdsa;
|
||||||
|
pub mod hash_eddsa;
|
||||||
|
mod message;
|
||||||
|
pub mod pure_eddsa;
|
||||||
|
pub(super) mod shared;
|
||||||
|
|
||||||
|
use core::iter;
|
||||||
|
use core::marker::PhantomData;
|
||||||
|
use core::ops::Add;
|
||||||
|
|
||||||
|
use derive_where::derive_where;
|
||||||
|
use digest::core_api::BlockSizeUser;
|
||||||
|
use digest::{Digest, Mac, Output, OutputSizeUser};
|
||||||
|
use generic_array::sequence::Concat;
|
||||||
|
use generic_array::typenum::{IsLess, Le, NonZero, Sum, U256};
|
||||||
|
use generic_array::{ArrayLength, GenericArray};
|
||||||
|
use hmac::Hmac;
|
||||||
|
use rand::{CryptoRng, RngCore};
|
||||||
|
use subtle::{ConstantTimeEq, CtOption};
|
||||||
|
use zeroize::Zeroize;
|
||||||
|
|
||||||
|
use self::message::Role;
|
||||||
|
pub use self::message::{CachedMessage, HashOutput, Message, MessageBuilder, VerifyMessage};
|
||||||
|
use crate::ciphersuite::{CipherSuite, KeGroup, KeHash};
|
||||||
|
use crate::envelope::NonceLen;
|
||||||
|
use crate::errors::{InternalError, ProtocolError};
|
||||||
|
use crate::hash::{Hash, OutputSize, ProxyHash};
|
||||||
|
use crate::key_exchange::group::Group;
|
||||||
|
use crate::key_exchange::shared::{derive_keys, generate_ke1, generate_nonce, transcript};
|
||||||
|
pub use crate::key_exchange::shared::{DiffieHellman, Ke1Message, Ke1State};
|
||||||
|
use crate::key_exchange::traits::{
|
||||||
|
CredentialRequestParts, CredentialResponseParts, Deserialize, GenerateKe2Result,
|
||||||
|
GenerateKe3Result, KeyExchange, Sealed, Serialize, SerializedContext, SerializedIdentifier,
|
||||||
|
SerializedIdentifiers,
|
||||||
|
};
|
||||||
|
use crate::keypair::{KeyPair, PrivateKey, PublicKey};
|
||||||
|
use crate::opaque::Identifiers;
|
||||||
|
use crate::serialization::{SliceExt, UpdateExt};
|
||||||
|
|
||||||
|
/// The SIGMA-I key exchange implementation
|
||||||
|
///
|
||||||
|
/// `SIG` determines the algorithm used for the signature. `KE` determines the
|
||||||
|
/// algorithm used for establishing the shared secret. `KEH` determines the hash
|
||||||
|
/// used for the key exchange.
|
||||||
|
///
|
||||||
|
/// # Remote Key
|
||||||
|
///
|
||||||
|
/// [`ServerLoginBuilder::data()`](crate::ServerLoginBuilder::data()) will
|
||||||
|
/// return [`Message`].
|
||||||
|
///
|
||||||
|
/// [`ServerLoginBuilder::build()`](crate::ServerLoginBuilder::build()) expects
|
||||||
|
/// a signature from signing the [message](Message::sign_message) with the
|
||||||
|
/// servers private key, and a ["verification
|
||||||
|
/// state"](SignatureProtocol::VerifyState).
|
||||||
|
///
|
||||||
|
/// To understand what kind of "verification state" is expected here exactly,
|
||||||
|
/// refer to the documentation of your chosen [`SignatureProtocol`] `SIG`. E.g.
|
||||||
|
/// [`Ecdsa`](ecdsa::Ecdsa), [`PureEddsa`](pure_eddsa::PureEddsa) or
|
||||||
|
/// [`HashEddsa`](hash_eddsa::HashEddsa).
|
||||||
|
pub struct SigmaI<SIG, KE, KEH>(PhantomData<(SIG, KE, KEH)>);
|
||||||
|
|
||||||
|
/// Trait to implement for `SIG` used in [`SigmaI`].
|
||||||
|
///
|
||||||
|
/// The [`sign()`] and [`verify()`] methods do not function independent of each
|
||||||
|
/// other. [`sign()`] is always called first and receives a [Message] containing
|
||||||
|
/// the message for both signing and verifying. A ["verification
|
||||||
|
/// state"](Self::VerifyState) is created by [`sign()`] and then passed onto
|
||||||
|
/// [`verify()`].
|
||||||
|
///
|
||||||
|
/// The most straightforward implementation would simply store the message for
|
||||||
|
/// verifying in [`VerifyState`](Self::VerifyState). However, protocols that
|
||||||
|
/// allow for pre-hashing don't need to store the whole message and can
|
||||||
|
/// preemptively hash the verification message and only store that instead,
|
||||||
|
/// getting rid of the much larger message.
|
||||||
|
///
|
||||||
|
/// [`sign()`]: Self::sign
|
||||||
|
/// [`verify()`]: Self::verify
|
||||||
|
pub trait SignatureProtocol {
|
||||||
|
/// The [`Group`] used to generate and derive keys.
|
||||||
|
type Group: Group;
|
||||||
|
/// The signature.
|
||||||
|
type Signature: Clone + Zeroize;
|
||||||
|
/// Length of a serialized [`Signature`](Self::Signature).
|
||||||
|
type SignatureLen: ArrayLength<u8>;
|
||||||
|
/// The state required to run the verification. This is used to cache the
|
||||||
|
/// pre-hash for curves that support that, otherwise the [`Message`] to
|
||||||
|
/// verify is stored via [`CachedMessage`].
|
||||||
|
type VerifyState<CS: CipherSuite, KE: Group>: Clone + Zeroize;
|
||||||
|
|
||||||
|
/// Returns a signature from the given message signed by the given private
|
||||||
|
/// key.
|
||||||
|
///
|
||||||
|
/// [`Message`] contains both signature messages for signing and
|
||||||
|
/// verification. If you need it again during verification, consider
|
||||||
|
/// using [`CachedMessage`].
|
||||||
|
///
|
||||||
|
/// The returned [`VerifyState`](Self::VerifyState) will be passed to
|
||||||
|
/// [`verify()`](Self::verify) and must contain the necessary
|
||||||
|
/// information to verify the incoming signature.
|
||||||
|
fn sign<R: CryptoRng + RngCore, CS: CipherSuite, KE: Group>(
|
||||||
|
sk: &<Self::Group as Group>::Sk,
|
||||||
|
rng: &mut R,
|
||||||
|
message: &Message<CS, KE>,
|
||||||
|
) -> (Self::Signature, Self::VerifyState<CS, KE>);
|
||||||
|
|
||||||
|
/// Validates that the signature was created by signing the message with the
|
||||||
|
/// corresponding private key.
|
||||||
|
///
|
||||||
|
/// The [`MessageBuilder`] can be used with [`CachedMessage`] to create
|
||||||
|
/// [`VerifyMessage`] which contains the message of the given `signature`.
|
||||||
|
///
|
||||||
|
/// The `state` is created by [`sign()`](Self::sign()).
|
||||||
|
fn verify<CS: CipherSuite, KE: Group>(
|
||||||
|
pk: &<Self::Group as Group>::Pk,
|
||||||
|
message_builder: MessageBuilder<'_, CS>,
|
||||||
|
state: Self::VerifyState<CS, KE>,
|
||||||
|
signature: &Self::Signature,
|
||||||
|
) -> Result<(), ProtocolError>;
|
||||||
|
|
||||||
|
/// Serialize [`Signature`](Self::Signature) into a fixed-sized byte array.
|
||||||
|
fn serialize_signature(signature: &Self::Signature) -> GenericArray<u8, Self::SignatureLen>;
|
||||||
|
|
||||||
|
/// Deserialize [`Signature`](Self::Signature) from the given `bytes`.
|
||||||
|
///
|
||||||
|
/// The deserialized bytes must be taken from `bytes`.
|
||||||
|
fn deserialize_take_signature(bytes: &mut &[u8]) -> Result<Self::Signature, ProtocolError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Builder for the second key exchange message
|
||||||
|
#[cfg_attr(
|
||||||
|
feature = "serde",
|
||||||
|
derive(serde::Deserialize, serde::Serialize),
|
||||||
|
serde(bound(deserialize = "'de: 'a", serialize = ""))
|
||||||
|
)]
|
||||||
|
#[derive_where(Clone, ZeroizeOnDrop)]
|
||||||
|
#[derive_where(Debug, Eq, Hash, PartialEq; PublicKey<KeGroup<CS>>, PublicKey<KE>)]
|
||||||
|
pub struct Ke2Builder<'a, CS: CipherSuite, KE: Group> {
|
||||||
|
transcript: Message<'a, CS, KE>,
|
||||||
|
server_nonce: GenericArray<u8, NonceLen>,
|
||||||
|
client_s_pk: PublicKey<KeGroup<CS>>,
|
||||||
|
server_e_pk: PublicKey<KE>,
|
||||||
|
expected_mac: Output<KeHash<CS>>,
|
||||||
|
session_key: Output<KeHash<CS>>,
|
||||||
|
#[cfg(test)]
|
||||||
|
km3: Output<KeHash<CS>>,
|
||||||
|
#[cfg(test)]
|
||||||
|
handshake_secret: Output<KeHash<CS>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The server state produced after the second key exchange message
|
||||||
|
#[cfg_attr(
|
||||||
|
feature = "serde",
|
||||||
|
derive(serde::Deserialize, serde::Serialize),
|
||||||
|
serde(bound(
|
||||||
|
deserialize = "SIG::VerifyState<CS, KE>: serde::Deserialize<'de>",
|
||||||
|
serialize = "SIG::VerifyState<CS, KE>: serde::Serialize"
|
||||||
|
))
|
||||||
|
)]
|
||||||
|
#[derive_where(Clone, ZeroizeOnDrop)]
|
||||||
|
#[derive_where(Debug, Eq, Hash, PartialEq; <SIG::Group as Group>::Pk, SIG::VerifyState<CS, KE>)]
|
||||||
|
pub struct Ke2State<CS: CipherSuite, SIG: SignatureProtocol, KE: Group> {
|
||||||
|
client_s_pk: PublicKey<SIG::Group>,
|
||||||
|
session_key: Output<KeHash<CS>>,
|
||||||
|
verify_state: SIG::VerifyState<CS, KE>,
|
||||||
|
expected_mac: Output<KeHash<CS>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The second key exchange message
|
||||||
|
#[cfg_attr(
|
||||||
|
feature = "serde",
|
||||||
|
derive(serde::Deserialize, serde::Serialize),
|
||||||
|
serde(bound(
|
||||||
|
deserialize = "SIG::Signature: serde::Deserialize<'de>",
|
||||||
|
serialize = "SIG::Signature: serde::Serialize"
|
||||||
|
))
|
||||||
|
)]
|
||||||
|
#[derive_where(Clone, ZeroizeOnDrop)]
|
||||||
|
#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; KE::Pk, SIG::Signature)]
|
||||||
|
pub struct Ke2Message<SIG: SignatureProtocol, KE: Group, KEH: Hash>
|
||||||
|
where
|
||||||
|
KEH::Core: ProxyHash,
|
||||||
|
<KEH::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
|
||||||
|
Le<<KEH::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
|
||||||
|
{
|
||||||
|
server_nonce: GenericArray<u8, NonceLen>,
|
||||||
|
server_e_pk: PublicKey<KE>,
|
||||||
|
signature: SIG::Signature,
|
||||||
|
mac: Output<KEH>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The third key exchange message
|
||||||
|
#[cfg_attr(
|
||||||
|
feature = "serde",
|
||||||
|
derive(serde::Deserialize, serde::Serialize),
|
||||||
|
serde(bound(
|
||||||
|
deserialize = "SIG::Signature: serde::Deserialize<'de>",
|
||||||
|
serialize = "SIG::Signature: serde::Serialize"
|
||||||
|
))
|
||||||
|
)]
|
||||||
|
#[derive_where(Clone, ZeroizeOnDrop)]
|
||||||
|
#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; SIG::Signature)]
|
||||||
|
pub struct Ke3Message<SIG: SignatureProtocol, KEH: OutputSizeUser> {
|
||||||
|
signature: SIG::Signature,
|
||||||
|
mac: Output<KEH>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<SIG: SignatureProtocol, KE: 'static + Group, KEH: Hash> KeyExchange for SigmaI<SIG, KE, KEH>
|
||||||
|
where
|
||||||
|
KE::Sk: DiffieHellman<KE>,
|
||||||
|
KEH::Core: ProxyHash,
|
||||||
|
<KEH::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
|
||||||
|
Le<<KEH::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
|
||||||
|
{
|
||||||
|
type Group = SIG::Group;
|
||||||
|
type Hash = KEH;
|
||||||
|
|
||||||
|
type KE1State = Ke1State<KE>;
|
||||||
|
type KE1Message = Ke1Message<KE>;
|
||||||
|
type KE2Builder<'a, CS: CipherSuite<KeyExchange = Self>> = Ke2Builder<'a, CS, KE>;
|
||||||
|
type KE2BuilderData<'a, CS: 'static + CipherSuite> = &'a Message<'a, CS, KE>;
|
||||||
|
type KE2BuilderInput<CS: CipherSuite> = (SIG::Signature, SIG::VerifyState<CS, KE>);
|
||||||
|
type KE2State<CS: CipherSuite> = Ke2State<CS, SIG, KE>;
|
||||||
|
type KE2Message = Ke2Message<SIG, KE, KEH>;
|
||||||
|
type KE3Message = Ke3Message<SIG, KEH>;
|
||||||
|
|
||||||
|
fn generate_ke1<R: RngCore + CryptoRng>(
|
||||||
|
rng: &mut R,
|
||||||
|
) -> Result<(Self::KE1State, Self::KE1Message), ProtocolError> {
|
||||||
|
generate_ke1(rng)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ke2_builder<'a, CS: CipherSuite<KeyExchange = Self>, R: RngCore + CryptoRng>(
|
||||||
|
rng: &mut R,
|
||||||
|
credential_request: CredentialRequestParts<CS>,
|
||||||
|
ke1_message: Self::KE1Message,
|
||||||
|
credential_response: CredentialResponseParts<CS>,
|
||||||
|
client_s_pk: PublicKey<Self::Group>,
|
||||||
|
identifiers: SerializedIdentifiers<'a, KeGroup<CS>>,
|
||||||
|
context: SerializedContext<'a>,
|
||||||
|
) -> Result<Self::KE2Builder<'a, CS>, ProtocolError> {
|
||||||
|
let server_e = KeyPair::<KE>::derive_random(rng);
|
||||||
|
let server_nonce = generate_nonce::<R>(rng);
|
||||||
|
|
||||||
|
let ke1_message_iter = ke1_message.to_iter();
|
||||||
|
let server_e_pk = server_e.public().serialize();
|
||||||
|
|
||||||
|
let transcript_hasher = transcript(
|
||||||
|
&context,
|
||||||
|
&identifiers,
|
||||||
|
&credential_request,
|
||||||
|
&ke1_message_iter,
|
||||||
|
&credential_response,
|
||||||
|
server_nonce,
|
||||||
|
&server_e_pk,
|
||||||
|
);
|
||||||
|
|
||||||
|
let shared_secret = server_e
|
||||||
|
.private()
|
||||||
|
.ke_diffie_hellman(&ke1_message.client_e_pk);
|
||||||
|
|
||||||
|
let derived_keys = derive_keys::<KEH>(
|
||||||
|
iter::once(shared_secret.as_slice()),
|
||||||
|
&transcript_hasher.finalize(),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let mut server_mac =
|
||||||
|
Hmac::<KEH>::new_from_slice(&derived_keys.km2).map_err(|_| InternalError::HmacError)?;
|
||||||
|
server_mac.update_iter(identifiers.server.iter());
|
||||||
|
let server_mac = server_mac.finalize().into_bytes();
|
||||||
|
|
||||||
|
let mut client_mac =
|
||||||
|
Hmac::<KEH>::new_from_slice(&derived_keys.km3).map_err(|_| InternalError::HmacError)?;
|
||||||
|
client_mac.update_iter(identifiers.client.iter());
|
||||||
|
let client_mac = client_mac.finalize().into_bytes();
|
||||||
|
|
||||||
|
let message = Message {
|
||||||
|
role: Role::Server,
|
||||||
|
context,
|
||||||
|
identifiers,
|
||||||
|
cache: CachedMessage {
|
||||||
|
credential_request,
|
||||||
|
ke1_message: ke1_message_iter,
|
||||||
|
credential_response,
|
||||||
|
server_nonce,
|
||||||
|
server_e_pk,
|
||||||
|
server_mac,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Ke2Builder {
|
||||||
|
transcript: message,
|
||||||
|
server_nonce,
|
||||||
|
client_s_pk,
|
||||||
|
server_e_pk: server_e.public().clone(),
|
||||||
|
expected_mac: client_mac,
|
||||||
|
session_key: derived_keys.session_key,
|
||||||
|
#[cfg(test)]
|
||||||
|
km3: derived_keys.km3,
|
||||||
|
#[cfg(test)]
|
||||||
|
handshake_secret: derived_keys.handshake_secret,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ke2_builder_data<'a, CS: 'static + CipherSuite<KeyExchange = Self>>(
|
||||||
|
builder: &'a Self::KE2Builder<'_, CS>,
|
||||||
|
) -> Self::KE2BuilderData<'a, CS> {
|
||||||
|
&builder.transcript
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_ke2_input<CS: CipherSuite<KeyExchange = Self>, R: CryptoRng + RngCore>(
|
||||||
|
builder: &Self::KE2Builder<'_, CS>,
|
||||||
|
rng: &mut R,
|
||||||
|
server_s_sk: &PrivateKey<Self::Group>,
|
||||||
|
) -> Self::KE2BuilderInput<CS> {
|
||||||
|
server_s_sk.sign::<_, CS, SIG, KE>(rng, &builder.transcript)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_ke2<CS: CipherSuite<KeyExchange = Self>>(
|
||||||
|
builder: Self::KE2Builder<'_, CS>,
|
||||||
|
input: Self::KE2BuilderInput<CS>,
|
||||||
|
) -> Result<GenerateKe2Result<CS>, ProtocolError> {
|
||||||
|
Ok((
|
||||||
|
Ke2State {
|
||||||
|
client_s_pk: builder.client_s_pk.clone(),
|
||||||
|
session_key: builder.session_key.clone(),
|
||||||
|
verify_state: input.1,
|
||||||
|
expected_mac: builder.expected_mac.clone(),
|
||||||
|
},
|
||||||
|
Ke2Message {
|
||||||
|
server_nonce: builder.server_nonce,
|
||||||
|
server_e_pk: builder.server_e_pk.clone(),
|
||||||
|
signature: input.0,
|
||||||
|
mac: builder.transcript.cache.server_mac.clone(),
|
||||||
|
},
|
||||||
|
#[cfg(test)]
|
||||||
|
builder.handshake_secret.clone(),
|
||||||
|
#[cfg(test)]
|
||||||
|
builder.km3.clone(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_ke3<CS: CipherSuite<KeyExchange = Self>, R: CryptoRng + RngCore>(
|
||||||
|
rng: &mut R,
|
||||||
|
credential_request: CredentialRequestParts<CS>,
|
||||||
|
ke1_message: Self::KE1Message,
|
||||||
|
credential_response: CredentialResponseParts<CS>,
|
||||||
|
ke2_message: Self::KE2Message,
|
||||||
|
ke1_state: &Self::KE1State,
|
||||||
|
server_s_pk: PublicKey<Self::Group>,
|
||||||
|
client_s_sk: PrivateKey<Self::Group>,
|
||||||
|
identifiers: SerializedIdentifiers<'_, KeGroup<CS>>,
|
||||||
|
context: SerializedContext<'_>,
|
||||||
|
) -> Result<GenerateKe3Result<Self>, ProtocolError> {
|
||||||
|
let ke1_message_iter = ke1_message.to_iter();
|
||||||
|
let server_e_pk = ke2_message.server_e_pk.serialize();
|
||||||
|
|
||||||
|
let transcript_hasher = transcript(
|
||||||
|
&context,
|
||||||
|
&identifiers,
|
||||||
|
&credential_request,
|
||||||
|
&ke1_message_iter,
|
||||||
|
&credential_response,
|
||||||
|
ke2_message.server_nonce,
|
||||||
|
&server_e_pk,
|
||||||
|
);
|
||||||
|
|
||||||
|
let shared_secret = ke1_state
|
||||||
|
.client_e_sk
|
||||||
|
.ke_diffie_hellman(&ke2_message.server_e_pk);
|
||||||
|
|
||||||
|
let derived_keys = derive_keys::<KEH>(
|
||||||
|
iter::once(shared_secret.as_slice()),
|
||||||
|
&transcript_hasher.finalize(),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let mut server_mac =
|
||||||
|
Hmac::<KEH>::new_from_slice(&derived_keys.km2).map_err(|_| InternalError::HmacError)?;
|
||||||
|
server_mac.update_iter(identifiers.server.iter());
|
||||||
|
let server_mac = server_mac.finalize().into_bytes();
|
||||||
|
|
||||||
|
bool::from(server_mac.ct_eq(&ke2_message.mac))
|
||||||
|
.then_some(())
|
||||||
|
.ok_or(ProtocolError::InvalidLoginError)?;
|
||||||
|
|
||||||
|
let mut client_mac =
|
||||||
|
Hmac::<KEH>::new_from_slice(&derived_keys.km3).map_err(|_| InternalError::HmacError)?;
|
||||||
|
client_mac.update_iter(identifiers.client.iter());
|
||||||
|
let client_mac = client_mac.finalize().into_bytes();
|
||||||
|
|
||||||
|
let message = Message {
|
||||||
|
role: Role::Client,
|
||||||
|
context: context.clone(),
|
||||||
|
identifiers: identifiers.clone(),
|
||||||
|
cache: CachedMessage {
|
||||||
|
credential_request,
|
||||||
|
ke1_message: ke1_message_iter,
|
||||||
|
credential_response,
|
||||||
|
server_nonce: ke2_message.server_nonce,
|
||||||
|
server_e_pk,
|
||||||
|
server_mac,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let (signature, state) = client_s_sk.sign::<_, CS, SIG, KE>(rng, &message);
|
||||||
|
|
||||||
|
server_s_pk.verify::<CS, SIG, KE>(
|
||||||
|
MessageBuilder {
|
||||||
|
role: Role::Client,
|
||||||
|
context,
|
||||||
|
identifier: identifiers.server,
|
||||||
|
},
|
||||||
|
state,
|
||||||
|
&ke2_message.signature,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
derived_keys.session_key,
|
||||||
|
Ke3Message {
|
||||||
|
signature,
|
||||||
|
mac: client_mac,
|
||||||
|
},
|
||||||
|
#[cfg(test)]
|
||||||
|
derived_keys.handshake_secret,
|
||||||
|
#[cfg(test)]
|
||||||
|
derived_keys.km3,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn finish_ke<CS: CipherSuite<KeyExchange = Self>>(
|
||||||
|
ke3_message: Self::KE3Message,
|
||||||
|
ke2_state: &Self::KE2State<CS>,
|
||||||
|
identifiers: Identifiers<'_>,
|
||||||
|
context: SerializedContext<'_>,
|
||||||
|
) -> Result<Output<KEH>, ProtocolError> {
|
||||||
|
ke2_state.client_s_pk.verify::<CS, SIG, KE>(
|
||||||
|
MessageBuilder {
|
||||||
|
role: Role::Server,
|
||||||
|
context,
|
||||||
|
identifier: SerializedIdentifier::from_identifier(
|
||||||
|
identifiers.client,
|
||||||
|
ke2_state.client_s_pk.serialize(),
|
||||||
|
)?,
|
||||||
|
},
|
||||||
|
ke2_state.verify_state.clone(),
|
||||||
|
&ke3_message.signature,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
CtOption::new(
|
||||||
|
ke2_state.session_key.clone(),
|
||||||
|
ke2_state.expected_mac.ct_eq(&ke3_message.mac),
|
||||||
|
)
|
||||||
|
.into_option()
|
||||||
|
.ok_or(ProtocolError::InvalidLoginError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<SIG: SignatureProtocol, KE: 'static + Group, KEH: Hash> Sealed for SigmaI<SIG, KE, KEH>
|
||||||
|
where
|
||||||
|
KEH::Core: ProxyHash,
|
||||||
|
<KEH::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
|
||||||
|
Le<<KEH::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<CS: CipherSuite, SIG: SignatureProtocol, KE: Group> Deserialize for Ke2State<CS, SIG, KE>
|
||||||
|
where
|
||||||
|
SIG::VerifyState<CS, KE>: Deserialize,
|
||||||
|
{
|
||||||
|
fn deserialize_take(input: &mut &[u8]) -> Result<Self, ProtocolError> {
|
||||||
|
Ok(Self {
|
||||||
|
client_s_pk: PublicKey::deserialize_take(input)?,
|
||||||
|
session_key: input.take_array("session key")?,
|
||||||
|
verify_state: SIG::VerifyState::deserialize_take(input)?,
|
||||||
|
expected_mac: input.take_array("expected mac")?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Ke2StateLen<CS, SIG: SignatureProtocol, KE> = Sum<
|
||||||
|
Sum<Sum<<SIG::Group as Group>::PkLen, OutputSize<KeHash<CS>>>, VerifyStateLen<CS, SIG, KE>>,
|
||||||
|
OutputSize<KeHash<CS>>,
|
||||||
|
>;
|
||||||
|
|
||||||
|
type VerifyStateLen<CS, SIG: SignatureProtocol, KE> = <SIG::VerifyState<CS, KE> as Serialize>::Len;
|
||||||
|
|
||||||
|
impl<CS: CipherSuite, SIG: SignatureProtocol, KE: Group> Serialize for Ke2State<CS, SIG, KE>
|
||||||
|
where
|
||||||
|
SIG::VerifyState<CS, KE>: Serialize,
|
||||||
|
// Ke2State: ((SigPk + Hash) + VerifyState) + Hash
|
||||||
|
<SIG::Group as Group>::PkLen: Add<OutputSize<KeHash<CS>>>,
|
||||||
|
Sum<<SIG::Group as Group>::PkLen, OutputSize<KeHash<CS>>>:
|
||||||
|
ArrayLength<u8> + Add<VerifyStateLen<CS, SIG, KE>>,
|
||||||
|
Sum<Sum<<SIG::Group as Group>::PkLen, OutputSize<KeHash<CS>>>, VerifyStateLen<CS, SIG, KE>>:
|
||||||
|
ArrayLength<u8> + Add<OutputSize<KeHash<CS>>>,
|
||||||
|
Ke2StateLen<CS, SIG, KE>: ArrayLength<u8>,
|
||||||
|
{
|
||||||
|
type Len = Ke2StateLen<CS, SIG, KE>;
|
||||||
|
|
||||||
|
fn serialize(&self) -> GenericArray<u8, Self::Len> {
|
||||||
|
self.client_s_pk
|
||||||
|
.serialize()
|
||||||
|
.concat(self.session_key.clone())
|
||||||
|
.concat(self.verify_state.serialize())
|
||||||
|
.concat(self.expected_mac.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<SIG: SignatureProtocol, KE: Group, KEH: Hash> Deserialize for Ke2Message<SIG, KE, KEH>
|
||||||
|
where
|
||||||
|
KEH::Core: ProxyHash,
|
||||||
|
<KEH::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
|
||||||
|
Le<<KEH::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
|
||||||
|
{
|
||||||
|
fn deserialize_take(input: &mut &[u8]) -> Result<Self, ProtocolError> {
|
||||||
|
Ok(Self {
|
||||||
|
server_nonce: input.take_array("server nonce")?,
|
||||||
|
server_e_pk: PublicKey::deserialize_take(input)?,
|
||||||
|
signature: SIG::deserialize_take_signature(input)?,
|
||||||
|
mac: input.take_array("mac")?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<SIG: SignatureProtocol, KE: Group, KEH: Hash> Serialize for Ke2Message<SIG, KE, KEH>
|
||||||
|
where
|
||||||
|
KEH::Core: ProxyHash,
|
||||||
|
<KEH::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
|
||||||
|
Le<<KEH::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
|
||||||
|
// Ke2Message: ((Nonce + KePk) + Signature) + Hash
|
||||||
|
NonceLen: Add<KE::PkLen>,
|
||||||
|
Sum<NonceLen, KE::PkLen>: ArrayLength<u8> + Add<SIG::SignatureLen>,
|
||||||
|
Sum<Sum<NonceLen, KE::PkLen>, SIG::SignatureLen>: ArrayLength<u8> + Add<OutputSize<KEH>>,
|
||||||
|
Sum<Sum<Sum<NonceLen, KE::PkLen>, SIG::SignatureLen>, OutputSize<KEH>>: ArrayLength<u8>,
|
||||||
|
{
|
||||||
|
type Len = Sum<Sum<Sum<NonceLen, KE::PkLen>, SIG::SignatureLen>, OutputSize<KEH>>;
|
||||||
|
|
||||||
|
fn serialize(&self) -> GenericArray<u8, Self::Len> {
|
||||||
|
self.server_nonce
|
||||||
|
.concat(self.server_e_pk.serialize())
|
||||||
|
.concat(SIG::serialize_signature(&self.signature))
|
||||||
|
.concat(self.mac.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<SIG: SignatureProtocol, KEH: Hash> Deserialize for Ke3Message<SIG, KEH>
|
||||||
|
where
|
||||||
|
KEH::Core: ProxyHash,
|
||||||
|
<KEH::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
|
||||||
|
Le<<KEH::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
|
||||||
|
{
|
||||||
|
fn deserialize_take(input: &mut &[u8]) -> Result<Self, ProtocolError> {
|
||||||
|
Ok(Self {
|
||||||
|
signature: SIG::deserialize_take_signature(input)?,
|
||||||
|
mac: input.take_array("mac")?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<SIG: SignatureProtocol, KEH: Hash> Serialize for Ke3Message<SIG, KEH>
|
||||||
|
where
|
||||||
|
KEH::Core: ProxyHash,
|
||||||
|
<KEH::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
|
||||||
|
Le<<KEH::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
|
||||||
|
// Ke2Message: Signature + Hash
|
||||||
|
SIG::SignatureLen: Add<OutputSize<KEH>>,
|
||||||
|
Sum<SIG::SignatureLen, OutputSize<KEH>>: ArrayLength<u8>,
|
||||||
|
{
|
||||||
|
type Len = Sum<SIG::SignatureLen, OutputSize<KEH>>;
|
||||||
|
|
||||||
|
fn serialize(&self) -> GenericArray<u8, Self::Len> {
|
||||||
|
SIG::serialize_signature(&self.signature).concat(self.mac.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////
|
||||||
|
// Test Implementations //
|
||||||
|
//===================== //
|
||||||
|
//////////////////////////
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
use crate::key_exchange::shared::Ke1MessageIter;
|
||||||
|
#[cfg(test)]
|
||||||
|
use crate::serialization::AssertZeroized;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
impl<CS: CipherSuite, KE: Group> AssertZeroized for CachedMessage<CS, KE>
|
||||||
|
where
|
||||||
|
Ke1MessageIter<KE>: AssertZeroized,
|
||||||
|
{
|
||||||
|
fn assert_zeroized(&self) {
|
||||||
|
let Self {
|
||||||
|
credential_request,
|
||||||
|
ke1_message,
|
||||||
|
credential_response,
|
||||||
|
server_nonce,
|
||||||
|
server_e_pk,
|
||||||
|
server_mac,
|
||||||
|
} = self;
|
||||||
|
|
||||||
|
credential_request.assert_zeroized();
|
||||||
|
ke1_message.assert_zeroized();
|
||||||
|
credential_response.assert_zeroized();
|
||||||
|
|
||||||
|
for byte in server_nonce.iter().chain(server_e_pk).chain(server_mac) {
|
||||||
|
assert_eq!(byte, &0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
impl<CS: CipherSuite, SIG: SignatureProtocol, KE: Group> AssertZeroized for Ke2State<CS, SIG, KE>
|
||||||
|
where
|
||||||
|
<SIG::Group as Group>::Pk: AssertZeroized,
|
||||||
|
SIG::VerifyState<CS, KE>: AssertZeroized,
|
||||||
|
{
|
||||||
|
fn assert_zeroized(&self) {
|
||||||
|
let Self {
|
||||||
|
client_s_pk,
|
||||||
|
session_key,
|
||||||
|
verify_state,
|
||||||
|
expected_mac,
|
||||||
|
} = self;
|
||||||
|
|
||||||
|
client_s_pk.assert_zeroized();
|
||||||
|
verify_state.assert_zeroized();
|
||||||
|
|
||||||
|
for byte in session_key.iter().chain(expected_mac) {
|
||||||
|
assert_eq!(byte, &0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||||
|
//
|
||||||
|
// This source code is dual-licensed under either the MIT license found in the
|
||||||
|
// LICENSE-MIT file in the root directory of this source tree or the Apache
|
||||||
|
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||||
|
// of this source tree. You may select, at your option, one of the above-listed
|
||||||
|
// licenses.
|
||||||
|
|
||||||
|
//! PureEdDSA implementation for [`SigmaI`](crate::SigmaI). Currently only
|
||||||
|
//! supports [`Ed25519`](crate::Ed25519).
|
||||||
|
|
||||||
|
use core::marker::PhantomData;
|
||||||
|
|
||||||
|
use generic_array::GenericArray;
|
||||||
|
use rand::{CryptoRng, RngCore};
|
||||||
|
use zeroize::Zeroize;
|
||||||
|
|
||||||
|
use self::implementation::PureEddsaImpl;
|
||||||
|
use super::{Message, MessageBuilder, SignatureProtocol};
|
||||||
|
use crate::ciphersuite::CipherSuite;
|
||||||
|
use crate::errors::ProtocolError;
|
||||||
|
use crate::key_exchange::group::Group;
|
||||||
|
use crate::key_exchange::sigma_i::CachedMessage;
|
||||||
|
|
||||||
|
/// PureEdDSA for [`SigmaI`](crate::SigmaI).
|
||||||
|
///
|
||||||
|
/// The ["verification state"](Self::VerifyState) is a [`CachedMessage`],
|
||||||
|
/// created by calling [`Message::to_cached()`].
|
||||||
|
pub struct PureEddsa<G>(PhantomData<G>);
|
||||||
|
|
||||||
|
impl<G: PureEddsaImpl> SignatureProtocol for PureEddsa<G> {
|
||||||
|
type Group = G;
|
||||||
|
type Signature = G::Signature;
|
||||||
|
type SignatureLen = G::SignatureLen;
|
||||||
|
type VerifyState<CS: CipherSuite, KE: Group> = CachedMessage<CS, KE>;
|
||||||
|
|
||||||
|
fn sign<'a, R: CryptoRng + RngCore, CS: CipherSuite, KE: Group>(
|
||||||
|
sk: &G::Sk,
|
||||||
|
_: &mut R,
|
||||||
|
message: &Message<CS, KE>,
|
||||||
|
) -> (Self::Signature, Self::VerifyState<CS, KE>) {
|
||||||
|
G::sign(sk, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn verify<CS: CipherSuite, KE: Group>(
|
||||||
|
pk: &G::Pk,
|
||||||
|
message_builder: MessageBuilder<'_, CS>,
|
||||||
|
state: Self::VerifyState<CS, KE>,
|
||||||
|
signature: &Self::Signature,
|
||||||
|
) -> Result<(), ProtocolError> {
|
||||||
|
G::verify(pk, message_builder, state, signature)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_take_signature(bytes: &mut &[u8]) -> Result<Self::Signature, ProtocolError> {
|
||||||
|
G::deserialize_take_signature(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_signature(signature: &Self::Signature) -> GenericArray<u8, Self::SignatureLen> {
|
||||||
|
G::serialize_signature(signature)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(in super::super) mod implementation {
|
||||||
|
use generic_array::ArrayLength;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub trait PureEddsaImpl: Group {
|
||||||
|
type Signature: Clone + Zeroize;
|
||||||
|
type SignatureLen: ArrayLength<u8>;
|
||||||
|
|
||||||
|
fn sign<CS: CipherSuite, KE: Group>(
|
||||||
|
sk: &Self::Sk,
|
||||||
|
message: &Message<CS, KE>,
|
||||||
|
) -> (Self::Signature, CachedMessage<CS, KE>);
|
||||||
|
|
||||||
|
fn verify<CS: CipherSuite, KE: Group>(
|
||||||
|
pk: &Self::Pk,
|
||||||
|
message_builder: MessageBuilder<'_, CS>,
|
||||||
|
state: CachedMessage<CS, KE>,
|
||||||
|
signature: &Self::Signature,
|
||||||
|
) -> Result<(), ProtocolError>;
|
||||||
|
|
||||||
|
fn deserialize_take_signature(bytes: &mut &[u8]) -> Result<Self::Signature, ProtocolError>;
|
||||||
|
|
||||||
|
fn serialize_signature(signature: &Self::Signature)
|
||||||
|
-> GenericArray<u8, Self::SignatureLen>;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||||
|
//
|
||||||
|
// This source code is dual-licensed under either the MIT license found in the
|
||||||
|
// LICENSE-MIT file in the root directory of this source tree or the Apache
|
||||||
|
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||||
|
// of this source tree. You may select, at your option, one of the above-listed
|
||||||
|
// licenses.
|
||||||
|
|
||||||
|
use derive_where::derive_where;
|
||||||
|
use digest::{Output, OutputSizeUser};
|
||||||
|
use generic_array::{ArrayLength, GenericArray};
|
||||||
|
|
||||||
|
use crate::errors::ProtocolError;
|
||||||
|
use crate::key_exchange::traits::{Deserialize, Serialize};
|
||||||
|
use crate::serialization::SliceExt;
|
||||||
|
|
||||||
|
/// Pre-hash of the message to be verified.
|
||||||
|
#[derive_where(Clone, Debug, Eq, Hash, PartialEq, Zeroize)]
|
||||||
|
#[derive_where(Copy; <H::OutputSize as ArrayLength<u8>>::ArrayType)]
|
||||||
|
#[cfg_attr(
|
||||||
|
feature = "serde",
|
||||||
|
derive(serde::Deserialize, serde::Serialize),
|
||||||
|
serde(bound = "")
|
||||||
|
)]
|
||||||
|
pub struct PreHash<H: OutputSizeUser>(pub Output<H>);
|
||||||
|
|
||||||
|
impl<H: OutputSizeUser> Deserialize for PreHash<H> {
|
||||||
|
fn deserialize_take(input: &mut &[u8]) -> Result<Self, ProtocolError> {
|
||||||
|
Ok(Self(input.take_array("pre-hash")?))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<H: OutputSizeUser> Serialize for PreHash<H> {
|
||||||
|
type Len = H::OutputSize;
|
||||||
|
|
||||||
|
fn serialize(&self) -> GenericArray<u8, Self::Len> {
|
||||||
|
self.0.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////
|
||||||
|
// Test Implementations //
|
||||||
|
//===================== //
|
||||||
|
//////////////////////////
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
use crate::serialization::AssertZeroized;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
impl<H: OutputSizeUser> AssertZeroized for PreHash<H> {
|
||||||
|
fn assert_zeroized(&self) {
|
||||||
|
assert_eq!(self.0, GenericArray::default());
|
||||||
|
}
|
||||||
|
}
|
||||||
+345
-68
@@ -6,83 +6,322 @@
|
|||||||
// of this source tree. You may select, at your option, one of the above-listed
|
// of this source tree. You may select, at your option, one of the above-listed
|
||||||
// licenses.
|
// licenses.
|
||||||
|
|
||||||
use digest::core_api::BlockSizeUser;
|
use core::iter;
|
||||||
|
use core::ops::Add;
|
||||||
|
|
||||||
|
use derive_where::derive_where;
|
||||||
|
use digest::core_api::{BlockSizeUser, CoreProxy};
|
||||||
use digest::Output;
|
use digest::Output;
|
||||||
use generic_array::typenum::{IsLess, Le, NonZero, U256};
|
use generic_array::sequence::Concat;
|
||||||
|
use generic_array::typenum::{IsLess, Le, NonZero, Sum, U2, U256};
|
||||||
use generic_array::{ArrayLength, GenericArray};
|
use generic_array::{ArrayLength, GenericArray};
|
||||||
use rand::{CryptoRng, RngCore};
|
use rand::{CryptoRng, RngCore};
|
||||||
use zeroize::ZeroizeOnDrop;
|
use voprf::{BlindedElement, EvaluationElement};
|
||||||
|
use zeroize::{Zeroize, ZeroizeOnDrop};
|
||||||
|
|
||||||
use crate::ciphersuite::{CipherSuite, OprfHash};
|
#[cfg(test)]
|
||||||
|
use crate::ciphersuite::KeHash;
|
||||||
|
use crate::ciphersuite::{CipherSuite, OprfGroup};
|
||||||
use crate::errors::ProtocolError;
|
use crate::errors::ProtocolError;
|
||||||
use crate::hash::{Hash, ProxyHash};
|
use crate::hash::{Hash, ProxyHash};
|
||||||
use crate::key_exchange::group::KeGroup;
|
use crate::key_exchange::group::Group;
|
||||||
|
use crate::key_exchange::shared::{NonceLen, STR_CONTEXT};
|
||||||
use crate::keypair::{PrivateKey, PublicKey};
|
use crate::keypair::{PrivateKey, PublicKey};
|
||||||
|
use crate::opaque::{Identifiers, MaskedResponse, MaskedResponseLen};
|
||||||
|
use crate::serialization::{i2osp, SliceExt};
|
||||||
|
|
||||||
pub trait KeyExchange<D: Hash, G: KeGroup>
|
/// The key exchange trait. This is only exposed so users can use it in generics
|
||||||
|
/// and qualified bounds.
|
||||||
|
#[allow(private_bounds)]
|
||||||
|
pub trait KeyExchange: Sealed
|
||||||
where
|
where
|
||||||
D::Core: ProxyHash,
|
<Self::Hash as CoreProxy>::Core: ProxyHash,
|
||||||
<D::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
|
<<Self::Hash as CoreProxy>::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
|
||||||
Le<<D::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
|
Le<<<Self::Hash as CoreProxy>::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
|
||||||
{
|
{
|
||||||
type KE1State: Deserialize + Serialize + ZeroizeOnDrop + Clone;
|
/// The group used for the key exchange.
|
||||||
type KE2State: Deserialize + Serialize + ZeroizeOnDrop + Clone;
|
type Group: Group;
|
||||||
type KE1Message: Deserialize + Serialize + ZeroizeOnDrop + Clone;
|
/// The has used for the key exchange.
|
||||||
type KE2Builder: ZeroizeOnDrop + Clone;
|
type Hash: Hash;
|
||||||
type KE2BuilderData<'a>;
|
|
||||||
type KE2BuilderInput;
|
|
||||||
type KE2Message: Deserialize + Serialize + ZeroizeOnDrop + Clone;
|
|
||||||
type KE3Message: Deserialize + Serialize + ZeroizeOnDrop + Clone;
|
|
||||||
|
|
||||||
fn generate_ke1<OprfCs: voprf::CipherSuite, R: RngCore + CryptoRng>(
|
#[doc(hidden)]
|
||||||
|
type KE1State: ZeroizeOnDrop + Clone;
|
||||||
|
#[doc(hidden)]
|
||||||
|
type KE2State<CS: CipherSuite>: ZeroizeOnDrop + Clone;
|
||||||
|
#[doc(hidden)]
|
||||||
|
type KE1Message: ZeroizeOnDrop + Clone;
|
||||||
|
#[doc(hidden)]
|
||||||
|
type KE2Builder<'a, CS: CipherSuite<KeyExchange = Self>>: ZeroizeOnDrop + Clone;
|
||||||
|
#[doc(hidden)]
|
||||||
|
type KE2BuilderData<'a, CS: 'static + CipherSuite>;
|
||||||
|
#[doc(hidden)]
|
||||||
|
type KE2BuilderInput<CS: CipherSuite>;
|
||||||
|
#[doc(hidden)]
|
||||||
|
type KE2Message: ZeroizeOnDrop + Clone;
|
||||||
|
#[doc(hidden)]
|
||||||
|
type KE3Message: ZeroizeOnDrop + Clone;
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
fn generate_ke1<R: RngCore + CryptoRng>(
|
||||||
rng: &mut R,
|
rng: &mut R,
|
||||||
) -> Result<(Self::KE1State, Self::KE1Message), ProtocolError>;
|
) -> Result<(Self::KE1State, Self::KE1Message), ProtocolError>;
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[doc(hidden)]
|
||||||
fn ke2_builder<'a, 'b, 'c, 'd, OprfCs: voprf::CipherSuite, R: RngCore + CryptoRng>(
|
fn ke2_builder<'a, CS: CipherSuite<KeyExchange = Self>, R: RngCore + CryptoRng>(
|
||||||
rng: &mut R,
|
rng: &mut R,
|
||||||
serialized_credential_request: impl Iterator<Item = &'a [u8]>,
|
credential_request: CredentialRequestParts<CS>,
|
||||||
serialized_credential_response: impl Iterator<Item = &'b [u8]>,
|
|
||||||
ke1_message: Self::KE1Message,
|
ke1_message: Self::KE1Message,
|
||||||
client_s_pk: PublicKey<G>,
|
credential_response: CredentialResponseParts<CS>,
|
||||||
id_u: impl Iterator<Item = &'c [u8]>,
|
client_s_pk: PublicKey<Self::Group>,
|
||||||
id_s: impl Iterator<Item = &'d [u8]>,
|
identifiers: SerializedIdentifiers<'a, Self::Group>,
|
||||||
context: &[u8],
|
context: SerializedContext<'a>,
|
||||||
) -> Result<Self::KE2Builder, ProtocolError>;
|
) -> Result<Self::KE2Builder<'a, CS>, ProtocolError>;
|
||||||
|
|
||||||
fn ke2_builder_data(builder: &Self::KE2Builder) -> Self::KE2BuilderData<'_>;
|
#[doc(hidden)]
|
||||||
|
fn ke2_builder_data<'a, CS: CipherSuite<KeyExchange = Self>>(
|
||||||
|
builder: &'a Self::KE2Builder<'_, CS>,
|
||||||
|
) -> Self::KE2BuilderData<'a, CS>;
|
||||||
|
|
||||||
fn generate_ke2_input(
|
#[doc(hidden)]
|
||||||
builder: &Self::KE2Builder,
|
fn generate_ke2_input<CS: CipherSuite<KeyExchange = Self>, R: CryptoRng + RngCore>(
|
||||||
server_s_sk: &PrivateKey<G>,
|
builder: &Self::KE2Builder<'_, CS>,
|
||||||
) -> Self::KE2BuilderInput;
|
rng: &mut R,
|
||||||
|
server_s_sk: &PrivateKey<Self::Group>,
|
||||||
|
) -> Self::KE2BuilderInput<CS>;
|
||||||
|
|
||||||
fn build_ke2(
|
#[doc(hidden)]
|
||||||
builder: Self::KE2Builder,
|
fn build_ke2<CS: CipherSuite<KeyExchange = Self>>(
|
||||||
input: Self::KE2BuilderInput,
|
builder: Self::KE2Builder<'_, CS>,
|
||||||
) -> Result<GenerateKe2Result<Self, D, G>, ProtocolError>;
|
input: Self::KE2BuilderInput<CS>,
|
||||||
|
) -> Result<GenerateKe2Result<CS>, ProtocolError>;
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn generate_ke3<'a, 'b, 'c, 'd>(
|
fn generate_ke3<CS: CipherSuite<KeyExchange = Self>, R: CryptoRng + RngCore>(
|
||||||
l2_component: impl Iterator<Item = &'a [u8]>,
|
rng: &mut R,
|
||||||
|
credential_request: CredentialRequestParts<CS>,
|
||||||
|
ke1_message: Self::KE1Message,
|
||||||
|
credential_response: CredentialResponseParts<CS>,
|
||||||
ke2_message: Self::KE2Message,
|
ke2_message: Self::KE2Message,
|
||||||
ke1_state: &Self::KE1State,
|
ke1_state: &Self::KE1State,
|
||||||
serialized_credential_request: impl Iterator<Item = &'b [u8]>,
|
server_s_pk: PublicKey<Self::Group>,
|
||||||
server_s_pk: PublicKey<G>,
|
client_s_sk: PrivateKey<Self::Group>,
|
||||||
client_s_sk: PrivateKey<G>,
|
identifiers: SerializedIdentifiers<'_, Self::Group>,
|
||||||
id_u: impl Iterator<Item = &'c [u8]>,
|
context: SerializedContext<'_>,
|
||||||
id_s: impl Iterator<Item = &'d [u8]>,
|
) -> Result<GenerateKe3Result<Self>, ProtocolError>;
|
||||||
context: &[u8],
|
|
||||||
) -> Result<GenerateKe3Result<Self, D, G>, ProtocolError>;
|
|
||||||
|
|
||||||
fn finish_ke(
|
#[doc(hidden)]
|
||||||
|
fn finish_ke<CS: CipherSuite<KeyExchange = Self>>(
|
||||||
ke3_message: Self::KE3Message,
|
ke3_message: Self::KE3Message,
|
||||||
ke2_state: &Self::KE2State,
|
ke2_state: &Self::KE2State<CS>,
|
||||||
) -> Result<Output<D>, ProtocolError>;
|
identifiers: Identifiers<'_>,
|
||||||
|
context: SerializedContext<'_>,
|
||||||
|
) -> Result<Output<Self::Hash>, ProtocolError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) trait Sealed {}
|
||||||
|
|
||||||
|
#[cfg_attr(
|
||||||
|
feature = "serde",
|
||||||
|
derive(serde::Deserialize, serde::Serialize),
|
||||||
|
serde(bound = "")
|
||||||
|
)]
|
||||||
|
#[derive_where(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Zeroize)]
|
||||||
|
pub struct CredentialRequestParts<CS: CipherSuite>(
|
||||||
|
GenericArray<u8, <OprfGroup<CS> as voprf::Group>::ElemLen>,
|
||||||
|
);
|
||||||
|
|
||||||
|
impl<CS: CipherSuite> CredentialRequestParts<CS> {
|
||||||
|
pub(crate) fn new(blinded_element: &BlindedElement<CS::OprfCs>) -> Self {
|
||||||
|
Self(blinded_element.serialize())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn iter(&self) -> impl Clone + Iterator<Item = &[u8]> {
|
||||||
|
iter::once(self.0.as_slice())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deserialize_take(input: &mut &[u8]) -> Result<Self, ProtocolError> {
|
||||||
|
Ok(Self(input.take_array("blinded element")?))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type CredentialRequestPartsLen<CS: CipherSuite> = <OprfGroup<CS> as voprf::Group>::ElemLen;
|
||||||
|
|
||||||
|
impl<CS: CipherSuite> Serialize for CredentialRequestParts<CS> {
|
||||||
|
type Len = CredentialRequestPartsLen<CS>;
|
||||||
|
|
||||||
|
fn serialize(&self) -> GenericArray<u8, Self::Len> {
|
||||||
|
self.0.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(
|
||||||
|
feature = "serde",
|
||||||
|
derive(serde::Deserialize, serde::Serialize),
|
||||||
|
serde(bound = "")
|
||||||
|
)]
|
||||||
|
#[derive_where(Clone, Debug, Eq, Hash, PartialEq, Zeroize)]
|
||||||
|
pub struct CredentialResponseParts<CS: CipherSuite> {
|
||||||
|
evaluation_element: GenericArray<u8, <OprfGroup<CS> as voprf::Group>::ElemLen>,
|
||||||
|
masking_nonce: GenericArray<u8, NonceLen>,
|
||||||
|
masked_response: MaskedResponse<CS>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<CS: CipherSuite> CredentialResponseParts<CS> {
|
||||||
|
pub(crate) fn new(
|
||||||
|
evaluation_element: &EvaluationElement<CS::OprfCs>,
|
||||||
|
masking_nonce: GenericArray<u8, NonceLen>,
|
||||||
|
masked_response: MaskedResponse<CS>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
evaluation_element: evaluation_element.serialize(),
|
||||||
|
masking_nonce,
|
||||||
|
masked_response,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn iter(&self) -> impl Clone + Iterator<Item = &[u8]> {
|
||||||
|
[self.evaluation_element.as_slice(), &self.masking_nonce]
|
||||||
|
.into_iter()
|
||||||
|
.chain(self.masked_response.iter())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deserialize_take(input: &mut &[u8]) -> Result<Self, ProtocolError> {
|
||||||
|
Ok(Self {
|
||||||
|
evaluation_element: input.take_array("evaluation element")?,
|
||||||
|
masking_nonce: input.take_array("masking nonce")?,
|
||||||
|
masked_response: MaskedResponse::deserialize_take(input)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type CredentialResponsePartsLen<CS: CipherSuite> =
|
||||||
|
Sum<Sum<<OprfGroup<CS> as voprf::Group>::ElemLen, NonceLen>, MaskedResponseLen<CS>>;
|
||||||
|
|
||||||
|
impl<CS: CipherSuite> Serialize for CredentialResponseParts<CS>
|
||||||
|
where
|
||||||
|
<OprfGroup<CS> as voprf::Group>::ElemLen: Add<NonceLen>,
|
||||||
|
Sum<<OprfGroup<CS> as voprf::Group>::ElemLen, NonceLen>:
|
||||||
|
ArrayLength<u8> + Add<MaskedResponseLen<CS>>,
|
||||||
|
CredentialResponsePartsLen<CS>: ArrayLength<u8>,
|
||||||
|
{
|
||||||
|
type Len = CredentialResponsePartsLen<CS>;
|
||||||
|
|
||||||
|
fn serialize(&self) -> GenericArray<u8, Self::Len> {
|
||||||
|
self.evaluation_element
|
||||||
|
.clone()
|
||||||
|
.concat(self.masking_nonce)
|
||||||
|
.concat(self.masked_response.serialize())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(
|
||||||
|
feature = "serde",
|
||||||
|
derive(serde::Deserialize, serde::Serialize),
|
||||||
|
serde(bound = "")
|
||||||
|
)]
|
||||||
|
#[derive(Clone, Debug, Eq, Hash, PartialEq, Zeroize)]
|
||||||
|
pub struct SerializedContext<'a> {
|
||||||
|
length: GenericArray<u8, U2>,
|
||||||
|
#[zeroize(skip)]
|
||||||
|
context: &'a [u8],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> SerializedContext<'a> {
|
||||||
|
pub(crate) fn from(context: Option<&'a [u8]>) -> Result<Self, ProtocolError> {
|
||||||
|
let context = context.unwrap_or(&[]);
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
length: i2osp::<U2>(context.len())?,
|
||||||
|
context,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn iter(&self) -> impl Clone + Iterator<Item = &[u8]> {
|
||||||
|
iter::once(STR_CONTEXT).chain([self.length.as_slice(), self.context])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(
|
||||||
|
feature = "serde",
|
||||||
|
derive(serde::Deserialize, serde::Serialize),
|
||||||
|
serde(bound(deserialize = "'de: 'a", serialize = ""))
|
||||||
|
)]
|
||||||
|
#[derive_where(Clone, Debug, Eq, Hash, PartialEq, Zeroize)]
|
||||||
|
pub struct SerializedIdentifiers<'a, G: Group> {
|
||||||
|
pub client: SerializedIdentifier<'a, G>,
|
||||||
|
pub server: SerializedIdentifier<'a, G>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Computes `I2OSP(len(input), max_bytes) || input` and helps hold output
|
||||||
|
/// without allocation.
|
||||||
|
#[cfg_attr(
|
||||||
|
feature = "serde",
|
||||||
|
derive(serde::Deserialize, serde::Serialize),
|
||||||
|
serde(bound(deserialize = "'de: 'a", serialize = ""))
|
||||||
|
)]
|
||||||
|
#[derive_where(Clone, Debug, Eq, Hash, PartialEq, Zeroize)]
|
||||||
|
pub struct SerializedIdentifier<'a, G: Group> {
|
||||||
|
length: GenericArray<u8, U2>,
|
||||||
|
identifier: Identifier<'a, G>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(
|
||||||
|
feature = "serde",
|
||||||
|
derive(serde::Deserialize, serde::Serialize),
|
||||||
|
serde(bound = "")
|
||||||
|
)]
|
||||||
|
#[derive_where(Clone, Debug, Eq, Hash, PartialEq, Zeroize)]
|
||||||
|
enum Identifier<'a, G: Group> {
|
||||||
|
Owned(GenericArray<u8, G::PkLen>),
|
||||||
|
#[derive_where(skip_inner(Zeroize))]
|
||||||
|
Borrowed(&'a [u8]),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, G: Group> SerializedIdentifiers<'a, G> {
|
||||||
|
pub(crate) fn from_identifiers(
|
||||||
|
ids: Identifiers<'a>,
|
||||||
|
client_s_pk: GenericArray<u8, G::PkLen>,
|
||||||
|
server_s_pk: GenericArray<u8, G::PkLen>,
|
||||||
|
) -> Result<Self, ProtocolError> {
|
||||||
|
let client = SerializedIdentifier::from_identifier(ids.client, client_s_pk)?;
|
||||||
|
let server = SerializedIdentifier::from_identifier(ids.server, server_s_pk)?;
|
||||||
|
|
||||||
|
Ok(Self { client, server })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, G: Group> SerializedIdentifier<'a, G> {
|
||||||
|
pub fn from_identifier(
|
||||||
|
id: Option<&'a [u8]>,
|
||||||
|
s_pk: GenericArray<u8, G::PkLen>,
|
||||||
|
) -> Result<Self, ProtocolError> {
|
||||||
|
if let Some(id) = id {
|
||||||
|
Ok(SerializedIdentifier {
|
||||||
|
length: i2osp::<U2>(id.len())?,
|
||||||
|
identifier: Identifier::Borrowed(id),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Ok(SerializedIdentifier {
|
||||||
|
length: i2osp::<U2>(s_pk.len())?,
|
||||||
|
identifier: Identifier::Owned(s_pk),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn iter(&self) -> impl Clone + Iterator<Item = &[u8]> {
|
||||||
|
// Some magic to make it output the same type in all branches.
|
||||||
|
[self.length.as_slice()]
|
||||||
|
.into_iter()
|
||||||
|
.chain(match &self.identifier {
|
||||||
|
Identifier::Owned(bytes) => [bytes.as_slice()],
|
||||||
|
Identifier::Borrowed(bytes) => [*bytes],
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Deserialize: Sized {
|
pub trait Deserialize: Sized {
|
||||||
fn deserialize(input: &[u8]) -> Result<Self, ProtocolError>;
|
fn deserialize_take(input: &mut &[u8]) -> Result<Self, ProtocolError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Serialize {
|
pub trait Serialize {
|
||||||
@@ -92,34 +331,72 @@ pub trait Serialize {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(test))]
|
#[cfg(not(test))]
|
||||||
pub type GenerateKe2Result<K, D, G> = (
|
pub type GenerateKe2Result<CS: CipherSuite> = (
|
||||||
<K as KeyExchange<D, G>>::KE2State,
|
<CS::KeyExchange as KeyExchange>::KE2State<CS>,
|
||||||
<K as KeyExchange<D, G>>::KE2Message,
|
<CS::KeyExchange as KeyExchange>::KE2Message,
|
||||||
);
|
);
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub type GenerateKe2Result<K, D, G> = (
|
pub type GenerateKe2Result<CS: CipherSuite> = (
|
||||||
<K as KeyExchange<D, G>>::KE2State,
|
<CS::KeyExchange as KeyExchange>::KE2State<CS>,
|
||||||
<K as KeyExchange<D, G>>::KE2Message,
|
<CS::KeyExchange as KeyExchange>::KE2Message,
|
||||||
Output<D>,
|
Output<KeHash<CS>>,
|
||||||
Output<D>,
|
Output<KeHash<CS>>,
|
||||||
);
|
);
|
||||||
#[cfg(not(test))]
|
#[cfg(not(test))]
|
||||||
pub type GenerateKe3Result<K, D, G> = (Output<D>, <K as KeyExchange<D, G>>::KE3Message);
|
pub type GenerateKe3Result<K: KeyExchange> = (Output<K::Hash>, K::KE3Message);
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub type GenerateKe3Result<K, D, G> = (
|
pub type GenerateKe3Result<K: KeyExchange> = (
|
||||||
Output<D>,
|
Output<K::Hash>,
|
||||||
<K as KeyExchange<D, G>>::KE3Message,
|
K::KE3Message,
|
||||||
Output<D>,
|
Output<K::Hash>,
|
||||||
Output<D>,
|
Output<K::Hash>,
|
||||||
);
|
);
|
||||||
|
|
||||||
pub type Ke1StateLen<CS: CipherSuite> =
|
pub type Ke1StateLen<CS: CipherSuite> =
|
||||||
<<CS::KeyExchange as KeyExchange<OprfHash<CS>, CS::KeGroup>>::KE1State as Serialize>::Len;
|
<<CS::KeyExchange as KeyExchange>::KE1State as Serialize>::Len;
|
||||||
pub type Ke1MessageLen<CS: CipherSuite> =
|
pub type Ke1MessageLen<CS: CipherSuite> =
|
||||||
<<CS::KeyExchange as KeyExchange<OprfHash<CS>, CS::KeGroup>>::KE1Message as Serialize>::Len;
|
<<CS::KeyExchange as KeyExchange>::KE1Message as Serialize>::Len;
|
||||||
pub type Ke2StateLen<CS: CipherSuite> =
|
pub type Ke2StateLen<CS: CipherSuite> =
|
||||||
<<CS::KeyExchange as KeyExchange<OprfHash<CS>, CS::KeGroup>>::KE2State as Serialize>::Len;
|
<<CS::KeyExchange as KeyExchange>::KE2State<CS> as Serialize>::Len;
|
||||||
pub type Ke2MessageLen<CS: CipherSuite> =
|
pub type Ke2MessageLen<CS: CipherSuite> =
|
||||||
<<CS::KeyExchange as KeyExchange<OprfHash<CS>, CS::KeGroup>>::KE2Message as Serialize>::Len;
|
<<CS::KeyExchange as KeyExchange>::KE2Message as Serialize>::Len;
|
||||||
pub type Ke3MessageLen<CS: CipherSuite> =
|
pub type Ke3MessageLen<CS: CipherSuite> =
|
||||||
<<CS::KeyExchange as KeyExchange<OprfHash<CS>, CS::KeGroup>>::KE3Message as Serialize>::Len;
|
<<CS::KeyExchange as KeyExchange>::KE3Message as Serialize>::Len;
|
||||||
|
|
||||||
|
//////////////////////////
|
||||||
|
// Test Implementations //
|
||||||
|
//===================== //
|
||||||
|
//////////////////////////
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
use crate::serialization::AssertZeroized;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
impl<CS: CipherSuite> AssertZeroized for CredentialRequestParts<CS> {
|
||||||
|
fn assert_zeroized(&self) {
|
||||||
|
let Self(blinded_element) = self;
|
||||||
|
|
||||||
|
for byte in blinded_element.iter() {
|
||||||
|
assert_eq!(byte, &0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
impl<CS: CipherSuite> AssertZeroized for CredentialResponseParts<CS> {
|
||||||
|
fn assert_zeroized(&self) {
|
||||||
|
let Self {
|
||||||
|
evaluation_element,
|
||||||
|
masking_nonce,
|
||||||
|
masked_response,
|
||||||
|
} = self;
|
||||||
|
|
||||||
|
for byte in evaluation_element
|
||||||
|
.iter()
|
||||||
|
.chain(masking_nonce)
|
||||||
|
.chain(masked_response.iter().flatten())
|
||||||
|
{
|
||||||
|
assert_eq!(byte, &0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+267
-501
@@ -7,42 +7,34 @@
|
|||||||
// licenses.
|
// licenses.
|
||||||
|
|
||||||
//! An implementation of the Triple Diffie-Hellman key exchange protocol
|
//! An implementation of the Triple Diffie-Hellman key exchange protocol
|
||||||
use core::convert::TryFrom;
|
|
||||||
|
use core::marker::PhantomData;
|
||||||
use core::ops::Add;
|
use core::ops::Add;
|
||||||
|
|
||||||
use derive_where::derive_where;
|
use derive_where::derive_where;
|
||||||
use digest::core_api::BlockSizeUser;
|
use digest::core_api::BlockSizeUser;
|
||||||
use digest::{Digest, Output};
|
use digest::{Digest, Mac, Output, OutputSizeUser};
|
||||||
use generic_array::sequence::Concat;
|
use generic_array::sequence::Concat;
|
||||||
use generic_array::typenum::{IsLess, Le, NonZero, Sum, Unsigned, U1, U2, U256, U32};
|
use generic_array::typenum::{IsLess, Le, NonZero, Sum, U256};
|
||||||
use generic_array::{ArrayLength, GenericArray};
|
use generic_array::{ArrayLength, GenericArray};
|
||||||
use hkdf::{Hkdf, HkdfExtract};
|
use hmac::Hmac;
|
||||||
use hmac::{Hmac, Mac};
|
|
||||||
use rand::{CryptoRng, RngCore};
|
use rand::{CryptoRng, RngCore};
|
||||||
|
use subtle::{ConstantTimeEq, CtOption};
|
||||||
use zeroize::{Zeroize, ZeroizeOnDrop};
|
use zeroize::{Zeroize, ZeroizeOnDrop};
|
||||||
|
|
||||||
use crate::errors::utils::{check_slice_size, check_slice_size_atleast};
|
use crate::ciphersuite::{CipherSuite, KeGroup};
|
||||||
use crate::errors::{InternalError, ProtocolError};
|
use crate::errors::{InternalError, ProtocolError};
|
||||||
use crate::hash::{Hash, OutputSize, ProxyHash};
|
use crate::hash::{Hash, OutputSize, ProxyHash};
|
||||||
use crate::key_exchange::group::KeGroup;
|
use crate::key_exchange::group::Group;
|
||||||
|
use crate::key_exchange::shared::{self, NonceLen};
|
||||||
|
pub use crate::key_exchange::shared::{DiffieHellman, Ke1Message, Ke1State};
|
||||||
use crate::key_exchange::traits::{
|
use crate::key_exchange::traits::{
|
||||||
Deserialize, GenerateKe2Result, GenerateKe3Result, KeyExchange, Serialize,
|
CredentialRequestParts, CredentialResponseParts, Deserialize, GenerateKe2Result,
|
||||||
|
GenerateKe3Result, KeyExchange, Sealed, Serialize, SerializedContext, SerializedIdentifiers,
|
||||||
};
|
};
|
||||||
use crate::keypair::{KeyPair, PrivateKey, PublicKey};
|
use crate::keypair::{KeyPair, PrivateKey, PublicKey};
|
||||||
use crate::serialization::{Input, UpdateExt};
|
use crate::opaque::Identifiers;
|
||||||
|
use crate::serialization::SliceExt;
|
||||||
///////////////
|
|
||||||
// Constants //
|
|
||||||
// ========= //
|
|
||||||
///////////////
|
|
||||||
|
|
||||||
pub(crate) type NonceLen = U32;
|
|
||||||
static STR_CONTEXT: &[u8] = b"OPAQUEv1-";
|
|
||||||
static STR_CLIENT_MAC: &[u8] = b"ClientMAC";
|
|
||||||
static STR_HANDSHAKE_SECRET: &[u8] = b"HandshakeSecret";
|
|
||||||
static STR_SERVER_MAC: &[u8] = b"ServerMAC";
|
|
||||||
static STR_SESSION_KEY: &[u8] = b"SessionKey";
|
|
||||||
static STR_OPAQUE: &[u8] = b"OPAQUE-";
|
|
||||||
|
|
||||||
////////////////////////////
|
////////////////////////////
|
||||||
// High-level API Structs //
|
// High-level API Structs //
|
||||||
@@ -55,36 +47,11 @@ static STR_OPAQUE: &[u8] = b"OPAQUE-";
|
|||||||
///
|
///
|
||||||
/// [`ServerLoginBuilder::data()`](crate::ServerLoginBuilder::data()) will
|
/// [`ServerLoginBuilder::data()`](crate::ServerLoginBuilder::data()) will
|
||||||
/// return the client's ephemeral public key.
|
/// return the client's ephemeral public key.
|
||||||
|
///
|
||||||
/// [`ServerLoginBuilder::build()`](crate::ServerLoginBuilder::build()) expects
|
/// [`ServerLoginBuilder::build()`](crate::ServerLoginBuilder::build()) expects
|
||||||
/// a shared secret computed through Diffie-Hellman from the server's private
|
/// a shared secret computed through Diffie-Hellman from the servers private key
|
||||||
/// key and the given public key.
|
/// and the given public key.
|
||||||
pub struct TripleDh;
|
pub struct TripleDh<G, H>(PhantomData<(G, H)>);
|
||||||
|
|
||||||
/// The client state produced after the first key exchange message
|
|
||||||
#[cfg_attr(
|
|
||||||
feature = "serde",
|
|
||||||
derive(serde::Deserialize, serde::Serialize),
|
|
||||||
serde(bound = "")
|
|
||||||
)]
|
|
||||||
#[derive_where(Clone, ZeroizeOnDrop)]
|
|
||||||
#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; KG::Sk)]
|
|
||||||
pub struct Ke1State<KG: KeGroup> {
|
|
||||||
client_e_sk: PrivateKey<KG>,
|
|
||||||
client_nonce: GenericArray<u8, NonceLen>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The first key exchange message
|
|
||||||
#[cfg_attr(
|
|
||||||
feature = "serde",
|
|
||||||
derive(serde::Deserialize, serde::Serialize),
|
|
||||||
serde(bound = "")
|
|
||||||
)]
|
|
||||||
#[derive_where(Clone, ZeroizeOnDrop)]
|
|
||||||
#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; KG::Pk)]
|
|
||||||
pub struct Ke1Message<KG: KeGroup> {
|
|
||||||
pub(crate) client_nonce: GenericArray<u8, NonceLen>,
|
|
||||||
pub(crate) client_e_pk: PublicKey<KG>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The server state produced after the second key exchange message
|
/// The server state produced after the second key exchange message
|
||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
@@ -93,15 +60,9 @@ pub struct Ke1Message<KG: KeGroup> {
|
|||||||
serde(bound = "")
|
serde(bound = "")
|
||||||
)]
|
)]
|
||||||
#[derive_where(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, ZeroizeOnDrop)]
|
#[derive_where(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, ZeroizeOnDrop)]
|
||||||
pub struct Ke2State<D: Hash>
|
pub struct Ke2State<H: OutputSizeUser> {
|
||||||
where
|
session_key: Output<H>,
|
||||||
D::Core: ProxyHash,
|
expected_mac: Output<H>,
|
||||||
<D::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
|
|
||||||
Le<<D::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
|
|
||||||
{
|
|
||||||
km3: Output<D>,
|
|
||||||
hashed_transcript: Output<D>,
|
|
||||||
session_key: Output<D>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Builder for the second key exchange message
|
/// Builder for the second key exchange message
|
||||||
@@ -109,24 +70,24 @@ where
|
|||||||
feature = "serde",
|
feature = "serde",
|
||||||
derive(serde::Deserialize, serde::Serialize),
|
derive(serde::Deserialize, serde::Serialize),
|
||||||
serde(bound(
|
serde(bound(
|
||||||
deserialize = "D: serde::Deserialize<'de>, PublicKey<KG>: serde::Deserialize<'de>",
|
deserialize = "H: serde::Deserialize<'de>, PublicKey<G>: serde::Deserialize<'de>",
|
||||||
serialize = "D: serde::Serialize, PublicKey<KG>: serde::Serialize",
|
serialize = "H: serde::Serialize, PublicKey<G>: serde::Serialize",
|
||||||
))
|
))
|
||||||
)]
|
)]
|
||||||
#[derive_where(Clone)]
|
#[derive_where(Clone)]
|
||||||
#[derive_where(Debug, Eq, Hash, PartialEq; D, PublicKey<KG>)]
|
#[derive_where(Debug, Eq, Hash, PartialEq; H, PublicKey<G>)]
|
||||||
pub struct Ke2Builder<D: Hash, KG: KeGroup>
|
pub struct Ke2Builder<G: Group, H: Hash>
|
||||||
where
|
where
|
||||||
D::Core: ProxyHash,
|
H::Core: ProxyHash,
|
||||||
<D::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
|
<H::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
|
||||||
Le<<D::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
|
Le<<H::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
|
||||||
{
|
{
|
||||||
server_nonce: GenericArray<u8, NonceLen>,
|
server_nonce: GenericArray<u8, NonceLen>,
|
||||||
transcript_hasher: D,
|
transcript_hasher: H,
|
||||||
client_e_pk: PublicKey<KG>,
|
client_e_pk: PublicKey<G>,
|
||||||
server_e_pk: PublicKey<KG>,
|
server_e_pk: PublicKey<G>,
|
||||||
shared_secret_1: GenericArray<u8, KG::PkLen>,
|
shared_secret_1: GenericArray<u8, G::PkLen>,
|
||||||
shared_secret_3: GenericArray<u8, KG::PkLen>,
|
shared_secret_3: GenericArray<u8, G::PkLen>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The second key exchange message
|
/// The second key exchange message
|
||||||
@@ -136,16 +97,16 @@ where
|
|||||||
serde(bound = "")
|
serde(bound = "")
|
||||||
)]
|
)]
|
||||||
#[derive_where(Clone, ZeroizeOnDrop)]
|
#[derive_where(Clone, ZeroizeOnDrop)]
|
||||||
#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; KG::Pk)]
|
#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; G::Pk)]
|
||||||
pub struct Ke2Message<D: Hash, KG: KeGroup>
|
pub struct Ke2Message<G: Group, H: Hash>
|
||||||
where
|
where
|
||||||
D::Core: ProxyHash,
|
H::Core: ProxyHash,
|
||||||
<D::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
|
<H::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
|
||||||
Le<<D::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
|
Le<<H::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
|
||||||
{
|
{
|
||||||
server_nonce: GenericArray<u8, NonceLen>,
|
server_nonce: GenericArray<u8, NonceLen>,
|
||||||
server_e_pk: PublicKey<KG>,
|
server_e_pk: PublicKey<G>,
|
||||||
mac: Output<D>,
|
mac: Output<H>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The third key exchange message
|
/// The third key exchange message
|
||||||
@@ -155,19 +116,13 @@ where
|
|||||||
serde(bound = "")
|
serde(bound = "")
|
||||||
)]
|
)]
|
||||||
#[derive_where(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, ZeroizeOnDrop)]
|
#[derive_where(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, ZeroizeOnDrop)]
|
||||||
pub struct Ke3Message<D: Hash>
|
pub struct Ke3Message<H: Hash>
|
||||||
where
|
where
|
||||||
D::Core: ProxyHash,
|
H::Core: ProxyHash,
|
||||||
<D::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
|
<H::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
|
||||||
Le<<D::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
|
Le<<H::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
|
||||||
{
|
{
|
||||||
mac: Output<D>,
|
mac: Output<H>,
|
||||||
}
|
|
||||||
|
|
||||||
/// Trait required by [`KeGroup::Sk`] to be compatible with [`TripleDh`].
|
|
||||||
pub trait DiffieHellman<KG: KeGroup> {
|
|
||||||
/// Diffie-Hellman key exchange.
|
|
||||||
fn diffie_hellman(self, pk: KG::Pk) -> GenericArray<u8, KG::PkLen>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////
|
////////////////////////////////
|
||||||
@@ -175,78 +130,55 @@ pub trait DiffieHellman<KG: KeGroup> {
|
|||||||
// ========================== //
|
// ========================== //
|
||||||
////////////////////////////////
|
////////////////////////////////
|
||||||
|
|
||||||
impl<D: Hash, KG: KeGroup + 'static> KeyExchange<D, KG> for TripleDh
|
impl<G: Group + 'static, H: Hash> KeyExchange for TripleDh<G, H>
|
||||||
where
|
where
|
||||||
KG::Sk: DiffieHellman<KG>,
|
G::Sk: DiffieHellman<G>,
|
||||||
D::Core: ProxyHash,
|
H::Core: ProxyHash,
|
||||||
<D::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
|
<H::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
|
||||||
Le<<D::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
|
Le<<H::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
|
||||||
// Ke1State: KeSk + Nonce
|
|
||||||
KG::SkLen: Add<NonceLen>,
|
|
||||||
Sum<KG::SkLen, NonceLen>: ArrayLength<u8>,
|
|
||||||
// Ke1Message: Nonce + KePk
|
|
||||||
NonceLen: Add<KG::PkLen>,
|
|
||||||
Sum<NonceLen, KG::PkLen>: ArrayLength<u8>,
|
|
||||||
// Ke2State: (Hash + Hash) + Hash
|
|
||||||
OutputSize<D>: Add<OutputSize<D>>,
|
|
||||||
Sum<OutputSize<D>, OutputSize<D>>: ArrayLength<u8> + Add<OutputSize<D>>,
|
|
||||||
Sum<Sum<OutputSize<D>, OutputSize<D>>, OutputSize<D>>: ArrayLength<u8>,
|
|
||||||
// Ke2Message: (Nonce + KePk) + Hash
|
|
||||||
NonceLen: Add<KG::PkLen>,
|
|
||||||
Sum<NonceLen, KG::PkLen>: ArrayLength<u8> + Add<OutputSize<D>>,
|
|
||||||
Sum<Sum<NonceLen, KG::PkLen>, OutputSize<D>>: ArrayLength<u8>,
|
|
||||||
{
|
{
|
||||||
type KE1State = Ke1State<KG>;
|
type Group = G;
|
||||||
type KE2State = Ke2State<D>;
|
type Hash = H;
|
||||||
type KE1Message = Ke1Message<KG>;
|
|
||||||
type KE2Builder = Ke2Builder<D, KG>;
|
|
||||||
type KE2BuilderData<'a> = &'a PublicKey<KG>;
|
|
||||||
type KE2BuilderInput = GenericArray<u8, KG::PkLen>;
|
|
||||||
type KE2Message = Ke2Message<D, KG>;
|
|
||||||
type KE3Message = Ke3Message<D>;
|
|
||||||
|
|
||||||
fn generate_ke1<OprfCs: voprf::CipherSuite, R: RngCore + CryptoRng>(
|
type KE1State = Ke1State<G>;
|
||||||
|
type KE2State<CS: CipherSuite> = Ke2State<H>;
|
||||||
|
type KE1Message = Ke1Message<G>;
|
||||||
|
type KE2Builder<'a, CS: CipherSuite<KeyExchange = Self>> = Ke2Builder<G, H>;
|
||||||
|
type KE2BuilderData<'a, CS: 'static + CipherSuite> = &'a PublicKey<G>;
|
||||||
|
type KE2BuilderInput<CS: CipherSuite> = GenericArray<u8, G::PkLen>;
|
||||||
|
type KE2Message = Ke2Message<G, H>;
|
||||||
|
type KE3Message = Ke3Message<H>;
|
||||||
|
|
||||||
|
fn generate_ke1<R: RngCore + CryptoRng>(
|
||||||
rng: &mut R,
|
rng: &mut R,
|
||||||
) -> Result<(Self::KE1State, Self::KE1Message), ProtocolError> {
|
) -> Result<(Self::KE1State, Self::KE1Message), ProtocolError> {
|
||||||
let client_e_kp = KeyPair::<KG>::generate_random::<OprfCs, _>(rng);
|
shared::generate_ke1(rng)
|
||||||
let client_nonce = generate_nonce::<R>(rng);
|
|
||||||
|
|
||||||
let ke1_message = Ke1Message {
|
|
||||||
client_nonce,
|
|
||||||
client_e_pk: client_e_kp.public().clone(),
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok((
|
|
||||||
Ke1State {
|
|
||||||
client_e_sk: client_e_kp.private().clone(),
|
|
||||||
client_nonce,
|
|
||||||
},
|
|
||||||
ke1_message,
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ke2_builder<'a, 'b, 'c, 'd, OprfCs: voprf::CipherSuite, R: RngCore + CryptoRng>(
|
fn ke2_builder<'a, CS: CipherSuite<KeyExchange = Self>, R: RngCore + CryptoRng>(
|
||||||
rng: &mut R,
|
rng: &mut R,
|
||||||
serialized_credential_request: impl Iterator<Item = &'a [u8]>,
|
credential_request: CredentialRequestParts<CS>,
|
||||||
serialized_credential_response: impl Iterator<Item = &'b [u8]>,
|
|
||||||
ke1_message: Self::KE1Message,
|
ke1_message: Self::KE1Message,
|
||||||
client_s_pk: PublicKey<KG>,
|
credential_response: CredentialResponseParts<CS>,
|
||||||
id_u: impl Iterator<Item = &'c [u8]>,
|
client_s_pk: PublicKey<G>,
|
||||||
id_s: impl Iterator<Item = &'d [u8]>,
|
identifiers: SerializedIdentifiers<'_, KeGroup<CS>>,
|
||||||
context: &[u8],
|
context: SerializedContext<'a>,
|
||||||
) -> Result<Self::KE2Builder, ProtocolError> {
|
) -> Result<Self::KE2Builder<'a, CS>, ProtocolError> {
|
||||||
let server_e = KeyPair::<KG>::generate_random::<OprfCs, _>(rng);
|
let server_e = KeyPair::<G>::derive_random(rng);
|
||||||
let server_nonce = generate_nonce::<R>(rng);
|
let server_nonce = shared::generate_nonce::<R>(rng);
|
||||||
|
|
||||||
let transcript_hasher = D::new()
|
let ke1_message_iter = ke1_message.to_iter();
|
||||||
.chain(STR_CONTEXT)
|
let server_e_pk = server_e.public().serialize();
|
||||||
.chain_iter(Input::<U2>::from(context)?.iter())
|
|
||||||
.chain_iter(id_u.into_iter())
|
let transcript_hasher = shared::transcript(
|
||||||
.chain_iter(serialized_credential_request)
|
&context,
|
||||||
.chain_iter(id_s.into_iter())
|
&identifiers,
|
||||||
.chain_iter(serialized_credential_response)
|
&credential_request,
|
||||||
.chain(server_nonce)
|
&ke1_message_iter,
|
||||||
.chain(server_e.public().serialize());
|
&credential_response,
|
||||||
|
server_nonce,
|
||||||
|
&server_e_pk,
|
||||||
|
);
|
||||||
|
|
||||||
let shared_secret_1 = server_e
|
let shared_secret_1 = server_e
|
||||||
.private()
|
.private()
|
||||||
@@ -263,40 +195,55 @@ where
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ke2_builder_data(builder: &Self::KE2Builder) -> Self::KE2BuilderData<'_> {
|
fn ke2_builder_data<'a, CS: 'static + CipherSuite<KeyExchange = Self>>(
|
||||||
|
builder: &'a Self::KE2Builder<'_, CS>,
|
||||||
|
) -> Self::KE2BuilderData<'a, CS> {
|
||||||
&builder.client_e_pk
|
&builder.client_e_pk
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_ke2_input(
|
fn generate_ke2_input<CS: CipherSuite<KeyExchange = Self>, R: CryptoRng + RngCore>(
|
||||||
builder: &Self::KE2Builder,
|
builder: &Self::KE2Builder<'_, CS>,
|
||||||
server_s_sk: &PrivateKey<KG>,
|
_: &mut R,
|
||||||
) -> Self::KE2BuilderInput {
|
server_s_sk: &PrivateKey<G>,
|
||||||
|
) -> Self::KE2BuilderInput<CS> {
|
||||||
server_s_sk.ke_diffie_hellman(&builder.client_e_pk)
|
server_s_sk.ke_diffie_hellman(&builder.client_e_pk)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_ke2(
|
fn build_ke2<CS: CipherSuite<KeyExchange = Self>>(
|
||||||
mut builder: Self::KE2Builder,
|
mut builder: Self::KE2Builder<'_, CS>,
|
||||||
shared_secret_2: Self::KE2BuilderInput,
|
shared_secret_2: Self::KE2BuilderInput<CS>,
|
||||||
) -> Result<GenerateKe2Result<Self, D, KG>, ProtocolError> {
|
) -> Result<GenerateKe2Result<CS>, ProtocolError> {
|
||||||
let result = derive_3dh_keys::<D, KG>(
|
let derived_keys = shared::derive_keys::<H>(
|
||||||
builder.shared_secret_1.clone(),
|
[
|
||||||
shared_secret_2,
|
builder.shared_secret_1.as_slice(),
|
||||||
builder.shared_secret_3.clone(),
|
&shared_secret_2,
|
||||||
|
&builder.shared_secret_3,
|
||||||
|
]
|
||||||
|
.into_iter(),
|
||||||
&builder.transcript_hasher.clone().finalize(),
|
&builder.transcript_hasher.clone().finalize(),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let mut mac_hasher =
|
let mut mac_hasher =
|
||||||
Hmac::<D>::new_from_slice(&result.1).map_err(|_| InternalError::HmacError)?;
|
Hmac::<H>::new_from_slice(&derived_keys.km2).map_err(|_| InternalError::HmacError)?;
|
||||||
mac_hasher.update(&builder.transcript_hasher.clone().finalize());
|
Mac::update(
|
||||||
|
&mut mac_hasher,
|
||||||
|
&builder.transcript_hasher.clone().finalize(),
|
||||||
|
);
|
||||||
let mac = mac_hasher.finalize().into_bytes();
|
let mac = mac_hasher.finalize().into_bytes();
|
||||||
|
|
||||||
Digest::update(&mut builder.transcript_hasher, &mac);
|
builder.transcript_hasher.update(&mac);
|
||||||
|
let mut mac_hasher =
|
||||||
|
Hmac::<H>::new_from_slice(&derived_keys.km3).map_err(|_| InternalError::HmacError)?;
|
||||||
|
Mac::update(
|
||||||
|
&mut mac_hasher,
|
||||||
|
&builder.transcript_hasher.clone().finalize(),
|
||||||
|
);
|
||||||
|
let expected_mac = mac_hasher.finalize().into_bytes();
|
||||||
|
|
||||||
Ok((
|
Ok((
|
||||||
Ke2State {
|
Ke2State {
|
||||||
km3: result.2,
|
session_key: derived_keys.session_key,
|
||||||
hashed_transcript: builder.transcript_hasher.clone().finalize(),
|
expected_mac,
|
||||||
session_key: result.0,
|
|
||||||
},
|
},
|
||||||
Ke2Message {
|
Ke2Message {
|
||||||
server_nonce: builder.server_nonce,
|
server_nonce: builder.server_nonce,
|
||||||
@@ -304,383 +251,195 @@ where
|
|||||||
mac,
|
mac,
|
||||||
},
|
},
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
result.3,
|
derived_keys.handshake_secret,
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
result.1,
|
derived_keys.km2,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::type_complexity)]
|
fn generate_ke3<CS: CipherSuite<KeyExchange = Self>, R: CryptoRng + RngCore>(
|
||||||
fn generate_ke3<'a, 'b, 'c, 'd>(
|
_: &mut R,
|
||||||
l2_component: impl Iterator<Item = &'a [u8]>,
|
credential_request: CredentialRequestParts<CS>,
|
||||||
|
ke1_message: Self::KE1Message,
|
||||||
|
credential_response: CredentialResponseParts<CS>,
|
||||||
ke2_message: Self::KE2Message,
|
ke2_message: Self::KE2Message,
|
||||||
ke1_state: &Self::KE1State,
|
ke1_state: &Self::KE1State,
|
||||||
serialized_credential_request: impl Iterator<Item = &'b [u8]>,
|
server_s_pk: PublicKey<G>,
|
||||||
server_s_pk: PublicKey<KG>,
|
client_s_sk: PrivateKey<G>,
|
||||||
client_s_sk: PrivateKey<KG>,
|
identifiers: SerializedIdentifiers<'_, KeGroup<CS>>,
|
||||||
id_u: impl Iterator<Item = &'c [u8]>,
|
context: SerializedContext<'_>,
|
||||||
id_s: impl Iterator<Item = &'d [u8]>,
|
) -> Result<GenerateKe3Result<Self>, ProtocolError> {
|
||||||
context: &[u8],
|
let mut transcript_hasher = shared::transcript(
|
||||||
) -> Result<GenerateKe3Result<Self, D, KG>, ProtocolError> {
|
&context,
|
||||||
let mut transcript_hasher = D::new()
|
&identifiers,
|
||||||
.chain(STR_CONTEXT)
|
&credential_request,
|
||||||
.chain_iter(Input::<U2>::from(context)?.iter())
|
&ke1_message.to_iter(),
|
||||||
.chain_iter(id_u)
|
&credential_response,
|
||||||
.chain_iter(serialized_credential_request)
|
ke2_message.server_nonce,
|
||||||
.chain_iter(id_s)
|
&ke2_message.server_e_pk.serialize(),
|
||||||
.chain_iter(l2_component)
|
);
|
||||||
.chain(ke2_message.to_bytes_without_mac());
|
|
||||||
|
|
||||||
let result = derive_3dh_keys::<D, KG>(
|
let shared_secret_1 = ke1_state
|
||||||
ke1_state
|
.client_e_sk
|
||||||
.client_e_sk
|
.ke_diffie_hellman(&ke2_message.server_e_pk);
|
||||||
.ke_diffie_hellman(&ke2_message.server_e_pk),
|
let shared_secret_2 = ke1_state.client_e_sk.ke_diffie_hellman(&server_s_pk);
|
||||||
ke1_state.client_e_sk.ke_diffie_hellman(&server_s_pk),
|
let shared_secret_3 = client_s_sk.ke_diffie_hellman(&ke2_message.server_e_pk);
|
||||||
client_s_sk.ke_diffie_hellman(&ke2_message.server_e_pk),
|
|
||||||
|
let derived_keys = shared::derive_keys::<H>(
|
||||||
|
[
|
||||||
|
shared_secret_1.as_slice(),
|
||||||
|
&shared_secret_2,
|
||||||
|
&shared_secret_3,
|
||||||
|
]
|
||||||
|
.into_iter(),
|
||||||
&transcript_hasher.clone().finalize(),
|
&transcript_hasher.clone().finalize(),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let mut server_mac =
|
let mut server_mac =
|
||||||
Hmac::<D>::new_from_slice(&result.1).map_err(|_| InternalError::HmacError)?;
|
Hmac::<H>::new_from_slice(&derived_keys.km2).map_err(|_| InternalError::HmacError)?;
|
||||||
server_mac.update(&transcript_hasher.clone().finalize());
|
Mac::update(&mut server_mac, &transcript_hasher.clone().finalize());
|
||||||
|
|
||||||
server_mac
|
server_mac
|
||||||
.verify(&ke2_message.mac)
|
.verify(&ke2_message.mac)
|
||||||
.map_err(|_| ProtocolError::InvalidLoginError)?;
|
.map_err(|_| ProtocolError::InvalidLoginError)?;
|
||||||
|
|
||||||
Digest::update(&mut transcript_hasher, &ke2_message.mac);
|
transcript_hasher.update(&ke2_message.mac);
|
||||||
|
|
||||||
let mut client_mac =
|
let mut client_mac =
|
||||||
Hmac::<D>::new_from_slice(&result.2).map_err(|_| InternalError::HmacError)?;
|
Hmac::<H>::new_from_slice(&derived_keys.km3).map_err(|_| InternalError::HmacError)?;
|
||||||
client_mac.update(&transcript_hasher.finalize());
|
Mac::update(&mut client_mac, &transcript_hasher.finalize());
|
||||||
|
|
||||||
Ok((
|
Ok((
|
||||||
result.0,
|
derived_keys.session_key,
|
||||||
Ke3Message {
|
Ke3Message {
|
||||||
mac: client_mac.finalize().into_bytes(),
|
mac: client_mac.finalize().into_bytes(),
|
||||||
},
|
},
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
result.3,
|
derived_keys.handshake_secret,
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
result.2,
|
derived_keys.km3,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish_ke(
|
fn finish_ke<CS: CipherSuite>(
|
||||||
ke3_message: Self::KE3Message,
|
ke3_message: Self::KE3Message,
|
||||||
ke2_state: &Self::KE2State,
|
ke2_state: &Self::KE2State<CS>,
|
||||||
) -> Result<Output<D>, ProtocolError> {
|
_: Identifiers<'_>,
|
||||||
let mut client_mac =
|
_: SerializedContext<'_>,
|
||||||
Hmac::<D>::new_from_slice(&ke2_state.km3).map_err(|_| InternalError::HmacError)?;
|
) -> Result<Output<H>, ProtocolError> {
|
||||||
client_mac.update(&ke2_state.hashed_transcript);
|
CtOption::new(
|
||||||
|
ke2_state.session_key.clone(),
|
||||||
client_mac
|
ke2_state.expected_mac.ct_eq(&ke3_message.mac),
|
||||||
.verify(&ke3_message.mac)
|
)
|
||||||
.map_err(|_| ProtocolError::InvalidLoginError)?;
|
.into_option()
|
||||||
|
.ok_or(ProtocolError::InvalidLoginError)
|
||||||
Ok(ke2_state.session_key.clone())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/////////////////////////
|
impl<G: Group + 'static, H: Hash> Sealed for TripleDh<G, H>
|
||||||
// Convenience Structs //
|
where
|
||||||
//==================== //
|
H::Core: ProxyHash,
|
||||||
/////////////////////////
|
<H::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
|
||||||
|
Le<<H::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
|
||||||
// Consists of a session key, followed by two mac keys: (session_key, km2, km3)
|
{
|
||||||
#[cfg(not(test))]
|
}
|
||||||
type TripleDhDerivationResult<D> = (Output<D>, Output<D>, Output<D>);
|
|
||||||
#[cfg(test)]
|
|
||||||
type TripleDhDerivationResult<D> = (Output<D>, Output<D>, Output<D>, Output<D>);
|
|
||||||
|
|
||||||
////////////////////////////////////////////////
|
////////////////////////////////////////////////
|
||||||
// Helper functions and Trait Implementations //
|
// Trait Implementations //
|
||||||
// ========================================== //
|
// ========================================== //
|
||||||
////////////////////////////////////////////////
|
////////////////////////////////////////////////
|
||||||
|
|
||||||
// Helper functions
|
impl<H: Hash> Deserialize for Ke2State<H>
|
||||||
|
|
||||||
// Internal function which takes the public and private components of the client
|
|
||||||
// and server keypairs, along with some auxiliary metadata, to produce the
|
|
||||||
// session key and two MAC keys
|
|
||||||
fn derive_3dh_keys<D: Hash, KG: KeGroup>(
|
|
||||||
shared_secret_1: GenericArray<u8, KG::PkLen>,
|
|
||||||
shared_secret_2: GenericArray<u8, KG::PkLen>,
|
|
||||||
shared_secret_3: GenericArray<u8, KG::PkLen>,
|
|
||||||
hashed_derivation_transcript: &[u8],
|
|
||||||
) -> Result<TripleDhDerivationResult<D>, ProtocolError>
|
|
||||||
where
|
where
|
||||||
D::Core: ProxyHash,
|
H::Core: ProxyHash,
|
||||||
<D::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
|
<H::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
|
||||||
Le<<D::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
|
Le<<H::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
|
||||||
{
|
{
|
||||||
let mut hkdf = HkdfExtract::<D>::new(None);
|
fn deserialize_take(input: &mut &[u8]) -> Result<Self, ProtocolError> {
|
||||||
|
|
||||||
hkdf.input_ikm(&shared_secret_1);
|
|
||||||
hkdf.input_ikm(&shared_secret_2);
|
|
||||||
hkdf.input_ikm(&shared_secret_3);
|
|
||||||
|
|
||||||
let (_, extracted_ikm) = hkdf.finalize();
|
|
||||||
let handshake_secret = derive_secrets::<D>(
|
|
||||||
&extracted_ikm,
|
|
||||||
STR_HANDSHAKE_SECRET,
|
|
||||||
hashed_derivation_transcript,
|
|
||||||
)?;
|
|
||||||
let session_key = derive_secrets::<D>(
|
|
||||||
&extracted_ikm,
|
|
||||||
STR_SESSION_KEY,
|
|
||||||
hashed_derivation_transcript,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let km2 = hkdf_expand_label::<D>(&handshake_secret, STR_SERVER_MAC, b"")?;
|
|
||||||
let km3 = hkdf_expand_label::<D>(&handshake_secret, STR_CLIENT_MAC, b"")?;
|
|
||||||
|
|
||||||
Ok((
|
|
||||||
session_key,
|
|
||||||
km2,
|
|
||||||
km3,
|
|
||||||
#[cfg(test)]
|
|
||||||
handshake_secret,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn hkdf_expand_label<D: Hash>(
|
|
||||||
secret: &[u8],
|
|
||||||
label: &[u8],
|
|
||||||
context: &[u8],
|
|
||||||
) -> Result<Output<D>, ProtocolError>
|
|
||||||
where
|
|
||||||
D::Core: ProxyHash,
|
|
||||||
<D::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
|
|
||||||
Le<<D::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
|
|
||||||
{
|
|
||||||
let h = Hkdf::<D>::from_prk(secret).map_err(|_| InternalError::HkdfError)?;
|
|
||||||
hkdf_expand_label_extracted(&h, label, context)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn hkdf_expand_label_extracted<D: Hash>(
|
|
||||||
hkdf: &Hkdf<D>,
|
|
||||||
label: &[u8],
|
|
||||||
context: &[u8],
|
|
||||||
) -> Result<Output<D>, ProtocolError>
|
|
||||||
where
|
|
||||||
D::Core: ProxyHash,
|
|
||||||
<D::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
|
|
||||||
Le<<D::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
|
|
||||||
{
|
|
||||||
let mut okm = GenericArray::default();
|
|
||||||
|
|
||||||
let length_u16: u16 =
|
|
||||||
u16::try_from(OutputSize::<D>::USIZE).map_err(|_| ProtocolError::SerializationError)?;
|
|
||||||
let label = Input::<U1>::from_label(STR_OPAQUE, label)?;
|
|
||||||
let label = label.to_array_3();
|
|
||||||
let context = Input::<U1>::from(context)?;
|
|
||||||
let context = context.to_array_2();
|
|
||||||
|
|
||||||
let hkdf_label = [
|
|
||||||
&length_u16.to_be_bytes(),
|
|
||||||
label[0],
|
|
||||||
label[1],
|
|
||||||
label[2],
|
|
||||||
context[0],
|
|
||||||
context[1],
|
|
||||||
];
|
|
||||||
|
|
||||||
hkdf.expand_multi_info(&hkdf_label, &mut okm)
|
|
||||||
.map_err(|_| InternalError::HkdfError)?;
|
|
||||||
Ok(okm)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn derive_secrets<D: Hash>(
|
|
||||||
hkdf: &Hkdf<D>,
|
|
||||||
label: &[u8],
|
|
||||||
hashed_derivation_transcript: &[u8],
|
|
||||||
) -> Result<Output<D>, ProtocolError>
|
|
||||||
where
|
|
||||||
D::Core: ProxyHash,
|
|
||||||
<D::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
|
|
||||||
Le<<D::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
|
|
||||||
{
|
|
||||||
hkdf_expand_label_extracted::<D>(hkdf, label, hashed_derivation_transcript)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate a random nonce up to NonceLen::USIZE bytes.
|
|
||||||
fn generate_nonce<R: RngCore + CryptoRng>(rng: &mut R) -> GenericArray<u8, NonceLen> {
|
|
||||||
let mut nonce_bytes = GenericArray::default();
|
|
||||||
rng.fill_bytes(&mut nonce_bytes);
|
|
||||||
nonce_bytes
|
|
||||||
}
|
|
||||||
|
|
||||||
// Serialization and deserialization implementations
|
|
||||||
|
|
||||||
impl<KG: KeGroup> Deserialize for Ke1State<KG> {
|
|
||||||
fn deserialize(bytes: &[u8]) -> Result<Self, ProtocolError> {
|
|
||||||
let key_len = KG::SkLen::USIZE;
|
|
||||||
|
|
||||||
let nonce_len = NonceLen::USIZE;
|
|
||||||
let checked_bytes = check_slice_size_atleast(bytes, key_len + nonce_len, "ke1_state")?;
|
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
client_e_sk: PrivateKey::deserialize(&checked_bytes[..key_len])?,
|
session_key: input.take_array("session key")?,
|
||||||
client_nonce: GenericArray::clone_from_slice(
|
expected_mac: input.take_array("expected mac")?,
|
||||||
&checked_bytes[key_len..key_len + nonce_len],
|
|
||||||
),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<KG: KeGroup> Serialize for Ke1State<KG>
|
impl<H: Hash> Serialize for Ke2State<H>
|
||||||
where
|
where
|
||||||
// Ke1State: KeSk + Nonce
|
H::Core: ProxyHash,
|
||||||
KG::SkLen: Add<NonceLen>,
|
<H::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
|
||||||
Sum<KG::SkLen, NonceLen>: ArrayLength<u8>,
|
Le<<H::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
|
||||||
|
// Ke2State: Hash + Hash
|
||||||
|
OutputSize<H>: Add<OutputSize<H>>,
|
||||||
|
Sum<OutputSize<H>, OutputSize<H>>: ArrayLength<u8>,
|
||||||
{
|
{
|
||||||
type Len = Sum<KG::SkLen, NonceLen>;
|
type Len = Sum<OutputSize<H>, OutputSize<H>>;
|
||||||
|
|
||||||
fn serialize(&self) -> GenericArray<u8, Self::Len> {
|
fn serialize(&self) -> GenericArray<u8, Self::Len> {
|
||||||
self.client_e_sk.serialize().concat(self.client_nonce)
|
self.session_key.clone().concat(self.expected_mac.clone())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<KG: KeGroup> Deserialize for Ke1Message<KG> {
|
impl<G: Group, H: Hash> Drop for Ke2Builder<G, H>
|
||||||
fn deserialize(ke1_message_bytes: &[u8]) -> Result<Self, ProtocolError> {
|
|
||||||
let nonce_len = NonceLen::USIZE;
|
|
||||||
let checked_nonce = check_slice_size(
|
|
||||||
ke1_message_bytes,
|
|
||||||
nonce_len + <KG as KeGroup>::PkLen::USIZE,
|
|
||||||
"ke1_message nonce",
|
|
||||||
)?;
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
client_nonce: GenericArray::clone_from_slice(&checked_nonce[..nonce_len]),
|
|
||||||
client_e_pk: PublicKey::deserialize(&checked_nonce[nonce_len..])?,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<KG: KeGroup> Serialize for Ke1Message<KG>
|
|
||||||
where
|
where
|
||||||
// Ke1Message: Nonce + KePk
|
H::Core: ProxyHash,
|
||||||
NonceLen: Add<KG::PkLen>,
|
<H::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
|
||||||
Sum<NonceLen, KG::PkLen>: ArrayLength<u8>,
|
Le<<H::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
|
||||||
{
|
|
||||||
type Len = Sum<NonceLen, KG::PkLen>;
|
|
||||||
|
|
||||||
fn serialize(&self) -> GenericArray<u8, Self::Len> {
|
|
||||||
self.client_nonce.concat(self.client_e_pk.serialize())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<D: Hash> Deserialize for Ke2State<D>
|
|
||||||
where
|
|
||||||
D::Core: ProxyHash,
|
|
||||||
<D::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
|
|
||||||
Le<<D::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
|
|
||||||
{
|
|
||||||
fn deserialize(input: &[u8]) -> Result<Self, ProtocolError> {
|
|
||||||
let hash_len = OutputSize::<D>::USIZE;
|
|
||||||
let checked_bytes = check_slice_size(input, 3 * hash_len, "ke2_state")?;
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
km3: GenericArray::clone_from_slice(&checked_bytes[..hash_len]),
|
|
||||||
hashed_transcript: GenericArray::clone_from_slice(
|
|
||||||
&checked_bytes[hash_len..2 * hash_len],
|
|
||||||
),
|
|
||||||
session_key: GenericArray::clone_from_slice(&checked_bytes[2 * hash_len..3 * hash_len]),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<D: Hash> Serialize for Ke2State<D>
|
|
||||||
where
|
|
||||||
D::Core: ProxyHash,
|
|
||||||
<D::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
|
|
||||||
Le<<D::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
|
|
||||||
// Ke2State: (Hash + Hash) + Hash
|
|
||||||
OutputSize<D>: Add<OutputSize<D>>,
|
|
||||||
Sum<OutputSize<D>, OutputSize<D>>: ArrayLength<u8> + Add<OutputSize<D>>,
|
|
||||||
Sum<Sum<OutputSize<D>, OutputSize<D>>, OutputSize<D>>: ArrayLength<u8>,
|
|
||||||
{
|
|
||||||
type Len = Sum<Sum<OutputSize<D>, OutputSize<D>>, OutputSize<D>>;
|
|
||||||
|
|
||||||
fn serialize(&self) -> GenericArray<u8, Self::Len> {
|
|
||||||
self.km3
|
|
||||||
.clone()
|
|
||||||
.concat(self.hashed_transcript.clone())
|
|
||||||
.concat(self.session_key.clone())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<KG: KeGroup, D: Hash> Drop for Ke2Builder<D, KG>
|
|
||||||
where
|
|
||||||
D::Core: ProxyHash,
|
|
||||||
<D::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
|
|
||||||
Le<<D::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
|
|
||||||
{
|
{
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
struct AssertZeroizeOnDrop<'a, T: ZeroizeOnDrop>(#[allow(unused)] &'a T);
|
struct AssertZeroizeOnDrop<'a, T: ZeroizeOnDrop>(#[allow(unused)] &'a T);
|
||||||
|
|
||||||
self.server_nonce.zeroize();
|
let Self {
|
||||||
self.transcript_hasher.reset();
|
server_nonce,
|
||||||
let _ = AssertZeroizeOnDrop(&self.client_e_pk);
|
transcript_hasher,
|
||||||
let _ = AssertZeroizeOnDrop(&self.server_e_pk);
|
client_e_pk,
|
||||||
self.shared_secret_1.zeroize();
|
server_e_pk,
|
||||||
self.shared_secret_3.zeroize();
|
shared_secret_1,
|
||||||
|
shared_secret_3,
|
||||||
|
} = self;
|
||||||
|
|
||||||
|
server_nonce.zeroize();
|
||||||
|
transcript_hasher.reset();
|
||||||
|
let _ = AssertZeroizeOnDrop(client_e_pk);
|
||||||
|
let _ = AssertZeroizeOnDrop(server_e_pk);
|
||||||
|
shared_secret_1.zeroize();
|
||||||
|
shared_secret_3.zeroize();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<KG: KeGroup, D: Hash> ZeroizeOnDrop for Ke2Builder<D, KG>
|
impl<G: Group, H: Hash> ZeroizeOnDrop for Ke2Builder<G, H>
|
||||||
where
|
where
|
||||||
D::Core: ProxyHash,
|
H::Core: ProxyHash,
|
||||||
<D::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
|
<H::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
|
||||||
Le<<D::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
|
Le<<H::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<KG: KeGroup, D: Hash> Deserialize for Ke2Message<D, KG>
|
impl<G: Group, H: Hash> Deserialize for Ke2Message<G, H>
|
||||||
where
|
where
|
||||||
D::Core: ProxyHash,
|
H::Core: ProxyHash,
|
||||||
<D::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
|
<H::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
|
||||||
Le<<D::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
|
Le<<H::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
|
||||||
{
|
{
|
||||||
fn deserialize(input: &[u8]) -> Result<Self, ProtocolError> {
|
fn deserialize_take(input: &mut &[u8]) -> Result<Self, ProtocolError> {
|
||||||
let key_len = <KG as KeGroup>::PkLen::USIZE;
|
|
||||||
let nonce_len = NonceLen::USIZE;
|
|
||||||
let checked_nonce = check_slice_size_atleast(input, nonce_len, "ke2_message nonce")?;
|
|
||||||
|
|
||||||
let unchecked_server_e_pk = check_slice_size_atleast(
|
|
||||||
&checked_nonce[nonce_len..],
|
|
||||||
key_len,
|
|
||||||
"ke2_message server_e_pk",
|
|
||||||
)?;
|
|
||||||
let checked_mac = check_slice_size(
|
|
||||||
&unchecked_server_e_pk[key_len..],
|
|
||||||
OutputSize::<D>::USIZE,
|
|
||||||
"ke1_message mac",
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// Check the public key bytes
|
|
||||||
let server_e_pk = PublicKey::deserialize(&unchecked_server_e_pk[..key_len])?;
|
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
server_nonce: GenericArray::clone_from_slice(&checked_nonce[..nonce_len]),
|
server_nonce: input.take_array("server nonce")?,
|
||||||
server_e_pk,
|
server_e_pk: PublicKey::deserialize_take(input)?,
|
||||||
mac: GenericArray::clone_from_slice(checked_mac),
|
mac: input.take_array("mac")?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D: Hash, KG: KeGroup> Serialize for Ke2Message<D, KG>
|
impl<H: Hash, G: Group> Serialize for Ke2Message<G, H>
|
||||||
where
|
where
|
||||||
D::Core: ProxyHash,
|
H::Core: ProxyHash,
|
||||||
<D::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
|
<H::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
|
||||||
Le<<D::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
|
Le<<H::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
|
||||||
// Ke2Message: (Nonce + KePk) + Hash
|
// Ke2Message: (Nonce + KePk) + Hash
|
||||||
NonceLen: Add<KG::PkLen>,
|
NonceLen: Add<G::PkLen>,
|
||||||
Sum<NonceLen, KG::PkLen>: ArrayLength<u8> + Add<OutputSize<D>>,
|
Sum<NonceLen, G::PkLen>: ArrayLength<u8> + Add<OutputSize<H>>,
|
||||||
Sum<Sum<NonceLen, KG::PkLen>, OutputSize<D>>: ArrayLength<u8>,
|
Sum<Sum<NonceLen, G::PkLen>, OutputSize<H>>: ArrayLength<u8>,
|
||||||
{
|
{
|
||||||
type Len = Sum<Sum<NonceLen, KG::PkLen>, OutputSize<D>>;
|
type Len = Sum<Sum<NonceLen, G::PkLen>, OutputSize<H>>;
|
||||||
|
|
||||||
fn serialize(&self) -> GenericArray<u8, Self::Len> {
|
fn serialize(&self) -> GenericArray<u8, Self::Len> {
|
||||||
self.server_nonce
|
self.server_nonce
|
||||||
@@ -689,43 +448,50 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D: Hash, KG: KeGroup> Ke2Message<D, KG>
|
impl<H: Hash> Deserialize for Ke3Message<H>
|
||||||
where
|
where
|
||||||
D::Core: ProxyHash,
|
H::Core: ProxyHash,
|
||||||
<D::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
|
<H::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
|
||||||
Le<<D::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
|
Le<<H::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
|
||||||
NonceLen: Add<KG::PkLen>,
|
|
||||||
Sum<NonceLen, KG::PkLen>: ArrayLength<u8>,
|
|
||||||
{
|
{
|
||||||
fn to_bytes_without_mac(&self) -> GenericArray<u8, Sum<NonceLen, KG::PkLen>> {
|
fn deserialize_take(bytes: &mut &[u8]) -> Result<Self, ProtocolError> {
|
||||||
self.server_nonce.concat(self.server_e_pk.serialize())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<D: Hash> Deserialize for Ke3Message<D>
|
|
||||||
where
|
|
||||||
D::Core: ProxyHash,
|
|
||||||
<D::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
|
|
||||||
Le<<D::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
|
|
||||||
{
|
|
||||||
fn deserialize(bytes: &[u8]) -> Result<Self, ProtocolError> {
|
|
||||||
let checked_bytes = check_slice_size(bytes, OutputSize::<D>::USIZE, "ke3_message")?;
|
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
mac: GenericArray::clone_from_slice(checked_bytes),
|
mac: bytes.take_array("mac")?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D: Hash> Serialize for Ke3Message<D>
|
impl<H: Hash> Serialize for Ke3Message<H>
|
||||||
where
|
where
|
||||||
D::Core: ProxyHash,
|
H::Core: ProxyHash,
|
||||||
<D::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
|
<H::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
|
||||||
Le<<D::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
|
Le<<H::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
|
||||||
{
|
{
|
||||||
type Len = OutputSize<D>;
|
type Len = OutputSize<H>;
|
||||||
|
|
||||||
fn serialize(&self) -> GenericArray<u8, Self::Len> {
|
fn serialize(&self) -> GenericArray<u8, Self::Len> {
|
||||||
self.mac.clone()
|
self.mac.clone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//////////////////////////
|
||||||
|
// Test Implementations //
|
||||||
|
//===================== //
|
||||||
|
//////////////////////////
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
use crate::serialization::AssertZeroized;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
impl<H: OutputSizeUser> AssertZeroized for Ke2State<H> {
|
||||||
|
fn assert_zeroized(&self) {
|
||||||
|
let Self {
|
||||||
|
session_key,
|
||||||
|
expected_mac,
|
||||||
|
} = self;
|
||||||
|
|
||||||
|
for byte in session_key.iter().chain(expected_mac) {
|
||||||
|
assert_eq!(byte, &0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+400
-190
@@ -11,12 +11,16 @@
|
|||||||
#![allow(unsafe_code)]
|
#![allow(unsafe_code)]
|
||||||
|
|
||||||
use derive_where::derive_where;
|
use derive_where::derive_where;
|
||||||
|
use digest::{Output, OutputSizeUser};
|
||||||
use generic_array::{ArrayLength, GenericArray};
|
use generic_array::{ArrayLength, GenericArray};
|
||||||
use rand::{CryptoRng, RngCore};
|
use rand::{CryptoRng, RngCore};
|
||||||
|
|
||||||
|
use crate::ciphersuite::CipherSuite;
|
||||||
use crate::errors::ProtocolError;
|
use crate::errors::ProtocolError;
|
||||||
use crate::key_exchange::group::KeGroup;
|
use crate::key_exchange::group::Group;
|
||||||
use crate::key_exchange::tripledh::DiffieHellman;
|
use crate::key_exchange::shared::DiffieHellman;
|
||||||
|
use crate::key_exchange::sigma_i::{Message, MessageBuilder, SignatureProtocol};
|
||||||
|
use crate::serialization::SliceExt;
|
||||||
|
|
||||||
/// A Keypair trait with public-private verification
|
/// A Keypair trait with public-private verification
|
||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
@@ -28,20 +32,24 @@ use crate::key_exchange::tripledh::DiffieHellman;
|
|||||||
))
|
))
|
||||||
)]
|
)]
|
||||||
#[derive_where(Clone)]
|
#[derive_where(Clone)]
|
||||||
#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; KG::Pk, SK)]
|
#[derive_where(Eq, Hash, Ord, PartialEq, PartialOrd; G::Pk, SK)]
|
||||||
pub struct KeyPair<KG: KeGroup, SK: Clone = PrivateKey<KG>> {
|
// `NonZeroScalar` doesn't implement `Debug`.
|
||||||
pk: PublicKey<KG>,
|
// TODO: remove after `elliptic-curve` bump to v0.14.
|
||||||
|
#[cfg_attr(not(test), derive_where(Debug; G::Pk, SK))]
|
||||||
|
#[cfg_attr(test, derive_where(Debug), derive_where(skip_inner(Debug)))]
|
||||||
|
pub struct KeyPair<G: Group, SK: Clone = PrivateKey<G>> {
|
||||||
|
pk: PublicKey<G>,
|
||||||
sk: SK,
|
sk: SK,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<KG: KeGroup, SK: Clone> KeyPair<KG, SK> {
|
impl<G: Group, SK: Clone> KeyPair<G, SK> {
|
||||||
/// Creates a new [`KeyPair`] from the given keys.
|
/// Creates a new [`KeyPair`] from the given keys.
|
||||||
pub fn new(sk: SK, pk: PublicKey<KG>) -> Self {
|
pub fn new(sk: SK, pk: PublicKey<G>) -> Self {
|
||||||
Self { pk, sk }
|
Self { pk, sk }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The public key component
|
/// The public key component
|
||||||
pub fn public(&self) -> &PublicKey<KG> {
|
pub fn public(&self) -> &PublicKey<G> {
|
||||||
&self.pk
|
&self.pk
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,15 +59,22 @@ impl<KG: KeGroup, SK: Clone> KeyPair<KG, SK> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<KG: KeGroup> KeyPair<KG> {
|
impl<G: Group> KeyPair<G> {
|
||||||
|
pub(crate) fn random<R: RngCore + CryptoRng>(rng: &mut R) -> Self {
|
||||||
|
let sk = G::random_sk(rng);
|
||||||
|
let pk = G::public_key(sk);
|
||||||
|
Self {
|
||||||
|
pk: PublicKey(pk),
|
||||||
|
sk: PrivateKey(sk),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Generating a random key pair given a cryptographic rng
|
/// Generating a random key pair given a cryptographic rng
|
||||||
pub(crate) fn generate_random<CS: voprf::CipherSuite, R: RngCore + CryptoRng>(
|
pub(crate) fn derive_random<R: RngCore + CryptoRng>(rng: &mut R) -> Self {
|
||||||
rng: &mut R,
|
let mut scalar_bytes = GenericArray::<_, <G as Group>::SkLen>::default();
|
||||||
) -> Self {
|
|
||||||
let mut scalar_bytes = GenericArray::<_, <KG as KeGroup>::SkLen>::default();
|
|
||||||
rng.fill_bytes(&mut scalar_bytes);
|
rng.fill_bytes(&mut scalar_bytes);
|
||||||
let sk = KG::derive_auth_keypair::<CS>(scalar_bytes).unwrap();
|
let sk = G::derive_scalar(scalar_bytes).unwrap();
|
||||||
let pk = KG::public_key(sk);
|
let pk = G::public_key(sk);
|
||||||
Self {
|
Self {
|
||||||
pk: PublicKey(pk),
|
pk: PublicKey(pk),
|
||||||
sk: PrivateKey(sk),
|
sk: PrivateKey(sk),
|
||||||
@@ -67,16 +82,237 @@ impl<KG: KeGroup> KeyPair<KG> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
/// Wrapper around a Key to enforce that it's a private one.
|
||||||
impl<KG: KeGroup> KeyPair<KG>
|
#[derive_where(Clone, ZeroizeOnDrop)]
|
||||||
|
#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; G::Sk)]
|
||||||
|
pub struct PrivateKey<G: Group>(G::Sk);
|
||||||
|
|
||||||
|
impl<G: Group> PrivateKey<G> {
|
||||||
|
pub(crate) fn new(key: G::Sk) -> Self {
|
||||||
|
Self(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns public key from private key
|
||||||
|
pub fn public_key(&self) -> PublicKey<G> {
|
||||||
|
PublicKey(G::public_key(self.0))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn serialize(&self) -> GenericArray<u8, G::SkLen> {
|
||||||
|
G::serialize_sk(self.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a [`PrivateKey`] from the given bytes.
|
||||||
|
pub fn deserialize(mut input: &[u8]) -> Result<Self, ProtocolError> {
|
||||||
|
Self::deserialize_take(&mut input)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn deserialize_take(input: &mut &[u8]) -> Result<Self, ProtocolError> {
|
||||||
|
G::deserialize_take_sk(input).map(Self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<G: Group> PrivateKey<G>
|
||||||
where
|
where
|
||||||
KG::Pk: std::fmt::Debug,
|
G::Sk: DiffieHellman<G>,
|
||||||
KG::Sk: std::fmt::Debug,
|
|
||||||
{
|
{
|
||||||
/// Test-only strategy returning a proptest Strategy based on
|
/// Diffie-Hellman key exchange implementation
|
||||||
/// [`Self::generate_random`]
|
pub(crate) fn ke_diffie_hellman(&self, pk: &PublicKey<G>) -> GenericArray<u8, G::PkLen> {
|
||||||
fn uniform_keypair_strategy<CS: voprf::CipherSuite>() -> proptest::prelude::BoxedStrategy<Self>
|
self.0.diffie_hellman(pk.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<G: Group> PrivateKey<G> {
|
||||||
|
/// Private-key signing implementation
|
||||||
|
pub(crate) fn sign<
|
||||||
|
R: CryptoRng + RngCore,
|
||||||
|
CS: CipherSuite,
|
||||||
|
SIG: SignatureProtocol<Group = G>,
|
||||||
|
KE: Group,
|
||||||
|
>(
|
||||||
|
&self,
|
||||||
|
rng: &mut R,
|
||||||
|
message: &Message<CS, KE>,
|
||||||
|
) -> (SIG::Signature, SIG::VerifyState<CS, KE>) {
|
||||||
|
SIG::sign(&self.0, rng, message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A trait to facilitate
|
||||||
|
/// [`ServerSetup::de/serialize`](crate::ServerSetup::serialize).
|
||||||
|
pub trait PrivateKeySerialization<G: Group>: Clone {
|
||||||
|
/// Custom error type that can be passed down to `ProtocolError::Custom`
|
||||||
|
type Error;
|
||||||
|
/// Serialization size in bytes.
|
||||||
|
type Len: ArrayLength<u8>;
|
||||||
|
|
||||||
|
/// Serialization into bytes
|
||||||
|
fn serialize_key_pair(key_pair: &KeyPair<G, Self>) -> GenericArray<u8, Self::Len>;
|
||||||
|
|
||||||
|
/// Deserialization from bytes
|
||||||
|
fn deserialize_take_key_pair(
|
||||||
|
input: &mut &[u8],
|
||||||
|
) -> Result<KeyPair<G, Self>, ProtocolError<Self::Error>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<G: Group> PrivateKeySerialization<G> for PrivateKey<G> {
|
||||||
|
type Error = core::convert::Infallible;
|
||||||
|
type Len = G::SkLen;
|
||||||
|
|
||||||
|
fn serialize_key_pair(key_pair: &KeyPair<G, Self>) -> GenericArray<u8, Self::Len> {
|
||||||
|
key_pair.private().serialize()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_take_key_pair(input: &mut &[u8]) -> Result<KeyPair<G, Self>, ProtocolError> {
|
||||||
|
let sk = PrivateKey::deserialize_take(input)?;
|
||||||
|
let pk = sk.public_key();
|
||||||
|
|
||||||
|
Ok(KeyPair::new(sk, pk))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
impl<'de, G: Group> serde::Deserialize<'de> for PrivateKey<G> {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
{
|
{
|
||||||
|
use serde::de::Error;
|
||||||
|
|
||||||
|
G::deserialize_take_sk(
|
||||||
|
&mut (GenericArray::<_, G::SkLen>::deserialize(deserializer)?.as_slice()),
|
||||||
|
)
|
||||||
|
.map(Self)
|
||||||
|
.map_err(D::Error::custom)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
impl<G: Group> serde::Serialize for PrivateKey<G> {
|
||||||
|
fn serialize<SK>(&self, serializer: SK) -> Result<SK::Ok, SK::Error>
|
||||||
|
where
|
||||||
|
SK: serde::Serializer,
|
||||||
|
{
|
||||||
|
G::serialize_sk(self.0).serialize(serializer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Wrapper around a Key to enforce that it's a public one.
|
||||||
|
#[derive_where(Clone, ZeroizeOnDrop)]
|
||||||
|
#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; G::Pk)]
|
||||||
|
pub struct PublicKey<G: Group + ?Sized>(G::Pk);
|
||||||
|
|
||||||
|
impl<G: Group> PublicKey<G> {
|
||||||
|
/// Convert from bytes
|
||||||
|
pub fn deserialize(mut key_bytes: &[u8]) -> Result<Self, ProtocolError> {
|
||||||
|
Self::deserialize_take(&mut key_bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn deserialize_take(key_bytes: &mut &[u8]) -> Result<Self, ProtocolError> {
|
||||||
|
G::deserialize_take_pk(key_bytes).map(Self)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert to bytes
|
||||||
|
pub fn serialize(&self) -> GenericArray<u8, G::PkLen> {
|
||||||
|
G::serialize_pk(self.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the inner [`Group::Pk`].
|
||||||
|
pub fn to_group_type(&self) -> G::Pk {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<G: Group> PublicKey<G> {
|
||||||
|
/// Public-key verifying implementation
|
||||||
|
pub(crate) fn verify<CS: CipherSuite, SIG: SignatureProtocol<Group = G>, KE: Group>(
|
||||||
|
&self,
|
||||||
|
message_builder: MessageBuilder<'_, CS>,
|
||||||
|
state: SIG::VerifyState<CS, KE>,
|
||||||
|
signature: &SIG::Signature,
|
||||||
|
) -> Result<(), ProtocolError> {
|
||||||
|
SIG::verify(&self.0, message_builder, state, signature)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
impl<'de, G: Group> serde::Deserialize<'de> for PublicKey<G> {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
use serde::de::Error;
|
||||||
|
|
||||||
|
G::deserialize_take_pk(
|
||||||
|
&mut (GenericArray::<_, G::PkLen>::deserialize(deserializer)?.as_slice()),
|
||||||
|
)
|
||||||
|
.map(Self)
|
||||||
|
.map_err(D::Error::custom)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
impl<G: Group> serde::Serialize for PublicKey<G> {
|
||||||
|
fn serialize<SK>(&self, serializer: SK) -> Result<SK::Ok, SK::Error>
|
||||||
|
where
|
||||||
|
SK: serde::Serializer,
|
||||||
|
{
|
||||||
|
G::serialize_pk(self.0).serialize(serializer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Default OPRF seed container.
|
||||||
|
#[cfg_attr(
|
||||||
|
feature = "serde",
|
||||||
|
derive(serde::Deserialize, serde::Serialize),
|
||||||
|
serde(bound = "")
|
||||||
|
)]
|
||||||
|
#[derive_where(Clone, Debug, Eq, Hash, PartialEq, ZeroizeOnDrop)]
|
||||||
|
pub struct OprfSeed<H: OutputSizeUser>(pub(crate) Output<H>);
|
||||||
|
|
||||||
|
/// A trait to facilitate
|
||||||
|
/// [`ServerSetup::de/serialize`](crate::ServerSetup::serialize).
|
||||||
|
///
|
||||||
|
/// Will be called with `E` being [`PrivateKeySerialization::Error`].
|
||||||
|
pub trait OprfSeedSerialization<H, E>: Sized {
|
||||||
|
/// Serialization size in bytes.
|
||||||
|
type Len: ArrayLength<u8>;
|
||||||
|
|
||||||
|
/// Serialization into bytes
|
||||||
|
fn serialize(&self) -> GenericArray<u8, Self::Len>;
|
||||||
|
|
||||||
|
/// Deserialization from bytes
|
||||||
|
fn deserialize_take(input: &mut &[u8]) -> Result<Self, ProtocolError<E>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<H: OutputSizeUser, E> OprfSeedSerialization<H, E> for OprfSeed<H> {
|
||||||
|
type Len = H::OutputSize;
|
||||||
|
|
||||||
|
fn serialize(&self) -> GenericArray<u8, Self::Len> {
|
||||||
|
self.0.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_take(input: &mut &[u8]) -> Result<Self, ProtocolError<E>> {
|
||||||
|
Ok(Self(
|
||||||
|
input
|
||||||
|
.take_array("OPRF seed")
|
||||||
|
.map_err(ProtocolError::into_custom)?,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////
|
||||||
|
// Test Implementations //
|
||||||
|
//===================== //
|
||||||
|
//////////////////////////
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
use crate::serialization::AssertZeroized;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
impl<G: Group> KeyPair<G> {
|
||||||
|
/// Test-only strategy returning a proptest Strategy based on
|
||||||
|
/// [`Self::derive_random`]
|
||||||
|
fn uniform_keypair_strategy() -> proptest::prelude::BoxedStrategy<Self> {
|
||||||
use proptest::prelude::*;
|
use proptest::prelude::*;
|
||||||
use rand::rngs::StdRng;
|
use rand::rngs::StdRng;
|
||||||
use rand::SeedableRng;
|
use rand::SeedableRng;
|
||||||
@@ -86,158 +322,61 @@ where
|
|||||||
any::<[u8; 32]>()
|
any::<[u8; 32]>()
|
||||||
.prop_filter_map("valid random keypair", |seed| {
|
.prop_filter_map("valid random keypair", |seed| {
|
||||||
let mut rng = StdRng::from_seed(seed);
|
let mut rng = StdRng::from_seed(seed);
|
||||||
Some(Self::generate_random::<CS, _>(&mut rng))
|
Some(Self::derive_random(&mut rng))
|
||||||
})
|
})
|
||||||
.no_shrink()
|
.no_shrink()
|
||||||
.boxed()
|
.boxed()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wrapper around a Key to enforce that it's a private one.
|
#[cfg(test)]
|
||||||
#[derive_where(Clone, ZeroizeOnDrop)]
|
impl<G: Group> AssertZeroized for PublicKey<G>
|
||||||
#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; KG::Sk)]
|
|
||||||
pub struct PrivateKey<KG: KeGroup>(KG::Sk);
|
|
||||||
|
|
||||||
impl<KG: KeGroup> PrivateKey<KG> {
|
|
||||||
/// Returns public key from private key
|
|
||||||
pub fn public_key(&self) -> PublicKey<KG> {
|
|
||||||
PublicKey(KG::public_key(self.0))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn serialize(&self) -> GenericArray<u8, KG::SkLen> {
|
|
||||||
KG::serialize_sk(self.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a [`PrivateKey`] from the given bytes.
|
|
||||||
pub fn deserialize(input: &[u8]) -> Result<Self, ProtocolError> {
|
|
||||||
KG::deserialize_sk(input).map(Self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<KG: KeGroup> PrivateKey<KG>
|
|
||||||
where
|
where
|
||||||
KG::Sk: DiffieHellman<KG>,
|
G::Pk: AssertZeroized,
|
||||||
{
|
{
|
||||||
/// Diffie-Hellman key exchange implementation
|
fn assert_zeroized(&self) {
|
||||||
pub(crate) fn ke_diffie_hellman(&self, pk: &PublicKey<KG>) -> GenericArray<u8, KG::PkLen> {
|
self.0.assert_zeroized();
|
||||||
self.0.diffie_hellman(pk.0)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A trait to facilitate
|
#[cfg(test)]
|
||||||
/// [`ServerSetup::de/serialize`](crate::ServerSetup::serialize).
|
impl<G: Group> AssertZeroized for PrivateKey<G>
|
||||||
pub trait PrivateKeySerialization<KG: KeGroup>: Clone {
|
where
|
||||||
/// Custom error type that can be passed down to `ProtocolError::Custom`
|
G::Sk: AssertZeroized,
|
||||||
type Error;
|
{
|
||||||
/// Serialization size in bytes.
|
fn assert_zeroized(&self) {
|
||||||
type Len: ArrayLength<u8>;
|
self.0.assert_zeroized();
|
||||||
|
|
||||||
/// Serialization into bytes
|
|
||||||
fn serialize_key_pair(key_pair: &KeyPair<KG, Self>) -> GenericArray<u8, Self::Len>;
|
|
||||||
|
|
||||||
/// Deserialization from bytes
|
|
||||||
fn deserialize_key_pair(input: &[u8]) -> Result<KeyPair<KG, Self>, ProtocolError<Self::Error>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<KG: KeGroup> PrivateKeySerialization<KG> for PrivateKey<KG> {
|
|
||||||
type Error = core::convert::Infallible;
|
|
||||||
type Len = KG::SkLen;
|
|
||||||
|
|
||||||
fn serialize_key_pair(key_pair: &KeyPair<KG, Self>) -> GenericArray<u8, Self::Len> {
|
|
||||||
key_pair.private().serialize()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn deserialize_key_pair(input: &[u8]) -> Result<KeyPair<KG, Self>, ProtocolError> {
|
|
||||||
let sk = PrivateKey::deserialize(input)?;
|
|
||||||
let pk = sk.public_key();
|
|
||||||
|
|
||||||
Ok(KeyPair::new(sk, pk))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
|
||||||
impl<'de, KG: KeGroup> serde::Deserialize<'de> for PrivateKey<KG> {
|
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
||||||
where
|
|
||||||
D: serde::Deserializer<'de>,
|
|
||||||
{
|
|
||||||
use serde::de::Error;
|
|
||||||
|
|
||||||
KG::deserialize_sk(&GenericArray::<_, KG::SkLen>::deserialize(deserializer)?)
|
|
||||||
.map(Self)
|
|
||||||
.map_err(D::Error::custom)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
|
||||||
impl<KG: KeGroup> serde::Serialize for PrivateKey<KG> {
|
|
||||||
fn serialize<SK>(&self, serializer: SK) -> Result<SK::Ok, SK::Error>
|
|
||||||
where
|
|
||||||
SK: serde::Serializer,
|
|
||||||
{
|
|
||||||
KG::serialize_sk(self.0).serialize(serializer)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Wrapper around a Key to enforce that it's a public one.
|
|
||||||
#[derive_where(Clone, ZeroizeOnDrop)]
|
|
||||||
#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; KG::Pk)]
|
|
||||||
pub struct PublicKey<KG: KeGroup>(KG::Pk);
|
|
||||||
|
|
||||||
impl<KG: KeGroup> PublicKey<KG> {
|
|
||||||
/// Convert from bytes
|
|
||||||
pub fn deserialize(key_bytes: &[u8]) -> Result<Self, ProtocolError> {
|
|
||||||
KG::deserialize_pk(key_bytes).map(Self)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Convert to bytes
|
|
||||||
pub fn serialize(&self) -> GenericArray<u8, KG::PkLen> {
|
|
||||||
KG::serialize_pk(self.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the inner [`KeGroup::Pk`].
|
|
||||||
pub fn to_group_type(&self) -> KG::Pk {
|
|
||||||
self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
|
||||||
impl<'de, KG: KeGroup> serde::Deserialize<'de> for PublicKey<KG> {
|
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
||||||
where
|
|
||||||
D: serde::Deserializer<'de>,
|
|
||||||
{
|
|
||||||
use serde::de::Error;
|
|
||||||
|
|
||||||
KG::deserialize_pk(&GenericArray::<_, KG::PkLen>::deserialize(deserializer)?)
|
|
||||||
.map(Self)
|
|
||||||
.map_err(D::Error::custom)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
|
||||||
impl<KG: KeGroup> serde::Serialize for PublicKey<KG> {
|
|
||||||
fn serialize<SK>(&self, serializer: SK) -> Result<SK::Ok, SK::Error>
|
|
||||||
where
|
|
||||||
SK: serde::Serializer,
|
|
||||||
{
|
|
||||||
KG::serialize_pk(self.0).serialize(serializer)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use core::ptr;
|
||||||
|
|
||||||
|
use hkdf::Hkdf;
|
||||||
use rand::rngs::OsRng;
|
use rand::rngs::OsRng;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::util;
|
use crate::ciphersuite::{KeGroup, OprfHash};
|
||||||
|
use crate::serialization::AssertZeroized;
|
||||||
|
use crate::{
|
||||||
|
CipherSuite, ClientLogin, ClientLoginFinishParameters, ClientLoginFinishResult,
|
||||||
|
ClientLoginStartResult, ClientRegistration, ClientRegistrationFinishParameters,
|
||||||
|
ClientRegistrationFinishResult, ClientRegistrationStartResult, ServerLogin,
|
||||||
|
ServerLoginParameters, ServerLoginStartResult, ServerRegistration,
|
||||||
|
ServerRegistrationStartResult, ServerSetup,
|
||||||
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_zeroize_key() {
|
fn test_zeroize_key() {
|
||||||
fn inner<G: KeGroup>() {
|
fn inner<G: Group>()
|
||||||
|
where
|
||||||
|
G::Sk: AssertZeroized,
|
||||||
|
{
|
||||||
let mut rng = OsRng;
|
let mut rng = OsRng;
|
||||||
let mut key = PrivateKey::<G>(G::random_sk(&mut rng));
|
let mut key = PrivateKey::<G>(G::random_sk(&mut rng));
|
||||||
util::test_zeroize_on_drop(&mut key);
|
unsafe { ptr::drop_in_place(&mut key) };
|
||||||
|
key.0.assert_zeroized();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "ristretto255")]
|
#[cfg(feature = "ristretto255")]
|
||||||
@@ -245,6 +384,10 @@ mod tests {
|
|||||||
inner::<::p256::NistP256>();
|
inner::<::p256::NistP256>();
|
||||||
inner::<::p384::NistP384>();
|
inner::<::p384::NistP384>();
|
||||||
inner::<::p521::NistP521>();
|
inner::<::p521::NistP521>();
|
||||||
|
#[cfg(feature = "curve25519")]
|
||||||
|
inner::<crate::Curve25519>();
|
||||||
|
#[cfg(feature = "ed25519")]
|
||||||
|
inner::<crate::Ed25519>();
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! test {
|
macro_rules! test {
|
||||||
@@ -258,15 +401,15 @@ mod tests {
|
|||||||
|
|
||||||
proptest! {
|
proptest! {
|
||||||
#[test]
|
#[test]
|
||||||
fn pub_from_priv(kp in KeyPair::<$point>::uniform_keypair_strategy::<$point>()) {
|
fn pub_from_priv(kp in KeyPair::<$point>::uniform_keypair_strategy()) {
|
||||||
let pk = kp.public();
|
let pk = kp.public();
|
||||||
let sk = kp.private();
|
let sk = kp.private();
|
||||||
prop_assert_eq!(&sk.public_key(), pk);
|
prop_assert_eq!(&sk.public_key(), pk);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn dh(kp1 in KeyPair::<$point>::uniform_keypair_strategy::<$point>(),
|
fn dh(kp1 in KeyPair::<$point>::uniform_keypair_strategy(),
|
||||||
kp2 in KeyPair::<$point>::uniform_keypair_strategy::<$point>()) {
|
kp2 in KeyPair::<$point>::uniform_keypair_strategy()) {
|
||||||
|
|
||||||
let dh1 = kp2.private().ke_diffie_hellman(&kp1.public());
|
let dh1 = kp2.private().ke_diffie_hellman(&kp1.public());
|
||||||
let dh2 = kp1.private().ke_diffie_hellman(kp2.public());
|
let dh2 = kp1.private().ke_diffie_hellman(kp2.public());
|
||||||
@@ -275,10 +418,10 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn private_key_slice(kp in KeyPair::<$point>::uniform_keypair_strategy::<$point>()) {
|
fn private_key_slice(kp in KeyPair::<$point>::uniform_keypair_strategy()) {
|
||||||
let sk_bytes = kp.private().serialize().to_vec();
|
let sk_bytes = kp.private().serialize().to_vec();
|
||||||
|
|
||||||
let kp2 = PrivateKey::<$point>::deserialize_key_pair(&sk_bytes)?;
|
let kp2 = PrivateKey::<$point>::deserialize_take_key_pair(&mut (sk_bytes.as_slice()))?;
|
||||||
let kp2_private_bytes = kp2.private().serialize().to_vec();
|
let kp2_private_bytes = kp2.private().serialize().to_vec();
|
||||||
|
|
||||||
prop_assert_eq!(sk_bytes, kp2_private_bytes);
|
prop_assert_eq!(sk_bytes, kp2_private_bytes);
|
||||||
@@ -294,41 +437,31 @@ mod tests {
|
|||||||
test!(p384, ::p384::NistP384);
|
test!(p384, ::p384::NistP384);
|
||||||
test!(p521, ::p521::NistP521);
|
test!(p521, ::p521::NistP521);
|
||||||
|
|
||||||
|
struct Default;
|
||||||
|
|
||||||
|
impl CipherSuite for Default {
|
||||||
|
#[cfg(feature = "ristretto255")]
|
||||||
|
type OprfCs = crate::Ristretto255;
|
||||||
|
#[cfg(not(feature = "ristretto255"))]
|
||||||
|
type OprfCs = ::p256::NistP256;
|
||||||
|
#[cfg(feature = "ristretto255")]
|
||||||
|
type KeyExchange = crate::TripleDh<crate::Ristretto255, sha2::Sha512>;
|
||||||
|
#[cfg(not(feature = "ristretto255"))]
|
||||||
|
type KeyExchange = crate::TripleDh<::p256::NistP256, sha2::Sha256>;
|
||||||
|
type Ksf = crate::ksf::Identity;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct RemoteSeed<H: OutputSizeUser>(Output<H>);
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct RemoteKey(PrivateKey<KeGroup<Default>>);
|
||||||
|
|
||||||
|
const PASSWORD: &str = "password";
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn remote_key() {
|
fn remote_key() {
|
||||||
use rand::rngs::OsRng;
|
let sk = PrivateKey(KeGroup::<Default>::random_sk(&mut OsRng));
|
||||||
|
|
||||||
use crate::{
|
|
||||||
CipherSuite, ClientLogin, ClientLoginFinishParameters, ClientLoginFinishResult,
|
|
||||||
ClientLoginStartResult, ClientRegistration, ClientRegistrationFinishParameters,
|
|
||||||
ClientRegistrationFinishResult, ClientRegistrationStartResult, ServerLogin,
|
|
||||||
ServerLoginStartParameters, ServerLoginStartResult, ServerRegistration,
|
|
||||||
ServerRegistrationStartResult, ServerSetup,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Default;
|
|
||||||
|
|
||||||
impl CipherSuite for Default {
|
|
||||||
#[cfg(feature = "ristretto255")]
|
|
||||||
type OprfCs = crate::Ristretto255;
|
|
||||||
#[cfg(not(feature = "ristretto255"))]
|
|
||||||
type OprfCs = ::p256::NistP256;
|
|
||||||
#[cfg(feature = "ristretto255")]
|
|
||||||
type KeGroup = crate::Ristretto255;
|
|
||||||
#[cfg(not(feature = "ristretto255"))]
|
|
||||||
type KeGroup = ::p256::NistP256;
|
|
||||||
type KeyExchange = crate::key_exchange::tripledh::TripleDh;
|
|
||||||
type Ksf = crate::ksf::Identity;
|
|
||||||
}
|
|
||||||
|
|
||||||
type KeCurve = <Default as CipherSuite>::KeGroup;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
struct RemoteKey(PrivateKey<KeCurve>);
|
|
||||||
|
|
||||||
const PASSWORD: &str = "password";
|
|
||||||
|
|
||||||
let sk = PrivateKey(KeCurve::random_sk(&mut OsRng));
|
|
||||||
let pk = sk.public_key();
|
let pk = sk.public_key();
|
||||||
let sk = RemoteKey(sk);
|
let sk = RemoteKey(sk);
|
||||||
let keypair = KeyPair::new(sk, pk);
|
let keypair = KeyPair::new(sk, pk);
|
||||||
@@ -362,7 +495,7 @@ mod tests {
|
|||||||
Some(file),
|
Some(file),
|
||||||
message,
|
message,
|
||||||
&[],
|
&[],
|
||||||
ServerLoginStartParameters::default(),
|
ServerLoginParameters::default(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let shared_secret = builder.private_key().0.ke_diffie_hellman(builder.data());
|
let shared_secret = builder.private_key().0.ke_diffie_hellman(builder.data());
|
||||||
@@ -373,11 +506,88 @@ mod tests {
|
|||||||
} = builder.build(shared_secret).unwrap();
|
} = builder.build(shared_secret).unwrap();
|
||||||
let ClientLoginFinishResult { message, .. } = client
|
let ClientLoginFinishResult { message, .. } = client
|
||||||
.finish(
|
.finish(
|
||||||
|
&mut OsRng,
|
||||||
PASSWORD.as_bytes(),
|
PASSWORD.as_bytes(),
|
||||||
message,
|
message,
|
||||||
ClientLoginFinishParameters::default(),
|
ClientLoginFinishParameters::default(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
server.finish(message).unwrap();
|
server
|
||||||
|
.finish(message, ServerLoginParameters::default())
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn remote_seed() {
|
||||||
|
let mut oprf_seed = RemoteSeed::<OprfHash<Default>>(GenericArray::default());
|
||||||
|
OsRng.fill_bytes(&mut oprf_seed.0);
|
||||||
|
|
||||||
|
let sk = PrivateKey(KeGroup::<Default>::random_sk(&mut OsRng));
|
||||||
|
let pk = sk.public_key();
|
||||||
|
let sk = RemoteKey(sk);
|
||||||
|
let keypair = KeyPair::new(sk, pk);
|
||||||
|
|
||||||
|
let server_setup = ServerSetup::<Default, _, _>::new_with_key_pair_and_seed(
|
||||||
|
&mut OsRng, keypair, oprf_seed,
|
||||||
|
);
|
||||||
|
|
||||||
|
let ClientRegistrationStartResult {
|
||||||
|
message,
|
||||||
|
state: client,
|
||||||
|
} = ClientRegistration::<Default>::start(&mut OsRng, PASSWORD.as_bytes()).unwrap();
|
||||||
|
let km = server_setup.key_material_info(&[]);
|
||||||
|
let mut ikm = GenericArray::default();
|
||||||
|
Hkdf::<OprfHash<Default>>::from_prk(&km.ikm.0)
|
||||||
|
.unwrap()
|
||||||
|
.expand_multi_info(&km.info, &mut ikm)
|
||||||
|
.unwrap();
|
||||||
|
let ServerRegistrationStartResult { message, .. } =
|
||||||
|
ServerRegistration::start_with_key_material(&server_setup, ikm, message).unwrap();
|
||||||
|
let ClientRegistrationFinishResult { message, .. } = client
|
||||||
|
.finish(
|
||||||
|
&mut OsRng,
|
||||||
|
PASSWORD.as_bytes(),
|
||||||
|
message,
|
||||||
|
ClientRegistrationFinishParameters::default(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let file = ServerRegistration::finish(message);
|
||||||
|
|
||||||
|
let ClientLoginStartResult {
|
||||||
|
message,
|
||||||
|
state: client,
|
||||||
|
} = ClientLogin::<Default>::start(&mut OsRng, PASSWORD.as_bytes()).unwrap();
|
||||||
|
let km = server_setup.key_material_info(&[]);
|
||||||
|
let mut ikm = GenericArray::default();
|
||||||
|
Hkdf::<OprfHash<Default>>::from_prk(&km.ikm.0)
|
||||||
|
.unwrap()
|
||||||
|
.expand_multi_info(&km.info, &mut ikm)
|
||||||
|
.unwrap();
|
||||||
|
let builder = ServerLogin::builder_with_key_material(
|
||||||
|
&mut OsRng,
|
||||||
|
&server_setup,
|
||||||
|
ikm,
|
||||||
|
Some(file),
|
||||||
|
message,
|
||||||
|
ServerLoginParameters::default(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let shared_secret = builder.private_key().0.ke_diffie_hellman(builder.data());
|
||||||
|
let ServerLoginStartResult {
|
||||||
|
message,
|
||||||
|
state: server,
|
||||||
|
..
|
||||||
|
} = builder.build(shared_secret).unwrap();
|
||||||
|
let ClientLoginFinishResult { message, .. } = client
|
||||||
|
.finish(
|
||||||
|
&mut OsRng,
|
||||||
|
PASSWORD.as_bytes(),
|
||||||
|
message,
|
||||||
|
ClientLoginFinishParameters::default(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
server
|
||||||
|
.finish(message, ServerLoginParameters::default())
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+301
-120
@@ -32,11 +32,12 @@
|
|||||||
//! We will use the following choices in this example:
|
//! We will use the following choices in this example:
|
||||||
//! ```ignore
|
//! ```ignore
|
||||||
//! use opaque_ke::CipherSuite;
|
//! use opaque_ke::CipherSuite;
|
||||||
|
//!
|
||||||
//! struct Default;
|
//! struct Default;
|
||||||
|
//!
|
||||||
//! impl CipherSuite for Default {
|
//! impl CipherSuite for Default {
|
||||||
//! type OprfCs = opaque_ke::Ristretto255;
|
//! type OprfCs = opaque_ke::Ristretto255;
|
||||||
//! type KeGroup = opaque_ke::Ristretto255;
|
//! type KeyExchange = opaque_ke::TripleDh<opaque_ke::Ristretto255, sha2::Sha512>;
|
||||||
//! type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDh;
|
|
||||||
//! type Ksf = opaque_ke::ksf::Identity;
|
//! type Ksf = opaque_ke::ksf::Identity;
|
||||||
//! }
|
//! }
|
||||||
//! ```
|
//! ```
|
||||||
@@ -60,19 +61,18 @@
|
|||||||
//! # #[cfg(feature = "ristretto255")]
|
//! # #[cfg(feature = "ristretto255")]
|
||||||
//! # impl CipherSuite for Default {
|
//! # impl CipherSuite for Default {
|
||||||
//! # type OprfCs = opaque_ke::Ristretto255;
|
//! # type OprfCs = opaque_ke::Ristretto255;
|
||||||
//! # type KeGroup = opaque_ke::Ristretto255;
|
//! # type KeyExchange = opaque_ke::TripleDh<opaque_ke::Ristretto255, sha2::Sha512>;
|
||||||
//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDh;
|
|
||||||
//! # type Ksf = opaque_ke::ksf::Identity;
|
//! # type Ksf = opaque_ke::ksf::Identity;
|
||||||
//! # }
|
//! # }
|
||||||
//! # #[cfg(not(feature = "ristretto255"))]
|
//! # #[cfg(not(feature = "ristretto255"))]
|
||||||
//! # impl CipherSuite for Default {
|
//! # impl CipherSuite for Default {
|
||||||
//! # type OprfCs = p256::NistP256;
|
//! # type OprfCs = p256::NistP256;
|
||||||
//! # type KeGroup = p256::NistP256;
|
//! # type KeyExchange = opaque_ke::TripleDh<p256::NistP256, sha2::Sha256>;
|
||||||
//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDh;
|
|
||||||
//! # type Ksf = opaque_ke::ksf::Identity;
|
//! # type Ksf = opaque_ke::ksf::Identity;
|
||||||
//! # }
|
//! # }
|
||||||
//! use rand::rngs::OsRng;
|
//! use rand::rngs::OsRng;
|
||||||
//! use rand::RngCore;
|
//! use rand::RngCore;
|
||||||
|
//!
|
||||||
//! let mut rng = OsRng;
|
//! let mut rng = OsRng;
|
||||||
//! let server_setup = ServerSetup::<Default>::new(&mut rng);
|
//! let server_setup = ServerSetup::<Default>::new(&mut rng);
|
||||||
//! # Ok::<(), ProtocolError>(())
|
//! # Ok::<(), ProtocolError>(())
|
||||||
@@ -117,20 +117,19 @@
|
|||||||
//! # #[cfg(feature = "ristretto255")]
|
//! # #[cfg(feature = "ristretto255")]
|
||||||
//! # impl CipherSuite for Default {
|
//! # impl CipherSuite for Default {
|
||||||
//! # type OprfCs = opaque_ke::Ristretto255;
|
//! # type OprfCs = opaque_ke::Ristretto255;
|
||||||
//! # type KeGroup = opaque_ke::Ristretto255;
|
//! # type KeyExchange = opaque_ke::TripleDh<opaque_ke::Ristretto255, sha2::Sha512>;
|
||||||
//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDh;
|
|
||||||
//! # type Ksf = opaque_ke::ksf::Identity;
|
//! # type Ksf = opaque_ke::ksf::Identity;
|
||||||
//! # }
|
//! # }
|
||||||
//! # #[cfg(not(feature = "ristretto255"))]
|
//! # #[cfg(not(feature = "ristretto255"))]
|
||||||
//! # impl CipherSuite for Default {
|
//! # impl CipherSuite for Default {
|
||||||
//! # type OprfCs = p256::NistP256;
|
//! # type OprfCs = p256::NistP256;
|
||||||
//! # type KeGroup = p256::NistP256;
|
//! # type KeyExchange = opaque_ke::TripleDh<p256::NistP256, sha2::Sha256>;
|
||||||
//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDh;
|
|
||||||
//! # type Ksf = opaque_ke::ksf::Identity;
|
//! # type Ksf = opaque_ke::ksf::Identity;
|
||||||
//! # }
|
//! # }
|
||||||
//! use opaque_ke::ClientRegistration;
|
//! use opaque_ke::ClientRegistration;
|
||||||
//! use rand::rngs::OsRng;
|
//! use rand::rngs::OsRng;
|
||||||
//! use rand::RngCore;
|
//! use rand::RngCore;
|
||||||
|
//!
|
||||||
//! let mut client_rng = OsRng;
|
//! let mut client_rng = OsRng;
|
||||||
//! let client_registration_start_result =
|
//! let client_registration_start_result =
|
||||||
//! ClientRegistration::<Default>::start(&mut client_rng, b"password")?;
|
//! ClientRegistration::<Default>::start(&mut client_rng, b"password")?;
|
||||||
@@ -156,15 +155,13 @@
|
|||||||
//! # #[cfg(feature = "ristretto255")]
|
//! # #[cfg(feature = "ristretto255")]
|
||||||
//! # impl CipherSuite for Default {
|
//! # impl CipherSuite for Default {
|
||||||
//! # type OprfCs = opaque_ke::Ristretto255;
|
//! # type OprfCs = opaque_ke::Ristretto255;
|
||||||
//! # type KeGroup = opaque_ke::Ristretto255;
|
//! # type KeyExchange = opaque_ke::TripleDh<opaque_ke::Ristretto255, sha2::Sha512>;
|
||||||
//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDh;
|
|
||||||
//! # type Ksf = opaque_ke::ksf::Identity;
|
//! # type Ksf = opaque_ke::ksf::Identity;
|
||||||
//! # }
|
//! # }
|
||||||
//! # #[cfg(not(feature = "ristretto255"))]
|
//! # #[cfg(not(feature = "ristretto255"))]
|
||||||
//! # impl CipherSuite for Default {
|
//! # impl CipherSuite for Default {
|
||||||
//! # type OprfCs = p256::NistP256;
|
//! # type OprfCs = p256::NistP256;
|
||||||
//! # type KeGroup = p256::NistP256;
|
//! # type KeyExchange = opaque_ke::TripleDh<p256::NistP256, sha2::Sha256>;
|
||||||
//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDh;
|
|
||||||
//! # type Ksf = opaque_ke::ksf::Identity;
|
//! # type Ksf = opaque_ke::ksf::Identity;
|
||||||
//! # }
|
//! # }
|
||||||
//! # use rand::{rngs::OsRng, RngCore};
|
//! # use rand::{rngs::OsRng, RngCore};
|
||||||
@@ -174,6 +171,7 @@
|
|||||||
//! # b"password",
|
//! # b"password",
|
||||||
//! # )?;
|
//! # )?;
|
||||||
//! use opaque_ke::ServerRegistration;
|
//! use opaque_ke::ServerRegistration;
|
||||||
|
//!
|
||||||
//! # let mut server_rng = OsRng;
|
//! # let mut server_rng = OsRng;
|
||||||
//! # let server_setup = ServerSetup::<Default>::new(&mut server_rng);
|
//! # let server_setup = ServerSetup::<Default>::new(&mut server_rng);
|
||||||
//! let server_registration_start_result = ServerRegistration::<Default>::start(
|
//! let server_registration_start_result = ServerRegistration::<Default>::start(
|
||||||
@@ -196,7 +194,7 @@
|
|||||||
//! ```
|
//! ```
|
||||||
//! # use opaque_ke::{
|
//! # use opaque_ke::{
|
||||||
//! # errors::ProtocolError,
|
//! # errors::ProtocolError,
|
||||||
//! # ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, ServerSetup,
|
//! # ClientRegistration, ServerRegistration, ServerSetup,
|
||||||
//! # ksf::Identity,
|
//! # ksf::Identity,
|
||||||
//! # };
|
//! # };
|
||||||
//! # use opaque_ke::CipherSuite;
|
//! # use opaque_ke::CipherSuite;
|
||||||
@@ -204,15 +202,13 @@
|
|||||||
//! # #[cfg(feature = "ristretto255")]
|
//! # #[cfg(feature = "ristretto255")]
|
||||||
//! # impl CipherSuite for Default {
|
//! # impl CipherSuite for Default {
|
||||||
//! # type OprfCs = opaque_ke::Ristretto255;
|
//! # type OprfCs = opaque_ke::Ristretto255;
|
||||||
//! # type KeGroup = opaque_ke::Ristretto255;
|
//! # type KeyExchange = opaque_ke::TripleDh<opaque_ke::Ristretto255, sha2::Sha512>;
|
||||||
//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDh;
|
|
||||||
//! # type Ksf = opaque_ke::ksf::Identity;
|
//! # type Ksf = opaque_ke::ksf::Identity;
|
||||||
//! # }
|
//! # }
|
||||||
//! # #[cfg(not(feature = "ristretto255"))]
|
//! # #[cfg(not(feature = "ristretto255"))]
|
||||||
//! # impl CipherSuite for Default {
|
//! # impl CipherSuite for Default {
|
||||||
//! # type OprfCs = p256::NistP256;
|
//! # type OprfCs = p256::NistP256;
|
||||||
//! # type KeGroup = p256::NistP256;
|
//! # type KeyExchange = opaque_ke::TripleDh<p256::NistP256, sha2::Sha256>;
|
||||||
//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDh;
|
|
||||||
//! # type Ksf = opaque_ke::ksf::Identity;
|
//! # type Ksf = opaque_ke::ksf::Identity;
|
||||||
//! # }
|
//! # }
|
||||||
//! # use rand::{rngs::OsRng, RngCore};
|
//! # use rand::{rngs::OsRng, RngCore};
|
||||||
@@ -224,6 +220,8 @@
|
|||||||
//! # let mut server_rng = OsRng;
|
//! # let mut server_rng = OsRng;
|
||||||
//! # let server_setup = ServerSetup::<Default>::new(&mut server_rng);
|
//! # let server_setup = ServerSetup::<Default>::new(&mut server_rng);
|
||||||
//! # let server_registration_start_result = ServerRegistration::<Default>::start(&server_setup, client_registration_start_result.message, b"[email protected]")?;
|
//! # let server_registration_start_result = ServerRegistration::<Default>::start(&server_setup, client_registration_start_result.message, b"[email protected]")?;
|
||||||
|
//! use opaque_ke::ClientRegistrationFinishParameters;
|
||||||
|
//!
|
||||||
//! let client_registration_finish_result = client_registration_start_result.state.finish(
|
//! let client_registration_finish_result = client_registration_start_result.state.finish(
|
||||||
//! &mut client_rng,
|
//! &mut client_rng,
|
||||||
//! b"password",
|
//! b"password",
|
||||||
@@ -252,15 +250,13 @@
|
|||||||
//! # #[cfg(feature = "ristretto255")]
|
//! # #[cfg(feature = "ristretto255")]
|
||||||
//! # impl CipherSuite for Default {
|
//! # impl CipherSuite for Default {
|
||||||
//! # type OprfCs = opaque_ke::Ristretto255;
|
//! # type OprfCs = opaque_ke::Ristretto255;
|
||||||
//! # type KeGroup = opaque_ke::Ristretto255;
|
//! # type KeyExchange = opaque_ke::TripleDh<opaque_ke::Ristretto255, sha2::Sha512>;
|
||||||
//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDh;
|
|
||||||
//! # type Ksf = opaque_ke::ksf::Identity;
|
//! # type Ksf = opaque_ke::ksf::Identity;
|
||||||
//! # }
|
//! # }
|
||||||
//! # #[cfg(not(feature = "ristretto255"))]
|
//! # #[cfg(not(feature = "ristretto255"))]
|
||||||
//! # impl CipherSuite for Default {
|
//! # impl CipherSuite for Default {
|
||||||
//! # type OprfCs = p256::NistP256;
|
//! # type OprfCs = p256::NistP256;
|
||||||
//! # type KeGroup = p256::NistP256;
|
//! # type KeyExchange = opaque_ke::TripleDh<p256::NistP256, sha2::Sha256>;
|
||||||
//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDh;
|
|
||||||
//! # type Ksf = opaque_ke::ksf::Identity;
|
//! # type Ksf = opaque_ke::ksf::Identity;
|
||||||
//! # }
|
//! # }
|
||||||
//! # use rand::{rngs::OsRng, RngCore};
|
//! # use rand::{rngs::OsRng, RngCore};
|
||||||
@@ -305,19 +301,18 @@
|
|||||||
//! # #[cfg(feature = "ristretto255")]
|
//! # #[cfg(feature = "ristretto255")]
|
||||||
//! # impl CipherSuite for Default {
|
//! # impl CipherSuite for Default {
|
||||||
//! # type OprfCs = opaque_ke::Ristretto255;
|
//! # type OprfCs = opaque_ke::Ristretto255;
|
||||||
//! # type KeGroup = opaque_ke::Ristretto255;
|
//! # type KeyExchange = opaque_ke::TripleDh<opaque_ke::Ristretto255, sha2::Sha512>;
|
||||||
//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDh;
|
|
||||||
//! # type Ksf = opaque_ke::ksf::Identity;
|
//! # type Ksf = opaque_ke::ksf::Identity;
|
||||||
//! # }
|
//! # }
|
||||||
//! # #[cfg(not(feature = "ristretto255"))]
|
//! # #[cfg(not(feature = "ristretto255"))]
|
||||||
//! # impl CipherSuite for Default {
|
//! # impl CipherSuite for Default {
|
||||||
//! # type OprfCs = p256::NistP256;
|
//! # type OprfCs = p256::NistP256;
|
||||||
//! # type KeGroup = p256::NistP256;
|
//! # type KeyExchange = opaque_ke::TripleDh<p256::NistP256, sha2::Sha256>;
|
||||||
//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDh;
|
|
||||||
//! # type Ksf = opaque_ke::ksf::Identity;
|
//! # type Ksf = opaque_ke::ksf::Identity;
|
||||||
//! # }
|
//! # }
|
||||||
//! # use rand::{rngs::OsRng, RngCore};
|
//! # use rand::{rngs::OsRng, RngCore};
|
||||||
//! use opaque_ke::ClientLogin;
|
//! use opaque_ke::ClientLogin;
|
||||||
|
//!
|
||||||
//! let mut client_rng = OsRng;
|
//! let mut client_rng = OsRng;
|
||||||
//! let client_login_start_result = ClientLogin::<Default>::start(&mut client_rng, b"password")?;
|
//! let client_login_start_result = ClientLogin::<Default>::start(&mut client_rng, b"password")?;
|
||||||
//! # Ok::<(), ProtocolError>(())
|
//! # Ok::<(), ProtocolError>(())
|
||||||
@@ -342,15 +337,13 @@
|
|||||||
//! # #[cfg(feature = "ristretto255")]
|
//! # #[cfg(feature = "ristretto255")]
|
||||||
//! # impl CipherSuite for Default {
|
//! # impl CipherSuite for Default {
|
||||||
//! # type OprfCs = opaque_ke::Ristretto255;
|
//! # type OprfCs = opaque_ke::Ristretto255;
|
||||||
//! # type KeGroup = opaque_ke::Ristretto255;
|
//! # type KeyExchange = opaque_ke::TripleDh<opaque_ke::Ristretto255, sha2::Sha512>;
|
||||||
//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDh;
|
|
||||||
//! # type Ksf = opaque_ke::ksf::Identity;
|
//! # type Ksf = opaque_ke::ksf::Identity;
|
||||||
//! # }
|
//! # }
|
||||||
//! # #[cfg(not(feature = "ristretto255"))]
|
//! # #[cfg(not(feature = "ristretto255"))]
|
||||||
//! # impl CipherSuite for Default {
|
//! # impl CipherSuite for Default {
|
||||||
//! # type OprfCs = p256::NistP256;
|
//! # type OprfCs = p256::NistP256;
|
||||||
//! # type KeGroup = p256::NistP256;
|
//! # type KeyExchange = opaque_ke::TripleDh<p256::NistP256, sha2::Sha256>;
|
||||||
//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDh;
|
|
||||||
//! # type Ksf = opaque_ke::ksf::Identity;
|
//! # type Ksf = opaque_ke::ksf::Identity;
|
||||||
//! # }
|
//! # }
|
||||||
//! # use rand::{rngs::OsRng, RngCore};
|
//! # use rand::{rngs::OsRng, RngCore};
|
||||||
@@ -368,7 +361,8 @@
|
|||||||
//! # &mut client_rng,
|
//! # &mut client_rng,
|
||||||
//! # b"password",
|
//! # b"password",
|
||||||
//! # )?;
|
//! # )?;
|
||||||
//! use opaque_ke::{ServerLogin, ServerLoginStartParameters};
|
//! use opaque_ke::{ServerLogin, ServerLoginParameters};
|
||||||
|
//!
|
||||||
//! let password_file = ServerRegistration::<Default>::deserialize(&password_file_bytes)?;
|
//! let password_file = ServerRegistration::<Default>::deserialize(&password_file_bytes)?;
|
||||||
//! let mut server_rng = OsRng;
|
//! let mut server_rng = OsRng;
|
||||||
//! let server_login_start_result = ServerLogin::start(
|
//! let server_login_start_result = ServerLogin::start(
|
||||||
@@ -377,7 +371,7 @@
|
|||||||
//! Some(password_file),
|
//! Some(password_file),
|
||||||
//! client_login_start_result.message,
|
//! client_login_start_result.message,
|
||||||
//! b"[email protected]",
|
//! b"[email protected]",
|
||||||
//! ServerLoginStartParameters::default(),
|
//! ServerLoginParameters::default(),
|
||||||
//! )?;
|
//! )?;
|
||||||
//! # Ok::<(), ProtocolError>(())
|
//! # Ok::<(), ProtocolError>(())
|
||||||
//! ```
|
//! ```
|
||||||
@@ -405,7 +399,7 @@
|
|||||||
//! ```
|
//! ```
|
||||||
//! # use opaque_ke::{
|
//! # use opaque_ke::{
|
||||||
//! # errors::ProtocolError,
|
//! # errors::ProtocolError,
|
||||||
//! # ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, ClientLogin, ClientLoginFinishParameters, ServerLogin, ServerLoginStartParameters, CredentialFinalization, ServerSetup,
|
//! # ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, ClientLogin, ServerLogin, ServerLoginParameters, CredentialFinalization, ServerSetup,
|
||||||
//! # ksf::Identity,
|
//! # ksf::Identity,
|
||||||
//! # };
|
//! # };
|
||||||
//! # use opaque_ke::CipherSuite;
|
//! # use opaque_ke::CipherSuite;
|
||||||
@@ -413,15 +407,13 @@
|
|||||||
//! # #[cfg(feature = "ristretto255")]
|
//! # #[cfg(feature = "ristretto255")]
|
||||||
//! # impl CipherSuite for Default {
|
//! # impl CipherSuite for Default {
|
||||||
//! # type OprfCs = opaque_ke::Ristretto255;
|
//! # type OprfCs = opaque_ke::Ristretto255;
|
||||||
//! # type KeGroup = opaque_ke::Ristretto255;
|
//! # type KeyExchange = opaque_ke::TripleDh<opaque_ke::Ristretto255, sha2::Sha512>;
|
||||||
//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDh;
|
|
||||||
//! # type Ksf = opaque_ke::ksf::Identity;
|
//! # type Ksf = opaque_ke::ksf::Identity;
|
||||||
//! # }
|
//! # }
|
||||||
//! # #[cfg(not(feature = "ristretto255"))]
|
//! # #[cfg(not(feature = "ristretto255"))]
|
||||||
//! # impl CipherSuite for Default {
|
//! # impl CipherSuite for Default {
|
||||||
//! # type OprfCs = p256::NistP256;
|
//! # type OprfCs = p256::NistP256;
|
||||||
//! # type KeGroup = p256::NistP256;
|
//! # type KeyExchange = opaque_ke::TripleDh<p256::NistP256, sha2::Sha256>;
|
||||||
//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDh;
|
|
||||||
//! # type Ksf = opaque_ke::ksf::Identity;
|
//! # type Ksf = opaque_ke::ksf::Identity;
|
||||||
//! # }
|
//! # }
|
||||||
//! # use rand::{rngs::OsRng, RngCore};
|
//! # use rand::{rngs::OsRng, RngCore};
|
||||||
@@ -444,8 +436,11 @@
|
|||||||
//! # &password_file_bytes,
|
//! # &password_file_bytes,
|
||||||
//! # )?;
|
//! # )?;
|
||||||
//! # let server_login_start_result =
|
//! # let server_login_start_result =
|
||||||
//! # ServerLogin::start(&mut server_rng, &server_setup, Some(password_file), client_login_start_result.message, b"[email protected]", ServerLoginStartParameters::default())?;
|
//! # ServerLogin::start(&mut server_rng, &server_setup, Some(password_file), client_login_start_result.message, b"[email protected]", ServerLoginParameters::default())?;
|
||||||
|
//! use opaque_ke::ClientLoginFinishParameters;
|
||||||
|
//!
|
||||||
//! let client_login_finish_result = client_login_start_result.state.finish(
|
//! let client_login_finish_result = client_login_start_result.state.finish(
|
||||||
|
//! &mut client_rng,
|
||||||
//! b"password",
|
//! b"password",
|
||||||
//! server_login_start_result.message,
|
//! server_login_start_result.message,
|
||||||
//! ClientLoginFinishParameters::default(),
|
//! ClientLoginFinishParameters::default(),
|
||||||
@@ -461,7 +456,7 @@
|
|||||||
//! ```
|
//! ```
|
||||||
//! # use opaque_ke::{
|
//! # use opaque_ke::{
|
||||||
//! # errors::ProtocolError,
|
//! # errors::ProtocolError,
|
||||||
//! # ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, ClientLogin, ClientLoginFinishParameters, ServerLogin, ServerLoginStartParameters, CredentialFinalization, ServerSetup,
|
//! # ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, ClientLogin, ClientLoginFinishParameters, ServerLogin, ServerLoginParameters, CredentialFinalization, ServerSetup,
|
||||||
//! # ksf::Identity,
|
//! # ksf::Identity,
|
||||||
//! # };
|
//! # };
|
||||||
//! # use opaque_ke::CipherSuite;
|
//! # use opaque_ke::CipherSuite;
|
||||||
@@ -469,15 +464,13 @@
|
|||||||
//! # #[cfg(feature = "ristretto255")]
|
//! # #[cfg(feature = "ristretto255")]
|
||||||
//! # impl CipherSuite for Default {
|
//! # impl CipherSuite for Default {
|
||||||
//! # type OprfCs = opaque_ke::Ristretto255;
|
//! # type OprfCs = opaque_ke::Ristretto255;
|
||||||
//! # type KeGroup = opaque_ke::Ristretto255;
|
//! # type KeyExchange = opaque_ke::TripleDh<opaque_ke::Ristretto255, sha2::Sha512>;
|
||||||
//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDh;
|
|
||||||
//! # type Ksf = opaque_ke::ksf::Identity;
|
//! # type Ksf = opaque_ke::ksf::Identity;
|
||||||
//! # }
|
//! # }
|
||||||
//! # #[cfg(not(feature = "ristretto255"))]
|
//! # #[cfg(not(feature = "ristretto255"))]
|
||||||
//! # impl CipherSuite for Default {
|
//! # impl CipherSuite for Default {
|
||||||
//! # type OprfCs = p256::NistP256;
|
//! # type OprfCs = p256::NistP256;
|
||||||
//! # type KeGroup = p256::NistP256;
|
//! # type KeyExchange = opaque_ke::TripleDh<p256::NistP256, sha2::Sha256>;
|
||||||
//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDh;
|
|
||||||
//! # type Ksf = opaque_ke::ksf::Identity;
|
//! # type Ksf = opaque_ke::ksf::Identity;
|
||||||
//! # }
|
//! # }
|
||||||
//! # use rand::{rngs::OsRng, RngCore};
|
//! # use rand::{rngs::OsRng, RngCore};
|
||||||
@@ -500,14 +493,16 @@
|
|||||||
//! # &password_file_bytes,
|
//! # &password_file_bytes,
|
||||||
//! # )?;
|
//! # )?;
|
||||||
//! # let server_login_start_result =
|
//! # let server_login_start_result =
|
||||||
//! # ServerLogin::start(&mut server_rng, &server_setup, Some(password_file), client_login_start_result.message, b"[email protected]", ServerLoginStartParameters::default())?;
|
//! # ServerLogin::start(&mut server_rng, &server_setup, Some(password_file), client_login_start_result.message, b"[email protected]", ServerLoginParameters::default())?;
|
||||||
//! # let client_login_finish_result = client_login_start_result.state.finish(
|
//! # let client_login_finish_result = client_login_start_result.state.finish(
|
||||||
|
//! # &mut client_rng,
|
||||||
//! # b"password",
|
//! # b"password",
|
||||||
//! # server_login_start_result.message,
|
//! # server_login_start_result.message,
|
||||||
//! # ClientLoginFinishParameters::default(),
|
//! # ClientLoginFinishParameters::default(),
|
||||||
//! # )?;
|
//! # )?;
|
||||||
//! let server_login_finish_result = server_login_start_result.state.finish(
|
//! let server_login_finish_result = server_login_start_result.state.finish(
|
||||||
//! client_login_finish_result.message,
|
//! client_login_finish_result.message,
|
||||||
|
//! ServerLoginParameters::default(),
|
||||||
//! )?;
|
//! )?;
|
||||||
//!
|
//!
|
||||||
//! assert_eq!(
|
//! assert_eq!(
|
||||||
@@ -558,7 +553,7 @@
|
|||||||
//! ```
|
//! ```
|
||||||
//! # use opaque_ke::{
|
//! # use opaque_ke::{
|
||||||
//! # errors::ProtocolError,
|
//! # errors::ProtocolError,
|
||||||
//! # ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, ClientLogin, ClientLoginFinishParameters, ServerLogin, ServerLoginStartParameters, CredentialFinalization, ServerSetup,
|
//! # ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, ClientLogin, ClientLoginFinishParameters, ServerLogin, ServerLoginParameters, CredentialFinalization, ServerSetup,
|
||||||
//! # ksf::Identity,
|
//! # ksf::Identity,
|
||||||
//! # };
|
//! # };
|
||||||
//! # use opaque_ke::CipherSuite;
|
//! # use opaque_ke::CipherSuite;
|
||||||
@@ -566,15 +561,13 @@
|
|||||||
//! # #[cfg(feature = "ristretto255")]
|
//! # #[cfg(feature = "ristretto255")]
|
||||||
//! # impl CipherSuite for Default {
|
//! # impl CipherSuite for Default {
|
||||||
//! # type OprfCs = opaque_ke::Ristretto255;
|
//! # type OprfCs = opaque_ke::Ristretto255;
|
||||||
//! # type KeGroup = opaque_ke::Ristretto255;
|
//! # type KeyExchange = opaque_ke::TripleDh<opaque_ke::Ristretto255, sha2::Sha512>;
|
||||||
//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDh;
|
|
||||||
//! # type Ksf = opaque_ke::ksf::Identity;
|
//! # type Ksf = opaque_ke::ksf::Identity;
|
||||||
//! # }
|
//! # }
|
||||||
//! # #[cfg(not(feature = "ristretto255"))]
|
//! # #[cfg(not(feature = "ristretto255"))]
|
||||||
//! # impl CipherSuite for Default {
|
//! # impl CipherSuite for Default {
|
||||||
//! # type OprfCs = p256::NistP256;
|
//! # type OprfCs = p256::NistP256;
|
||||||
//! # type KeGroup = p256::NistP256;
|
//! # type KeyExchange = opaque_ke::TripleDh<p256::NistP256, sha2::Sha256>;
|
||||||
//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDh;
|
|
||||||
//! # type Ksf = opaque_ke::ksf::Identity;
|
//! # type Ksf = opaque_ke::ksf::Identity;
|
||||||
//! # }
|
//! # }
|
||||||
//! # use rand::{rngs::OsRng, RngCore};
|
//! # use rand::{rngs::OsRng, RngCore};
|
||||||
@@ -604,10 +597,11 @@
|
|||||||
//! # &password_file_bytes,
|
//! # &password_file_bytes,
|
||||||
//! # )?;
|
//! # )?;
|
||||||
//! # let server_login_start_result =
|
//! # let server_login_start_result =
|
||||||
//! # ServerLogin::start(&mut server_rng, &server_setup, Some(password_file), client_login_start_result.message, b"[email protected]", ServerLoginStartParameters::default())?;
|
//! # ServerLogin::start(&mut server_rng, &server_setup, Some(password_file), client_login_start_result.message, b"[email protected]", ServerLoginParameters::default())?;
|
||||||
//!
|
//!
|
||||||
//! // And then later, during login...
|
//! // And then later, during login...
|
||||||
//! let client_login_finish_result = client_login_start_result.state.finish(
|
//! let client_login_finish_result = client_login_start_result.state.finish(
|
||||||
|
//! &mut client_rng,
|
||||||
//! b"password",
|
//! b"password",
|
||||||
//! server_login_start_result.message,
|
//! server_login_start_result.message,
|
||||||
//! ClientLoginFinishParameters::default(),
|
//! ClientLoginFinishParameters::default(),
|
||||||
@@ -656,7 +650,7 @@
|
|||||||
//! ```
|
//! ```
|
||||||
//! # use opaque_ke::{
|
//! # use opaque_ke::{
|
||||||
//! # errors::ProtocolError,
|
//! # errors::ProtocolError,
|
||||||
//! # ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, ClientLogin, ClientLoginFinishParameters, ServerLogin, ServerLoginStartParameters, CredentialFinalization, ServerSetup,
|
//! # ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, ClientLogin, ClientLoginFinishParameters, ServerLogin, ServerLoginParameters, CredentialFinalization, ServerSetup,
|
||||||
//! # ksf::Identity,
|
//! # ksf::Identity,
|
||||||
//! # };
|
//! # };
|
||||||
//! # use opaque_ke::CipherSuite;
|
//! # use opaque_ke::CipherSuite;
|
||||||
@@ -664,15 +658,13 @@
|
|||||||
//! # #[cfg(feature = "ristretto255")]
|
//! # #[cfg(feature = "ristretto255")]
|
||||||
//! # impl CipherSuite for Default {
|
//! # impl CipherSuite for Default {
|
||||||
//! # type OprfCs = opaque_ke::Ristretto255;
|
//! # type OprfCs = opaque_ke::Ristretto255;
|
||||||
//! # type KeGroup = opaque_ke::Ristretto255;
|
//! # type KeyExchange = opaque_ke::TripleDh<opaque_ke::Ristretto255, sha2::Sha512>;
|
||||||
//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDh;
|
|
||||||
//! # type Ksf = opaque_ke::ksf::Identity;
|
//! # type Ksf = opaque_ke::ksf::Identity;
|
||||||
//! # }
|
//! # }
|
||||||
//! # #[cfg(not(feature = "ristretto255"))]
|
//! # #[cfg(not(feature = "ristretto255"))]
|
||||||
//! # impl CipherSuite for Default {
|
//! # impl CipherSuite for Default {
|
||||||
//! # type OprfCs = p256::NistP256;
|
//! # type OprfCs = p256::NistP256;
|
||||||
//! # type KeGroup = p256::NistP256;
|
//! # type KeyExchange = opaque_ke::TripleDh<p256::NistP256, sha2::Sha256>;
|
||||||
//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDh;
|
|
||||||
//! # type Ksf = opaque_ke::ksf::Identity;
|
//! # type Ksf = opaque_ke::ksf::Identity;
|
||||||
//! # }
|
//! # }
|
||||||
//! # use rand::{rngs::OsRng, RngCore};
|
//! # use rand::{rngs::OsRng, RngCore};
|
||||||
@@ -701,10 +693,11 @@
|
|||||||
//! # &password_file_bytes,
|
//! # &password_file_bytes,
|
||||||
//! # )?;
|
//! # )?;
|
||||||
//! # let server_login_start_result =
|
//! # let server_login_start_result =
|
||||||
//! # ServerLogin::start(&mut server_rng, &server_setup, Some(password_file), client_login_start_result.message, b"[email protected]", ServerLoginStartParameters::default())?;
|
//! # ServerLogin::start(&mut server_rng, &server_setup, Some(password_file), client_login_start_result.message, b"[email protected]", ServerLoginParameters::default())?;
|
||||||
//!
|
//!
|
||||||
//! // And then later, during login...
|
//! // And then later, during login...
|
||||||
//! let client_login_finish_result = client_login_start_result.state.finish(
|
//! let client_login_finish_result = client_login_start_result.state.finish(
|
||||||
|
//! &mut client_rng,
|
||||||
//! b"password",
|
//! b"password",
|
||||||
//! server_login_start_result.message,
|
//! server_login_start_result.message,
|
||||||
//! ClientLoginFinishParameters::default(),
|
//! ClientLoginFinishParameters::default(),
|
||||||
@@ -745,15 +738,13 @@
|
|||||||
//! # #[cfg(feature = "ristretto255")]
|
//! # #[cfg(feature = "ristretto255")]
|
||||||
//! # impl CipherSuite for Default {
|
//! # impl CipherSuite for Default {
|
||||||
//! # type OprfCs = opaque_ke::Ristretto255;
|
//! # type OprfCs = opaque_ke::Ristretto255;
|
||||||
//! # type KeGroup = opaque_ke::Ristretto255;
|
//! # type KeyExchange = opaque_ke::TripleDh<opaque_ke::Ristretto255, sha2::Sha512>;
|
||||||
//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDh;
|
|
||||||
//! # type Ksf = opaque_ke::ksf::Identity;
|
//! # type Ksf = opaque_ke::ksf::Identity;
|
||||||
//! # }
|
//! # }
|
||||||
//! # #[cfg(not(feature = "ristretto255"))]
|
//! # #[cfg(not(feature = "ristretto255"))]
|
||||||
//! # impl CipherSuite for Default {
|
//! # impl CipherSuite for Default {
|
||||||
//! # type OprfCs = p256::NistP256;
|
//! # type OprfCs = p256::NistP256;
|
||||||
//! # type KeGroup = p256::NistP256;
|
//! # type KeyExchange = opaque_ke::TripleDh<p256::NistP256, sha2::Sha256>;
|
||||||
//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDh;
|
|
||||||
//! # type Ksf = opaque_ke::ksf::Identity;
|
//! # type Ksf = opaque_ke::ksf::Identity;
|
||||||
//! # }
|
//! # }
|
||||||
//! # use rand::{rngs::OsRng, RngCore};
|
//! # use rand::{rngs::OsRng, RngCore};
|
||||||
@@ -780,8 +771,8 @@
|
|||||||
//! # Ok::<(), ProtocolError>(())
|
//! # Ok::<(), ProtocolError>(())
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! The same identifiers must also be supplied using
|
//! The same identifiers must also be supplied using [`ServerLoginParameters`]
|
||||||
//! [`ServerLoginStartParameters`] in [Server Login Start](#server-login-start):
|
//! in [Server Login Start](#server-login-start):
|
||||||
//! ```
|
//! ```
|
||||||
//! # use opaque_ke::{
|
//! # use opaque_ke::{
|
||||||
//! # errors::ProtocolError,
|
//! # errors::ProtocolError,
|
||||||
@@ -793,15 +784,13 @@
|
|||||||
//! # #[cfg(feature = "ristretto255")]
|
//! # #[cfg(feature = "ristretto255")]
|
||||||
//! # impl CipherSuite for Default {
|
//! # impl CipherSuite for Default {
|
||||||
//! # type OprfCs = opaque_ke::Ristretto255;
|
//! # type OprfCs = opaque_ke::Ristretto255;
|
||||||
//! # type KeGroup = opaque_ke::Ristretto255;
|
//! # type KeyExchange = opaque_ke::TripleDh<opaque_ke::Ristretto255, sha2::Sha512>;
|
||||||
//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDh;
|
|
||||||
//! # type Ksf = opaque_ke::ksf::Identity;
|
//! # type Ksf = opaque_ke::ksf::Identity;
|
||||||
//! # }
|
//! # }
|
||||||
//! # #[cfg(not(feature = "ristretto255"))]
|
//! # #[cfg(not(feature = "ristretto255"))]
|
||||||
//! # impl CipherSuite for Default {
|
//! # impl CipherSuite for Default {
|
||||||
//! # type OprfCs = p256::NistP256;
|
//! # type OprfCs = p256::NistP256;
|
||||||
//! # type KeGroup = p256::NistP256;
|
//! # type KeyExchange = opaque_ke::TripleDh<p256::NistP256, sha2::Sha256>;
|
||||||
//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDh;
|
|
||||||
//! # type Ksf = opaque_ke::ksf::Identity;
|
//! # type Ksf = opaque_ke::ksf::Identity;
|
||||||
//! # }
|
//! # }
|
||||||
//! # use rand::{rngs::OsRng, RngCore};
|
//! # use rand::{rngs::OsRng, RngCore};
|
||||||
@@ -819,7 +808,7 @@
|
|||||||
//! # &mut client_rng,
|
//! # &mut client_rng,
|
||||||
//! # b"password",
|
//! # b"password",
|
||||||
//! # )?;
|
//! # )?;
|
||||||
//! # use opaque_ke::{ServerLogin, ServerLoginStartParameters};
|
//! # use opaque_ke::{ServerLogin, ServerLoginParameters};
|
||||||
//! # let password_file = ServerRegistration::<Default>::deserialize(&password_file_bytes)?;
|
//! # let password_file = ServerRegistration::<Default>::deserialize(&password_file_bytes)?;
|
||||||
//! # let mut server_rng = OsRng;
|
//! # let mut server_rng = OsRng;
|
||||||
//! let server_login_start_result = ServerLogin::start(
|
//! let server_login_start_result = ServerLogin::start(
|
||||||
@@ -828,7 +817,7 @@
|
|||||||
//! Some(password_file),
|
//! Some(password_file),
|
||||||
//! client_login_start_result.message,
|
//! client_login_start_result.message,
|
||||||
//! b"[email protected]",
|
//! b"[email protected]",
|
||||||
//! ServerLoginStartParameters {
|
//! ServerLoginParameters {
|
||||||
//! context: None,
|
//! context: None,
|
||||||
//! identifiers: Identifiers {
|
//! identifiers: Identifiers {
|
||||||
//! client: Some(b"Alice_the_Cryptographer"),
|
//! client: Some(b"Alice_the_Cryptographer"),
|
||||||
@@ -844,7 +833,7 @@
|
|||||||
//! ```
|
//! ```
|
||||||
//! # use opaque_ke::{
|
//! # use opaque_ke::{
|
||||||
//! # errors::ProtocolError,
|
//! # errors::ProtocolError,
|
||||||
//! # ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, ClientLogin, ClientLoginFinishParameters, Identifiers, ServerLogin, ServerLoginStartParameters, CredentialFinalization, ServerSetup,
|
//! # ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, ClientLogin, ClientLoginFinishParameters, Identifiers, ServerLogin, ServerLoginParameters, CredentialFinalization, ServerSetup,
|
||||||
//! # ksf::Identity,
|
//! # ksf::Identity,
|
||||||
//! # };
|
//! # };
|
||||||
//! # use opaque_ke::CipherSuite;
|
//! # use opaque_ke::CipherSuite;
|
||||||
@@ -852,15 +841,13 @@
|
|||||||
//! # #[cfg(feature = "ristretto255")]
|
//! # #[cfg(feature = "ristretto255")]
|
||||||
//! # impl CipherSuite for Default {
|
//! # impl CipherSuite for Default {
|
||||||
//! # type OprfCs = opaque_ke::Ristretto255;
|
//! # type OprfCs = opaque_ke::Ristretto255;
|
||||||
//! # type KeGroup = opaque_ke::Ristretto255;
|
//! # type KeyExchange = opaque_ke::TripleDh<opaque_ke::Ristretto255, sha2::Sha512>;
|
||||||
//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDh;
|
|
||||||
//! # type Ksf = opaque_ke::ksf::Identity;
|
//! # type Ksf = opaque_ke::ksf::Identity;
|
||||||
//! # }
|
//! # }
|
||||||
//! # #[cfg(not(feature = "ristretto255"))]
|
//! # #[cfg(not(feature = "ristretto255"))]
|
||||||
//! # impl CipherSuite for Default {
|
//! # impl CipherSuite for Default {
|
||||||
//! # type OprfCs = p256::NistP256;
|
//! # type OprfCs = p256::NistP256;
|
||||||
//! # type KeGroup = p256::NistP256;
|
//! # type KeyExchange = opaque_ke::TripleDh<p256::NistP256, sha2::Sha256>;
|
||||||
//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDh;
|
|
||||||
//! # type Ksf = opaque_ke::ksf::Identity;
|
//! # type Ksf = opaque_ke::ksf::Identity;
|
||||||
//! # }
|
//! # }
|
||||||
//! # use rand::{rngs::OsRng, RngCore};
|
//! # use rand::{rngs::OsRng, RngCore};
|
||||||
@@ -883,8 +870,9 @@
|
|||||||
//! # &password_file_bytes,
|
//! # &password_file_bytes,
|
||||||
//! # )?;
|
//! # )?;
|
||||||
//! # let server_login_start_result =
|
//! # let server_login_start_result =
|
||||||
//! # ServerLogin::start(&mut server_rng, &server_setup, Some(password_file), client_login_start_result.message, b"[email protected]", ServerLoginStartParameters { context: None, identifiers: Identifiers { client: Some(b"Alice_the_Cryptographer"), server: Some(b"Facebook") } })?;
|
//! # ServerLogin::start(&mut server_rng, &server_setup, Some(password_file), client_login_start_result.message, b"[email protected]", ServerLoginParameters { context: None, identifiers: Identifiers { client: Some(b"Alice_the_Cryptographer"), server: Some(b"Facebook") } })?;
|
||||||
//! let client_login_finish_result = client_login_start_result.state.finish(
|
//! let client_login_finish_result = client_login_start_result.state.finish(
|
||||||
|
//! &mut client_rng,
|
||||||
//! b"password",
|
//! b"password",
|
||||||
//! server_login_start_result.message,
|
//! server_login_start_result.message,
|
||||||
//! ClientLoginFinishParameters::new(
|
//! ClientLoginFinishParameters::new(
|
||||||
@@ -899,6 +887,62 @@
|
|||||||
//!
|
//!
|
||||||
//! # Ok::<(), ProtocolError>(())
|
//! # Ok::<(), ProtocolError>(())
|
||||||
//! ```
|
//! ```
|
||||||
|
//! and in [`ServerLoginParameters`] in [Server Login
|
||||||
|
//! Finish](#server-login-finish):
|
||||||
|
//! ```
|
||||||
|
//! # use opaque_ke::{
|
||||||
|
//! # errors::ProtocolError,
|
||||||
|
//! # ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, ClientLogin, ClientLoginFinishParameters, Identifiers, ServerLogin, ServerLoginParameters, CredentialFinalization, ServerSetup,
|
||||||
|
//! # ksf::Identity,
|
||||||
|
//! # };
|
||||||
|
//! # use opaque_ke::CipherSuite;
|
||||||
|
//! # struct Default;
|
||||||
|
//! # #[cfg(feature = "ristretto255")]
|
||||||
|
//! # impl CipherSuite for Default {
|
||||||
|
//! # type OprfCs = opaque_ke::Ristretto255;
|
||||||
|
//! # type KeyExchange = opaque_ke::TripleDh<opaque_ke::Ristretto255, sha2::Sha512>;
|
||||||
|
//! # type Ksf = opaque_ke::ksf::Identity;
|
||||||
|
//! # }
|
||||||
|
//! # #[cfg(not(feature = "ristretto255"))]
|
||||||
|
//! # impl CipherSuite for Default {
|
||||||
|
//! # type OprfCs = p256::NistP256;
|
||||||
|
//! # type KeyExchange = opaque_ke::TripleDh<p256::NistP256, sha2::Sha256>;
|
||||||
|
//! # type Ksf = opaque_ke::ksf::Identity;
|
||||||
|
//! # }
|
||||||
|
//! # use rand::{rngs::OsRng, RngCore};
|
||||||
|
//! # let mut client_rng = OsRng;
|
||||||
|
//! # let client_registration_start_result = ClientRegistration::<Default>::start(
|
||||||
|
//! # &mut client_rng,
|
||||||
|
//! # b"password",
|
||||||
|
//! # )?;
|
||||||
|
//! # let mut server_rng = OsRng;
|
||||||
|
//! # let server_setup = ServerSetup::<Default>::new(&mut server_rng);
|
||||||
|
//! # let server_registration_start_result = ServerRegistration::<Default>::start(&server_setup, client_registration_start_result.message, b"[email protected]")?;
|
||||||
|
//! # let client_registration_finish_result = client_registration_start_result.state.finish(&mut client_rng, b"password", server_registration_start_result.message, ClientRegistrationFinishParameters::new(Identifiers { client: Some(b"Alice_the_Cryptographer"), server: Some(b"Facebook") }, None))?;
|
||||||
|
//! # let password_file_bytes = ServerRegistration::<Default>::finish(client_registration_finish_result.message).serialize();
|
||||||
|
//! # let client_login_start_result = ClientLogin::<Default>::start(
|
||||||
|
//! # &mut client_rng,
|
||||||
|
//! # b"password",
|
||||||
|
//! # )?;
|
||||||
|
//! # let password_file =
|
||||||
|
//! # ServerRegistration::<Default>::deserialize(
|
||||||
|
//! # &password_file_bytes,
|
||||||
|
//! # )?;
|
||||||
|
//! # let server_login_start_result =
|
||||||
|
//! # ServerLogin::start(&mut server_rng, &server_setup, Some(password_file), client_login_start_result.message, b"[email protected]", ServerLoginParameters { context: None, identifiers: Identifiers { client: Some(b"Alice_the_Cryptographer"), server: Some(b"Facebook") } })?;
|
||||||
|
//! # let client_login_finish_result = client_login_start_result.state.finish(
|
||||||
|
//! # &mut client_rng,
|
||||||
|
//! # b"password",
|
||||||
|
//! # server_login_start_result.message,
|
||||||
|
//! # ClientLoginFinishParameters::new(None, Identifiers { client: Some(b"Alice_the_Cryptographer"), server: Some(b"Facebook") }, None),
|
||||||
|
//! # )?;
|
||||||
|
//! let server_login_finish_result = server_login_start_result.state.finish(
|
||||||
|
//! client_login_finish_result.message,
|
||||||
|
//! ServerLoginParameters { context: None, identifiers: Identifiers { client: Some(b"Alice_the_Cryptographer"), server: Some(b"Facebook") } },
|
||||||
|
//! )?;
|
||||||
|
//!
|
||||||
|
//! # Ok::<(), ProtocolError>(())
|
||||||
|
//! ```
|
||||||
//! Failing to supply the same pair of custom identifiers in any of the three
|
//! Failing to supply the same pair of custom identifiers in any of the three
|
||||||
//! steps above will result in an error in attempting to complete the protocol!
|
//! steps above will result in an error in attempting to complete the protocol!
|
||||||
//!
|
//!
|
||||||
@@ -912,16 +956,12 @@
|
|||||||
//! complete, so as to bind the integrity of application-specific data or
|
//! complete, so as to bind the integrity of application-specific data or
|
||||||
//! configuration parameters to the security of the key exchange. During the
|
//! configuration parameters to the security of the key exchange. During the
|
||||||
//! login phase, the client and server can specify this context using:
|
//! login phase, the client and server can specify this context using:
|
||||||
//! - The second login message, where the server can populate
|
//! - In [Server Login Start](#server-login-start), where the server can
|
||||||
//! [`ServerLoginStartParameters`], and
|
//! populate [`ServerLoginParameters::context`].
|
||||||
//! - The third login message, where the client can populate
|
//! - In [Client Login Finish](#client-login-finish), where the client can
|
||||||
//! [`ClientLoginFinishParameters`].
|
//! populate [`ClientLoginFinishParameters::context`].
|
||||||
//!
|
//! - In [Server Login Finish](#server-login-finish), where the server can
|
||||||
//! For both of these messages, the `WithContextAndIdentifiers` variant can be
|
//! populate [`ServerLoginParameters::context`].
|
||||||
//! used to specify these fields in addition to [custom
|
|
||||||
//! identifiers](#custom-identifiers), with the ordering of the fields as
|
|
||||||
//! `WithContextAndIdentifiers(context,
|
|
||||||
//! Identifiers::ClientAndServerIdentifiers(username, server_name))`.
|
|
||||||
//!
|
//!
|
||||||
//! ## Dummy Server Login
|
//! ## Dummy Server Login
|
||||||
//!
|
//!
|
||||||
@@ -940,59 +980,59 @@
|
|||||||
//! exposing the bytes of the private key to this library.
|
//! exposing the bytes of the private key to this library.
|
||||||
//! ```
|
//! ```
|
||||||
//! # use generic_array::{GenericArray, typenum::U0};
|
//! # use generic_array::{GenericArray, typenum::U0};
|
||||||
//! # use opaque_ke::{CipherSuite, ClientLogin, ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, errors::ProtocolError, keypair::PrivateKey, key_exchange::{group::KeGroup as OKeGroup, tripledh::DiffieHellman}};
|
//! # use opaque_ke::{CipherSuite, ClientLogin, ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, keypair::{PrivateKey, PublicKey}, key_exchange::{KeyExchange, group::Group, tripledh::DiffieHellman}};
|
||||||
//! # use rand::rngs::OsRng;
|
//! # use rand::rngs::OsRng;
|
||||||
|
//! # type Ristretto255 = <<Default as CipherSuite>::KeyExchange as KeyExchange>::Group;
|
||||||
//! # struct Default;
|
//! # struct Default;
|
||||||
//! # #[cfg(feature = "ristretto255")]
|
//! # #[cfg(feature = "ristretto255")]
|
||||||
//! # impl CipherSuite for Default {
|
//! # impl CipherSuite for Default {
|
||||||
//! # type OprfCs = opaque_ke::Ristretto255;
|
//! # type OprfCs = opaque_ke::Ristretto255;
|
||||||
//! # type KeGroup = opaque_ke::Ristretto255;
|
//! # type KeyExchange = opaque_ke::TripleDh<opaque_ke::Ristretto255, sha2::Sha512>;
|
||||||
//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDh;
|
|
||||||
//! # type Ksf = opaque_ke::ksf::Identity;
|
//! # type Ksf = opaque_ke::ksf::Identity;
|
||||||
//! # }
|
//! # }
|
||||||
//! # #[cfg(not(feature = "ristretto255"))]
|
//! # #[cfg(not(feature = "ristretto255"))]
|
||||||
//! # impl CipherSuite for Default {
|
//! # impl CipherSuite for Default {
|
||||||
//! # type OprfCs = p256::NistP256;
|
//! # type OprfCs = p256::NistP256;
|
||||||
//! # type KeGroup = p256::NistP256;
|
//! # type KeyExchange = opaque_ke::TripleDh<p256::NistP256, sha2::Sha256>;
|
||||||
//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDh;
|
|
||||||
//! # type Ksf = opaque_ke::ksf::Identity;
|
//! # type Ksf = opaque_ke::ksf::Identity;
|
||||||
//! # }
|
//! # }
|
||||||
//! # type KeGroup = <Default as CipherSuite>::KeGroup;
|
|
||||||
//! # #[derive(Debug, thiserror::Error)]
|
//! # #[derive(Debug, thiserror::Error)]
|
||||||
//! # #[error("test error")]
|
//! # #[error("test error")]
|
||||||
//! # struct YourRemoteKeyError;
|
//! # struct YourRemoteKeyError;
|
||||||
//! # #[derive(Clone)]
|
//! # #[derive(Clone)]
|
||||||
//! # struct YourRemoteKey(<KeGroup as OKeGroup>::Sk);
|
//! # struct YourRemoteKey(<Ristretto255 as Group>::Sk);
|
||||||
//! # impl YourRemoteKey {
|
//! # impl YourRemoteKey {
|
||||||
//! # fn diffie_hellman(&self, pk: &PublicKey<KeGroup>) -> Result<GenericArray<u8, <KeGroup as OKeGroup>::PkLen>, YourRemoteKeyError> {
|
//! # fn diffie_hellman(&self, pk: &PublicKey<Ristretto255>) -> Result<GenericArray<u8, <Ristretto255 as Group>::PkLen>, YourRemoteKeyError> {
|
||||||
//! # Ok(<<KeGroup as OKeGroup>::Sk as DiffieHellman<KeGroup>>::diffie_hellman(self.0, KeGroup::deserialize_pk(&pk.serialize()).unwrap()))
|
//! # Ok(<<Ristretto255 as Group>::Sk as DiffieHellman<Ristretto255>>::diffie_hellman(self.0, pk.to_group_type()))
|
||||||
//! # }
|
//! # }
|
||||||
//! # }
|
//! # }
|
||||||
//! use opaque_ke::{ServerLogin, ServerLoginStartParameters, ServerSetup};
|
//! use opaque_ke::{ServerLogin, ServerLoginParameters, ServerSetup};
|
||||||
//! use opaque_ke::keypair::{KeyPair, PrivateKeySerialization, PublicKey};
|
//! use opaque_ke::keypair::{KeyPair, PrivateKeySerialization};
|
||||||
|
//! use opaque_ke::errors::ProtocolError;
|
||||||
//!
|
//!
|
||||||
//! // Implement if you intend to use `ServerSetup::de/serialize` instead of `serde`.
|
//! // Implement if you intend to use `ServerSetup::de/serialize` instead of `serde`.
|
||||||
//! impl PrivateKeySerialization<KeGroup> for YourRemoteKey {
|
//! impl PrivateKeySerialization<Ristretto255> for YourRemoteKey {
|
||||||
//! type Error = YourRemoteKeyError;
|
//! type Error = YourRemoteKeyError;
|
||||||
//! type Len = U0;
|
//! type Len = U0;
|
||||||
//!
|
//!
|
||||||
//! fn serialize_key_pair(_: &KeyPair<KeGroup, Self>) -> GenericArray<u8, Self::Len> {
|
//! fn serialize_key_pair(_: &KeyPair<Ristretto255, Self>) -> GenericArray<u8, Self::Len> {
|
||||||
//! unimplemented!()
|
//! unimplemented!()
|
||||||
//! }
|
//! }
|
||||||
//!
|
//!
|
||||||
//! fn deserialize_key_pair(input: &[u8]) -> Result<KeyPair<KeGroup, Self>, ProtocolError<Self::Error>> {
|
//! fn deserialize_take_key_pair(input: &mut &[u8]) -> Result<KeyPair<Ristretto255, Self>, ProtocolError<Self::Error>> {
|
||||||
//! unimplemented!()
|
//! unimplemented!()
|
||||||
//! }
|
//! }
|
||||||
//! }
|
//! }
|
||||||
//!
|
//!
|
||||||
//! # let sk = KeGroup::random_sk(&mut OsRng);
|
//! # let sk = Ristretto255::random_sk(&mut OsRng);
|
||||||
//! # let pk = KeGroup::public_key(sk);
|
//! # let pk = Ristretto255::public_key(sk);
|
||||||
//! # let pk = KeGroup::serialize_pk(pk);
|
//! # let pk = Ristretto255::serialize_pk(pk);
|
||||||
//! # let public_key = PublicKey::deserialize(&pk).unwrap();
|
//! # let public_key = PublicKey::deserialize(&pk).unwrap();
|
||||||
//! # let remote_key = YourRemoteKey(sk);
|
//! # let remote_key = YourRemoteKey(sk);
|
||||||
//! # let mut server_rng = OsRng;
|
//! # let mut server_rng = OsRng;
|
||||||
//! let keypair = KeyPair::new(remote_key, public_key);
|
//! let keypair = KeyPair::new(remote_key, public_key);
|
||||||
//! let server_setup = ServerSetup::<Default, YourRemoteKey>::new_with_key_pair(&mut server_rng, keypair);
|
//! let server_setup = ServerSetup::<Default, YourRemoteKey>::new_with_key_pair(&mut server_rng, keypair);
|
||||||
|
//!
|
||||||
//! # let client_registration_start_result = ClientRegistration::<Default>::start(
|
//! # let client_registration_start_result = ClientRegistration::<Default>::start(
|
||||||
//! # &mut OsRng,
|
//! # &mut OsRng,
|
||||||
//! # b"password",
|
//! # b"password",
|
||||||
@@ -1004,17 +1044,152 @@
|
|||||||
//! # &mut OsRng,
|
//! # &mut OsRng,
|
||||||
//! # b"password",
|
//! # b"password",
|
||||||
//! # )?;
|
//! # )?;
|
||||||
//! let password_file = ServerRegistration::<Default>::deserialize(&password_file_bytes)?;
|
//! # let password_file = ServerRegistration::<Default>::deserialize(&password_file_bytes)?;
|
||||||
|
//! // Use `ServerLogin::builder()` instead of `ServerLogin::start()`.
|
||||||
//! let server_login_builder = ServerLogin::builder(
|
//! let server_login_builder = ServerLogin::builder(
|
||||||
//! &mut server_rng,
|
//! &mut server_rng,
|
||||||
//! &server_setup,
|
//! &server_setup,
|
||||||
//! Some(password_file),
|
//! Some(password_file),
|
||||||
//! client_login_start_result.message,
|
//! client_login_start_result.message,
|
||||||
//! b"[email protected]",
|
//! b"[email protected]",
|
||||||
//! ServerLoginStartParameters::default(),
|
//! ServerLoginParameters::default(),
|
||||||
//! )?;
|
//! )?;
|
||||||
|
//!
|
||||||
|
//! // Run Diffie-Hellman on your remote key.
|
||||||
//! let client_e_public_key = server_login_builder.data();
|
//! let client_e_public_key = server_login_builder.data();
|
||||||
//! let shared_secret = server_login_builder.private_key().diffie_hellman(&client_e_public_key)?;
|
//! let shared_secret = server_login_builder.private_key().diffie_hellman(&client_e_public_key)?;
|
||||||
|
//!
|
||||||
|
//! // Use the shared secret to build `ServerLogin`.
|
||||||
|
//! let server_login_start_result = server_login_builder.build(shared_secret)?;
|
||||||
|
//! # Ok::<(), anyhow::Error>(())
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! ## Remote OPRF Seeds
|
||||||
|
//!
|
||||||
|
//! In addition, the OPRF seed can be stored in an external location as well, by
|
||||||
|
//! using [`ServerRegistration::start_with_key_material()`] and
|
||||||
|
//! [`ServerLogin::builder_with_key_material()`] in combination with
|
||||||
|
//! [`ServerSetup::key_material_info()`].
|
||||||
|
//! ```
|
||||||
|
//! # use digest::Output;
|
||||||
|
//! # use generic_array::{GenericArray, typenum::U0};
|
||||||
|
//! # use hkdf::Hkdf;
|
||||||
|
//! # use opaque_ke::{CipherSuite, ClientLogin, ClientRegistration, ClientRegistrationFinishParameters, keypair::{PrivateKey, PublicKey}, key_exchange::{KeyExchange, group::Group, tripledh::DiffieHellman}};
|
||||||
|
//! # use rand::rngs::OsRng;
|
||||||
|
//! # use rand::RngCore;
|
||||||
|
//! # type Ristretto255 = <<Default as CipherSuite>::KeyExchange as KeyExchange>::Group;
|
||||||
|
//! # type Hash = <<Default as CipherSuite>::KeyExchange as KeyExchange>::Hash;
|
||||||
|
//! # type OprfGroup = <<Default as CipherSuite>::OprfCs as voprf::CipherSuite>::Group;
|
||||||
|
//! # struct Default;
|
||||||
|
//! # #[cfg(feature = "ristretto255")]
|
||||||
|
//! # impl CipherSuite for Default {
|
||||||
|
//! # type OprfCs = opaque_ke::Ristretto255;
|
||||||
|
//! # type KeyExchange = opaque_ke::TripleDh<opaque_ke::Ristretto255, sha2::Sha512>;
|
||||||
|
//! # type Ksf = opaque_ke::ksf::Identity;
|
||||||
|
//! # }
|
||||||
|
//! # #[cfg(not(feature = "ristretto255"))]
|
||||||
|
//! # impl CipherSuite for Default {
|
||||||
|
//! # type OprfCs = p256::NistP256;
|
||||||
|
//! # type KeyExchange = opaque_ke::TripleDh<p256::NistP256, sha2::Sha256>;
|
||||||
|
//! # type Ksf = opaque_ke::ksf::Identity;
|
||||||
|
//! # }
|
||||||
|
//! # #[derive(Debug, thiserror::Error)]
|
||||||
|
//! # #[error("test error")]
|
||||||
|
//! # struct YourRemoteSecretsError;
|
||||||
|
//! # #[derive(Clone)]
|
||||||
|
//! # struct YourRemoteSeed(Output<Hash>);
|
||||||
|
//! # impl YourRemoteSeed {
|
||||||
|
//! # fn hkdf(&self, info: &[&[u8]]) -> GenericArray<u8, <OprfGroup as voprf::Group>::ScalarLen> {
|
||||||
|
//! # let mut ikm = GenericArray::default();
|
||||||
|
//! # Hkdf::<Hash>::from_prk(&self.0)
|
||||||
|
//! # .unwrap()
|
||||||
|
//! # .expand_multi_info(info, &mut ikm)
|
||||||
|
//! # .unwrap();
|
||||||
|
//! # ikm
|
||||||
|
//! # }
|
||||||
|
//! # }
|
||||||
|
//! # #[derive(Clone)]
|
||||||
|
//! # struct YourRemoteKey(<Ristretto255 as Group>::Sk);
|
||||||
|
//! # impl YourRemoteKey {
|
||||||
|
//! # fn diffie_hellman(&self, pk: &PublicKey<Ristretto255>) -> Result<GenericArray<u8, <Ristretto255 as Group>::PkLen>, YourRemoteSecretsError> {
|
||||||
|
//! # Ok(<<Ristretto255 as Group>::Sk as DiffieHellman<Ristretto255>>::diffie_hellman(self.0, pk.to_group_type()))
|
||||||
|
//! # }
|
||||||
|
//! # }
|
||||||
|
//! use opaque_ke::{ServerLogin, ServerLoginParameters, ServerRegistration, ServerSetup};
|
||||||
|
//! use opaque_ke::keypair::{KeyPair, OprfSeedSerialization};
|
||||||
|
//! use opaque_ke::errors::ProtocolError;
|
||||||
|
//!
|
||||||
|
//! // Implement if you intend to use `ServerSetup::de/serialize` instead of `serde`.
|
||||||
|
//! impl OprfSeedSerialization<sha2::Sha512, YourRemoteSecretsError> for YourRemoteSeed {
|
||||||
|
//! type Len = U0;
|
||||||
|
//!
|
||||||
|
//! fn serialize(&self) -> GenericArray<u8, Self::Len> {
|
||||||
|
//! unimplemented!()
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! fn deserialize_take(input: &mut &[u8]) -> Result<YourRemoteSeed, ProtocolError<YourRemoteSecretsError>> {
|
||||||
|
//! unimplemented!()
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! # let mut oprf_seed = YourRemoteSeed(GenericArray::default());
|
||||||
|
//! # OsRng.fill_bytes(&mut oprf_seed.0);
|
||||||
|
//! # let sk = Ristretto255::random_sk(&mut OsRng);
|
||||||
|
//! # let pk = Ristretto255::public_key(sk);
|
||||||
|
//! # let pk = Ristretto255::serialize_pk(pk);
|
||||||
|
//! # let public_key = PublicKey::deserialize(&pk).unwrap();
|
||||||
|
//! # let remote_key = YourRemoteKey(sk);
|
||||||
|
//! # let mut server_rng = OsRng;
|
||||||
|
//! let keypair = KeyPair::new(remote_key, public_key);
|
||||||
|
//! let server_setup = ServerSetup::<Default, YourRemoteKey, YourRemoteSeed>::new_with_key_pair_and_seed(&mut server_rng, keypair, oprf_seed);
|
||||||
|
//!
|
||||||
|
//! // Incoming registration ...
|
||||||
|
//! # let client_registration_start_result = ClientRegistration::<Default>::start(
|
||||||
|
//! # &mut OsRng,
|
||||||
|
//! # b"password",
|
||||||
|
//! # )?;
|
||||||
|
//!
|
||||||
|
//! // Run HKDF on your remote OPRF seed.
|
||||||
|
//! let info = server_setup.key_material_info(b"[email protected]");
|
||||||
|
//! let key_material = info.ikm.hkdf(&info.info);
|
||||||
|
//!
|
||||||
|
//! // Use `ServerRegistration::start_with_key_material()` instead of `ServerRegistration::start()`.
|
||||||
|
//! let server_registration_start_result = ServerRegistration::<Default>::start_with_key_material(
|
||||||
|
//! &server_setup,
|
||||||
|
//! key_material,
|
||||||
|
//! client_registration_start_result.message,
|
||||||
|
//! )?;
|
||||||
|
//!
|
||||||
|
//! // Finish registration ...
|
||||||
|
//! # let client_registration_finish_result = client_registration_start_result.state.finish(&mut OsRng, b"password", server_registration_start_result.message, ClientRegistrationFinishParameters::default())?;
|
||||||
|
//!
|
||||||
|
//! // Incoming login ...
|
||||||
|
//! # let password_file_bytes = ServerRegistration::<Default>::finish(client_registration_finish_result.message).serialize();
|
||||||
|
//! # let client_login_start_result = ClientLogin::<Default>::start(
|
||||||
|
//! # &mut OsRng,
|
||||||
|
//! # b"password",
|
||||||
|
//! # )?;
|
||||||
|
//! # let password_file = ServerRegistration::<Default>::deserialize(&password_file_bytes)?;
|
||||||
|
//!
|
||||||
|
//! // Run HKDF on your remote OPRF seed.
|
||||||
|
//! let info = server_setup.key_material_info(b"[email protected]");
|
||||||
|
//! let key_material = info.ikm.hkdf(&info.info);
|
||||||
|
//!
|
||||||
|
//! // Use `ServerLogin::builder_with_key_material()` instead of `ServerLogin::start()`.
|
||||||
|
//! let server_login_builder = ServerLogin::builder_with_key_material(
|
||||||
|
//! &mut server_rng,
|
||||||
|
//! &server_setup,
|
||||||
|
//! key_material,
|
||||||
|
//! Some(password_file),
|
||||||
|
//! client_login_start_result.message,
|
||||||
|
//! ServerLoginParameters::default(),
|
||||||
|
//! )?;
|
||||||
|
//!
|
||||||
|
//! // Run Diffie-Hellman on your remote key.
|
||||||
|
//! let client_e_public_key = server_login_builder.data();
|
||||||
|
//! let shared_secret = server_login_builder.private_key().diffie_hellman(&client_e_public_key)?;
|
||||||
|
//!
|
||||||
|
//! // Use the shared secret to build `ServerLogin`.
|
||||||
//! let server_login_start_result = server_login_builder.build(shared_secret)?;
|
//! let server_login_start_result = server_login_builder.build(shared_secret)?;
|
||||||
//! # Ok::<(), anyhow::Error>(())
|
//! # Ok::<(), anyhow::Error>(())
|
||||||
//! ```
|
//! ```
|
||||||
@@ -1028,11 +1203,13 @@
|
|||||||
//! can be used.
|
//! can be used.
|
||||||
//! ```
|
//! ```
|
||||||
//! # use generic_array::GenericArray;
|
//! # use generic_array::GenericArray;
|
||||||
|
//! use opaque_ke::ksf::Ksf;
|
||||||
|
//!
|
||||||
//! #[derive(Default)]
|
//! #[derive(Default)]
|
||||||
//! struct CustomKsf(scrypt::Params);
|
//! struct CustomKsf(scrypt::Params);
|
||||||
//!
|
//!
|
||||||
//! // The Ksf trait must be implemented to be used in the ciphersuite.
|
//! // The Ksf trait must be implemented to be used in the ciphersuite.
|
||||||
//! impl opaque_ke::ksf::Ksf for CustomKsf {
|
//! impl Ksf for CustomKsf {
|
||||||
//! fn hash<L: generic_array::ArrayLength<u8>>(
|
//! fn hash<L: generic_array::ArrayLength<u8>>(
|
||||||
//! &self,
|
//! &self,
|
||||||
//! input: GenericArray<u8, L>,
|
//! input: GenericArray<u8, L>,
|
||||||
@@ -1064,15 +1241,13 @@
|
|||||||
//! # #[cfg(feature = "ristretto255")]
|
//! # #[cfg(feature = "ristretto255")]
|
||||||
//! # impl CipherSuite for DefaultCipherSuite {
|
//! # impl CipherSuite for DefaultCipherSuite {
|
||||||
//! # type OprfCs = opaque_ke::Ristretto255;
|
//! # type OprfCs = opaque_ke::Ristretto255;
|
||||||
//! # type KeGroup = opaque_ke::Ristretto255;
|
//! # type KeyExchange = opaque_ke::TripleDh<opaque_ke::Ristretto255, sha2::Sha512>;
|
||||||
//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDh;
|
|
||||||
//! # type Ksf = argon2::Argon2<'static>;
|
//! # type Ksf = argon2::Argon2<'static>;
|
||||||
//! # }
|
//! # }
|
||||||
//! # #[cfg(not(feature = "ristretto255"))]
|
//! # #[cfg(not(feature = "ristretto255"))]
|
||||||
//! # impl CipherSuite for DefaultCipherSuite {
|
//! # impl CipherSuite for DefaultCipherSuite {
|
||||||
//! # type OprfCs = p256::NistP256;
|
//! # type OprfCs = p256::NistP256;
|
||||||
//! # type KeGroup = p256::NistP256;
|
//! # type KeyExchange = opaque_ke::TripleDh<p256::NistP256, sha2::Sha256>;
|
||||||
//! # type KeyExchange = opaque_ke::key_exchange::tripledh::TripleDh;
|
|
||||||
//! # type Ksf = argon2::Argon2<'static>;
|
//! # type Ksf = argon2::Argon2<'static>;
|
||||||
//! # }
|
//! # }
|
||||||
//! #
|
//! #
|
||||||
@@ -1126,7 +1301,7 @@
|
|||||||
//! more computationally intensive the `Ksf` function is, the more resistant
|
//! more computationally intensive the `Ksf` function is, the more resistant
|
||||||
//! the server's password file records will be against offline dictionary and precomputation
|
//! the server's password file records will be against offline dictionary and precomputation
|
||||||
//! attacks; see [the OPAQUE paper](https://eprint.iacr.org/2018/163.pdf) for
|
//! attacks; see [the OPAQUE paper](https://eprint.iacr.org/2018/163.pdf) for
|
||||||
//! more details.
|
//! more details. The `argon2` feature requires [`alloc`].
|
||||||
//!
|
//!
|
||||||
//! - The `serde` feature, enabled by default, provides convenience functions for serializing and deserializing with [serde](https://serde.rs/).
|
//! - The `serde` feature, enabled by default, provides convenience functions for serializing and deserializing with [serde](https://serde.rs/).
|
||||||
//!
|
//!
|
||||||
@@ -1137,15 +1312,14 @@
|
|||||||
//! - The `curve25519` feature enables Curve25519 as a `KeGroup`. To select a
|
//! - The `curve25519` feature enables Curve25519 as a `KeGroup`. To select a
|
||||||
//! specific backend see the [curve25519-dalek] documentation.
|
//! specific backend see the [curve25519-dalek] documentation.
|
||||||
//!
|
//!
|
||||||
//! - The `p256` feature enables the use of [`p256::NistP256`] as a `KeGroup`
|
//! - The `ecdsa` feature enables using [`elliptic_curve`]s with [`Ecdsa`] for
|
||||||
//! and a `OprfCs` for `CipherSuite`.
|
//! [`SigmaI`]s signature algorithm.
|
||||||
//!
|
//!
|
||||||
//! - The `bench` feature is used only for running performance benchmarks for
|
//! - The `ed25519` feature enables using [`Ed25519`]s with [`PureEddsa`] and
|
||||||
//! this implementation.
|
//! [`HashEddsa`] for [`SigmaI`]s signature algorithm.
|
||||||
//!
|
//!
|
||||||
//! [curve25519-dalek]:
|
//! [`alloc`]: https://doc.rust-lang.org/alloc
|
||||||
//! (https://docs.rs/curve25519-dalek/4.0.0-pre.5/curve25519_dalek/index.html#backends)
|
//! [curve25519-dalek]: https://docs.rs/curve25519-dalek/4/curve25519_dalek/index.html#backends
|
||||||
//! [`p256::NistP256`]: https://docs.rs/p256/latest/p256/struct.NistP256.html
|
|
||||||
|
|
||||||
#![no_std]
|
#![no_std]
|
||||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||||
@@ -1169,7 +1343,6 @@ pub mod ksf;
|
|||||||
mod messages;
|
mod messages;
|
||||||
mod opaque;
|
mod opaque;
|
||||||
mod serialization;
|
mod serialization;
|
||||||
mod util;
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
@@ -1181,8 +1354,16 @@ pub use rand;
|
|||||||
|
|
||||||
#[cfg(feature = "curve25519")]
|
#[cfg(feature = "curve25519")]
|
||||||
pub use crate::key_exchange::group::curve25519::Curve25519;
|
pub use crate::key_exchange::group::curve25519::Curve25519;
|
||||||
|
#[cfg(feature = "ed25519")]
|
||||||
|
pub use crate::key_exchange::group::ed25519::Ed25519;
|
||||||
#[cfg(feature = "ristretto255")]
|
#[cfg(feature = "ristretto255")]
|
||||||
pub use crate::key_exchange::group::ristretto255::Ristretto255;
|
pub use crate::key_exchange::group::ristretto255::Ristretto255;
|
||||||
|
#[cfg(feature = "ecdsa")]
|
||||||
|
pub use crate::key_exchange::sigma_i::ecdsa::Ecdsa;
|
||||||
|
pub use crate::key_exchange::sigma_i::hash_eddsa::HashEddsa;
|
||||||
|
pub use crate::key_exchange::sigma_i::pure_eddsa::PureEddsa;
|
||||||
|
pub use crate::key_exchange::sigma_i::SigmaI;
|
||||||
|
pub use crate::key_exchange::tripledh::TripleDh;
|
||||||
pub use crate::messages::{
|
pub use crate::messages::{
|
||||||
CredentialFinalization, CredentialFinalizationLen, CredentialRequest, CredentialRequestLen,
|
CredentialFinalization, CredentialFinalizationLen, CredentialRequest, CredentialRequestLen,
|
||||||
CredentialResponse, CredentialResponseLen, RegistrationRequest, RegistrationRequestLen,
|
CredentialResponse, CredentialResponseLen, RegistrationRequest, RegistrationRequestLen,
|
||||||
@@ -1193,6 +1374,6 @@ pub use crate::opaque::{
|
|||||||
ClientLogin, ClientLoginFinishParameters, ClientLoginFinishResult, ClientLoginStartResult,
|
ClientLogin, ClientLoginFinishParameters, ClientLoginFinishResult, ClientLoginStartResult,
|
||||||
ClientRegistration, ClientRegistrationFinishParameters, ClientRegistrationFinishResult,
|
ClientRegistration, ClientRegistrationFinishParameters, ClientRegistrationFinishResult,
|
||||||
ClientRegistrationStartResult, Identifiers, KeyMaterialInfo, ServerLogin,
|
ClientRegistrationStartResult, Identifiers, KeyMaterialInfo, ServerLogin,
|
||||||
ServerLoginFinishResult, ServerLoginStartParameters, ServerLoginStartResult,
|
ServerLoginFinishResult, ServerLoginParameters, ServerLoginStartResult, ServerRegistration,
|
||||||
ServerRegistration, ServerRegistrationLen, ServerRegistrationStartResult, ServerSetup,
|
ServerRegistrationLen, ServerRegistrationStartResult, ServerSetup,
|
||||||
};
|
};
|
||||||
|
|||||||
+120
-170
@@ -16,24 +16,24 @@ use generic_array::sequence::Concat;
|
|||||||
use generic_array::typenum::{Sum, Unsigned};
|
use generic_array::typenum::{Sum, Unsigned};
|
||||||
use generic_array::{ArrayLength, GenericArray};
|
use generic_array::{ArrayLength, GenericArray};
|
||||||
use rand::{CryptoRng, RngCore};
|
use rand::{CryptoRng, RngCore};
|
||||||
use subtle::ConstantTimeEq;
|
use voprf::{BlindedElement, BlindedElementLen, EvaluationElement, EvaluationElementLen};
|
||||||
use voprf::Group;
|
|
||||||
use zeroize::Zeroizing;
|
use zeroize::Zeroizing;
|
||||||
|
|
||||||
use crate::ciphersuite::{CipherSuite, OprfGroup, OprfHash};
|
use crate::ciphersuite::{CipherSuite, KeGroup, OprfGroup, OprfHash};
|
||||||
use crate::envelope::{Envelope, EnvelopeLen};
|
use crate::envelope::{Envelope, EnvelopeLen};
|
||||||
use crate::errors::utils::{check_slice_size, check_slice_size_atleast};
|
|
||||||
use crate::errors::ProtocolError;
|
use crate::errors::ProtocolError;
|
||||||
use crate::hash::OutputSize;
|
use crate::hash::OutputSize;
|
||||||
use crate::key_exchange::group::KeGroup;
|
use crate::key_exchange::group::Group;
|
||||||
|
use crate::key_exchange::shared::NonceLen;
|
||||||
use crate::key_exchange::traits::{
|
use crate::key_exchange::traits::{
|
||||||
Deserialize, Ke1MessageLen, Ke2MessageLen, Ke3MessageLen, KeyExchange, Serialize,
|
CredentialRequestParts, CredentialResponseParts, Deserialize, Ke1MessageLen, Ke2MessageLen,
|
||||||
|
Ke3MessageLen, KeyExchange, Serialize,
|
||||||
};
|
};
|
||||||
use crate::key_exchange::tripledh::NonceLen;
|
|
||||||
use crate::keypair::PublicKey;
|
use crate::keypair::PublicKey;
|
||||||
use crate::opaque::{
|
use crate::opaque::{
|
||||||
MaskedResponse, MaskedResponseLen, ServerLogin, ServerLoginStartResult, ServerSetup,
|
MaskedResponse, MaskedResponseLen, ServerLogin, ServerLoginStartResult, ServerSetup,
|
||||||
};
|
};
|
||||||
|
use crate::serialization::SliceExt;
|
||||||
|
|
||||||
////////////////////////////
|
////////////////////////////
|
||||||
// High-level API Structs //
|
// High-level API Structs //
|
||||||
@@ -61,12 +61,12 @@ pub struct RegistrationRequest<CS: CipherSuite> {
|
|||||||
serde(bound = "")
|
serde(bound = "")
|
||||||
)]
|
)]
|
||||||
#[derive_where(Clone)]
|
#[derive_where(Clone)]
|
||||||
#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; voprf::EvaluationElement<CS::OprfCs>, <CS::KeGroup as KeGroup>::Pk)]
|
#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; voprf::EvaluationElement<CS::OprfCs>, <KeGroup<CS> as Group>::Pk)]
|
||||||
pub struct RegistrationResponse<CS: CipherSuite> {
|
pub struct RegistrationResponse<CS: CipherSuite> {
|
||||||
/// The server's oprf output
|
/// The server's oprf output
|
||||||
pub(crate) evaluation_element: voprf::EvaluationElement<CS::OprfCs>,
|
pub(crate) evaluation_element: voprf::EvaluationElement<CS::OprfCs>,
|
||||||
/// Server's static public key
|
/// Server's static public key
|
||||||
pub(crate) server_s_pk: PublicKey<CS::KeGroup>,
|
pub(crate) server_s_pk: PublicKey<KeGroup<CS>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The final message from the client, containing sealed cryptographic
|
/// The final message from the client, containing sealed cryptographic
|
||||||
@@ -77,7 +77,7 @@ pub struct RegistrationResponse<CS: CipherSuite> {
|
|||||||
serde(bound = "")
|
serde(bound = "")
|
||||||
)]
|
)]
|
||||||
#[derive_where(Clone, ZeroizeOnDrop)]
|
#[derive_where(Clone, ZeroizeOnDrop)]
|
||||||
#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; <CS::KeGroup as KeGroup>::Pk)]
|
#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; <KeGroup<CS> as Group>::Pk)]
|
||||||
pub struct RegistrationUpload<CS: CipherSuite> {
|
pub struct RegistrationUpload<CS: CipherSuite> {
|
||||||
/// The "envelope" generated by the user, containing sealed cryptographic
|
/// The "envelope" generated by the user, containing sealed cryptographic
|
||||||
/// identifiers
|
/// identifiers
|
||||||
@@ -85,7 +85,7 @@ pub struct RegistrationUpload<CS: CipherSuite> {
|
|||||||
/// The masking key used to mask the envelope
|
/// The masking key used to mask the envelope
|
||||||
pub(crate) masking_key: Output<OprfHash<CS>>,
|
pub(crate) masking_key: Output<OprfHash<CS>>,
|
||||||
/// The user's public key
|
/// The user's public key
|
||||||
pub(crate) client_s_pk: PublicKey<CS::KeGroup>,
|
pub(crate) client_s_pk: PublicKey<KeGroup<CS>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The message sent by the user to the server, to initiate registration
|
/// The message sent by the user to the server, to initiate registration
|
||||||
@@ -93,21 +93,19 @@ pub struct RegistrationUpload<CS: CipherSuite> {
|
|||||||
feature = "serde",
|
feature = "serde",
|
||||||
derive(serde::Deserialize, serde::Serialize),
|
derive(serde::Deserialize, serde::Serialize),
|
||||||
serde(bound(
|
serde(bound(
|
||||||
deserialize = "<CS::KeyExchange as KeyExchange<OprfHash<CS>, CS::KeGroup>>::KE1Message: \
|
deserialize = "<CS::KeyExchange as KeyExchange>::KE1Message: serde::Deserialize<'de>",
|
||||||
serde::Deserialize<'de>",
|
serialize = "<CS::KeyExchange as KeyExchange>::KE1Message: serde::Serialize"
|
||||||
serialize = "<CS::KeyExchange as KeyExchange<OprfHash<CS>, CS::KeGroup>>::KE1Message: \
|
|
||||||
serde::Serialize"
|
|
||||||
))
|
))
|
||||||
)]
|
)]
|
||||||
#[derive_where(Clone, ZeroizeOnDrop)]
|
#[derive_where(Clone, ZeroizeOnDrop)]
|
||||||
#[derive_where(
|
#[derive_where(
|
||||||
Debug, Eq, Hash, PartialEq;
|
Debug, Eq, Hash, PartialEq;
|
||||||
voprf::BlindedElement<CS::OprfCs>,
|
voprf::BlindedElement<CS::OprfCs>,
|
||||||
<CS::KeyExchange as KeyExchange<OprfHash<CS>, CS::KeGroup>>::KE1Message,
|
<CS::KeyExchange as KeyExchange>::KE1Message,
|
||||||
)]
|
)]
|
||||||
pub struct CredentialRequest<CS: CipherSuite> {
|
pub struct CredentialRequest<CS: CipherSuite> {
|
||||||
pub(crate) blinded_element: voprf::BlindedElement<CS::OprfCs>,
|
pub(crate) blinded_element: voprf::BlindedElement<CS::OprfCs>,
|
||||||
pub(crate) ke1_message: <CS::KeyExchange as KeyExchange<OprfHash<CS>, CS::KeGroup>>::KE1Message,
|
pub(crate) ke1_message: <CS::KeyExchange as KeyExchange>::KE1Message,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Builder for [`ServerLogin`](crate::ServerLogin) when using remote keys.
|
/// Builder for [`ServerLogin`](crate::ServerLogin) when using remote keys.
|
||||||
@@ -115,37 +113,36 @@ pub struct CredentialRequest<CS: CipherSuite> {
|
|||||||
feature = "serde",
|
feature = "serde",
|
||||||
derive(serde::Deserialize, serde::Serialize),
|
derive(serde::Deserialize, serde::Serialize),
|
||||||
serde(bound(
|
serde(bound(
|
||||||
deserialize = "SK: serde::Deserialize<'de>, <CS::KeyExchange as KeyExchange<OprfHash<CS>, \
|
deserialize = "SK: serde::Deserialize<'de>, <CS::KeyExchange as \
|
||||||
CS::KeGroup>>::KE2Builder: serde::Deserialize<'de>",
|
KeyExchange>::KE2Builder<'a, CS>: serde::Deserialize<'de>",
|
||||||
serialize = "SK: serde::Serialize, <CS::KeyExchange as KeyExchange<OprfHash<CS>, \
|
serialize = "SK: serde::Serialize, <CS::KeyExchange as KeyExchange>::KE2Builder<'a, CS>: \
|
||||||
CS::KeGroup>>::KE2Builder: serde::Serialize"
|
serde::Serialize"
|
||||||
))
|
))
|
||||||
)]
|
)]
|
||||||
#[derive_where(Clone)]
|
#[derive_where(Clone)]
|
||||||
#[derive_where(
|
#[derive_where(
|
||||||
Debug, Eq, PartialEq;
|
Debug, Eq, PartialEq;
|
||||||
|
<KeGroup<CS> as Group>::Pk,
|
||||||
SK,
|
SK,
|
||||||
voprf::EvaluationElement<CS::OprfCs>,
|
voprf::EvaluationElement<CS::OprfCs>,
|
||||||
<CS::KeyExchange as KeyExchange<OprfHash<CS>, CS::KeGroup>>::KE2Builder,
|
<CS::KeyExchange as KeyExchange>::KE2Builder<'a, CS>,
|
||||||
)]
|
)]
|
||||||
pub struct ServerLoginBuilder<CS: CipherSuite, SK: Clone> {
|
pub struct ServerLoginBuilder<'a, CS: CipherSuite, SK: Clone> {
|
||||||
pub(crate) server_s_sk: SK,
|
pub(crate) server_s_sk: SK,
|
||||||
pub(crate) evaluation_element: voprf::EvaluationElement<CS::OprfCs>,
|
pub(crate) evaluation_element: voprf::EvaluationElement<CS::OprfCs>,
|
||||||
pub(crate) masking_nonce: Zeroizing<GenericArray<u8, NonceLen>>,
|
pub(crate) masking_nonce: Zeroizing<GenericArray<u8, NonceLen>>,
|
||||||
pub(crate) masked_response: MaskedResponse<CS>,
|
pub(crate) masked_response: MaskedResponse<CS>,
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub(crate) oprf_key: Zeroizing<GenericArray<u8, <OprfGroup<CS> as Group>::ScalarLen>>,
|
pub(crate) oprf_key: Zeroizing<GenericArray<u8, <OprfGroup<CS> as voprf::Group>::ScalarLen>>,
|
||||||
pub(crate) ke2_builder: <CS::KeyExchange as KeyExchange<OprfHash<CS>, CS::KeGroup>>::KE2Builder,
|
pub(crate) ke2_builder: <CS::KeyExchange as KeyExchange>::KE2Builder<'a, CS>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<CS: CipherSuite, SK: Clone> ServerLoginBuilder<CS, SK> {
|
impl<CS: CipherSuite, SK: Clone> ServerLoginBuilder<'_, CS, SK> {
|
||||||
/// The returned data here has to be processed and the result given as an
|
/// The returned data here has to be processed and the result given as an
|
||||||
/// input to [`ServerLoginBuilder::build()`]. To understand what kind of
|
/// input to [`ServerLoginBuilder::build()`]. To understand what kind of
|
||||||
/// output is expected here and how to process it, refer to the
|
/// output is expected here and how to process it, refer to the
|
||||||
/// documentation of your chosen [`CipherSuite::KeyExchange`].
|
/// documentation of your chosen [`CipherSuite::KeyExchange`].
|
||||||
pub fn data(
|
pub fn data(&self) -> <CS::KeyExchange as KeyExchange>::KE2BuilderData<'_, CS> {
|
||||||
&self,
|
|
||||||
) -> <CS::KeyExchange as KeyExchange<OprfHash<CS>, CS::KeGroup>>::KE2BuilderData<'_> {
|
|
||||||
CS::KeyExchange::ke2_builder_data(&self.ke2_builder)
|
CS::KeyExchange::ke2_builder_data(&self.ke2_builder)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,7 +158,7 @@ impl<CS: CipherSuite, SK: Clone> ServerLoginBuilder<CS, SK> {
|
|||||||
/// See [`ServerLogin::start()`] for the regular path.
|
/// See [`ServerLogin::start()`] for the regular path.
|
||||||
pub fn build(
|
pub fn build(
|
||||||
self,
|
self,
|
||||||
input: <CS::KeyExchange as KeyExchange<OprfHash<CS>, CS::KeGroup>>::KE2BuilderInput,
|
input: <CS::KeyExchange as KeyExchange>::KE2BuilderInput<CS>,
|
||||||
) -> Result<ServerLoginStartResult<CS>, ProtocolError> {
|
) -> Result<ServerLoginStartResult<CS>, ProtocolError> {
|
||||||
ServerLogin::build(self, input)
|
ServerLogin::build(self, input)
|
||||||
}
|
}
|
||||||
@@ -173,24 +170,22 @@ impl<CS: CipherSuite, SK: Clone> ServerLoginBuilder<CS, SK> {
|
|||||||
feature = "serde",
|
feature = "serde",
|
||||||
derive(serde::Deserialize, serde::Serialize),
|
derive(serde::Deserialize, serde::Serialize),
|
||||||
serde(bound(
|
serde(bound(
|
||||||
deserialize = "<CS::KeyExchange as KeyExchange<OprfHash<CS>, CS::KeGroup>>::KE2Message: \
|
deserialize = "<CS::KeyExchange as KeyExchange>::KE2Message: serde::Deserialize<'de>",
|
||||||
serde::Deserialize<'de>",
|
serialize = "<CS::KeyExchange as KeyExchange>::KE2Message: serde::Serialize"
|
||||||
serialize = "<CS::KeyExchange as KeyExchange<OprfHash<CS>, CS::KeGroup>>::KE2Message: \
|
|
||||||
serde::Serialize"
|
|
||||||
))
|
))
|
||||||
)]
|
)]
|
||||||
#[derive_where(Clone)]
|
#[derive_where(Clone)]
|
||||||
#[derive_where(
|
#[derive_where(
|
||||||
Debug, Eq, Hash, PartialEq;
|
Debug, Eq, Hash, PartialEq;
|
||||||
voprf::EvaluationElement<CS::OprfCs>,
|
voprf::EvaluationElement<CS::OprfCs>,
|
||||||
<CS::KeyExchange as KeyExchange<OprfHash<CS>, CS::KeGroup>>::KE2Message,
|
<CS::KeyExchange as KeyExchange>::KE2Message,
|
||||||
)]
|
)]
|
||||||
pub struct CredentialResponse<CS: CipherSuite> {
|
pub struct CredentialResponse<CS: CipherSuite> {
|
||||||
/// the server's oprf output
|
/// the server's oprf output
|
||||||
pub(crate) evaluation_element: voprf::EvaluationElement<CS::OprfCs>,
|
pub(crate) evaluation_element: voprf::EvaluationElement<CS::OprfCs>,
|
||||||
pub(crate) masking_nonce: GenericArray<u8, NonceLen>,
|
pub(crate) masking_nonce: GenericArray<u8, NonceLen>,
|
||||||
pub(crate) masked_response: MaskedResponse<CS>,
|
pub(crate) masked_response: MaskedResponse<CS>,
|
||||||
pub(crate) ke2_message: <CS::KeyExchange as KeyExchange<OprfHash<CS>, CS::KeGroup>>::KE2Message,
|
pub(crate) ke2_message: <CS::KeyExchange as KeyExchange>::KE2Message,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The answer sent by the client to the server, upon reception of the sealed
|
/// The answer sent by the client to the server, upon reception of the sealed
|
||||||
@@ -199,19 +194,17 @@ pub struct CredentialResponse<CS: CipherSuite> {
|
|||||||
feature = "serde",
|
feature = "serde",
|
||||||
derive(serde::Deserialize, serde::Serialize),
|
derive(serde::Deserialize, serde::Serialize),
|
||||||
serde(bound(
|
serde(bound(
|
||||||
deserialize = "<CS::KeyExchange as KeyExchange<OprfHash<CS>, CS::KeGroup>>::KE3Message: \
|
deserialize = "<CS::KeyExchange as KeyExchange>::KE3Message: serde::Deserialize<'de>",
|
||||||
serde::Deserialize<'de>",
|
serialize = "<CS::KeyExchange as KeyExchange>::KE3Message: serde::Serialize"
|
||||||
serialize = "<CS::KeyExchange as KeyExchange<OprfHash<CS>, CS::KeGroup>>::KE3Message: \
|
|
||||||
serde::Serialize"
|
|
||||||
))
|
))
|
||||||
)]
|
)]
|
||||||
#[derive_where(Clone)]
|
#[derive_where(Clone)]
|
||||||
#[derive_where(
|
#[derive_where(
|
||||||
Debug, Eq, Hash, PartialEq;
|
Debug, Eq, Hash, PartialEq;
|
||||||
<CS::KeyExchange as KeyExchange<OprfHash<CS>, CS::KeGroup>>::KE3Message,
|
<CS::KeyExchange as KeyExchange>::KE3Message,
|
||||||
)]
|
)]
|
||||||
pub struct CredentialFinalization<CS: CipherSuite> {
|
pub struct CredentialFinalization<CS: CipherSuite> {
|
||||||
pub(crate) ke3_message: <CS::KeyExchange as KeyExchange<OprfHash<CS>, CS::KeGroup>>::KE3Message,
|
pub(crate) ke3_message: <CS::KeyExchange as KeyExchange>::KE3Message,
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////
|
////////////////////////////////
|
||||||
@@ -220,18 +213,18 @@ pub struct CredentialFinalization<CS: CipherSuite> {
|
|||||||
////////////////////////////////
|
////////////////////////////////
|
||||||
|
|
||||||
/// Length of [`RegistrationRequest`] in bytes for serialization.
|
/// Length of [`RegistrationRequest`] in bytes for serialization.
|
||||||
pub type RegistrationRequestLen<CS: CipherSuite> = <OprfGroup<CS> as Group>::ElemLen;
|
pub type RegistrationRequestLen<CS: CipherSuite> = <OprfGroup<CS> as voprf::Group>::ElemLen;
|
||||||
|
|
||||||
impl<CS: CipherSuite> RegistrationRequest<CS> {
|
impl<CS: CipherSuite> RegistrationRequest<CS> {
|
||||||
/// Only used for testing purposes
|
/// Only used for testing purposes
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub fn get_blinded_element_for_testing(&self) -> voprf::BlindedElement<CS::OprfCs> {
|
pub(crate) fn get_blinded_element_for_testing(&self) -> voprf::BlindedElement<CS::OprfCs> {
|
||||||
self.blinded_element.clone()
|
self.blinded_element.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Serialization into bytes
|
/// Serialization into bytes
|
||||||
pub fn serialize(&self) -> GenericArray<u8, RegistrationRequestLen<CS>> {
|
pub fn serialize(&self) -> GenericArray<u8, RegistrationRequestLen<CS>> {
|
||||||
<OprfGroup<CS> as Group>::serialize_elem(self.blinded_element.value())
|
<OprfGroup<CS> as voprf::Group>::serialize_elem(self.blinded_element.value())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Deserialization from bytes
|
/// Deserialization from bytes
|
||||||
@@ -244,40 +237,38 @@ impl<CS: CipherSuite> RegistrationRequest<CS> {
|
|||||||
|
|
||||||
/// Length of [`RegistrationResponse`] in bytes for serialization.
|
/// Length of [`RegistrationResponse`] in bytes for serialization.
|
||||||
pub type RegistrationResponseLen<CS: CipherSuite> =
|
pub type RegistrationResponseLen<CS: CipherSuite> =
|
||||||
Sum<<OprfGroup<CS> as Group>::ElemLen, <CS::KeGroup as KeGroup>::PkLen>;
|
Sum<<OprfGroup<CS> as voprf::Group>::ElemLen, <KeGroup<CS> as Group>::PkLen>;
|
||||||
|
|
||||||
impl<CS: CipherSuite> RegistrationResponse<CS> {
|
impl<CS: CipherSuite> RegistrationResponse<CS> {
|
||||||
/// Serialization into bytes
|
/// Serialization into bytes
|
||||||
pub fn serialize(&self) -> GenericArray<u8, RegistrationResponseLen<CS>>
|
pub fn serialize(&self) -> GenericArray<u8, RegistrationResponseLen<CS>>
|
||||||
where
|
where
|
||||||
// RegistrationResponse: KgPk + KePk
|
// RegistrationResponse: KgPk + KePk
|
||||||
<OprfGroup<CS> as Group>::ElemLen: Add<<CS::KeGroup as KeGroup>::PkLen>,
|
<OprfGroup<CS> as voprf::Group>::ElemLen: Add<<KeGroup<CS> as Group>::PkLen>,
|
||||||
RegistrationResponseLen<CS>: ArrayLength<u8>,
|
RegistrationResponseLen<CS>: ArrayLength<u8>,
|
||||||
{
|
{
|
||||||
<OprfGroup<CS> as Group>::serialize_elem(self.evaluation_element.value())
|
<OprfGroup<CS> as voprf::Group>::serialize_elem(self.evaluation_element.value())
|
||||||
.concat(self.server_s_pk.serialize())
|
.concat(self.server_s_pk.serialize())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Deserialization from bytes
|
/// Deserialization from bytes
|
||||||
pub fn deserialize(input: &[u8]) -> Result<Self, ProtocolError> {
|
pub fn deserialize(mut input: &[u8]) -> Result<Self, ProtocolError> {
|
||||||
let elem_len = <OprfGroup<CS> as Group>::ElemLen::USIZE;
|
let evaluation_element = EvaluationElement::deserialize(input)?;
|
||||||
let key_len = <CS::KeGroup as KeGroup>::PkLen::USIZE;
|
input = &input[EvaluationElementLen::<CS::OprfCs>::USIZE..];
|
||||||
let checked_slice =
|
|
||||||
check_slice_size(input, elem_len + key_len, "registration_response_bytes")?;
|
|
||||||
|
|
||||||
// Ensure that public key is valid
|
|
||||||
let server_s_pk = PublicKey::deserialize(&checked_slice[elem_len..])?;
|
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
evaluation_element: voprf::EvaluationElement::deserialize(&checked_slice[..elem_len])?,
|
evaluation_element,
|
||||||
server_s_pk,
|
server_s_pk: PublicKey::deserialize_take(&mut input)?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
/// Only used for tests, where we can set the beta value to test for the
|
/// Only used for tests, where we can set the beta value to test for the
|
||||||
/// reflection error case
|
/// reflection error case
|
||||||
pub fn set_evaluation_element_for_testing(&self, beta: <OprfGroup<CS> as Group>::Elem) -> Self {
|
pub(crate) fn set_evaluation_element_for_testing(
|
||||||
|
&self,
|
||||||
|
beta: <OprfGroup<CS> as voprf::Group>::Elem,
|
||||||
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
evaluation_element: voprf::EvaluationElement::from_value_unchecked(beta),
|
evaluation_element: voprf::EvaluationElement::from_value_unchecked(beta),
|
||||||
server_s_pk: self.server_s_pk.clone(),
|
server_s_pk: self.server_s_pk.clone(),
|
||||||
@@ -287,18 +278,15 @@ impl<CS: CipherSuite> RegistrationResponse<CS> {
|
|||||||
|
|
||||||
/// Length of [`RegistrationUpload`] in bytes for serialization.
|
/// Length of [`RegistrationUpload`] in bytes for serialization.
|
||||||
pub type RegistrationUploadLen<CS: CipherSuite> =
|
pub type RegistrationUploadLen<CS: CipherSuite> =
|
||||||
Sum<Sum<<CS::KeGroup as KeGroup>::PkLen, OutputSize<OprfHash<CS>>>, EnvelopeLen<CS>>;
|
Sum<Sum<<KeGroup<CS> as Group>::PkLen, OutputSize<OprfHash<CS>>>, EnvelopeLen<CS>>;
|
||||||
|
|
||||||
impl<CS: CipherSuite> RegistrationUpload<CS> {
|
impl<CS: CipherSuite> RegistrationUpload<CS> {
|
||||||
/// Serialization into bytes
|
/// Serialization into bytes
|
||||||
pub fn serialize(&self) -> GenericArray<u8, RegistrationUploadLen<CS>>
|
pub fn serialize(&self) -> GenericArray<u8, RegistrationUploadLen<CS>>
|
||||||
where
|
where
|
||||||
// Envelope: Nonce + Hash
|
|
||||||
NonceLen: Add<OutputSize<OprfHash<CS>>>,
|
|
||||||
EnvelopeLen<CS>: ArrayLength<u8>,
|
|
||||||
// RegistrationUpload: (KePk + Hash) + Envelope
|
// RegistrationUpload: (KePk + Hash) + Envelope
|
||||||
<CS::KeGroup as KeGroup>::PkLen: Add<OutputSize<OprfHash<CS>>>,
|
<KeGroup<CS> as Group>::PkLen: Add<OutputSize<OprfHash<CS>>>,
|
||||||
Sum<<CS::KeGroup as KeGroup>::PkLen, OutputSize<OprfHash<CS>>>:
|
Sum<<KeGroup<CS> as Group>::PkLen, OutputSize<OprfHash<CS>>>:
|
||||||
ArrayLength<u8> + Add<EnvelopeLen<CS>>,
|
ArrayLength<u8> + Add<EnvelopeLen<CS>>,
|
||||||
RegistrationUploadLen<CS>: ArrayLength<u8>,
|
RegistrationUploadLen<CS>: ArrayLength<u8>,
|
||||||
{
|
{
|
||||||
@@ -309,18 +297,11 @@ impl<CS: CipherSuite> RegistrationUpload<CS> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Deserialization from bytes
|
/// Deserialization from bytes
|
||||||
pub fn deserialize(input: &[u8]) -> Result<Self, ProtocolError> {
|
pub fn deserialize(mut input: &[u8]) -> Result<Self, ProtocolError> {
|
||||||
let key_len = <CS::KeGroup as KeGroup>::PkLen::USIZE;
|
|
||||||
let hash_len = OutputSize::<OprfHash<CS>>::USIZE;
|
|
||||||
let checked_slice =
|
|
||||||
check_slice_size_atleast(input, key_len + hash_len, "registration_upload_bytes")?;
|
|
||||||
let envelope = Envelope::<CS>::deserialize(&checked_slice[key_len + hash_len..])?;
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
envelope,
|
client_s_pk: PublicKey::deserialize_take(&mut input)?,
|
||||||
masking_key: GenericArray::clone_from_slice(
|
masking_key: input.take_array("masking key")?,
|
||||||
&checked_slice[key_len..key_len + hash_len],
|
envelope: Envelope::deserialize_take(&mut input)?,
|
||||||
),
|
|
||||||
client_s_pk: PublicKey::deserialize(&checked_slice[..key_len])?,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -342,57 +323,49 @@ impl<CS: CipherSuite> RegistrationUpload<CS> {
|
|||||||
|
|
||||||
/// Length of [`CredentialRequest`] in bytes for serialization.
|
/// Length of [`CredentialRequest`] in bytes for serialization.
|
||||||
pub type CredentialRequestLen<CS: CipherSuite> =
|
pub type CredentialRequestLen<CS: CipherSuite> =
|
||||||
Sum<<OprfGroup<CS> as Group>::ElemLen, Ke1MessageLen<CS>>;
|
Sum<<OprfGroup<CS> as voprf::Group>::ElemLen, Ke1MessageLen<CS>>;
|
||||||
|
|
||||||
impl<CS: CipherSuite> CredentialRequest<CS> {
|
impl<CS: CipherSuite> CredentialRequest<CS> {
|
||||||
/// Serialization into bytes
|
/// Serialization into bytes
|
||||||
pub fn serialize(&self) -> GenericArray<u8, CredentialRequestLen<CS>>
|
pub fn serialize(&self) -> GenericArray<u8, CredentialRequestLen<CS>>
|
||||||
where
|
where
|
||||||
|
<CS::KeyExchange as KeyExchange>::KE1Message: Serialize,
|
||||||
// CredentialRequest: KgPk + Ke1Message
|
// CredentialRequest: KgPk + Ke1Message
|
||||||
<OprfGroup<CS> as Group>::ElemLen: Add<Ke1MessageLen<CS>>,
|
<OprfGroup<CS> as voprf::Group>::ElemLen: Add<Ke1MessageLen<CS>>,
|
||||||
CredentialRequestLen<CS>: ArrayLength<u8>,
|
CredentialRequestLen<CS>: ArrayLength<u8>,
|
||||||
{
|
{
|
||||||
<OprfGroup<CS> as Group>::serialize_elem(self.blinded_element.value())
|
<OprfGroup<CS> as voprf::Group>::serialize_elem(self.blinded_element.value())
|
||||||
.concat(self.ke1_message.serialize())
|
.concat(self.ke1_message.serialize())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn serialize_iter<'a>(
|
/// Deserialization from bytes
|
||||||
blinded_element: &'a GenericArray<u8, <OprfGroup<CS> as Group>::ElemLen>,
|
pub fn deserialize(mut input: &[u8]) -> Result<Self, ProtocolError>
|
||||||
ke1_message: &'a GenericArray<u8, Ke1MessageLen<CS>>,
|
where
|
||||||
) -> impl Iterator<Item = &'a [u8]> {
|
<CS::KeyExchange as KeyExchange>::KE1Message: Deserialize,
|
||||||
[blinded_element.as_slice(), ke1_message].into_iter()
|
{
|
||||||
|
Self::deserialize_take(&mut input)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Deserialization from bytes
|
pub(crate) fn deserialize_take(input: &mut &[u8]) -> Result<Self, ProtocolError>
|
||||||
pub fn deserialize(input: &[u8]) -> Result<Self, ProtocolError> {
|
where
|
||||||
let elem_len = <OprfGroup<CS> as Group>::ElemLen::USIZE;
|
<CS::KeyExchange as KeyExchange>::KE1Message: Deserialize,
|
||||||
|
{
|
||||||
let checked_slice = check_slice_size_atleast(input, elem_len, "login_first_message_bytes")?;
|
let blinded_element = BlindedElement::deserialize(input)?;
|
||||||
|
*input = &input[BlindedElementLen::<CS::OprfCs>::USIZE..];
|
||||||
// Check that the message is actually containing an element of the correct
|
|
||||||
// subgroup
|
|
||||||
let blinded_element =
|
|
||||||
voprf::BlindedElement::<CS::OprfCs>::deserialize(&checked_slice[..elem_len])?;
|
|
||||||
|
|
||||||
// Throw an error if the identity group element is encountered
|
|
||||||
if bool::from(<OprfGroup<CS> as Group>::identity_elem().ct_eq(&blinded_element.value())) {
|
|
||||||
return Err(ProtocolError::IdentityGroupElementError);
|
|
||||||
}
|
|
||||||
|
|
||||||
let ke1_message =
|
|
||||||
<CS::KeyExchange as KeyExchange<OprfHash<CS>, CS::KeGroup>>::KE1Message::deserialize(
|
|
||||||
&checked_slice[elem_len..],
|
|
||||||
)?;
|
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
blinded_element,
|
blinded_element,
|
||||||
ke1_message,
|
ke1_message: <CS::KeyExchange as KeyExchange>::KE1Message::deserialize_take(input)?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn to_parts(&self) -> CredentialRequestParts<CS> {
|
||||||
|
CredentialRequestParts::new(&self.blinded_element)
|
||||||
|
}
|
||||||
|
|
||||||
/// Only used for testing purposes
|
/// Only used for testing purposes
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub fn get_blinded_element_for_testing(&self) -> voprf::BlindedElement<CS::OprfCs> {
|
pub(crate) fn get_blinded_element_for_testing(&self) -> voprf::BlindedElement<CS::OprfCs> {
|
||||||
self.blinded_element.clone()
|
self.blinded_element.clone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -402,90 +375,61 @@ pub type CredentialResponseLen<CS: CipherSuite> =
|
|||||||
Sum<CredentialResponseWithoutKeLen<CS>, Ke2MessageLen<CS>>;
|
Sum<CredentialResponseWithoutKeLen<CS>, Ke2MessageLen<CS>>;
|
||||||
|
|
||||||
pub(crate) type CredentialResponseWithoutKeLen<CS: CipherSuite> =
|
pub(crate) type CredentialResponseWithoutKeLen<CS: CipherSuite> =
|
||||||
Sum<Sum<<OprfGroup<CS> as Group>::ElemLen, NonceLen>, MaskedResponseLen<CS>>;
|
Sum<Sum<<OprfGroup<CS> as voprf::Group>::ElemLen, NonceLen>, MaskedResponseLen<CS>>;
|
||||||
|
|
||||||
impl<CS: CipherSuite> CredentialResponse<CS> {
|
impl<CS: CipherSuite> CredentialResponse<CS> {
|
||||||
/// Serialization into bytes
|
/// Serialization into bytes
|
||||||
pub fn serialize(&self) -> GenericArray<u8, CredentialResponseLen<CS>>
|
pub fn serialize(&self) -> GenericArray<u8, CredentialResponseLen<CS>>
|
||||||
where
|
where
|
||||||
|
<CS::KeyExchange as KeyExchange>::KE2Message: Serialize,
|
||||||
// CredentialResponseWithoutKeLen: (KgPk + Nonce) + MaskedResponse
|
// CredentialResponseWithoutKeLen: (KgPk + Nonce) + MaskedResponse
|
||||||
<OprfGroup<CS> as Group>::ElemLen: Add<NonceLen>,
|
<OprfGroup<CS> as voprf::Group>::ElemLen: Add<NonceLen>,
|
||||||
Sum<<OprfGroup<CS> as Group>::ElemLen, NonceLen>:
|
Sum<<OprfGroup<CS> as voprf::Group>::ElemLen, NonceLen>:
|
||||||
ArrayLength<u8> + Add<MaskedResponseLen<CS>>,
|
ArrayLength<u8> + Add<MaskedResponseLen<CS>>,
|
||||||
CredentialResponseWithoutKeLen<CS>: ArrayLength<u8>,
|
CredentialResponseWithoutKeLen<CS>: ArrayLength<u8>,
|
||||||
// MaskedResponse: (Nonce + Hash) + KePk
|
|
||||||
NonceLen: Add<OutputSize<OprfHash<CS>>>,
|
|
||||||
Sum<NonceLen, OutputSize<OprfHash<CS>>>:
|
|
||||||
ArrayLength<u8> + Add<<CS::KeGroup as KeGroup>::PkLen>,
|
|
||||||
MaskedResponseLen<CS>: ArrayLength<u8>,
|
|
||||||
// CredentialResponse: CredentialResponseWithoutKeLen + Ke2Message
|
// CredentialResponse: CredentialResponseWithoutKeLen + Ke2Message
|
||||||
CredentialResponseWithoutKeLen<CS>: Add<Ke2MessageLen<CS>>,
|
CredentialResponseWithoutKeLen<CS>: Add<Ke2MessageLen<CS>>,
|
||||||
CredentialResponseLen<CS>: ArrayLength<u8>,
|
CredentialResponseLen<CS>: ArrayLength<u8>,
|
||||||
{
|
{
|
||||||
<OprfGroup<CS> as Group>::serialize_elem(self.evaluation_element.value())
|
<OprfGroup<CS> as voprf::Group>::serialize_elem(self.evaluation_element.value())
|
||||||
.concat(self.masking_nonce)
|
.concat(self.masking_nonce)
|
||||||
.concat(self.masked_response.serialize())
|
.concat(self.masked_response.serialize())
|
||||||
.concat(self.ke2_message.serialize())
|
.concat(self.ke2_message.serialize())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn serialize_without_ke<'a>(
|
|
||||||
beta: &'a GenericArray<u8, <OprfGroup<CS> as Group>::ElemLen>,
|
|
||||||
masking_nonce: &'a GenericArray<u8, NonceLen>,
|
|
||||||
masked_response: &'a MaskedResponse<CS>,
|
|
||||||
) -> impl Iterator<Item = &'a [u8]> {
|
|
||||||
[beta.as_slice(), masking_nonce.as_slice()]
|
|
||||||
.into_iter()
|
|
||||||
.chain(masked_response.iter())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Deserialization from bytes
|
/// Deserialization from bytes
|
||||||
pub fn deserialize(input: &[u8]) -> Result<Self, ProtocolError> {
|
pub fn deserialize(mut input: &[u8]) -> Result<Self, ProtocolError>
|
||||||
let elem_len = <OprfGroup<CS> as Group>::ElemLen::USIZE;
|
where
|
||||||
let key_len = <CS::KeGroup as KeGroup>::PkLen::USIZE;
|
<CS::KeyExchange as KeyExchange>::KE2Message: Deserialize,
|
||||||
let nonce_len = NonceLen::USIZE;
|
{
|
||||||
let envelope_len = Envelope::<CS>::len();
|
let evaluation_element = EvaluationElement::deserialize(input)?;
|
||||||
let masked_response_len = key_len + envelope_len;
|
input = &input[voprf::EvaluationElementLen::<CS::OprfCs>::USIZE..];
|
||||||
let ke2_message_len = Ke2MessageLen::<CS>::USIZE;
|
|
||||||
|
|
||||||
let checked_slice = check_slice_size_atleast(
|
|
||||||
input,
|
|
||||||
elem_len + nonce_len + masked_response_len + ke2_message_len,
|
|
||||||
"credential_response_bytes",
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// Check that the message is actually containing an element of the correct
|
|
||||||
// subgroup
|
|
||||||
let beta_bytes = &checked_slice[..elem_len];
|
|
||||||
let evaluation_element = voprf::EvaluationElement::<CS::OprfCs>::deserialize(beta_bytes)?;
|
|
||||||
|
|
||||||
// Throw an error if the identity group element is encountered
|
|
||||||
if bool::from(<OprfGroup<CS> as Group>::identity_elem().ct_eq(&evaluation_element.value()))
|
|
||||||
{
|
|
||||||
return Err(ProtocolError::IdentityGroupElementError);
|
|
||||||
}
|
|
||||||
|
|
||||||
let masking_nonce =
|
|
||||||
GenericArray::clone_from_slice(&checked_slice[elem_len..elem_len + nonce_len]);
|
|
||||||
let masked_response = MaskedResponse::deserialize(
|
|
||||||
&checked_slice[elem_len + nonce_len..elem_len + nonce_len + masked_response_len],
|
|
||||||
);
|
|
||||||
let ke2_message =
|
|
||||||
<CS::KeyExchange as KeyExchange<OprfHash<CS>, CS::KeGroup>>::KE2Message::deserialize(
|
|
||||||
&checked_slice[elem_len + nonce_len + masked_response_len..],
|
|
||||||
)?;
|
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
evaluation_element,
|
evaluation_element,
|
||||||
masking_nonce,
|
masking_nonce: input.take_array("masking nonce")?,
|
||||||
masked_response,
|
masked_response: MaskedResponse::deserialize_take(&mut input)?,
|
||||||
ke2_message,
|
ke2_message: <CS::KeyExchange as KeyExchange>::KE2Message::deserialize_take(
|
||||||
|
&mut input,
|
||||||
|
)?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn to_parts(&self) -> CredentialResponseParts<CS> {
|
||||||
|
CredentialResponseParts::new(
|
||||||
|
&self.evaluation_element,
|
||||||
|
self.masking_nonce,
|
||||||
|
self.masked_response.clone(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
/// Only used for tests, where we can set the beta value to test for the
|
/// Only used for tests, where we can set the beta value to test for the
|
||||||
/// reflection error case
|
/// reflection error case
|
||||||
pub fn set_evaluation_element_for_testing(&self, beta: <OprfGroup<CS> as Group>::Elem) -> Self {
|
pub(crate) fn set_evaluation_element_for_testing(
|
||||||
|
&self,
|
||||||
|
beta: <OprfGroup<CS> as voprf::Group>::Elem,
|
||||||
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
evaluation_element: voprf::EvaluationElement::from_value_unchecked(beta),
|
evaluation_element: voprf::EvaluationElement::from_value_unchecked(beta),
|
||||||
masking_nonce: self.masking_nonce,
|
masking_nonce: self.masking_nonce,
|
||||||
@@ -500,16 +444,22 @@ pub type CredentialFinalizationLen<CS: CipherSuite> = Ke3MessageLen<CS>;
|
|||||||
|
|
||||||
impl<CS: CipherSuite> CredentialFinalization<CS> {
|
impl<CS: CipherSuite> CredentialFinalization<CS> {
|
||||||
/// Serialization into bytes
|
/// Serialization into bytes
|
||||||
pub fn serialize(&self) -> GenericArray<u8, CredentialFinalizationLen<CS>> {
|
pub fn serialize(&self) -> GenericArray<u8, CredentialFinalizationLen<CS>>
|
||||||
|
where
|
||||||
|
<CS::KeyExchange as KeyExchange>::KE3Message: Serialize,
|
||||||
|
{
|
||||||
self.ke3_message.serialize()
|
self.ke3_message.serialize()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Deserialization from bytes
|
/// Deserialization from bytes
|
||||||
pub fn deserialize(input: &[u8]) -> Result<Self, ProtocolError> {
|
pub fn deserialize(mut input: &[u8]) -> Result<Self, ProtocolError>
|
||||||
let ke3_message =
|
where
|
||||||
<CS::KeyExchange as KeyExchange<OprfHash<CS>, CS::KeGroup>>::KE3Message::deserialize(
|
<CS::KeyExchange as KeyExchange>::KE3Message: Deserialize,
|
||||||
input,
|
{
|
||||||
)?;
|
Ok(Self {
|
||||||
Ok(Self { ke3_message })
|
ke3_message: <CS::KeyExchange as KeyExchange>::KE3Message::deserialize_take(
|
||||||
|
&mut input,
|
||||||
|
)?,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+261
-300
@@ -11,30 +11,32 @@
|
|||||||
use core::ops::{Add, Deref};
|
use core::ops::{Add, Deref};
|
||||||
|
|
||||||
use derive_where::derive_where;
|
use derive_where::derive_where;
|
||||||
use digest::{Output, OutputSizeUser};
|
use digest::Output;
|
||||||
use generic_array::sequence::Concat;
|
use generic_array::sequence::Concat;
|
||||||
use generic_array::typenum::{Sum, Unsigned, U2};
|
use generic_array::typenum::{Sum, Unsigned};
|
||||||
use generic_array::{ArrayLength, GenericArray};
|
use generic_array::{ArrayLength, GenericArray};
|
||||||
use hkdf::{Hkdf, HkdfExtract};
|
use hkdf::{Hkdf, HkdfExtract};
|
||||||
use rand::{CryptoRng, RngCore};
|
use rand::{CryptoRng, RngCore};
|
||||||
use subtle::{Choice, ConstantTimeEq, CtOption};
|
use subtle::{Choice, ConstantTimeEq, CtOption};
|
||||||
use voprf::Group;
|
use voprf::{BlindedElement, Group as _, OprfClient, OprfClientLen};
|
||||||
use zeroize::Zeroizing;
|
use zeroize::Zeroizing;
|
||||||
|
|
||||||
use crate::ciphersuite::{CipherSuite, OprfGroup, OprfHash};
|
use crate::ciphersuite::{CipherSuite, KeGroup, KeHash, OprfGroup, OprfHash};
|
||||||
use crate::envelope::{Envelope, EnvelopeLen};
|
use crate::envelope::{Envelope, EnvelopeLen};
|
||||||
use crate::errors::utils::check_slice_size;
|
|
||||||
use crate::errors::{InternalError, ProtocolError};
|
use crate::errors::{InternalError, ProtocolError};
|
||||||
use crate::hash::OutputSize;
|
use crate::hash::OutputSize;
|
||||||
use crate::key_exchange::group::KeGroup;
|
use crate::key_exchange::group::Group;
|
||||||
|
use crate::key_exchange::shared::NonceLen;
|
||||||
use crate::key_exchange::traits::{
|
use crate::key_exchange::traits::{
|
||||||
Deserialize, Ke1MessageLen, Ke1StateLen, Ke2StateLen, KeyExchange, Serialize,
|
CredentialResponseParts, Deserialize, Ke1MessageLen, Ke1StateLen, Ke2StateLen, KeyExchange,
|
||||||
|
Serialize, SerializedContext, SerializedIdentifiers,
|
||||||
|
};
|
||||||
|
use crate::keypair::{
|
||||||
|
KeyPair, OprfSeed, OprfSeedSerialization, PrivateKey, PrivateKeySerialization, PublicKey,
|
||||||
};
|
};
|
||||||
use crate::key_exchange::tripledh::NonceLen;
|
|
||||||
use crate::keypair::{KeyPair, PrivateKey, PrivateKeySerialization, PublicKey};
|
|
||||||
use crate::ksf::Ksf;
|
use crate::ksf::Ksf;
|
||||||
use crate::messages::{CredentialRequestLen, RegistrationUploadLen};
|
use crate::messages::{CredentialRequestLen, RegistrationUploadLen};
|
||||||
use crate::serialization::Input;
|
use crate::serialization::{GenericArrayExt, SliceExt};
|
||||||
use crate::{
|
use crate::{
|
||||||
CredentialFinalization, CredentialRequest, CredentialResponse, RegistrationRequest,
|
CredentialFinalization, CredentialRequest, CredentialResponse, RegistrationRequest,
|
||||||
RegistrationResponse, RegistrationUpload, ServerLoginBuilder,
|
RegistrationResponse, RegistrationUpload, ServerLoginBuilder,
|
||||||
@@ -65,15 +67,15 @@ const STR_OPAQUE_DERIVE_KEY_PAIR: &[u8; 20] = b"OPAQUE-DeriveKeyPair";
|
|||||||
))
|
))
|
||||||
)]
|
)]
|
||||||
#[derive_where(Clone)]
|
#[derive_where(Clone)]
|
||||||
#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; <CS::KeGroup as KeGroup>::Pk, <CS::KeGroup as KeGroup>::Sk, SK, OS)]
|
#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; <KeGroup<CS> as Group>::Pk, <KeGroup<CS> as Group>::Sk, SK, OS)]
|
||||||
pub struct ServerSetup<
|
pub struct ServerSetup<
|
||||||
CS: CipherSuite,
|
CS: CipherSuite,
|
||||||
SK: Clone = PrivateKey<<CS as CipherSuite>::KeGroup>,
|
SK: Clone = PrivateKey<KeGroup<CS>>,
|
||||||
OS: Clone = Zeroizing<Output<OprfHash<CS>>>,
|
OS: Clone = OprfSeed<OprfHash<CS>>,
|
||||||
> {
|
> {
|
||||||
oprf_seed: OS,
|
oprf_seed: OS,
|
||||||
keypair: KeyPair<CS::KeGroup, SK>,
|
keypair: KeyPair<KeGroup<CS>, SK>,
|
||||||
pub(crate) fake_keypair: KeyPair<CS::KeGroup>,
|
pub(crate) fake_keypair: KeyPair<KeGroup<CS>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The state elements the client holds to register itself
|
/// The state elements the client holds to register itself
|
||||||
@@ -100,7 +102,7 @@ pub struct ClientRegistration<CS: CipherSuite> {
|
|||||||
serde(bound = "")
|
serde(bound = "")
|
||||||
)]
|
)]
|
||||||
#[derive_where(Clone, ZeroizeOnDrop)]
|
#[derive_where(Clone, ZeroizeOnDrop)]
|
||||||
#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; <CS::KeGroup as KeGroup>::Pk)]
|
#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; <KeGroup<CS> as Group>::Pk)]
|
||||||
pub struct ServerRegistration<CS: CipherSuite>(pub(crate) RegistrationUpload<CS>);
|
pub struct ServerRegistration<CS: CipherSuite>(pub(crate) RegistrationUpload<CS>);
|
||||||
|
|
||||||
/// The state elements the client holds to perform a login
|
/// The state elements the client holds to perform a login
|
||||||
@@ -108,24 +110,22 @@ pub struct ServerRegistration<CS: CipherSuite>(pub(crate) RegistrationUpload<CS>
|
|||||||
feature = "serde",
|
feature = "serde",
|
||||||
derive(serde::Deserialize, serde::Serialize),
|
derive(serde::Deserialize, serde::Serialize),
|
||||||
serde(bound(
|
serde(bound(
|
||||||
deserialize = "<CS::KeyExchange as KeyExchange<OprfHash<CS>, CS::KeGroup>>::KE1Message: \
|
deserialize = "<CS::KeyExchange as KeyExchange>::KE1Message: serde::Deserialize<'de>, \
|
||||||
serde::Deserialize<'de>, <CS::KeyExchange as KeyExchange<OprfHash<CS>, \
|
<CS::KeyExchange as KeyExchange>::KE1State: serde::Deserialize<'de>",
|
||||||
CS::KeGroup>>::KE1State: serde::Deserialize<'de>",
|
serialize = "<CS::KeyExchange as KeyExchange>::KE1Message: serde::Serialize, \
|
||||||
serialize = "<CS::KeyExchange as KeyExchange<OprfHash<CS>, CS::KeGroup>>::KE1Message: \
|
<CS::KeyExchange as KeyExchange>::KE1State: serde::Serialize"
|
||||||
serde::Serialize, <CS::KeyExchange as KeyExchange<OprfHash<CS>, \
|
|
||||||
CS::KeGroup>>::KE1State: serde::Serialize"
|
|
||||||
))
|
))
|
||||||
)]
|
)]
|
||||||
#[derive_where(Clone, ZeroizeOnDrop)]
|
#[derive_where(Clone, ZeroizeOnDrop)]
|
||||||
#[derive_where(
|
#[derive_where(
|
||||||
Debug, Eq, Hash, PartialEq;
|
Debug, Eq, Hash, PartialEq;
|
||||||
voprf::OprfClient<CS::OprfCs>,
|
voprf::OprfClient<CS::OprfCs>,
|
||||||
<CS::KeyExchange as KeyExchange<OprfHash<CS>, CS::KeGroup>>::KE1State,
|
<CS::KeyExchange as KeyExchange>::KE1State,
|
||||||
CredentialRequest<CS>,
|
CredentialRequest<CS>,
|
||||||
)]
|
)]
|
||||||
pub struct ClientLogin<CS: CipherSuite> {
|
pub struct ClientLogin<CS: CipherSuite> {
|
||||||
pub(crate) oprf_client: voprf::OprfClient<CS::OprfCs>,
|
pub(crate) oprf_client: voprf::OprfClient<CS::OprfCs>,
|
||||||
pub(crate) ke1_state: <CS::KeyExchange as KeyExchange<OprfHash<CS>, CS::KeGroup>>::KE1State,
|
pub(crate) ke1_state: <CS::KeyExchange as KeyExchange>::KE1State,
|
||||||
pub(crate) credential_request: CredentialRequest<CS>,
|
pub(crate) credential_request: CredentialRequest<CS>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,19 +134,14 @@ pub struct ClientLogin<CS: CipherSuite> {
|
|||||||
feature = "serde",
|
feature = "serde",
|
||||||
derive(serde::Deserialize, serde::Serialize),
|
derive(serde::Deserialize, serde::Serialize),
|
||||||
serde(bound(
|
serde(bound(
|
||||||
deserialize = "<CS::KeyExchange as KeyExchange<OprfHash<CS>, CS::KeGroup>>::KE2State: \
|
deserialize = "<CS::KeyExchange as KeyExchange>::KE2State<CS>: serde::Deserialize<'de>",
|
||||||
serde::Deserialize<'de>",
|
serialize = "<CS::KeyExchange as KeyExchange>::KE2State<CS>: serde::Serialize"
|
||||||
serialize = "<CS::KeyExchange as KeyExchange<OprfHash<CS>, CS::KeGroup>>::KE2State: \
|
|
||||||
serde::Serialize"
|
|
||||||
))
|
))
|
||||||
)]
|
)]
|
||||||
#[derive_where(Clone, ZeroizeOnDrop)]
|
#[derive_where(Clone, ZeroizeOnDrop)]
|
||||||
#[derive_where(
|
#[derive_where(Debug, Eq, Hash, PartialEq; <CS::KeyExchange as KeyExchange>::KE2State<CS>)]
|
||||||
Debug, Eq, Hash, PartialEq;
|
|
||||||
<CS::KeyExchange as KeyExchange<OprfHash<CS>, CS::KeGroup>>::KE2State,
|
|
||||||
)]
|
|
||||||
pub struct ServerLogin<CS: CipherSuite> {
|
pub struct ServerLogin<CS: CipherSuite> {
|
||||||
ke2_state: <CS::KeyExchange as KeyExchange<OprfHash<CS>, CS::KeGroup>>::KE2State,
|
ke2_state: <CS::KeyExchange as KeyExchange>::KE2State<CS>,
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////
|
////////////////////////////////
|
||||||
@@ -157,10 +152,10 @@ pub struct ServerLogin<CS: CipherSuite> {
|
|||||||
// Server Setup
|
// Server Setup
|
||||||
// ============
|
// ============
|
||||||
|
|
||||||
impl<CS: CipherSuite> ServerSetup<CS, PrivateKey<CS::KeGroup>> {
|
impl<CS: CipherSuite> ServerSetup<CS, PrivateKey<KeGroup<CS>>> {
|
||||||
/// Generate a new instance of server setup
|
/// Generate a new instance of server setup
|
||||||
pub fn new<R: CryptoRng + RngCore>(rng: &mut R) -> Self {
|
pub fn new<R: CryptoRng + RngCore>(rng: &mut R) -> Self {
|
||||||
let keypair = KeyPair::generate_random::<CS::OprfCs, _>(rng);
|
let keypair = KeyPair::random(rng);
|
||||||
Self::new_with_key_pair(rng, keypair)
|
Self::new_with_key_pair(rng, keypair)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -168,9 +163,9 @@ impl<CS: CipherSuite> ServerSetup<CS, PrivateKey<CS::KeGroup>> {
|
|||||||
/// Length of [`ServerSetup`] in bytes for serialization.
|
/// Length of [`ServerSetup`] in bytes for serialization.
|
||||||
pub type ServerSetupLen<
|
pub type ServerSetupLen<
|
||||||
CS: CipherSuite,
|
CS: CipherSuite,
|
||||||
SK: PrivateKeySerialization<CS::KeGroup>,
|
SK: PrivateKeySerialization<KeGroup<CS>>,
|
||||||
OS: OprfSeedSerialization<OprfHash<CS>, SK::Error>,
|
OS: OprfSeedSerialization<OprfHash<CS>, SK::Error>,
|
||||||
> = Sum<Sum<OS::Len, SK::Len>, <CS::KeGroup as KeGroup>::SkLen>;
|
> = Sum<Sum<OS::Len, SK::Len>, <KeGroup<CS> as Group>::SkLen>;
|
||||||
|
|
||||||
impl<CS: CipherSuite, SK: Clone, OS: Clone> ServerSetup<CS, SK, OS> {
|
impl<CS: CipherSuite, SK: Clone, OS: Clone> ServerSetup<CS, SK, OS> {
|
||||||
/// Create [`ServerSetup`] with the given keypair and OPRF seed.
|
/// Create [`ServerSetup`] with the given keypair and OPRF seed.
|
||||||
@@ -180,13 +175,13 @@ impl<CS: CipherSuite, SK: Clone, OS: Clone> ServerSetup<CS, SK, OS> {
|
|||||||
/// [`ServerSetup::deserialize`] for this purpose.
|
/// [`ServerSetup::deserialize`] for this purpose.
|
||||||
pub fn new_with_key_pair_and_seed<R: CryptoRng + RngCore>(
|
pub fn new_with_key_pair_and_seed<R: CryptoRng + RngCore>(
|
||||||
rng: &mut R,
|
rng: &mut R,
|
||||||
keypair: KeyPair<CS::KeGroup, SK>,
|
keypair: KeyPair<KeGroup<CS>, SK>,
|
||||||
oprf_seed: OS,
|
oprf_seed: OS,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
oprf_seed,
|
oprf_seed,
|
||||||
keypair,
|
keypair,
|
||||||
fake_keypair: KeyPair::<CS::KeGroup>::generate_random::<CS::OprfCs, _>(rng),
|
fake_keypair: KeyPair::<KeGroup<CS>>::random(rng),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -206,11 +201,11 @@ impl<CS: CipherSuite, SK: Clone, OS: Clone> ServerSetup<CS, SK, OS> {
|
|||||||
/// Serialization into bytes
|
/// Serialization into bytes
|
||||||
pub fn serialize(&self) -> GenericArray<u8, ServerSetupLen<CS, SK, OS>>
|
pub fn serialize(&self) -> GenericArray<u8, ServerSetupLen<CS, SK, OS>>
|
||||||
where
|
where
|
||||||
SK: PrivateKeySerialization<CS::KeGroup>,
|
SK: PrivateKeySerialization<KeGroup<CS>>,
|
||||||
OS: OprfSeedSerialization<OprfHash<CS>, SK::Error>,
|
OS: OprfSeedSerialization<OprfHash<CS>, SK::Error>,
|
||||||
// ServerSetup: Hash + KeSk + KeSk
|
// ServerSetup: Hash + KeSk + KeSk
|
||||||
OS::Len: Add<SK::Len>,
|
OS::Len: Add<SK::Len>,
|
||||||
Sum<OS::Len, SK::Len>: ArrayLength<u8> + Add<<CS::KeGroup as KeGroup>::SkLen>,
|
Sum<OS::Len, SK::Len>: ArrayLength<u8> + Add<<KeGroup<CS> as Group>::SkLen>,
|
||||||
ServerSetupLen<CS, SK, OS>: ArrayLength<u8>,
|
ServerSetupLen<CS, SK, OS>: ArrayLength<u8>,
|
||||||
{
|
{
|
||||||
self.oprf_seed
|
self.oprf_seed
|
||||||
@@ -220,26 +215,21 @@ impl<CS: CipherSuite, SK: Clone, OS: Clone> ServerSetup<CS, SK, OS> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Deserialization from bytes
|
/// Deserialization from bytes
|
||||||
pub fn deserialize(input: &[u8]) -> Result<Self, ProtocolError<SK::Error>>
|
pub fn deserialize(mut input: &[u8]) -> Result<Self, ProtocolError<SK::Error>>
|
||||||
where
|
where
|
||||||
SK: PrivateKeySerialization<CS::KeGroup>,
|
SK: PrivateKeySerialization<KeGroup<CS>>,
|
||||||
OS: OprfSeedSerialization<OprfHash<CS>, SK::Error>,
|
OS: OprfSeedSerialization<OprfHash<CS>, SK::Error>,
|
||||||
{
|
{
|
||||||
let seed_len = OS::Len::USIZE;
|
|
||||||
let key_len = <CS::KeGroup as KeGroup>::SkLen::USIZE;
|
|
||||||
let checked_slice = check_slice_size(input, seed_len + key_len + key_len, "server_setup")
|
|
||||||
.map_err(ProtocolError::into_custom)?;
|
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
oprf_seed: OS::deserialize(&checked_slice[..seed_len])?,
|
oprf_seed: OS::deserialize_take(&mut input)?,
|
||||||
keypair: SK::deserialize_key_pair(&checked_slice[seed_len..seed_len + key_len])?,
|
keypair: SK::deserialize_take_key_pair(&mut input)?,
|
||||||
fake_keypair: PrivateKey::deserialize_key_pair(&checked_slice[seed_len + key_len..])
|
fake_keypair: PrivateKey::deserialize_take_key_pair(&mut input)
|
||||||
.map_err(ProtocolError::into_custom)?,
|
.map_err(ProtocolError::into_custom)?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the keypair
|
/// Returns the keypair
|
||||||
pub fn keypair(&self) -> &KeyPair<CS::KeGroup, SK> {
|
pub fn keypair(&self) -> &KeyPair<KeGroup<CS>, SK> {
|
||||||
&self.keypair
|
&self.keypair
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -252,44 +242,12 @@ impl<CS: CipherSuite, SK: Clone> ServerSetup<CS, SK> {
|
|||||||
/// [`ServerSetup::deserialize`] for this purpose.
|
/// [`ServerSetup::deserialize`] for this purpose.
|
||||||
pub fn new_with_key_pair<R: CryptoRng + RngCore>(
|
pub fn new_with_key_pair<R: CryptoRng + RngCore>(
|
||||||
rng: &mut R,
|
rng: &mut R,
|
||||||
keypair: KeyPair<CS::KeGroup, SK>,
|
keypair: KeyPair<KeGroup<CS>, SK>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut oprf_seed = GenericArray::default();
|
let mut oprf_seed = GenericArray::default();
|
||||||
rng.fill_bytes(&mut oprf_seed);
|
rng.fill_bytes(&mut oprf_seed);
|
||||||
|
|
||||||
Self {
|
Self::new_with_key_pair_and_seed(rng, keypair, OprfSeed(oprf_seed))
|
||||||
oprf_seed: Zeroizing::new(oprf_seed),
|
|
||||||
keypair,
|
|
||||||
fake_keypair: KeyPair::<CS::KeGroup>::generate_random::<CS::OprfCs, _>(rng),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A trait to facilitate
|
|
||||||
/// [`ServerSetup::de/serialize`](crate::ServerSetup::serialize).
|
|
||||||
pub trait OprfSeedSerialization<H, E>: Sized {
|
|
||||||
/// Serialization size in bytes.
|
|
||||||
type Len: ArrayLength<u8>;
|
|
||||||
|
|
||||||
/// Serialization into bytes
|
|
||||||
fn serialize(&self) -> GenericArray<u8, Self::Len>;
|
|
||||||
|
|
||||||
/// Deserialization from bytes
|
|
||||||
fn deserialize(input: &[u8]) -> Result<Self, ProtocolError<E>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<H: OutputSizeUser, E> OprfSeedSerialization<H, E> for Zeroizing<Output<H>> {
|
|
||||||
type Len = H::OutputSize;
|
|
||||||
|
|
||||||
fn serialize(&self) -> GenericArray<u8, Self::Len> {
|
|
||||||
self.deref().clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn deserialize(input: &[u8]) -> Result<Self, ProtocolError<E>> {
|
|
||||||
check_slice_size(input, H::OutputSize::USIZE, "oprf_seed")
|
|
||||||
.map_err(ProtocolError::into_custom)?;
|
|
||||||
|
|
||||||
Ok(Zeroizing::new(GenericArray::clone_from_slice(input)))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -297,9 +255,9 @@ impl<H: OutputSizeUser, E> OprfSeedSerialization<H, E> for Zeroizing<Output<H>>
|
|||||||
/// [`ServerRegistration::start_with_key_material()`] and
|
/// [`ServerRegistration::start_with_key_material()`] and
|
||||||
/// [`ServerLogin::builder_with_key_material()`].
|
/// [`ServerLogin::builder_with_key_material()`].
|
||||||
///
|
///
|
||||||
/// Use an HKDF, with the input key material [`ikm`](Self::ikm), expand
|
/// Use a HKDF, with the input key material [`ikm`](Self::ikm), expand operation
|
||||||
/// operation with [`info`](Self::info) with an output length
|
/// with [`info`](Self::info) with an output length
|
||||||
/// of [`CS::OprfCs::ScalarLen`](Group::ScalarLen).
|
/// of [`CS::OprfCs::ScalarLen`](voprf::Group::ScalarLen).
|
||||||
pub struct KeyMaterialInfo<'ci, OS: Clone> {
|
pub struct KeyMaterialInfo<'ci, OS: Clone> {
|
||||||
/// Input key material for the HKDF.
|
/// Input key material for the HKDF.
|
||||||
pub ikm: OS,
|
pub ikm: OS,
|
||||||
@@ -311,14 +269,14 @@ pub struct KeyMaterialInfo<'ci, OS: Clone> {
|
|||||||
// ============
|
// ============
|
||||||
|
|
||||||
pub(crate) type ClientRegistrationLen<CS: CipherSuite> =
|
pub(crate) type ClientRegistrationLen<CS: CipherSuite> =
|
||||||
Sum<<OprfGroup<CS> as Group>::ScalarLen, <OprfGroup<CS> as Group>::ElemLen>;
|
Sum<<OprfGroup<CS> as voprf::Group>::ScalarLen, <OprfGroup<CS> as voprf::Group>::ElemLen>;
|
||||||
|
|
||||||
impl<CS: CipherSuite> ClientRegistration<CS> {
|
impl<CS: CipherSuite> ClientRegistration<CS> {
|
||||||
/// Serialization into bytes
|
/// Serialization into bytes
|
||||||
pub fn serialize(&self) -> GenericArray<u8, ClientRegistrationLen<CS>>
|
pub fn serialize(&self) -> GenericArray<u8, ClientRegistrationLen<CS>>
|
||||||
where
|
where
|
||||||
// ClientRegistration: KgSk + KgPk
|
// ClientRegistration: KgSk + KgPk
|
||||||
<OprfGroup<CS> as Group>::ScalarLen: Add<<OprfGroup<CS> as Group>::ElemLen>,
|
<OprfGroup<CS> as voprf::Group>::ScalarLen: Add<<OprfGroup<CS> as voprf::Group>::ElemLen>,
|
||||||
ClientRegistrationLen<CS>: ArrayLength<u8>,
|
ClientRegistrationLen<CS>: ArrayLength<u8>,
|
||||||
{
|
{
|
||||||
self.oprf_client
|
self.oprf_client
|
||||||
@@ -327,28 +285,18 @@ impl<CS: CipherSuite> ClientRegistration<CS> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Deserialization from bytes
|
/// Deserialization from bytes
|
||||||
pub fn deserialize(input: &[u8]) -> Result<Self, ProtocolError> {
|
pub fn deserialize(mut input: &[u8]) -> Result<Self, ProtocolError> {
|
||||||
let client_len = <OprfGroup<CS> as Group>::ScalarLen::USIZE;
|
let oprf_client = OprfClient::deserialize(input)?;
|
||||||
let element_len = <OprfGroup<CS> as Group>::ElemLen::USIZE;
|
input = &input[OprfClientLen::<CS::OprfCs>::USIZE..];
|
||||||
let checked_slice =
|
|
||||||
check_slice_size(input, client_len + element_len, "client_registration")?;
|
let blinded_element = BlindedElement::deserialize(input)?;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
oprf_client: voprf::OprfClient::deserialize(&checked_slice[..client_len])?,
|
oprf_client,
|
||||||
blinded_element: voprf::BlindedElement::deserialize(&checked_slice[client_len..])?,
|
blinded_element,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Only used for testing zeroize
|
|
||||||
#[cfg(test)]
|
|
||||||
pub(crate) fn to_vec(&self) -> std::vec::Vec<u8> {
|
|
||||||
[
|
|
||||||
self.oprf_client.serialize().to_vec(),
|
|
||||||
self.blinded_element.serialize().to_vec(),
|
|
||||||
]
|
|
||||||
.concat()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns an initial "blinded" request to send to the server, as well as a
|
/// Returns an initial "blinded" request to send to the server, as well as a
|
||||||
/// [`ClientRegistration`]
|
/// [`ClientRegistration`]
|
||||||
pub fn start<R: RngCore + CryptoRng>(
|
pub fn start<R: RngCore + CryptoRng>(
|
||||||
@@ -433,12 +381,9 @@ impl<CS: CipherSuite> ServerRegistration<CS> {
|
|||||||
/// Serialization into bytes
|
/// Serialization into bytes
|
||||||
pub fn serialize(&self) -> GenericArray<u8, ServerRegistrationLen<CS>>
|
pub fn serialize(&self) -> GenericArray<u8, ServerRegistrationLen<CS>>
|
||||||
where
|
where
|
||||||
// Envelope: Nonce + Hash
|
|
||||||
NonceLen: Add<OutputSize<OprfHash<CS>>>,
|
|
||||||
EnvelopeLen<CS>: ArrayLength<u8>,
|
|
||||||
// RegistrationUpload: (KePk + Hash) + Envelope
|
// RegistrationUpload: (KePk + Hash) + Envelope
|
||||||
<CS::KeGroup as KeGroup>::PkLen: Add<OutputSize<OprfHash<CS>>>,
|
<KeGroup<CS> as Group>::PkLen: Add<OutputSize<OprfHash<CS>>>,
|
||||||
Sum<<CS::KeGroup as KeGroup>::PkLen, OutputSize<OprfHash<CS>>>:
|
Sum<<KeGroup<CS> as Group>::PkLen, OutputSize<OprfHash<CS>>>:
|
||||||
ArrayLength<u8> + Add<EnvelopeLen<CS>>,
|
ArrayLength<u8> + Add<EnvelopeLen<CS>>,
|
||||||
RegistrationUploadLen<CS>: ArrayLength<u8>,
|
RegistrationUploadLen<CS>: ArrayLength<u8>,
|
||||||
// ServerRegistration = RegistrationUpload
|
// ServerRegistration = RegistrationUpload
|
||||||
@@ -457,7 +402,7 @@ impl<CS: CipherSuite> ServerRegistration<CS> {
|
|||||||
/// See [`ServerRegistration::start()`] for the regular path.
|
/// See [`ServerRegistration::start()`] for the regular path.
|
||||||
pub fn start_with_key_material<SK: Clone, OS: Clone>(
|
pub fn start_with_key_material<SK: Clone, OS: Clone>(
|
||||||
server_setup: &ServerSetup<CS, SK, OS>,
|
server_setup: &ServerSetup<CS, SK, OS>,
|
||||||
key_material: GenericArray<u8, <OprfGroup<CS> as Group>::ScalarLen>,
|
key_material: GenericArray<u8, <OprfGroup<CS> as voprf::Group>::ScalarLen>,
|
||||||
message: RegistrationRequest<CS>,
|
message: RegistrationRequest<CS>,
|
||||||
) -> Result<ServerRegistrationStartResult<CS>, ProtocolError> {
|
) -> Result<ServerRegistrationStartResult<CS>, ProtocolError> {
|
||||||
let oprf_key = oprf_key_from_key_material::<CS>(key_material)?;
|
let oprf_key = oprf_key_from_key_material::<CS>(key_material)?;
|
||||||
@@ -486,7 +431,7 @@ impl<CS: CipherSuite> ServerRegistration<CS> {
|
|||||||
ikm: oprf_seed,
|
ikm: oprf_seed,
|
||||||
info,
|
info,
|
||||||
} = server_setup.key_material_info(credential_identifier);
|
} = server_setup.key_material_info(credential_identifier);
|
||||||
let key_material = oprf_key_material::<CS>(&oprf_seed, &info)?;
|
let key_material = oprf_key_material::<CS>(&oprf_seed.0, &info)?;
|
||||||
|
|
||||||
Self::start_with_key_material(server_setup, key_material, message)
|
Self::start_with_key_material(server_setup, key_material, message)
|
||||||
}
|
}
|
||||||
@@ -510,18 +455,20 @@ impl<CS: CipherSuite> ServerRegistration<CS> {
|
|||||||
// =====
|
// =====
|
||||||
|
|
||||||
pub(crate) type ClientLoginLen<CS: CipherSuite> =
|
pub(crate) type ClientLoginLen<CS: CipherSuite> =
|
||||||
Sum<Sum<<OprfGroup<CS> as Group>::ScalarLen, CredentialRequestLen<CS>>, Ke1StateLen<CS>>;
|
Sum<Sum<<OprfGroup<CS> as voprf::Group>::ScalarLen, CredentialRequestLen<CS>>, Ke1StateLen<CS>>;
|
||||||
|
|
||||||
impl<CS: CipherSuite> ClientLogin<CS> {
|
impl<CS: CipherSuite> ClientLogin<CS> {
|
||||||
/// Serialization into bytes
|
/// Serialization into bytes
|
||||||
pub fn serialize(&self) -> GenericArray<u8, ClientLoginLen<CS>>
|
pub fn serialize(&self) -> GenericArray<u8, ClientLoginLen<CS>>
|
||||||
where
|
where
|
||||||
// CredentialRequest: KgPk + Ke1Message
|
// CredentialRequest: KgPk + Ke1Message
|
||||||
<OprfGroup<CS> as Group>::ElemLen: Add<Ke1MessageLen<CS>>,
|
<CS::KeyExchange as KeyExchange>::KE1Message: Serialize,
|
||||||
|
<OprfGroup<CS> as voprf::Group>::ElemLen: Add<Ke1MessageLen<CS>>,
|
||||||
CredentialRequestLen<CS>: ArrayLength<u8>,
|
CredentialRequestLen<CS>: ArrayLength<u8>,
|
||||||
// ClientLogin: KgSk + CredentialRequest + Ke1State
|
// ClientLogin: KgSk + CredentialRequest + Ke1State
|
||||||
<OprfGroup<CS> as Group>::ScalarLen: Add<CredentialRequestLen<CS>>,
|
<OprfGroup<CS> as voprf::Group>::ScalarLen: Add<CredentialRequestLen<CS>>,
|
||||||
Sum<<OprfGroup<CS> as Group>::ScalarLen, CredentialRequestLen<CS>>:
|
<CS::KeyExchange as KeyExchange>::KE1State: Serialize,
|
||||||
|
Sum<<OprfGroup<CS> as voprf::Group>::ScalarLen, CredentialRequestLen<CS>>:
|
||||||
ArrayLength<u8> + Add<Ke1StateLen<CS>>,
|
ArrayLength<u8> + Add<Ke1StateLen<CS>>,
|
||||||
ClientLoginLen<CS>: ArrayLength<u8>,
|
ClientLoginLen<CS>: ArrayLength<u8>,
|
||||||
{
|
{
|
||||||
@@ -532,23 +479,18 @@ impl<CS: CipherSuite> ClientLogin<CS> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Deserialization from bytes
|
/// Deserialization from bytes
|
||||||
pub fn deserialize(input: &[u8]) -> Result<Self, ProtocolError> {
|
pub fn deserialize(mut input: &[u8]) -> Result<Self, ProtocolError>
|
||||||
let client_len = <OprfGroup<CS> as Group>::ScalarLen::USIZE;
|
where
|
||||||
let request_len = <OprfGroup<CS> as Group>::ElemLen::USIZE + Ke1MessageLen::<CS>::USIZE;
|
<CS::KeyExchange as KeyExchange>::KE1Message: Deserialize + Serialize,
|
||||||
let state_len = Ke1StateLen::<CS>::USIZE;
|
<CS::KeyExchange as KeyExchange>::KE1State: Deserialize + Serialize,
|
||||||
let checked_slice =
|
{
|
||||||
check_slice_size(input, client_len + request_len + state_len, "client_login")?;
|
let oprf_client = OprfClient::deserialize(input)?;
|
||||||
|
input = &input[OprfClientLen::<CS::OprfCs>::USIZE..];
|
||||||
|
|
||||||
let ke1_state =
|
|
||||||
<CS::KeyExchange as KeyExchange<OprfHash<CS>, CS::KeGroup>>::KE1State::deserialize(
|
|
||||||
&checked_slice[client_len + request_len..],
|
|
||||||
)?;
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
oprf_client: voprf::OprfClient::deserialize(&checked_slice[..client_len])?,
|
oprf_client,
|
||||||
credential_request: CredentialRequest::deserialize(
|
credential_request: CredentialRequest::deserialize_take(&mut input)?,
|
||||||
&checked_slice[client_len..client_len + request_len],
|
ke1_state: <CS::KeyExchange as KeyExchange>::KE1State::deserialize_take(&mut input)?,
|
||||||
)?,
|
|
||||||
ke1_state,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -561,7 +503,7 @@ impl<CS: CipherSuite> ClientLogin<CS> {
|
|||||||
password: &[u8],
|
password: &[u8],
|
||||||
) -> Result<ClientLoginStartResult<CS>, ProtocolError> {
|
) -> Result<ClientLoginStartResult<CS>, ProtocolError> {
|
||||||
let blind_result = blind::<CS, _>(rng, password)?;
|
let blind_result = blind::<CS, _>(rng, password)?;
|
||||||
let (ke1_state, ke1_message) = CS::KeyExchange::generate_ke1::<CS::OprfCs, _>(rng)?;
|
let (ke1_state, ke1_message) = CS::KeyExchange::generate_ke1(rng)?;
|
||||||
|
|
||||||
let credential_request = CredentialRequest {
|
let credential_request = CredentialRequest {
|
||||||
blinded_element: blind_result.message,
|
blinded_element: blind_result.message,
|
||||||
@@ -580,19 +522,13 @@ impl<CS: CipherSuite> ClientLogin<CS> {
|
|||||||
|
|
||||||
/// "Unblinds" the server's answer and returns the opened assets from the
|
/// "Unblinds" the server's answer and returns the opened assets from the
|
||||||
/// server
|
/// server
|
||||||
pub fn finish(
|
pub fn finish<R: CryptoRng + RngCore>(
|
||||||
self,
|
self,
|
||||||
|
rng: &mut R,
|
||||||
password: &[u8],
|
password: &[u8],
|
||||||
credential_response: CredentialResponse<CS>,
|
credential_response: CredentialResponse<CS>,
|
||||||
params: ClientLoginFinishParameters<CS>,
|
params: ClientLoginFinishParameters<CS>,
|
||||||
) -> Result<ClientLoginFinishResult<CS>, ProtocolError>
|
) -> Result<ClientLoginFinishResult<CS>, ProtocolError> {
|
||||||
where
|
|
||||||
// MaskedResponse: (Nonce + Hash) + KePk
|
|
||||||
NonceLen: Add<OutputSize<OprfHash<CS>>>,
|
|
||||||
Sum<NonceLen, OutputSize<OprfHash<CS>>>:
|
|
||||||
ArrayLength<u8> + Add<<CS::KeGroup as KeGroup>::PkLen>,
|
|
||||||
MaskedResponseLen<CS>: ArrayLength<u8>,
|
|
||||||
{
|
|
||||||
// Check if beta value from server is equal to alpha value from client
|
// Check if beta value from server is equal to alpha value from client
|
||||||
if self
|
if self
|
||||||
.credential_request
|
.credential_request
|
||||||
@@ -639,29 +575,19 @@ impl<CS: CipherSuite> ClientLogin<CS> {
|
|||||||
err => err,
|
err => err,
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let beta = OprfGroup::<CS>::serialize_elem(credential_response.evaluation_element.value());
|
let context = SerializedContext::from(params.context)?;
|
||||||
let credential_response_component = CredentialResponse::<CS>::serialize_without_ke(
|
|
||||||
&beta,
|
|
||||||
&credential_response.masking_nonce,
|
|
||||||
&credential_response.masked_response,
|
|
||||||
);
|
|
||||||
|
|
||||||
let blinded_element =
|
|
||||||
OprfGroup::<CS>::serialize_elem(self.credential_request.blinded_element.value());
|
|
||||||
let ke1_message = self.credential_request.ke1_message.serialize();
|
|
||||||
let serialized_credential_request =
|
|
||||||
CredentialRequest::<CS>::serialize_iter(&blinded_element, &ke1_message);
|
|
||||||
|
|
||||||
let result = CS::KeyExchange::generate_ke3(
|
let result = CS::KeyExchange::generate_ke3(
|
||||||
credential_response_component,
|
rng,
|
||||||
|
self.credential_request.to_parts(),
|
||||||
|
self.credential_request.ke1_message.clone(),
|
||||||
|
credential_response.to_parts(),
|
||||||
credential_response.ke2_message,
|
credential_response.ke2_message,
|
||||||
&self.ke1_state,
|
&self.ke1_state,
|
||||||
serialized_credential_request,
|
|
||||||
server_s_pk.clone(),
|
server_s_pk.clone(),
|
||||||
opened_envelope.client_static_keypair.private().clone(),
|
opened_envelope.client_static_keypair.private().clone(),
|
||||||
opened_envelope.id_u.iter(),
|
opened_envelope.identifiers,
|
||||||
opened_envelope.id_s.iter(),
|
context,
|
||||||
params.context.unwrap_or(&[]),
|
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
Ok(ClientLoginFinishResult {
|
Ok(ClientLoginFinishResult {
|
||||||
@@ -683,16 +609,22 @@ impl<CS: CipherSuite> ClientLogin<CS> {
|
|||||||
|
|
||||||
impl<CS: CipherSuite> ServerLogin<CS> {
|
impl<CS: CipherSuite> ServerLogin<CS> {
|
||||||
/// Serialization into bytes
|
/// Serialization into bytes
|
||||||
pub fn serialize(&self) -> GenericArray<u8, Ke2StateLen<CS>> {
|
pub fn serialize(&self) -> GenericArray<u8, Ke2StateLen<CS>>
|
||||||
|
where
|
||||||
|
<CS::KeyExchange as KeyExchange>::KE2State<CS>: Serialize,
|
||||||
|
{
|
||||||
self.ke2_state.serialize()
|
self.ke2_state.serialize()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Deserialization from bytes
|
/// Deserialization from bytes
|
||||||
pub fn deserialize(bytes: &[u8]) -> Result<Self, ProtocolError> {
|
pub fn deserialize(mut bytes: &[u8]) -> Result<Self, ProtocolError>
|
||||||
|
where
|
||||||
|
<CS::KeyExchange as KeyExchange>::KE2State<CS>: Deserialize,
|
||||||
|
{
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
ke2_state:
|
ke2_state:
|
||||||
<CS::KeyExchange as KeyExchange<OprfHash<CS>, CS::KeGroup>>::KE2State::deserialize(
|
<<CS::KeyExchange as KeyExchange>::KE2State<CS> as Deserialize>::deserialize_take(
|
||||||
bytes,
|
&mut bytes,
|
||||||
)?,
|
)?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -703,24 +635,17 @@ impl<CS: CipherSuite> ServerLogin<CS> {
|
|||||||
///
|
///
|
||||||
/// See [`ServerLogin::start()`] for the regular path. Or
|
/// See [`ServerLogin::start()`] for the regular path. Or
|
||||||
/// [`ServerLogin::builder()`] with just a remote private key.
|
/// [`ServerLogin::builder()`] with just a remote private key.
|
||||||
pub fn builder_with_key_material<R: RngCore + CryptoRng, SK: Clone, OS: Clone>(
|
pub fn builder_with_key_material<'a, R: RngCore + CryptoRng, SK: Clone, OS: Clone>(
|
||||||
rng: &mut R,
|
rng: &mut R,
|
||||||
server_setup: &ServerSetup<CS, SK, OS>,
|
server_setup: &ServerSetup<CS, SK, OS>,
|
||||||
key_material: GenericArray<u8, <OprfGroup<CS> as Group>::ScalarLen>,
|
key_material: GenericArray<u8, <OprfGroup<CS> as voprf::Group>::ScalarLen>,
|
||||||
password_file: Option<ServerRegistration<CS>>,
|
password_file: Option<ServerRegistration<CS>>,
|
||||||
credential_request: CredentialRequest<CS>,
|
credential_request: CredentialRequest<CS>,
|
||||||
ServerLoginStartParameters {
|
ServerLoginParameters {
|
||||||
context,
|
context,
|
||||||
identifiers,
|
identifiers,
|
||||||
}: ServerLoginStartParameters,
|
}: ServerLoginParameters<'a, 'a>,
|
||||||
) -> Result<ServerLoginBuilder<CS, SK>, ProtocolError>
|
) -> Result<ServerLoginBuilder<'a, CS, SK>, ProtocolError> {
|
||||||
where
|
|
||||||
// MaskedResponse: (Nonce + Hash) + KePk
|
|
||||||
NonceLen: Add<OutputSize<OprfHash<CS>>>,
|
|
||||||
Sum<NonceLen, OutputSize<OprfHash<CS>>>:
|
|
||||||
ArrayLength<u8> + Add<<CS::KeGroup as KeGroup>::PkLen>,
|
|
||||||
MaskedResponseLen<CS>: ArrayLength<u8>,
|
|
||||||
{
|
|
||||||
let record = CtOption::new(
|
let record = CtOption::new(
|
||||||
ServerRegistration::dummy(rng, server_setup),
|
ServerRegistration::dummy(rng, server_setup),
|
||||||
Choice::from(password_file.is_none() as u8),
|
Choice::from(password_file.is_none() as u8),
|
||||||
@@ -729,7 +654,7 @@ impl<CS: CipherSuite> ServerLogin<CS> {
|
|||||||
.unwrap_or_else(|| password_file.unwrap());
|
.unwrap_or_else(|| password_file.unwrap());
|
||||||
|
|
||||||
let client_s_pk = record.0.client_s_pk.clone();
|
let client_s_pk = record.0.client_s_pk.clone();
|
||||||
let context = context.unwrap_or(&[]);
|
let context = SerializedContext::from(context)?;
|
||||||
let server_s_pk = server_setup.keypair.public();
|
let server_s_pk = server_setup.keypair.public();
|
||||||
|
|
||||||
let mut masking_nonce = GenericArray::<_, NonceLen>::default();
|
let mut masking_nonce = GenericArray::<_, NonceLen>::default();
|
||||||
@@ -742,34 +667,31 @@ impl<CS: CipherSuite> ServerLogin<CS> {
|
|||||||
&record.0.envelope,
|
&record.0.envelope,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let (id_u, id_s) = bytestrings_from_identifiers::<CS::KeGroup>(
|
let serialized_client_s_pk = client_s_pk.serialize();
|
||||||
|
let serialized_server_s_pk = server_s_pk.serialize();
|
||||||
|
let identifiers = SerializedIdentifiers::<KeGroup<CS>>::from_identifiers(
|
||||||
identifiers,
|
identifiers,
|
||||||
client_s_pk.serialize(),
|
serialized_client_s_pk.clone(),
|
||||||
server_s_pk.serialize(),
|
serialized_server_s_pk.clone(),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let blinded_element =
|
|
||||||
OprfGroup::<CS>::serialize_elem(credential_request.blinded_element.value());
|
|
||||||
let ke1_message = credential_request.ke1_message.serialize();
|
|
||||||
let credential_request_bytes =
|
|
||||||
CredentialRequest::<CS>::serialize_iter(&blinded_element, &ke1_message);
|
|
||||||
|
|
||||||
let oprf_key = oprf_key_from_key_material::<CS>(key_material)?;
|
let oprf_key = oprf_key_from_key_material::<CS>(key_material)?;
|
||||||
let server = voprf::OprfServer::new_with_key(&oprf_key).map_err(ProtocolError::from)?;
|
let server = voprf::OprfServer::new_with_key(&oprf_key).map_err(ProtocolError::from)?;
|
||||||
let evaluation_element = server.blind_evaluate(&credential_request.blinded_element);
|
let evaluation_element = server.blind_evaluate(&credential_request.blinded_element);
|
||||||
|
|
||||||
let beta = OprfGroup::<CS>::serialize_elem(evaluation_element.value());
|
let credential_response = CredentialResponseParts::new(
|
||||||
let credential_response_component =
|
&evaluation_element,
|
||||||
CredentialResponse::<CS>::serialize_without_ke(&beta, &masking_nonce, &masked_response);
|
masking_nonce,
|
||||||
|
masked_response.clone(),
|
||||||
|
);
|
||||||
|
|
||||||
let ke2_builder = CS::KeyExchange::ke2_builder::<CS::OprfCs, _>(
|
let ke2_builder = CS::KeyExchange::ke2_builder(
|
||||||
rng,
|
rng,
|
||||||
credential_request_bytes,
|
credential_request.to_parts(),
|
||||||
credential_response_component,
|
|
||||||
credential_request.ke1_message.clone(),
|
credential_request.ke1_message.clone(),
|
||||||
|
credential_response,
|
||||||
client_s_pk,
|
client_s_pk,
|
||||||
id_u.iter(),
|
identifiers,
|
||||||
id_s.iter(),
|
|
||||||
context,
|
context,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
@@ -787,26 +709,19 @@ impl<CS: CipherSuite> ServerLogin<CS> {
|
|||||||
/// Create a [`ServerLoginBuilder`] to use with a remote private key.
|
/// Create a [`ServerLoginBuilder`] to use with a remote private key.
|
||||||
///
|
///
|
||||||
/// See [`ServerLogin::start()`] for the regular path.
|
/// See [`ServerLogin::start()`] for the regular path.
|
||||||
pub fn builder<R: RngCore + CryptoRng, SK: Clone>(
|
pub fn builder<'a, R: RngCore + CryptoRng, SK: Clone>(
|
||||||
rng: &mut R,
|
rng: &mut R,
|
||||||
server_setup: &ServerSetup<CS, SK>,
|
server_setup: &ServerSetup<CS, SK>,
|
||||||
password_file: Option<ServerRegistration<CS>>,
|
password_file: Option<ServerRegistration<CS>>,
|
||||||
credential_request: CredentialRequest<CS>,
|
credential_request: CredentialRequest<CS>,
|
||||||
credential_identifier: &[u8],
|
credential_identifier: &[u8],
|
||||||
params: ServerLoginStartParameters,
|
params: ServerLoginParameters<'a, 'a>,
|
||||||
) -> Result<ServerLoginBuilder<CS, SK>, ProtocolError>
|
) -> Result<ServerLoginBuilder<'a, CS, SK>, ProtocolError> {
|
||||||
where
|
|
||||||
// MaskedResponse: (Nonce + Hash) + KePk
|
|
||||||
NonceLen: Add<OutputSize<OprfHash<CS>>>,
|
|
||||||
Sum<NonceLen, OutputSize<OprfHash<CS>>>:
|
|
||||||
ArrayLength<u8> + Add<<CS::KeGroup as KeGroup>::PkLen>,
|
|
||||||
MaskedResponseLen<CS>: ArrayLength<u8>,
|
|
||||||
{
|
|
||||||
let KeyMaterialInfo {
|
let KeyMaterialInfo {
|
||||||
ikm: oprf_seed,
|
ikm: oprf_seed,
|
||||||
info,
|
info,
|
||||||
} = server_setup.key_material_info(credential_identifier);
|
} = server_setup.key_material_info(credential_identifier);
|
||||||
let key_material = oprf_key_material::<CS>(&oprf_seed, &info)?;
|
let key_material = oprf_key_material::<CS>(&oprf_seed.0, &info)?;
|
||||||
|
|
||||||
Self::builder_with_key_material(
|
Self::builder_with_key_material(
|
||||||
rng,
|
rng,
|
||||||
@@ -820,7 +735,7 @@ impl<CS: CipherSuite> ServerLogin<CS> {
|
|||||||
|
|
||||||
pub(crate) fn build<SK: Clone>(
|
pub(crate) fn build<SK: Clone>(
|
||||||
builder: ServerLoginBuilder<CS, SK>,
|
builder: ServerLoginBuilder<CS, SK>,
|
||||||
input: <CS::KeyExchange as KeyExchange<OprfHash<CS>, CS::KeGroup>>::KE2BuilderInput,
|
input: <CS::KeyExchange as KeyExchange>::KE2BuilderInput<CS>,
|
||||||
) -> Result<ServerLoginStartResult<CS>, ProtocolError> {
|
) -> Result<ServerLoginStartResult<CS>, ProtocolError> {
|
||||||
let result = CS::KeyExchange::build_ke2(builder.ke2_builder.clone(), input)?;
|
let result = CS::KeyExchange::build_ke2(builder.ke2_builder.clone(), input)?;
|
||||||
|
|
||||||
@@ -853,15 +768,8 @@ impl<CS: CipherSuite> ServerLogin<CS> {
|
|||||||
password_file: Option<ServerRegistration<CS>>,
|
password_file: Option<ServerRegistration<CS>>,
|
||||||
credential_request: CredentialRequest<CS>,
|
credential_request: CredentialRequest<CS>,
|
||||||
credential_identifier: &[u8],
|
credential_identifier: &[u8],
|
||||||
parameters: ServerLoginStartParameters,
|
parameters: ServerLoginParameters,
|
||||||
) -> Result<ServerLoginStartResult<CS>, ProtocolError>
|
) -> Result<ServerLoginStartResult<CS>, ProtocolError> {
|
||||||
where
|
|
||||||
// MaskedResponse: (Nonce + Hash) + KePk
|
|
||||||
NonceLen: Add<OutputSize<OprfHash<CS>>>,
|
|
||||||
Sum<NonceLen, OutputSize<OprfHash<CS>>>:
|
|
||||||
ArrayLength<u8> + Add<<CS::KeGroup as KeGroup>::PkLen>,
|
|
||||||
MaskedResponseLen<CS>: ArrayLength<u8>,
|
|
||||||
{
|
|
||||||
let builder = Self::builder(
|
let builder = Self::builder(
|
||||||
rng,
|
rng,
|
||||||
server_setup,
|
server_setup,
|
||||||
@@ -872,6 +780,7 @@ impl<CS: CipherSuite> ServerLogin<CS> {
|
|||||||
)?;
|
)?;
|
||||||
let input = CS::KeyExchange::generate_ke2_input(
|
let input = CS::KeyExchange::generate_ke2_input(
|
||||||
&builder.ke2_builder,
|
&builder.ke2_builder,
|
||||||
|
rng,
|
||||||
server_setup.keypair.private(),
|
server_setup.keypair.private(),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -883,10 +792,15 @@ impl<CS: CipherSuite> ServerLogin<CS> {
|
|||||||
pub fn finish(
|
pub fn finish(
|
||||||
self,
|
self,
|
||||||
message: CredentialFinalization<CS>,
|
message: CredentialFinalization<CS>,
|
||||||
|
parameters: ServerLoginParameters,
|
||||||
) -> Result<ServerLoginFinishResult<CS>, ProtocolError> {
|
) -> Result<ServerLoginFinishResult<CS>, ProtocolError> {
|
||||||
let session_key = <CS::KeyExchange as KeyExchange<OprfHash<CS>, CS::KeGroup>>::finish_ke(
|
let context = SerializedContext::from(parameters.context)?;
|
||||||
|
|
||||||
|
let session_key = <CS::KeyExchange as KeyExchange>::finish_ke(
|
||||||
message.ke3_message,
|
message.ke3_message,
|
||||||
&self.ke2_state,
|
&self.ke2_state,
|
||||||
|
parameters.identifiers,
|
||||||
|
context,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
Ok(ServerLoginFinishResult {
|
Ok(ServerLoginFinishResult {
|
||||||
@@ -945,7 +859,7 @@ pub struct ClientRegistrationFinishResult<CS: CipherSuite> {
|
|||||||
/// The export key output by client registration
|
/// The export key output by client registration
|
||||||
pub export_key: Output<OprfHash<CS>>,
|
pub export_key: Output<OprfHash<CS>>,
|
||||||
/// The server's static public key
|
/// The server's static public key
|
||||||
pub server_s_pk: PublicKey<CS::KeGroup>,
|
pub server_s_pk: PublicKey<KeGroup<CS>>,
|
||||||
/// Instance of the ClientRegistration, only used in tests for checking
|
/// Instance of the ClientRegistration, only used in tests for checking
|
||||||
/// zeroize
|
/// zeroize
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -966,7 +880,7 @@ pub struct ServerRegistrationStartResult<CS: CipherSuite> {
|
|||||||
pub message: RegistrationResponse<CS>,
|
pub message: RegistrationResponse<CS>,
|
||||||
/// OPRF key, only used in tests
|
/// OPRF key, only used in tests
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub oprf_key: GenericArray<u8, <OprfGroup<CS> as Group>::ScalarLen>,
|
pub oprf_key: GenericArray<u8, <OprfGroup<CS> as voprf::Group>::ScalarLen>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Contains the fields that are returned by a client login start
|
/// Contains the fields that are returned by a client login start
|
||||||
@@ -1011,20 +925,20 @@ pub struct ClientLoginFinishResult<CS: CipherSuite> {
|
|||||||
/// The message to send to the server to complete the protocol
|
/// The message to send to the server to complete the protocol
|
||||||
pub message: CredentialFinalization<CS>,
|
pub message: CredentialFinalization<CS>,
|
||||||
/// The session key
|
/// The session key
|
||||||
pub session_key: Output<OprfHash<CS>>,
|
pub session_key: Output<KeHash<CS>>,
|
||||||
/// The client-side export key
|
/// The client-side export key
|
||||||
pub export_key: Output<OprfHash<CS>>,
|
pub export_key: Output<OprfHash<CS>>,
|
||||||
/// The server's static public key
|
/// The server's static public key
|
||||||
pub server_s_pk: PublicKey<CS::KeGroup>,
|
pub server_s_pk: PublicKey<KeGroup<CS>>,
|
||||||
/// Instance of the ClientLogin, only used in tests for checking zeroize
|
/// Instance of the ClientLogin, only used in tests for checking zeroize
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub state: ClientLogin<CS>,
|
pub state: ClientLogin<CS>,
|
||||||
/// Handshake secret, only used in tests
|
/// Handshake secret, only used in tests
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub handshake_secret: Output<OprfHash<CS>>,
|
pub handshake_secret: Output<KeHash<CS>>,
|
||||||
/// Client MAC key, only used in tests
|
/// Client MAC key, only used in tests
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub client_mac_key: Output<OprfHash<CS>>,
|
pub client_mac_key: Output<KeHash<CS>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Contains the fields that are returned by a server login finish
|
/// Contains the fields that are returned by a server login finish
|
||||||
@@ -1033,16 +947,16 @@ pub struct ClientLoginFinishResult<CS: CipherSuite> {
|
|||||||
#[cfg_attr(test, derive_where(Debug; ServerLogin<CS>))]
|
#[cfg_attr(test, derive_where(Debug; ServerLogin<CS>))]
|
||||||
pub struct ServerLoginFinishResult<CS: CipherSuite> {
|
pub struct ServerLoginFinishResult<CS: CipherSuite> {
|
||||||
/// The session key between client and server
|
/// The session key between client and server
|
||||||
pub session_key: Output<OprfHash<CS>>,
|
pub session_key: Output<KeHash<CS>>,
|
||||||
/// Instance of the ClientRegistration, only used in tests for checking
|
/// Instance of the ClientRegistration, only used in tests for checking
|
||||||
/// zeroize
|
/// zeroize
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub state: ServerLogin<CS>,
|
pub state: ServerLogin<CS>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Optional parameters for server login start
|
/// Optional parameters for server login start and finish
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
pub struct ServerLoginStartParameters<'c, 'i> {
|
pub struct ServerLoginParameters<'c, 'i> {
|
||||||
/// Specifying a context field that the client must agree on
|
/// Specifying a context field that the client must agree on
|
||||||
pub context: Option<&'c [u8]>,
|
pub context: Option<&'c [u8]>,
|
||||||
/// Specifying a user identifier and server identifier that will be matched
|
/// Specifying a user identifier and server identifier that will be matched
|
||||||
@@ -1054,9 +968,10 @@ pub struct ServerLoginStartParameters<'c, 'i> {
|
|||||||
#[derive_where(Clone)]
|
#[derive_where(Clone)]
|
||||||
#[derive_where(
|
#[derive_where(
|
||||||
Debug;
|
Debug;
|
||||||
|
<KeGroup<CS> as Group>::Pk,
|
||||||
voprf::EvaluationElement<CS::OprfCs>,
|
voprf::EvaluationElement<CS::OprfCs>,
|
||||||
<CS::KeyExchange as KeyExchange<OprfHash<CS>, CS::KeGroup>>::KE2Message,
|
<CS::KeyExchange as KeyExchange>::KE2Message,
|
||||||
<CS::KeyExchange as KeyExchange<OprfHash<CS>, CS::KeGroup>>::KE2State,
|
<CS::KeyExchange as KeyExchange>::KE2State<CS>,
|
||||||
)]
|
)]
|
||||||
pub struct ServerLoginStartResult<CS: CipherSuite> {
|
pub struct ServerLoginStartResult<CS: CipherSuite> {
|
||||||
/// The message to send back to the client
|
/// The message to send back to the client
|
||||||
@@ -1065,13 +980,13 @@ pub struct ServerLoginStartResult<CS: CipherSuite> {
|
|||||||
pub state: ServerLogin<CS>,
|
pub state: ServerLogin<CS>,
|
||||||
/// Handshake secret, only used in tests
|
/// Handshake secret, only used in tests
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub handshake_secret: Output<OprfHash<CS>>,
|
pub handshake_secret: Output<KeHash<CS>>,
|
||||||
/// Server MAC key, only used in tests
|
/// Server MAC key, only used in tests
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub server_mac_key: Output<OprfHash<CS>>,
|
pub server_mac_key: Output<KeHash<CS>>,
|
||||||
/// OPRF key, only used in tests
|
/// OPRF key, only used in tests
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub oprf_key: GenericArray<u8, <OprfGroup<CS> as Group>::ScalarLen>,
|
pub oprf_key: GenericArray<u8, <OprfGroup<CS> as voprf::Group>::ScalarLen>,
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////
|
////////////////////////////////////////////////
|
||||||
@@ -1105,8 +1020,8 @@ fn get_password_derived_key<CS: CipherSuite>(
|
|||||||
fn oprf_key_material<CS: CipherSuite>(
|
fn oprf_key_material<CS: CipherSuite>(
|
||||||
oprf_seed: &Output<OprfHash<CS>>,
|
oprf_seed: &Output<OprfHash<CS>>,
|
||||||
info: &[&[u8]],
|
info: &[&[u8]],
|
||||||
) -> Result<GenericArray<u8, <OprfGroup<CS> as Group>::ScalarLen>, InternalError> {
|
) -> Result<GenericArray<u8, <OprfGroup<CS> as voprf::Group>::ScalarLen>, InternalError> {
|
||||||
let mut ikm = GenericArray::<_, <OprfGroup<CS> as Group>::ScalarLen>::default();
|
let mut ikm = GenericArray::<_, <OprfGroup<CS> as voprf::Group>::ScalarLen>::default();
|
||||||
Hkdf::<OprfHash<CS>>::from_prk(oprf_seed)
|
Hkdf::<OprfHash<CS>>::from_prk(oprf_seed)
|
||||||
.ok()
|
.ok()
|
||||||
.and_then(|hkdf| hkdf.expand_multi_info(info, &mut ikm).ok())
|
.and_then(|hkdf| hkdf.expand_multi_info(info, &mut ikm).ok())
|
||||||
@@ -1116,8 +1031,8 @@ fn oprf_key_material<CS: CipherSuite>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn oprf_key_from_key_material<CS: CipherSuite>(
|
fn oprf_key_from_key_material<CS: CipherSuite>(
|
||||||
input: GenericArray<u8, <OprfGroup<CS> as Group>::ScalarLen>,
|
input: GenericArray<u8, <OprfGroup<CS> as voprf::Group>::ScalarLen>,
|
||||||
) -> Result<GenericArray<u8, <OprfGroup<CS> as Group>::ScalarLen>, InternalError> {
|
) -> Result<GenericArray<u8, <OprfGroup<CS> as voprf::Group>::ScalarLen>, InternalError> {
|
||||||
Ok(OprfGroup::<CS>::serialize_scalar(voprf::derive_key::<
|
Ok(OprfGroup::<CS>::serialize_scalar(voprf::derive_key::<
|
||||||
CS::OprfCs,
|
CS::OprfCs,
|
||||||
>(
|
>(
|
||||||
@@ -1132,42 +1047,31 @@ fn oprf_key_from_key_material<CS: CipherSuite>(
|
|||||||
derive(serde::Deserialize, serde::Serialize),
|
derive(serde::Deserialize, serde::Serialize),
|
||||||
serde(bound = "")
|
serde(bound = "")
|
||||||
)]
|
)]
|
||||||
#[derive_where(Clone, ZeroizeOnDrop)]
|
#[derive_where(Clone, Zeroize)]
|
||||||
#[derive_where(Debug, Eq, Hash, PartialEq)]
|
#[derive_where(Debug, Eq, Hash, PartialEq)]
|
||||||
pub(crate) struct MaskedResponse<CS: CipherSuite> {
|
pub(crate) struct MaskedResponse<CS: CipherSuite> {
|
||||||
pub(crate) nonce: GenericArray<u8, NonceLen>,
|
pub(crate) nonce: GenericArray<u8, NonceLen>,
|
||||||
pub(crate) hash: Output<OprfHash<CS>>,
|
pub(crate) hash: Output<OprfHash<CS>>,
|
||||||
pub(crate) pk: GenericArray<u8, <CS::KeGroup as KeGroup>::PkLen>,
|
pub(crate) pk: GenericArray<u8, <KeGroup<CS> as Group>::PkLen>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) type MaskedResponseLen<CS: CipherSuite> =
|
pub(crate) type MaskedResponseLen<CS: CipherSuite> =
|
||||||
Sum<Sum<NonceLen, OutputSize<OprfHash<CS>>>, <CS::KeGroup as KeGroup>::PkLen>;
|
Sum<Sum<OutputSize<OprfHash<CS>>, NonceLen>, <KeGroup<CS> as Group>::PkLen>;
|
||||||
|
|
||||||
impl<CS: CipherSuite> MaskedResponse<CS> {
|
impl<CS: CipherSuite> MaskedResponse<CS> {
|
||||||
pub(crate) fn serialize(&self) -> GenericArray<u8, MaskedResponseLen<CS>>
|
pub(crate) fn serialize(&self) -> GenericArray<u8, MaskedResponseLen<CS>> {
|
||||||
where
|
self.nonce.concat_ext(&self.hash).concat(self.pk.clone())
|
||||||
// MaskedResponse: (Nonce + Hash) + KePk
|
|
||||||
NonceLen: Add<OutputSize<OprfHash<CS>>>,
|
|
||||||
Sum<NonceLen, OutputSize<OprfHash<CS>>>:
|
|
||||||
ArrayLength<u8> + Add<<CS::KeGroup as KeGroup>::PkLen>,
|
|
||||||
MaskedResponseLen<CS>: ArrayLength<u8>,
|
|
||||||
{
|
|
||||||
self.nonce.concat(self.hash.clone()).concat(self.pk.clone())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn deserialize(bytes: &[u8]) -> Self {
|
pub(crate) fn deserialize_take(bytes: &mut &[u8]) -> Result<Self, ProtocolError> {
|
||||||
let nonce = NonceLen::USIZE;
|
Ok(Self {
|
||||||
let hash = nonce + OutputSize::<OprfHash<CS>>::USIZE;
|
nonce: bytes.take_array("masked nonce")?,
|
||||||
let pk = hash + <CS::KeGroup as KeGroup>::PkLen::USIZE;
|
hash: bytes.take_array("masked hash")?,
|
||||||
|
pk: bytes.take_array("masked public key")?,
|
||||||
Self {
|
})
|
||||||
nonce: GenericArray::clone_from_slice(&bytes[..nonce]),
|
|
||||||
hash: GenericArray::clone_from_slice(&bytes[nonce..hash]),
|
|
||||||
pk: GenericArray::clone_from_slice(&bytes[hash..pk]),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn iter(&self) -> impl Iterator<Item = &[u8]> {
|
pub(crate) fn iter(&self) -> impl Clone + Iterator<Item = &[u8]> {
|
||||||
[self.nonce.as_slice(), &self.hash, &self.pk].into_iter()
|
[self.nonce.as_slice(), &self.hash, &self.pk].into_iter()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1175,15 +1079,9 @@ impl<CS: CipherSuite> MaskedResponse<CS> {
|
|||||||
fn mask_response<CS: CipherSuite>(
|
fn mask_response<CS: CipherSuite>(
|
||||||
masking_key: &[u8],
|
masking_key: &[u8],
|
||||||
masking_nonce: &[u8],
|
masking_nonce: &[u8],
|
||||||
server_s_pk: &PublicKey<CS::KeGroup>,
|
server_s_pk: &PublicKey<KeGroup<CS>>,
|
||||||
envelope: &Envelope<CS>,
|
envelope: &Envelope<CS>,
|
||||||
) -> Result<MaskedResponse<CS>, ProtocolError>
|
) -> Result<MaskedResponse<CS>, ProtocolError> {
|
||||||
where
|
|
||||||
// MaskedResponse: (Nonce + Hash) + KePk
|
|
||||||
NonceLen: Add<OutputSize<OprfHash<CS>>>,
|
|
||||||
Sum<NonceLen, OutputSize<OprfHash<CS>>>: ArrayLength<u8> + Add<<CS::KeGroup as KeGroup>::PkLen>,
|
|
||||||
MaskedResponseLen<CS>: ArrayLength<u8>,
|
|
||||||
{
|
|
||||||
let mut xor_pad = GenericArray::<_, MaskedResponseLen<CS>>::default();
|
let mut xor_pad = GenericArray::<_, MaskedResponseLen<CS>>::default();
|
||||||
|
|
||||||
Hkdf::<OprfHash<CS>>::from_prk(masking_key)
|
Hkdf::<OprfHash<CS>>::from_prk(masking_key)
|
||||||
@@ -1201,20 +1099,14 @@ where
|
|||||||
*x1 ^= x2
|
*x1 ^= x2
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(MaskedResponse::deserialize(&xor_pad))
|
MaskedResponse::deserialize_take(&mut (xor_pad.as_slice()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unmask_response<CS: CipherSuite>(
|
fn unmask_response<CS: CipherSuite>(
|
||||||
masking_key: &[u8],
|
masking_key: &[u8],
|
||||||
masking_nonce: &[u8],
|
masking_nonce: &[u8],
|
||||||
masked_response: &MaskedResponse<CS>,
|
masked_response: &MaskedResponse<CS>,
|
||||||
) -> Result<(PublicKey<CS::KeGroup>, Envelope<CS>), ProtocolError>
|
) -> Result<(PublicKey<KeGroup<CS>>, Envelope<CS>), ProtocolError> {
|
||||||
where
|
|
||||||
// MaskedResponse: (Nonce + Hash) + KePk
|
|
||||||
NonceLen: Add<OutputSize<OprfHash<CS>>>,
|
|
||||||
Sum<NonceLen, OutputSize<OprfHash<CS>>>: ArrayLength<u8> + Add<<CS::KeGroup as KeGroup>::PkLen>,
|
|
||||||
MaskedResponseLen<CS>: ArrayLength<u8>,
|
|
||||||
{
|
|
||||||
let mut xor_pad = GenericArray::<_, MaskedResponseLen<CS>>::default();
|
let mut xor_pad = GenericArray::<_, MaskedResponseLen<CS>>::default();
|
||||||
|
|
||||||
Hkdf::<OprfHash<CS>>::from_prk(masking_key)
|
Hkdf::<OprfHash<CS>>::from_prk(masking_key)
|
||||||
@@ -1226,34 +1118,14 @@ where
|
|||||||
*x1 ^= x2
|
*x1 ^= x2
|
||||||
}
|
}
|
||||||
|
|
||||||
let key_len = <CS::KeGroup as KeGroup>::PkLen::USIZE;
|
let mut xor_pad = xor_pad.as_slice();
|
||||||
let server_s_pk = PublicKey::deserialize(&xor_pad[..key_len])
|
let server_s_pk =
|
||||||
.map_err(|_| ProtocolError::SerializationError)?;
|
PublicKey::deserialize_take(&mut xor_pad).map_err(|_| ProtocolError::SerializationError)?;
|
||||||
let envelope = Envelope::deserialize(&xor_pad[key_len..])?;
|
let envelope = Envelope::deserialize_take(&mut xor_pad)?;
|
||||||
|
|
||||||
Ok((server_s_pk, envelope))
|
Ok((server_s_pk, envelope))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::type_complexity)]
|
|
||||||
pub(crate) fn bytestrings_from_identifiers<KG: KeGroup>(
|
|
||||||
ids: Identifiers,
|
|
||||||
client_s_pk: GenericArray<u8, KG::PkLen>,
|
|
||||||
server_s_pk: GenericArray<u8, KG::PkLen>,
|
|
||||||
) -> Result<(Input<U2, KG::PkLen>, Input<U2, KG::PkLen>), ProtocolError> {
|
|
||||||
let client_identity = if let Some(client) = ids.client {
|
|
||||||
Input::<U2, _>::from(client)?
|
|
||||||
} else {
|
|
||||||
Input::<U2, _>::from_owned(client_s_pk)?
|
|
||||||
};
|
|
||||||
let server_identity = if let Some(server) = ids.server {
|
|
||||||
Input::<U2, _>::from(server)?
|
|
||||||
} else {
|
|
||||||
Input::<U2, _>::from_owned(server_s_pk)?
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok((client_identity, server_identity))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Internal function for computing the blind result by calling the voprf
|
/// Internal function for computing the blind result by calling the voprf
|
||||||
/// library. Note that for tests, we use the deterministic blinding in order to
|
/// library. Note that for tests, we use the deterministic blinding in order to
|
||||||
/// be able to set the blinding factor directly from the passed-in rng.
|
/// be able to set the blinding factor directly from the passed-in rng.
|
||||||
@@ -1266,10 +1138,11 @@ fn blind<CS: CipherSuite, R: RngCore + CryptoRng>(
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
let result = {
|
let result = {
|
||||||
let mut blind_bytes = GenericArray::<_, <OprfGroup<CS> as Group>::ScalarLen>::default();
|
let mut blind_bytes =
|
||||||
|
GenericArray::<_, <OprfGroup<CS> as voprf::Group>::ScalarLen>::default();
|
||||||
let blind = loop {
|
let blind = loop {
|
||||||
rng.fill_bytes(&mut blind_bytes);
|
rng.fill_bytes(&mut blind_bytes);
|
||||||
if let Ok(scalar) = <OprfGroup<CS> as Group>::deserialize_scalar(&blind_bytes) {
|
if let Ok(scalar) = <OprfGroup<CS> as voprf::Group>::deserialize_scalar(&blind_bytes) {
|
||||||
break scalar;
|
break scalar;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -1278,3 +1151,91 @@ fn blind<CS: CipherSuite, R: RngCore + CryptoRng>(
|
|||||||
|
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//////////////////////////
|
||||||
|
// Test Implementations //
|
||||||
|
//===================== //
|
||||||
|
//////////////////////////
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
use crate::serialization::AssertZeroized;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
impl<CS: CipherSuite> AssertZeroized for ClientRegistration<CS> {
|
||||||
|
fn assert_zeroized(&self) {
|
||||||
|
let Self {
|
||||||
|
oprf_client,
|
||||||
|
blinded_element,
|
||||||
|
} = self;
|
||||||
|
|
||||||
|
for byte in oprf_client
|
||||||
|
.serialize()
|
||||||
|
.iter()
|
||||||
|
.chain(&blinded_element.serialize())
|
||||||
|
{
|
||||||
|
assert_eq!(byte, &0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
impl<CS: CipherSuite> AssertZeroized for ServerRegistration<CS>
|
||||||
|
where
|
||||||
|
<KeGroup<CS> as Group>::Pk: AssertZeroized,
|
||||||
|
{
|
||||||
|
fn assert_zeroized(&self) {
|
||||||
|
let RegistrationUpload {
|
||||||
|
envelope,
|
||||||
|
masking_key,
|
||||||
|
client_s_pk,
|
||||||
|
} = &self.0;
|
||||||
|
|
||||||
|
envelope.assert_zeroized();
|
||||||
|
|
||||||
|
assert_eq!(masking_key, &GenericArray::default());
|
||||||
|
client_s_pk.assert_zeroized();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
impl<CS: CipherSuite> AssertZeroized for ClientLogin<CS>
|
||||||
|
where
|
||||||
|
<CS::KeyExchange as KeyExchange>::KE1State: AssertZeroized,
|
||||||
|
<CS::KeyExchange as KeyExchange>::KE1Message: AssertZeroized,
|
||||||
|
{
|
||||||
|
fn assert_zeroized(&self) {
|
||||||
|
let Self {
|
||||||
|
ke1_state,
|
||||||
|
credential_request,
|
||||||
|
oprf_client,
|
||||||
|
} = self;
|
||||||
|
let CredentialRequest {
|
||||||
|
blinded_element,
|
||||||
|
ke1_message,
|
||||||
|
} = credential_request;
|
||||||
|
|
||||||
|
ke1_state.assert_zeroized();
|
||||||
|
ke1_message.assert_zeroized();
|
||||||
|
|
||||||
|
for byte in oprf_client
|
||||||
|
.serialize()
|
||||||
|
.iter()
|
||||||
|
.chain(&blinded_element.serialize())
|
||||||
|
{
|
||||||
|
assert_eq!(byte, &0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
impl<CS: CipherSuite> AssertZeroized for ServerLogin<CS>
|
||||||
|
where
|
||||||
|
PublicKey<KeGroup<CS>>: AssertZeroized,
|
||||||
|
<CS::KeyExchange as KeyExchange>::KE2State<CS>: AssertZeroized,
|
||||||
|
{
|
||||||
|
fn assert_zeroized(&self) {
|
||||||
|
let Self { ke2_state } = self;
|
||||||
|
|
||||||
|
ke2_state.assert_zeroized();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+61
-91
@@ -6,12 +6,12 @@
|
|||||||
// of this source tree. You may select, at your option, one of the above-listed
|
// of this source tree. You may select, at your option, one of the above-listed
|
||||||
// licenses.
|
// licenses.
|
||||||
|
|
||||||
use core::marker::PhantomData;
|
use core::ops::Add;
|
||||||
|
|
||||||
use digest::Update;
|
use digest::Update;
|
||||||
use generic_array::typenum::{U0, U2};
|
use generic_array::sequence::Concat;
|
||||||
|
use generic_array::typenum::Sum;
|
||||||
use generic_array::{ArrayLength, GenericArray};
|
use generic_array::{ArrayLength, GenericArray};
|
||||||
use hmac::Mac;
|
|
||||||
|
|
||||||
use crate::errors::ProtocolError;
|
use crate::errors::ProtocolError;
|
||||||
|
|
||||||
@@ -44,93 +44,19 @@ pub(crate) fn os2ip(input: &[u8]) -> Result<usize, ProtocolError> {
|
|||||||
Ok(usize::from_be_bytes(output_array))
|
Ok(usize::from_be_bytes(output_array))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Computes `I2OSP(len(input), max_bytes) || input` and helps hold output
|
|
||||||
/// without allocation.
|
|
||||||
pub(crate) struct Input<'a, L1: ArrayLength<u8>, L2: ArrayLength<u8> = U0, L3: ArrayLength<u8> = U0>
|
|
||||||
{
|
|
||||||
octet: GenericArray<u8, L1>,
|
|
||||||
input: InnerInput<'a, L2, L3>,
|
|
||||||
}
|
|
||||||
|
|
||||||
enum InnerInput<'a, L1: ArrayLength<u8>, L2: ArrayLength<u8>> {
|
|
||||||
Owned(GenericArray<u8, L1>),
|
|
||||||
Borrowed(&'a [u8]),
|
|
||||||
Label(([&'a [u8]; 2], PhantomData<L2>)),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, L1: ArrayLength<u8>, L2: ArrayLength<u8>, L3: ArrayLength<u8>> Input<'a, L1, L2, L3> {
|
|
||||||
// Variation of `serialize` that takes a borrowed `input
|
|
||||||
pub(crate) fn from(input: &'a [u8]) -> Result<Input<'a, L1, L2>, ProtocolError> {
|
|
||||||
Ok(Input {
|
|
||||||
octet: i2osp::<L1>(input.len())?,
|
|
||||||
input: InnerInput::Borrowed(input),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Variation of `serialize` that takes an owned `input`
|
|
||||||
pub(crate) fn from_owned(
|
|
||||||
input: GenericArray<u8, L2>,
|
|
||||||
) -> Result<Input<'a, L1, L2>, ProtocolError> {
|
|
||||||
Ok(Input {
|
|
||||||
octet: i2osp::<L1>(input.len())?,
|
|
||||||
input: InnerInput::Owned(input),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Variation of `serialize` that takes a label
|
|
||||||
pub(crate) fn from_label(
|
|
||||||
opaque: &'a [u8],
|
|
||||||
label: &'a [u8],
|
|
||||||
) -> Result<Input<'a, L1, U0, U2>, ProtocolError> {
|
|
||||||
Ok(Input {
|
|
||||||
octet: i2osp::<L1>(opaque.len() + label.len())?,
|
|
||||||
input: InnerInput::Label(([opaque, label], PhantomData)),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn iter(&self) -> impl Iterator<Item = &[u8]> {
|
|
||||||
// Some magic to make it output the same type in all branches.
|
|
||||||
[self.octet.as_slice()]
|
|
||||||
.into_iter()
|
|
||||||
.chain(match &self.input {
|
|
||||||
InnerInput::Owned(bytes) => [bytes.as_slice()],
|
|
||||||
InnerInput::Borrowed(bytes) => [*bytes],
|
|
||||||
InnerInput::Label((iter, _)) => [iter[0]],
|
|
||||||
})
|
|
||||||
.chain(if let InnerInput::Label((iter, _)) = &self.input {
|
|
||||||
Some(iter[1])
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<L1: ArrayLength<u8>, L2: ArrayLength<u8>> Input<'_, L1, L2, U0> {
|
|
||||||
pub(crate) fn to_array_2(&self) -> [&[u8]; 2] {
|
|
||||||
let input = match &self.input {
|
|
||||||
InnerInput::Borrowed(value) => value,
|
|
||||||
InnerInput::Owned(value) => value.as_slice(),
|
|
||||||
_ => unreachable!("unexpected `Serialize` constructed with wrong generics"),
|
|
||||||
};
|
|
||||||
|
|
||||||
[self.octet.as_slice(), input]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<L1: ArrayLength<u8>, L2: ArrayLength<u8>> Input<'_, L1, L2, U2> {
|
|
||||||
pub(crate) fn to_array_3(&self) -> [&[u8]; 3] {
|
|
||||||
match self.input {
|
|
||||||
InnerInput::Label((label, _)) => [self.octet.as_slice(), label[0], label[1]],
|
|
||||||
_ => unreachable!("unexpected `Serialize` constructed with wrong generics"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) trait UpdateExt {
|
pub(crate) trait UpdateExt {
|
||||||
|
fn update_iter<'a>(&mut self, iter: impl Iterator<Item = &'a [u8]>);
|
||||||
|
|
||||||
fn chain_iter<'a>(self, iter: impl Iterator<Item = &'a [u8]>) -> Self;
|
fn chain_iter<'a>(self, iter: impl Iterator<Item = &'a [u8]>) -> Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Update> UpdateExt for T {
|
impl<T: Update> UpdateExt for T {
|
||||||
|
fn update_iter<'a>(&mut self, iter: impl Iterator<Item = &'a [u8]>) {
|
||||||
|
for bytes in iter {
|
||||||
|
self.update(bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn chain_iter<'a>(self, iter: impl Iterator<Item = &'a [u8]>) -> Self {
|
fn chain_iter<'a>(self, iter: impl Iterator<Item = &'a [u8]>) -> Self {
|
||||||
let mut self_ = self;
|
let mut self_ = self;
|
||||||
|
|
||||||
@@ -142,18 +68,62 @@ impl<T: Update> UpdateExt for T {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) trait MacExt {
|
pub(crate) trait SliceExt {
|
||||||
fn update_iter<'a>(&mut self, iter: impl Iterator<Item = &'a [u8]>);
|
fn take_array<L: ArrayLength<u8>>(
|
||||||
|
self: &mut &Self,
|
||||||
|
name: &'static str,
|
||||||
|
) -> Result<GenericArray<u8, L>, ProtocolError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Mac> MacExt for T {
|
impl SliceExt for [u8] {
|
||||||
fn update_iter<'a>(&mut self, iter: impl Iterator<Item = &'a [u8]>) {
|
fn take_array<L: ArrayLength<u8>>(
|
||||||
for bytes in iter {
|
self: &mut &Self,
|
||||||
self.update(bytes);
|
name: &'static str,
|
||||||
|
) -> Result<GenericArray<u8, L>, ProtocolError> {
|
||||||
|
if L::USIZE > self.len() {
|
||||||
|
return Err(ProtocolError::SizeError {
|
||||||
|
name,
|
||||||
|
len: L::USIZE,
|
||||||
|
actual_len: self.len(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let (front, back) = self.split_at(L::USIZE);
|
||||||
|
*self = back;
|
||||||
|
Ok(GenericArray::clone_from_slice(front))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) trait GenericArrayExt<O: ArrayLength<u8>> {
|
||||||
|
type Output: ArrayLength<u8>;
|
||||||
|
|
||||||
|
/// This allows us to concat two [`GenericArray`]s but with `where` bounds
|
||||||
|
/// `Other + Self`. Because sometimes `Self + Other` doesn't imply the
|
||||||
|
/// bounds and we have to add them to every call.
|
||||||
|
fn concat_ext(&self, rest: &GenericArray<u8, O>) -> GenericArray<u8, Self::Output>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<L: ArrayLength<u8>, O: ArrayLength<u8>> GenericArrayExt<O> for GenericArray<u8, L>
|
||||||
|
where
|
||||||
|
O: Add<L>,
|
||||||
|
Sum<O, L>: ArrayLength<u8>,
|
||||||
|
{
|
||||||
|
type Output = Sum<O, L>;
|
||||||
|
|
||||||
|
fn concat_ext(&self, other: &GenericArray<u8, O>) -> GenericArray<u8, Self::Output> {
|
||||||
|
let mut output = GenericArray::<u8, O>::default().concat(GenericArray::<u8, L>::default());
|
||||||
|
output[..L::USIZE].copy_from_slice(self);
|
||||||
|
output[L::USIZE..].copy_from_slice(other);
|
||||||
|
|
||||||
|
output
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub(crate) trait AssertZeroized {
|
||||||
|
fn assert_zeroized(&self);
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|
||||||
|
|||||||
+462
-147
@@ -17,17 +17,17 @@ use proptest::collection::vec;
|
|||||||
use proptest::prelude::*;
|
use proptest::prelude::*;
|
||||||
use rand::rngs::OsRng;
|
use rand::rngs::OsRng;
|
||||||
use rand::RngCore;
|
use rand::RngCore;
|
||||||
use voprf::Group;
|
use voprf::Group as _;
|
||||||
|
|
||||||
use crate::ciphersuite::{CipherSuite, OprfGroup, OprfHash};
|
use crate::ciphersuite::{CipherSuite, KeGroup, OprfGroup, OprfHash};
|
||||||
use crate::envelope::{Envelope, EnvelopeLen, InnerEnvelopeMode};
|
use crate::envelope::{Envelope, EnvelopeLen, InnerEnvelopeMode};
|
||||||
use crate::errors::*;
|
use crate::errors::*;
|
||||||
use crate::hash::OutputSize;
|
use crate::hash::OutputSize;
|
||||||
use crate::key_exchange::group::KeGroup;
|
use crate::key_exchange::group::Group;
|
||||||
|
use crate::key_exchange::shared::NonceLen;
|
||||||
use crate::key_exchange::traits::{
|
use crate::key_exchange::traits::{
|
||||||
Deserialize, Ke1MessageLen, Ke1StateLen, Ke2MessageLen, KeyExchange, Serialize,
|
Deserialize, Ke1MessageLen, Ke1StateLen, Ke2MessageLen, KeyExchange, Serialize,
|
||||||
};
|
};
|
||||||
use crate::key_exchange::tripledh::{NonceLen, TripleDh};
|
|
||||||
use crate::keypair::KeyPair;
|
use crate::keypair::KeyPair;
|
||||||
use crate::messages::CredentialResponseWithoutKeLen;
|
use crate::messages::CredentialResponseWithoutKeLen;
|
||||||
use crate::opaque::{ClientLoginLen, ClientRegistrationLen, MaskedResponseLen};
|
use crate::opaque::{ClientLoginLen, ClientRegistrationLen, MaskedResponseLen};
|
||||||
@@ -35,47 +35,101 @@ use crate::serialization::{i2osp, os2ip};
|
|||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
#[cfg(feature = "ristretto255")]
|
#[cfg(feature = "ristretto255")]
|
||||||
struct Ristretto255;
|
struct TripleDhRistretto255;
|
||||||
|
|
||||||
#[cfg(feature = "ristretto255")]
|
#[cfg(feature = "ristretto255")]
|
||||||
impl CipherSuite for Ristretto255 {
|
impl CipherSuite for TripleDhRistretto255 {
|
||||||
type OprfCs = crate::Ristretto255;
|
type OprfCs = Ristretto255;
|
||||||
type KeGroup = crate::Ristretto255;
|
type KeyExchange = TripleDh<Ristretto255, sha2::Sha512>;
|
||||||
type KeyExchange = TripleDh;
|
|
||||||
type Ksf = crate::ksf::Identity;
|
type Ksf = crate::ksf::Identity;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct P256;
|
#[cfg(all(feature = "ristretto255", feature = "curve25519"))]
|
||||||
|
struct TripleDhCurve25519;
|
||||||
|
|
||||||
impl CipherSuite for P256 {
|
#[cfg(all(feature = "ristretto255", feature = "curve25519"))]
|
||||||
|
impl CipherSuite for TripleDhCurve25519 {
|
||||||
|
type OprfCs = Ristretto255;
|
||||||
|
type KeyExchange = TripleDh<Curve25519, sha2::Sha512>;
|
||||||
|
type Ksf = crate::ksf::Identity;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TripleDhP256;
|
||||||
|
|
||||||
|
impl CipherSuite for TripleDhP256 {
|
||||||
type OprfCs = ::p256::NistP256;
|
type OprfCs = ::p256::NistP256;
|
||||||
type KeGroup = ::p256::NistP256;
|
type KeyExchange = TripleDh<::p256::NistP256, sha2::Sha256>;
|
||||||
type KeyExchange = TripleDh;
|
|
||||||
type Ksf = crate::ksf::Identity;
|
type Ksf = crate::ksf::Identity;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct P384;
|
struct TripleDhP384;
|
||||||
|
|
||||||
impl CipherSuite for P384 {
|
impl CipherSuite for TripleDhP384 {
|
||||||
type OprfCs = ::p384::NistP384;
|
type OprfCs = ::p384::NistP384;
|
||||||
type KeGroup = ::p384::NistP384;
|
type KeyExchange = TripleDh<::p384::NistP384, sha2::Sha384>;
|
||||||
type KeyExchange = TripleDh;
|
|
||||||
type Ksf = crate::ksf::Identity;
|
type Ksf = crate::ksf::Identity;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct P521;
|
struct TripleDhP521;
|
||||||
|
|
||||||
impl CipherSuite for P521 {
|
impl CipherSuite for TripleDhP521 {
|
||||||
type OprfCs = ::p521::NistP521;
|
type OprfCs = ::p521::NistP521;
|
||||||
type KeGroup = ::p521::NistP521;
|
type KeyExchange = TripleDh<::p521::NistP521, sha2::Sha512>;
|
||||||
type KeyExchange = TripleDh;
|
|
||||||
type Ksf = crate::ksf::Identity;
|
type Ksf = crate::ksf::Identity;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn random_point<CS: CipherSuite>() -> <CS::KeGroup as KeGroup>::Pk {
|
#[cfg(feature = "ecdsa")]
|
||||||
|
struct SigmaIP256;
|
||||||
|
|
||||||
|
#[cfg(feature = "ecdsa")]
|
||||||
|
impl CipherSuite for SigmaIP256 {
|
||||||
|
type OprfCs = ::p256::NistP256;
|
||||||
|
type KeyExchange =
|
||||||
|
SigmaI<Ecdsa<::p256::NistP256, sha2::Sha256>, ::p256::NistP256, sha2::Sha256>;
|
||||||
|
type Ksf = crate::ksf::Identity;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "ecdsa")]
|
||||||
|
struct SigmaIP384;
|
||||||
|
|
||||||
|
#[cfg(feature = "ecdsa")]
|
||||||
|
impl CipherSuite for SigmaIP384 {
|
||||||
|
type OprfCs = ::p384::NistP384;
|
||||||
|
type KeyExchange =
|
||||||
|
SigmaI<Ecdsa<::p384::NistP384, sha2::Sha384>, ::p384::NistP384, sha2::Sha384>;
|
||||||
|
type Ksf = crate::ksf::Identity;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(all(feature = "ristretto255", feature = "ed25519",))]
|
||||||
|
struct SigmaIEd25519;
|
||||||
|
|
||||||
|
#[cfg(all(feature = "ristretto255", feature = "ed25519",))]
|
||||||
|
impl CipherSuite for SigmaIEd25519 {
|
||||||
|
type OprfCs = Ristretto255;
|
||||||
|
type KeyExchange = SigmaI<PureEddsa<Ed25519>, Ristretto255, sha2::Sha512>;
|
||||||
|
type Ksf = crate::ksf::Identity;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(all(feature = "ristretto255", feature = "ed25519"))]
|
||||||
|
struct SigmaIEd25519Ph;
|
||||||
|
|
||||||
|
#[cfg(all(feature = "ristretto255", feature = "ed25519",))]
|
||||||
|
impl CipherSuite for SigmaIEd25519Ph {
|
||||||
|
type OprfCs = Ristretto255;
|
||||||
|
type KeyExchange = SigmaI<HashEddsa<Ed25519>, Ristretto255, sha2::Sha512>;
|
||||||
|
type Ksf = crate::ksf::Identity;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn random_point<CS: CipherSuite>() -> <KeGroup<CS> as Group>::Pk {
|
||||||
let mut rng = OsRng;
|
let mut rng = OsRng;
|
||||||
let sk = CS::KeGroup::random_sk(&mut rng);
|
let sk = KeGroup::<CS>::random_sk(&mut rng);
|
||||||
CS::KeGroup::public_key(sk)
|
KeGroup::<CS>::public_key(sk)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn random_element<CS: CipherSuite>() -> <OprfGroup<CS> as voprf::Group>::Elem {
|
||||||
|
let mut rng = OsRng;
|
||||||
|
let scalar = OprfGroup::<CS>::random_scalar(&mut rng);
|
||||||
|
OprfGroup::<CS>::base_elem() * &scalar
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -83,7 +137,7 @@ fn client_registration_roundtrip() -> Result<(), ProtocolError> {
|
|||||||
fn inner<CS: CipherSuite>() -> Result<(), ProtocolError>
|
fn inner<CS: CipherSuite>() -> Result<(), ProtocolError>
|
||||||
where
|
where
|
||||||
// ClientRegistration: KgSk + KgPk
|
// ClientRegistration: KgSk + KgPk
|
||||||
<OprfGroup<CS> as Group>::ScalarLen: Add<<OprfGroup<CS> as Group>::ElemLen>,
|
<OprfGroup<CS> as voprf::Group>::ScalarLen: Add<<OprfGroup<CS> as voprf::Group>::ElemLen>,
|
||||||
ClientRegistrationLen<CS>: ArrayLength<u8>,
|
ClientRegistrationLen<CS>: ArrayLength<u8>,
|
||||||
{
|
{
|
||||||
let pw = b"hunter2";
|
let pw = b"hunter2";
|
||||||
@@ -106,10 +160,20 @@ fn client_registration_roundtrip() -> Result<(), ProtocolError> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "ristretto255")]
|
#[cfg(feature = "ristretto255")]
|
||||||
inner::<Ristretto255>()?;
|
inner::<TripleDhRistretto255>()?;
|
||||||
inner::<P256>()?;
|
#[cfg(all(feature = "ristretto255", feature = "curve25519"))]
|
||||||
inner::<P384>()?;
|
inner::<TripleDhCurve25519>()?;
|
||||||
inner::<P521>()?;
|
inner::<TripleDhP256>()?;
|
||||||
|
inner::<TripleDhP384>()?;
|
||||||
|
inner::<TripleDhP521>()?;
|
||||||
|
#[cfg(feature = "ecdsa")]
|
||||||
|
inner::<SigmaIP256>()?;
|
||||||
|
#[cfg(feature = "ecdsa")]
|
||||||
|
inner::<SigmaIP384>()?;
|
||||||
|
#[cfg(all(feature = "ristretto255", feature = "ed25519"))]
|
||||||
|
inner::<SigmaIEd25519>()?;
|
||||||
|
#[cfg(all(feature = "ristretto255", feature = "ed25519"))]
|
||||||
|
inner::<SigmaIEd25519Ph>()?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -118,12 +182,9 @@ fn client_registration_roundtrip() -> Result<(), ProtocolError> {
|
|||||||
fn server_registration_roundtrip() -> Result<(), ProtocolError> {
|
fn server_registration_roundtrip() -> Result<(), ProtocolError> {
|
||||||
fn inner<CS: CipherSuite>() -> Result<(), ProtocolError>
|
fn inner<CS: CipherSuite>() -> Result<(), ProtocolError>
|
||||||
where
|
where
|
||||||
// Envelope: Nonce + Hash
|
|
||||||
NonceLen: Add<OutputSize<OprfHash<CS>>>,
|
|
||||||
EnvelopeLen<CS>: ArrayLength<u8>,
|
|
||||||
// RegistrationUpload: (KePk + Hash) + Envelope
|
// RegistrationUpload: (KePk + Hash) + Envelope
|
||||||
<CS::KeGroup as KeGroup>::PkLen: Add<OutputSize<OprfHash<CS>>>,
|
<KeGroup<CS> as Group>::PkLen: Add<OutputSize<OprfHash<CS>>>,
|
||||||
Sum<<CS::KeGroup as KeGroup>::PkLen, OutputSize<OprfHash<CS>>>:
|
Sum<<KeGroup<CS> as Group>::PkLen, OutputSize<OprfHash<CS>>>:
|
||||||
ArrayLength<u8> + Add<EnvelopeLen<CS>>,
|
ArrayLength<u8> + Add<EnvelopeLen<CS>>,
|
||||||
RegistrationUploadLen<CS>: ArrayLength<u8>,
|
RegistrationUploadLen<CS>: ArrayLength<u8>,
|
||||||
// ServerRegistration = RegistrationUpload
|
// ServerRegistration = RegistrationUpload
|
||||||
@@ -142,7 +203,7 @@ fn server_registration_roundtrip() -> Result<(), ProtocolError> {
|
|||||||
// length-MAC_SIZE hmac
|
// length-MAC_SIZE hmac
|
||||||
mock_envelope_bytes.extend_from_slice(&Output::<OprfHash<CS>>::default());
|
mock_envelope_bytes.extend_from_slice(&Output::<OprfHash<CS>>::default());
|
||||||
|
|
||||||
let mock_client_kp = KeyPair::<CS::KeGroup>::generate_random::<CS::OprfCs, _>(&mut rng);
|
let mock_client_kp = KeyPair::<KeGroup<CS>>::derive_random(&mut rng);
|
||||||
// serialization order: oprf_key, public key, envelope
|
// serialization order: oprf_key, public key, envelope
|
||||||
let mut bytes = Vec::<u8>::new();
|
let mut bytes = Vec::<u8>::new();
|
||||||
bytes.extend_from_slice(&mock_client_kp.public().serialize());
|
bytes.extend_from_slice(&mock_client_kp.public().serialize());
|
||||||
@@ -155,10 +216,20 @@ fn server_registration_roundtrip() -> Result<(), ProtocolError> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "ristretto255")]
|
#[cfg(feature = "ristretto255")]
|
||||||
inner::<Ristretto255>()?;
|
inner::<TripleDhRistretto255>()?;
|
||||||
inner::<P256>()?;
|
#[cfg(all(feature = "ristretto255", feature = "curve25519"))]
|
||||||
inner::<P384>()?;
|
inner::<TripleDhCurve25519>()?;
|
||||||
inner::<P521>()?;
|
inner::<TripleDhP256>()?;
|
||||||
|
inner::<TripleDhP384>()?;
|
||||||
|
inner::<TripleDhP521>()?;
|
||||||
|
#[cfg(feature = "ecdsa")]
|
||||||
|
inner::<SigmaIP256>()?;
|
||||||
|
#[cfg(feature = "ecdsa")]
|
||||||
|
inner::<SigmaIP384>()?;
|
||||||
|
#[cfg(all(feature = "ristretto255", feature = "ed25519"))]
|
||||||
|
inner::<SigmaIEd25519>()?;
|
||||||
|
#[cfg(all(feature = "ristretto255", feature = "ed25519"))]
|
||||||
|
inner::<SigmaIEd25519Ph>()?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -166,11 +237,11 @@ fn server_registration_roundtrip() -> Result<(), ProtocolError> {
|
|||||||
#[test]
|
#[test]
|
||||||
fn registration_request_roundtrip() -> Result<(), ProtocolError> {
|
fn registration_request_roundtrip() -> Result<(), ProtocolError> {
|
||||||
fn inner<CS: CipherSuite>() -> Result<(), ProtocolError> {
|
fn inner<CS: CipherSuite>() -> Result<(), ProtocolError> {
|
||||||
let pt = random_point::<CS>();
|
let elem = random_element::<CS>();
|
||||||
let pt_bytes = CS::KeGroup::serialize_pk(pt);
|
let elem_bytes = OprfGroup::<CS>::serialize_elem(elem);
|
||||||
|
|
||||||
let mut input = Vec::new();
|
let mut input = Vec::new();
|
||||||
input.extend_from_slice(&pt_bytes);
|
input.extend_from_slice(&elem_bytes);
|
||||||
|
|
||||||
let r1 = RegistrationRequest::<CS>::deserialize(&input)?;
|
let r1 = RegistrationRequest::<CS>::deserialize(&input)?;
|
||||||
let r1_bytes = r1.serialize();
|
let r1_bytes = r1.serialize();
|
||||||
@@ -191,10 +262,20 @@ fn registration_request_roundtrip() -> Result<(), ProtocolError> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "ristretto255")]
|
#[cfg(feature = "ristretto255")]
|
||||||
inner::<Ristretto255>()?;
|
inner::<TripleDhRistretto255>()?;
|
||||||
inner::<P256>()?;
|
#[cfg(all(feature = "ristretto255", feature = "curve25519"))]
|
||||||
inner::<P384>()?;
|
inner::<TripleDhCurve25519>()?;
|
||||||
inner::<P521>()?;
|
inner::<TripleDhP256>()?;
|
||||||
|
inner::<TripleDhP384>()?;
|
||||||
|
inner::<TripleDhP521>()?;
|
||||||
|
#[cfg(feature = "ecdsa")]
|
||||||
|
inner::<SigmaIP256>()?;
|
||||||
|
#[cfg(feature = "ecdsa")]
|
||||||
|
inner::<SigmaIP384>()?;
|
||||||
|
#[cfg(all(feature = "ristretto255", feature = "ed25519"))]
|
||||||
|
inner::<SigmaIEd25519>()?;
|
||||||
|
#[cfg(all(feature = "ristretto255", feature = "ed25519"))]
|
||||||
|
inner::<SigmaIEd25519Ph>()?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -204,13 +285,13 @@ fn registration_response_roundtrip() -> Result<(), ProtocolError> {
|
|||||||
fn inner<CS: CipherSuite>() -> Result<(), ProtocolError>
|
fn inner<CS: CipherSuite>() -> Result<(), ProtocolError>
|
||||||
where
|
where
|
||||||
// RegistrationResponse: KgPk + KePk
|
// RegistrationResponse: KgPk + KePk
|
||||||
<OprfGroup<CS> as Group>::ElemLen: Add<<CS::KeGroup as KeGroup>::PkLen>,
|
<OprfGroup<CS> as voprf::Group>::ElemLen: Add<<KeGroup<CS> as Group>::PkLen>,
|
||||||
RegistrationResponseLen<CS>: ArrayLength<u8>,
|
RegistrationResponseLen<CS>: ArrayLength<u8>,
|
||||||
{
|
{
|
||||||
let pt = random_point::<CS>();
|
let elem = random_element::<CS>();
|
||||||
let beta_bytes = CS::KeGroup::serialize_pk(pt);
|
let beta_bytes = OprfGroup::<CS>::serialize_elem(elem);
|
||||||
let mut rng = OsRng;
|
let mut rng = OsRng;
|
||||||
let skp = KeyPair::<CS::KeGroup>::generate_random::<CS::OprfCs, _>(&mut rng);
|
let skp = KeyPair::<KeGroup<CS>>::derive_random(&mut rng);
|
||||||
let pubkey_bytes = skp.public().serialize();
|
let pubkey_bytes = skp.public().serialize();
|
||||||
|
|
||||||
let mut input = Vec::new();
|
let mut input = Vec::new();
|
||||||
@@ -238,10 +319,20 @@ fn registration_response_roundtrip() -> Result<(), ProtocolError> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "ristretto255")]
|
#[cfg(feature = "ristretto255")]
|
||||||
inner::<Ristretto255>()?;
|
inner::<TripleDhRistretto255>()?;
|
||||||
inner::<P256>()?;
|
#[cfg(all(feature = "ristretto255", feature = "curve25519"))]
|
||||||
inner::<P384>()?;
|
inner::<TripleDhCurve25519>()?;
|
||||||
inner::<P521>()?;
|
inner::<TripleDhP256>()?;
|
||||||
|
inner::<TripleDhP384>()?;
|
||||||
|
inner::<TripleDhP521>()?;
|
||||||
|
#[cfg(feature = "ecdsa")]
|
||||||
|
inner::<SigmaIP256>()?;
|
||||||
|
#[cfg(feature = "ecdsa")]
|
||||||
|
inner::<SigmaIP384>()?;
|
||||||
|
#[cfg(all(feature = "ristretto255", feature = "ed25519"))]
|
||||||
|
inner::<SigmaIEd25519>()?;
|
||||||
|
#[cfg(all(feature = "ristretto255", feature = "ed25519"))]
|
||||||
|
inner::<SigmaIEd25519Ph>()?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -250,17 +341,14 @@ fn registration_response_roundtrip() -> Result<(), ProtocolError> {
|
|||||||
fn registration_upload_roundtrip() -> Result<(), ProtocolError> {
|
fn registration_upload_roundtrip() -> Result<(), ProtocolError> {
|
||||||
fn inner<CS: CipherSuite>() -> Result<(), ProtocolError>
|
fn inner<CS: CipherSuite>() -> Result<(), ProtocolError>
|
||||||
where
|
where
|
||||||
// Envelope: Nonce + Hash
|
|
||||||
NonceLen: Add<OutputSize<OprfHash<CS>>>,
|
|
||||||
EnvelopeLen<CS>: ArrayLength<u8>,
|
|
||||||
// RegistrationUpload: (KePk + Hash) + Envelope
|
// RegistrationUpload: (KePk + Hash) + Envelope
|
||||||
<CS::KeGroup as KeGroup>::PkLen: Add<OutputSize<OprfHash<CS>>>,
|
<KeGroup<CS> as Group>::PkLen: Add<OutputSize<OprfHash<CS>>>,
|
||||||
Sum<<CS::KeGroup as KeGroup>::PkLen, OutputSize<OprfHash<CS>>>:
|
Sum<<KeGroup<CS> as Group>::PkLen, OutputSize<OprfHash<CS>>>:
|
||||||
ArrayLength<u8> + Add<EnvelopeLen<CS>>,
|
ArrayLength<u8> + Add<EnvelopeLen<CS>>,
|
||||||
RegistrationUploadLen<CS>: ArrayLength<u8>,
|
RegistrationUploadLen<CS>: ArrayLength<u8>,
|
||||||
{
|
{
|
||||||
let mut rng = OsRng;
|
let mut rng = OsRng;
|
||||||
let skp = KeyPair::<CS::KeGroup>::generate_random::<CS::OprfCs, _>(&mut rng);
|
let skp = KeyPair::<KeGroup<CS>>::derive_random(&mut rng);
|
||||||
let pubkey_bytes = skp.public().serialize();
|
let pubkey_bytes = skp.public().serialize();
|
||||||
|
|
||||||
let mut key = [0u8; 32];
|
let mut key = [0u8; 32];
|
||||||
@@ -295,27 +383,38 @@ fn registration_upload_roundtrip() -> Result<(), ProtocolError> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "ristretto255")]
|
#[cfg(feature = "ristretto255")]
|
||||||
inner::<Ristretto255>()?;
|
inner::<TripleDhRistretto255>()?;
|
||||||
inner::<P256>()?;
|
#[cfg(all(feature = "ristretto255", feature = "curve25519"))]
|
||||||
inner::<P384>()?;
|
inner::<TripleDhCurve25519>()?;
|
||||||
inner::<P521>()?;
|
inner::<TripleDhP256>()?;
|
||||||
|
inner::<TripleDhP384>()?;
|
||||||
|
inner::<TripleDhP521>()?;
|
||||||
|
#[cfg(feature = "ecdsa")]
|
||||||
|
inner::<SigmaIP256>()?;
|
||||||
|
#[cfg(feature = "ecdsa")]
|
||||||
|
inner::<SigmaIP384>()?;
|
||||||
|
#[cfg(all(feature = "ristretto255", feature = "ed25519"))]
|
||||||
|
inner::<SigmaIEd25519>()?;
|
||||||
|
#[cfg(all(feature = "ristretto255", feature = "ed25519"))]
|
||||||
|
inner::<SigmaIEd25519Ph>()?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn credential_request_roundtrip() -> Result<(), ProtocolError> {
|
fn triple_dh_credential_request_roundtrip() -> Result<(), ProtocolError> {
|
||||||
fn inner<CS: CipherSuite>() -> Result<(), ProtocolError>
|
fn inner<CS: CipherSuite>() -> Result<(), ProtocolError>
|
||||||
where
|
where
|
||||||
|
<CS::KeyExchange as KeyExchange>::KE1Message: Deserialize + Serialize,
|
||||||
// CredentialRequest: KgPk + Ke1Message
|
// CredentialRequest: KgPk + Ke1Message
|
||||||
<OprfGroup<CS> as Group>::ElemLen: Add<Ke1MessageLen<CS>>,
|
<OprfGroup<CS> as voprf::Group>::ElemLen: Add<Ke1MessageLen<CS>>,
|
||||||
CredentialRequestLen<CS>: ArrayLength<u8>,
|
CredentialRequestLen<CS>: ArrayLength<u8>,
|
||||||
{
|
{
|
||||||
let mut rng = OsRng;
|
let mut rng = OsRng;
|
||||||
let alpha = random_point::<CS>();
|
let alpha = random_element::<CS>();
|
||||||
let alpha_bytes = CS::KeGroup::serialize_pk(alpha);
|
let alpha_bytes = OprfGroup::<CS>::serialize_elem(alpha);
|
||||||
|
|
||||||
let client_e_kp = KeyPair::<CS::KeGroup>::generate_random::<CS::OprfCs, _>(&mut rng);
|
let client_e_kp = KeyPair::<KeGroup<CS>>::derive_random(&mut rng);
|
||||||
let mut client_nonce = [0u8; NonceLen::USIZE];
|
let mut client_nonce = [0u8; NonceLen::USIZE];
|
||||||
rng.fill_bytes(&mut client_nonce);
|
rng.fill_bytes(&mut client_nonce);
|
||||||
|
|
||||||
@@ -348,34 +447,33 @@ fn credential_request_roundtrip() -> Result<(), ProtocolError> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "ristretto255")]
|
#[cfg(feature = "ristretto255")]
|
||||||
inner::<Ristretto255>()?;
|
inner::<TripleDhRistretto255>()?;
|
||||||
inner::<P256>()?;
|
#[cfg(all(feature = "ristretto255", feature = "curve25519"))]
|
||||||
inner::<P384>()?;
|
inner::<TripleDhCurve25519>()?;
|
||||||
inner::<P521>()?;
|
inner::<TripleDhP256>()?;
|
||||||
|
inner::<TripleDhP384>()?;
|
||||||
|
inner::<TripleDhP521>()?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn credential_response_roundtrip() -> Result<(), ProtocolError> {
|
fn triple_dh_credential_response_roundtrip() -> Result<(), ProtocolError> {
|
||||||
fn inner<CS: CipherSuite>() -> Result<(), ProtocolError>
|
fn inner<CS: CipherSuite>() -> Result<(), ProtocolError>
|
||||||
where
|
where
|
||||||
|
<CS::KeyExchange as KeyExchange>::KE2Message: Deserialize,
|
||||||
// CredentialResponseWithoutKeLen: (KgPk + Nonce) + MaskedResponse
|
// CredentialResponseWithoutKeLen: (KgPk + Nonce) + MaskedResponse
|
||||||
<OprfGroup<CS> as Group>::ElemLen: Add<NonceLen>,
|
<OprfGroup<CS> as voprf::Group>::ElemLen: Add<NonceLen>,
|
||||||
Sum<<OprfGroup<CS> as Group>::ElemLen, NonceLen>:
|
Sum<<OprfGroup<CS> as voprf::Group>::ElemLen, NonceLen>:
|
||||||
ArrayLength<u8> + Add<MaskedResponseLen<CS>>,
|
ArrayLength<u8> + Add<MaskedResponseLen<CS>>,
|
||||||
CredentialResponseWithoutKeLen<CS>: ArrayLength<u8>,
|
CredentialResponseWithoutKeLen<CS>: ArrayLength<u8>,
|
||||||
// MaskedResponse: (Nonce + Hash) + KePk
|
|
||||||
NonceLen: Add<OutputSize<OprfHash<CS>>>,
|
|
||||||
Sum<NonceLen, OutputSize<OprfHash<CS>>>:
|
|
||||||
ArrayLength<u8> + Add<<CS::KeGroup as KeGroup>::PkLen>,
|
|
||||||
MaskedResponseLen<CS>: ArrayLength<u8>,
|
|
||||||
// CredentialResponse: CredentialResponseWithoutKeLen + Ke2Message
|
// CredentialResponse: CredentialResponseWithoutKeLen + Ke2Message
|
||||||
|
<CS::KeyExchange as KeyExchange>::KE2Message: Serialize,
|
||||||
CredentialResponseWithoutKeLen<CS>: Add<Ke2MessageLen<CS>>,
|
CredentialResponseWithoutKeLen<CS>: Add<Ke2MessageLen<CS>>,
|
||||||
CredentialResponseLen<CS>: ArrayLength<u8>,
|
CredentialResponseLen<CS>: ArrayLength<u8>,
|
||||||
{
|
{
|
||||||
let pt = random_point::<CS>();
|
let elem = random_element::<CS>();
|
||||||
let pt_bytes = CS::KeGroup::serialize_pk(pt);
|
let elem_bytes = OprfGroup::<CS>::serialize_elem(elem);
|
||||||
|
|
||||||
let mut rng = OsRng;
|
let mut rng = OsRng;
|
||||||
|
|
||||||
@@ -383,10 +481,10 @@ fn credential_response_roundtrip() -> Result<(), ProtocolError> {
|
|||||||
rng.fill_bytes(&mut masking_nonce);
|
rng.fill_bytes(&mut masking_nonce);
|
||||||
|
|
||||||
let mut masked_response =
|
let mut masked_response =
|
||||||
vec![0u8; <OprfGroup<CS> as Group>::ElemLen::USIZE + Envelope::<CS>::len()];
|
vec![0u8; <OprfGroup<CS> as voprf::Group>::ElemLen::USIZE + Envelope::<CS>::len()];
|
||||||
rng.fill_bytes(&mut masked_response);
|
rng.fill_bytes(&mut masked_response);
|
||||||
|
|
||||||
let server_e_kp = KeyPair::<CS::KeGroup>::generate_random::<CS::OprfCs, _>(&mut rng);
|
let server_e_kp = KeyPair::<KeGroup<CS>>::derive_random(&mut rng);
|
||||||
let mut mac = Output::<OprfHash<CS>>::default();
|
let mut mac = Output::<OprfHash<CS>>::default();
|
||||||
rng.fill_bytes(&mut mac);
|
rng.fill_bytes(&mut mac);
|
||||||
let mut server_nonce = [0u8; NonceLen::USIZE];
|
let mut server_nonce = [0u8; NonceLen::USIZE];
|
||||||
@@ -399,6 +497,94 @@ fn credential_response_roundtrip() -> Result<(), ProtocolError> {
|
|||||||
]
|
]
|
||||||
.concat();
|
.concat();
|
||||||
|
|
||||||
|
let mut input = Vec::new();
|
||||||
|
input.extend_from_slice(&elem_bytes);
|
||||||
|
input.extend_from_slice(&masking_nonce);
|
||||||
|
input.extend_from_slice(&masked_response);
|
||||||
|
input.extend_from_slice(&ke2m);
|
||||||
|
|
||||||
|
let l2 = CredentialResponse::<CS>::deserialize(&input).unwrap();
|
||||||
|
let l2_bytes = l2.serialize();
|
||||||
|
assert_eq!(input, *l2_bytes);
|
||||||
|
|
||||||
|
// Assert that identity group element is rejected
|
||||||
|
let identity = OprfGroup::<CS>::identity_elem();
|
||||||
|
let identity_bytes = OprfGroup::<CS>::serialize_elem(identity).to_vec();
|
||||||
|
|
||||||
|
assert!(matches!(
|
||||||
|
CredentialResponse::<CS>::deserialize(
|
||||||
|
&[
|
||||||
|
identity_bytes,
|
||||||
|
masking_nonce.to_vec(),
|
||||||
|
masked_response,
|
||||||
|
ke2m.to_vec()
|
||||||
|
]
|
||||||
|
.concat()
|
||||||
|
),
|
||||||
|
Err(ProtocolError::LibraryError(InternalError::OprfError(
|
||||||
|
voprf::Error::Deserialization,
|
||||||
|
)))
|
||||||
|
));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "ristretto255")]
|
||||||
|
inner::<TripleDhRistretto255>()?;
|
||||||
|
#[cfg(all(feature = "ristretto255", feature = "curve25519"))]
|
||||||
|
inner::<TripleDhCurve25519>()?;
|
||||||
|
inner::<TripleDhP256>()?;
|
||||||
|
inner::<TripleDhP384>()?;
|
||||||
|
inner::<TripleDhP521>()?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(feature = "ecdsa")]
|
||||||
|
fn sigma_i_ecdsa_credential_response_roundtrip() -> Result<(), ProtocolError> {
|
||||||
|
fn inner<CS: CipherSuite>() -> Result<(), ProtocolError>
|
||||||
|
where
|
||||||
|
<CS::KeyExchange as KeyExchange>::KE2Message: Deserialize,
|
||||||
|
// CredentialResponseWithoutKeLen: (KgPk + Nonce) + MaskedResponse
|
||||||
|
<OprfGroup<CS> as voprf::Group>::ElemLen: Add<NonceLen>,
|
||||||
|
Sum<<OprfGroup<CS> as voprf::Group>::ElemLen, NonceLen>:
|
||||||
|
ArrayLength<u8> + Add<MaskedResponseLen<CS>>,
|
||||||
|
CredentialResponseWithoutKeLen<CS>: ArrayLength<u8>,
|
||||||
|
// CredentialResponse: CredentialResponseWithoutKeLen + Ke2Message
|
||||||
|
<CS::KeyExchange as KeyExchange>::KE2Message: Serialize,
|
||||||
|
CredentialResponseWithoutKeLen<CS>: Add<Ke2MessageLen<CS>>,
|
||||||
|
CredentialResponseLen<CS>: ArrayLength<u8>,
|
||||||
|
{
|
||||||
|
let pt = random_point::<CS>();
|
||||||
|
let pt_bytes = KeGroup::<CS>::serialize_pk(pt);
|
||||||
|
|
||||||
|
let mut rng = OsRng;
|
||||||
|
|
||||||
|
let mut masking_nonce = [0u8; 32];
|
||||||
|
rng.fill_bytes(&mut masking_nonce);
|
||||||
|
|
||||||
|
let mut masked_response =
|
||||||
|
vec![0u8; <OprfGroup<CS> as voprf::Group>::ElemLen::USIZE + Envelope::<CS>::len()];
|
||||||
|
rng.fill_bytes(&mut masked_response);
|
||||||
|
|
||||||
|
let server_e_kp = KeyPair::<KeGroup<CS>>::derive_random(&mut rng);
|
||||||
|
let r = KeGroup::<CS>::serialize_sk(KeGroup::<CS>::random_sk(&mut rng));
|
||||||
|
let s = KeGroup::<CS>::serialize_sk(KeGroup::<CS>::random_sk(&mut rng));
|
||||||
|
let mut mac = Output::<OprfHash<CS>>::default();
|
||||||
|
rng.fill_bytes(&mut mac);
|
||||||
|
let mut server_nonce = [0u8; NonceLen::USIZE];
|
||||||
|
rng.fill_bytes(&mut server_nonce);
|
||||||
|
|
||||||
|
let ke2m: Vec<u8> = [
|
||||||
|
server_nonce.as_ref(),
|
||||||
|
server_e_kp.public().serialize().as_ref(),
|
||||||
|
&r,
|
||||||
|
&s,
|
||||||
|
&mac,
|
||||||
|
]
|
||||||
|
.concat();
|
||||||
|
|
||||||
let mut input = Vec::new();
|
let mut input = Vec::new();
|
||||||
input.extend_from_slice(&pt_bytes);
|
input.extend_from_slice(&pt_bytes);
|
||||||
input.extend_from_slice(&masking_nonce);
|
input.extend_from_slice(&masking_nonce);
|
||||||
@@ -431,18 +617,18 @@ fn credential_response_roundtrip() -> Result<(), ProtocolError> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "ristretto255")]
|
inner::<SigmaIP256>()?;
|
||||||
inner::<Ristretto255>()?;
|
inner::<SigmaIP384>()?;
|
||||||
inner::<P256>()?;
|
|
||||||
inner::<P384>()?;
|
|
||||||
inner::<P521>()?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn credential_finalization_roundtrip() -> Result<(), ProtocolError> {
|
fn triple_dh_credential_finalization_roundtrip() -> Result<(), ProtocolError> {
|
||||||
fn inner<CS: CipherSuite>() -> Result<(), ProtocolError> {
|
fn inner<CS: CipherSuite>() -> Result<(), ProtocolError>
|
||||||
|
where
|
||||||
|
<CS::KeyExchange as KeyExchange>::KE3Message: Deserialize + Serialize,
|
||||||
|
{
|
||||||
let mut rng = OsRng;
|
let mut rng = OsRng;
|
||||||
let mut mac = Output::<OprfHash<CS>>::default();
|
let mut mac = Output::<OprfHash<CS>>::default();
|
||||||
rng.fill_bytes(&mut mac);
|
rng.fill_bytes(&mut mac);
|
||||||
@@ -457,31 +643,70 @@ fn credential_finalization_roundtrip() -> Result<(), ProtocolError> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "ristretto255")]
|
#[cfg(feature = "ristretto255")]
|
||||||
inner::<Ristretto255>()?;
|
inner::<TripleDhRistretto255>()?;
|
||||||
inner::<P256>()?;
|
#[cfg(all(feature = "ristretto255", feature = "curve25519"))]
|
||||||
inner::<P384>()?;
|
inner::<TripleDhCurve25519>()?;
|
||||||
inner::<P521>()?;
|
inner::<TripleDhP256>()?;
|
||||||
|
inner::<TripleDhP384>()?;
|
||||||
|
inner::<TripleDhP521>()?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn client_login_roundtrip() -> Result<(), ProtocolError> {
|
#[cfg(feature = "ecdsa")]
|
||||||
|
fn sigma_i_ecdsa_credential_finalization_roundtrip() -> Result<(), ProtocolError> {
|
||||||
fn inner<CS: CipherSuite>() -> Result<(), ProtocolError>
|
fn inner<CS: CipherSuite>() -> Result<(), ProtocolError>
|
||||||
where
|
where
|
||||||
|
<CS::KeyExchange as KeyExchange>::KE3Message: Deserialize + Serialize,
|
||||||
|
{
|
||||||
|
let mut rng = OsRng;
|
||||||
|
|
||||||
|
let r = KeGroup::<CS>::serialize_sk(KeGroup::<CS>::random_sk(&mut rng));
|
||||||
|
let s = KeGroup::<CS>::serialize_sk(KeGroup::<CS>::random_sk(&mut rng));
|
||||||
|
|
||||||
|
let mut mac = Output::<OprfHash<CS>>::default();
|
||||||
|
rng.fill_bytes(&mut mac);
|
||||||
|
|
||||||
|
let mut input = Vec::new();
|
||||||
|
input.extend_from_slice(&r);
|
||||||
|
input.extend_from_slice(&s);
|
||||||
|
input.extend_from_slice(&mac);
|
||||||
|
|
||||||
|
let l3 = CredentialFinalization::<CS>::deserialize(&input)?;
|
||||||
|
let l3_bytes = l3.serialize();
|
||||||
|
assert_eq!(input.as_slice(), l3_bytes.as_slice());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
inner::<SigmaIP256>()?;
|
||||||
|
inner::<SigmaIP384>()?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn triple_dh_client_login_roundtrip() -> Result<(), ProtocolError> {
|
||||||
|
fn inner<CS: CipherSuite>() -> Result<(), ProtocolError>
|
||||||
|
where
|
||||||
|
<CS::KeyExchange as KeyExchange>::KE1Message: Deserialize,
|
||||||
|
<CS::KeyExchange as KeyExchange>::KE1State: Deserialize,
|
||||||
// CredentialRequest: KgPk + Ke1Message
|
// CredentialRequest: KgPk + Ke1Message
|
||||||
<OprfGroup<CS> as Group>::ElemLen: Add<Ke1MessageLen<CS>>,
|
<CS::KeyExchange as KeyExchange>::KE1Message: Serialize,
|
||||||
|
<OprfGroup<CS> as voprf::Group>::ElemLen: Add<Ke1MessageLen<CS>>,
|
||||||
CredentialRequestLen<CS>: ArrayLength<u8>,
|
CredentialRequestLen<CS>: ArrayLength<u8>,
|
||||||
// ClientLogin: KgSk + CredentialRequest + Ke1State
|
// ClientLogin: KgSk + CredentialRequest + Ke1State
|
||||||
<OprfGroup<CS> as Group>::ScalarLen: Add<CredentialRequestLen<CS>>,
|
<OprfGroup<CS> as voprf::Group>::ScalarLen: Add<CredentialRequestLen<CS>>,
|
||||||
Sum<<OprfGroup<CS> as Group>::ScalarLen, CredentialRequestLen<CS>>:
|
<CS::KeyExchange as KeyExchange>::KE1State: Serialize,
|
||||||
|
Sum<<OprfGroup<CS> as voprf::Group>::ScalarLen, CredentialRequestLen<CS>>:
|
||||||
ArrayLength<u8> + Add<Ke1StateLen<CS>>,
|
ArrayLength<u8> + Add<Ke1StateLen<CS>>,
|
||||||
ClientLoginLen<CS>: ArrayLength<u8>,
|
ClientLoginLen<CS>: ArrayLength<u8>,
|
||||||
{
|
{
|
||||||
let pw = b"hunter2";
|
let pw = b"hunter2";
|
||||||
let mut rng = OsRng;
|
let mut rng = OsRng;
|
||||||
|
|
||||||
let client_e_kp = KeyPair::<CS::KeGroup>::generate_random::<CS::OprfCs, _>(&mut rng);
|
let client_e_kp = KeyPair::<KeGroup<CS>>::derive_random(&mut rng);
|
||||||
let mut client_nonce = [0; NonceLen::USIZE];
|
let mut client_nonce = [0; NonceLen::USIZE];
|
||||||
rng.fill_bytes(&mut client_nonce);
|
rng.fill_bytes(&mut client_nonce);
|
||||||
|
|
||||||
@@ -495,14 +720,14 @@ fn client_login_roundtrip() -> Result<(), ProtocolError> {
|
|||||||
|
|
||||||
let credential_request = CredentialRequest::<CS> {
|
let credential_request = CredentialRequest::<CS> {
|
||||||
blinded_element: blind_result.message,
|
blinded_element: blind_result.message,
|
||||||
ke1_message:
|
ke1_message: <CS::KeyExchange as KeyExchange>::KE1Message::deserialize_take(
|
||||||
<CS::KeyExchange as KeyExchange<OprfHash<CS>, CS::KeGroup>>::KE1Message::deserialize(
|
&mut ([
|
||||||
&[
|
client_nonce.as_ref(),
|
||||||
client_nonce.as_ref(),
|
client_e_kp.public().serialize().as_ref(),
|
||||||
client_e_kp.public().serialize().as_ref(),
|
]
|
||||||
]
|
.concat()
|
||||||
.concat(),
|
.as_slice()),
|
||||||
)?,
|
)?,
|
||||||
};
|
};
|
||||||
|
|
||||||
let bytes: Vec<u8> = blind_result
|
let bytes: Vec<u8> = blind_result
|
||||||
@@ -520,20 +745,25 @@ fn client_login_roundtrip() -> Result<(), ProtocolError> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "ristretto255")]
|
#[cfg(feature = "ristretto255")]
|
||||||
inner::<Ristretto255>()?;
|
inner::<TripleDhRistretto255>()?;
|
||||||
inner::<P256>()?;
|
#[cfg(all(feature = "ristretto255", feature = "curve25519"))]
|
||||||
inner::<P384>()?;
|
inner::<TripleDhCurve25519>()?;
|
||||||
inner::<P521>()?;
|
inner::<TripleDhP256>()?;
|
||||||
|
inner::<TripleDhP384>()?;
|
||||||
|
inner::<TripleDhP521>()?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn ke1_message_roundtrip() -> Result<(), ProtocolError> {
|
fn triple_dh_ke1_message_roundtrip() -> Result<(), ProtocolError> {
|
||||||
fn inner<CS: CipherSuite>() -> Result<(), ProtocolError> {
|
fn inner<CS: CipherSuite>() -> Result<(), ProtocolError>
|
||||||
|
where
|
||||||
|
<CS::KeyExchange as KeyExchange>::KE1Message: Deserialize + Serialize,
|
||||||
|
{
|
||||||
let mut rng = OsRng;
|
let mut rng = OsRng;
|
||||||
|
|
||||||
let client_e_kp = KeyPair::<CS::KeGroup>::generate_random::<CS::OprfCs, _>(&mut rng);
|
let client_e_kp = KeyPair::<KeGroup<CS>>::derive_random(&mut rng);
|
||||||
let mut client_nonce = vec![0u8; NonceLen::USIZE];
|
let mut client_nonce = vec![0u8; NonceLen::USIZE];
|
||||||
rng.fill_bytes(&mut client_nonce);
|
rng.fill_bytes(&mut client_nonce);
|
||||||
|
|
||||||
@@ -543,9 +773,7 @@ fn ke1_message_roundtrip() -> Result<(), ProtocolError> {
|
|||||||
]
|
]
|
||||||
.concat();
|
.concat();
|
||||||
let reg =
|
let reg =
|
||||||
<CS::KeyExchange as KeyExchange<OprfHash<CS>, CS::KeGroup>>::KE1Message::deserialize(
|
<CS::KeyExchange as KeyExchange>::KE1Message::deserialize_take(&mut (ke1m.as_slice()))?;
|
||||||
&ke1m,
|
|
||||||
)?;
|
|
||||||
let reg_bytes = reg.serialize();
|
let reg_bytes = reg.serialize();
|
||||||
assert_eq!(*reg_bytes, ke1m);
|
assert_eq!(*reg_bytes, ke1m);
|
||||||
|
|
||||||
@@ -553,20 +781,25 @@ fn ke1_message_roundtrip() -> Result<(), ProtocolError> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "ristretto255")]
|
#[cfg(feature = "ristretto255")]
|
||||||
inner::<Ristretto255>()?;
|
inner::<TripleDhRistretto255>()?;
|
||||||
inner::<P256>()?;
|
#[cfg(all(feature = "ristretto255", feature = "curve25519"))]
|
||||||
inner::<P384>()?;
|
inner::<TripleDhCurve25519>()?;
|
||||||
inner::<P521>()?;
|
inner::<TripleDhP256>()?;
|
||||||
|
inner::<TripleDhP384>()?;
|
||||||
|
inner::<TripleDhP521>()?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn ke2_message_roundtrip() -> Result<(), ProtocolError> {
|
fn triple_dh_ke2_message_roundtrip() -> Result<(), ProtocolError> {
|
||||||
fn inner<CS: CipherSuite>() -> Result<(), ProtocolError> {
|
fn inner<CS: CipherSuite>() -> Result<(), ProtocolError>
|
||||||
|
where
|
||||||
|
<CS::KeyExchange as KeyExchange>::KE2Message: Deserialize + Serialize,
|
||||||
|
{
|
||||||
let mut rng = OsRng;
|
let mut rng = OsRng;
|
||||||
|
|
||||||
let server_e_kp = KeyPair::<CS::KeGroup>::generate_random::<CS::OprfCs, _>(&mut rng);
|
let server_e_kp = KeyPair::<KeGroup<CS>>::derive_random(&mut rng);
|
||||||
let mut mac = Output::<OprfHash<CS>>::default();
|
let mut mac = Output::<OprfHash<CS>>::default();
|
||||||
rng.fill_bytes(&mut mac);
|
rng.fill_bytes(&mut mac);
|
||||||
let mut server_nonce = vec![0u8; NonceLen::USIZE];
|
let mut server_nonce = vec![0u8; NonceLen::USIZE];
|
||||||
@@ -580,9 +813,7 @@ fn ke2_message_roundtrip() -> Result<(), ProtocolError> {
|
|||||||
.concat();
|
.concat();
|
||||||
|
|
||||||
let reg =
|
let reg =
|
||||||
<CS::KeyExchange as KeyExchange<OprfHash<CS>, CS::KeGroup>>::KE2Message::deserialize(
|
<CS::KeyExchange as KeyExchange>::KE2Message::deserialize_take(&mut (ke2m.as_slice()))?;
|
||||||
&ke2m,
|
|
||||||
)?;
|
|
||||||
let reg_bytes = reg.serialize();
|
let reg_bytes = reg.serialize();
|
||||||
assert_eq!(*reg_bytes, ke2m);
|
assert_eq!(*reg_bytes, ke2m);
|
||||||
|
|
||||||
@@ -590,17 +821,62 @@ fn ke2_message_roundtrip() -> Result<(), ProtocolError> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "ristretto255")]
|
#[cfg(feature = "ristretto255")]
|
||||||
inner::<Ristretto255>()?;
|
inner::<TripleDhRistretto255>()?;
|
||||||
inner::<P256>()?;
|
#[cfg(all(feature = "ristretto255", feature = "curve25519"))]
|
||||||
inner::<P384>()?;
|
inner::<TripleDhCurve25519>()?;
|
||||||
inner::<P521>()?;
|
inner::<TripleDhP256>()?;
|
||||||
|
inner::<TripleDhP384>()?;
|
||||||
|
inner::<TripleDhP521>()?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn ke3_message_roundtrip() -> Result<(), ProtocolError> {
|
#[cfg(feature = "ecdsa")]
|
||||||
fn inner<CS: CipherSuite>() -> Result<(), ProtocolError> {
|
fn sigma_i_ecdsa_ke2_message_roundtrip() -> Result<(), ProtocolError> {
|
||||||
|
fn inner<CS: CipherSuite>() -> Result<(), ProtocolError>
|
||||||
|
where
|
||||||
|
<CS::KeyExchange as KeyExchange>::KE2Message: Deserialize + Serialize,
|
||||||
|
{
|
||||||
|
let mut rng = OsRng;
|
||||||
|
|
||||||
|
let server_e_kp = KeyPair::<KeGroup<CS>>::derive_random(&mut rng);
|
||||||
|
let mut mac = Output::<OprfHash<CS>>::default();
|
||||||
|
rng.fill_bytes(&mut mac);
|
||||||
|
let mut server_nonce = vec![0u8; NonceLen::USIZE];
|
||||||
|
rng.fill_bytes(&mut server_nonce);
|
||||||
|
let r = KeGroup::<CS>::serialize_sk(KeGroup::<CS>::random_sk(&mut rng));
|
||||||
|
let s = KeGroup::<CS>::serialize_sk(KeGroup::<CS>::random_sk(&mut rng));
|
||||||
|
|
||||||
|
let ke2m: Vec<u8> = [
|
||||||
|
server_nonce.as_slice(),
|
||||||
|
server_e_kp.public().serialize().as_ref(),
|
||||||
|
&r,
|
||||||
|
&s,
|
||||||
|
&mac,
|
||||||
|
]
|
||||||
|
.concat();
|
||||||
|
|
||||||
|
let reg =
|
||||||
|
<CS::KeyExchange as KeyExchange>::KE2Message::deserialize_take(&mut (ke2m.as_slice()))?;
|
||||||
|
let reg_bytes = reg.serialize();
|
||||||
|
assert_eq!(*reg_bytes, ke2m);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
inner::<SigmaIP256>()?;
|
||||||
|
inner::<SigmaIP384>()?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn triple_dh_ke3_message_roundtrip() -> Result<(), ProtocolError> {
|
||||||
|
fn inner<CS: CipherSuite>() -> Result<(), ProtocolError>
|
||||||
|
where
|
||||||
|
<CS::KeyExchange as KeyExchange>::KE3Message: Deserialize + Serialize,
|
||||||
|
{
|
||||||
let mut rng = OsRng;
|
let mut rng = OsRng;
|
||||||
let mut mac = Output::<OprfHash<CS>>::default();
|
let mut mac = Output::<OprfHash<CS>>::default();
|
||||||
rng.fill_bytes(&mut mac);
|
rng.fill_bytes(&mut mac);
|
||||||
@@ -608,9 +884,7 @@ fn ke3_message_roundtrip() -> Result<(), ProtocolError> {
|
|||||||
let ke3m: Vec<u8> = [mac].concat();
|
let ke3m: Vec<u8> = [mac].concat();
|
||||||
|
|
||||||
let reg =
|
let reg =
|
||||||
<CS::KeyExchange as KeyExchange<OprfHash<CS>, CS::KeGroup>>::KE3Message::deserialize(
|
<CS::KeyExchange as KeyExchange>::KE3Message::deserialize_take(&mut (ke3m.as_slice()))?;
|
||||||
&ke3m,
|
|
||||||
)?;
|
|
||||||
let reg_bytes = reg.serialize();
|
let reg_bytes = reg.serialize();
|
||||||
assert_eq!(*reg_bytes, ke3m);
|
assert_eq!(*reg_bytes, ke3m);
|
||||||
|
|
||||||
@@ -618,10 +892,41 @@ fn ke3_message_roundtrip() -> Result<(), ProtocolError> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "ristretto255")]
|
#[cfg(feature = "ristretto255")]
|
||||||
inner::<Ristretto255>()?;
|
inner::<TripleDhRistretto255>()?;
|
||||||
inner::<P256>()?;
|
#[cfg(all(feature = "ristretto255", feature = "curve25519"))]
|
||||||
inner::<P384>()?;
|
inner::<TripleDhCurve25519>()?;
|
||||||
inner::<P521>()?;
|
inner::<TripleDhP256>()?;
|
||||||
|
inner::<TripleDhP384>()?;
|
||||||
|
inner::<TripleDhP521>()?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(feature = "ecdsa")]
|
||||||
|
fn sigma_i_ecdsa_ke3_message_roundtrip() -> Result<(), ProtocolError> {
|
||||||
|
fn inner<CS: CipherSuite>() -> Result<(), ProtocolError>
|
||||||
|
where
|
||||||
|
<CS::KeyExchange as KeyExchange>::KE3Message: Deserialize + Serialize,
|
||||||
|
{
|
||||||
|
let mut rng = OsRng;
|
||||||
|
let r = KeGroup::<CS>::serialize_sk(KeGroup::<CS>::random_sk(&mut rng));
|
||||||
|
let s = KeGroup::<CS>::serialize_sk(KeGroup::<CS>::random_sk(&mut rng));
|
||||||
|
let mut mac = Output::<OprfHash<CS>>::default();
|
||||||
|
rng.fill_bytes(&mut mac);
|
||||||
|
|
||||||
|
let ke3m: Vec<u8> = [mac.as_slice(), &r, &s].concat();
|
||||||
|
|
||||||
|
let reg =
|
||||||
|
<CS::KeyExchange as KeyExchange>::KE3Message::deserialize_take(&mut (ke3m.as_slice()))?;
|
||||||
|
let reg_bytes = reg.serialize();
|
||||||
|
assert_eq!(*reg_bytes, ke3m);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
inner::<SigmaIP256>()?;
|
||||||
|
inner::<SigmaIP384>()?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -672,7 +977,7 @@ macro_rules! test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_nocrash_credential_request(bytes in vec(any::<u8>(), 0..500)) {
|
fn test_nocrash_credential_request(bytes in vec(any::<u8>(), 0..500)) {
|
||||||
let _ = CredentialRequest::<$CS>::deserialize(&bytes).map_or(true, |_| true);
|
let _ = CredentialRequest::<$CS>::deserialize(&mut (bytes.as_slice())).map_or(true, |_| true);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -710,7 +1015,17 @@ macro_rules! test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "ristretto255")]
|
#[cfg(feature = "ristretto255")]
|
||||||
test!(ristretto255, Ristretto255);
|
test!(triple_dh_ristretto255, TripleDhRistretto255);
|
||||||
test!(p256, P256);
|
#[cfg(all(feature = "ristretto255", feature = "curve25519"))]
|
||||||
test!(p384, P384);
|
test!(triple_dh_curve25519, TripleDhCurve25519);
|
||||||
test!(p521, P521);
|
test!(triple_dh_p256, TripleDhP256);
|
||||||
|
test!(triple_dh_p384, TripleDhP384);
|
||||||
|
test!(triple_dh_p521, TripleDhP521);
|
||||||
|
#[cfg(feature = "ecdsa")]
|
||||||
|
test!(sigma_i_p256, SigmaIP256);
|
||||||
|
#[cfg(feature = "ecdsa")]
|
||||||
|
test!(sigma_i_p384, SigmaIP384);
|
||||||
|
#[cfg(all(feature = "ristretto255", feature = "ed25519",))]
|
||||||
|
test!(sigma_i_ed25519, SigmaIEd25519);
|
||||||
|
#[cfg(all(feature = "ristretto255", feature = "ed25519"))]
|
||||||
|
test!(sigma_i_ed25519_ph, SigmaIEd25519Ph);
|
||||||
|
|||||||
+330
-583
File diff suppressed because it is too large
Load Diff
+3902
-606
File diff suppressed because it is too large
Load Diff
@@ -12,6 +12,4 @@ mod full_test_vectors;
|
|||||||
pub mod mock_rng;
|
pub mod mock_rng;
|
||||||
mod opaque_vectors;
|
mod opaque_vectors;
|
||||||
mod parser;
|
mod parser;
|
||||||
#[cfg(test_hsm)]
|
|
||||||
mod remote_key;
|
|
||||||
mod test_opaque_vectors;
|
mod test_opaque_vectors;
|
||||||
|
|||||||
@@ -16,15 +16,16 @@ use generic_array::{ArrayLength, GenericArray};
|
|||||||
use rand::rngs::OsRng;
|
use rand::rngs::OsRng;
|
||||||
use rand::RngCore;
|
use rand::RngCore;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use voprf::Group;
|
|
||||||
|
|
||||||
use crate::ciphersuite::{CipherSuite, OprfGroup, OprfHash};
|
use crate::ciphersuite::{CipherSuite, KeGroup, OprfGroup, OprfHash};
|
||||||
use crate::envelope::EnvelopeLen;
|
use crate::envelope::EnvelopeLen;
|
||||||
use crate::errors::*;
|
use crate::errors::*;
|
||||||
use crate::hash::OutputSize;
|
use crate::hash::OutputSize;
|
||||||
use crate::key_exchange::group::KeGroup;
|
use crate::key_exchange::group::Group;
|
||||||
use crate::key_exchange::traits::{Ke1MessageLen, Ke2MessageLen};
|
use crate::key_exchange::shared::NonceLen;
|
||||||
use crate::key_exchange::tripledh::{NonceLen, TripleDh};
|
use crate::key_exchange::traits::{
|
||||||
|
Deserialize, Ke1MessageLen, Ke2MessageLen, KeyExchange, Serialize,
|
||||||
|
};
|
||||||
use crate::ksf::Identity;
|
use crate::ksf::Identity;
|
||||||
use crate::messages::{
|
use crate::messages::{
|
||||||
CredentialRequestLen, CredentialResponseLen, CredentialResponseWithoutKeLen,
|
CredentialRequestLen, CredentialResponseLen, CredentialResponseWithoutKeLen,
|
||||||
@@ -99,7 +100,7 @@ fn populate_test_vectors<CS: CipherSuite>(values: &Value) -> OpaqueTestVectorPar
|
|||||||
dummy_private_key: {
|
dummy_private_key: {
|
||||||
match decode(values, "client_private_key") {
|
match decode(values, "client_private_key") {
|
||||||
Some(value) => value,
|
Some(value) => value,
|
||||||
None => CS::KeGroup::serialize_sk(CS::KeGroup::random_sk(&mut OsRng)).to_vec(),
|
None => KeGroup::<CS>::serialize_sk(KeGroup::<CS>::random_sk(&mut OsRng)).to_vec(),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
dummy_masking_key: {
|
dummy_masking_key: {
|
||||||
@@ -149,12 +150,9 @@ fn populate_test_vectors<CS: CipherSuite>(values: &Value) -> OpaqueTestVectorPar
|
|||||||
|
|
||||||
fn get_password_file_bytes<CS: CipherSuite>(parameters: &OpaqueTestVectorParameters) -> Vec<u8>
|
fn get_password_file_bytes<CS: CipherSuite>(parameters: &OpaqueTestVectorParameters) -> Vec<u8>
|
||||||
where
|
where
|
||||||
// Envelope: Nonce + Hash
|
|
||||||
NonceLen: Add<OutputSize<OprfHash<CS>>>,
|
|
||||||
EnvelopeLen<CS>: ArrayLength<u8>,
|
|
||||||
// RegistrationUpload: (KePk + Hash) + Envelope
|
// RegistrationUpload: (KePk + Hash) + Envelope
|
||||||
<CS::KeGroup as KeGroup>::PkLen: Add<OutputSize<OprfHash<CS>>>,
|
<KeGroup<CS> as Group>::PkLen: Add<OutputSize<OprfHash<CS>>>,
|
||||||
Sum<<CS::KeGroup as KeGroup>::PkLen, OutputSize<OprfHash<CS>>>:
|
Sum<<KeGroup<CS> as Group>::PkLen, OutputSize<OprfHash<CS>>>:
|
||||||
ArrayLength<u8> + Add<EnvelopeLen<CS>>,
|
ArrayLength<u8> + Add<EnvelopeLen<CS>>,
|
||||||
RegistrationUploadLen<CS>: ArrayLength<u8>,
|
RegistrationUploadLen<CS>: ArrayLength<u8>,
|
||||||
// ServerRegistration = RegistrationUpload
|
// ServerRegistration = RegistrationUpload
|
||||||
@@ -194,8 +192,7 @@ fn tests() -> Result<(), ProtocolError> {
|
|||||||
struct Ristretto255Sha512NoKsf;
|
struct Ristretto255Sha512NoKsf;
|
||||||
impl CipherSuite for Ristretto255Sha512NoKsf {
|
impl CipherSuite for Ristretto255Sha512NoKsf {
|
||||||
type OprfCs = crate::Ristretto255;
|
type OprfCs = crate::Ristretto255;
|
||||||
type KeGroup = crate::Ristretto255;
|
type KeyExchange = TripleDh<crate::Ristretto255, sha2::Sha512>;
|
||||||
type KeyExchange = TripleDh;
|
|
||||||
type Ksf = Identity;
|
type Ksf = Identity;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -233,8 +230,7 @@ fn tests() -> Result<(), ProtocolError> {
|
|||||||
struct Ristretto255Sha512Curve25519NoKsf;
|
struct Ristretto255Sha512Curve25519NoKsf;
|
||||||
impl CipherSuite for Ristretto255Sha512Curve25519NoKsf {
|
impl CipherSuite for Ristretto255Sha512Curve25519NoKsf {
|
||||||
type OprfCs = crate::Ristretto255;
|
type OprfCs = crate::Ristretto255;
|
||||||
type KeGroup = crate::Curve25519;
|
type KeyExchange = TripleDh<crate::Curve25519, sha2::Sha512>;
|
||||||
type KeyExchange = TripleDh;
|
|
||||||
type Ksf = Identity;
|
type Ksf = Identity;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -270,8 +266,7 @@ fn tests() -> Result<(), ProtocolError> {
|
|||||||
struct P256Sha256NoKsf;
|
struct P256Sha256NoKsf;
|
||||||
impl CipherSuite for P256Sha256NoKsf {
|
impl CipherSuite for P256Sha256NoKsf {
|
||||||
type OprfCs = p256::NistP256;
|
type OprfCs = p256::NistP256;
|
||||||
type KeGroup = p256::NistP256;
|
type KeyExchange = TripleDh<p256::NistP256, sha2::Sha256>;
|
||||||
type KeyExchange = TripleDh;
|
|
||||||
type Ksf = Identity;
|
type Ksf = Identity;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -325,7 +320,7 @@ fn test_registration_response<CS: CipherSuite>(
|
|||||||
) -> Result<(), ProtocolError>
|
) -> Result<(), ProtocolError>
|
||||||
where
|
where
|
||||||
// RegistrationResponse: KgPk + KePk
|
// RegistrationResponse: KgPk + KePk
|
||||||
<OprfGroup<CS> as Group>::ElemLen: Add<<CS::KeGroup as KeGroup>::PkLen>,
|
<OprfGroup<CS> as voprf::Group>::ElemLen: Add<<KeGroup<CS> as Group>::PkLen>,
|
||||||
RegistrationResponseLen<CS>: ArrayLength<u8>,
|
RegistrationResponseLen<CS>: ArrayLength<u8>,
|
||||||
{
|
{
|
||||||
for parameters in tvs {
|
for parameters in tvs {
|
||||||
@@ -362,12 +357,9 @@ fn test_registration_upload<CS: CipherSuite>(
|
|||||||
tvs: &[OpaqueTestVectorParameters],
|
tvs: &[OpaqueTestVectorParameters],
|
||||||
) -> Result<(), ProtocolError>
|
) -> Result<(), ProtocolError>
|
||||||
where
|
where
|
||||||
// Envelope: Nonce + Hash
|
|
||||||
NonceLen: Add<OutputSize<OprfHash<CS>>>,
|
|
||||||
EnvelopeLen<CS>: ArrayLength<u8>,
|
|
||||||
// RegistrationUpload: (KePk + Hash) + Envelope
|
// RegistrationUpload: (KePk + Hash) + Envelope
|
||||||
<CS::KeGroup as KeGroup>::PkLen: Add<OutputSize<OprfHash<CS>>>,
|
<KeGroup<CS> as Group>::PkLen: Add<OutputSize<OprfHash<CS>>>,
|
||||||
Sum<<CS::KeGroup as KeGroup>::PkLen, OutputSize<OprfHash<CS>>>:
|
Sum<<KeGroup<CS> as Group>::PkLen, OutputSize<OprfHash<CS>>>:
|
||||||
ArrayLength<u8> + Add<EnvelopeLen<CS>>,
|
ArrayLength<u8> + Add<EnvelopeLen<CS>>,
|
||||||
RegistrationUploadLen<CS>: ArrayLength<u8>,
|
RegistrationUploadLen<CS>: ArrayLength<u8>,
|
||||||
{
|
{
|
||||||
@@ -413,7 +405,8 @@ where
|
|||||||
fn test_ke1<CS: CipherSuite>(tvs: &[OpaqueTestVectorParameters]) -> Result<(), ProtocolError>
|
fn test_ke1<CS: CipherSuite>(tvs: &[OpaqueTestVectorParameters]) -> Result<(), ProtocolError>
|
||||||
where
|
where
|
||||||
// CredentialRequest: KgPk + Ke1Message
|
// CredentialRequest: KgPk + Ke1Message
|
||||||
<OprfGroup<CS> as Group>::ElemLen: Add<Ke1MessageLen<CS>>,
|
<CS::KeyExchange as KeyExchange>::KE1Message: Serialize,
|
||||||
|
<OprfGroup<CS> as voprf::Group>::ElemLen: Add<Ke1MessageLen<CS>>,
|
||||||
CredentialRequestLen<CS>: ArrayLength<u8>,
|
CredentialRequestLen<CS>: ArrayLength<u8>,
|
||||||
{
|
{
|
||||||
for parameters in tvs {
|
for parameters in tvs {
|
||||||
@@ -437,28 +430,20 @@ where
|
|||||||
|
|
||||||
fn test_ke2<CS: CipherSuite>(tvs: &[OpaqueTestVectorParameters]) -> Result<(), ProtocolError>
|
fn test_ke2<CS: CipherSuite>(tvs: &[OpaqueTestVectorParameters]) -> Result<(), ProtocolError>
|
||||||
where
|
where
|
||||||
// Envelope: Nonce + Hash
|
<CS::KeyExchange as KeyExchange>::KE1Message: Deserialize,
|
||||||
NonceLen: Add<OutputSize<OprfHash<CS>>>,
|
|
||||||
EnvelopeLen<CS>: ArrayLength<u8>,
|
|
||||||
// RegistrationUpload: (KePk + Hash) + Envelope
|
// RegistrationUpload: (KePk + Hash) + Envelope
|
||||||
<CS::KeGroup as KeGroup>::PkLen: Add<OutputSize<OprfHash<CS>>>,
|
<KeGroup<CS> as Group>::PkLen: Add<OutputSize<OprfHash<CS>>>,
|
||||||
Sum<<CS::KeGroup as KeGroup>::PkLen, OutputSize<OprfHash<CS>>>:
|
Sum<<KeGroup<CS> as Group>::PkLen, OutputSize<OprfHash<CS>>>:
|
||||||
ArrayLength<u8> + Add<EnvelopeLen<CS>>,
|
ArrayLength<u8> + Add<EnvelopeLen<CS>>,
|
||||||
RegistrationUploadLen<CS>: ArrayLength<u8>,
|
RegistrationUploadLen<CS>: ArrayLength<u8>,
|
||||||
// ServerRegistration = RegistrationUpload
|
// ServerRegistration = RegistrationUpload
|
||||||
// MaskedResponse: (Nonce + Hash) + KePk
|
|
||||||
NonceLen: Add<OutputSize<OprfHash<CS>>>,
|
|
||||||
Sum<NonceLen, OutputSize<OprfHash<CS>>>: ArrayLength<u8> + Add<<CS::KeGroup as KeGroup>::PkLen>,
|
|
||||||
MaskedResponseLen<CS>: ArrayLength<u8>,
|
|
||||||
// CredentialResponseWithoutKeLen: (KgPk + Nonce) + MaskedResponse
|
// CredentialResponseWithoutKeLen: (KgPk + Nonce) + MaskedResponse
|
||||||
<OprfGroup<CS> as Group>::ElemLen: Add<NonceLen>,
|
<OprfGroup<CS> as voprf::Group>::ElemLen: Add<NonceLen>,
|
||||||
Sum<<OprfGroup<CS> as Group>::ElemLen, NonceLen>: ArrayLength<u8> + Add<MaskedResponseLen<CS>>,
|
Sum<<OprfGroup<CS> as voprf::Group>::ElemLen, NonceLen>:
|
||||||
|
ArrayLength<u8> + Add<MaskedResponseLen<CS>>,
|
||||||
CredentialResponseWithoutKeLen<CS>: ArrayLength<u8>,
|
CredentialResponseWithoutKeLen<CS>: ArrayLength<u8>,
|
||||||
// MaskedResponse: (Nonce + Hash) + KePk
|
|
||||||
NonceLen: Add<OutputSize<OprfHash<CS>>>,
|
|
||||||
Sum<NonceLen, OutputSize<OprfHash<CS>>>: ArrayLength<u8> + Add<<CS::KeGroup as KeGroup>::PkLen>,
|
|
||||||
MaskedResponseLen<CS>: ArrayLength<u8>,
|
|
||||||
// CredentialResponse: CredentialResponseWithoutKeLen + Ke2Message
|
// CredentialResponse: CredentialResponseWithoutKeLen + Ke2Message
|
||||||
|
<CS::KeyExchange as KeyExchange>::KE2Message: Serialize,
|
||||||
CredentialResponseWithoutKeLen<CS>: Add<Ke2MessageLen<CS>>,
|
CredentialResponseWithoutKeLen<CS>: Add<Ke2MessageLen<CS>>,
|
||||||
CredentialResponseLen<CS>: ArrayLength<u8>,
|
CredentialResponseLen<CS>: ArrayLength<u8>,
|
||||||
{
|
{
|
||||||
@@ -490,7 +475,7 @@ where
|
|||||||
Some(record),
|
Some(record),
|
||||||
CredentialRequest::<CS>::deserialize(¶meters.KE1).unwrap(),
|
CredentialRequest::<CS>::deserialize(¶meters.KE1).unwrap(),
|
||||||
¶meters.credential_identifier,
|
¶meters.credential_identifier,
|
||||||
ServerLoginStartParameters {
|
ServerLoginParameters {
|
||||||
context: Some(¶meters.context),
|
context: Some(¶meters.context),
|
||||||
identifiers: Identifiers {
|
identifiers: Identifiers {
|
||||||
client: parameters.client_identity.as_deref(),
|
client: parameters.client_identity.as_deref(),
|
||||||
@@ -520,10 +505,8 @@ where
|
|||||||
|
|
||||||
fn test_ke3<CS: CipherSuite>(tvs: &[OpaqueTestVectorParameters]) -> Result<(), ProtocolError>
|
fn test_ke3<CS: CipherSuite>(tvs: &[OpaqueTestVectorParameters]) -> Result<(), ProtocolError>
|
||||||
where
|
where
|
||||||
// MaskedResponse: (Nonce + Hash) + KePk
|
<CS::KeyExchange as KeyExchange>::KE2Message: Deserialize + Serialize,
|
||||||
NonceLen: Add<OutputSize<OprfHash<CS>>>,
|
<CS::KeyExchange as KeyExchange>::KE3Message: Serialize,
|
||||||
Sum<NonceLen, OutputSize<OprfHash<CS>>>: ArrayLength<u8> + Add<<CS::KeGroup as KeGroup>::PkLen>,
|
|
||||||
MaskedResponseLen<CS>: ArrayLength<u8>,
|
|
||||||
{
|
{
|
||||||
for parameters in tvs {
|
for parameters in tvs {
|
||||||
let client_login_start = [
|
let client_login_start = [
|
||||||
@@ -537,6 +520,7 @@ where
|
|||||||
ClientLogin::<CS>::start(&mut client_login_start_rng, ¶meters.password)?;
|
ClientLogin::<CS>::start(&mut client_login_start_rng, ¶meters.password)?;
|
||||||
|
|
||||||
let client_login_finish_result = client_login_start_result.state.finish(
|
let client_login_finish_result = client_login_start_result.state.finish(
|
||||||
|
&mut OsRng,
|
||||||
¶meters.password,
|
¶meters.password,
|
||||||
CredentialResponse::<CS>::deserialize(¶meters.KE2)?,
|
CredentialResponse::<CS>::deserialize(¶meters.KE2)?,
|
||||||
ClientLoginFinishParameters::new(
|
ClientLoginFinishParameters::new(
|
||||||
@@ -577,19 +561,14 @@ fn test_server_login_finish<CS: CipherSuite>(
|
|||||||
tvs: &[OpaqueTestVectorParameters],
|
tvs: &[OpaqueTestVectorParameters],
|
||||||
) -> Result<(), ProtocolError>
|
) -> Result<(), ProtocolError>
|
||||||
where
|
where
|
||||||
// Envelope: Nonce + Hash
|
<CS::KeyExchange as KeyExchange>::KE1Message: Deserialize,
|
||||||
NonceLen: Add<OutputSize<OprfHash<CS>>>,
|
<CS::KeyExchange as KeyExchange>::KE3Message: Deserialize,
|
||||||
EnvelopeLen<CS>: ArrayLength<u8>,
|
|
||||||
// RegistrationUpload: (KePk + Hash) + Envelope
|
// RegistrationUpload: (KePk + Hash) + Envelope
|
||||||
<CS::KeGroup as KeGroup>::PkLen: Add<OutputSize<OprfHash<CS>>>,
|
<KeGroup<CS> as Group>::PkLen: Add<OutputSize<OprfHash<CS>>>,
|
||||||
Sum<<CS::KeGroup as KeGroup>::PkLen, OutputSize<OprfHash<CS>>>:
|
Sum<<KeGroup<CS> as Group>::PkLen, OutputSize<OprfHash<CS>>>:
|
||||||
ArrayLength<u8> + Add<EnvelopeLen<CS>>,
|
ArrayLength<u8> + Add<EnvelopeLen<CS>>,
|
||||||
RegistrationUploadLen<CS>: ArrayLength<u8>,
|
RegistrationUploadLen<CS>: ArrayLength<u8>,
|
||||||
// ServerRegistration = RegistrationUpload
|
// ServerRegistration = RegistrationUpload
|
||||||
// MaskedResponse: (Nonce + Hash) + KePk
|
|
||||||
NonceLen: Add<OutputSize<OprfHash<CS>>>,
|
|
||||||
Sum<NonceLen, OutputSize<OprfHash<CS>>>: ArrayLength<u8> + Add<<CS::KeGroup as KeGroup>::PkLen>,
|
|
||||||
MaskedResponseLen<CS>: ArrayLength<u8>,
|
|
||||||
{
|
{
|
||||||
for parameters in tvs {
|
for parameters in tvs {
|
||||||
let server_setup = ServerSetup::<CS>::deserialize(
|
let server_setup = ServerSetup::<CS>::deserialize(
|
||||||
@@ -619,7 +598,7 @@ where
|
|||||||
Some(record),
|
Some(record),
|
||||||
CredentialRequest::<CS>::deserialize(¶meters.KE1).unwrap(),
|
CredentialRequest::<CS>::deserialize(¶meters.KE1).unwrap(),
|
||||||
¶meters.credential_identifier,
|
¶meters.credential_identifier,
|
||||||
ServerLoginStartParameters {
|
ServerLoginParameters {
|
||||||
context: Some(¶meters.context),
|
context: Some(¶meters.context),
|
||||||
identifiers: Identifiers {
|
identifiers: Identifiers {
|
||||||
client: parameters.client_identity.as_deref(),
|
client: parameters.client_identity.as_deref(),
|
||||||
@@ -628,9 +607,16 @@ where
|
|||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let server_login_result = server_login_start_result
|
let server_login_result = server_login_start_result.state.finish(
|
||||||
.state
|
CredentialFinalization::deserialize(¶meters.KE3)?,
|
||||||
.finish(CredentialFinalization::deserialize(¶meters.KE3)?)?;
|
ServerLoginParameters {
|
||||||
|
context: Some(¶meters.context),
|
||||||
|
identifiers: Identifiers {
|
||||||
|
client: parameters.client_identity.as_deref(),
|
||||||
|
server: parameters.server_identity.as_deref(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
hex::encode(¶meters.session_key),
|
hex::encode(¶meters.session_key),
|
||||||
@@ -644,15 +630,14 @@ fn test_fake_vectors<CS: CipherSuite>(
|
|||||||
tvs: &[OpaqueTestVectorParameters],
|
tvs: &[OpaqueTestVectorParameters],
|
||||||
) -> Result<(), ProtocolError>
|
) -> Result<(), ProtocolError>
|
||||||
where
|
where
|
||||||
// MaskedResponse: (Nonce + Hash) + KePk
|
<CS::KeyExchange as KeyExchange>::KE1Message: Deserialize,
|
||||||
NonceLen: Add<OutputSize<OprfHash<CS>>>,
|
|
||||||
Sum<NonceLen, OutputSize<OprfHash<CS>>>: ArrayLength<u8> + Add<<CS::KeGroup as KeGroup>::PkLen>,
|
|
||||||
MaskedResponseLen<CS>: ArrayLength<u8>,
|
|
||||||
// CredentialResponseWithoutKeLen: (KgPk + Nonce) + MaskedResponse
|
// CredentialResponseWithoutKeLen: (KgPk + Nonce) + MaskedResponse
|
||||||
<OprfGroup<CS> as Group>::ElemLen: Add<NonceLen>,
|
<OprfGroup<CS> as voprf::Group>::ElemLen: Add<NonceLen>,
|
||||||
Sum<<OprfGroup<CS> as Group>::ElemLen, NonceLen>: ArrayLength<u8> + Add<MaskedResponseLen<CS>>,
|
Sum<<OprfGroup<CS> as voprf::Group>::ElemLen, NonceLen>:
|
||||||
|
ArrayLength<u8> + Add<MaskedResponseLen<CS>>,
|
||||||
CredentialResponseWithoutKeLen<CS>: ArrayLength<u8>,
|
CredentialResponseWithoutKeLen<CS>: ArrayLength<u8>,
|
||||||
// CredentialResponse: CredentialResponseWithoutKeLen + Ke2Message
|
// CredentialResponse: CredentialResponseWithoutKeLen + Ke2Message
|
||||||
|
<CS::KeyExchange as KeyExchange>::KE2Message: Serialize,
|
||||||
CredentialResponseWithoutKeLen<CS>: Add<Ke2MessageLen<CS>>,
|
CredentialResponseWithoutKeLen<CS>: Add<Ke2MessageLen<CS>>,
|
||||||
CredentialResponseLen<CS>: ArrayLength<u8>,
|
CredentialResponseLen<CS>: ArrayLength<u8>,
|
||||||
{
|
{
|
||||||
@@ -681,7 +666,7 @@ where
|
|||||||
None,
|
None,
|
||||||
CredentialRequest::<CS>::deserialize(¶meters.KE1).unwrap(),
|
CredentialRequest::<CS>::deserialize(¶meters.KE1).unwrap(),
|
||||||
¶meters.credential_identifier,
|
¶meters.credential_identifier,
|
||||||
ServerLoginStartParameters {
|
ServerLoginParameters {
|
||||||
context: Some(¶meters.context),
|
context: Some(¶meters.context),
|
||||||
identifiers: Identifiers {
|
identifiers: Identifiers {
|
||||||
client: parameters.client_identity.as_deref(),
|
client: parameters.client_identity.as_deref(),
|
||||||
|
|||||||
-42
@@ -1,42 +0,0 @@
|
|||||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
||||||
//
|
|
||||||
// This source code is dual-licensed under either the MIT license found in the
|
|
||||||
// LICENSE-MIT file in the root directory of this source tree or the Apache
|
|
||||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
|
||||||
// of this source tree. You may select, at your option, one of the above-listed
|
|
||||||
// licenses.
|
|
||||||
|
|
||||||
//! Utility functions.
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
pub(crate) fn test_zeroize_on_drop<T: Sized>(value: &mut T) {
|
|
||||||
drop_manually(value);
|
|
||||||
|
|
||||||
test_zeroized(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
pub(crate) fn test_zeroized<T: Sized>(value: &mut T) {
|
|
||||||
use std::{mem, slice, vec};
|
|
||||||
|
|
||||||
let test =
|
|
||||||
unsafe { slice::from_raw_parts(value as *const _ as *const u8, mem::size_of::<T>()) };
|
|
||||||
|
|
||||||
assert_eq!(test, vec![0; mem::size_of::<T>()]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
pub(crate) fn drop_manually<T: Sized>(value: &mut T) {
|
|
||||||
use std::{mem, ptr, vec};
|
|
||||||
|
|
||||||
assert!(mem::needs_drop::<T>());
|
|
||||||
let mut test_holder = vec![value];
|
|
||||||
let ptr = &mut *test_holder[0] as *mut T;
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
test_holder.set_len(0);
|
|
||||||
ptr::drop_in_place(ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
assert_eq!(test_holder.capacity(), 1);
|
|
||||||
}
|
|
||||||
@@ -6,113 +6,130 @@
|
|||||||
// of this source tree. You may select, at your option, one of the above-listed
|
// of this source tree. You may select, at your option, one of the above-listed
|
||||||
// licenses.
|
// licenses.
|
||||||
|
|
||||||
|
#![cfg(test_hsm)]
|
||||||
|
#![allow(type_alias_bounds)]
|
||||||
|
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::ops::Add;
|
|
||||||
use std::sync::{LazyLock, Mutex};
|
use std::sync::{LazyLock, Mutex};
|
||||||
use std::vec::Vec;
|
use std::vec::Vec;
|
||||||
|
|
||||||
|
#[cfg(feature = "ecdsa")]
|
||||||
|
use ::ecdsa::SignatureSize;
|
||||||
use cryptoki::context::{CInitializeArgs, Pkcs11};
|
use cryptoki::context::{CInitializeArgs, Pkcs11};
|
||||||
use cryptoki::mechanism::elliptic_curve::{EcKdf, Ecdh1DeriveParams};
|
use cryptoki::mechanism::elliptic_curve::{EcKdf, Ecdh1DeriveParams};
|
||||||
use cryptoki::mechanism::Mechanism;
|
use cryptoki::mechanism::Mechanism;
|
||||||
use cryptoki::object::{Attribute, AttributeType, KeyType, ObjectClass, ObjectHandle};
|
use cryptoki::object::{Attribute, AttributeType, KeyType, ObjectClass, ObjectHandle};
|
||||||
use cryptoki::session::{Session, UserType};
|
use cryptoki::session::{Session, UserType};
|
||||||
use cryptoki::types::AuthPin;
|
use cryptoki::types::AuthPin;
|
||||||
|
#[cfg(feature = "ecdsa")]
|
||||||
|
use digest::Digest;
|
||||||
use digest::OutputSizeUser;
|
use digest::OutputSizeUser;
|
||||||
|
use elliptic_curve::group::prime::PrimeCurveAffine;
|
||||||
use elliptic_curve::group::Curve;
|
use elliptic_curve::group::Curve;
|
||||||
use elliptic_curve::pkcs8::der::asn1::{OctetString, OctetStringRef};
|
use elliptic_curve::pkcs8::der::asn1::{OctetString, OctetStringRef};
|
||||||
use elliptic_curve::pkcs8::der::{Decode, Encode};
|
use elliptic_curve::pkcs8::der::{Decode, Encode};
|
||||||
use elliptic_curve::pkcs8::{AssociatedOid, ObjectIdentifier};
|
use elliptic_curve::pkcs8::{AssociatedOid, ObjectIdentifier};
|
||||||
use elliptic_curve::point::{AffineCoordinates, DecompressPoint};
|
use elliptic_curve::point::{AffineCoordinates, DecompressPoint};
|
||||||
use elliptic_curve::sec1::{ModulusSize, Tag, ToEncodedPoint};
|
use elliptic_curve::sec1::{FromEncodedPoint, ModulusSize, Tag, ToEncodedPoint};
|
||||||
use elliptic_curve::{AffinePoint, CurveArithmetic, FieldBytesSize, Group, ProjectivePoint};
|
#[cfg(feature = "ecdsa")]
|
||||||
use generic_array::typenum::{Sum, Unsigned};
|
use elliptic_curve::PrimeCurve;
|
||||||
|
use elliptic_curve::{AffinePoint, CurveArithmetic, FieldBytesSize, Group as _, ProjectivePoint};
|
||||||
|
use generic_array::typenum::Unsigned;
|
||||||
use generic_array::{ArrayLength, GenericArray};
|
use generic_array::{ArrayLength, GenericArray};
|
||||||
|
#[cfg(all(feature = "ristretto255", feature = "ed25519"))]
|
||||||
|
use opaque_ke::key_exchange::group::ed25519::{self, Ed25519};
|
||||||
|
use opaque_ke::key_exchange::group::elliptic_curve::NonIdentity;
|
||||||
|
use opaque_ke::key_exchange::group::Group;
|
||||||
|
#[cfg(feature = "ecdsa")]
|
||||||
|
use opaque_ke::key_exchange::sigma_i::ecdsa::{self, Ecdsa, PreHash};
|
||||||
|
#[cfg(all(feature = "ristretto255", feature = "ed25519"))]
|
||||||
|
use opaque_ke::key_exchange::sigma_i::pure_eddsa::PureEddsa;
|
||||||
|
#[cfg(feature = "ecdsa")]
|
||||||
|
use opaque_ke::key_exchange::sigma_i::{CachedMessage, HashOutput, Message, SigmaI};
|
||||||
|
use opaque_ke::key_exchange::tripledh::TripleDh;
|
||||||
|
use opaque_ke::key_exchange::KeyExchange;
|
||||||
|
use opaque_ke::keypair::{KeyPair, PublicKey};
|
||||||
|
use opaque_ke::ksf::Identity;
|
||||||
|
use opaque_ke::{
|
||||||
|
CipherSuite, ClientLogin, ClientLoginFinishParameters, ClientLoginStartResult,
|
||||||
|
ClientRegistration, ClientRegistrationFinishParameters, ClientRegistrationStartResult,
|
||||||
|
ServerLogin, ServerLoginParameters, ServerLoginStartResult, ServerRegistration, ServerSetup,
|
||||||
|
};
|
||||||
|
#[cfg(all(feature = "curve25519", feature = "ristretto255"))]
|
||||||
|
use opaque_ke::{Curve25519, Ristretto255};
|
||||||
use p256::NistP256;
|
use p256::NistP256;
|
||||||
use p384::NistP384;
|
use p384::NistP384;
|
||||||
use p521::NistP521;
|
use p521::NistP521;
|
||||||
use rand::rngs::OsRng;
|
use rand::rngs::OsRng;
|
||||||
|
use sha2::{Sha256, Sha384, Sha512};
|
||||||
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq};
|
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq};
|
||||||
|
|
||||||
use crate::ciphersuite::{OprfGroup, OprfHash};
|
type OprfGroup<CS: CipherSuite> = <CS::OprfCs as voprf::CipherSuite>::Group;
|
||||||
use crate::envelope::NonceLen;
|
type OprfHash<CS: CipherSuite> = <CS::OprfCs as voprf::CipherSuite>::Hash;
|
||||||
use crate::hash::OutputSize;
|
type KeGroup<CS: CipherSuite> = <CS::KeyExchange as KeyExchange>::Group;
|
||||||
use crate::key_exchange::group::KeGroup;
|
|
||||||
use crate::key_exchange::tripledh::{DiffieHellman, TripleDh};
|
|
||||||
use crate::keypair::{KeyPair, PublicKey};
|
|
||||||
use crate::ksf::Identity;
|
|
||||||
use crate::opaque::MaskedResponseLen;
|
|
||||||
use crate::{
|
|
||||||
CipherSuite, ClientLogin, ClientLoginFinishParameters, ClientLoginStartResult,
|
|
||||||
ClientRegistration, ClientRegistrationFinishParameters, ClientRegistrationStartResult,
|
|
||||||
ServerLogin, ServerLoginStartParameters, ServerLoginStartResult, ServerRegistration,
|
|
||||||
ServerSetup,
|
|
||||||
};
|
|
||||||
#[cfg(all(feature = "curve25519", feature = "ristretto255"))]
|
|
||||||
use crate::{Curve25519, Ristretto255};
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn p256() {
|
fn triple_dh_p256() {
|
||||||
struct Suite;
|
struct Suite;
|
||||||
|
|
||||||
impl CipherSuite for Suite {
|
impl CipherSuite for Suite {
|
||||||
type OprfCs = NistP256;
|
type OprfCs = NistP256;
|
||||||
type KeGroup = NistP256;
|
type KeyExchange = TripleDh<NistP256, Sha256>;
|
||||||
type KeyExchange = TripleDh;
|
|
||||||
type Ksf = Identity;
|
type Ksf = Identity;
|
||||||
}
|
}
|
||||||
|
|
||||||
test::<Suite>(
|
test::<Suite>(
|
||||||
Mechanism::EccKeyPairGen,
|
Mechanism::EccKeyPairGen,
|
||||||
NistP256::OID,
|
NistP256::OID,
|
||||||
|
Attribute::Derive(true),
|
||||||
Mechanism::Sha256Hmac,
|
Mechanism::Sha256Hmac,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn p384() {
|
fn triple_dh_p384() {
|
||||||
struct Suite;
|
struct Suite;
|
||||||
|
|
||||||
impl CipherSuite for Suite {
|
impl CipherSuite for Suite {
|
||||||
type OprfCs = NistP384;
|
type OprfCs = NistP384;
|
||||||
type KeGroup = NistP384;
|
type KeyExchange = TripleDh<NistP384, Sha384>;
|
||||||
type KeyExchange = TripleDh;
|
|
||||||
type Ksf = Identity;
|
type Ksf = Identity;
|
||||||
}
|
}
|
||||||
|
|
||||||
test::<Suite>(
|
test::<Suite>(
|
||||||
Mechanism::EccKeyPairGen,
|
Mechanism::EccKeyPairGen,
|
||||||
NistP384::OID,
|
NistP384::OID,
|
||||||
|
Attribute::Derive(true),
|
||||||
Mechanism::Sha384Hmac,
|
Mechanism::Sha384Hmac,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn p521() {
|
fn triple_dh_p521() {
|
||||||
struct Suite;
|
struct Suite;
|
||||||
|
|
||||||
impl CipherSuite for Suite {
|
impl CipherSuite for Suite {
|
||||||
type OprfCs = NistP521;
|
type OprfCs = NistP521;
|
||||||
type KeGroup = NistP521;
|
type KeyExchange = TripleDh<NistP521, Sha512>;
|
||||||
type KeyExchange = TripleDh;
|
|
||||||
type Ksf = Identity;
|
type Ksf = Identity;
|
||||||
}
|
}
|
||||||
|
|
||||||
test::<Suite>(
|
test::<Suite>(
|
||||||
Mechanism::EccKeyPairGen,
|
Mechanism::EccKeyPairGen,
|
||||||
NistP521::OID,
|
NistP521::OID,
|
||||||
|
Attribute::Derive(true),
|
||||||
Mechanism::Sha512Hmac,
|
Mechanism::Sha512Hmac,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(all(feature = "curve25519", feature = "ristretto255"))]
|
#[cfg(all(feature = "curve25519", feature = "ristretto255"))]
|
||||||
fn curve25519() {
|
fn triple_dh_curve25519() {
|
||||||
struct Suite;
|
struct Suite;
|
||||||
|
|
||||||
impl CipherSuite for Suite {
|
impl CipherSuite for Suite {
|
||||||
type OprfCs = Ristretto255;
|
type OprfCs = Ristretto255;
|
||||||
type KeGroup = Curve25519;
|
type KeyExchange = TripleDh<Curve25519, Sha512>;
|
||||||
type KeyExchange = TripleDh;
|
|
||||||
type Ksf = Identity;
|
type Ksf = Identity;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,6 +138,64 @@ fn curve25519() {
|
|||||||
// implementation. See https://github.com/softhsm/SoftHSMv2/issues/647.
|
// implementation. See https://github.com/softhsm/SoftHSMv2/issues/647.
|
||||||
Mechanism::EccEdwardsKeyPairGen,
|
Mechanism::EccEdwardsKeyPairGen,
|
||||||
ObjectIdentifier::new("1.3.101.110").unwrap(),
|
ObjectIdentifier::new("1.3.101.110").unwrap(),
|
||||||
|
Attribute::Derive(true),
|
||||||
|
Mechanism::Sha512Hmac,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(feature = "ecdsa")]
|
||||||
|
fn sigma_i_p256() {
|
||||||
|
struct Suite;
|
||||||
|
|
||||||
|
impl CipherSuite for Suite {
|
||||||
|
type OprfCs = NistP256;
|
||||||
|
type KeyExchange = SigmaI<Ecdsa<NistP256, Sha256>, NistP256, Sha256>;
|
||||||
|
type Ksf = Identity;
|
||||||
|
}
|
||||||
|
|
||||||
|
test::<Suite>(
|
||||||
|
Mechanism::EccKeyPairGen,
|
||||||
|
NistP256::OID,
|
||||||
|
Attribute::Sign(true),
|
||||||
|
Mechanism::Sha256Hmac,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(feature = "ecdsa")]
|
||||||
|
fn sigma_i_p384() {
|
||||||
|
struct Suite;
|
||||||
|
|
||||||
|
impl CipherSuite for Suite {
|
||||||
|
type OprfCs = NistP384;
|
||||||
|
type KeyExchange = SigmaI<Ecdsa<NistP384, Sha384>, NistP384, Sha384>;
|
||||||
|
type Ksf = Identity;
|
||||||
|
}
|
||||||
|
|
||||||
|
test::<Suite>(
|
||||||
|
Mechanism::EccKeyPairGen,
|
||||||
|
NistP384::OID,
|
||||||
|
Attribute::Sign(true),
|
||||||
|
Mechanism::Sha384Hmac,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(all(feature = "ristretto255", feature = "ed25519"))]
|
||||||
|
fn sigma_i_ed25519() {
|
||||||
|
struct Suite;
|
||||||
|
|
||||||
|
impl CipherSuite for Suite {
|
||||||
|
type OprfCs = Ristretto255;
|
||||||
|
type KeyExchange = SigmaI<PureEddsa<Ed25519>, Ristretto255, Sha512>;
|
||||||
|
type Ksf = Identity;
|
||||||
|
}
|
||||||
|
|
||||||
|
test::<Suite>(
|
||||||
|
Mechanism::EccEdwardsKeyPairGen,
|
||||||
|
ObjectIdentifier::new_unwrap("1.3.101.112"),
|
||||||
|
Attribute::Sign(true),
|
||||||
Mechanism::Sha512Hmac,
|
Mechanism::Sha512Hmac,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -128,43 +203,31 @@ fn curve25519() {
|
|||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct RemoteKey(ObjectHandle);
|
struct RemoteKey(ObjectHandle);
|
||||||
|
|
||||||
trait Pkcs11DiffieHellman<KG: KeGroup> {
|
trait Pkcs11PublicKey
|
||||||
fn pkcs11_diffie_hellman(
|
where
|
||||||
&self,
|
Self: Group,
|
||||||
server_pk: &PublicKey<KG>,
|
{
|
||||||
client_pk: &PublicKey<KG>,
|
fn pkcs11_public_key(data: &[u8]) -> PublicKey<Self>;
|
||||||
) -> GenericArray<u8, KG::PkLen>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test<CS: CipherSuite<KeyExchange = TripleDh>>(
|
trait Pkcs11KeyExchange<KE: KeyExchange> {
|
||||||
|
fn pkcs11_key_exchange<CS: CipherSuite>(
|
||||||
|
&self,
|
||||||
|
server_pk: &PublicKey<KE::Group>,
|
||||||
|
data: KE::KE2BuilderData<'_, CS>,
|
||||||
|
) -> KE::KE2BuilderInput<CS>;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test<CS: 'static + CipherSuite>(
|
||||||
dh_mechanism: Mechanism,
|
dh_mechanism: Mechanism,
|
||||||
oid: ObjectIdentifier,
|
oid: ObjectIdentifier,
|
||||||
|
attribute: Attribute,
|
||||||
hmac_mechanism: Mechanism,
|
hmac_mechanism: Mechanism,
|
||||||
) where
|
) where
|
||||||
RemoteKey: Pkcs11DiffieHellman<CS::KeGroup>,
|
KeGroup<CS>: Pkcs11PublicKey,
|
||||||
<CS::KeGroup as KeGroup>::Sk: DiffieHellman<CS::KeGroup>,
|
RemoteKey: Pkcs11KeyExchange<CS::KeyExchange>,
|
||||||
// MaskedResponse: (Nonce + Hash) + KePk
|
|
||||||
NonceLen: Add<OutputSize<OprfHash<CS>>>,
|
|
||||||
Sum<NonceLen, OutputSize<OprfHash<CS>>>: ArrayLength<u8> + Add<<CS::KeGroup as KeGroup>::PkLen>,
|
|
||||||
MaskedResponseLen<CS>: ArrayLength<u8>,
|
|
||||||
// Ke1State: KeSk + Nonce
|
|
||||||
<CS::KeGroup as KeGroup>::SkLen: Add<NonceLen>,
|
|
||||||
Sum<<CS::KeGroup as KeGroup>::SkLen, NonceLen>: ArrayLength<u8>,
|
|
||||||
// Ke1Message: Nonce + KePk
|
|
||||||
NonceLen: Add<<CS::KeGroup as KeGroup>::PkLen>,
|
|
||||||
Sum<NonceLen, <CS::KeGroup as KeGroup>::PkLen>: ArrayLength<u8>,
|
|
||||||
// Ke2State: (Hash + Hash) + Hash
|
|
||||||
OutputSize<OprfHash<CS>>: Add<OutputSize<OprfHash<CS>>>,
|
|
||||||
Sum<OutputSize<OprfHash<CS>>, OutputSize<OprfHash<CS>>>:
|
|
||||||
ArrayLength<u8> + Add<OutputSize<OprfHash<CS>>>,
|
|
||||||
Sum<Sum<OutputSize<OprfHash<CS>>, OutputSize<OprfHash<CS>>>, OutputSize<OprfHash<CS>>>:
|
|
||||||
ArrayLength<u8>,
|
|
||||||
// Ke2Message: (Nonce + KePk) + Hash
|
|
||||||
NonceLen: Add<<CS::KeGroup as KeGroup>::PkLen>,
|
|
||||||
Sum<NonceLen, <CS::KeGroup as KeGroup>::PkLen>: ArrayLength<u8> + Add<OutputSize<OprfHash<CS>>>,
|
|
||||||
Sum<Sum<NonceLen, <CS::KeGroup as KeGroup>::PkLen>, OutputSize<OprfHash<CS>>>: ArrayLength<u8>,
|
|
||||||
{
|
{
|
||||||
let (remote_key, pk) = pkcs11_generate_key_pair(dh_mechanism, oid);
|
let (remote_key, pk) = pkcs11_generate_key_pair(dh_mechanism, oid, attribute);
|
||||||
|
|
||||||
let keypair = KeyPair::new(RemoteKey(remote_key), pk);
|
let keypair = KeyPair::new(RemoteKey(remote_key), pk);
|
||||||
let oprf_seed = pkcs11_generate_oprf_seed(<OprfHash<CS> as OutputSizeUser>::OutputSize::U64);
|
let oprf_seed = pkcs11_generate_oprf_seed(<OprfHash<CS> as OutputSizeUser>::OutputSize::U64);
|
||||||
@@ -212,12 +275,12 @@ fn test<CS: CipherSuite<KeyExchange = TripleDh>>(
|
|||||||
key_material,
|
key_material,
|
||||||
Some(file),
|
Some(file),
|
||||||
message,
|
message,
|
||||||
ServerLoginStartParameters::default(),
|
ServerLoginParameters::default(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let shared_secret = builder
|
let shared_secret = builder
|
||||||
.private_key()
|
.private_key()
|
||||||
.pkcs11_diffie_hellman(server_setup.keypair().public(), builder.data());
|
.pkcs11_key_exchange(server_setup.keypair().public(), builder.data());
|
||||||
|
|
||||||
let ServerLoginStartResult {
|
let ServerLoginStartResult {
|
||||||
message,
|
message,
|
||||||
@@ -228,6 +291,7 @@ fn test<CS: CipherSuite<KeyExchange = TripleDh>>(
|
|||||||
let message = client
|
let message = client
|
||||||
.clone()
|
.clone()
|
||||||
.finish(
|
.finish(
|
||||||
|
&mut OsRng,
|
||||||
PASSWORD.as_bytes(),
|
PASSWORD.as_bytes(),
|
||||||
message,
|
message,
|
||||||
ClientLoginFinishParameters::default(),
|
ClientLoginFinishParameters::default(),
|
||||||
@@ -235,7 +299,11 @@ fn test<CS: CipherSuite<KeyExchange = TripleDh>>(
|
|||||||
.map(|result| result.message);
|
.map(|result| result.message);
|
||||||
|
|
||||||
message
|
message
|
||||||
.map(|message| server.finish(message).unwrap())
|
.map(|message| {
|
||||||
|
server
|
||||||
|
.finish(message, ServerLoginParameters::default())
|
||||||
|
.unwrap()
|
||||||
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -263,10 +331,11 @@ static SESSION: LazyLock<Mutex<Session>> = LazyLock::new(|| {
|
|||||||
Mutex::new(session)
|
Mutex::new(session)
|
||||||
});
|
});
|
||||||
|
|
||||||
fn pkcs11_generate_key_pair<KG: KeGroup>(
|
fn pkcs11_generate_key_pair<G: Group + Pkcs11PublicKey>(
|
||||||
mechanism: Mechanism,
|
mechanism: Mechanism,
|
||||||
oid: ObjectIdentifier,
|
oid: ObjectIdentifier,
|
||||||
) -> (ObjectHandle, PublicKey<KG>) {
|
attribute: Attribute,
|
||||||
|
) -> (ObjectHandle, PublicKey<G>) {
|
||||||
let session = SESSION.lock().unwrap();
|
let session = SESSION.lock().unwrap();
|
||||||
let (pk, remote_key) = session
|
let (pk, remote_key) = session
|
||||||
.generate_key_pair(
|
.generate_key_pair(
|
||||||
@@ -275,7 +344,7 @@ fn pkcs11_generate_key_pair<KG: KeGroup>(
|
|||||||
Attribute::Token(false),
|
Attribute::Token(false),
|
||||||
Attribute::EcParams(oid.to_der().unwrap()),
|
Attribute::EcParams(oid.to_der().unwrap()),
|
||||||
],
|
],
|
||||||
&[Attribute::Token(false), Attribute::Derive(true)],
|
&[Attribute::Token(false), attribute],
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -290,7 +359,7 @@ fn pkcs11_generate_key_pair<KG: KeGroup>(
|
|||||||
drop(session);
|
drop(session);
|
||||||
|
|
||||||
let pk = OctetString::from_der(&pk).unwrap();
|
let pk = OctetString::from_der(&pk).unwrap();
|
||||||
let pk = PublicKey::deserialize(pk.as_bytes()).unwrap();
|
let pk = G::pkcs11_public_key(pk.as_bytes());
|
||||||
|
|
||||||
(remote_key, pk)
|
(remote_key, pk)
|
||||||
}
|
}
|
||||||
@@ -342,50 +411,131 @@ fn pkcs11_hkdf<CS: CipherSuite>(
|
|||||||
okm
|
okm
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pkcs11DiffieHellman<NistP256> for RemoteKey {
|
impl Pkcs11PublicKey for NistP256 {
|
||||||
fn pkcs11_diffie_hellman(
|
fn pkcs11_public_key(data: &[u8]) -> PublicKey<NistP256> {
|
||||||
&self,
|
pkcs11_ec_public_key(data)
|
||||||
server_pk: &PublicKey<NistP256>,
|
|
||||||
client_pk: &PublicKey<NistP256>,
|
|
||||||
) -> GenericArray<u8, <NistP256 as KeGroup>::PkLen> {
|
|
||||||
ec_pkcs_11_derive_secret::<NistP256>(self.0, server_pk, client_pk)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pkcs11DiffieHellman<NistP384> for RemoteKey {
|
impl Pkcs11PublicKey for NistP384 {
|
||||||
fn pkcs11_diffie_hellman(
|
fn pkcs11_public_key(data: &[u8]) -> PublicKey<NistP384> {
|
||||||
&self,
|
pkcs11_ec_public_key(data)
|
||||||
server_pk: &PublicKey<NistP384>,
|
|
||||||
client_pk: &PublicKey<NistP384>,
|
|
||||||
) -> GenericArray<u8, <NistP384 as KeGroup>::PkLen> {
|
|
||||||
ec_pkcs_11_derive_secret::<NistP384>(self.0, server_pk, client_pk)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pkcs11DiffieHellman<NistP521> for RemoteKey {
|
impl Pkcs11PublicKey for NistP521 {
|
||||||
fn pkcs11_diffie_hellman(
|
fn pkcs11_public_key(data: &[u8]) -> PublicKey<NistP521> {
|
||||||
&self,
|
pkcs11_ec_public_key(data)
|
||||||
server_pk: &PublicKey<NistP521>,
|
|
||||||
client_pk: &PublicKey<NistP521>,
|
|
||||||
) -> GenericArray<u8, <NistP521 as KeGroup>::PkLen> {
|
|
||||||
ec_pkcs_11_derive_secret::<NistP521>(self.0, server_pk, client_pk)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(all(feature = "curve25519", feature = "ristretto255"))]
|
#[cfg(all(feature = "curve25519", feature = "ristretto255"))]
|
||||||
impl Pkcs11DiffieHellman<Curve25519> for RemoteKey {
|
impl Pkcs11PublicKey for Curve25519 {
|
||||||
fn pkcs11_diffie_hellman(
|
fn pkcs11_public_key(data: &[u8]) -> PublicKey<Curve25519> {
|
||||||
|
PublicKey::deserialize(data).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(all(feature = "ristretto255", feature = "ed25519"))]
|
||||||
|
impl Pkcs11PublicKey for Ed25519 {
|
||||||
|
fn pkcs11_public_key(data: &[u8]) -> PublicKey<Ed25519> {
|
||||||
|
PublicKey::deserialize(data).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pkcs11KeyExchange<TripleDh<NistP256, Sha256>> for RemoteKey {
|
||||||
|
fn pkcs11_key_exchange<CS: CipherSuite>(
|
||||||
|
&self,
|
||||||
|
server_pk: &PublicKey<NistP256>,
|
||||||
|
client_pk: &PublicKey<NistP256>,
|
||||||
|
) -> GenericArray<u8, <NistP256 as Group>::PkLen> {
|
||||||
|
pkcs_11_ecdsa_derive_secret::<NistP256>(self.0, server_pk, client_pk)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pkcs11KeyExchange<TripleDh<NistP384, Sha384>> for RemoteKey {
|
||||||
|
fn pkcs11_key_exchange<CS: CipherSuite>(
|
||||||
|
&self,
|
||||||
|
server_pk: &PublicKey<NistP384>,
|
||||||
|
client_pk: &PublicKey<NistP384>,
|
||||||
|
) -> GenericArray<u8, <NistP384 as Group>::PkLen> {
|
||||||
|
pkcs_11_ecdsa_derive_secret::<NistP384>(self.0, server_pk, client_pk)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pkcs11KeyExchange<TripleDh<NistP521, Sha512>> for RemoteKey {
|
||||||
|
fn pkcs11_key_exchange<CS: CipherSuite>(
|
||||||
|
&self,
|
||||||
|
server_pk: &PublicKey<NistP521>,
|
||||||
|
client_pk: &PublicKey<NistP521>,
|
||||||
|
) -> GenericArray<u8, <NistP521 as Group>::PkLen> {
|
||||||
|
pkcs_11_ecdsa_derive_secret::<NistP521>(self.0, server_pk, client_pk)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(all(feature = "curve25519", feature = "ristretto255"))]
|
||||||
|
impl Pkcs11KeyExchange<TripleDh<Curve25519, Sha512>> for RemoteKey {
|
||||||
|
fn pkcs11_key_exchange<CS: CipherSuite>(
|
||||||
&self,
|
&self,
|
||||||
_: &PublicKey<Curve25519>,
|
_: &PublicKey<Curve25519>,
|
||||||
pk: &PublicKey<Curve25519>,
|
pk: &PublicKey<Curve25519>,
|
||||||
) -> GenericArray<u8, <Curve25519 as KeGroup>::PkLen> {
|
) -> GenericArray<u8, <Curve25519 as Group>::PkLen> {
|
||||||
let shared_secret = pkcs11_derive_secret(self.0, &pk.serialize());
|
let shared_secret = pkcs_11_dh_derive_secret(self.0, &pk.serialize());
|
||||||
|
|
||||||
GenericArray::clone_from_slice(&shared_secret)
|
GenericArray::clone_from_slice(&shared_secret)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pkcs11_derive_secret(sk: ObjectHandle, pk: &[u8]) -> Vec<u8> {
|
#[cfg(feature = "ecdsa")]
|
||||||
|
impl Pkcs11KeyExchange<SigmaI<Ecdsa<NistP256, Sha256>, NistP256, Sha256>> for RemoteKey {
|
||||||
|
fn pkcs11_key_exchange<CS: CipherSuite>(
|
||||||
|
&self,
|
||||||
|
_: &PublicKey<NistP256>,
|
||||||
|
message: &Message<CS, NistP256>,
|
||||||
|
) -> (ecdsa::Signature<NistP256>, PreHash<Sha256>) {
|
||||||
|
pkcs_11_ecdsa_sign::<NistP256, Sha256>(self.0, message.hash())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "ecdsa")]
|
||||||
|
impl Pkcs11KeyExchange<SigmaI<Ecdsa<NistP384, Sha384>, NistP384, Sha384>> for RemoteKey {
|
||||||
|
fn pkcs11_key_exchange<CS: CipherSuite>(
|
||||||
|
&self,
|
||||||
|
_: &PublicKey<NistP384>,
|
||||||
|
message: &Message<CS, NistP384>,
|
||||||
|
) -> (ecdsa::Signature<NistP384>, PreHash<Sha384>) {
|
||||||
|
pkcs_11_ecdsa_sign::<NistP384, Sha384>(self.0, message.hash())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(all(feature = "ristretto255", feature = "ed25519"))]
|
||||||
|
impl Pkcs11KeyExchange<SigmaI<PureEddsa<Ed25519>, Ristretto255, Sha512>> for RemoteKey {
|
||||||
|
fn pkcs11_key_exchange<CS: CipherSuite>(
|
||||||
|
&self,
|
||||||
|
_: &PublicKey<Ed25519>,
|
||||||
|
message: &Message<CS, Ristretto255>,
|
||||||
|
) -> (ed25519::Signature, CachedMessage<CS, Ristretto255>) {
|
||||||
|
pkcs_11_eddsa_sign(self.0, message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pkcs11_ec_public_key<G>(data: &[u8]) -> PublicKey<G>
|
||||||
|
where
|
||||||
|
G: Group<Pk = NonIdentity<G>> + CurveArithmetic,
|
||||||
|
FieldBytesSize<G>: ModulusSize,
|
||||||
|
AffinePoint<G>:
|
||||||
|
FromEncodedPoint<G> + ToEncodedPoint<G> + PrimeCurveAffine<Curve = ProjectivePoint<G>>,
|
||||||
|
{
|
||||||
|
PublicKey::deserialize(
|
||||||
|
elliptic_curve::PublicKey::<G>::from_sec1_bytes(data)
|
||||||
|
.unwrap()
|
||||||
|
.to_encoded_point(true)
|
||||||
|
.as_bytes(),
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pkcs_11_dh_derive_secret(sk: ObjectHandle, pk: &[u8]) -> Vec<u8> {
|
||||||
let session = SESSION.lock().unwrap();
|
let session = SESSION.lock().unwrap();
|
||||||
let shared_secret = session
|
let shared_secret = session
|
||||||
.derive_key(
|
.derive_key(
|
||||||
@@ -413,23 +563,23 @@ fn pkcs11_derive_secret(sk: ObjectHandle, pk: &[u8]) -> Vec<u8> {
|
|||||||
shared_secret
|
shared_secret
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ec_pkcs_11_derive_secret<KG>(
|
fn pkcs_11_ecdsa_derive_secret<G>(
|
||||||
server_sk: ObjectHandle,
|
server_sk: ObjectHandle,
|
||||||
server_pk: &PublicKey<KG>,
|
server_pk: &PublicKey<G>,
|
||||||
client_pk: &PublicKey<KG>,
|
client_pk: &PublicKey<G>,
|
||||||
) -> GenericArray<u8, <KG as KeGroup>::PkLen>
|
) -> GenericArray<u8, <G as Group>::PkLen>
|
||||||
where
|
where
|
||||||
KG: KeGroup<Pk = ProjectivePoint<KG>> + CurveArithmetic,
|
G: Group<Pk = NonIdentity<G>> + CurveArithmetic,
|
||||||
AffinePoint<KG>: DecompressPoint<KG> + ToEncodedPoint<KG>,
|
AffinePoint<G>: DecompressPoint<G> + ToEncodedPoint<G>,
|
||||||
FieldBytesSize<KG>: ModulusSize,
|
FieldBytesSize<G>: ModulusSize,
|
||||||
{
|
{
|
||||||
let client_pk_point = client_pk.to_group_type();
|
let client_pk_point = client_pk.to_group_type();
|
||||||
let client_pk = client_pk.serialize();
|
let client_pk = client_pk.serialize();
|
||||||
let client_pk = OctetStringRef::new(&client_pk).unwrap();
|
let client_pk = OctetStringRef::new(&client_pk).unwrap();
|
||||||
let client_pk = client_pk.to_der().unwrap();
|
let client_pk = client_pk.to_der().unwrap();
|
||||||
|
|
||||||
let shared_secret_bytes = pkcs11_derive_secret(server_sk, &client_pk);
|
let shared_secret_bytes = pkcs_11_dh_derive_secret(server_sk, &client_pk);
|
||||||
let shared_secret_point = AffinePoint::<KG>::decompress(
|
let shared_secret_point = AffinePoint::<G>::decompress(
|
||||||
&GenericArray::clone_from_slice(&shared_secret_bytes),
|
&GenericArray::clone_from_slice(&shared_secret_bytes),
|
||||||
Choice::from(0),
|
Choice::from(0),
|
||||||
)
|
)
|
||||||
@@ -437,14 +587,14 @@ where
|
|||||||
let mut shared_secret = GenericArray::default();
|
let mut shared_secret = GenericArray::default();
|
||||||
shared_secret[1..].copy_from_slice(&shared_secret_bytes);
|
shared_secret[1..].copy_from_slice(&shared_secret_bytes);
|
||||||
|
|
||||||
let shifted_client_pk = client_pk_point + ProjectivePoint::<KG>::generator();
|
let shifted_client_pk = client_pk_point.0.to_point() + ProjectivePoint::<G>::generator();
|
||||||
let shifted_client_pk = shifted_client_pk.to_affine().to_encoded_point(true);
|
let shifted_client_pk = shifted_client_pk.to_affine().to_encoded_point(true);
|
||||||
let shifted_client_pk = OctetStringRef::new(shifted_client_pk.as_bytes()).unwrap();
|
let shifted_client_pk = OctetStringRef::new(shifted_client_pk.as_bytes()).unwrap();
|
||||||
let shifted_client_pk = shifted_client_pk.to_der().unwrap();
|
let shifted_client_pk = shifted_client_pk.to_der().unwrap();
|
||||||
|
|
||||||
let check_point = pkcs11_derive_secret(server_sk, &shifted_client_pk);
|
let check_point = pkcs_11_dh_derive_secret(server_sk, &shifted_client_pk);
|
||||||
|
|
||||||
let shifted_server_pk = server_pk.to_group_type() + shared_secret_point;
|
let shifted_server_pk = server_pk.to_group_type().0.to_point() + shared_secret_point;
|
||||||
let shifted_server_pk = shifted_server_pk.to_affine();
|
let shifted_server_pk = shifted_server_pk.to_affine();
|
||||||
|
|
||||||
let tag = u8::conditional_select(
|
let tag = u8::conditional_select(
|
||||||
@@ -456,3 +606,52 @@ where
|
|||||||
|
|
||||||
shared_secret
|
shared_secret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "ecdsa")]
|
||||||
|
fn pkcs_11_ecdsa_sign<G: CurveArithmetic + PrimeCurve, H: Clone + Digest>(
|
||||||
|
sk: ObjectHandle,
|
||||||
|
hashes: HashOutput<H>,
|
||||||
|
) -> (ecdsa::Signature<G>, PreHash<H>)
|
||||||
|
where
|
||||||
|
SignatureSize<G>: ArrayLength<u8>,
|
||||||
|
{
|
||||||
|
let sign_pre_hash = hashes.sign.finalize();
|
||||||
|
|
||||||
|
let session = SESSION.lock().unwrap();
|
||||||
|
let signature = session.sign(&Mechanism::Ecdsa, sk, &sign_pre_hash).unwrap();
|
||||||
|
drop(session);
|
||||||
|
|
||||||
|
let signature = ::ecdsa::Signature::from_slice(&signature).unwrap();
|
||||||
|
|
||||||
|
(
|
||||||
|
ecdsa::Signature(signature),
|
||||||
|
PreHash(hashes.verify.finalize()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(all(feature = "ristretto255", feature = "ed25519"))]
|
||||||
|
fn pkcs_11_eddsa_sign<CS: CipherSuite>(
|
||||||
|
sk: ObjectHandle,
|
||||||
|
message: &Message<CS, Ristretto255>,
|
||||||
|
) -> (ed25519::Signature, CachedMessage<CS, Ristretto255>) {
|
||||||
|
use cryptoki::mechanism::eddsa::{EddsaParams, EddsaSignatureScheme};
|
||||||
|
|
||||||
|
let mut message_bytes = Vec::new();
|
||||||
|
message
|
||||||
|
.sign_message()
|
||||||
|
.for_each(|bytes| message_bytes.extend_from_slice(bytes));
|
||||||
|
|
||||||
|
let session = SESSION.lock().unwrap();
|
||||||
|
let signature = session
|
||||||
|
.sign(
|
||||||
|
&Mechanism::Eddsa(EddsaParams::new(EddsaSignatureScheme::Pure)),
|
||||||
|
sk,
|
||||||
|
&message_bytes,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
drop(session);
|
||||||
|
|
||||||
|
let signature = ed25519::Signature::from_slice(&signature).unwrap();
|
||||||
|
|
||||||
|
(signature, message.to_cached())
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user