-
Notifications
You must be signed in to change notification settings - Fork 2
/
layout.rs
427 lines (368 loc) · 15.2 KB
/
layout.rs
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
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
// Copyright 2016 Jeffrey Burdges.
//! Sphinx header layout routines
//!
//! ...
use std::borrow::{Borrow,BorrowMut};
use std::marker::PhantomData;
use keys::{RoutingName,RoutingNameBytes,ROUTING_NAME_LENGTH};
use keys::time::{ValidityPeriod};
use curve::{AlphaBytes,ALPHA_LENGTH};
use super::stream::{Gamma,GammaBytes,GAMMA_LENGTH,HeaderCipher};
use super::commands::{Command,CommandGamma,CommandData,CommandNode,MAX_SURB_BETA_LENGTH};
use super::error::*;
use super::slice::*;
use super::*; // {PacketName,PACKET_NAME_LENGTH};
/// We use `usize` for indexing, like all Rust programs, but we may
/// specify a smaller type for user specified indexes.
pub type Length = usize;
/// Sphinx packet format paramaters
///
/// We handle these paramaters only at the type level, only
/// instantiating `PhantomData<P>` where `P: Params`.
///
/// We require they be `Clone` only because `#[derive(Clone)]` uses
/// [incorrect bounds](https://github.com/rust-lang/rust/issues/26925).
/// We could fix this by adding manual clone instances for every
/// type with a type paramater `P: Params`, but adding the supertrait
/// seems easier for now.
///
/// In some cases, there could be minor performance hits if some
/// of these are not multiples of the ChaCha blocksize of 64 byte.
pub trait Params: Sized+Clone+Copy {
/// Unique numeric identifier for the protocol
const PROTOCOL_ID: surbs::ProtocolId;
/// Unique string identifier for the protocol
const PROTOCOL_NAME: &'static str;
/// Length of the routing information block `Beta`.
const BETA_LENGTH: Length;
/// Maximal amount of routing infomrmation in `Beta` consumed
/// by a single sub-hop.
const MAX_BETA_TAIL_LENGTH: Length;
/// Maximum length of the SURB. At most half of `BETA_LENGTH - 48`.
///
/// Alpha and Gamma are encoded into the "bottom" of beta, and
/// hence do not contribute here. This is unlikely to change.
/// As a result this should not exceed `BETA_LENGTH`
const MAX_SURB_BETA_LENGTH: Length;
/// Length of the SURB log.
const SURB_LOG_LENGTH: Length;
/// Approved message body lengths
const SURB_BETA_LENGTHS: &'static [Length];
/// Approved message body lengths
const BODY_LENGTHS: &'static [Length];
/// Rate paramater lambda for the exponential distribution of
/// from which we sample the senders' sugested delays in
/// `Stream::delay`.
const DELAY_LAMBDA: f64;
/// Sphinx header length
#[inline(always)]
fn header_length() -> usize {
ALPHA_LENGTH + GAMMA_LENGTH
+ Self::BETA_LENGTH as usize
+ Self::SURB_LOG_LENGTH as usize
}
fn max_hops_capacity() -> usize {
// Rust bug: https://github.com/rust-lang/rust/issues/26264
// Write CommandNode::Transmit here when fixed.
let c = Command::Transmit::<Gamma,usize> {
route: RoutingName([0u8; ROUTING_NAME_LENGTH]),
gamma: Gamma([0u8; GAMMA_LENGTH]),
};
Self::BETA_LENGTH / c.command_length()
}
}
/*
pub struct ParamsEtc<P: Params>(PhantomData<P>);
impl<P> Params for ParamsEtc<P> where P: Params, PhantomData<P>: 'static {
const PROTOCOL_NAME: &'static str
= P::PROTOCOL_NAME;
const BETA_LENGTH: Length
= P::BETA_LENGTH;
const MAX_BETA_TAIL_LENGTH: Length
= P::MAX_BETA_TAIL_LENGTH;
const MAX_SURB_BETA_LENGTH: Length
= P::MAX_SURB_BETA_LENGTH;
const SURB_LOG_LENGTH: Length
= P::SURB_LOG_LENGTH;
const BODY_LENGTHS: &'static [Length]
= P::BODY_LENGTHS;
const DELAY_LAMBDA: f64
= P::DELAY_LAMBDA;
}
*/
/// Just a helper trait to provide inherent methods on types
/// satisfying `Params`.
pub trait ImplParams: Params {
fn boxed_zeroed_header() -> Box<[u8]>;
fn boxed_zeroed_body(i: usize) -> Box<[u8]>;
fn check_body_length(body_length: usize) -> SphinxResult<()>;
}
impl<P: Params> ImplParams for P {
/// Create a `Box<[u8]>` with the required header length
/// and containing zeros.
fn boxed_zeroed_header() -> Box<[u8]> {
vec![0u8; P::header_length()].into_boxed_slice()
}
/// Create a `Box<[u8]>` with the requested body length
/// from `SphinxParams::BODY_LENGTHS` and containing zeros.
fn boxed_zeroed_body(i: usize) -> Box<[u8]> {
vec![0u8; P::BODY_LENGTHS[i]].into_boxed_slice()
}
/// Returns an error if the body length is not approved by the paramaters.
fn check_body_length(body_length: usize) -> SphinxResult<()> {
// Just for debugging convenience we check all lengths
// instead of only the one we need.
for l in P::BODY_LENGTHS {
body::BodyCipher::<P>::compatable_length(*l) ?;
}
if P::BODY_LENGTHS.len() == 0 {
if body_length == 0 {
Ok(()) // All body lengths are zero if no body lengths were specified
} else {
Err( SphinxError::BadLength("Nonempty body with no body lengths specified", body_length) )
}
} else if P::BODY_LENGTHS.contains(&body_length) {
Ok(())
} else {
Err( SphinxError::BadLength("Unapproaved body length",body_length) )
}
}
}
/*
use std::ops::{Deref,DerefMut};
struct HideMut<'a,T>(&'a mut T) where T: ?Sized + 'a;
impl<'a,T> HideMut<'a,T> where T: ?Sized {
pub fn new(m: &'a mut T) -> HideMut<'a,T> { HideMut(m) }
}
impl<'a,T> Deref for HideMut<'a,T> where T: ?Sized {
type Target = T;
fn deref(&self) -> &T { self.0 }
}
impl<'a,T> DerefMut for HideMut<'a,T> where T: ?Sized {
fn deref_mut(&mut self) -> &mut T { self.0 }
}
*/
/// A Sphinx header structured by individual components.
///
/// Create by applying `new_sliced` to `&mut [u8]` slice of the
/// correct length, like that created by `boxed_zeroed_header`.
/// We check all lengths in `new_sliced` so that methods and
/// functions using `HeaderMuts` may assume all header lengths to
/// be correct, and even that the slices are contiguious.
///
/// We mostly handle `HeaderMuts` via mutable borrows so that we may
/// change the referred to values without interrior mutability.
/// We thus do not make the slice references themselves non-public
/// because accessor methods would borrow the whole struct.
/// As a result, any caller could invalidate our requirement that
/// slices be contiguous. If desired, this could be prevented using
/// the `HideMut` struct above. See http://stackoverflow.com/a/42376165/667457
pub struct HeaderMuts<'a,P> where P: Params {
params: PhantomData<P>,
pub route: &'a mut RoutingNameBytes,
pub alpha: &'a mut AlphaBytes,
pub gamma: &'a mut GammaBytes,
pub beta: &'a mut [u8],
pub surb_log: &'a mut [u8],
}
impl<'a,P> HeaderMuts<'a,P> where P: Params {
/*
pub fn alpha(&'a self) -> &'a AlphaBytes { self.alpha }
pub fn gamma(&'a self) -> &'a GammaBytes { self.gamma }
pub fn beta(&'a self) -> &'a [u8] { self.beta }
pub fn surb_log(&'a self) -> &'a [u8] { self.surb_log }
pub fn alpha_mut(&'a mut self) -> &'a mut AlphaBytes { self.alpha }
pub fn gamma_mut(&'a mut self) -> &'a mut GammaBytes { self.gamma }
pub fn beta_mut(&'a mut self) -> &'a mut [u8] { self.beta }
pub fn surb_log_mut(&'a mut self) -> &'a mut [u8] { self.surb_log }
*/
/// Borrow a mutable slice `&mut [u8]` as a `HeaderMuts` consisting.
/// of subspices for the various header components. You may mutate
/// these freely so that after the borrow ends the original slice
/// contains the new header.
///
pub fn new_sliced<'s>(mut header: &'s mut [u8]) -> SphinxResult<HeaderMuts<'s,P>>
{
// Prevent configurations that support long SURB attacks.
if 2*P::MAX_SURB_BETA_LENGTH > P::BETA_LENGTH - ALPHA_LENGTH + GAMMA_LENGTH {
return Err( SphinxError::BadLength("Maximum SURB is so long that it degrades sender security",
P::MAX_SURB_BETA_LENGTH) );
}
if P::MAX_SURB_BETA_LENGTH > MAX_SURB_BETA_LENGTH as Length {
return Err( SphinxError::BadLength("Maximum SURB length exceeds encoding",
P::MAX_SURB_BETA_LENGTH) );
}
let orig_len = header.len();
if orig_len < P::header_length() {
return Err( SphinxError::BadLength("Header is too short",orig_len) );
}
let hr = HeaderMuts {
params: PhantomData,
route: reserve_fixed_mut!(&mut header,ROUTING_NAME_LENGTH),
alpha: reserve_fixed_mut!(&mut header,ALPHA_LENGTH),
gamma: reserve_fixed_mut!(&mut header,GAMMA_LENGTH),
beta: reserve_mut(&mut header,P::BETA_LENGTH as usize),
surb_log: reserve_mut(&mut header,P::SURB_LOG_LENGTH as usize),
};
if header.len() > 0 {
return Err( SphinxError::BadLength("Header is too long",orig_len) );
}
Ok(hr)
}
/// Verify the poly1305 MAC `Gamma` given in a Sphinx packet by
/// calling `HeaderCipher::verify_gamma` with the provided fields.
pub fn verify_gamma(&self, hop: &HeaderCipher<P>) -> SphinxResult<()> {
hop.verify_gamma(self.beta, &Gamma(*self.gamma))
}
/// Compute gamma from Beta and the SURB. Probably not useful.
pub fn create_gamma(&self, hop: &HeaderCipher<P>) -> SphinxResult<Gamma> {
hop.create_gamma(self.beta) // .map(|x| { x.0 })
}
/// Prepend a `PacketName` to the SURB log.
/// Used in SURB rerouting so that SURB unwinding works.
pub fn prepend_to_surb_log(&mut self, packet_name: &PacketName) {
prepend_slice_of_slices(self.surb_log, &[&packet_name.0]);
// prepend_iterator(self.surb_log, packet_name.0.iter().map(|x| *x));
}
/// Prepend a command to beta for creating beta.
///
/// TODO: Remove as this should never be used.
pub fn prepend_to_beta<G,D>(&mut self, cmd: &Command<G,D>) -> usize
where G: CommandGamma, D: CommandData {
cmd.prepend_bytes(self.beta)
}
/// Decrypt beta, read a command from an initial segment of beta,
/// shift beta forward by the command's length, and pad the tail
/// of beta.
pub fn peal_beta(&mut self, hop: &mut HeaderCipher<P>) -> SphinxResult<CommandNode> {
hop.xor_beta(self.beta,0,0) ?; // InternalError
let (command, eaten) = Command::parse(self.beta) ?; // BadPacket: Unknown Command
if eaten > P::MAX_BETA_TAIL_LENGTH as usize {
return Err( SphinxError::InternalError("Ate too much Beta!") );
}
// We could reduce our calls to ChaCha by partially processing
// commands here, like zeroing beta's during cross over, or
// ignoring beta entirely for delivery commands.
let length = self.beta.len();
debug_assert_eq!(length, P::BETA_LENGTH as usize);
// let beta = &mut refs.beta[..length];
for i in eaten..length { self.beta[i-eaten] = self.beta[i]; }
hop.set_beta_tail(&mut self.beta[length-eaten..length]) ?; // InternalError
Ok(command)
}
}
// TODO: Consider using owning_refs crate to provide
// pub fn new_sliced_header(&self) -> SphinxResult<OwningHandle<Box<[u8]>,HeaderMuts>> { }
// ref. https://kimundi.github.io/owning-ref-rs/owning_ref/struct.OwningHandle.html
/*
pub struct HeaderIter<'a,P: Params> {
offset: usize,
header_refs: HeaderMuts<'a>,
}
impl<'a,P: Params> Iterator for HeaderIter<'a,P> {
type Item=u8;
fn next(&mut self) -> Option<u8> {
let i = self.offset;
self.offset += 1;
if i < ALPHA_LENGTH { return Some(self.alpha[i]) }
i -= ALPHA_LENGTH;
if i < GAMMA_LENGTH { return Some(self.gamma[i]) }
i -= GAMMA_LENGTH;
if i < P::BETA_LENGTH as usize { return Some(self.beta[i]) }
i -= P::BETA_LENGTH as usize;
if i < P::SURB_LOG_LENGTH as usize { return Some(self.surb_log[i]) }
i -= P::SURB_LOG_LENGTH as usize;
if i < P::surb_length as usize { return Some(self.surb[i]) }
i -= P::surb_length as usize;
self.offset -= 1; None
}
fn size_hint(&self) -> (usize, Option<usize>) {
let l = P::header_length();
(l, Some(l))
}
}
impl<'a, P: Params> Iterator ExactSizeIterator for HeaderIter<'a> {
fn len(&self) -> usize { P::header_length() }
// fn is_empty(&self) -> bool { false }
}
impl<'a, P: Params> IntoIterator for HeaderMuts<'a,P> {
type Item=u8;
type IntoIter = HeaderIter<'a>;
fn into_iter(self) -> HeaderIter<'a> {
HeaderIter { offset: 0, header_refs: self }
}
}
*/
/// Unsent full or partial Sphinx headers, including SURBs.
///
/// TODO: Avoid one needless allocation by using DSTs. We could
/// eliminate the extra allocation by using `Box::<T>::into_raw()`
/// and `Box::<U>::from_raw` along with a cast `*T as *U` and the
/// endianness conversion, except that our `T` and `U` are `[u8]`
/// and `PreHeader` with `Box<[u8]>` replaced by `[u8]`, which are
/// unsized, so this being careful.
pub struct PreHeader {
pub validity: ValidityPeriod,
pub route: RoutingName,
pub alpha: AlphaBytes,
pub gamma: Gamma,
pub beta: Box<[u8]>,
// pub keys: ...,
}
impl PreHeader {
/// Encode a SURB for storage or transmission
pub fn encode_surb(self) -> Box<[u8]> {
let mut v = Vec::with_capacity(
16 + ROUTING_NAME_LENGTH + ALPHA_LENGTH + GAMMA_LENGTH + self.beta.len()
);
v.extend_from_slice( & self.validity.to_bytes() );
v.extend_from_slice( &self.route.0 );
v.extend_from_slice( &self.alpha );
v.extend_from_slice( &self.gamma.0 );
v.extend_from_slice( self.beta.borrow() );
v.into_boxed_slice()
}
/// Encode a SURB from storage or transmission.
pub fn decode_surb(mut surb: &[u8]) -> PreHeader {
PreHeader {
route: RoutingName(*reserve_fixed!(&mut surb, ROUTING_NAME_LENGTH)),
validity: ValidityPeriod::from_bytes(reserve_fixed!(&mut surb, 16)),
alpha: *reserve_fixed!(&mut surb, ALPHA_LENGTH),
gamma: Gamma(*reserve_fixed!(&mut surb, GAMMA_LENGTH)),
beta: surb.to_owned().into_boxed_slice(),
}
}
}
use rand::Rng;
pub fn encode_header<P: Params,R: Rng>(rng: &mut Rng, preheader: PreHeader)
-> SphinxResult<Box<[u8]>> {
if preheader.beta.len() != P::BETA_LENGTH as usize {
return Err( SphinxError::InternalError("Used SURB as sending header!") );
}
let mut h = P::boxed_zeroed_header();
{
let refs = HeaderMuts::<P>::new_sliced(h.borrow_mut()).unwrap();
*refs.route = preheader.route.0;
*refs.alpha = preheader.alpha;
*refs.gamma = preheader.gamma.0;
refs.beta.copy_from_slice(preheader.beta.borrow());
rng.fill_bytes(refs.surb_log);
// argument: seed: &[u8; 32]
// use chacha::ChaCha as ChaCha20;
// use keystream::{KeyStream};
// let mut chacha = ChaCha20::new_chacha20(seed, &[0u8; 8]);
// self.stream.xor_read(refs.surb_log).unwrap();
}
Ok(h)
}
/// Reads a `PacketName` from the SURB log and trims the SURB log
/// to removing it. Used in SURB unwinding.
///
/// We avoid making this a method to `HeaderMuts` because it trims
/// the SURB log by shortening the slice, violating the inveriant
/// assumed by `HeaderRef`.
///
/// TODO: Is it used??
pub fn read_n_trim_surb_log(surb_log: &mut &[u8]) -> PacketName {
PacketName(*reserve_fixed!(surb_log,PACKET_NAME_LENGTH))
}