1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
// Copyright 2022 Parity Technologies (UK) Ltd.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

//! Key exchange, secret derivation, MAC computation, and encryption.

use super::{
	delay::{DelaySeed, DELAY_SEED_SIZE},
	packet::{Actions, KxPublic, Mac, Payload, MAX_HOPS},
};
use arrayref::array_refs;
use arrayvec::ArrayVec;
use blake2::{
	digest::{
		consts::{U16, U32, U64},
		generic_array::{sequence::Concat, GenericArray},
		FixedOutput, Mac as DigestMac,
	},
	Blake2bMac,
};
use c2_chacha::{
	stream_cipher::{NewStreamCipher, SyncStreamCipher},
	ChaCha20,
};
use curve25519_dalek::{scalar::clamp_integer, MontgomeryPoint, Scalar};
use lioness::LionessDefault;
use rand::{CryptoRng, Rng};

const KX_BLINDING_FACTOR_PERSONAL: &[u8; 16] = b"sphinx-blind-fac";
const SMALL_DERIVED_SECRETS_PERSONAL: &[u8; 16] = b"sphinx-small-d-s";
const PAYLOAD_ENCRYPTION_KEY_PERSONAL: &[u8; 16] = b"sphinx-pl-en-key";

/// Size in bytes of a [`SharedSecret`].
pub const SHARED_SECRET_SIZE: usize = 32;
/// Either produced by key exchange or shared in a SURB.
pub type SharedSecret = [u8; SHARED_SECRET_SIZE];

////////////////////////////////////////////////////////////////////////////////
// Key exchange
////////////////////////////////////////////////////////////////////////////////

/// An _unclamped_ key-exchange secret key.
pub type KxSecret = [u8; 32];

/// Generate an _unclamped_ key-exchange secret key.
pub fn gen_kx_secret(rng: &mut (impl Rng + CryptoRng)) -> KxSecret {
	let mut secret = [0; 32];
	rng.fill_bytes(&mut secret);
	secret
}

/// Derive the public key corresponding to a secret key.
pub fn derive_kx_public(kx_secret: &KxSecret) -> KxPublic {
	MontgomeryPoint::mul_base_clamped(*kx_secret).to_bytes()
}

/// Returns the _unclamped_ blinding factor.
fn derive_kx_blinding_factor(kx_public: &KxPublic, kx_shared_secret: &SharedSecret) -> [u8; 32] {
	let kx_public: &GenericArray<_, _> = kx_public.into();
	let key = kx_public.concat((*kx_shared_secret).into());
	let h = Blake2bMac::<U32>::new_with_salt_and_personal(&key, b"", KX_BLINDING_FACTOR_PERSONAL)
		.expect("Key, salt, and personalisation sizes are fixed and small enough");
	h.finalize().into_bytes().into()
}

/// Apply the blinding factor to `kx_public`.
pub fn blind_kx_public(kx_public: &KxPublic, kx_shared_secret: &SharedSecret) -> KxPublic {
	MontgomeryPoint(*kx_public)
		.mul_clamped(derive_kx_blinding_factor(kx_public, kx_shared_secret))
		.to_bytes()
}

pub fn derive_kx_shared_secret(kx_public: &KxPublic, kx_secret: &KxSecret) -> SharedSecret {
	MontgomeryPoint(*kx_public).mul_clamped(*kx_secret).to_bytes()
}

/// Generate a public key to go in a packet and the corresponding shared secrets for each hop.
pub fn gen_kx_public_and_shared_secrets(
	kx_public: &mut KxPublic,
	kx_shared_secrets: &mut ArrayVec<SharedSecret, MAX_HOPS>,
	rng: &mut (impl Rng + CryptoRng),
	their_kx_publics: &[KxPublic],
) {
	let kx_secret = gen_kx_secret(rng);
	*kx_public = derive_kx_public(&kx_secret);

	let mut kx_secret = Scalar::from_bytes_mod_order(clamp_integer(kx_secret));
	let mut kx_public = *kx_public;
	for (i, their_kx_public) in their_kx_publics.iter().enumerate() {
		if i != 0 {
			if i != 1 {
				// An alternative would be to use blind_kx_public, but this is much cheaper
				kx_public = MontgomeryPoint::mul_base(&kx_secret).to_bytes();
			}
			let kx_shared_secret = kx_shared_secrets.last().expect(
				"On at least second iteration of loop, shared secret pushed every iteration",
			);
			kx_secret *= Scalar::from_bytes_mod_order(clamp_integer(derive_kx_blinding_factor(
				&kx_public,
				kx_shared_secret,
			)));
		}
		kx_shared_secrets.push((MontgomeryPoint(*their_kx_public) * kx_secret).to_bytes());
	}
}

