added support for aead in nym-crypto
This commit is contained in:
Generated
+17
@@ -86,6 +86,21 @@ dependencies = [
|
||||
"subtle 2.5.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aes-gcm-siv"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae0784134ba9375416d469ec31e7c5f9fa94405049cf08c5ce5b4698be673e0d"
|
||||
dependencies = [
|
||||
"aead",
|
||||
"aes",
|
||||
"cipher",
|
||||
"ctr",
|
||||
"polyval",
|
||||
"subtle 2.5.0",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.7.8"
|
||||
@@ -4770,7 +4785,9 @@ dependencies = [
|
||||
name = "nym-crypto"
|
||||
version = "0.4.0"
|
||||
dependencies = [
|
||||
"aead",
|
||||
"aes",
|
||||
"aes-gcm-siv",
|
||||
"blake3",
|
||||
"bs58",
|
||||
"cipher",
|
||||
|
||||
@@ -169,6 +169,8 @@ readme = "README.md"
|
||||
addr = "0.15.6"
|
||||
aes = "0.8.1"
|
||||
aes-gcm = "0.10.1"
|
||||
aes-gcm-siv = "0.11.1"
|
||||
aead = "0.5.2"
|
||||
anyhow = "1.0.89"
|
||||
argon2 = "0.5.0"
|
||||
async-trait = "0.1.82"
|
||||
|
||||
@@ -18,7 +18,7 @@ nym-ecash-time = { path = "../ecash-time" }
|
||||
nym-credential-storage = { path = "../credential-storage" }
|
||||
nym-credentials = { path = "../credentials" }
|
||||
nym-credentials-interface = { path = "../credentials-interface" }
|
||||
nym-crypto = { path = "../crypto", features = ["rand", "asymmetric", "symmetric", "aes", "hashing"] }
|
||||
nym-crypto = { path = "../crypto", features = ["rand", "asymmetric", "stream_cipher", "aes", "hashing"] }
|
||||
nym-network-defaults = { path = "../network-defaults" }
|
||||
nym-validator-client = { path = "../client-libs/validator-client", default-features = false }
|
||||
nym-ecash-contract-common = { path = "../cosmwasm-smart-contracts/ecash-contract" }
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use nym_crypto::asymmetric::identity::Ed25519RecoveryError;
|
||||
use nym_gateway_requests::registration::handshake::shared_key::SharedKeyConversionError;
|
||||
use nym_gateway_requests::registration::handshake::SharedKeyConversionError;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
use crate::BadGateway;
|
||||
use cosmrs::AccountId;
|
||||
use nym_crypto::asymmetric::identity;
|
||||
use nym_gateway_requests::registration::handshake::SharedKeys;
|
||||
use nym_gateway_requests::registration::handshake::LegacySharedKeys;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::str::FromStr;
|
||||
@@ -64,7 +64,7 @@ impl From<GatewayDetails> for GatewayRegistration {
|
||||
impl GatewayDetails {
|
||||
pub fn new_remote(
|
||||
gateway_id: identity::PublicKey,
|
||||
derived_aes128_ctr_blake3_hmac_keys: Arc<SharedKeys>,
|
||||
derived_aes128_ctr_blake3_hmac_keys: Arc<LegacySharedKeys>,
|
||||
gateway_owner_address: Option<AccountId>,
|
||||
gateway_listener: Url,
|
||||
) -> Self {
|
||||
@@ -87,7 +87,7 @@ impl GatewayDetails {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn shared_key(&self) -> Option<&SharedKeys> {
|
||||
pub fn shared_key(&self) -> Option<&LegacySharedKeys> {
|
||||
match self {
|
||||
GatewayDetails::Remote(details) => Some(&details.derived_aes128_ctr_blake3_hmac_keys),
|
||||
GatewayDetails::Custom(_) => None,
|
||||
@@ -185,11 +185,13 @@ impl TryFrom<RawRemoteGatewayDetails> for RemoteGatewayDetails {
|
||||
})?;
|
||||
|
||||
let derived_aes128_ctr_blake3_hmac_keys = Arc::new(
|
||||
SharedKeys::try_from_base58_string(&value.derived_aes128_ctr_blake3_hmac_keys_bs58)
|
||||
.map_err(|source| BadGateway::MalformedSharedKeys {
|
||||
gateway_id: value.gateway_id_bs58.clone(),
|
||||
source,
|
||||
})?,
|
||||
LegacySharedKeys::try_from_base58_string(
|
||||
&value.derived_aes128_ctr_blake3_hmac_keys_bs58,
|
||||
)
|
||||
.map_err(|source| BadGateway::MalformedSharedKeys {
|
||||
gateway_id: value.gateway_id_bs58.clone(),
|
||||
source,
|
||||
})?,
|
||||
);
|
||||
|
||||
let gateway_owner_address = value
|
||||
@@ -242,7 +244,7 @@ pub struct RemoteGatewayDetails {
|
||||
|
||||
// note: `SharedKeys` implement ZeroizeOnDrop, meaning when `RemoteGatewayDetails` is dropped,
|
||||
// the keys will be zeroized
|
||||
pub derived_aes128_ctr_blake3_hmac_keys: Arc<SharedKeys>,
|
||||
pub derived_aes128_ctr_blake3_hmac_keys: Arc<LegacySharedKeys>,
|
||||
|
||||
pub gateway_owner_address: Option<AccountId>,
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ pub mod v1_1_33 {
|
||||
use nym_client_core_gateways_storage::{
|
||||
CustomGatewayDetails, GatewayDetails, GatewayRegistration, RemoteGatewayDetails,
|
||||
};
|
||||
use nym_gateway_requests::registration::handshake::SharedKeys;
|
||||
use nym_gateway_requests::registration::handshake::LegacySharedKeys;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sha2::{digest::Digest, Sha256};
|
||||
use std::ops::Deref;
|
||||
@@ -58,7 +58,7 @@ pub mod v1_1_33 {
|
||||
}
|
||||
|
||||
impl PersistedGatewayConfig {
|
||||
fn verify(&self, shared_key: &SharedKeys) -> bool {
|
||||
fn verify(&self, shared_key: &LegacySharedKeys) -> bool {
|
||||
let key_bytes = Zeroizing::new(shared_key.to_bytes());
|
||||
|
||||
let mut key_hasher = Sha256::new();
|
||||
@@ -74,7 +74,7 @@ pub mod v1_1_33 {
|
||||
gateway_id: String,
|
||||
}
|
||||
|
||||
fn load_shared_key<P: AsRef<Path>>(path: P) -> Result<SharedKeys, ClientCoreError> {
|
||||
fn load_shared_key<P: AsRef<Path>>(path: P) -> Result<LegacySharedKeys, ClientCoreError> {
|
||||
// the shared key was a simple pem file
|
||||
Ok(nym_pemstore::load_key(path)?)
|
||||
}
|
||||
@@ -83,7 +83,7 @@ pub mod v1_1_33 {
|
||||
gateway_id: String,
|
||||
gateway_owner: String,
|
||||
gateway_listener: String,
|
||||
gateway_shared_key: SharedKeys,
|
||||
gateway_shared_key: LegacySharedKeys,
|
||||
) -> Result<GatewayDetails, ClientCoreError> {
|
||||
Ok(GatewayDetails::Remote(RemoteGatewayDetails {
|
||||
gateway_id: gateway_id
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
use crate::client::key_manager::persistence::KeyStore;
|
||||
use nym_crypto::asymmetric::{encryption, identity};
|
||||
use nym_gateway_requests::registration::handshake::SharedKeys;
|
||||
use nym_gateway_requests::registration::handshake::LegacySharedKeys;
|
||||
use nym_sphinx::acknowledgements::AckKey;
|
||||
use rand::{CryptoRng, RngCore};
|
||||
use std::sync::Arc;
|
||||
@@ -84,5 +84,5 @@ fn _assert_keys_zeroize_on_drop() {
|
||||
_assert_zeroize_on_drop::<identity::KeyPair>();
|
||||
_assert_zeroize_on_drop::<encryption::KeyPair>();
|
||||
_assert_zeroize_on_drop::<AckKey>();
|
||||
_assert_zeroize_on_drop::<SharedKeys>();
|
||||
_assert_zeroize_on_drop::<LegacySharedKeys>();
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ use nym_client_core_gateways_storage::{
|
||||
};
|
||||
use nym_crypto::asymmetric::identity;
|
||||
use nym_gateway_client::client::InitGatewayClient;
|
||||
use nym_gateway_requests::registration::handshake::SharedKeys;
|
||||
use nym_gateway_requests::registration::handshake::LegacySharedKeys;
|
||||
use nym_sphinx::addressing::clients::Recipient;
|
||||
use nym_topology::gateway;
|
||||
use nym_validator_client::client::IdentityKey;
|
||||
@@ -104,7 +104,7 @@ impl SelectedGateway {
|
||||
/// - shared keys derived between ourselves and the node
|
||||
/// - an authenticated handle of an ephemeral handle created for the purposes of registration
|
||||
pub struct RegistrationResult {
|
||||
pub shared_keys: Arc<SharedKeys>,
|
||||
pub shared_keys: Arc<LegacySharedKeys>,
|
||||
pub authenticated_ephemeral_client: InitGatewayClient,
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ use nym_credentials::CredentialSpendingData;
|
||||
use nym_crypto::asymmetric::identity;
|
||||
use nym_gateway_requests::authentication::encrypted_address::EncryptedAddressBytes;
|
||||
use nym_gateway_requests::iv::IV;
|
||||
use nym_gateway_requests::registration::handshake::{client_handshake, SharedKeys};
|
||||
use nym_gateway_requests::registration::handshake::{client_handshake, LegacySharedKeys};
|
||||
use nym_gateway_requests::{
|
||||
BinaryRequest, ClientControlRequest, ServerResponse, CREDENTIAL_UPDATE_V2_PROTOCOL_VERSION,
|
||||
CURRENT_PROTOCOL_VERSION,
|
||||
@@ -80,7 +80,7 @@ pub struct GatewayClient<C, St = EphemeralCredentialStorage> {
|
||||
gateway_address: String,
|
||||
gateway_identity: identity::PublicKey,
|
||||
local_identity: Arc<identity::KeyPair>,
|
||||
shared_key: Option<Arc<SharedKeys>>,
|
||||
shared_key: Option<Arc<LegacySharedKeys>>,
|
||||
connection: SocketState,
|
||||
packet_router: PacketRouter,
|
||||
bandwidth_controller: Option<BandwidthController<C, St>>,
|
||||
@@ -98,7 +98,7 @@ impl<C, St> GatewayClient<C, St> {
|
||||
gateway_config: GatewayConfig,
|
||||
local_identity: Arc<identity::KeyPair>,
|
||||
// TODO: make it mandatory. if you don't want to pass it, use `new_init`
|
||||
shared_key: Option<Arc<SharedKeys>>,
|
||||
shared_key: Option<Arc<LegacySharedKeys>>,
|
||||
packet_router: PacketRouter,
|
||||
bandwidth_controller: Option<BandwidthController<C, St>>,
|
||||
task_client: TaskClient,
|
||||
@@ -450,7 +450,7 @@ impl<C, St> GatewayClient<C, St> {
|
||||
|
||||
async fn authenticate(
|
||||
&mut self,
|
||||
shared_key: Option<SharedKeys>,
|
||||
shared_key: Option<LegacySharedKeys>,
|
||||
) -> Result<(), GatewayClientError> {
|
||||
if shared_key.is_none() && self.shared_key.is_none() {
|
||||
return Err(GatewayClientError::NoSharedKeyAvailable);
|
||||
@@ -508,7 +508,13 @@ impl<C, St> GatewayClient<C, St> {
|
||||
/// Helper method to either call register or authenticate based on self.shared_key value
|
||||
pub async fn perform_initial_authentication(
|
||||
&mut self,
|
||||
) -> Result<Arc<SharedKeys>, GatewayClientError> {
|
||||
) -> Result<Arc<LegacySharedKeys>, GatewayClientError> {
|
||||
// 1. check gateway's protocol version
|
||||
// 2. if error or new handshake unsupported => fallback to the old registration/authentication
|
||||
// 3. otherwise continue with the updated key derivation
|
||||
|
||||
// ?. if new protocol is supported and we have an old key, upgrade it to aes-gcm-siv
|
||||
|
||||
if self.authenticated {
|
||||
debug!("Already authenticated");
|
||||
return if let Some(shared_key) = &self.shared_key {
|
||||
@@ -833,7 +839,9 @@ impl<C, St> GatewayClient<C, St> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn authenticate_and_start(&mut self) -> Result<Arc<SharedKeys>, GatewayClientError>
|
||||
pub async fn authenticate_and_start(
|
||||
&mut self,
|
||||
) -> Result<Arc<LegacySharedKeys>, GatewayClientError>
|
||||
where
|
||||
C: DkgQueryClient + Send + Sync,
|
||||
St: CredentialStorage,
|
||||
|
||||
@@ -7,7 +7,7 @@ use nym_gateway_requests::BinaryResponse;
|
||||
use tungstenite::{protocol::Message, Error as WsError};
|
||||
|
||||
pub use client::{config::GatewayClientConfig, GatewayClient, GatewayConfig};
|
||||
pub use nym_gateway_requests::registration::handshake::SharedKeys;
|
||||
pub use nym_gateway_requests::registration::handshake::LegacySharedKeys;
|
||||
pub use packet_router::{
|
||||
AcknowledgementReceiver, AcknowledgementSender, MixnetMessageReceiver, MixnetMessageSender,
|
||||
PacketRouter,
|
||||
@@ -45,7 +45,7 @@ pub(crate) fn cleanup_socket_messages(
|
||||
|
||||
pub(crate) fn try_decrypt_binary_message(
|
||||
bin_msg: Vec<u8>,
|
||||
shared_keys: &SharedKeys,
|
||||
shared_keys: &LegacySharedKeys,
|
||||
) -> Option<Vec<u8>> {
|
||||
match BinaryResponse::try_from_encrypted_tagged_bytes(bin_msg, shared_keys) {
|
||||
Ok(bin_response) => match bin_response {
|
||||
|
||||
@@ -10,7 +10,7 @@ use futures::channel::oneshot;
|
||||
use futures::stream::{SplitSink, SplitStream};
|
||||
use futures::{SinkExt, StreamExt};
|
||||
use log::*;
|
||||
use nym_gateway_requests::registration::handshake::SharedKeys;
|
||||
use nym_gateway_requests::registration::handshake::LegacySharedKeys;
|
||||
use nym_gateway_requests::{ServerResponse, SimpleGatewayRequestsError};
|
||||
use nym_task::TaskClient;
|
||||
use std::os::raw::c_int as RawFd;
|
||||
@@ -62,7 +62,7 @@ pub(crate) struct PartiallyDelegatedHandle {
|
||||
|
||||
struct PartiallyDelegatedRouter {
|
||||
packet_router: PacketRouter,
|
||||
shared_key: Arc<SharedKeys>,
|
||||
shared_key: Arc<LegacySharedKeys>,
|
||||
client_bandwidth: ClientBandwidth,
|
||||
|
||||
stream_return: SplitStreamSender,
|
||||
@@ -72,7 +72,7 @@ struct PartiallyDelegatedRouter {
|
||||
impl PartiallyDelegatedRouter {
|
||||
fn new(
|
||||
packet_router: PacketRouter,
|
||||
shared_key: Arc<SharedKeys>,
|
||||
shared_key: Arc<LegacySharedKeys>,
|
||||
client_bandwidth: ClientBandwidth,
|
||||
stream_return: SplitStreamSender,
|
||||
stream_return_requester: oneshot::Receiver<()>,
|
||||
@@ -247,7 +247,7 @@ impl PartiallyDelegatedHandle {
|
||||
pub(crate) fn split_and_listen_for_mixnet_messages(
|
||||
conn: WsConn,
|
||||
packet_router: PacketRouter,
|
||||
shared_key: Arc<SharedKeys>,
|
||||
shared_key: Arc<LegacySharedKeys>,
|
||||
client_bandwidth: ClientBandwidth,
|
||||
shutdown: TaskClient,
|
||||
) -> Self {
|
||||
|
||||
@@ -8,7 +8,9 @@ license = { workspace = true }
|
||||
repository = { workspace = true }
|
||||
|
||||
[dependencies]
|
||||
aes-gcm-siv = { workspace = true, optional = true }
|
||||
aes = { workspace = true, optional = true }
|
||||
aead = { workspace = true, optional = true }
|
||||
bs58 = { workspace = true }
|
||||
blake3 = { workspace = true, features = ["traits-preview"], optional = true }
|
||||
ctr = { workspace = true, optional = true }
|
||||
@@ -35,9 +37,10 @@ rand_chacha = { workspace = true }
|
||||
|
||||
[features]
|
||||
default = ["sphinx"]
|
||||
aead = ["dep:aead", "aead/std", "aes-gcm-siv", "generic-array"]
|
||||
serde = ["dep:serde", "serde_bytes", "ed25519-dalek/serde", "x25519-dalek/serde"]
|
||||
asymmetric = ["x25519-dalek", "ed25519-dalek", "zeroize"]
|
||||
hashing = ["blake3", "digest", "hkdf", "hmac", "generic-array"]
|
||||
symmetric = ["aes", "ctr", "cipher", "generic-array"]
|
||||
stream_cipher = ["aes", "ctr", "cipher", "generic-array"]
|
||||
sphinx = ["nym-sphinx-types/sphinx"]
|
||||
outfox = ["nym-sphinx-types/outfox"]
|
||||
|
||||
@@ -10,21 +10,22 @@ pub mod crypto_hash;
|
||||
pub mod hkdf;
|
||||
#[cfg(feature = "hashing")]
|
||||
pub mod hmac;
|
||||
#[cfg(all(feature = "asymmetric", feature = "hashing", feature = "symmetric"))]
|
||||
#[cfg(all(feature = "asymmetric", feature = "hashing", feature = "stream_cipher"))]
|
||||
pub mod shared_key;
|
||||
#[cfg(feature = "symmetric")]
|
||||
pub mod symmetric;
|
||||
|
||||
#[cfg(feature = "hashing")]
|
||||
pub use digest::{Digest, OutputSizeUser};
|
||||
#[cfg(any(feature = "hashing", feature = "symmetric"))]
|
||||
#[cfg(any(feature = "hashing", feature = "stream_cipher", feature = "aead"))]
|
||||
pub use generic_array;
|
||||
|
||||
// with the below my idea was to try to introduce having a single place of importing all hashing, encryption,
|
||||
// etc. algorithms and import them elsewhere as needed via common/crypto
|
||||
#[cfg(feature = "symmetric")]
|
||||
#[cfg(feature = "stream_cipher")]
|
||||
pub use aes;
|
||||
#[cfg(feature = "aead")]
|
||||
pub use aes_gcm_siv::{Aes128GcmSiv, Aes256GcmSiv};
|
||||
#[cfg(feature = "hashing")]
|
||||
pub use blake3;
|
||||
#[cfg(feature = "symmetric")]
|
||||
#[cfg(feature = "stream_cipher")]
|
||||
pub use ctr;
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use aead::{Aead, AeadCore, AeadInPlace, Buffer, KeyInit, Payload};
|
||||
|
||||
#[cfg(feature = "rand")]
|
||||
use rand::{CryptoRng, RngCore};
|
||||
|
||||
pub use aead::{Error as AeadError, Key as AeadKey, KeySizeUser, Nonce, Tag};
|
||||
|
||||
#[cfg(feature = "rand")]
|
||||
pub fn generate_key<A, R>(rng: &mut R) -> AeadKey<A>
|
||||
where
|
||||
A: KeyInit,
|
||||
R: RngCore + CryptoRng,
|
||||
{
|
||||
let mut key = AeadKey::<A>::default();
|
||||
rng.fill_bytes(&mut key);
|
||||
key
|
||||
}
|
||||
|
||||
#[cfg(feature = "rand")]
|
||||
pub fn random_nonce<A, R>(rng: &mut R) -> Nonce<A>
|
||||
where
|
||||
A: AeadCore,
|
||||
R: RngCore + CryptoRng,
|
||||
{
|
||||
<A as AeadCore>::generate_nonce(rng)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn encrypt<'msg, 'aad, A>(
|
||||
key: &AeadKey<A>,
|
||||
nonce: &Nonce<A>,
|
||||
plaintext: impl Into<Payload<'msg, 'aad>>,
|
||||
) -> Result<Vec<u8>, AeadError>
|
||||
where
|
||||
A: Aead + KeyInit,
|
||||
{
|
||||
let cipher = A::new(key);
|
||||
cipher.encrypt(nonce, plaintext)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn decrypt<'msg, 'aad, A>(
|
||||
key: &AeadKey<A>,
|
||||
nonce: &Nonce<A>,
|
||||
ciphertext: impl Into<Payload<'msg, 'aad>>,
|
||||
) -> Result<Vec<u8>, AeadError>
|
||||
where
|
||||
A: Aead + KeyInit,
|
||||
{
|
||||
let cipher = A::new(key);
|
||||
cipher.decrypt(nonce, ciphertext)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn encrypt_in_place<A>(
|
||||
key: &AeadKey<A>,
|
||||
nonce: &Nonce<A>,
|
||||
associated_data: &[u8],
|
||||
buffer: &mut dyn Buffer,
|
||||
) -> Result<(), AeadError>
|
||||
where
|
||||
A: AeadInPlace + KeyInit,
|
||||
{
|
||||
let cipher = A::new(key);
|
||||
cipher.encrypt_in_place(nonce, associated_data, buffer)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn decrypt_in_place<A>(
|
||||
key: &AeadKey<A>,
|
||||
nonce: &Nonce<A>,
|
||||
associated_data: &[u8],
|
||||
buffer: &mut dyn Buffer,
|
||||
) -> Result<(), AeadError>
|
||||
where
|
||||
A: AeadInPlace + KeyInit,
|
||||
{
|
||||
let cipher = A::new(key);
|
||||
cipher.decrypt_in_place(nonce, associated_data, buffer)
|
||||
}
|
||||
@@ -1,4 +1,7 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#[cfg(feature = "aead")]
|
||||
pub mod aead;
|
||||
#[cfg(feature = "stream_cipher")]
|
||||
pub mod stream_cipher;
|
||||
|
||||
@@ -2,12 +2,13 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use cipher::{Iv, StreamCipher};
|
||||
pub use cipher::{IvSizeUser, KeyIvInit, KeySizeUser};
|
||||
|
||||
#[cfg(feature = "rand")]
|
||||
use rand::{CryptoRng, RngCore};
|
||||
|
||||
// re-export this for ease of use
|
||||
pub use cipher::Key as CipherKey;
|
||||
pub use cipher::{IvSizeUser, KeyIvInit, KeySizeUser};
|
||||
|
||||
// SECURITY:
|
||||
// TODO: note that this is not the most secure approach here
|
||||
|
||||
@@ -21,7 +21,7 @@ thiserror = { workspace = true }
|
||||
tracing = { workspace = true, features = ["log"] }
|
||||
zeroize = { workspace = true }
|
||||
|
||||
nym-crypto = { path = "../crypto" }
|
||||
nym-crypto = { path = "../crypto", features = ["aead"] }
|
||||
nym-pemstore = { path = "../pemstore" }
|
||||
nym-sphinx = { path = "../nymsphinx" }
|
||||
nym-task = { path = "../task" }
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::iv::IV;
|
||||
use crate::registration::handshake::shared_key::SharedKeys;
|
||||
use crate::registration::handshake::LegacySharedKeys;
|
||||
use nym_crypto::symmetric::stream_cipher;
|
||||
use nym_sphinx::params::GatewayEncryptionAlgorithm;
|
||||
use nym_sphinx::params::LegacyGatewayEncryptionAlgorithm;
|
||||
use nym_sphinx::{DestinationAddressBytes, DESTINATION_ADDRESS_LENGTH};
|
||||
use thiserror::Error;
|
||||
|
||||
@@ -28,8 +28,8 @@ pub enum EncryptedAddressConversionError {
|
||||
}
|
||||
|
||||
impl EncryptedAddressBytes {
|
||||
pub fn new(address: &DestinationAddressBytes, key: &SharedKeys, iv: &IV) -> Self {
|
||||
let ciphertext = stream_cipher::encrypt::<GatewayEncryptionAlgorithm>(
|
||||
pub fn new(address: &DestinationAddressBytes, key: &LegacySharedKeys, iv: &IV) -> Self {
|
||||
let ciphertext = stream_cipher::encrypt::<LegacyGatewayEncryptionAlgorithm>(
|
||||
key.encryption_key(),
|
||||
iv.inner(),
|
||||
address.as_bytes_ref(),
|
||||
@@ -40,7 +40,12 @@ impl EncryptedAddressBytes {
|
||||
EncryptedAddressBytes(enc_address)
|
||||
}
|
||||
|
||||
pub fn verify(&self, address: &DestinationAddressBytes, key: &SharedKeys, iv: &IV) -> bool {
|
||||
pub fn verify(
|
||||
&self,
|
||||
address: &DestinationAddressBytes,
|
||||
key: &LegacySharedKeys,
|
||||
iv: &IV,
|
||||
) -> bool {
|
||||
self == &Self::new(address, key, iv)
|
||||
}
|
||||
|
||||
|
||||
@@ -3,15 +3,15 @@
|
||||
|
||||
use nym_crypto::generic_array::{typenum::Unsigned, GenericArray};
|
||||
use nym_crypto::symmetric::stream_cipher::{random_iv, IvSizeUser, IV as CryptoIV};
|
||||
use nym_sphinx::params::GatewayEncryptionAlgorithm;
|
||||
use nym_sphinx::params::LegacyGatewayEncryptionAlgorithm;
|
||||
use rand::{CryptoRng, RngCore};
|
||||
use thiserror::Error;
|
||||
|
||||
type NonceSize = <GatewayEncryptionAlgorithm as IvSizeUser>::IvSize;
|
||||
type NonceSize = <LegacyGatewayEncryptionAlgorithm as IvSizeUser>::IvSize;
|
||||
|
||||
// I think 'IV' looks better than 'Iv', feel free to change that.
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
pub struct IV(CryptoIV<GatewayEncryptionAlgorithm>);
|
||||
pub struct IV(CryptoIV<LegacyGatewayEncryptionAlgorithm>);
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
// I think 'IV' looks better than 'Iv', feel free to change that.
|
||||
@@ -29,7 +29,7 @@ pub enum IVConversionError {
|
||||
|
||||
impl IV {
|
||||
pub fn new_random<R: RngCore + CryptoRng>(rng: &mut R) -> Self {
|
||||
IV(random_iv::<GatewayEncryptionAlgorithm, _>(rng))
|
||||
IV(random_iv::<LegacyGatewayEncryptionAlgorithm, _>(rng))
|
||||
}
|
||||
|
||||
pub fn try_from_bytes(bytes: &[u8]) -> Result<Self, IVConversionError> {
|
||||
@@ -48,7 +48,7 @@ impl IV {
|
||||
self.0.as_ref()
|
||||
}
|
||||
|
||||
pub fn inner(&self) -> &CryptoIV<GatewayEncryptionAlgorithm> {
|
||||
pub fn inner(&self) -> &CryptoIV<LegacyGatewayEncryptionAlgorithm> {
|
||||
&self.0
|
||||
}
|
||||
|
||||
|
||||
@@ -13,15 +13,17 @@ pub mod models;
|
||||
pub mod registration;
|
||||
pub mod types;
|
||||
|
||||
pub const CURRENT_PROTOCOL_VERSION: u8 = CREDENTIAL_UPDATE_V2_PROTOCOL_VERSION;
|
||||
pub const CURRENT_PROTOCOL_VERSION: u8 = AES_GCM_SIV_PROTOCOL_VERSION;
|
||||
|
||||
/// Defines the current version of the communication protocol between gateway and clients.
|
||||
/// It has to be incremented for any breaking change.
|
||||
// history:
|
||||
// 1 - initial release
|
||||
// 2 - changes to client credentials structure
|
||||
// 3 - change to AES-GCM-SIV and non-zero IVs
|
||||
pub const INITIAL_PROTOCOL_VERSION: u8 = 1;
|
||||
pub const CREDENTIAL_UPDATE_V2_PROTOCOL_VERSION: u8 = 2;
|
||||
pub const AES_GCM_SIV_PROTOCOL_VERSION: u8 = 3;
|
||||
|
||||
pub type GatewayMac = HmacOutput<GatewayIntegrityHmacAlgorithm>;
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright 2020 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::registration::handshake::shared_key::SharedKeys;
|
||||
use crate::registration::handshake::state::State;
|
||||
use crate::registration::handshake::LegacySharedKeys;
|
||||
use crate::registration::handshake::{error::HandshakeError, WsItem};
|
||||
use futures::future::BoxFuture;
|
||||
use futures::task::{Context, Poll};
|
||||
@@ -15,7 +15,7 @@ use std::pin::Pin;
|
||||
use tungstenite::Message as WsMessage;
|
||||
|
||||
pub(crate) struct ClientHandshake<'a> {
|
||||
handshake_future: BoxFuture<'a, Result<SharedKeys, HandshakeError>>,
|
||||
handshake_future: BoxFuture<'a, Result<LegacySharedKeys, HandshakeError>>,
|
||||
}
|
||||
|
||||
impl<'a> ClientHandshake<'a> {
|
||||
@@ -121,7 +121,7 @@ impl<'a> ClientHandshake<'a> {
|
||||
}
|
||||
|
||||
impl<'a> Future for ClientHandshake<'a> {
|
||||
type Output = Result<SharedKeys, HandshakeError>;
|
||||
type Output = Result<LegacySharedKeys, HandshakeError>;
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
Pin::new(&mut self.handshake_future).poll(cx)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright 2020 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::registration::handshake::shared_key::SharedKeys;
|
||||
use crate::registration::handshake::state::State;
|
||||
use crate::registration::handshake::LegacySharedKeys;
|
||||
use crate::registration::handshake::{error::HandshakeError, WsItem};
|
||||
use futures::future::BoxFuture;
|
||||
use futures::task::{Context, Poll};
|
||||
@@ -14,7 +14,7 @@ use std::pin::Pin;
|
||||
use tungstenite::Message as WsMessage;
|
||||
|
||||
pub(crate) struct GatewayHandshake<'a> {
|
||||
handshake_future: BoxFuture<'a, Result<SharedKeys, HandshakeError>>,
|
||||
handshake_future: BoxFuture<'a, Result<LegacySharedKeys, HandshakeError>>,
|
||||
}
|
||||
|
||||
impl<'a> GatewayHandshake<'a> {
|
||||
@@ -106,7 +106,7 @@ impl<'a> GatewayHandshake<'a> {
|
||||
}
|
||||
|
||||
impl<'a> Future for GatewayHandshake<'a> {
|
||||
type Output = Result<SharedKeys, HandshakeError>;
|
||||
type Output = Result<LegacySharedKeys, HandshakeError>;
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
Pin::new(&mut self.handshake_future).poll(cx)
|
||||
|
||||
@@ -3,25 +3,29 @@
|
||||
|
||||
use self::client::ClientHandshake;
|
||||
use self::error::HandshakeError;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use self::gateway::GatewayHandshake;
|
||||
pub use self::shared_key::{SharedKeySize, SharedKeys};
|
||||
use futures::{Sink, Stream};
|
||||
use nym_crypto::asymmetric::identity;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use nym_task::TaskClient;
|
||||
use rand::{CryptoRng, RngCore};
|
||||
use tungstenite::{Error as WsError, Message as WsMessage};
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use self::gateway::GatewayHandshake;
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use nym_task::TaskClient;
|
||||
|
||||
pub(crate) type WsItem = Result<WsMessage, WsError>;
|
||||
|
||||
mod client;
|
||||
pub mod error;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
mod gateway;
|
||||
pub mod shared_key;
|
||||
mod shared_key;
|
||||
mod state;
|
||||
|
||||
pub use self::shared_key::legacy::{LegacySharedKeySize, LegacySharedKeys};
|
||||
pub use self::shared_key::{SharedKeyConversionError, SharedSymmetricKey};
|
||||
|
||||
// Note: the handshake is built on top of WebSocket, but in principle it shouldn't be too difficult
|
||||
// to remove that restriction, by just changing Sink<WsMessage> and Stream<Item = WsMessage> into
|
||||
// AsyncWrite and AsyncRead and slightly adjusting the implementation. But right now
|
||||
@@ -34,7 +38,7 @@ pub async fn client_handshake<'a, S>(
|
||||
gateway_pubkey: identity::PublicKey,
|
||||
expects_credential_usage: bool,
|
||||
#[cfg(not(target_arch = "wasm32"))] shutdown: TaskClient,
|
||||
) -> Result<SharedKeys, HandshakeError>
|
||||
) -> Result<LegacySharedKeys, HandshakeError>
|
||||
where
|
||||
S: Stream<Item = WsItem> + Sink<WsMessage> + Unpin + Send + 'a,
|
||||
{
|
||||
@@ -57,7 +61,7 @@ pub async fn gateway_handshake<'a, S>(
|
||||
identity: &'a identity::KeyPair,
|
||||
received_init_payload: Vec<u8>,
|
||||
shutdown: TaskClient,
|
||||
) -> Result<SharedKeys, HandshakeError>
|
||||
) -> Result<LegacySharedKeys, HandshakeError>
|
||||
where
|
||||
S: Stream<Item = WsItem> + Sink<WsMessage> + Unpin + Send + 'a,
|
||||
{
|
||||
|
||||
+22
-32
@@ -1,6 +1,7 @@
|
||||
// Copyright 2020-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::registration::handshake::shared_key::SharedKeyConversionError;
|
||||
use crate::{GatewayMacSize, GatewayRequestsError};
|
||||
use nym_crypto::generic_array::{
|
||||
typenum::{Sum, Unsigned, U16},
|
||||
@@ -9,43 +10,32 @@ use nym_crypto::generic_array::{
|
||||
use nym_crypto::hmac::{compute_keyed_hmac, recompute_keyed_hmac_and_verify_tag};
|
||||
use nym_crypto::symmetric::stream_cipher::{self, CipherKey, KeySizeUser, IV};
|
||||
use nym_pemstore::traits::PemStorableKey;
|
||||
use nym_sphinx::params::{GatewayEncryptionAlgorithm, GatewayIntegrityHmacAlgorithm};
|
||||
use nym_sphinx::params::{GatewayIntegrityHmacAlgorithm, LegacyGatewayEncryptionAlgorithm};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use thiserror::Error;
|
||||
use zeroize::{Zeroize, ZeroizeOnDrop};
|
||||
|
||||
// shared key is as long as the encryption key and the MAC key combined.
|
||||
pub type SharedKeySize = Sum<EncryptionKeySize, MacKeySize>;
|
||||
pub type LegacySharedKeySize = Sum<EncryptionKeySize, MacKeySize>;
|
||||
|
||||
// we're using 16 byte long key in sphinx, so let's use the same one here
|
||||
type MacKeySize = U16;
|
||||
type EncryptionKeySize = <GatewayEncryptionAlgorithm as KeySizeUser>::KeySize;
|
||||
type EncryptionKeySize = <LegacyGatewayEncryptionAlgorithm as KeySizeUser>::KeySize;
|
||||
|
||||
/// Shared key used when computing MAC for messages exchanged between client and its gateway.
|
||||
pub type MacKey = GenericArray<u8, MacKeySize>;
|
||||
|
||||
#[derive(Debug, PartialEq, Serialize, Deserialize, Zeroize, ZeroizeOnDrop)]
|
||||
pub struct SharedKeys {
|
||||
encryption_key: CipherKey<GatewayEncryptionAlgorithm>,
|
||||
pub struct LegacySharedKeys {
|
||||
encryption_key: CipherKey<LegacyGatewayEncryptionAlgorithm>,
|
||||
mac_key: MacKey,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Error)]
|
||||
pub enum SharedKeyConversionError {
|
||||
#[error("the string representation of the shared keys was malformed - {0}")]
|
||||
DecodeError(#[from] bs58::decode::Error),
|
||||
#[error(
|
||||
"the received shared keys had invalid size. Got: {received}, but expected: {expected}"
|
||||
)]
|
||||
InvalidSharedKeysSize { received: usize, expected: usize },
|
||||
}
|
||||
|
||||
impl SharedKeys {
|
||||
impl LegacySharedKeys {
|
||||
pub fn try_from_bytes(bytes: &[u8]) -> Result<Self, SharedKeyConversionError> {
|
||||
if bytes.len() != SharedKeySize::to_usize() {
|
||||
if bytes.len() != LegacySharedKeySize::to_usize() {
|
||||
return Err(SharedKeyConversionError::InvalidSharedKeysSize {
|
||||
received: bytes.len(),
|
||||
expected: SharedKeySize::to_usize(),
|
||||
expected: LegacySharedKeySize::to_usize(),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -53,7 +43,7 @@ impl SharedKeys {
|
||||
GenericArray::clone_from_slice(&bytes[..EncryptionKeySize::to_usize()]);
|
||||
let mac_key = GenericArray::clone_from_slice(&bytes[EncryptionKeySize::to_usize()..]);
|
||||
|
||||
Ok(SharedKeys {
|
||||
Ok(LegacySharedKeys {
|
||||
encryption_key,
|
||||
mac_key,
|
||||
})
|
||||
@@ -65,17 +55,17 @@ impl SharedKeys {
|
||||
pub fn encrypt_and_tag(
|
||||
&self,
|
||||
data: &[u8],
|
||||
iv: Option<&IV<GatewayEncryptionAlgorithm>>,
|
||||
iv: Option<&IV<LegacyGatewayEncryptionAlgorithm>>,
|
||||
) -> Vec<u8> {
|
||||
let encrypted_data = match iv {
|
||||
Some(iv) => stream_cipher::encrypt::<GatewayEncryptionAlgorithm>(
|
||||
Some(iv) => stream_cipher::encrypt::<LegacyGatewayEncryptionAlgorithm>(
|
||||
self.encryption_key(),
|
||||
iv,
|
||||
data,
|
||||
),
|
||||
None => {
|
||||
let zero_iv = stream_cipher::zero_iv::<GatewayEncryptionAlgorithm>();
|
||||
stream_cipher::encrypt::<GatewayEncryptionAlgorithm>(
|
||||
let zero_iv = stream_cipher::zero_iv::<LegacyGatewayEncryptionAlgorithm>();
|
||||
stream_cipher::encrypt::<LegacyGatewayEncryptionAlgorithm>(
|
||||
self.encryption_key(),
|
||||
&zero_iv,
|
||||
data,
|
||||
@@ -93,7 +83,7 @@ impl SharedKeys {
|
||||
pub fn decrypt_tagged(
|
||||
&self,
|
||||
enc_data: &[u8],
|
||||
iv: Option<&IV<GatewayEncryptionAlgorithm>>,
|
||||
iv: Option<&IV<LegacyGatewayEncryptionAlgorithm>>,
|
||||
) -> Result<Vec<u8>, GatewayRequestsError> {
|
||||
let mac_size = GatewayMacSize::to_usize();
|
||||
if enc_data.len() < mac_size {
|
||||
@@ -115,9 +105,9 @@ impl SharedKeys {
|
||||
// together with a mutable one
|
||||
let message_bytes_mut = &mut enc_data.to_vec()[mac_size..];
|
||||
|
||||
let zero_iv = stream_cipher::zero_iv::<GatewayEncryptionAlgorithm>();
|
||||
let zero_iv = stream_cipher::zero_iv::<LegacyGatewayEncryptionAlgorithm>();
|
||||
let iv = iv.unwrap_or(&zero_iv);
|
||||
stream_cipher::decrypt_in_place::<GatewayEncryptionAlgorithm>(
|
||||
stream_cipher::decrypt_in_place::<LegacyGatewayEncryptionAlgorithm>(
|
||||
self.encryption_key(),
|
||||
iv,
|
||||
message_bytes_mut,
|
||||
@@ -125,7 +115,7 @@ impl SharedKeys {
|
||||
Ok(message_bytes_mut.to_vec())
|
||||
}
|
||||
|
||||
pub fn encryption_key(&self) -> &CipherKey<GatewayEncryptionAlgorithm> {
|
||||
pub fn encryption_key(&self) -> &CipherKey<LegacyGatewayEncryptionAlgorithm> {
|
||||
&self.encryption_key
|
||||
}
|
||||
|
||||
@@ -145,7 +135,7 @@ impl SharedKeys {
|
||||
val: S,
|
||||
) -> Result<Self, SharedKeyConversionError> {
|
||||
let decoded = bs58::decode(val.into()).into_vec()?;
|
||||
SharedKeys::try_from_bytes(&decoded)
|
||||
LegacySharedKeys::try_from_bytes(&decoded)
|
||||
}
|
||||
|
||||
pub fn to_base58_string(&self) -> String {
|
||||
@@ -153,13 +143,13 @@ impl SharedKeys {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SharedKeys> for String {
|
||||
fn from(keys: SharedKeys) -> Self {
|
||||
impl From<LegacySharedKeys> for String {
|
||||
fn from(keys: LegacySharedKeys) -> Self {
|
||||
keys.to_base58_string()
|
||||
}
|
||||
}
|
||||
|
||||
impl PemStorableKey for SharedKeys {
|
||||
impl PemStorableKey for LegacySharedKeys {
|
||||
type Error = SharedKeyConversionError;
|
||||
|
||||
fn pem_type() -> &'static str {
|
||||
@@ -0,0 +1,90 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::GatewayRequestsError;
|
||||
use nym_crypto::generic_array::{typenum::Unsigned, GenericArray};
|
||||
use nym_crypto::symmetric::aead::{self, AeadKey, KeySizeUser, Nonce};
|
||||
use nym_pemstore::traits::PemStorableKey;
|
||||
use nym_sphinx::params::GatewayEncryptionAlgorithm;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use thiserror::Error;
|
||||
use zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing};
|
||||
|
||||
pub mod legacy;
|
||||
|
||||
#[derive(Debug, PartialEq, Serialize, Deserialize, Zeroize, ZeroizeOnDrop)]
|
||||
pub struct SharedSymmetricKey(AeadKey<GatewayEncryptionAlgorithm>);
|
||||
|
||||
type KeySize = <GatewayEncryptionAlgorithm as KeySizeUser>::KeySize;
|
||||
|
||||
#[derive(Debug, Clone, Copy, Error)]
|
||||
pub enum SharedKeyConversionError {
|
||||
#[error("the string representation of the shared key was malformed: {0}")]
|
||||
DecodeError(#[from] bs58::decode::Error),
|
||||
#[error(
|
||||
"the received shared keys had invalid size. Got: {received}, but expected: {expected}"
|
||||
)]
|
||||
InvalidSharedKeysSize { received: usize, expected: usize },
|
||||
}
|
||||
|
||||
impl SharedSymmetricKey {
|
||||
pub fn try_from_bytes(bytes: &[u8]) -> Result<Self, SharedKeyConversionError> {
|
||||
if bytes.len() != KeySize::to_usize() {
|
||||
return Err(SharedKeyConversionError::InvalidSharedKeysSize {
|
||||
received: bytes.len(),
|
||||
expected: KeySize::to_usize(),
|
||||
});
|
||||
}
|
||||
|
||||
Ok(SharedSymmetricKey(GenericArray::clone_from_slice(bytes)))
|
||||
}
|
||||
|
||||
pub fn to_bytes(&self) -> Vec<u8> {
|
||||
self.0.iter().copied().collect()
|
||||
}
|
||||
|
||||
pub fn try_from_base58_string<S: Into<String>>(
|
||||
val: S,
|
||||
) -> Result<Self, SharedKeyConversionError> {
|
||||
let bs58_str = Zeroizing::new(val.into());
|
||||
let decoded = Zeroizing::new(bs58::decode(bs58_str).into_vec()?);
|
||||
Self::try_from_bytes(&decoded)
|
||||
}
|
||||
|
||||
pub fn to_base58_string(&self) -> String {
|
||||
let bytes = Zeroizing::new(self.to_bytes());
|
||||
bs58::encode(bytes).into_string()
|
||||
}
|
||||
|
||||
pub fn encrypt(
|
||||
&self,
|
||||
plaintext: &[u8],
|
||||
nonce: &Nonce<GatewayEncryptionAlgorithm>,
|
||||
) -> Result<Vec<u8>, GatewayRequestsError> {
|
||||
aead::encrypt::<GatewayEncryptionAlgorithm>(&self.0, &nonce, plaintext).map_err(Into::into)
|
||||
}
|
||||
|
||||
pub fn decrypt(
|
||||
&self,
|
||||
ciphertext: &[u8],
|
||||
nonce: &Nonce<GatewayEncryptionAlgorithm>,
|
||||
) -> Result<Vec<u8>, GatewayRequestsError> {
|
||||
aead::decrypt::<GatewayEncryptionAlgorithm>(&self.0, &nonce, ciphertext).map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
impl PemStorableKey for SharedSymmetricKey {
|
||||
type Error = SharedKeyConversionError;
|
||||
|
||||
fn pem_type() -> &'static str {
|
||||
"AES-256-GCM-SIV GATEWAY SHARED KEY"
|
||||
}
|
||||
|
||||
fn to_bytes(&self) -> Vec<u8> {
|
||||
self.to_bytes()
|
||||
}
|
||||
|
||||
fn from_bytes(bytes: &[u8]) -> Result<Self, Self::Error> {
|
||||
Self::try_from_bytes(bytes)
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,8 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::registration::handshake::error::HandshakeError;
|
||||
use crate::registration::handshake::shared_key::{SharedKeySize, SharedKeys};
|
||||
use crate::registration::handshake::WsItem;
|
||||
use crate::registration::handshake::{LegacySharedKeySize, LegacySharedKeys};
|
||||
use crate::types;
|
||||
use futures::{Sink, SinkExt, Stream, StreamExt};
|
||||
use nym_crypto::{
|
||||
@@ -12,7 +12,7 @@ use nym_crypto::{
|
||||
hkdf,
|
||||
symmetric::stream_cipher,
|
||||
};
|
||||
use nym_sphinx::params::{GatewayEncryptionAlgorithm, GatewaySharedKeyHkdfAlgorithm};
|
||||
use nym_sphinx::params::{GatewaySharedKeyHkdfAlgorithm, LegacyGatewayEncryptionAlgorithm};
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use nym_task::TaskClient;
|
||||
use rand::{CryptoRng, RngCore};
|
||||
@@ -41,7 +41,7 @@ pub(crate) struct State<'a, S> {
|
||||
ephemeral_keypair: encryption::KeyPair,
|
||||
|
||||
/// The derived shared key using the ephemeral keys of both parties.
|
||||
derived_shared_keys: Option<SharedKeys>,
|
||||
derived_shared_keys: Option<LegacySharedKeys>,
|
||||
|
||||
/// The known or received public identity key of the remote.
|
||||
/// Ideally it would always be known before the handshake was initiated.
|
||||
@@ -128,12 +128,12 @@ impl<'a, S> State<'a, S> {
|
||||
None,
|
||||
&dh_result,
|
||||
None,
|
||||
SharedKeySize::to_usize(),
|
||||
LegacySharedKeySize::to_usize(),
|
||||
)
|
||||
.expect("somehow too long okm was provided");
|
||||
|
||||
let derived_shared_key =
|
||||
SharedKeys::try_from_bytes(&okm).expect("okm was expanded to incorrect length!");
|
||||
LegacySharedKeys::try_from_bytes(&okm).expect("okm was expanded to incorrect length!");
|
||||
|
||||
self.derived_shared_keys = Some(derived_shared_key)
|
||||
}
|
||||
@@ -153,8 +153,8 @@ impl<'a, S> State<'a, S> {
|
||||
.collect();
|
||||
|
||||
let signature = self.identity.private_key().sign(message);
|
||||
let zero_iv = stream_cipher::zero_iv::<GatewayEncryptionAlgorithm>();
|
||||
stream_cipher::encrypt::<GatewayEncryptionAlgorithm>(
|
||||
let zero_iv = stream_cipher::zero_iv::<LegacyGatewayEncryptionAlgorithm>();
|
||||
stream_cipher::encrypt::<LegacyGatewayEncryptionAlgorithm>(
|
||||
self.derived_shared_keys.as_ref().unwrap().encryption_key(),
|
||||
&zero_iv,
|
||||
&signature.to_bytes(),
|
||||
@@ -178,8 +178,8 @@ impl<'a, S> State<'a, S> {
|
||||
.expect("shared key was not derived!");
|
||||
|
||||
// first decrypt received data
|
||||
let zero_iv = stream_cipher::zero_iv::<GatewayEncryptionAlgorithm>();
|
||||
let decrypted_signature = stream_cipher::decrypt::<GatewayEncryptionAlgorithm>(
|
||||
let zero_iv = stream_cipher::zero_iv::<LegacyGatewayEncryptionAlgorithm>();
|
||||
let decrypted_signature = stream_cipher::decrypt::<LegacyGatewayEncryptionAlgorithm>(
|
||||
derived_shared_key.encryption_key(),
|
||||
&zero_iv,
|
||||
remote_material,
|
||||
@@ -320,7 +320,7 @@ impl<'a, S> State<'a, S> {
|
||||
|
||||
/// Finish the handshake, yielding the derived shared key and implicitly dropping all borrowed
|
||||
/// values.
|
||||
pub(crate) fn finalize_handshake(self) -> SharedKeys {
|
||||
pub(crate) fn finalize_handshake(self) -> LegacySharedKeys {
|
||||
self.derived_shared_keys.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
use crate::authentication::encrypted_address::EncryptedAddressBytes;
|
||||
use crate::iv::{IVConversionError, IV};
|
||||
use crate::models::CredentialSpendingRequest;
|
||||
use crate::registration::handshake::SharedKeys;
|
||||
use crate::registration::handshake::LegacySharedKeys;
|
||||
use crate::{GatewayMacSize, CURRENT_PROTOCOL_VERSION, INITIAL_PROTOCOL_VERSION};
|
||||
use nym_credentials::ecash::bandwidth::CredentialSpendingData;
|
||||
use nym_credentials_interface::CompactEcashError;
|
||||
@@ -14,11 +14,12 @@ use nym_crypto::symmetric::stream_cipher;
|
||||
use nym_sphinx::addressing::nodes::NymNodeRoutingAddressError;
|
||||
use nym_sphinx::forwarding::packet::{MixPacket, MixPacketFormattingError};
|
||||
use nym_sphinx::params::packet_sizes::PacketSize;
|
||||
use nym_sphinx::params::{GatewayEncryptionAlgorithm, GatewayIntegrityHmacAlgorithm};
|
||||
use nym_sphinx::params::{GatewayIntegrityHmacAlgorithm, LegacyGatewayEncryptionAlgorithm};
|
||||
use nym_sphinx::DestinationAddressBytes;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tracing::log::error;
|
||||
|
||||
use nym_crypto::symmetric::aead::AeadError;
|
||||
use std::str::FromStr;
|
||||
use std::string::FromUtf8Error;
|
||||
use thiserror::Error;
|
||||
@@ -154,6 +155,9 @@ pub enum GatewayRequestsError {
|
||||
#[error("the provided [v1] credential has invalid number of parameters - {0}")]
|
||||
InvalidNumberOfEmbededParameters(u32),
|
||||
|
||||
#[error("failed to either encrypt or decrypt provided message")]
|
||||
AeadFailure(#[from] AeadError),
|
||||
|
||||
// variant to catch legacy errors
|
||||
#[error("{0}")]
|
||||
Other(String),
|
||||
@@ -236,7 +240,7 @@ impl ClientControlRequest {
|
||||
|
||||
pub fn new_enc_ecash_credential(
|
||||
credential: CredentialSpendingData,
|
||||
shared_key: &SharedKeys,
|
||||
shared_key: &LegacySharedKeys,
|
||||
iv: IV,
|
||||
) -> Self {
|
||||
let cred = CredentialSpendingRequest::new(credential);
|
||||
@@ -251,7 +255,7 @@ impl ClientControlRequest {
|
||||
|
||||
pub fn try_from_enc_ecash_credential(
|
||||
enc_credential: Vec<u8>,
|
||||
shared_key: &SharedKeys,
|
||||
shared_key: &LegacySharedKeys,
|
||||
iv: Vec<u8>,
|
||||
) -> Result<CredentialSpendingRequest, GatewayRequestsError> {
|
||||
let iv = IV::try_from_bytes(&iv)?;
|
||||
@@ -388,7 +392,7 @@ pub enum BinaryRequest {
|
||||
impl BinaryRequest {
|
||||
pub fn try_from_encrypted_tagged_bytes(
|
||||
raw_req: Vec<u8>,
|
||||
shared_keys: &SharedKeys,
|
||||
shared_keys: &LegacySharedKeys,
|
||||
) -> Result<Self, GatewayRequestsError> {
|
||||
let message_bytes = &shared_keys.decrypt_tagged(&raw_req, None)?;
|
||||
|
||||
@@ -398,7 +402,7 @@ impl BinaryRequest {
|
||||
Ok(BinaryRequest::ForwardSphinx(mix_packet))
|
||||
}
|
||||
|
||||
pub fn into_encrypted_tagged_bytes(self, shared_key: &SharedKeys) -> Vec<u8> {
|
||||
pub fn into_encrypted_tagged_bytes(self, shared_key: &LegacySharedKeys) -> Vec<u8> {
|
||||
match self {
|
||||
BinaryRequest::ForwardSphinx(mix_packet) => {
|
||||
let forwarding_data = match mix_packet.into_bytes() {
|
||||
@@ -421,7 +425,7 @@ impl BinaryRequest {
|
||||
BinaryRequest::ForwardSphinx(mix_packet)
|
||||
}
|
||||
|
||||
pub fn into_ws_message(self, shared_key: &SharedKeys) -> Message {
|
||||
pub fn into_ws_message(self, shared_key: &LegacySharedKeys) -> Message {
|
||||
Message::Binary(self.into_encrypted_tagged_bytes(shared_key))
|
||||
}
|
||||
}
|
||||
@@ -434,7 +438,7 @@ pub enum BinaryResponse {
|
||||
impl BinaryResponse {
|
||||
pub fn try_from_encrypted_tagged_bytes(
|
||||
raw_req: Vec<u8>,
|
||||
shared_keys: &SharedKeys,
|
||||
shared_keys: &LegacySharedKeys,
|
||||
) -> Result<Self, GatewayRequestsError> {
|
||||
let mac_size = GatewayMacSize::to_usize();
|
||||
if raw_req.len() < mac_size {
|
||||
@@ -452,8 +456,8 @@ impl BinaryResponse {
|
||||
return Err(GatewayRequestsError::InvalidMac);
|
||||
}
|
||||
|
||||
let zero_iv = stream_cipher::zero_iv::<GatewayEncryptionAlgorithm>();
|
||||
let plaintext = stream_cipher::decrypt::<GatewayEncryptionAlgorithm>(
|
||||
let zero_iv = stream_cipher::zero_iv::<LegacyGatewayEncryptionAlgorithm>();
|
||||
let plaintext = stream_cipher::decrypt::<LegacyGatewayEncryptionAlgorithm>(
|
||||
shared_keys.encryption_key(),
|
||||
&zero_iv,
|
||||
message_bytes,
|
||||
@@ -462,7 +466,7 @@ impl BinaryResponse {
|
||||
Ok(BinaryResponse::PushedMixMessage(plaintext))
|
||||
}
|
||||
|
||||
pub fn into_encrypted_tagged_bytes(self, shared_key: &SharedKeys) -> Vec<u8> {
|
||||
pub fn into_encrypted_tagged_bytes(self, shared_key: &LegacySharedKeys) -> Vec<u8> {
|
||||
match self {
|
||||
// TODO: it could be theoretically slightly more efficient if the data wasn't taken
|
||||
// by reference because then it makes a copy for encryption rather than do it in place
|
||||
@@ -474,7 +478,7 @@ impl BinaryResponse {
|
||||
BinaryResponse::PushedMixMessage(msg)
|
||||
}
|
||||
|
||||
pub fn into_ws_message(self, shared_key: &SharedKeys) -> Message {
|
||||
pub fn into_ws_message(self, shared_key: &LegacySharedKeys) -> Message {
|
||||
Message::Binary(self.into_encrypted_tagged_bytes(shared_key))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ use models::{
|
||||
VerifiedTicket, WireguardPeer,
|
||||
};
|
||||
use nym_credentials_interface::ClientTicket;
|
||||
use nym_gateway_requests::registration::handshake::SharedKeys;
|
||||
use nym_gateway_requests::registration::handshake::LegacySharedKeys;
|
||||
use nym_sphinx::DestinationAddressBytes;
|
||||
use shared_keys::SharedKeysManager;
|
||||
use sqlx::ConnectOptions;
|
||||
@@ -46,7 +46,7 @@ pub trait Storage: Send + Sync {
|
||||
async fn insert_shared_keys(
|
||||
&self,
|
||||
client_address: DestinationAddressBytes,
|
||||
shared_keys: &SharedKeys,
|
||||
shared_keys: &LegacySharedKeys,
|
||||
) -> Result<i64, StorageError>;
|
||||
|
||||
/// Tries to retrieve shared keys stored for the particular client.
|
||||
@@ -330,7 +330,7 @@ impl Storage for PersistentStorage {
|
||||
async fn insert_shared_keys(
|
||||
&self,
|
||||
client_address: DestinationAddressBytes,
|
||||
shared_keys: &SharedKeys,
|
||||
shared_keys: &LegacySharedKeys,
|
||||
) -> Result<i64, StorageError> {
|
||||
let client_id = self
|
||||
.client_manager
|
||||
|
||||
@@ -14,7 +14,7 @@ generic-array = { workspace = true, optional = true, features = ["serde"] }
|
||||
thiserror = { workspace = true }
|
||||
zeroize = { workspace = true }
|
||||
|
||||
nym-crypto = { path = "../../crypto", features = ["symmetric", "rand"] }
|
||||
nym-crypto = { path = "../../crypto", features = ["stream_cipher", "rand"] }
|
||||
nym-pemstore = { path = "../../pemstore" }
|
||||
nym-sphinx-addressing = { path = "../addressing" }
|
||||
nym-sphinx-params = { path = "../params" }
|
||||
|
||||
@@ -13,7 +13,7 @@ bs58 = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
|
||||
nym-crypto = { path = "../../crypto", features = ["symmetric", "rand"] }
|
||||
nym-crypto = { path = "../../crypto", features = ["stream_cipher", "rand"] }
|
||||
nym-sphinx-addressing = { path = "../addressing" }
|
||||
nym-sphinx-params = { path = "../params" }
|
||||
nym-sphinx-routing = { path = "../routing" }
|
||||
|
||||
@@ -11,7 +11,7 @@ repository = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
|
||||
nym-crypto = { path = "../../crypto", features = ["hashing", "symmetric"] }
|
||||
nym-crypto = { path = "../../crypto", features = ["hashing", "stream_cipher", "aes-gcm-siv"] }
|
||||
nym-sphinx-types = { path = "../types" }
|
||||
|
||||
[features]
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
use nym_crypto::aes::Aes128;
|
||||
use nym_crypto::blake3;
|
||||
use nym_crypto::ctr;
|
||||
use nym_crypto::Aes256GcmSiv;
|
||||
|
||||
type Aes128Ctr = ctr::Ctr64BE<Aes128>;
|
||||
|
||||
@@ -48,7 +49,7 @@ pub type GatewaySharedKeyHkdfAlgorithm = blake3::Hasher;
|
||||
pub type ReplySurbKeyDigestAlgorithm = blake3::Hasher;
|
||||
|
||||
/// Hashing algorithm used when computing integrity (H)Mac for message exchanged between client and gateway.
|
||||
// TODO: if updated, the pem type defined in gateway\gateway-requests\src\registration\handshake\shared_key
|
||||
// TODO: if updated, the pem type defined in gateway\gateway-requests\src\registration\handshake\legacy_shared_key
|
||||
// needs updating!
|
||||
pub type GatewayIntegrityHmacAlgorithm = blake3::Hasher;
|
||||
|
||||
@@ -59,11 +60,16 @@ pub type GatewayIntegrityHmacAlgorithm = blake3::Hasher;
|
||||
// - the pem type defined in nym\common\nymsphinx\acknowledgements\src\key needs updating!
|
||||
pub type AckEncryptionAlgorithm = Aes128Ctr;
|
||||
|
||||
/// Legacy encryption algorithm used for end-to-end encryption of messages exchanged between clients
|
||||
/// and their gateways.
|
||||
// TODO: if updated, the pem type defined in gateway\gateway-requests\src\registration\handshake\legacy_shared_key
|
||||
// needs updating!
|
||||
pub type LegacyGatewayEncryptionAlgorithm = Aes128Ctr;
|
||||
|
||||
/// Encryption algorithm used for end-to-end encryption of messages exchanged between clients
|
||||
/// and their gateways.
|
||||
// TODO: if updated, the pem type defined in gateway\gateway-requests\src\registration\handshake\shared_key
|
||||
// needs updating!
|
||||
pub type GatewayEncryptionAlgorithm = Aes128Ctr;
|
||||
// NOTE: if updated, the pem type defined in gateway\gateway-requests\src\registration\handshake\shared_key
|
||||
pub type GatewayEncryptionAlgorithm = Aes256GcmSiv;
|
||||
|
||||
/// Encryption algorithm used for end-to-end encryption of messages exchanged between clients that are
|
||||
/// encapsulated inside sphinx packets.
|
||||
|
||||
@@ -21,10 +21,11 @@ use nym_crypto::asymmetric::identity;
|
||||
use nym_gateway_requests::authentication::encrypted_address::{
|
||||
EncryptedAddressBytes, EncryptedAddressConversionError,
|
||||
};
|
||||
use nym_gateway_requests::registration::handshake::shared_key::SharedKeyConversionError;
|
||||
use nym_gateway_requests::{
|
||||
iv::{IVConversionError, IV},
|
||||
registration::handshake::{error::HandshakeError, gateway_handshake, SharedKeys},
|
||||
registration::handshake::{
|
||||
error::HandshakeError, gateway_handshake, LegacySharedKeys, SharedKeyConversionError,
|
||||
},
|
||||
types::{ClientControlRequest, ServerResponse},
|
||||
BinaryResponse, CURRENT_PROTOCOL_VERSION, INITIAL_PROTOCOL_VERSION,
|
||||
};
|
||||
@@ -177,7 +178,7 @@ where
|
||||
async fn perform_registration_handshake(
|
||||
&mut self,
|
||||
init_msg: Vec<u8>,
|
||||
) -> Result<SharedKeys, HandshakeError>
|
||||
) -> Result<LegacySharedKeys, HandshakeError>
|
||||
where
|
||||
S: AsyncRead + AsyncWrite + Unpin + Send,
|
||||
{
|
||||
@@ -258,7 +259,7 @@ where
|
||||
/// * `packets`: unwrapped packets that are to be pushed back to the client.
|
||||
pub(crate) async fn push_packets_to_client(
|
||||
&mut self,
|
||||
shared_keys: &SharedKeys,
|
||||
shared_keys: &LegacySharedKeys,
|
||||
packets: Vec<Vec<u8>>,
|
||||
) -> Result<(), WsError>
|
||||
where
|
||||
@@ -313,7 +314,7 @@ where
|
||||
async fn push_stored_messages_to_client(
|
||||
&mut self,
|
||||
client_address: DestinationAddressBytes,
|
||||
shared_keys: &SharedKeys,
|
||||
shared_keys: &LegacySharedKeys,
|
||||
) -> Result<(), InitialAuthenticationError>
|
||||
where
|
||||
S: AsyncRead + AsyncWrite + Unpin,
|
||||
@@ -367,7 +368,7 @@ where
|
||||
client_address: DestinationAddressBytes,
|
||||
encrypted_address: EncryptedAddressBytes,
|
||||
iv: IV,
|
||||
) -> Result<Option<SharedKeys>, InitialAuthenticationError> {
|
||||
) -> Result<Option<LegacySharedKeys>, InitialAuthenticationError> {
|
||||
let shared_keys = self
|
||||
.shared_state
|
||||
.storage
|
||||
@@ -378,7 +379,7 @@ where
|
||||
// this should never fail as we only ever construct persisted shared keys ourselves when inserting
|
||||
// data to the storage. The only way it could fail is if we somehow changed implementation without
|
||||
// performing proper migration
|
||||
let keys = SharedKeys::try_from_base58_string(
|
||||
let keys = LegacySharedKeys::try_from_base58_string(
|
||||
shared_keys.derived_aes128_ctr_blake3_hmac_keys_bs58,
|
||||
)
|
||||
.map_err(|source| {
|
||||
@@ -451,7 +452,7 @@ where
|
||||
client_address: DestinationAddressBytes,
|
||||
encrypted_address: EncryptedAddressBytes,
|
||||
iv: IV,
|
||||
) -> Result<Option<SharedKeys>, InitialAuthenticationError>
|
||||
) -> Result<Option<LegacySharedKeys>, InitialAuthenticationError>
|
||||
where
|
||||
S: AsyncRead + AsyncWrite + Unpin,
|
||||
{
|
||||
@@ -616,7 +617,7 @@ where
|
||||
async fn register_client(
|
||||
&mut self,
|
||||
client_address: DestinationAddressBytes,
|
||||
client_shared_keys: &SharedKeys,
|
||||
client_shared_keys: &LegacySharedKeys,
|
||||
) -> Result<i64, InitialAuthenticationError>
|
||||
where
|
||||
S: AsyncRead + AsyncWrite + Unpin,
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
use crate::config::Config;
|
||||
use nym_credential_verification::BandwidthFlushingBehaviourConfig;
|
||||
use nym_gateway_requests::registration::handshake::SharedKeys;
|
||||
use nym_gateway_requests::registration::handshake::LegacySharedKeys;
|
||||
use nym_gateway_requests::ServerResponse;
|
||||
use nym_gateway_storage::Storage;
|
||||
use nym_sphinx::DestinationAddressBytes;
|
||||
@@ -46,11 +46,15 @@ pub(crate) struct ClientDetails {
|
||||
#[zeroize(skip)]
|
||||
pub(crate) address: DestinationAddressBytes,
|
||||
pub(crate) id: i64,
|
||||
pub(crate) shared_keys: SharedKeys,
|
||||
pub(crate) shared_keys: LegacySharedKeys,
|
||||
}
|
||||
|
||||
impl ClientDetails {
|
||||
pub(crate) fn new(id: i64, address: DestinationAddressBytes, shared_keys: SharedKeys) -> Self {
|
||||
pub(crate) fn new(
|
||||
id: i64,
|
||||
address: DestinationAddressBytes,
|
||||
shared_keys: LegacySharedKeys,
|
||||
) -> Self {
|
||||
ClientDetails {
|
||||
address,
|
||||
id,
|
||||
|
||||
Reference in New Issue
Block a user