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
// (c) 2024 Ross Younger
// Code in this module: Protocol definitions
//!
//! # 📖 The QCP protocol
//! `qcp` is a **hybrid protocol**.
//! The binary contains the complete protocol implementation,
//! except for the ssh binary used to establish the control channel itself.
//!
//! The protocol flow looks like this:
//!
//! 1. The user runs `qcp` from the machine we will call the _initiator_ or _client_.
//! * qcp uses ssh to connect to the _remote_ machine and start a `qcp --server` process there.
//! * We call this link between the two processes the _control channel_.
//! * The _remote_ machine is also known as the _server_, in keeping with other communication protocols.
//! 1. Both sides generate ephemeral TLS keypairs, either as self-signed TLS certificates or RFC7250 raw public keys.
//! 1. The remote machine binds to a UDP port and sets up a [QUIC] _endpoint_.
//! 1. The two machines exchange messages over the [control] channel containing:
//! * cryptographic identities
//! * server UDP port
//! * bandwidth configuration and any resulting warning
//! 1. The initiator opens up a QUIC connection to the remote.
//! * N.B. While UDP is a connectionless protocol, QUIC provides connection semantics, with multiple bidirectional _streams_ possible on top of a connection between two endpoints.)
//! 1. For each file to be transferred in either direction, the initiator opens a QUIC _stream_ over the existing connection.
//! * We call this a _session_.
//! * The two endpoints use the [session] protocol to move data to where it needs to be.
//! 1. When all is said and done, the initiator closes the control channel. This leads to everything being torn down.
//!
//! ## Motivation
//!
//! This protocol exists because I needed to copy multiple large (3+ GB) files from
//! a server in Europe to my home in New Zealand (ping time is around 300ms).
//!
//! I've got nothing against `ssh` or `scp`. They're brilliant. I've been using them since the 1990s.
//! However they run on top of [TCP], which does not perform very well when the network is congested.
//! With a fast fibre internet connection, a long round-trip time and noticeable packet
//! loss, I was right in the sour spot.
//! TCP did its thing and slowed down, but when the congestion cleared it was very slow to
//! get back up to speed.
//!
//! If you've ever been frustrated by download performance from distant websites,
//! you might have been experiencing this same issue.
//! Friends with satellite (pre-Starlink) internet connections seem to be particularly badly affected.
//!
//! ## 🛡️ Security design
//!
//! The security goals for this project are fairly straightforward:
//!
//! - Only authenticated users can transfer files to/from a system
//! - Data in transit should be kept confidential, with its authenticity and integrity protected; all of this by well-known, reputable cryptographic algorithms
//! - **Security of data at rest at either end is out of scope**, save for the obvious requirement that the copied file be put where the user wanted us to put it
//! - _I do not want to write my own cryptography or user authentication_
//! - _I do not want to rely on PKI if I can help it_
//!
//! [ssh] includes a perfectly serviceable, well understood and battle-tested user authentication system.
//! Sysadmins can set their own policies regarding password, cryptographic or other authentication methods.
//!
//!
//! [QUIC] traffic is protected by [TLS] 1.3. In many cases, a QUIC server would have a TLS certificate
//! signed by a [CA] in the same way as a website.
//!
//! However, I wanted bidirectional endpoint authentication. I also didn't want the hassle of setting
//! up and maintaining certificates at both ends. ([LetsEncrypt] is great for many things,
//! but not so useful in this case; I don't want to run a web server on my home net connection.)
//!
//! After some thought I realised that the solution lay in a hybrid, bootstrapping protocol.
//! * Each endpoint generates a fresh, ephemeral TLS keypair every time.
//! * With ssh connecting the two endpoints, we have an easy way to ensure that TLS
//! credentials genuinely belong to the other end.
//!
//! ### Results
//!
//! The endpoints will only establish a connection:
//!
//! * to one specific TLS instance;
//! * identified by a self-signed certificate that it just received over the control channel, which is assumed secure;
//! * confirmed by use of a private key that only the other endpoint knows (having just generated it).
//!
//! Therefore, data remains secure in transit provided:
//!
//! * the ssh and TLS protocols themselves have not been compromised
//! * your credentials to log in to the remote machine have not been compromised
//! * the random number generators on both endpoints are of sufficient quality
//! * nobody has perpetrated a software supply chain attack on qcp, ssh, or any of the myriad components they depend on
//!
//! ### Note on TLS cipher suite selection
//!
//! QUIC uses TLS 1.3. With TLS 1.3 there is a movement towards automatically selecting cipher suites, taking the cryptographic configuration out of the user's hands.
//!
//! As of qcp 0.6, the rustls component which implements TLS does not have any sophisticated logic here, so qcp takes care of it internally.
//!
//! * We detect whether the CPU has internal support for AES. If not, we prefer ChaCha20 (cipher suite `TLS13_CHACHA20_POLY1305_SHA256`).
//! * Either side may set the `aes256` configuration, which selects the `TLS13_AES_256_GCM_SHA384` suite, overriding the CPU detection.
//! * If neither of those rules apply, we weakly prefer AES128 (cipher suite `TLS13_AES_128_GCM_SHA256`) but will use either of the others
//! if the server prefers it.
//!
//! See [crate::control::crypto::select_cipher_suites] for more details.
//!
//! ## Prior Art
//!
//! * [FASP](https://en.wikipedia.org/wiki/Fast_and_Secure_Protocol) is a high-speed data transfer protocol that runs on UDP.
//! It is proprietary and patented; the patents are held by [Aspera](http://ibm.com/aspera/) which was acquired by IBM.
//! * [QUIC] was invented by a team at Google in 2012, and adopted as a standard by the IETF in 2016.
//! The idea is simple: your data travels over UDP instead of TCP.
//! * Obviously, you lose the benefits of TCP (reliability, packet sequencing, flow control), so you have to reimplement those.
//! While TCP is somewhat ossified, the team behind QUIC picked and chose the best bits and changed its shape.
//! * [quinn](https://docs.rs/quinn/latest/quinn/), a Rust implementation of QUIC
//! * [quicfiletransfer](https://github.com/sirgallo/quicfiletransfer) uses [QUIC] to transfer files, but without an automated control channel.
//!
//! ## References
//! * [RFC 9000 "QUIC: A UDP-Based Multiplexed and Secure Transport"](https://www.rfc-editor.org/rfc/rfc9000.html)
//! * [RFC 9001 "Using TLS to Secure QUIC"](https://www.rfc-editor.org/rfc/rfc9001.html)
//! * [RFC 9002 "QUIC Loss Detection and Congestion Control"](https://www.rfc-editor.org/rfc/rfc9002.html)
//! * [quinn comparison of TCP, UDP and QUIC](https://quinn-rs.github.io/quinn/)
//!
//! [QUIC]: <https://quicwg.github.io/>
//! [ssh]: <https://en.wikipedia.org/wiki/Secure_Shell>
//! [TCP]: <https://en.wikipedia.org/wiki/Transmission_Control_Protocol>
//! [TLS]: <https://en.wikipedia.org/wiki/Transport_Layer_Security>
//! [CA]: <https://en.wikipedia.org/wiki/Certificate_authority>
//! [LetsEncrypt]: <https://letsencrypt.org/>
pub use ProtocolMessage;
pub use ;
pub use ;
/// Convenient includes for protocol building blocks
///
/// Caution: This prelude includes the following external crate definitions:
/// - [`serde::Serialize`] and [`serde::Deserialize`]
/// - [`serde_bare::Uint`]
pub