├── .gitignore ├── Cargo.toml ├── LICENSE ├── Makefile ├── README.markdown ├── crates ├── client │ ├── Cargo.toml │ ├── build.rs │ └── src │ │ └── main.rs ├── double-ratchet │ ├── .gitignore │ ├── Cargo.toml │ └── src │ │ ├── chains.rs │ │ ├── encrypt.rs │ │ ├── lib.rs │ │ ├── ratchet.rs │ │ ├── session.rs │ │ ├── tests.rs │ │ └── util.rs ├── server │ ├── Cargo.toml │ ├── build.rs │ └── src │ │ └── main.rs ├── signal-common │ ├── Cargo.toml │ └── src │ │ ├── convert.rs │ │ ├── error.rs │ │ ├── keys.rs │ │ └── lib.rs ├── signal │ ├── Cargo.toml │ └── src │ │ └── lib.rs └── x3dh │ ├── .gitignore │ ├── Cargo.toml │ └── src │ ├── keyserver.rs │ ├── lib.rs │ ├── one.rs │ ├── participant.rs │ └── peer.rs └── schema ├── keyserver.capnp └── util.capnp /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | **/*.rs.*.bk 4 | Cargo.lock 5 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | 3 | members = [ 4 | "crates/client", 5 | "crates/double-ratchet", 6 | "crates/server", 7 | "crates/signal", 8 | "crates/signal-common", 9 | "crates/x3dh", 10 | ] 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright ©2018 Andrew Dona-Couch 4 | 5 | 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: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | 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. 10 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: default 2 | default: target/debug/server target/debug/client 3 | rm -rf bob_key alice_share 4 | target/debug/client bob localhost:9999 bob_key alice_share & 5 | target/debug/client alice localhost:9999 bob_key alice_share 6 | rm -rf bob_key alice_share 7 | 8 | .PHONY: server 9 | server: 10 | target/debug/server localhost:9999 11 | 12 | target/debug/server: crates/server/src/main.rs 13 | cargo build --bin server 14 | 15 | target/debug/client: crates/client/src/main.rs 16 | cargo build --bin client 17 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | signal-rs 2 | ========= 3 | 4 | A Rust implementation of the [Signal Protocol]. Quite rough around 5 | the edges, and no security guarantees given. Just for curiosity. 6 | 7 | * Overview 8 | * Quick Start 9 | * Components 10 | * Road Map 11 | * More Information 12 | 13 | Overview 14 | -------- 15 | 16 | The Signal Protocol is at the heart of all mainstream end-to-end 17 | encrypted messaging these days: in addition to Signal, both Whats 18 | App and Facebook Messenger use it (along with a lot of other 19 | messaging apps). It is composed of two main parts: the X3DH key 20 | exchange protocol, and the Double Ratchet algorithm. 21 | 22 | Quick Start 23 | ----------- 24 | 25 | Run `cargo test --all`. Among others, this runs the test in 26 | `crates/signal/src/lib.rs`, which is a mockup of a complete end-to-end 27 | conversation including both key exchange and several iterations of 28 | the ratchet, however, it is entirely in-process. 29 | 30 | Then try `make server`, followed by (in another tab) `make`. This 31 | runs the multi-process example. The first command starts the server, 32 | which provides key & message relay. The second command starts the 33 | two client processes, which communicate with one another via the 34 | server. 35 | 36 | Components 37 | ---------- 38 | 39 | The two key crates are `x3dh`, which implements the key exchange 40 | algorithm, and `double-ratchet`, which implements the session key 41 | ratcheting algorithm. 42 | 43 | ### `x3dh` 44 | 45 | The main entity is the `Participant`, which manages generating and 46 | storing the various keys in play. In addition, you can create a 47 | local `Keyserver` to simulate the key relay functions in-process. 48 | 49 | ### `double-ratchet` 50 | 51 | The main entity is the `Session`, which can be created with a 52 | `SessionBuilder`. Initialize it with the shared session key, 53 | begin a connection, and then use the `Session` to encrypt and 54 | decrypt messages. 55 | 56 | Road Map 57 | -------- 58 | 59 | - Clean up `x3dh` and `double-ratchet` public APIs & docs. 60 | - Get rid of any unwrap or panic! 61 | - Use the `log` crate for logging. 62 | - Use a more curated set of crypto implementations. 63 | - Implement the encrypted headers extension. 64 | - Complete `client` and `server` example implementations. 65 | - Make a little interactive chat client example. 66 | - Make `Keyserver` trait asyncable so we can use it with a 67 | remote server? 68 | - Make a `Relayserver` trait for `double-ratchet` that 69 | corresponds to the `Keyserver `trait for `x3dh`? 70 | 71 | More Information 72 | ---------------- 73 | 74 | * The Signal Protocol [Specifications], specifically: 75 | * [X3DH], and 76 | * [Double Ratchet]. 77 | * Curve25519 math by Dalek Cryptography 78 | * [`curve25519-dalek`] 79 | * [`ed25519-dalek`] 80 | * [`x25519-dalek`] 81 | * Some primitives from RustCrypto: [`aes`] and [`sha2`]. 82 | * HMAC and HKDF implementations from [`orion`]. 83 | 84 | [Signal Protocol]: https://signal.org/docs/ 85 | [Specifications]: https://signal.org/docs/ 86 | [X3DH]: https://signal.org/docs/specifications/x3dh/ 87 | [Double Ratchet]: https://signal.org/docs/specifications/doubleratchet/ 88 | [`curve25519-dalek`]: https://doc.dalek.rs/curve25519_dalek/index.html 89 | [`ed25519-dalek`]: https://doc.dalek.rs/ed25519_dalek/index.html 90 | [`x25519-dalek`]: https://doc.dalek.rs/x25519_dalek/index.html 91 | [`aes`]: https://github.com/RustCrypto/block-ciphers 92 | [`sha2`]: https://github.com/RustCrypto/hashes 93 | [`orion`]: https://github.com/brycx/orion 94 | -------------------------------------------------------------------------------- /crates/client/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "client" 3 | version = "0.1.0" 4 | authors = ["Andrew Dona-Couch "] 5 | 6 | [dependencies] 7 | capnp = "0.8" 8 | capnp-rpc = "0.8" 9 | futures = "0.1" 10 | rand = "0.5.5" 11 | sha2 = "0.7" 12 | tokio = "0.1" 13 | 14 | double-ratchet = { path = "../double-ratchet" } 15 | signal-common = { path = "../signal-common" } 16 | x3dh = { path = "../x3dh" } 17 | 18 | [build-dependencies] 19 | capnpc = "0.8" 20 | -------------------------------------------------------------------------------- /crates/client/build.rs: -------------------------------------------------------------------------------- 1 | extern crate capnpc; 2 | 3 | fn main() { 4 | ::capnpc::CompilerCommand::new() 5 | .src_prefix("../../schema/") 6 | .file("../../schema/keyserver.capnp") 7 | .file("../../schema/util.capnp") 8 | .run() 9 | .unwrap(); 10 | } 11 | -------------------------------------------------------------------------------- /crates/client/src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate capnp; 2 | #[macro_use] 3 | extern crate capnp_rpc; 4 | extern crate futures; 5 | extern crate rand; 6 | extern crate tokio; 7 | 8 | extern crate double_ratchet; 9 | extern crate signal_common; 10 | extern crate x3dh; 11 | 12 | use std::io::{Read, Write}; 13 | 14 | use capnp_rpc::{RpcSystem, twoparty, rpc_twoparty_capnp}; 15 | use futures::Future; 16 | use tokio::io::AsyncRead; 17 | 18 | use double_ratchet::session::{Header, SessionBuilder}; 19 | use signal_common::keys::{ 20 | ChainKey, 21 | EphemeralKeyPublic, 22 | IdentityKeyPublic, 23 | OneTimePrekeyPublic, 24 | PrekeyBundle, 25 | RatchetKeyPair, 26 | RatchetKeyPublic, 27 | SignedPrekeyPublic, 28 | Signature, 29 | }; 30 | use x3dh::participant::Participant; 31 | 32 | pub mod keyserver_capnp { 33 | include!(concat!(env!("OUT_DIR"), "/keyserver_capnp.rs")); 34 | } 35 | 36 | pub mod util_capnp { 37 | include!(concat!(env!("OUT_DIR"), "/util_capnp.rs")); 38 | } 39 | 40 | pub fn main() { 41 | use std::net::ToSocketAddrs; 42 | let args: Vec = std::env::args().collect(); 43 | if args.len() != 5 { 44 | eprintln!("usage: {} ADDRESS[:PORT] [KEYFILE] [SHAREFILE]", args[0]); 45 | return; 46 | } 47 | 48 | let actor = args[1].to_owned(); 49 | let server_addr = args[2].to_owned(); 50 | let bob_keyfile = args[3].to_owned(); 51 | let alice_sharefile = args[4].to_owned(); 52 | 53 | let info = &b"this is my info"[..]; 54 | 55 | let mut runtime = ::tokio::runtime::current_thread::Runtime::new().unwrap(); 56 | 57 | let addr = server_addr.to_socket_addrs().unwrap().next().expect("could not parse address"); 58 | let stream = runtime.block_on(tokio::net::TcpStream::connect(&addr)).unwrap(); 59 | stream.set_nodelay(true).unwrap(); 60 | let (reader, writer) = stream.split(); 61 | let network = 62 | Box::new(twoparty::VatNetwork::new(reader, writer, 63 | rpc_twoparty_capnp::Side::Client, 64 | Default::default())); 65 | let mut rpc_system = RpcSystem::new(network, None); 66 | let server: keyserver_capnp::keyserver::Client = rpc_system.bootstrap(rpc_twoparty_capnp::Side::Server); 67 | runtime.spawn(rpc_system.map_err(|_e| ())); 68 | 69 | use rand::{ChaChaRng, OsRng, SeedableRng}; 70 | let mut csprng = ChaChaRng::from_rng(OsRng::new().unwrap()).unwrap(); 71 | 72 | // Initialize my keys 73 | println!("{}: Intializing my keys...", actor); 74 | let mut participant = Participant::new(&mut csprng); 75 | 76 | let ik = participant.ik(); 77 | let spk = participant.spk(); 78 | let spk_sig = participant.spk_sig(); 79 | 80 | let opk = participant.create_opk(&mut csprng); 81 | 82 | // Send them to the server 83 | { 84 | println!("{}: Registering identity with keyserver", actor); 85 | 86 | let mut request = server.update_identity_request(); 87 | 88 | { 89 | let mut body = request.get(); 90 | 91 | body.set_ik(ik.as_bytes()); 92 | 93 | let mut s = body.init_spk(); 94 | s.set_key(spk.as_bytes()).unwrap(); 95 | s.set_sig(&spk_sig.to_bytes()); 96 | } 97 | 98 | runtime.block_on(request.send().promise).unwrap(); 99 | } 100 | 101 | { 102 | println!("{}: Registering one-time prekey with keyserver", actor); 103 | 104 | let mut request = server.add_opks_request(); 105 | 106 | { 107 | let mut body = request.get(); 108 | 109 | body.set_ik(ik.as_bytes()); 110 | 111 | let mut os = body.init_opks(1); 112 | 113 | let mut o = os.reborrow().get(0); 114 | o.set_sig(&opk.1.to_bytes()); 115 | 116 | let mut o = o.init_key(); 117 | o.set_id(opk.0.index()); 118 | o.set_key(opk.0.as_bytes()); 119 | } 120 | 121 | runtime.block_on(request.send().promise).unwrap(); 122 | } 123 | 124 | // [If Bob] Save public key so it can be used to start Alice 125 | if actor == "bob" { 126 | println!("bob: Saving public key to {}", bob_keyfile); 127 | let mut file = std::fs::File::create(bob_keyfile.clone()).unwrap(); 128 | file.write_all(ik.as_bytes()).unwrap(); 129 | } 130 | 131 | let _session = if actor == "alice" { 132 | // [If Alice] Request Bob's prekey bundle from the server 133 | 134 | println!("alice: Loading Bob's key from {}", bob_keyfile); 135 | 136 | let bob_key = loop { 137 | let mut file = match std::fs::File::open(bob_keyfile.clone()) { 138 | Err(e) => { 139 | if let std::io::ErrorKind::NotFound = e.kind() { 140 | continue; 141 | } 142 | eprintln!("error opening file: {:?}", e); 143 | panic!(); 144 | }, 145 | Ok(f) => f, 146 | }; 147 | let mut buf_reader = std::io::BufReader::new(file); 148 | let mut bob_key_bytes = Vec::with_capacity(32); 149 | buf_reader.read_to_end(&mut bob_key_bytes).unwrap(); 150 | 151 | if bob_key_bytes.len() != 32 { continue; } 152 | 153 | let mut bob_key = [0; 32]; 154 | bob_key.copy_from_slice(&bob_key_bytes[0..32]); 155 | break IdentityKeyPublic::from_bytes(bob_key.into()).unwrap(); 156 | }; 157 | 158 | participant.add_peer(&bob_key); 159 | 160 | println!("alice: Requesting Bob's prekey bundle from keyserver"); 161 | 162 | let mut request = server.prekey_bundle_request(); 163 | 164 | request.get().set_ik(bob_key.as_bytes()); 165 | 166 | let ((sk, opk_id, ek), bob_addr) = runtime.block_on(request.send().promise.and_then(|response| { 167 | let response = pry!(pry!(response.get()).get_bundle()); 168 | let s = pry!(response.get_spk()); 169 | let o = pry!(response.get_opk()); 170 | 171 | let ik = { 172 | let mut ik = [0; 32]; 173 | ik.copy_from_slice(&pry!(response.get_ik())[0..32]); 174 | IdentityKeyPublic::from_bytes(ik.into()).unwrap() 175 | }; 176 | let spk = { 177 | let mut spk = [0; 32]; 178 | spk.copy_from_slice(&pry!(s.get_key())[0..32]); 179 | SignedPrekeyPublic::from_bytes(spk.into()).unwrap() 180 | }; 181 | let spk_sig = { 182 | let mut spk_sig = [0; 64]; 183 | spk_sig.copy_from_slice(&pry!(s.get_sig())[0..64]); 184 | Signature::from_bytes(spk_sig.into()).unwrap() 185 | }; 186 | 187 | let opk = match o.which() { 188 | Ok(util_capnp::maybe::Which::None(())) => None, 189 | Ok(util_capnp::maybe::Which::Some(o)) => { 190 | let o = pry!(o); 191 | let id = o.get_id(); 192 | let key = { 193 | let mut opk = [0; 32]; 194 | opk.copy_from_slice(&pry!(o.get_key())[0..32]); 195 | opk.into() 196 | }; 197 | match OneTimePrekeyPublic::from_bytes(id, key) { 198 | Ok(opk) => Some(opk), 199 | _ => return capnp::capability::Promise::err( 200 | capnp::Error::failed("unable to get OPK".to_owned()), 201 | ), 202 | } 203 | }, 204 | Err(_) => return capnp::capability::Promise::err( 205 | capnp::Error::failed("Bad Request".to_owned()), 206 | ), 207 | }; 208 | 209 | if opk.is_none() { 210 | panic!("Empty OPK not yet supported!!!!"); 211 | } 212 | 213 | let bob_addr = RatchetKeyPublic::from(&spk); 214 | 215 | let prekey_bundle = PrekeyBundle { ik, spk, spk_sig, opk }; 216 | 217 | println!("alice: Generating session key from bundle"); 218 | 219 | capnp::capability::Promise::ok(( 220 | participant.accept_bundle(prekey_bundle, &mut csprng).unwrap(), 221 | bob_addr, 222 | )) 223 | 224 | })).unwrap(); 225 | 226 | println!("alice: Generated session key: {:?}", sk); 227 | 228 | let mut session = SessionBuilder::new(info, ChainKey::from(sk)) 229 | .connect_to(&bob_addr, &mut csprng); 230 | 231 | let message = b"Hello, Bob! Nice secret channel we have here!"; 232 | let (header, secret) = session.send(message); 233 | 234 | println!("alice: Saving sharefile to {}", alice_sharefile); 235 | let mut file = std::fs::File::create(alice_sharefile.clone()).unwrap(); 236 | file.write_all(ik.as_bytes()).unwrap(); 237 | file.write_all(&[ 238 | (opk_id >> 56) as u8, 239 | (opk_id >> 48) as u8, 240 | (opk_id >> 40) as u8, 241 | (opk_id >> 32) as u8, 242 | (opk_id >> 24) as u8, 243 | (opk_id >> 16) as u8, 244 | (opk_id >> 8) as u8, 245 | opk_id as u8, 246 | ]).unwrap(); 247 | file.write_all(ek.as_bytes()).unwrap(); 248 | 249 | file.write_all(&header.public_key.to_bytes()[..]).unwrap(); 250 | file.write_all(&[ 251 | (header.prev_count >> 24) as u8, 252 | (header.prev_count >> 16) as u8, 253 | (header.prev_count >> 8) as u8, 254 | header.prev_count as u8, 255 | ]).unwrap(); 256 | file.write_all(&[ 257 | (header.count >> 24) as u8, 258 | (header.count >> 16) as u8, 259 | (header.count >> 8) as u8, 260 | header.count as u8, 261 | ]).unwrap(); 262 | 263 | let content_length = secret.len() as u64; 264 | file.write_all(&[ 265 | (content_length >> 56) as u8, 266 | (content_length >> 48) as u8, 267 | (content_length >> 40) as u8, 268 | (content_length >> 32) as u8, 269 | (content_length >> 24) as u8, 270 | (content_length >> 16) as u8, 271 | (content_length >> 8) as u8, 272 | content_length as u8, 273 | ]).unwrap(); 274 | 275 | file.write_all(&secret).unwrap(); 276 | 277 | session 278 | 279 | } else /* if actor == "bob" */ { 280 | // [If Bob] Wait until Alice writes her sharefile. 281 | 282 | const SHAREFILE_HEADER: usize = 120; 283 | let mut sharefile_size = SHAREFILE_HEADER; 284 | 285 | // The first time through we don't know how much to expect. 286 | 'try_read: loop { 287 | 288 | println!("bob: Waiting for Alice to write {}", alice_sharefile); 289 | let alice_share_bytes = loop { 290 | let file = match std::fs::File::open(alice_sharefile.clone()) { 291 | Err(e) => { 292 | if let std::io::ErrorKind::NotFound = e.kind() { 293 | continue; 294 | } 295 | eprintln!("error opening file: {:?}", e); 296 | panic!(); 297 | }, 298 | Ok(f) => f, 299 | }; 300 | 301 | println!("bob: Got Alice's sharefile, loading..."); 302 | let mut buf_reader = std::io::BufReader::new(file); 303 | let mut alice_share_bytes = Vec::with_capacity(sharefile_size); 304 | buf_reader.read_to_end(&mut alice_share_bytes).unwrap(); 305 | 306 | if alice_share_bytes.len() < sharefile_size { 307 | println!( 308 | "bob: Not enough bytes, only {}, wanted {}", 309 | alice_share_bytes.len(), 310 | sharefile_size, 311 | ); 312 | continue; 313 | } 314 | 315 | break alice_share_bytes; 316 | }; 317 | 318 | let alice_key = { 319 | let mut alice_key = [0; 32]; 320 | alice_key.copy_from_slice(&alice_share_bytes[0..32]); 321 | IdentityKeyPublic::from_bytes(alice_key).unwrap() 322 | }; 323 | let opk_id = { 324 | let b = &alice_share_bytes[32..40]; 325 | 326 | (b[0] as u64) << 56 | 327 | (b[1] as u64) << 48 | 328 | (b[2] as u64) << 40 | 329 | (b[3] as u64) << 32 | 330 | (b[4] as u64) << 24 | 331 | (b[5] as u64) << 16 | 332 | (b[6] as u64) << 8 | 333 | (b[7] as u64) 334 | }; 335 | let ek = { 336 | let mut ek_bytes = [0; 32]; 337 | ek_bytes.copy_from_slice(&alice_share_bytes[40..72]); 338 | EphemeralKeyPublic::from_bytes(ek_bytes).unwrap() 339 | }; 340 | let public_key = { 341 | let mut rk_bytes = [0; 32]; 342 | rk_bytes.copy_from_slice(&alice_share_bytes[72..104]); 343 | RatchetKeyPublic::from_bytes(rk_bytes).unwrap() 344 | }; 345 | let prev_count = { 346 | let b = &alice_share_bytes[104..108]; 347 | 348 | (b[0] as u32) << 24 | 349 | (b[1] as u32) << 16 | 350 | (b[2] as u32) << 8 | 351 | (b[3] as u32) 352 | }; 353 | let count = { 354 | let b = &alice_share_bytes[108..112]; 355 | 356 | (b[0] as u32) << 24 | 357 | (b[1] as u32) << 16 | 358 | (b[2] as u32) << 8 | 359 | (b[3] as u32) 360 | }; 361 | let content_length = { 362 | let b = &alice_share_bytes[112..120]; 363 | 364 | (b[0] as u64) << 56 | 365 | (b[1] as u64) << 48 | 366 | (b[2] as u64) << 40 | 367 | (b[3] as u64) << 32 | 368 | (b[4] as u64) << 24 | 369 | (b[5] as u64) << 16 | 370 | (b[6] as u64) << 8 | 371 | (b[7] as u64) 372 | }; 373 | 374 | // a pointer to up top when editing. 375 | assert_eq!(120, SHAREFILE_HEADER); 376 | 377 | sharefile_size = SHAREFILE_HEADER + content_length as usize; 378 | 379 | if alice_share_bytes.len() != sharefile_size { 380 | continue 'try_read; 381 | } 382 | 383 | let secret = &alice_share_bytes[SHAREFILE_HEADER..]; 384 | 385 | println!("bob: Generating session key from sharefile"); 386 | 387 | let sk = participant.complete_exchange(&alice_key, opk_id, ek).unwrap(); 388 | 389 | println!("bob: Generated session key: {:?}", sk); 390 | 391 | let ratchet_key = RatchetKeyPair::from(participant.spk_pair()); 392 | 393 | let mut session = SessionBuilder::new(info, ChainKey::from(sk)) 394 | .accept_with(ratchet_key); 395 | 396 | let header = Header { public_key, prev_count, count }; 397 | 398 | let message = session.receive(header, &secret, &mut csprng).unwrap(); 399 | 400 | println!("bob: Received: {:?}", String::from_utf8(message).unwrap()); 401 | 402 | break session; 403 | } 404 | }; 405 | } 406 | -------------------------------------------------------------------------------- /crates/double-ratchet/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | -------------------------------------------------------------------------------- /crates/double-ratchet/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "double-ratchet" 3 | version = "0.1.0" 4 | authors = ["Andrew Dona-Couch "] 5 | 6 | [dependencies] 7 | aes = "0.3.2" 8 | block-modes = "0.2.0" 9 | curve25519-dalek = "0.21.0" 10 | ed25519-dalek = "0.8.1" 11 | orion = "0.9.1" 12 | rand = "0.5.5" 13 | sha2 = "0.7" 14 | 15 | signal-common = { path = "../signal-common" } 16 | 17 | [dependencies.x25519-dalek] 18 | version = "0.3.0" 19 | default-features = false 20 | features = ["std", "u64_backend"] 21 | -------------------------------------------------------------------------------- /crates/double-ratchet/src/chains.rs: -------------------------------------------------------------------------------- 1 | use signal_common::keys::{ChainKey, SessionKey, MessageKey}; 2 | 3 | use crate::util::{hkdf, hmac}; 4 | 5 | pub trait Kdf { 6 | type Input; 7 | type Output; 8 | 9 | fn derive(&self, key: ChainKey, input: Self::Input) -> (ChainKey, Self::Output); 10 | } 11 | 12 | struct ChainData { 13 | key: Option, 14 | kdf: K, 15 | n: u32, 16 | } 17 | 18 | impl ChainData { 19 | fn init(kdf: K, key: ChainKey) -> ChainData { 20 | ChainData { key: Some(key), kdf, n: 0 } 21 | } 22 | 23 | fn advance(&mut self, input: K::Input) -> (u32, K::Output) { 24 | let key = self.key.take().unwrap(); 25 | let (key, output) = self.kdf.derive(key, input); 26 | self.key = Some(key); 27 | let n = self.n; 28 | self.n += 1; 29 | (n, output) 30 | } 31 | 32 | fn into_kdf(self) -> K { 33 | self.kdf 34 | } 35 | } 36 | 37 | struct Chainer { 38 | kdf: K, 39 | } 40 | 41 | impl Chainer { 42 | fn with_kdf(kdf: K) -> Chainer { 43 | Chainer { kdf } 44 | } 45 | 46 | fn into_chain_data(self, key: ChainKey) -> ChainData { 47 | ChainData::init(self.kdf, key) 48 | } 49 | } 50 | 51 | enum ChainState { 52 | Init(Chainer), 53 | Run(ChainData), 54 | } 55 | 56 | pub struct Chain { 57 | state: Option>, 58 | } 59 | 60 | impl Chain { 61 | fn with_kdf(kdf: K) -> Chain { 62 | Chain { state: Some(ChainState::Init(Chainer::with_kdf(kdf))) } 63 | } 64 | 65 | 66 | fn next_chain(&mut self, key: ChainKey) { 67 | self.state = Some(ChainState::Run(match self.state.take().unwrap() { 68 | ChainState::Init(chainer) => chainer.into_chain_data(key), 69 | ChainState::Run(chain) => { 70 | let kdf = chain.into_kdf(); 71 | ChainData::init(kdf, key) 72 | }, 73 | })) 74 | } 75 | 76 | fn advance(&mut self, input: K::Input) -> (u32, K::Output) { 77 | match self.state { 78 | Some(ChainState::Run(ref mut chain)) => chain.advance(input), 79 | // TODO: don't panic 80 | _ => panic!("Chain not initialized!"), 81 | } 82 | } 83 | } 84 | 85 | pub struct RootKdf(Vec); 86 | 87 | impl Kdf for RootKdf { 88 | type Input = SessionKey; 89 | type Output = ChainKey; 90 | 91 | fn derive(&self, key: ChainKey, input: SessionKey) -> (ChainKey, ChainKey) { 92 | let bytes = hkdf(&key, &input, &self.0); 93 | let new_key = ChainKey::from(&bytes[0..32]); 94 | let output = ChainKey::from(&bytes[32..64]); 95 | (new_key, output) 96 | } 97 | } 98 | 99 | pub struct ChainKdf; 100 | 101 | impl Kdf for ChainKdf { 102 | type Input = (); 103 | type Output = MessageKey; 104 | 105 | fn derive(&self, key: ChainKey, _input: ()) -> (ChainKey, MessageKey) { 106 | let raw_key = hmac(&key, &[0x02]); 107 | let raw_out = hmac(&key, &[0x01]); 108 | 109 | let new_key = ChainKey::from(&raw_key[..]); 110 | let output = MessageKey::from(&raw_out[..]); 111 | 112 | (new_key, output) 113 | } 114 | } 115 | 116 | pub struct Chains { 117 | root: Chain, 118 | sending: Chain, 119 | receiving: Chain, 120 | } 121 | 122 | impl Chains { 123 | pub fn init>(info: Bytes, rk: ChainKey) -> Chains { 124 | let mut root = Chain::with_kdf(RootKdf((*info).into())); 125 | root.next_chain(rk); 126 | let sending = Chain::with_kdf(ChainKdf); 127 | let receiving = Chain::with_kdf(ChainKdf); 128 | Chains { root, sending, receiving } 129 | } 130 | 131 | pub fn next_sending_chain(&mut self, key: SessionKey) { 132 | let key = self.root.advance(key).1; 133 | self.sending.next_chain(key); 134 | } 135 | 136 | pub fn next_receiving_chain(&mut self, key: SessionKey) { 137 | let key = self.root.advance(key).1; 138 | self.receiving.next_chain(key); 139 | } 140 | 141 | pub fn next_sending_key(&mut self) -> (u32, MessageKey) { 142 | self.sending.advance(()) 143 | } 144 | 145 | pub fn next_receiving_key(&mut self) -> (u32, MessageKey) { 146 | self.receiving.advance(()) 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /crates/double-ratchet/src/encrypt.rs: -------------------------------------------------------------------------------- 1 | //! Encrypt and decrypt messages. 2 | //! 3 | //! This module is the workhorse of the Signal protocol. All the rest 4 | //! of the song and dance is just a complicated way to get a really big 5 | //! number. Here is where the rubber hits the road: we use that number 6 | //! to scramble up something legible and then unscramble it again later. 7 | 8 | use signal_common::error::{Error, Result}; 9 | use signal_common::keys::MessageKey; 10 | 11 | use crate::util::{ 12 | hkdf_sha512, 13 | hmac_sha512_two_slices, 14 | aes256_cbc_pkcs7_encrypt, 15 | aes256_cbc_pkcs7_decrypt, 16 | }; 17 | 18 | const HMAC_WIDTH: usize = 64; 19 | const MARGIN: usize = 32; 20 | 21 | /// An implementation of Authenticated Encryption with Associated Data. 22 | pub struct AeadCipher<'a> { 23 | info: &'static [u8], 24 | key: MessageKey, 25 | ad: &'a [u8], 26 | } 27 | 28 | impl<'a> AeadCipher<'a> { 29 | /// Initialize the cipher with the given parameters. 30 | pub fn new(info: &'static [u8], key: MessageKey, ad: &'a [u8]) -> AeadCipher<'a> { 31 | AeadCipher { info, key, ad } 32 | } 33 | 34 | /// Encrypt a message with this cipher. 35 | pub fn encrypt(self, plaintext: &[u8]) -> Vec { 36 | let len = plaintext.len(); 37 | 38 | let mut buffer: Vec<_> = std::iter::repeat(0) 39 | .take(len + HMAC_WIDTH + MARGIN) 40 | .collect(); 41 | 42 | let salt = [0; 64]; 43 | let mut keys = [0; 80]; 44 | hkdf_sha512(&salt, &self.key, self.info, &mut keys); 45 | 46 | let encryption_key = &keys[..32]; 47 | let authentication_key = &keys[32..64]; 48 | let iv = &keys[64..]; 49 | 50 | buffer[..len].copy_from_slice(plaintext); 51 | 52 | let enc_len = { 53 | let encrypted = aes256_cbc_pkcs7_encrypt( 54 | encryption_key, iv, &mut buffer, len, 55 | ); 56 | encrypted.len() 57 | }; 58 | 59 | let mac = hmac_sha512_two_slices( 60 | authentication_key, self.ad, &buffer[..enc_len] 61 | ); 62 | 63 | let verify_len = enc_len + HMAC_WIDTH; 64 | 65 | buffer[enc_len..verify_len].copy_from_slice(&mac); 66 | 67 | buffer.split_off(verify_len); 68 | buffer 69 | } 70 | 71 | /// Decrypt a message with this cipher. 72 | pub fn decrypt(self, ciphertext: &[u8]) -> Result> { 73 | use orion::util::compare_ct; 74 | 75 | let msg_len = ciphertext.len(); 76 | let enc_len = msg_len - HMAC_WIDTH; 77 | 78 | let salt = [0; 64]; 79 | let mut keys = [0; 80]; 80 | hkdf_sha512(&salt, &self.key, self.info, &mut keys); 81 | 82 | let encryption_key = &keys[..32]; 83 | let authentication_key = &keys[32..64]; 84 | let iv = &keys[64..]; 85 | 86 | let mac = hmac_sha512_two_slices( 87 | authentication_key, self.ad, &ciphertext[..enc_len], 88 | ); 89 | 90 | let msg_mac = &ciphertext[enc_len..]; 91 | 92 | match compare_ct(&mac, msg_mac) { 93 | Ok(true) => {}, 94 | _ => return Err(Error), 95 | } 96 | 97 | let mut buffer = std::iter::repeat(0).take(enc_len).collect::>(); 98 | 99 | buffer.copy_from_slice(&ciphertext[..enc_len]); 100 | 101 | let msg_len = { 102 | let msg = aes256_cbc_pkcs7_decrypt( 103 | encryption_key, iv, &mut buffer, 104 | )?; 105 | msg.len() 106 | }; 107 | 108 | buffer.split_off(msg_len); 109 | Ok(buffer) 110 | } 111 | } 112 | 113 | #[cfg(test)] 114 | mod tests { 115 | use super::*; 116 | 117 | #[test] 118 | fn test_encrypt_roundtrip() { 119 | let info = &b"application info string"[..]; 120 | let ad = &b"associated data string"[..]; 121 | 122 | let plaintext = &b"the quick brown fox jumps over the lazy dog"[..]; 123 | 124 | let key = MessageKey::from(&[0x42; 32][..]); 125 | let cipher = AeadCipher::new(info, key, ad); 126 | let ciphertext = cipher.encrypt(plaintext); 127 | 128 | let key = MessageKey::from(&[0x42; 32][..]); 129 | let cipher = AeadCipher::new(info, key, ad); 130 | let roundtripped = cipher.decrypt(&ciphertext).unwrap(); 131 | 132 | assert_eq!(plaintext, &roundtripped[..]); 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /crates/double-ratchet/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! An implementation of the Double Ratchet protocol. 2 | //! 3 | //! See the [`Session`] documentation for details. 4 | //! 5 | //! [`Session`]: session/struct.Session.html 6 | 7 | extern crate aes; 8 | extern crate block_modes; 9 | extern crate curve25519_dalek; 10 | extern crate ed25519_dalek; 11 | extern crate orion; 12 | extern crate rand; 13 | extern crate sha2; 14 | extern crate x25519_dalek; 15 | 16 | extern crate signal_common; 17 | 18 | pub mod chains; 19 | pub mod encrypt; 20 | pub mod ratchet; 21 | pub mod session; 22 | pub mod util; 23 | 24 | pub use session::*; 25 | 26 | #[cfg(test)] 27 | mod tests; 28 | -------------------------------------------------------------------------------- /crates/double-ratchet/src/ratchet.rs: -------------------------------------------------------------------------------- 1 | use std::ops::Deref; 2 | 3 | use rand::{CryptoRng, Rng}; 4 | 5 | use signal_common::keys::{ 6 | ChainKey, 7 | RatchetKeyPair, 8 | MessageKey, 9 | RatchetKeyPublic, 10 | RatchetKeySecret, 11 | SessionKey, 12 | }; 13 | 14 | use crate::chains::Chains; 15 | 16 | /// The state of one end of a double ratchet session. 17 | /// 18 | /// Maintains the several KDF chains used in the double ratchet algorithm, 19 | /// advancing each as necessary with the appropriate keys. 20 | /// 21 | /// There is an asymmetry to the protocol startup. The participant who 22 | /// initiates the session needs to have the public key for the other 23 | /// participant. The session can then be created with: 24 | /// 25 | /// ```ignore 26 | /// # extern crate curve25519_dalek; 27 | /// # extern crate double_ratchet; 28 | /// # extern crate rand; 29 | /// # use curve25519_dalek::edwards::CompressedEdwardsY; 30 | /// # use double_ratchet::keys::{ChainKey, RatchetKeyPublic}; 31 | /// # use double_ratchet::ratchet::DoubleRatchet; 32 | /// # use rand::OsRng; 33 | /// # let root_key = ChainKey::from(&[0; 32][..]); 34 | /// # let bob_public_key = RatchetKeyPublic::from(&CompressedEdwardsY::from_slice(&[0; 32][..]).decompress().unwrap().to_montgomery()); 35 | /// # let mut csprng = OsRng::new().unwrap(); 36 | /// # 37 | /// let mut alice = DoubleRatchet::::with_peer(&[1, 2, 3][..], root_key, &mut csprng, &bob_public_key); 38 | /// ``` 39 | /// 40 | /// The participant receiving the first message must have the secret key 41 | /// corresponding to the public key that the other participant has. The 42 | /// second session can be created with: 43 | /// 44 | /// ```ignore 45 | /// # extern crate double_ratchet; 46 | /// # extern crate rand; 47 | /// # use double_ratchet::keys::{ChainKey, RatchetKeyPair}; 48 | /// # use double_ratchet::ratchet::DoubleRatchet; 49 | /// # use rand::OsRng; 50 | /// # let root_key = ChainKey::from(&[0; 32][..]); 51 | /// # let bob_keypair = RatchetKeyPair::generate(&mut OsRng::new().unwrap()); 52 | /// # let mut csprng = OsRng::new().unwrap(); 53 | /// # 54 | /// let mut bob = DoubleRatchet::::with_keypair(&[1, 2, 3][..], root_key, &mut csprng, bob_keypair); 55 | /// ``` 56 | /// 57 | /// After that the participants operate the same. Upon receipt of a 58 | /// message with a new public key, perform a Diffie-Hellman ratchet. 59 | /// Then get the message key to decrypt with: 60 | /// 61 | /// ```ignore 62 | /// # extern crate curve25519_dalek; 63 | /// # extern crate double_ratchet; 64 | /// # extern crate rand; 65 | /// # use curve25519_dalek::edwards::CompressedEdwardsY; 66 | /// # use double_ratchet::keys::{ChainKey, RatchetKeyPair, RatchetKeyPublic}; 67 | /// # use double_ratchet::ratchet::DoubleRatchet; 68 | /// # use rand::OsRng; 69 | /// # let root_key = ChainKey::from(&[0; 32][..]); 70 | /// # let mut csprng = OsRng::new().unwrap(); 71 | /// # let bob_keypair = RatchetKeyPair::generate(&mut csprng); 72 | /// # let mut csprng = OsRng::new().unwrap(); 73 | /// # let mut bob = DoubleRatchet::::with_keypair(&[1, 2, 3][..], root_key, &mut csprng, bob_keypair); 74 | /// # let root_key = ChainKey::from(&[0; 32][..]); 75 | /// # let mut csprng = OsRng::new().unwrap(); 76 | /// # let mut alice = DoubleRatchet::::with_peer(&[1, 2, 3][..], root_key, &mut csprng, bob.public()); 77 | /// # 78 | /// bob.ratchet(alice.public()); 79 | /// let message_key = bob.next_receiving_key(); 80 | /// 81 | /// alice.ratchet(bob.public()); 82 | /// let message_key = alice.next_receiving_key(); 83 | /// ``` 84 | /// 85 | /// Upon receipt of a message with the same public key as a prior one, 86 | /// perform a symmetric ratchet by just asking for the next key: 87 | /// 88 | /// ```ignore 89 | /// # extern crate curve25519_dalek; 90 | /// # extern crate double_ratchet; 91 | /// # extern crate rand; 92 | /// # use curve25519_dalek::edwards::CompressedEdwardsY; 93 | /// # use double_ratchet::keys::{ChainKey, RatchetKeyPair, RatchetKeyPublic}; 94 | /// # use double_ratchet::ratchet::DoubleRatchet; 95 | /// # use rand::OsRng; 96 | /// # let root_key = ChainKey::from(&[0; 32][..]); 97 | /// # let mut csprng = OsRng::new().unwrap(); 98 | /// # let bob_keypair = RatchetKeyPair::generate(&mut csprng); 99 | /// # let mut csprng = OsRng::new().unwrap(); 100 | /// # let mut bob = DoubleRatchet::::with_keypair(&[1, 2, 3][..], root_key, &mut csprng, bob_keypair); 101 | /// # let root_key = ChainKey::from(&[0; 32][..]); 102 | /// # let mut csprng = OsRng::new().unwrap(); 103 | /// # let mut alice = DoubleRatchet::::with_peer(&[1, 2, 3][..], root_key, &mut csprng, bob.public()); 104 | /// # bob.ratchet(alice.public()); 105 | /// # 106 | /// let message_key = alice.next_receiving_key(); 107 | /// let message_key = alice.next_receiving_key(); 108 | /// let message_key = alice.next_receiving_key(); 109 | /// ``` 110 | /// 111 | /// To send a message, perform a symmetric ratchet on the sending chain 112 | /// by requesting the next sending key: 113 | /// 114 | /// ```ignore 115 | /// # extern crate curve25519_dalek; 116 | /// # extern crate double_ratchet; 117 | /// # extern crate rand; 118 | /// # use curve25519_dalek::edwards::CompressedEdwardsY; 119 | /// # use double_ratchet::keys::{ChainKey, RatchetKeyPair, RatchetKeyPublic}; 120 | /// # use double_ratchet::ratchet::DoubleRatchet; 121 | /// # use rand::OsRng; 122 | /// # let root_key = ChainKey::from(&[0; 32][..]); 123 | /// # let bob_keypair = RatchetKeyPair::generate(&mut OsRng::new().unwrap()); 124 | /// # let mut csprng = OsRng::new().unwrap(); 125 | /// # let mut bob = DoubleRatchet::::with_keypair(&[1, 2, 3][..], root_key, &mut csprng, bob_keypair); 126 | /// # let root_key = ChainKey::from(&[0; 32][..]); 127 | /// # let mut csprng = OsRng::new().unwrap(); 128 | /// # let mut alice = DoubleRatchet::::with_peer(&[1, 2, 3][..], root_key, &mut csprng, bob.public()); 129 | /// # bob.ratchet(alice.public()); 130 | /// # 131 | /// let message_key = bob.next_sending_key(); 132 | /// ``` 133 | pub struct DoubleRatchet { 134 | chains: Chains, 135 | keypair: Option, 136 | } 137 | 138 | impl DoubleRatchet { 139 | pub fn with_peer, R: CryptoRng + Rng>( 140 | info: Bytes, 141 | root_key: ChainKey, 142 | csprng: &mut R, 143 | peer: &RatchetKeyPublic, 144 | ) -> DoubleRatchet { 145 | let mut session = DoubleRatchet { 146 | chains: Chains::init(info, root_key), 147 | keypair: None, 148 | }; 149 | session.half_ratchet(csprng, peer); 150 | session 151 | } 152 | 153 | pub fn with_keypair>( 154 | info: Bytes, 155 | root_key: ChainKey, 156 | keypair: RatchetKeyPair, 157 | ) -> DoubleRatchet { 158 | DoubleRatchet { 159 | chains: Chains::init(info, root_key), 160 | keypair: Some(keypair), 161 | } 162 | } 163 | 164 | fn generate_keypair(&mut self, csprng: &mut R) { 165 | self.keypair = Some(RatchetKeyPair::generate(csprng)); 166 | } 167 | 168 | fn secret(&self) -> &RatchetKeySecret { 169 | match self.keypair { 170 | None => panic!("DoubleRatchet has no secret key initialized!"), 171 | Some(ref keypair) => &(*keypair).secret, 172 | } 173 | } 174 | 175 | pub fn public(&self) -> &RatchetKeyPublic { 176 | match self.keypair { 177 | None => panic!("DoubleRatchet has no secret key initialized!"), 178 | Some(ref keypair) => &(*keypair).public, 179 | } 180 | } 181 | 182 | fn half_ratchet(&mut self, csprng: &mut R, peer: &RatchetKeyPublic) { 183 | if self.keypair.is_some() { 184 | panic!("DoubleRatchet can only perform half-ratchet on init!"); 185 | } 186 | 187 | self.generate_keypair(csprng); 188 | 189 | let sk = dh(peer, self.secret()); 190 | self.chains.next_sending_chain(sk); 191 | } 192 | 193 | pub fn ratchet(&mut self, csprng: &mut R, peer: &RatchetKeyPublic) { 194 | if self.keypair.is_none() { 195 | panic!("DoubleRatchet can only perform full ratchet after init!"); 196 | } 197 | 198 | let sk = dh(peer, self.secret()); 199 | self.chains.next_receiving_chain(sk); 200 | 201 | self.generate_keypair(csprng); 202 | 203 | let sk = dh(peer, self.secret()); 204 | self.chains.next_sending_chain(sk); 205 | } 206 | 207 | pub fn next_sending_key(&mut self) -> (u32, MessageKey) { 208 | self.chains.next_sending_key() 209 | } 210 | 211 | pub fn next_receiving_key(&mut self) -> (u32, MessageKey) { 212 | self.chains.next_receiving_key() 213 | } 214 | } 215 | 216 | pub fn dh(peer: &RatchetKeyPublic, me: &RatchetKeySecret) -> SessionKey { 217 | use x25519_dalek::diffie_hellman; 218 | 219 | let bytes = diffie_hellman(me.as_bytes(), peer.as_bytes()); 220 | 221 | SessionKey::from(&bytes[..]) 222 | } 223 | -------------------------------------------------------------------------------- /crates/double-ratchet/src/session.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use std::ops::Deref; 3 | 4 | use rand::{CryptoRng, Rng}; 5 | 6 | use signal_common::error::Result; 7 | use signal_common::keys::{ 8 | ChainKey, 9 | RatchetKeyPair, 10 | MessageKey, 11 | RatchetKeyPublic, 12 | }; 13 | 14 | use crate::encrypt::AeadCipher; 15 | use crate::ratchet::DoubleRatchet; 16 | 17 | const MAX_SKIP: u32 = 777; 18 | 19 | /// A factory for [`Session`]s. 20 | /// 21 | /// Make one with [`new`], then build a session. 22 | /// For the initial sender: 23 | /// 24 | /// ```ignore 25 | /// let mut alice = SessionBuilder::new(&b"info"[..], root_key) 26 | /// .connect_to(&bob_public_key, &mut csprng); 27 | /// ``` 28 | /// 29 | /// And for the initial receiver: 30 | /// 31 | /// ```ignore 32 | /// let mut bob = SessionBuilder::new(&b"info"[..], root_key) 33 | /// .accept_with(bob_keypair); 34 | /// ``` 35 | /// 36 | /// [`Session`]: struct.Session.html 37 | /// [`new`]: #method.new 38 | pub struct SessionBuilder { 39 | info: Bytes, 40 | root_key: ChainKey, 41 | } 42 | 43 | impl> SessionBuilder { 44 | pub fn new(info: Bytes, root_key: ChainKey) -> SessionBuilder { 45 | SessionBuilder { info, root_key } 46 | } 47 | 48 | pub fn connect_to( 49 | self, 50 | peer: &RatchetKeyPublic, 51 | csprng: &mut R, 52 | ) -> Session { 53 | Session::new( 54 | DoubleRatchet::with_peer( 55 | self.info, self.root_key, csprng, peer, 56 | ), 57 | Some(peer.clone()), 58 | ) 59 | } 60 | 61 | pub fn accept_with(self, keypair: RatchetKeyPair) -> Session { 62 | Session::new( 63 | DoubleRatchet::with_keypair( 64 | self.info, self.root_key, keypair, 65 | ), 66 | None, 67 | ) 68 | } 69 | } 70 | 71 | /// A message header. 72 | #[derive(Clone)] 73 | pub struct Header { 74 | /// The public ratchet key currently in use. 75 | pub public_key: RatchetKeyPublic, 76 | /// The number of messages in the previous sending chain. 77 | pub prev_count: u32, 78 | /// The number of messages in the current sending chain. 79 | pub count: u32, 80 | } 81 | 82 | /// The state of one end of a double ratchet session. 83 | /// 84 | /// Build one with a [`SessionBuilder`], and then [`send`] and 85 | /// [`receive`] to encrypt and decrypt. 86 | /// 87 | /// [`SessionBuilder`]: struct.SessionBuilder.html 88 | /// [`send`]: #method.send 89 | /// [`receive`]: #method.receive 90 | pub struct Session { 91 | ratchet: DoubleRatchet, 92 | last_peer_key: Option, 93 | ns: u32, 94 | nr: u32, 95 | p_ns: u32, 96 | mk_skipped: HashMap<(RatchetKeyPublic, u32), MessageKey>, 97 | } 98 | 99 | impl Session { 100 | fn new(ratchet: DoubleRatchet, last_peer_key: Option) -> Session { 101 | Session { 102 | ratchet, 103 | last_peer_key, 104 | ns: 0, 105 | nr: 0, 106 | p_ns: 0, 107 | mk_skipped: HashMap::new(), 108 | } 109 | } 110 | 111 | /// Get the receiving chain message key for the message. 112 | pub fn receive_key( 113 | &mut self, 114 | header: Header, 115 | csprng: &mut R, 116 | ) -> MessageKey { 117 | let public_clone = header.public_key.clone(); 118 | 119 | match self.mk_skipped.remove(&(public_clone, header.count)) { 120 | Some(mk) => return mk, 121 | _ => {}, 122 | } 123 | 124 | match self.last_peer_key { 125 | Some(ref key) if key == &header.public_key => { 126 | // Still on the same DH ratchet step.. 127 | }, 128 | _ => { 129 | self.p_ns = self.ns; 130 | self.skip_message_keys(header.prev_count); 131 | self.ratchet.ratchet(csprng, &header.public_key); 132 | self.last_peer_key = Some(header.public_key.clone()); 133 | } 134 | } 135 | 136 | self.skip_message_keys(header.count); 137 | 138 | let mk = self.ratchet.next_receiving_key().1; 139 | self.nr += 1; 140 | mk 141 | } 142 | 143 | /// Decrypt a message in the session. 144 | pub fn receive( 145 | &mut self, 146 | header: Header, 147 | ciphertext: &[u8], 148 | csprng: &mut R, 149 | ) -> Result> { 150 | let key = self.receive_key(header.clone(), csprng); 151 | let ad = generate_ad(&b"ad"[..], &header); 152 | let cipher = AeadCipher::new(&b"another info"[..], key, &ad); 153 | cipher.decrypt(ciphertext) 154 | } 155 | 156 | /// Encrypt a message in the session. 157 | pub fn send( 158 | &mut self, 159 | plaintext: &[u8], 160 | ) -> (Header, Vec) { 161 | let (header, key) = self.send_key(); 162 | let ad = generate_ad(&b"ad"[..], &header); 163 | let cipher = AeadCipher::new(&b"another info"[..], key, &ad); 164 | let msg = cipher.encrypt(plaintext); 165 | (header, msg) 166 | } 167 | 168 | fn skip_message_keys(&mut self, count: u32) { 169 | let skip = count - self.nr; 170 | if skip > MAX_SKIP { 171 | // TODO: don't panic! 172 | panic!("Cannot skip more than {}, tried: {:?}!", MAX_SKIP, skip); 173 | } 174 | 175 | while self.nr < count { 176 | self.skip_message_key() 177 | } 178 | } 179 | 180 | fn skip_message_key(&mut self) { 181 | let mk = self.ratchet.next_receiving_key().1; 182 | let n = self.nr; 183 | self.nr += 1; 184 | // TODO: is this unwrap sound? 185 | self.mk_skipped.insert((self.last_peer_key.clone().unwrap(), n), mk); 186 | } 187 | 188 | /// Get the next header and sending chain message key. 189 | pub fn send_key(&mut self) -> (Header, MessageKey) { 190 | let mk = self.ratchet.next_sending_key().1; 191 | let count = self.ns; 192 | self.ns += 1; 193 | let h = Header { 194 | public_key: self.ratchet.public().clone(), 195 | prev_count: self.p_ns, 196 | count, 197 | }; 198 | (h, mk) 199 | } 200 | } 201 | 202 | pub(crate) fn generate_ad(bytes: &[u8], header: &Header) -> Vec { 203 | let mut res = vec![]; 204 | res.extend_from_slice(bytes); 205 | res.extend_from_slice(header.public_key.as_bytes()); 206 | res.extend_from_slice(&slice_word(header.prev_count)[..]); 207 | res.extend_from_slice(&slice_word(header.count)[..]); 208 | res 209 | } 210 | 211 | fn slice_word(word: u32) -> [u8; 4] { 212 | [ 213 | ((word >> 24) & 0xFF) as u8, 214 | ((word >> 16) & 0xFF) as u8, 215 | ((word >> 8) & 0xFF) as u8, 216 | ((word >> 0) & 0xFF) as u8, 217 | ] 218 | } 219 | -------------------------------------------------------------------------------- /crates/double-ratchet/src/tests.rs: -------------------------------------------------------------------------------- 1 | use signal_common::keys::*; 2 | 3 | use super::chains::*; 4 | use super::ratchet::*; 5 | use super::session::*; 6 | 7 | fn get_rk() -> ChainKey { 8 | let key = std::iter::repeat(0x42).take(32).collect::>(); 9 | ChainKey::from(key.as_slice()) 10 | } 11 | 12 | fn get_sk(i: u8) -> SessionKey { 13 | // just pretend this is Diffie-Hellman 14 | let key = &std::iter::repeat(i).take(32).collect::>(); 15 | SessionKey::from(key.as_slice()) 16 | } 17 | 18 | #[test] 19 | fn test_mirror_manual() { 20 | let info = b"foobar!"; 21 | let mut alice = Chains::init(&info[..], get_rk()); 22 | let mut bob = Chains::init(&info[..], get_rk()); 23 | 24 | let first_key = get_sk(1); 25 | alice.next_sending_chain(first_key); 26 | let m1_a = alice.next_sending_key(); 27 | let m2_a = alice.next_sending_key(); 28 | let first_key = get_sk(1); 29 | bob.next_receiving_chain(first_key); 30 | let m1_b = bob.next_receiving_key(); 31 | let m2_b = bob.next_receiving_key(); 32 | 33 | let second_key = get_sk(2); 34 | bob.next_sending_chain(second_key); 35 | let m3_b = bob.next_sending_key(); 36 | let m4_b = bob.next_sending_key(); 37 | let second_key = get_sk(2); 38 | alice.next_receiving_chain(second_key); 39 | let m3_a = alice.next_receiving_key(); 40 | let m4_a = alice.next_receiving_key(); 41 | 42 | assert_eq!(m1_a, m1_b); 43 | assert_eq!(m2_a, m2_b); 44 | assert_eq!(m3_a, m3_b); 45 | assert_eq!(m4_a, m4_b); 46 | 47 | assert_eq!(m1_a.0, 0); 48 | assert_eq!(m2_a.0, 1); 49 | assert_eq!(m3_a.0, 0); 50 | assert_eq!(m4_a.0, 1); 51 | 52 | assert!(m1_a.1 != MessageKey::from(&[0; 32][..])); 53 | assert!(m2_a.1 != MessageKey::from(&[0; 32][..])); 54 | assert!(m3_a.1 != MessageKey::from(&[0; 32][..])); 55 | assert!(m4_a.1 != MessageKey::from(&[0; 32][..])); 56 | 57 | assert!(m1_a.1 != m2_a.1); 58 | assert!(m1_a.1 != m3_a.1); 59 | assert!(m1_a.1 != m4_a.1); 60 | assert!(m2_a.1 != m1_a.1); 61 | assert!(m2_a.1 != m3_a.1); 62 | assert!(m2_a.1 != m4_a.1); 63 | assert!(m3_a.1 != m1_a.1); 64 | assert!(m3_a.1 != m2_a.1); 65 | assert!(m3_a.1 != m4_a.1); 66 | assert!(m4_a.1 != m1_a.1); 67 | assert!(m4_a.1 != m2_a.1); 68 | assert!(m4_a.1 != m3_a.1); 69 | } 70 | 71 | #[test] 72 | fn test_mirror_double_ratchet() { 73 | use rand::OsRng; 74 | 75 | let info = b"foobar!"; 76 | let bob_keys = RatchetKeyPair::generate(&mut OsRng::new().unwrap()); 77 | let mut csprng = OsRng::new().unwrap(); 78 | let mut alice = DoubleRatchet::with_peer( 79 | &info[..], get_rk(), &mut csprng, &bob_keys.public 80 | ); 81 | let mut bob = DoubleRatchet::with_keypair( 82 | &info[..], get_rk(), bob_keys 83 | ); 84 | 85 | let m1_a = alice.next_sending_key(); 86 | let m2_a = alice.next_sending_key(); 87 | bob.ratchet(&mut csprng, alice.public()); 88 | let m1_b = bob.next_receiving_key(); 89 | let m2_b = bob.next_receiving_key(); 90 | 91 | let m3_b = bob.next_sending_key(); 92 | let m4_b = bob.next_sending_key(); 93 | alice.ratchet(&mut csprng, bob.public()); 94 | let m3_a = alice.next_receiving_key(); 95 | let m4_a = alice.next_receiving_key(); 96 | 97 | assert_eq!(m1_a, m1_b); 98 | assert_eq!(m2_a, m2_b); 99 | assert_eq!(m3_a, m3_b); 100 | assert_eq!(m4_a, m4_b); 101 | 102 | assert_eq!(m1_a.0, 0); 103 | assert_eq!(m2_a.0, 1); 104 | assert_eq!(m3_a.0, 0); 105 | assert_eq!(m4_a.0, 1); 106 | 107 | assert!(m1_a.1 != MessageKey::from(&[0; 32][..])); 108 | assert!(m2_a.1 != MessageKey::from(&[0; 32][..])); 109 | assert!(m3_a.1 != MessageKey::from(&[0; 32][..])); 110 | assert!(m4_a.1 != MessageKey::from(&[0; 32][..])); 111 | 112 | assert!(m1_a.1 != m2_a.1); 113 | assert!(m1_a.1 != m3_a.1); 114 | assert!(m1_a.1 != m4_a.1); 115 | assert!(m2_a.1 != m1_a.1); 116 | assert!(m2_a.1 != m3_a.1); 117 | assert!(m2_a.1 != m4_a.1); 118 | assert!(m3_a.1 != m1_a.1); 119 | assert!(m3_a.1 != m2_a.1); 120 | assert!(m3_a.1 != m4_a.1); 121 | assert!(m4_a.1 != m1_a.1); 122 | assert!(m4_a.1 != m2_a.1); 123 | assert!(m4_a.1 != m3_a.1); 124 | } 125 | 126 | #[test] 127 | fn test_mirror_session() { 128 | use rand::OsRng; 129 | 130 | let info = b"foobar!"; 131 | let session = || SessionBuilder::new(&info[..], get_rk()); 132 | let mut csprng = OsRng::new().unwrap(); 133 | 134 | let bob_keys = RatchetKeyPair::generate(&mut csprng); 135 | 136 | let mut alice = session().connect_to(&bob_keys.public, &mut csprng); 137 | let mut bob = session().accept_with(bob_keys); 138 | 139 | let (h1, m1_a) = alice.send_key(); 140 | let (h2, m2_a) = alice.send_key(); 141 | let (h3, m3_a) = alice.send_key(); 142 | let (h4, m4_a) = alice.send_key(); 143 | 144 | // This receive will ratchet Bob's end. 145 | let m1_b = bob.receive_key(h1, &mut csprng); 146 | 147 | let (h5, m5_b) = bob.send_key(); 148 | 149 | // Receiving out-of-order is no problem. 150 | let m3_b = bob.receive_key(h3, &mut csprng); 151 | let m2_b = bob.receive_key(h2, &mut csprng); 152 | 153 | let (h6, m6_b) = bob.send_key(); 154 | 155 | // This receive will ratchet Alice's end. 156 | let m5_a = alice.receive_key(h5, &mut csprng); 157 | 158 | let (h7, m7_a) = alice.send_key(); 159 | let (h8, m8_a) = alice.send_key(); 160 | 161 | let m6_a = alice.receive_key(h6, &mut csprng); 162 | 163 | // This receive will ratchet Bob's end, having missed a few messages. 164 | let m8_b = bob.receive_key(h8, &mut csprng); 165 | 166 | // Let's just receive the others for good measure. 167 | let m7_b = bob.receive_key(h7, &mut csprng); 168 | let m4_b = bob.receive_key(h4, &mut csprng); 169 | 170 | assert_eq!(m1_a, m1_b); 171 | assert_eq!(m2_a, m2_b); 172 | assert_eq!(m3_a, m3_b); 173 | assert_eq!(m4_a, m4_b); 174 | assert_eq!(m5_a, m5_b); 175 | assert_eq!(m6_a, m6_b); 176 | assert_eq!(m7_a, m7_b); 177 | assert_eq!(m8_a, m8_b); 178 | 179 | assert!(m1_a != MessageKey::from(&[0; 32][..])); 180 | assert!(m2_a != MessageKey::from(&[0; 32][..])); 181 | assert!(m3_a != MessageKey::from(&[0; 32][..])); 182 | assert!(m4_a != MessageKey::from(&[0; 32][..])); 183 | assert!(m5_a != MessageKey::from(&[0; 32][..])); 184 | assert!(m6_a != MessageKey::from(&[0; 32][..])); 185 | assert!(m7_a != MessageKey::from(&[0; 32][..])); 186 | assert!(m8_a != MessageKey::from(&[0; 32][..])); 187 | 188 | assert!(m1_a != m2_a); 189 | assert!(m1_a != m3_a); 190 | assert!(m1_a != m4_a); 191 | assert!(m1_a != m5_a); 192 | assert!(m1_a != m6_a); 193 | assert!(m1_a != m7_a); 194 | assert!(m1_a != m8_a); 195 | 196 | assert!(m2_a != m1_a); 197 | assert!(m2_a != m3_a); 198 | assert!(m2_a != m4_a); 199 | assert!(m2_a != m5_a); 200 | assert!(m2_a != m6_a); 201 | assert!(m2_a != m7_a); 202 | assert!(m2_a != m8_a); 203 | 204 | assert!(m3_a != m1_a); 205 | assert!(m3_a != m2_a); 206 | assert!(m3_a != m4_a); 207 | assert!(m3_a != m5_a); 208 | assert!(m3_a != m6_a); 209 | assert!(m3_a != m7_a); 210 | assert!(m3_a != m8_a); 211 | 212 | assert!(m4_a != m1_a); 213 | assert!(m4_a != m2_a); 214 | assert!(m4_a != m3_a); 215 | assert!(m4_a != m5_a); 216 | assert!(m4_a != m6_a); 217 | assert!(m4_a != m7_a); 218 | assert!(m4_a != m8_a); 219 | 220 | assert!(m5_a != m1_a); 221 | assert!(m5_a != m2_a); 222 | assert!(m5_a != m3_a); 223 | assert!(m5_a != m4_a); 224 | assert!(m5_a != m6_a); 225 | assert!(m5_a != m7_a); 226 | assert!(m5_a != m8_a); 227 | 228 | assert!(m6_a != m1_a); 229 | assert!(m6_a != m2_a); 230 | assert!(m6_a != m3_a); 231 | assert!(m6_a != m4_a); 232 | assert!(m6_a != m5_a); 233 | assert!(m6_a != m7_a); 234 | assert!(m6_a != m8_a); 235 | 236 | assert!(m7_a != m1_a); 237 | assert!(m7_a != m2_a); 238 | assert!(m7_a != m3_a); 239 | assert!(m7_a != m4_a); 240 | assert!(m7_a != m5_a); 241 | assert!(m7_a != m6_a); 242 | assert!(m7_a != m8_a); 243 | 244 | assert!(m8_a != m1_a); 245 | assert!(m8_a != m2_a); 246 | assert!(m8_a != m3_a); 247 | assert!(m8_a != m4_a); 248 | assert!(m8_a != m5_a); 249 | assert!(m8_a != m6_a); 250 | assert!(m8_a != m7_a); 251 | } 252 | 253 | #[test] 254 | fn test_unique_sessions() { 255 | use rand::OsRng; 256 | 257 | let info = b"foobar!"; 258 | let session = || SessionBuilder::new(&info[..], get_rk()); 259 | let mut csprng = OsRng::new().unwrap(); 260 | 261 | let bob_keys = RatchetKeyPair::generate(&mut csprng); 262 | 263 | let mut alice = session().connect_to(&bob_keys.public, &mut csprng); 264 | let (_, m1_a) = alice.send_key(); 265 | 266 | let mut alice = session().connect_to(&bob_keys.public, &mut csprng); 267 | let (_, m2_a) = alice.send_key(); 268 | 269 | assert!(m1_a != m2_a); 270 | } 271 | 272 | #[test] 273 | fn test_session_roundtrip_encrypted() { 274 | use rand::OsRng; 275 | 276 | let info = b"foobar!"; 277 | let session = || SessionBuilder::new(&info[..], get_rk()); 278 | let mut csprng = OsRng::new().unwrap(); 279 | 280 | let bob_keys = RatchetKeyPair::generate(&mut csprng); 281 | 282 | let mut alice = session().connect_to(&bob_keys.public, &mut csprng); 283 | let mut bob = session().accept_with(bob_keys); 284 | 285 | let m1_p = b"hello bob!"; 286 | let (h1, m1_c) = alice.send(m1_p); 287 | let m1_r = bob.receive(h1, &m1_c, &mut csprng).unwrap(); 288 | 289 | let m2_p = b"hi alice, how are you?"; 290 | let (h2, m2_c) = bob.send(m2_p); 291 | let m2_r = alice.receive(h2, &m2_c, &mut csprng).unwrap(); 292 | 293 | let m3_p = b"oh and can you pick up milk please?"; 294 | let (h3, m3_c) = bob.send(m3_p); 295 | let m3_r = alice.receive(h3, &m3_c, &mut csprng).unwrap(); 296 | 297 | let m4_p = b"i'd be glad to."; 298 | let (h4, m4_c) = alice.send(m4_p); 299 | let m4_r = bob.receive(h4, &m4_c, &mut csprng).unwrap(); 300 | 301 | assert_eq!(&m1_p[..], &m1_r[..]); 302 | assert_eq!(&m2_p[..], &m2_r[..]); 303 | assert_eq!(&m3_p[..], &m3_r[..]); 304 | assert_eq!(&m4_p[..], &m4_r[..]); 305 | } 306 | 307 | #[test] 308 | fn test_authenticated_encryption() { 309 | use rand::OsRng; 310 | 311 | use session::generate_ad; 312 | use encrypt::AeadCipher; 313 | 314 | let info = b"foobar!"; 315 | let session = || SessionBuilder::new(&info[..], get_rk()); 316 | let mut csprng = OsRng::new().unwrap(); 317 | 318 | let bob_keys = RatchetKeyPair::generate(&mut csprng); 319 | 320 | let mut alice = session().connect_to(&bob_keys.public, &mut csprng); 321 | let mut bob = session().accept_with(bob_keys); 322 | 323 | let m1_p = b"hello bob!"; 324 | let (h1, m1_c) = alice.send(m1_p); 325 | 326 | let mut bad_header = h1.clone(); 327 | bad_header.count += 1; 328 | 329 | let key = bob.receive_key(h1, &mut csprng); 330 | let ad = generate_ad(&b"ad"[..], &bad_header); 331 | let cipher = AeadCipher::new(&b"another info"[..], key, &ad); 332 | let res = cipher.decrypt(&m1_c); 333 | 334 | assert!(res.is_err()); 335 | } 336 | -------------------------------------------------------------------------------- /crates/double-ratchet/src/util.rs: -------------------------------------------------------------------------------- 1 | //! Various cryptographic utility functions. 2 | //! 3 | //! These simply wrap the actual implementations, locking 4 | //! in parameters and exposing an interface focused on their 5 | //! use in this project. You are recommended to use the 6 | //! underlying functions themselves. 7 | 8 | use signal_common::error::{Error, Result}; 9 | 10 | pub fn hkdf( 11 | salt: &[u8], 12 | input: &[u8], 13 | info: &[u8], 14 | ) -> [u8; 64] { 15 | use orion::hazardous::kdf::hkdf; 16 | 17 | let mut key = [0; 64]; 18 | 19 | // TODO: not unwrap! 20 | hkdf::derive_key(&salt, &input, &info, &mut key).unwrap(); 21 | 22 | key 23 | } 24 | 25 | pub fn hkdf_sha512( 26 | salt: &[u8], 27 | input: &[u8], 28 | info: &[u8], 29 | key: &mut [u8], 30 | ) { 31 | use orion::hazardous::kdf::hkdf; 32 | 33 | // TODO: not unwrap! 34 | hkdf::derive_key(salt, input, info, key).unwrap(); 35 | } 36 | 37 | pub fn hmac( 38 | key: &[u8], 39 | input: &[u8], 40 | ) -> [u8; 32] { 41 | use orion::hazardous::mac::hmac; 42 | 43 | let mut mac = hmac::init(key); 44 | 45 | // TODO: not unwrap! 46 | mac.update(input).unwrap(); 47 | 48 | // TODO: not unwrap! 49 | let hash = mac.finalize().unwrap(); 50 | 51 | let mut res = [0; 32]; 52 | for i in 0..32 { 53 | res[i] = hash[i]; 54 | } 55 | 56 | res 57 | } 58 | 59 | pub fn hmac_sha512( 60 | key: &[u8], 61 | input: &[u8], 62 | ) -> [u8; 64] { 63 | use orion::hazardous::mac::hmac; 64 | 65 | let mut mac = hmac::init(key); 66 | 67 | // TODO: not unwrap! 68 | mac.update(input).unwrap(); 69 | 70 | // TODO: not unwrap! 71 | mac.finalize().unwrap() 72 | } 73 | 74 | pub fn hmac_sha512_two_slices( 75 | key: &[u8], 76 | input1: &[u8], 77 | input2: &[u8], 78 | ) -> [u8; 64] { 79 | use orion::hazardous::mac::hmac; 80 | 81 | let mut mac = hmac::init(key); 82 | 83 | // TODO: not unwrap! 84 | mac.update(input1).unwrap(); 85 | 86 | // TODO: not unwrap! 87 | mac.update(input2).unwrap(); 88 | 89 | // TODO: not unwrap! 90 | mac.finalize().unwrap() 91 | } 92 | 93 | pub fn aes256_cbc_pkcs7_encrypt<'a>( 94 | key: &[u8], 95 | iv: &[u8], 96 | buffer: &'a mut [u8], 97 | len: usize, 98 | ) -> &'a [u8] { 99 | use aes::Aes256; 100 | use aes::block_cipher_trait::generic_array::GenericArray; 101 | use block_modes::{BlockMode, BlockModeIv, Cbc}; 102 | use block_modes::block_padding::Pkcs7; 103 | 104 | type Aes256Cbc = Cbc; 105 | 106 | //let key = GenericArray::from_slice(key); 107 | let iv = GenericArray::from_slice(iv); 108 | let cipher = Aes256Cbc::new_varkey(key, iv).unwrap(); 109 | 110 | // TODO: not unwrap! 111 | cipher.encrypt_pad(buffer, len).unwrap() 112 | } 113 | 114 | pub fn aes256_cbc_pkcs7_decrypt<'a>( 115 | key: &[u8], 116 | iv: &[u8], 117 | buffer: &'a mut [u8], 118 | ) -> Result<&'a [u8]> { 119 | use aes::Aes256; 120 | use aes::block_cipher_trait::generic_array::GenericArray; 121 | use block_modes::{BlockMode, BlockModeIv, Cbc}; 122 | use block_modes::block_padding::Pkcs7; 123 | 124 | type Aes256Cbc = Cbc; 125 | 126 | //let key = GenericArray::from_slice(key); 127 | let iv = GenericArray::from_slice(iv); 128 | let cipher = Aes256Cbc::new_varkey(key, iv).unwrap(); 129 | 130 | cipher.decrypt_pad(buffer).map_err(|_| Error) 131 | } 132 | -------------------------------------------------------------------------------- /crates/server/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "server" 3 | version = "0.1.0" 4 | authors = ["Andrew Dona-Couch "] 5 | 6 | [dependencies] 7 | capnp = "0.8" 8 | capnp-rpc = "0.8" 9 | futures = "0.1" 10 | tokio = "0.1" 11 | 12 | signal-common = { path = "../signal-common" } 13 | x3dh = { path = "../x3dh" } 14 | 15 | [build-dependencies] 16 | capnpc = "0.8" 17 | -------------------------------------------------------------------------------- /crates/server/build.rs: -------------------------------------------------------------------------------- 1 | extern crate capnpc; 2 | 3 | fn main() { 4 | ::capnpc::CompilerCommand::new() 5 | .src_prefix("../../schema/") 6 | .file("../../schema/keyserver.capnp") 7 | .file("../../schema/util.capnp") 8 | .run() 9 | .unwrap(); 10 | } 11 | -------------------------------------------------------------------------------- /crates/server/src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate capnp; 2 | extern crate capnp_rpc; 3 | extern crate futures; 4 | extern crate tokio; 5 | 6 | extern crate signal_common; 7 | extern crate x3dh; 8 | 9 | use capnp_rpc::{RpcSystem, twoparty, rpc_twoparty_capnp}; 10 | use futures::{Future, Stream}; 11 | use tokio::io::AsyncRead; 12 | use tokio::runtime::current_thread; 13 | 14 | use signal_common::keys::{ 15 | IdentityKeyPublic, 16 | OneTimePrekeyPublic, 17 | SignedPrekeyPublic, 18 | Signature, 19 | }; 20 | use x3dh::keyserver; 21 | 22 | pub mod keyserver_capnp { 23 | include!(concat!(env!("OUT_DIR"), "/keyserver_capnp.rs")); 24 | } 25 | 26 | pub mod util_capnp { 27 | include!(concat!(env!("OUT_DIR"), "/util_capnp.rs")); 28 | } 29 | 30 | struct Keyserver { 31 | server: keyserver::Keyserver, 32 | } 33 | 34 | impl Keyserver { 35 | fn new() -> Keyserver { 36 | Keyserver { server: keyserver::Keyserver::new() } 37 | } 38 | } 39 | 40 | impl keyserver_capnp::keyserver::Server for Keyserver { 41 | fn prekey_bundle( 42 | &mut self, 43 | params: keyserver_capnp::keyserver::PrekeyBundleParams, 44 | mut result: keyserver_capnp::keyserver::PrekeyBundleResults, 45 | ) -> capnp::capability::Promise<(), capnp::Error> { 46 | println!("server: Responding to prekeyBundle"); 47 | 48 | let params = match params.get() { 49 | Ok(ps) => ps, 50 | _ => return capnp::capability::Promise::err( 51 | capnp::Error::failed(format!("Unable to get params?")) 52 | ), 53 | }; 54 | let ik = match params.get_ik() { 55 | Ok(ik) if ik.len() == 32 => { 56 | let mut arr = [0; 32]; 57 | arr.copy_from_slice(ik); 58 | match IdentityKeyPublic::from_bytes(arr) { 59 | Ok(ik) => ik, 60 | _ => return capnp::capability::Promise::err( 61 | capnp::Error::failed(format!("Unable to get IK.")) 62 | ), 63 | } 64 | }, 65 | _ => return capnp::capability::Promise::err( 66 | capnp::Error::failed(format!("Unable to get IK.")) 67 | ), 68 | }; 69 | match self.server.prekey_bundle(&ik) { 70 | Some(bundle) => { 71 | let mut b = result.get().init_bundle(); 72 | 73 | b.set_ik(bundle.ik.as_bytes()); 74 | 75 | { 76 | let mut s = b.reborrow().init_spk(); 77 | 78 | s.set_key(bundle.spk.as_bytes()).unwrap(); 79 | s.set_sig(&bundle.spk_sig.to_bytes()); 80 | } 81 | 82 | match bundle.opk { 83 | None => b.init_opk().set_none(()), 84 | Some(opk) => { 85 | let mut o = b.init_opk().init_some(); 86 | 87 | o.set_id(opk.index()); 88 | o.set_key(opk.as_bytes()); 89 | }, 90 | } 91 | 92 | capnp::capability::Promise::ok(()) 93 | }, 94 | None => capnp::capability::Promise::err( 95 | capnp::Error::failed(format!("Identity unknown.")) 96 | ), 97 | } 98 | } 99 | 100 | fn update_identity( 101 | &mut self, 102 | params: keyserver_capnp::keyserver::UpdateIdentityParams, 103 | _: keyserver_capnp::keyserver::UpdateIdentityResults, 104 | ) -> capnp::capability::Promise<(), capnp::Error> { 105 | println!("server: Responding to updateIdentity"); 106 | 107 | let params = match params.get() { 108 | Ok(ps) => ps, 109 | _ => return capnp::capability::Promise::err( 110 | capnp::Error::failed(format!("Unable to get params?")) 111 | ), 112 | }; 113 | let ik = match params.get_ik() { 114 | Ok(k) if k.len() == 32 => { 115 | let mut arr = [0; 32]; 116 | arr.copy_from_slice(k); 117 | match IdentityKeyPublic::from_bytes(arr) { 118 | Ok(ik) => ik, 119 | _ => return capnp::capability::Promise::err( 120 | capnp::Error::failed(format!("Unable to get IK.")) 121 | ), 122 | } 123 | }, 124 | _ => return capnp::capability::Promise::err( 125 | capnp::Error::failed(format!("Unable to get IK.")) 126 | ), 127 | }; 128 | let (spk, spk_sig) = match params.get_spk() { 129 | Ok(k) => { 130 | let key = match k.get_key() { 131 | Ok(k) => k, 132 | _ => return capnp::capability::Promise::err( 133 | capnp::Error::failed(format!("Unable to get SPK.")) 134 | ), 135 | }; 136 | let sig = match k.get_sig() { 137 | Ok(k) => k, 138 | _ => return capnp::capability::Promise::err( 139 | capnp::Error::failed(format!("Unable to get SPK sig.")) 140 | ), 141 | }; 142 | if key.len() == 32 && sig.len() == 64 { 143 | let mut karr = [0; 32]; 144 | karr.copy_from_slice(key); 145 | let mut sarr = [0; 64]; 146 | sarr.copy_from_slice(sig); 147 | let spk = match SignedPrekeyPublic::from_bytes(karr) { 148 | Ok(spk) => spk, 149 | _ => return capnp::capability::Promise::err( 150 | capnp::Error::failed(format!("Unable to get SPK.")) 151 | ), 152 | }; 153 | let spk_sig = match Signature::from_bytes(sarr) { 154 | Ok(spk_sig) => spk_sig, 155 | _ => return capnp::capability::Promise::err( 156 | capnp::Error::failed(format!("Unable to get SPK sig.")) 157 | ), 158 | }; 159 | (spk, spk_sig) 160 | } else { 161 | return capnp::capability::Promise::err( 162 | capnp::Error::failed(format!("Unable to get SPK.")) 163 | ); 164 | } 165 | }, 166 | _ => return capnp::capability::Promise::err( 167 | capnp::Error::failed(format!("Unable to get SPK.")) 168 | ), 169 | }; 170 | 171 | self.server.update_identity(&ik, &spk, &spk_sig).unwrap(); 172 | 173 | capnp::capability::Promise::ok(()) 174 | } 175 | 176 | fn add_opks( 177 | &mut self, 178 | params: keyserver_capnp::keyserver::AddOpksParams, 179 | _: keyserver_capnp::keyserver::AddOpksResults, 180 | ) -> capnp::capability::Promise<(), capnp::Error> { 181 | println!("server: Responding to addOpks"); 182 | 183 | let params = match params.get() { 184 | Ok(ps) => ps, 185 | _ => return capnp::capability::Promise::err( 186 | capnp::Error::failed(format!("Unable to get params?")) 187 | ), 188 | }; 189 | let ik = match params.get_ik() { 190 | Ok(ik) if ik.len() == 32 => { 191 | let mut arr = [0; 32]; 192 | arr.copy_from_slice(ik); 193 | match IdentityKeyPublic::from_bytes(arr) { 194 | Ok(ik) => ik, 195 | _ => return capnp::capability::Promise::err( 196 | capnp::Error::failed(format!("Unable to get IK.")) 197 | ), 198 | } 199 | }, 200 | _ => return capnp::capability::Promise::err( 201 | capnp::Error::failed(format!("Unable to get IK.")) 202 | ), 203 | }; 204 | let opks = match params.get_opks() { 205 | Ok(os) => os, 206 | _ => return capnp::capability::Promise::err( 207 | capnp::Error::failed(format!("Unable to get OPKS.")) 208 | ), 209 | }; 210 | for i in 0..opks.len() { 211 | let signed_opk = opks.get(i); 212 | 213 | let opk = match signed_opk.get_key() { 214 | Ok(k) => k, 215 | _ => return capnp::capability::Promise::err( 216 | capnp::Error::failed(format!("Unable to get OPK.")) 217 | ), 218 | }; 219 | let sig = match signed_opk.get_sig() { 220 | Ok(k) => k, 221 | _ => return capnp::capability::Promise::err( 222 | capnp::Error::failed(format!("Unable to get OPK sig.")) 223 | ), 224 | }; 225 | 226 | let id = opk.get_id(); 227 | let key = match opk.get_key() { 228 | Ok(k) => k, 229 | _ => return capnp::capability::Promise::err( 230 | capnp::Error::failed(format!("Unable to get OPK key.")) 231 | ), 232 | }; 233 | 234 | let mut new_key = [0; 32]; 235 | new_key.copy_from_slice(key); 236 | let opk = OneTimePrekeyPublic::from_bytes(id, new_key).unwrap(); 237 | 238 | let mut new_sig = [0; 64]; 239 | new_sig.copy_from_slice(sig); 240 | let opk_sig = match Signature::from_bytes(new_sig) { 241 | Ok(opk_sig) => opk_sig, 242 | _ => return capnp::capability::Promise::err( 243 | capnp::Error::failed(format!("Unable to get OPK sig.")) 244 | ), 245 | }; 246 | self.server.add_opk(&ik, &opk, &opk_sig).unwrap(); 247 | } 248 | capnp::capability::Promise::ok(()) 249 | } 250 | } 251 | 252 | pub fn main() { 253 | use std::net::ToSocketAddrs; 254 | let args: Vec = ::std::env::args().collect(); 255 | if args.len() != 2 { 256 | eprintln!("usage: {} ADDRESS[:PORT]", args[0]); 257 | return; 258 | } 259 | 260 | let addr = args[1].to_socket_addrs().unwrap().next().expect("could not parse address"); 261 | let socket = ::tokio::net::TcpListener::bind(&addr).unwrap(); 262 | 263 | let server = 264 | keyserver_capnp::keyserver::ToClient::new(Keyserver::new()).from_server::<::capnp_rpc::Server>(); 265 | 266 | let done = socket.incoming().for_each(move |socket| { 267 | println!("server: Accepting incoming connection."); 268 | 269 | socket.set_nodelay(true)?; 270 | let (reader, writer) = socket.split(); 271 | 272 | let network = 273 | twoparty::VatNetwork::new(reader, writer, 274 | rpc_twoparty_capnp::Side::Server, Default::default()); 275 | 276 | let rpc_system = RpcSystem::new(Box::new(network), Some(server.clone().client)); 277 | current_thread::spawn(rpc_system.map_err(|e| eprintln!("server error: {:?}", e))); 278 | Ok(()) 279 | }); 280 | 281 | println!("server: Listening on {}...", args[1]); 282 | 283 | current_thread::block_on_all(done).unwrap(); 284 | } 285 | -------------------------------------------------------------------------------- /crates/signal-common/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "signal-common" 3 | version = "0.1.0" 4 | authors = ["Andrew Dona-Couch "] 5 | 6 | [dependencies] 7 | curve25519-dalek = "0.21.0" 8 | ed25519-dalek = "0.8.1" 9 | rand = "0.5.5" 10 | sha2 = "0.7" 11 | 12 | [dependencies.x25519-dalek] 13 | version = "0.3.0" 14 | default-features = false 15 | features = ["std", "u64_backend"] 16 | -------------------------------------------------------------------------------- /crates/signal-common/src/convert.rs: -------------------------------------------------------------------------------- 1 | //! Convert Ed25519 keys to X25519. 2 | //! 3 | //! These functions are for internal use. You are recommended to use 4 | //! the comparable ones in the dalek crates instead. 5 | 6 | // TODO: can we make these really public or gone? 7 | 8 | use ed25519_dalek::Keypair; 9 | use curve25519_dalek::edwards::CompressedEdwardsY; 10 | use curve25519_dalek::montgomery::MontgomeryPoint; 11 | use sha2::{Digest, Sha512}; 12 | 13 | use crate::error::{Error, Result}; 14 | 15 | pub struct SecretKey(pub(crate) [u8; 32]); 16 | 17 | impl SecretKey { 18 | pub fn from_bytes(bytes: [u8; 32]) -> SecretKey { 19 | SecretKey(bytes) 20 | } 21 | 22 | pub fn as_bytes(&self) -> &[u8; 32] { 23 | &self.0 24 | } 25 | } 26 | 27 | pub struct X25519Keypair { 28 | pub public: MontgomeryPoint, 29 | pub secret: SecretKey, 30 | } 31 | 32 | pub fn convert_public_key( 33 | pk: &[u8; 32], 34 | ) -> Result { 35 | let ed25519_pk_c = CompressedEdwardsY::from_slice(pk); 36 | let ed25519_pk = match ed25519_pk_c.decompress() { 37 | Some(p) => p, 38 | None => return Err(Error), 39 | }; 40 | Ok(ed25519_pk.to_montgomery()) 41 | } 42 | 43 | pub fn convert_secret_key( 44 | sk: &ed25519_dalek::SecretKey, 45 | ) -> Result { 46 | let mut hasher = Sha512::new(); 47 | hasher.input(sk.as_bytes()); 48 | let hash = hasher.result(); 49 | 50 | let mut x25519_sk = [0; 32]; 51 | 52 | for i in 0..32 { 53 | x25519_sk[i] = hash[i]; 54 | } 55 | 56 | x25519_sk[0] &= 248; 57 | x25519_sk[31] &= 127; 58 | x25519_sk[31] |= 64; 59 | 60 | Ok(SecretKey(x25519_sk)) 61 | } 62 | 63 | pub fn convert_ed25519_to_x25519(ed25519: &Keypair) -> Result { 64 | let public = convert_public_key(&ed25519.public.as_bytes())?; 65 | let secret = convert_secret_key(&ed25519.secret)?; 66 | 67 | Ok(X25519Keypair { secret, public }) 68 | } 69 | 70 | #[cfg(test)] 71 | mod tests { 72 | use rand::OsRng; 73 | use sha2::Sha512; 74 | use ed25519_dalek::Keypair; 75 | use x25519_dalek::generate_public; 76 | 77 | use super::*; 78 | 79 | #[test] 80 | fn test_convert_isomorphism() { 81 | let mut csprng = OsRng::new().unwrap(); 82 | 83 | let pair = Keypair::generate::(&mut csprng); 84 | let conv = convert_ed25519_to_x25519(&pair).unwrap(); 85 | 86 | let regen_pub = generate_public(&conv.secret.as_bytes()); 87 | 88 | let conv_len = conv.public.as_bytes().len(); 89 | let regen_len = regen_pub.as_bytes().len(); 90 | 91 | assert_eq!(conv_len, regen_len); 92 | for i in 0..conv_len { 93 | assert_eq!(conv.public.as_bytes()[i], regen_pub.as_bytes()[i]); 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /crates/signal-common/src/error.rs: -------------------------------------------------------------------------------- 1 | //! Error and result types. 2 | //! 3 | //! Configuration errors return specific information to help 4 | //! the developer to build correct code. All runtime errors 5 | //! return the generic empty `Error` struct, because granular 6 | //! error reporting in cryptographic libraries can be fraught. 7 | 8 | use std::fmt; 9 | 10 | /// A runtime error in the signal protocol. 11 | #[derive(Clone, PartialEq, Eq)] 12 | pub struct Error; 13 | 14 | impl fmt::Debug for Error { 15 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 16 | write!(f, "crypto error in Signal protocol") 17 | } 18 | } 19 | 20 | impl fmt::Display for Error { 21 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 22 | ::fmt(self, f) 23 | } 24 | } 25 | 26 | impl std::error::Error for Error {} 27 | 28 | impl From for Error { 29 | fn from(_: ed25519_dalek::SignatureError) -> Error { Error } 30 | } 31 | 32 | impl From<()> for Error { 33 | fn from(_: ()) -> Error { Error } 34 | } 35 | 36 | /// A helful alias for a `Result` with our `Error`. 37 | pub type Result = std::result::Result; 38 | 39 | /// Errors that arise from parameter configuration. 40 | #[derive(Clone, PartialEq, Eq)] 41 | pub enum ParameterError { 42 | /// The curve specified is unsupported by this implementation. 43 | UnsupportedCurve, 44 | /// The supplied info value is not valid ASCII. 45 | InvalidAscii, 46 | } 47 | 48 | impl std::fmt::Debug for ParameterError { 49 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 50 | match self { 51 | ParameterError::UnsupportedCurve=> write!(f, "CURVE X448 is not supported."), 52 | ParameterError::InvalidAscii => write!(f, "INFO must be ASCII."), 53 | } 54 | } 55 | } 56 | 57 | impl fmt::Display for ParameterError { 58 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 59 | ::fmt(self, f) 60 | } 61 | } 62 | 63 | impl std::error::Error for ParameterError {} 64 | 65 | /// Errors that arise during parameter configuration when using 66 | /// the operating system's random number generator. 67 | pub enum WithOsRngError { 68 | /// A parameter configuration error. 69 | Parameter(ParameterError), 70 | /// An error getting the OS random number generator. 71 | OsRng(rand::Error), 72 | } 73 | 74 | impl std::fmt::Debug for WithOsRngError { 75 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 76 | match self { 77 | WithOsRngError::Parameter(p) => p.fmt(f), 78 | WithOsRngError::OsRng(r) => r.fmt(f), 79 | } 80 | } 81 | } 82 | 83 | impl fmt::Display for WithOsRngError { 84 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 85 | ::fmt(self, f) 86 | } 87 | } 88 | 89 | impl std::error::Error for WithOsRngError {} 90 | 91 | impl From for WithOsRngError { 92 | fn from(err: ParameterError) -> WithOsRngError { 93 | WithOsRngError::Parameter(err) 94 | } 95 | } 96 | 97 | impl From for WithOsRngError { 98 | fn from(err: rand::Error) -> WithOsRngError { 99 | WithOsRngError::OsRng(err) 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /crates/signal-common/src/keys.rs: -------------------------------------------------------------------------------- 1 | //! Key types used by the signal protocol. 2 | //! 3 | //! We use a number of specific type wrappers to differentiate 4 | //! between the many different keys we need to manage over the 5 | //! course of the key exchange and double ratchet. These 6 | //! semantic keys ultimately wrap either a signing key or a 7 | //! key exchange key, but it's easier to keep them all straight 8 | //! this way. 9 | 10 | use std::hash::{Hash, Hasher}; 11 | 12 | use curve25519_dalek::montgomery::MontgomeryPoint; 13 | use rand::{CryptoRng, RngCore}; 14 | 15 | use crate::error::{Error, Result}; 16 | 17 | /// A free signature of some bytes. 18 | /// 19 | /// The only signing key in this scheme is the identity key of 20 | /// X3DH. The standard specifies a process of signing the 21 | /// signed prekey, and the extensions here also sign the one-time 22 | /// prekey to prove validity. So this signature is from either 23 | /// an SPK or an OPK, and was signed by an IK. 24 | // TODO: elsewhere? 25 | #[derive(Clone)] 26 | pub struct Signature(ed25519_dalek::Signature); 27 | 28 | impl Signature { 29 | /// Deserialize the provided bytes into a signature. 30 | pub fn from_bytes(bytes: [u8; 64]) -> Result { 31 | Ok(Signature(ed25519_dalek::Signature::from_bytes(&bytes)?)) 32 | } 33 | 34 | /// Serialize this signature into a byte array. 35 | pub fn to_bytes(&self) -> [u8; 64] { 36 | self.0.to_bytes() 37 | } 38 | 39 | /// Get a reference to the underlying dalek signature. 40 | pub fn as_dalek(&self) -> &ed25519_dalek::Signature { 41 | &self.0 42 | } 43 | } 44 | 45 | impl From for Signature { 46 | fn from(sig: ed25519_dalek::Signature) -> Signature { 47 | Signature(sig) 48 | } 49 | } 50 | 51 | /// The result of performing a Diffie-Hellman key exchange. 52 | /// 53 | /// Some key material. In X3DH we combine four (or sometimes 54 | /// three) pieces of key material obtained from independent 55 | /// Diffie-Hellman exchanges, and then use the result as the 56 | /// root key for a Double Ratchet chain. 57 | #[derive(Debug, PartialEq, Eq)] 58 | pub struct KeyMaterial([u8; 32]); 59 | 60 | impl KeyMaterial { 61 | pub fn as_bytes(&self) -> &[u8] { 62 | &self.0 63 | } 64 | } 65 | 66 | impl From<[u8; 32]> for KeyMaterial { 67 | fn from(bytes: [u8; 32]) -> KeyMaterial { KeyMaterial(bytes) } 68 | } 69 | 70 | /// A prekey bundle for a peer, fetched from the keyserver. 71 | pub struct PrekeyBundle { 72 | pub ik: IdentityKeyPublic, 73 | pub spk: SignedPrekeyPublic, 74 | pub spk_sig: Signature, 75 | pub opk: Option, 76 | } 77 | 78 | /// A key that can participate in Diffie-Hellman. 79 | pub trait PublicKey { 80 | fn key(&self) -> &Ed25519KeyPublic; 81 | } 82 | 83 | /// An identity pair for a user. 84 | /// 85 | /// This is used in X3DH to sign other keys to prove authenticity, 86 | /// as well as incorporated into Diffie-Hellman exchanges to 87 | /// lock the derived session key to the parties. 88 | pub struct IdentityKeyPair(Ed25519KeyPair); 89 | 90 | /// The public half of a user's identity key pair. 91 | /// 92 | /// In X3DH this is used to identify users publicly. It is used to 93 | /// request a specific peer's prekey bundle from the keyserver, and 94 | /// to validate out-of-band that the conversation is with the right 95 | /// peer. 96 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] 97 | pub struct IdentityKeyPublic(Ed25519KeyPublic); 98 | 99 | impl IdentityKeyPair { 100 | /// Generate a new identity key pair with the provided source of randomness. 101 | pub fn generate(csprng: &mut R) -> IdentityKeyPair { 102 | IdentityKeyPair(Ed25519KeyPair::generate(csprng)) 103 | } 104 | 105 | /// Get the public half of this identity key. 106 | pub fn public(&self) -> IdentityKeyPublic { 107 | IdentityKeyPublic(self.0.public.clone()) 108 | } 109 | 110 | /// Sign a message using this identity key. 111 | pub fn sign(&self, msg: &[u8]) -> Signature { 112 | self.0.sign(msg) 113 | } 114 | 115 | /// Perform a Diffie-Hellman key exchange with the provided public key. 116 | pub fn diffie_hellman(&self, pk: &K) -> Result { 117 | self.0.diffie_hellman(pk.key()) 118 | } 119 | } 120 | 121 | impl IdentityKeyPublic { 122 | /// Deserialize the provided bytes into an identity key. 123 | pub fn from_bytes(bytes: [u8; 32]) -> Result { 124 | Ok(IdentityKeyPublic(Ed25519KeyPublic::from_bytes(bytes)?)) 125 | } 126 | 127 | /// Serialize this identity key into a byte array. 128 | pub fn to_bytes(&self) -> [u8; 32] { 129 | self.0.to_bytes() 130 | } 131 | 132 | /// Get a view of this identity key as bytes. 133 | pub fn as_bytes(&self) -> &[u8] { 134 | self.0.as_bytes() 135 | } 136 | 137 | /// Verify a message signed by this identity key. 138 | pub fn verify(&self, msg: &[u8], sig: &Signature) -> Result<()> { 139 | self.0.verify(msg, sig) 140 | } 141 | } 142 | 143 | impl PublicKey for IdentityKeyPublic { 144 | fn key(&self) -> &Ed25519KeyPublic { &self.0 } 145 | } 146 | 147 | /// The signed prekey pair for a user. 148 | /// 149 | /// In X3DH this provides long-lived, non-identity keying material 150 | /// for a user. It is always accompanied by a signature proving 151 | /// that the holder of the corresponding identity key was in fact 152 | /// the issuer of this signed prekey. 153 | pub struct SignedPrekeyPair(Ed25519KeyPair); 154 | 155 | /// The public half of a signed prekey. 156 | /// 157 | /// This half is shared via the keyserver and distributed as a part 158 | /// of a user's prekey bundle. 159 | #[derive(Debug, Clone, PartialEq, Eq)] 160 | pub struct SignedPrekeyPublic(Ed25519KeyPublic); 161 | 162 | impl SignedPrekeyPair { 163 | /// Generate a new signed prekey pair with the provided source of randomness. 164 | pub fn generate(csprng: &mut R) -> SignedPrekeyPair { 165 | SignedPrekeyPair(Ed25519KeyPair::generate(csprng)) 166 | } 167 | 168 | /// Get the public half of this signed prekey. 169 | pub fn public(&self) -> SignedPrekeyPublic { 170 | SignedPrekeyPublic(self.0.public.clone()) 171 | } 172 | 173 | /// Perform a Diffie-Hellman key exchange with the provided public key. 174 | pub fn diffie_hellman(&self, pk: &K) -> Result { 175 | self.0.diffie_hellman(pk.key()) 176 | } 177 | } 178 | 179 | impl SignedPrekeyPublic { 180 | /// Deserialize the provided bytes into a signed prekey. 181 | pub fn from_bytes(bytes: [u8; 32]) -> Result { 182 | Ok(SignedPrekeyPublic(Ed25519KeyPublic::from_bytes(bytes)?)) 183 | } 184 | 185 | /// Serialize this signed prekey into a byte array. 186 | pub fn to_bytes(&self) -> [u8; 32] { 187 | self.0.to_bytes() 188 | } 189 | 190 | /// Get a view of this signed prekey as bytes. 191 | pub fn as_bytes(&self) -> &[u8] { 192 | self.0.as_bytes() 193 | } 194 | } 195 | 196 | impl PublicKey for SignedPrekeyPublic { 197 | fn key(&self) -> &Ed25519KeyPublic { &self.0 } 198 | } 199 | 200 | /// A one-time prekey pair. 201 | /// 202 | /// One-time prekeys provide forward secrecy from the very first 203 | /// message sent. By preregistering these one-time keys, a user is 204 | /// able to participate in an asynchronous Diffie-Hellman exchange 205 | /// that uses one-time keying material on both sides, ensuring 206 | /// forward secrecy. 207 | pub struct OneTimePrekeyPair(u64, Ed25519KeyPair); 208 | 209 | /// The public half of a one-time prekey. 210 | /// 211 | /// This half is shared via the keyserver and distributed as a part 212 | /// of a user's prekey bundle. 213 | #[derive(Debug, Clone, PartialEq, Eq)] 214 | pub struct OneTimePrekeyPublic(u64, Ed25519KeyPublic); 215 | 216 | impl OneTimePrekeyPair { 217 | /// Generate a new one-time prekey pair with the provided source of randomness. 218 | pub fn generate(csprng: &mut R, index: u64) -> OneTimePrekeyPair 219 | where R: CryptoRng + RngCore { 220 | OneTimePrekeyPair(index, Ed25519KeyPair::generate(csprng)) 221 | } 222 | 223 | /// Get the public half of this one-time prekey. 224 | pub fn public(&self) -> OneTimePrekeyPublic { 225 | OneTimePrekeyPublic(self.0, self.1.public.clone()) 226 | } 227 | 228 | /// Perform a Diffie-Hellman key exchange with the provided public key. 229 | pub fn diffie_hellman(&self, pk: &K) -> Result { 230 | self.1.diffie_hellman(pk.key()) 231 | } 232 | } 233 | 234 | impl OneTimePrekeyPublic { 235 | /// Deserialize the provided bytes into a one-time prekey. 236 | pub fn from_bytes( 237 | index: u64, 238 | bytes: [u8; 32], 239 | ) -> Result { 240 | Ok(OneTimePrekeyPublic(index, Ed25519KeyPublic::from_bytes(bytes)?)) 241 | } 242 | 243 | /// Get the key index of this one-time prekey. 244 | pub fn index(&self) -> u64 { 245 | self.0 246 | } 247 | 248 | /// Serialize this one-time prekey into a byte array. 249 | pub fn to_bytes(&self) -> [u8; 32] { 250 | self.1.to_bytes() 251 | } 252 | 253 | /// Get a view of this one-time prekey as bytes. 254 | pub fn as_bytes(&self) -> &[u8] { 255 | self.1.as_bytes() 256 | } 257 | } 258 | 259 | impl PublicKey for OneTimePrekeyPublic { 260 | fn key(&self) -> &Ed25519KeyPublic { &self.1 } 261 | } 262 | 263 | /// An ephemeral key generated for the X3DH exchange. 264 | /// 265 | /// This key is the initiating party's contribution of one-time 266 | /// keying material. It is used to perform Diffie-Hellman exchanges 267 | /// with all three (two) of the receiving party's keys. 268 | pub struct EphemeralKeyPair(Ed25519KeyPair); 269 | 270 | /// The public half of the ephemeral key. 271 | /// 272 | /// This half is transmitted as part of the initial message 273 | /// after X3DH, to allow the receiving party to complete the exchange. 274 | #[derive(Clone)] 275 | pub struct EphemeralKeyPublic(Ed25519KeyPublic); 276 | 277 | impl EphemeralKeyPair { 278 | /// Generate a new ephemeral key pair with the provided source of randomness. 279 | pub fn generate(csprng: &mut R) -> EphemeralKeyPair { 280 | EphemeralKeyPair(Ed25519KeyPair::generate(csprng)) 281 | } 282 | 283 | /// Get the public half of this ephemeral key. 284 | pub fn public(&self) -> EphemeralKeyPublic { 285 | EphemeralKeyPublic(self.0.public.clone()) 286 | } 287 | 288 | /// Perform a Diffie-Hellman key exchange with the provided public key. 289 | pub fn diffie_hellman(&self, pk: &K) -> Result { 290 | self.0.diffie_hellman(pk.key()) 291 | } 292 | } 293 | 294 | impl EphemeralKeyPublic { 295 | /// Deserialize the provided bytes into a ephemeral key. 296 | pub fn from_bytes(bytes: [u8; 32]) -> Result { 297 | Ok(EphemeralKeyPublic(Ed25519KeyPublic::from_bytes(bytes)?)) 298 | } 299 | 300 | /// Serialize this ephemeral key into a byte array. 301 | pub fn to_bytes(&self) -> [u8; 32] { 302 | self.0.to_bytes() 303 | } 304 | 305 | /// Get a view of this ephemeral key as bytes. 306 | pub fn as_bytes(&self) -> &[u8] { 307 | self.0.as_bytes() 308 | } 309 | } 310 | 311 | impl PublicKey for EphemeralKeyPublic { 312 | fn key(&self) -> &Ed25519KeyPublic { &self.0 } 313 | } 314 | 315 | /// An Ed25519 key pair. 316 | /// 317 | /// This is for the internal use of the signal crates. It is 318 | /// recommended that you use the underlying dalek crates instead. 319 | pub struct Ed25519KeyPair { 320 | pub public: Ed25519KeyPublic, 321 | secret: ed25519_dalek::SecretKey, 322 | } 323 | 324 | /// The public half of an Ed25519 key pair. 325 | #[derive(Debug, Clone, PartialEq, Eq)] 326 | pub struct Ed25519KeyPublic(ed25519_dalek::PublicKey); 327 | 328 | 329 | impl Ed25519KeyPair { 330 | /// Generate a new Ed25519 key pair with the provided source of randomness. 331 | pub fn generate(csprng: &mut R) -> Ed25519KeyPair { 332 | use ed25519_dalek::Keypair; 333 | use sha2::Sha512; 334 | let Keypair { public, secret } = Keypair::generate::(csprng); 335 | Ed25519KeyPair { 336 | public: Ed25519KeyPublic(public), 337 | secret, 338 | } 339 | } 340 | 341 | // TODO: some way to serialize/deserialize secret key for storage 342 | 343 | /// Sign a message using this key. 344 | pub fn sign(&self, msg: &[u8]) -> Signature { 345 | use sha2::Sha512; 346 | self.secret.expand::() 347 | .sign::(msg, &self.public.0).into() 348 | } 349 | 350 | /// Perform a Diffie-Hellman key exchange with the provided public key. 351 | pub fn diffie_hellman(&self, peer: &Ed25519KeyPublic) -> Result { 352 | use x25519_dalek::diffie_hellman; 353 | use crate::convert::{convert_public_key, convert_secret_key}; 354 | let secret = convert_secret_key(&self.secret)?; 355 | let public = convert_public_key(&peer.to_bytes())?; 356 | Ok(diffie_hellman(secret.as_bytes(), public.as_bytes()).into()) 357 | } 358 | } 359 | 360 | impl Ed25519KeyPublic { 361 | /// Deserialize the provided bytes into an Ed25519 public key. 362 | pub fn from_bytes(bytes: [u8; 32]) -> Result { 363 | Ok(Ed25519KeyPublic(ed25519_dalek::PublicKey::from_bytes(&bytes)?)) 364 | } 365 | 366 | /// Serialize this key into a byte array. 367 | pub fn to_bytes(&self) -> [u8; 32] { 368 | self.0.to_bytes() 369 | } 370 | 371 | /// Get a view of this key as bytes. 372 | pub fn as_bytes(&self) -> &[u8] { 373 | self.0.as_bytes() 374 | } 375 | 376 | /// Verify a message signed by this key. 377 | pub fn verify(&self, msg: &[u8], sig: &Signature) -> Result<()> { 378 | use sha2::Sha512; 379 | Ok(self.0.verify::(msg, sig.as_dalek())?) 380 | } 381 | } 382 | 383 | impl std::hash::Hash for Ed25519KeyPublic { 384 | fn hash(&self, hasher: &mut H) { 385 | hasher.write(self.0.as_bytes()) 386 | } 387 | } 388 | 389 | #[cfg(test)] 390 | mod x3dh_tests { 391 | use rand::OsRng; 392 | 393 | use super::*; 394 | 395 | #[test] 396 | fn test_signature() { 397 | let mut csprng = OsRng::new().unwrap(); 398 | let keypair = IdentityKeyPair::generate(&mut csprng); 399 | let public_key = keypair.public(); 400 | let message = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; 401 | 402 | let signature = keypair.sign(&message); 403 | 404 | assert!(public_key.verify(&message, &signature).is_ok()); 405 | } 406 | 407 | #[test] 408 | fn test_diffie_hellman() { 409 | let mut csprng = OsRng::new().unwrap(); 410 | let alice = IdentityKeyPair::generate(&mut csprng); 411 | let bob = EphemeralKeyPair::generate(&mut csprng); 412 | 413 | let session_key_alice = alice.diffie_hellman(&bob.public()).unwrap(); 414 | let session_key_bob = bob.diffie_hellman(&alice.public()).unwrap(); 415 | 416 | assert_eq!(session_key_alice, session_key_bob); 417 | } 418 | 419 | #[test] 420 | fn test_x3dh() { 421 | let mut csprng = OsRng::new().unwrap(); 422 | 423 | let alice = IdentityKeyPair::generate(&mut csprng); 424 | 425 | let bob = IdentityKeyPair::generate(&mut csprng); 426 | let bob_spk = SignedPrekeyPair::generate(&mut csprng); 427 | let bob_opk = OneTimePrekeyPair::generate(&mut csprng, 42); 428 | 429 | let ek = EphemeralKeyPair::generate(&mut csprng); 430 | 431 | let dh1_a = alice.diffie_hellman(&bob_spk.public()).unwrap(); 432 | let dh2_a = ek.diffie_hellman(&bob.public()).unwrap(); 433 | let dh3_a = ek.diffie_hellman(&bob_spk.public()).unwrap(); 434 | let dh4_a = ek.diffie_hellman(&bob_opk.public()).unwrap(); 435 | 436 | let dh1_b = bob_spk.diffie_hellman(&alice.public()).unwrap(); 437 | let dh2_b = bob.diffie_hellman(&ek.public()).unwrap(); 438 | let dh3_b = bob_spk.diffie_hellman(&ek.public()).unwrap(); 439 | let dh4_b = bob_opk.diffie_hellman(&ek.public()).unwrap(); 440 | 441 | assert_eq!(dh1_a, dh1_b); 442 | assert_eq!(dh2_a, dh2_b); 443 | assert_eq!(dh3_a, dh3_b); 444 | assert_eq!(dh4_a, dh4_b); 445 | } 446 | } 447 | 448 | /// A key in a double ratchet chain. 449 | /// 450 | /// Each of the three chains in the double ratchet is a sequence 451 | /// of chain keys derived from the previous chain key and the next 452 | /// ratchet key. 453 | #[derive(Debug, PartialEq, Eq)] 454 | pub struct ChainKey(pub(crate) [u8; 32]); 455 | 456 | impl std::ops::Deref for ChainKey { 457 | type Target = [u8]; 458 | 459 | fn deref(&self) -> &[u8] { 460 | &self.0 461 | } 462 | } 463 | 464 | impl<'a> From<&'a [u8]> for ChainKey { 465 | fn from(slice: &[u8]) -> ChainKey { 466 | let len = if slice.len() < 32 { slice.len() } else { 32 }; 467 | 468 | let mut arr = [0; 32]; 469 | for i in 0..len { 470 | arr[i] = slice[i]; 471 | } 472 | 473 | ChainKey(arr) 474 | } 475 | } 476 | 477 | impl From for ChainKey { 478 | fn from(bytes: KeyMaterial) -> ChainKey { 479 | ChainKey(bytes.0) 480 | } 481 | } 482 | 483 | /// The output of the first ratchet is a session key. 484 | /// 485 | /// The synchronous Diffie-Hellman ratchet produces a session 486 | /// key, which is used as the next root key for the sending 487 | /// and receiving chains. 488 | #[derive(Debug, PartialEq, Eq)] 489 | pub struct SessionKey(pub(crate) [u8; 32]); 490 | 491 | impl std::ops::Deref for SessionKey { 492 | type Target = [u8]; 493 | 494 | fn deref(&self) -> &[u8] { 495 | &self.0 496 | } 497 | } 498 | 499 | impl<'a> From<&'a [u8]> for SessionKey { 500 | fn from(slice: &[u8]) -> SessionKey { 501 | let len = if slice.len() < 32 { slice.len() } else { 32 }; 502 | 503 | let mut arr = [0; 32]; 504 | for i in 0..len { 505 | arr[i] = slice[i]; 506 | } 507 | 508 | SessionKey(arr) 509 | } 510 | } 511 | 512 | /// The output of the second ratchet is a message key. 513 | /// 514 | /// The asynchronous ratchet produces message keys, which are 515 | /// each used to encrypt or decrypt one message. 516 | #[derive(Debug, PartialEq, Eq)] 517 | pub struct MessageKey(pub(crate) [u8; 32]); 518 | 519 | impl std::ops::Deref for MessageKey { 520 | type Target = [u8]; 521 | 522 | fn deref(&self) -> &[u8] { 523 | &self.0 524 | } 525 | } 526 | 527 | impl<'a> From<&'a [u8]> for MessageKey { 528 | fn from(slice: &[u8]) -> MessageKey { 529 | let len = if slice.len() < 32 { slice.len() } else { 32 }; 530 | 531 | let mut arr = [0; 32]; 532 | for i in 0..len { 533 | arr[i] = slice[i]; 534 | } 535 | 536 | MessageKey(arr) 537 | } 538 | } 539 | 540 | /// A secret key half of a Diffie-Hellman ratchet key pair. 541 | /// 542 | /// This is generated, used to ratchet the sending chain, and then 543 | /// thrown away. 544 | #[derive(Debug, PartialEq, Eq)] 545 | pub struct RatchetKeySecret(pub(crate) [u8; 32]); 546 | 547 | impl RatchetKeySecret { 548 | pub fn as_bytes(&self) -> &[u8; 32] { 549 | &self.0 550 | } 551 | } 552 | 553 | impl std::ops::Deref for RatchetKeySecret { 554 | type Target = [u8]; 555 | 556 | fn deref(&self) -> &[u8] { 557 | &self.0 558 | } 559 | } 560 | 561 | impl<'a> From<&'a [u8]> for RatchetKeySecret { 562 | fn from(slice: &[u8]) -> RatchetKeySecret { 563 | let len = if slice.len() < 32 { slice.len() } else { 32 }; 564 | 565 | let mut arr = [0; 32]; 566 | for i in 0..len { 567 | arr[i] = slice[i]; 568 | } 569 | 570 | RatchetKeySecret(arr) 571 | } 572 | } 573 | 574 | /// The public half of the Diffie-Hellman ratchet key pair. 575 | /// 576 | /// This half is sent along with each message to inform the peer 577 | /// that a Diffie-Hellman ratchet is necessary, and to provide the 578 | /// keying material for that ratchet. 579 | #[derive(Clone, Debug, PartialEq, Eq)] 580 | pub struct RatchetKeyPublic(pub(crate) MontgomeryPoint); 581 | 582 | impl RatchetKeyPublic { 583 | /// Deserialize the provided bytes into a ratchet public key. 584 | pub fn from_bytes(bytes: [u8; 32]) -> Result { 585 | use curve25519_dalek::edwards::CompressedEdwardsY; 586 | Ok(RatchetKeyPublic( 587 | CompressedEdwardsY::from_slice(&bytes[..]) 588 | .decompress() 589 | .ok_or(Error)? 590 | .to_montgomery() 591 | )) 592 | } 593 | 594 | /// Serialize this ratchet public key into a byte array. 595 | pub fn to_bytes(&self) -> [u8; 32] { 596 | let ed = match self.0.to_edwards(0) { 597 | Some(e) => e, 598 | None => { 599 | match self.0.to_edwards(1) { 600 | Some(e) => e, 601 | None => panic!("i hope we can't actually panic here!"), 602 | } 603 | }, 604 | }; 605 | 606 | ed.compress().to_bytes() 607 | } 608 | } 609 | 610 | impl std::ops::Deref for RatchetKeyPublic { 611 | type Target = MontgomeryPoint; 612 | 613 | fn deref(&self) -> &MontgomeryPoint { 614 | &self.0 615 | } 616 | } 617 | 618 | impl Hash for RatchetKeyPublic { 619 | fn hash(&self, hasher: &mut H) { 620 | self.0.as_bytes().hash(hasher); 621 | } 622 | } 623 | 624 | impl<'a> From<&'a MontgomeryPoint> for RatchetKeyPublic { 625 | fn from(point: &MontgomeryPoint) -> RatchetKeyPublic { 626 | RatchetKeyPublic(point.clone()) 627 | } 628 | } 629 | 630 | impl<'a> From<&'a SignedPrekeyPublic> for RatchetKeyPublic { 631 | fn from(spk: &'a SignedPrekeyPublic) -> RatchetKeyPublic { 632 | use crate::convert::convert_public_key; 633 | let public = convert_public_key(&spk.to_bytes()).unwrap(); 634 | RatchetKeyPublic(public) 635 | } 636 | } 637 | 638 | /// A key pair used in the Diffie-Hellman ratchet. 639 | /// 640 | /// The first ratchet of the Double Ratchet is the synchronous 641 | /// Diffie-Hellman ratchet. On each half-round-trip, the receiver 642 | /// ratchets this twice: once to initialize the next receiving chain 643 | /// and once to initialize the next sending chain. 644 | #[derive(Debug, PartialEq, Eq)] 645 | pub struct RatchetKeyPair { 646 | pub public: RatchetKeyPublic, 647 | pub secret: RatchetKeySecret, 648 | } 649 | 650 | impl RatchetKeyPair { 651 | pub fn generate(csprng: &mut R) -> RatchetKeyPair { 652 | use sha2::Sha512; 653 | use crate::convert::{convert_public_key, convert_secret_key}; 654 | 655 | let ed_pair = ed25519_dalek::Keypair::generate::(csprng); 656 | 657 | // TODO: not unwrap 658 | let public = convert_public_key(&ed_pair.public.as_bytes()).unwrap(); 659 | let public = RatchetKeyPublic(public); 660 | let secret = convert_secret_key(&ed_pair.secret).unwrap(); 661 | let secret = RatchetKeySecret(secret.0); 662 | 663 | RatchetKeyPair { public, secret } 664 | } 665 | } 666 | 667 | impl<'a> From<&'a SignedPrekeyPair> for RatchetKeyPair { 668 | fn from(spk: &'a SignedPrekeyPair) -> RatchetKeyPair { 669 | use crate::convert::{convert_public_key, convert_secret_key}; 670 | 671 | // TODO: not unwrap 672 | let public = convert_public_key(&spk.0.public.0.to_bytes()).unwrap(); 673 | let public = RatchetKeyPublic(public); 674 | let secret = convert_secret_key(&spk.0.secret).unwrap(); 675 | let secret = RatchetKeySecret(secret.0); 676 | 677 | RatchetKeyPair { public, secret } 678 | } 679 | } 680 | -------------------------------------------------------------------------------- /crates/signal-common/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Shared utilities for Signal crates. 2 | //! 3 | //! The modules contained here provide basic types that are shared among 4 | //! other crates in the Signal project. 5 | 6 | extern crate curve25519_dalek; 7 | extern crate ed25519_dalek; 8 | extern crate rand; 9 | extern crate sha2; 10 | extern crate x25519_dalek; 11 | 12 | pub mod convert; 13 | pub mod error; 14 | pub mod keys; 15 | -------------------------------------------------------------------------------- /crates/signal/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "signal" 3 | version = "0.1.0" 4 | authors = ["Andrew Dona-Couch "] 5 | 6 | [dependencies] 7 | curve25519-dalek = "0.21.0" 8 | rand = "0.5.5" 9 | 10 | double-ratchet = { path = "../double-ratchet" } 11 | signal-common = { path = "../signal-common" } 12 | x3dh = { path = "../x3dh" } 13 | -------------------------------------------------------------------------------- /crates/signal/src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate curve25519_dalek; 2 | extern crate rand; 3 | 4 | extern crate double_ratchet; 5 | extern crate signal_common; 6 | extern crate x3dh; 7 | 8 | #[cfg(test)] 9 | mod tests { 10 | use rand::OsRng; 11 | 12 | use double_ratchet::session::SessionBuilder; 13 | use signal_common::keys::{ChainKey, RatchetKeyPair}; 14 | use x3dh::keyserver::Keyserver; 15 | use x3dh::participant::Participant; 16 | 17 | #[test] 18 | fn test_everything() { 19 | let mut csprng = OsRng::new().unwrap(); 20 | 21 | // First, the key agreement with X3DH. 22 | 23 | let mut server = Keyserver::new(); 24 | 25 | let mut alice = Participant::new(&mut csprng); 26 | alice.register(&mut server).unwrap(); 27 | 28 | let mut bob = Participant::new(&mut csprng); 29 | bob.register(&mut server).unwrap(); 30 | bob.add_opk(&mut server, &mut csprng).unwrap(); 31 | 32 | alice.add_peer(&bob.ik()); 33 | 34 | let (sk, opk_id, ek) = alice.begin_exchange( 35 | &bob.ik(), &mut server, &mut csprng, 36 | ).unwrap(); 37 | 38 | // And now, the connection with Double Ratchet. 39 | 40 | let info = &b"foobar!"[..]; 41 | 42 | let bob_keys = RatchetKeyPair::from(bob.spk_pair()); 43 | 44 | let mut alice2 = SessionBuilder::new(info, ChainKey::from(sk)) 45 | .connect_to(&bob_keys.public, &mut csprng); 46 | 47 | let message1 = b"Hello, Bob! Nice secret channel!"; 48 | let (h1, secret1) = alice2.send(message1); 49 | 50 | let sk = bob.complete_exchange(&alice.ik(), opk_id, ek).unwrap(); 51 | 52 | let mut bob2 = SessionBuilder::new(info, ChainKey::from(sk)) 53 | .accept_with(bob_keys); 54 | 55 | let decrypt1 = bob2.receive(h1, &secret1, &mut csprng).unwrap(); 56 | 57 | assert_eq!(decrypt1, message1); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /crates/x3dh/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | -------------------------------------------------------------------------------- /crates/x3dh/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "x3dh" 3 | version = "0.1.0" 4 | authors = ["Andrew Dona-Couch "] 5 | 6 | [dependencies] 7 | curve25519-dalek = "0.21.0" 8 | ed25519-dalek = "0.8.1" 9 | orion = "0.9.1" 10 | rand = "0.5.5" 11 | sha2 = "0.7" 12 | 13 | signal-common = { path = "../signal-common" } 14 | 15 | [dependencies.x25519-dalek] 16 | version = "0.3.0" 17 | default-features = false 18 | features = ["std", "u64_backend"] 19 | -------------------------------------------------------------------------------- /crates/x3dh/src/keyserver.rs: -------------------------------------------------------------------------------- 1 | //! A key server providing escrow features for X3DH. 2 | //! 3 | //! To enable a user to initiate a key exchange with another 4 | //! user, even one who is offline, X3DH makes use of a key escrow 5 | //! server to transmit public keys between peers. 6 | 7 | use std::collections::HashMap; 8 | 9 | use signal_common::error::{Error, Result}; 10 | use signal_common::keys::{ 11 | IdentityKeyPublic, 12 | SignedPrekeyPublic, 13 | OneTimePrekeyPublic, 14 | PrekeyBundle, 15 | Signature, 16 | }; 17 | 18 | /// An entry for a single participant in the key server. 19 | pub struct KeyEntry { 20 | spk: SignedPrekeyPublic, 21 | spk_sig: Signature, 22 | opks: Vec, 23 | } 24 | 25 | /// A server storing public keys and prekey material of participants. 26 | pub struct Keyserver { 27 | entries: HashMap, 28 | } 29 | 30 | impl Keyserver { 31 | /// Initialize a new key server. 32 | pub fn new() -> Keyserver { 33 | Keyserver { entries: HashMap::new() } 34 | } 35 | 36 | /// Get the prekey bundle for a participant based on their public key. 37 | pub fn prekey_bundle(&mut self, ik: &IdentityKeyPublic) -> Option { 38 | self.entries.get_mut(ik).map(|entry| { 39 | let opk = if entry.opks.len() > 0 { 40 | let mine = entry.opks.pop().unwrap(); 41 | Some(mine) 42 | } else { 43 | None 44 | }; 45 | PrekeyBundle { 46 | ik: ik.clone(), 47 | spk: entry.spk.clone(), 48 | spk_sig: entry.spk_sig.clone(), 49 | opk, 50 | } 51 | }) 52 | } 53 | 54 | /// Add the identity of a new participant to the server, along with 55 | /// their initial signed prekey. 56 | pub fn update_identity( 57 | &mut self, 58 | ik: &IdentityKeyPublic, 59 | spk: &SignedPrekeyPublic, 60 | spk_sig: &Signature, 61 | ) -> Result<()> { 62 | ik.verify(spk.as_bytes(), spk_sig)?; 63 | 64 | self.entries.insert(ik.clone(), KeyEntry { 65 | spk: spk.clone(), 66 | spk_sig: spk_sig.clone(), 67 | opks: vec![], 68 | }); 69 | 70 | Ok(()) 71 | } 72 | 73 | /* 74 | /// Update a participant's signed prekey in the keyserver. 75 | pub fn update_spk( 76 | &mut self, 77 | ik: &PublicKey, 78 | spk: &PublicKey, 79 | spk_sig: &Signature, 80 | ) -> Result<(), CryptoError> { 81 | ik.verify(spk.as_bytes(), spk_sig)?; 82 | 83 | match self.entries.get_mut(ik) { 84 | None => Err(CryptoError), 85 | Some(entry) => { 86 | entry.spk = spk.clone(); 87 | entry.spk_sig = spk_sig.clone(); 88 | Ok(()) 89 | }, 90 | } 91 | } 92 | */ 93 | 94 | /// Add a one-time prekey to the server for a participant. 95 | pub fn add_opk( 96 | &mut self, 97 | ik: &IdentityKeyPublic, 98 | opk: &OneTimePrekeyPublic, 99 | opk_sig: &Signature, 100 | ) -> Result<()> { 101 | ik.verify(opk.as_bytes(), opk_sig)?; 102 | 103 | match self.entries.get_mut(ik) { 104 | None => Err(Error), 105 | Some(entry) => { 106 | entry.opks.push(opk.clone()); 107 | Ok(()) 108 | }, 109 | } 110 | } 111 | } 112 | 113 | #[cfg(test)] 114 | mod tests { 115 | use rand::OsRng; 116 | 117 | use signal_common::keys::{ 118 | IdentityKeyPair, 119 | SignedPrekeyPair, 120 | OneTimePrekeyPair, 121 | Signature, 122 | }; 123 | 124 | use super::*; 125 | 126 | #[test] 127 | fn test_keyserver() { 128 | let mut csprng = OsRng::new().unwrap(); 129 | 130 | let mut server = Keyserver::new(); 131 | 132 | let (ik_b, spk_b, spk_b_sig, opk_b, opk_b_sig) = { 133 | let ik_b = IdentityKeyPair::generate(&mut csprng); 134 | let spk_b = SignedPrekeyPair::generate(&mut csprng); 135 | let opk_b = OneTimePrekeyPair::generate(&mut csprng, 0); 136 | 137 | let ik_b_public = ik_b.public(); 138 | let spk_b_public = spk_b.public(); 139 | let opk_b_public = opk_b.public(); 140 | 141 | let spk_b_sig = ik_b.sign(spk_b_public.as_bytes()); 142 | let opk_b_sig = ik_b.sign(opk_b_public.as_bytes()); 143 | 144 | (ik_b_public, spk_b_public, spk_b_sig, opk_b_public, opk_b_sig) 145 | }; 146 | 147 | server.update_identity(&ik_b, &spk_b, &spk_b_sig).unwrap(); 148 | server.add_opk(&ik_b, &opk_b, &opk_b_sig).unwrap(); 149 | 150 | let pkb = server.prekey_bundle(&ik_b).unwrap(); 151 | let opk = pkb.opk.unwrap(); 152 | 153 | assert_eq!(pkb.ik, ik_b); 154 | assert_eq!(pkb.spk, spk_b); 155 | assert_eq!(opk, opk_b); 156 | 157 | ik_b.verify(pkb.spk.as_bytes(), &spk_b_sig).unwrap(); 158 | ik_b.verify(opk.as_bytes(), &opk_b_sig).unwrap(); 159 | 160 | assert_eq!(opk.index(), 0); 161 | } 162 | 163 | #[test] 164 | fn test_rejects_bad_sigs() { 165 | let mut csprng = OsRng::new().unwrap(); 166 | 167 | let mut server = Keyserver::new(); 168 | 169 | let (ik_b, spk_b, spk_b_sig, opk_b, opk_b_sig, spk2_b, spk2_b_sig) = { 170 | let ik_b = IdentityKeyPair::generate(&mut csprng); 171 | let spk_b = SignedPrekeyPair::generate(&mut csprng); 172 | let opk_b = OneTimePrekeyPair::generate(&mut csprng, 0); 173 | let spk2_b = SignedPrekeyPair::generate(&mut csprng); 174 | 175 | let ik_b_public = ik_b.public(); 176 | let spk_b_public = spk_b.public(); 177 | let opk_b_public = opk_b.public(); 178 | let spk2_b_public = spk2_b.public(); 179 | 180 | let spk_b_sig = ik_b.sign(spk_b_public.as_bytes()); 181 | let opk_b_sig = ik_b.sign(opk_b_public.as_bytes()); 182 | let spk2_b_sig = ik_b.sign(spk2_b_public.as_bytes()); 183 | 184 | ( 185 | ik_b_public, 186 | spk_b_public, spk_b_sig, 187 | opk_b_public, opk_b_sig, 188 | spk2_b_public, spk2_b_sig, 189 | ) 190 | }; 191 | 192 | let bad_sig = Signature::from_bytes([0; 64]).unwrap(); 193 | assert!(server.update_identity(&ik_b, &spk_b, &bad_sig).is_err()); 194 | 195 | server.update_identity(&ik_b, &spk_b, &spk_b_sig).unwrap(); 196 | 197 | assert!(server.add_opk(&ik_b, &opk_b, &bad_sig).is_err()); 198 | 199 | server.add_opk(&ik_b, &opk_b, &opk_b_sig).unwrap(); 200 | 201 | assert!(server.update_identity(&ik_b, &spk2_b, &bad_sig).is_err()); 202 | 203 | server.update_identity(&ik_b, &spk2_b, &spk2_b_sig).unwrap(); 204 | } 205 | } 206 | -------------------------------------------------------------------------------- /crates/x3dh/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! An implementation of the X3DH key agreement algorithm. 2 | //! 3 | //! See the documentation for [`Keyserver`] and [`Participant`] 4 | //! for more details. 5 | //! 6 | //! [`Keyserver`]: keyserver/struct.Keyserver.html 7 | //! [`Participant`]: participant/struct.Participant.html 8 | //! 9 | //! # Examples 10 | //! 11 | //! ``` 12 | //! extern crate rand; 13 | //! 14 | //! extern crate signal_common; 15 | //! extern crate x3dh; 16 | //! 17 | //! use rand::OsRng; 18 | //! 19 | //! use signal_common::keys::{ChainKey, RatchetKeyPair}; 20 | //! use x3dh::keyserver::Keyserver; 21 | //! use x3dh::participant::Participant; 22 | //! 23 | //! fn main() { 24 | //! let mut csprng = OsRng::new().unwrap(); 25 | //! 26 | //! // First, the key agreement with X3DH. 27 | //! 28 | //! let mut server = Keyserver::new(); 29 | //! 30 | //! let mut alice = Participant::new(&mut csprng); 31 | //! alice.register(&mut server).unwrap(); 32 | //! 33 | //! let mut bob = Participant::new(&mut csprng); 34 | //! bob.register(&mut server).unwrap(); 35 | //! bob.add_opk(&mut server, &mut csprng).unwrap(); 36 | //! 37 | //! alice.add_peer(&bob.ik()); 38 | //! 39 | //! let (sk_a, opk_id, ek) = alice.begin_exchange( 40 | //! &bob.ik(), &mut server, &mut csprng, 41 | //! ).unwrap(); 42 | //! 43 | //! let sk_b = bob.complete_exchange(&alice.ik(), opk_id, ek).unwrap(); 44 | //! 45 | //! assert_eq!(sk_a, sk_b); 46 | //! } 47 | //! ``` 48 | 49 | extern crate curve25519_dalek; 50 | extern crate ed25519_dalek; 51 | extern crate orion; 52 | extern crate rand; 53 | extern crate sha2; 54 | extern crate x25519_dalek; 55 | 56 | extern crate signal_common; 57 | 58 | pub mod keyserver; 59 | pub mod participant; 60 | pub mod peer; 61 | 62 | #[cfg(test)] 63 | pub mod one; 64 | 65 | #[cfg(test)] 66 | mod tests { 67 | use super::*; 68 | 69 | #[test] 70 | fn it_works() { 71 | one::example(); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /crates/x3dh/src/one.rs: -------------------------------------------------------------------------------- 1 | use ed25519_dalek::Keypair; 2 | use orion::default::hkdf; 3 | use rand::OsRng; 4 | use sha2::Sha512; 5 | use x25519_dalek::diffie_hellman; 6 | use x25519_dalek::generate_public; 7 | 8 | use signal_common::convert::convert_ed25519_to_x25519; 9 | 10 | pub fn example() { 11 | let mut csprng = OsRng::new().unwrap(); 12 | 13 | let info = "foobar!".as_bytes(); 14 | 15 | // 2.4 Keys 16 | let ik_a = convert_ed25519_to_x25519( 17 | &Keypair::generate::(&mut csprng), 18 | ).unwrap(); 19 | 20 | let ik_a_public = generate_public(&ik_a.secret.as_bytes()); 21 | assert_eq!(ik_a_public.as_bytes().len(), ik_a.public.as_bytes().len()); 22 | for i in 0..ik_a_public.as_bytes().len() { 23 | assert_eq!(ik_a_public.as_bytes()[i], ik_a.public.as_bytes()[i]); 24 | } 25 | 26 | // 3.2 Publishing keys 27 | let ( 28 | (iks_b, ik_b, spk_b, spk_b_sig, opk_b), 29 | b_secrets, 30 | ) = { 31 | let iks_b = Keypair::generate::(&mut csprng); 32 | let ik_b = convert_ed25519_to_x25519(&iks_b).unwrap(); 33 | let spk_b = convert_ed25519_to_x25519( 34 | &Keypair::generate::(&mut csprng), 35 | ).unwrap(); 36 | let opk_b = convert_ed25519_to_x25519( 37 | &Keypair::generate::(&mut csprng), 38 | ).unwrap(); 39 | 40 | let spk_b_sig = iks_b.sign::(spk_b.public.as_bytes()); 41 | 42 | ( 43 | (iks_b.public, ik_b.public, spk_b.public, spk_b_sig, opk_b.public), 44 | (ik_b, spk_b, opk_b), 45 | ) 46 | }; 47 | 48 | // 3.3 Sending the initial message 49 | iks_b.verify::(spk_b.as_bytes(), &spk_b_sig).unwrap(); 50 | 51 | let (sk1, ek_a) = { 52 | let ek_a = convert_ed25519_to_x25519( 53 | &Keypair::generate::(&mut csprng), 54 | ).unwrap(); 55 | 56 | let dh1 = diffie_hellman(ik_a.secret.as_bytes(), spk_b.as_bytes()); 57 | let dh2 = diffie_hellman(ek_a.secret.as_bytes(), ik_b.as_bytes()); 58 | let dh3 = diffie_hellman(ek_a.secret.as_bytes(), spk_b.as_bytes()); 59 | let dh4 = diffie_hellman(ek_a.secret.as_bytes(), opk_b.as_bytes()); 60 | 61 | (kdf(info, &dh1, &dh2, &dh3, &dh4), ek_a.public) 62 | }; 63 | 64 | /* 65 | let ad: Vec = ik_a.public.as_bytes().iter().chain( 66 | ik_b.as_bytes().iter() 67 | ).cloned().collect(); 68 | */ 69 | 70 | let message = (ik_a.public, ek_a); 71 | 72 | // 3.4 Receiving the initial message 73 | let (ik_a, ek_a) = message; 74 | let (ik_b, spk_b, opk_b) = b_secrets; 75 | 76 | let sk2 = { 77 | let dh1 = diffie_hellman(spk_b.secret.as_bytes(), ik_a.as_bytes()); 78 | let dh2 = diffie_hellman(ik_b.secret.as_bytes(), ek_a.as_bytes()); 79 | let dh3 = diffie_hellman(spk_b.secret.as_bytes(), ek_a.as_bytes()); 80 | let dh4 = diffie_hellman(opk_b.secret.as_bytes(), ek_a.as_bytes()); 81 | 82 | kdf(info, &dh1, &dh2, &dh3, &dh4) 83 | }; 84 | 85 | assert_eq!(sk1.len(), sk2.len()); 86 | for i in 0..sk1.len() { 87 | assert_eq!(sk1[i], sk2[i]); 88 | } 89 | } 90 | 91 | fn kdf(info: &[u8], dh1: &[u8], dh2: &[u8], dh3: &[u8], dh4: &[u8]) -> [u8; 32] { 92 | let input: Vec = std::iter::repeat(0xFF).take(32) 93 | .chain(dh1.iter().cloned()) 94 | .chain(dh2.iter().cloned()) 95 | .chain(dh3.iter().cloned()) 96 | .chain(dh4.iter().cloned()) 97 | .collect(); 98 | 99 | hkdf(&[0; 32], &input, info).unwrap() 100 | } 101 | -------------------------------------------------------------------------------- /crates/x3dh/src/participant.rs: -------------------------------------------------------------------------------- 1 | //! An X3DH participant. 2 | //! 3 | //! Manages the state for a single participant, including 4 | //! identity key, signed prekey, one-time prekeys, and peers. 5 | 6 | use std::collections::HashMap; 7 | 8 | use rand::{CryptoRng, RngCore}; 9 | 10 | use signal_common::error::{Error, Result}; 11 | use signal_common::keys::{ 12 | EphemeralKeyPublic, 13 | IdentityKeyPair, 14 | IdentityKeyPublic, 15 | OneTimePrekeyPair, 16 | OneTimePrekeyPublic, 17 | PrekeyBundle, 18 | Signature, 19 | KeyMaterial, 20 | SignedPrekeyPair, 21 | SignedPrekeyPublic, 22 | }; 23 | 24 | use crate::keyserver::Keyserver; 25 | use crate::peer::{Peer}; 26 | 27 | /// The state for a participant in an X3DH system. 28 | pub struct Participant { 29 | ik: IdentityKeyPair, 30 | spk: SignedPrekeyPair, 31 | next_opk: u64, 32 | opks: HashMap, 33 | peers: HashMap, 34 | } 35 | 36 | impl Participant { 37 | /// Initialize a new participant, using the given randomness 38 | /// to generate keys. 39 | pub fn new(csprng: &mut R) -> Participant { 40 | let ik = IdentityKeyPair::generate(csprng); 41 | let spk = SignedPrekeyPair::generate(csprng); 42 | let next_opk = 1; 43 | let opks = HashMap::new(); 44 | let peers = HashMap::new(); 45 | 46 | Participant { ik, spk, next_opk, opks, peers } 47 | } 48 | 49 | // TODO: some way to serialize/deserialize a participant to storage 50 | 51 | /// The participant's public identity key. 52 | pub fn ik(&self) -> IdentityKeyPublic { 53 | self.ik.public() 54 | } 55 | 56 | /// The participant's signed prekey. 57 | pub fn spk(&self) -> SignedPrekeyPublic { 58 | self.spk.public() 59 | } 60 | 61 | /// The signature for the participant's signed prekey. 62 | pub fn spk_sig(&self) -> Signature { 63 | self.ik.sign(self.spk().as_bytes()) 64 | } 65 | 66 | /// The participant's signed prekey secret key. Be careful! 67 | pub fn spk_pair(&self) -> &SignedPrekeyPair { 68 | &self.spk 69 | } 70 | 71 | /// Create a new one-time prekey for this participant. 72 | pub fn create_opk(&mut self, csprng: &mut R) -> (OneTimePrekeyPublic, Signature) { 73 | let id = self.next_opk; 74 | self.next_opk = id + 1; 75 | 76 | let opk = OneTimePrekeyPair::generate(csprng, id); 77 | let opk_public = opk.public(); 78 | 79 | self.opks.insert(id, opk); 80 | 81 | let sig = self.ik.sign(opk_public.as_bytes()).into(); 82 | (opk_public, sig) 83 | } 84 | 85 | /// Register this participant with the given key server. 86 | pub fn register(&self, keyserver: &mut Keyserver) -> Result<()> { 87 | let ik = self.ik.public(); 88 | let spk = self.spk.public(); 89 | let spk_sig = self.ik.sign(spk.as_bytes()); 90 | 91 | keyserver.update_identity(&ik, &spk, &spk_sig) 92 | } 93 | 94 | /// Add a new one-time prekey to the given key server. 95 | pub fn add_opk( 96 | &mut self, 97 | keyserver: &mut Keyserver, 98 | csprng: &mut R, 99 | ) -> Result<()> { 100 | let ik = self.ik.public(); 101 | let opk = self.create_opk(csprng); 102 | 103 | keyserver.add_opk(&ik, &opk.0, &opk.1) 104 | } 105 | 106 | /// Add a peer, in preparation for future communication. 107 | pub fn add_peer(&mut self, peer: &IdentityKeyPublic) { 108 | self.peers.insert(peer.clone(), Peer::HaveIdentity(peer.clone())); 109 | } 110 | 111 | /// Begin a key agreement exchange with the peer using the keyserver. 112 | pub fn begin_exchange( 113 | &mut self, 114 | peer: &IdentityKeyPublic, 115 | keyserver: &mut Keyserver, 116 | csprng: &mut R, 117 | ) -> Result<(KeyMaterial, u64, EphemeralKeyPublic)> { 118 | let bundle = match keyserver.prekey_bundle(peer) { 119 | Some(b) => b, 120 | None => return Err(Error), 121 | }; 122 | 123 | self.accept_bundle(bundle, csprng) 124 | } 125 | 126 | /// Begin the key agreement exchange with the peer by accepting a prekey bundle. 127 | pub fn accept_bundle( 128 | &mut self, 129 | bundle: PrekeyBundle, 130 | csprng: &mut R, 131 | ) -> Result<(KeyMaterial, u64, EphemeralKeyPublic)> { 132 | let peer_state = match self.peers.remove(&bundle.ik) { 133 | Some(s) => s, 134 | None => return Err(Error), 135 | }; 136 | 137 | let peer = bundle.ik.clone(); 138 | 139 | let peer_state = peer_state.accept_prekey_bundle(bundle)?; 140 | 141 | let (peer_state, sk, opk_id, ek) = peer_state.derive_key(csprng, &self.ik)?; 142 | 143 | self.peers.insert(peer, peer_state); 144 | 145 | Ok((sk, opk_id, ek)) 146 | } 147 | 148 | /// Complete a key agreement exchange previously started by a peer. 149 | pub fn complete_exchange( 150 | &mut self, 151 | peer: &IdentityKeyPublic, 152 | opk_id: u64, 153 | ek: EphemeralKeyPublic, 154 | ) -> Result { 155 | let opk = match self.opks.remove(&opk_id) { 156 | None => return Err(Error), 157 | Some(opk) => opk, 158 | }; 159 | 160 | // TODO: this???? 161 | self.add_peer(peer); 162 | 163 | let peer_state = match self.peers.remove(peer) { 164 | Some(s) => s, 165 | None => return Err(Error), 166 | }; 167 | 168 | let (peer_state, sk) = peer_state.match_key(&self.ik, &self.spk, &opk, &ek)?; 169 | 170 | self.peers.insert(peer.clone(), peer_state); 171 | 172 | Ok(sk) 173 | } 174 | } 175 | 176 | #[cfg(test)] 177 | mod tests { 178 | use rand::OsRng; 179 | 180 | use crate::keyserver::Keyserver; 181 | 182 | use super::*; 183 | 184 | #[test] 185 | fn test_exchange() { 186 | let mut csprng = OsRng::new().unwrap(); 187 | 188 | let mut server = Keyserver::new(); 189 | 190 | let mut alice = Participant::new(&mut csprng); 191 | alice.register(&mut server).unwrap(); 192 | 193 | let mut bob = Participant::new(&mut csprng); 194 | bob.register(&mut server).unwrap(); 195 | bob.add_opk(&mut server, &mut csprng).unwrap(); 196 | 197 | alice.add_peer(&bob.ik()); 198 | 199 | let (sk1, opk_id, ek) = alice.begin_exchange(&bob.ik(), &mut server, &mut csprng) 200 | .unwrap(); 201 | 202 | let sk2 = bob.complete_exchange(&alice.ik(), opk_id, ek).unwrap(); 203 | 204 | assert_eq!(sk1, sk2); 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /crates/x3dh/src/peer.rs: -------------------------------------------------------------------------------- 1 | //! The state machine of a peer connection in an X3DH exchange. 2 | //! 3 | //! The key echange forms a simple state machine, and 4 | //! so we model it here likewise. This module is fairly 5 | //! low-level, see `Participant` for the use of this. 6 | 7 | use orion::default::hkdf; 8 | use rand::{CryptoRng, RngCore}; 9 | 10 | use signal_common::error::{Error, Result}; 11 | use signal_common::keys::{ 12 | EphemeralKeyPair, 13 | EphemeralKeyPublic, 14 | IdentityKeyPair, 15 | IdentityKeyPublic, 16 | OneTimePrekeyPair, 17 | SignedPrekeyPair, 18 | KeyMaterial, 19 | PrekeyBundle, 20 | }; 21 | 22 | /// The state of a single peer. 23 | pub enum Peer { 24 | /// We have never heard of this peer. 25 | Unknown, 26 | /// We have this peer's identity key. 27 | HaveIdentity(IdentityKeyPublic), 28 | /// We have a prekey bundle for this peer. 29 | HavePrekeyBundle(PrekeyBundle), 30 | /// The key exchange has been completed. 31 | Connected, 32 | } 33 | 34 | impl Peer { 35 | /// Are we connected to the peer? 36 | pub fn is_connected(&self) -> bool { 37 | match self { 38 | Peer::Connected => true, 39 | _ => false, 40 | } 41 | } 42 | 43 | /// Are we ready to send an initial message to make a connection? 44 | /// First we need the prekey bundle for the peer. 45 | pub fn ready_to_send(&self) -> bool { 46 | match self { 47 | Peer::HavePrekeyBundle(_) => true, 48 | _ => false, 49 | } 50 | } 51 | 52 | /// We've obtained a prekey bundle for this peer, so move to 53 | /// the ready to send state. 54 | pub fn accept_prekey_bundle(self, bundle: PrekeyBundle) -> Result { 55 | bundle.ik.verify(bundle.spk.as_bytes(), &bundle.spk_sig)?; 56 | 57 | Ok(Peer::HavePrekeyBundle(bundle)) 58 | } 59 | 60 | /// Derive a key for this peer to use for an initial message. 61 | /// Requires that we're ready to send. 62 | pub fn derive_key( 63 | self, 64 | csprng: &mut R, 65 | me: &IdentityKeyPair, 66 | ) -> Result<(Peer, KeyMaterial, u64, EphemeralKeyPublic)> { 67 | match self { 68 | Peer::HavePrekeyBundle(bundle) => { 69 | let ek = EphemeralKeyPair::generate(csprng); 70 | 71 | let ik = bundle.ik; 72 | let spk = bundle.spk; 73 | 74 | // TODO: not unwrap 75 | let opk = bundle.opk.unwrap(); 76 | 77 | let dh1 = me.diffie_hellman(&spk)?; 78 | let dh2 = ek.diffie_hellman(&ik)?; 79 | let dh3 = ek.diffie_hellman(&spk)?; 80 | let dh4 = ek.diffie_hellman(&opk)?; 81 | 82 | let sk = kdf(dh1, dh2, dh3, dh4); 83 | 84 | Ok((Peer::Connected, sk, opk.index(), ek.public())) 85 | }, 86 | _ => Err(Error), 87 | } 88 | } 89 | 90 | /// Complete the key exchange initiated by the peer. 91 | pub fn match_key( 92 | self, 93 | ik: &IdentityKeyPair, 94 | spk: &SignedPrekeyPair, 95 | opk: &OneTimePrekeyPair, 96 | ek: &EphemeralKeyPublic, 97 | ) -> Result<(Peer, KeyMaterial)> { 98 | let you = match self { 99 | Peer::HavePrekeyBundle(bundle) => { 100 | bundle.ik 101 | }, 102 | Peer::HaveIdentity(pk) => { 103 | pk 104 | }, 105 | _ => return Err(Error), 106 | }; 107 | 108 | let dh1 = spk.diffie_hellman(&you)?; 109 | let dh2 = ik.diffie_hellman(ek)?; 110 | let dh3 = spk.diffie_hellman(ek)?; 111 | let dh4 = opk.diffie_hellman(ek)?; 112 | 113 | let sk = kdf(dh1, dh2, dh3, dh4); 114 | 115 | Ok((Peer::Connected, sk)) 116 | } 117 | } 118 | 119 | fn kdf( 120 | dh1: KeyMaterial, 121 | dh2: KeyMaterial, 122 | dh3: KeyMaterial, 123 | dh4: KeyMaterial, 124 | ) -> KeyMaterial { 125 | let input: Vec = std::iter::repeat(0xFF).take(32) 126 | .chain(dh1.as_bytes().iter().cloned()) 127 | .chain(dh2.as_bytes().iter().cloned()) 128 | .chain(dh3.as_bytes().iter().cloned()) 129 | .chain(dh4.as_bytes().iter().cloned()) 130 | .collect(); 131 | 132 | hkdf(&[0; 32], &input, b"this is my info string").unwrap().into() 133 | } 134 | 135 | #[cfg(test)] 136 | mod tests { 137 | use rand::OsRng; 138 | 139 | use signal_common::keys::PrekeyBundle; 140 | 141 | use super::*; 142 | 143 | #[test] 144 | fn test_key_exchange() { 145 | let mut csprng = OsRng::new().unwrap(); 146 | 147 | let (bundle, secrets) = { 148 | let ik_b = IdentityKeyPair::generate(&mut csprng); 149 | let spk_b = SignedPrekeyPair::generate(&mut csprng); 150 | let opk_b = OneTimePrekeyPair::generate(&mut csprng, 0); 151 | 152 | let ik_b_public = ik_b.public(); 153 | let spk_b_public = spk_b.public(); 154 | let opk_b_public = opk_b.public(); 155 | 156 | let spk_b_sig = ik_b.sign(spk_b_public.as_bytes()); 157 | 158 | ( 159 | PrekeyBundle { 160 | ik: ik_b_public, 161 | spk: spk_b_public, 162 | spk_sig: spk_b_sig, 163 | opk: Some(opk_b_public), 164 | }, 165 | (ik_b, spk_b, opk_b), 166 | ) 167 | }; 168 | 169 | let ik_a = IdentityKeyPair::generate(&mut csprng); 170 | 171 | let peer_b = Peer::HavePrekeyBundle(bundle); 172 | 173 | let (new_peer, sk1, _id, ek) = peer_b.derive_key(&mut csprng, &ik_a).unwrap(); 174 | 175 | assert!(new_peer.is_connected()); 176 | 177 | let peer_a = Peer::HaveIdentity(ik_a.public()); 178 | 179 | let (ik_b, spk_b, opk_b) = secrets; 180 | 181 | let (new_peer, sk2) = peer_a.match_key(&ik_b, &spk_b, &opk_b, &ek).unwrap(); 182 | 183 | assert!(new_peer.is_connected()); 184 | 185 | assert_eq!(sk1, sk2); 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /schema/keyserver.capnp: -------------------------------------------------------------------------------- 1 | @0xb18650cec7d93286; 2 | 3 | using Util = import "util.capnp"; 4 | using Signed = Util.Signed; 5 | using Maybe = Util.Maybe; 6 | 7 | interface Keyserver { 8 | prekeyBundle @0 (ik :Data) -> (bundle :PrekeyBundle); 9 | updateIdentity @1 (ik :Data, spk :Signed(PublicKey)); 10 | addOpks @2 (ik :Data, opks :List(Signed(OneTimePrekey))); 11 | } 12 | 13 | using PublicKey = Data; 14 | 15 | struct PrekeyBundle { 16 | ik @0 :PublicKey; 17 | spk @1 :Signed(PublicKey); 18 | opk @2 :Maybe(OneTimePrekey); 19 | } 20 | 21 | struct OneTimePrekey { 22 | id @0 :UInt64; 23 | key @1 :PublicKey; 24 | } 25 | -------------------------------------------------------------------------------- /schema/util.capnp: -------------------------------------------------------------------------------- 1 | @0x8d97c14f572a2ee7; 2 | 3 | using Signature = Data; 4 | 5 | # A signature that accompanies some signed content. The content 6 | # signed might be all of T or it could be some part, the exact 7 | # bits and the necessary keys are left up to the context of use. 8 | struct Signed(T) { 9 | # The value that has been signed. 10 | key @0 :T; 11 | # The signature of the value. 12 | sig @1 :Signature; 13 | } 14 | 15 | # An optional value analogous to standard Option. 16 | struct Maybe(T) { 17 | union { 18 | # No value exists. 19 | none @0 :Void; 20 | # A value. 21 | some @1 :T; 22 | } 23 | } 24 | --------------------------------------------------------------------------------