////////////////////////////////////////////////////////////////////////////////
// Additional secret derivation
////////////////////////////////////////////////////////////////////////////////

fn derive_secret(derived: &mut [u8], shared_secret: &SharedSecret, personal: &[u8; 16]) {
	for (i, chunk) in derived.chunks_mut(64).enumerate() {
		// This is the construction libsodium uses for crypto_kdf_derive_from_key; see
		// https://doc.libsodium.org/key_derivation/
		let h = Blake2bMac::<U64>::new_with_salt_and_personal(
			shared_secret,
			&i.to_le_bytes(),
			personal,
		)
		.expect("Key, salt, and personalisation sizes are fixed and small enough");
		h.finalize_into(GenericArray::from_mut_slice(chunk));
	}
}

const MAC_KEY_SIZE: usize = 16;
pub type MacKey = [u8; MAC_KEY_SIZE];
const ACTIONS_ENCRYPTION_KEY_SIZE: usize = 32;
pub type ActionsEncryptionKey = [u8; ACTIONS_ENCRYPTION_KEY_SIZE];
const SMALL_DERIVED_SECRETS_SIZE: usize =
	MAC_KEY_SIZE + ACTIONS_ENCRYPTION_KEY_SIZE + DELAY_SEED_SIZE;

pub struct SmallDerivedSecrets([u8; SMALL_DERIVED_SECRETS_SIZE]);

impl SmallDerivedSecrets {
	pub fn new(shared_secret: &SharedSecret) -> Self {
		let mut derived = [0; SMALL_DERIVED_SECRETS_SIZE];
		derive_secret(&mut derived, shared_secret, SMALL_DERIVED_SECRETS_PERSONAL);
		Self(derived)
	}

	fn split(&self) -> (&MacKey, &ActionsEncryptionKey, &DelaySeed) {
		array_refs![&self.0, MAC_KEY_SIZE, ACTIONS_ENCRYPTION_KEY_SIZE, DELAY_SEED_SIZE]
	}

	pub fn mac_key(&self) -> &MacKey {
		self.split().0
	}

	pub fn actions_encryption_key(&self) -> &ActionsEncryptionKey {
		self.split().1
	}

	pub fn delay_seed(&self) -> &DelaySeed {
		self.split().2
	}
}

pub const PAYLOAD_ENCRYPTION_KEY_SIZE: usize = 192;
pub type PayloadEncryptionKey = [u8; PAYLOAD_ENCRYPTION_KEY_SIZE];

pub fn derive_payload_encryption_key(shared_secret: &SharedSecret) -> PayloadEncryptionKey {
	let mut derived = [0; PAYLOAD_ENCRYPTION_KEY_SIZE];
	derive_secret(&mut derived, shared_secret, PAYLOAD_ENCRYPTION_KEY_PERSONAL);
	derived
}

////////////////////////////////////////////////////////////////////////////////
// MAC computation
////////////////////////////////////////////////////////////////////////////////

pub fn compute_mac(actions: &[u8], pad: &[u8], key: &MacKey) -> Mac {
	let mut h = Blake2bMac::<U16>::new_from_slice(key).expect("Key size is fixed and small enough");
	h.update(actions);
	h.update(pad);
	h.finalize().into_bytes().into()
}

pub fn mac_ok(mac: &Mac, actions: &Actions, key: &MacKey) -> bool {
	let mut h = Blake2bMac::<U16>::new_from_slice(key).expect("Key size is fixed and small enough");
	h.update(actions);
	h.verify(mac.into()).is_ok()
}

////////////////////////////////////////////////////////////////////////////////
// Actions encryption
////////////////////////////////////////////////////////////////////////////////

pub fn apply_actions_encryption_keystream(data: &mut [u8], key: &ActionsEncryptionKey) {
	// Key is only used once, so fine for nonce to be 0
	let mut c = ChaCha20::new(key.into(), &[0; 8].into());
	c.apply_keystream(data);
}

pub fn apply_keystream(data: &mut [u8], keystream: &[u8]) {
	for (d, k) in data.iter_mut().zip(keystream) {
		*d ^= *k;
	}
}

////////////////////////////////////////////////////////////////////////////////
// Payload encryption
////////////////////////////////////////////////////////////////////////////////

pub fn encrypt_payload(payload: &mut Payload, key: &PayloadEncryptionKey) {
	let l = LionessDefault::new_raw(key);
	l.encrypt(payload).expect("Payload size is fixed and large enough");
}

pub fn decrypt_payload(payload: &mut Payload, key: &PayloadEncryptionKey) {
	let l = LionessDefault::new_raw(key);
	l.decrypt(payload).expect("Payload size is fixed and large enough");
}