├── rust-toolchain ├── rustfmt.toml ├── .gitignore ├── Gemfile ├── Gemfile.lock ├── src ├── oprf.rs ├── opaque.rs ├── key_exchange │ ├── triple_diffie.rs │ ├── mod.rs │ └── sigma.rs ├── envelope.rs ├── client.rs ├── lib.rs └── bin │ └── opaque.rs ├── .headache-run ├── Cargo.toml ├── ToDo.md ├── tests ├── opaque_test.rs └── sample_run.txt ├── DomainModeling.markdown ├── LICENSE └── README.md /rust-toolchain: -------------------------------------------------------------------------------- 1 | nightly 2 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | max_width = 80 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | 2 | source 'https://rubygems.org' do 3 | gem 'markdown_helper', '~> 2.5', '>= 2.5.3' 4 | end 5 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | markdown_helper (2.5.3) 5 | 6 | PLATFORMS 7 | ruby 8 | 9 | DEPENDENCIES 10 | markdown_helper (~> 2.5, >= 2.5.3)! 11 | 12 | BUNDLED WITH 13 | 2.1.4 14 | -------------------------------------------------------------------------------- /src/oprf.rs: -------------------------------------------------------------------------------- 1 | 2 | 3 | pub trait Oprf {} 4 | pub trait VerifiedOprf {} 5 | 6 | // Protocol for computing DH-OPRF, U with input x and S with input k: 7 | // U: choose random r in [0..q-1], send alpha=H'(x)*g^r to S 8 | 9 | // The simplified form with the base point factor dropped: 10 | // spec: alpha=(H'(x))^r in the first message and set the 11 | // function output to H(x,v,beta^{1/r}) 12 | -------------------------------------------------------------------------------- /.headache-run: -------------------------------------------------------------------------------- 1 | # Generated by headache | 1577981238 -- commit me! 2 | encoded_configuration:ewogICJoZWFkZXJGaWxlIjogIi4vQ09QWVJJR0hUIiwKICAic3R5bGUiOiAiU2xhc2hTdGFyIiwKICAiaW5jbHVkZXMiOiBbInNyYy8qKi8qLnJzIl0sCiAgImRhdGEiOiB7CiAgICAiT3duZXIiOiAiUGxhaW50ZXh0LCBMTEMiCiAgfQp9Cgo= 3 | encoded_header:Q29weXJpZ2h0IHt7LlllYXJSYW5nZX19IHt7Lk93bmVyfX0gLSBBbGwgUmlnaHRzIFJlc2VydmVkCgpVbmF1dGhvcml6ZWQgY29weWluZyBvZiB0aGlzIGZpbGUsIHZpYSBhbnkgbWVkaXVtIGlzIHN0cmljdGx5IHByb2hpYml0ZWQuClByb3ByaWV0YXJ5IGFuZCBjb25maWRlbnRpYWwuCg== 4 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "opaque" 3 | version = "0.0.1" 4 | authors = ["gustin "] 5 | edition = "2018" 6 | 7 | [features] 8 | nightly = ["curve25519-dalek/nightly"] 9 | 10 | [dependencies] 11 | aes-gcm-siv = "0.2.1" 12 | bincode = "1.2.0" 13 | curve25519-dalek = "= 1.2.3" 14 | ed25519-dalek = "= 1.0.0-pre.2" 15 | hex = "0.4.0" 16 | hkdf = "0.8.0" 17 | hmac = "0.7.1" 18 | lazy_static = "1.4.0" 19 | rand_chacha = "= 0.2.1" 20 | rand_core = "= 0.3.1" 21 | rand_os = { version = "= 0.1.3", features = ["wasm-bindgen"] } 22 | serde = { version = "1.0", features = ["derive"] } 23 | serde_bytes = "0.11.2" 24 | sha2 = "0.8.0" 25 | sha3 = "0.8.2" 26 | typenum = "1.11.2" 27 | -------------------------------------------------------------------------------- /src/opaque.rs: -------------------------------------------------------------------------------- 1 | use crate::key_exchange::KeyExchangeProtocol; 2 | use crate::oprf::*; 3 | use crate::key_exchange::sigma::*; 4 | use crate::key_exchange::triple_diffie::*; 5 | 6 | // Opaque can take an OPRF type and a Key Exchange 7 | 8 | pub struct Opaque { 9 | key_exchange: T, 10 | oprf: U, 11 | } 12 | 13 | 14 | impl Opaque { 15 | pub fn registration(&self) { 16 | self.key_exchange.initiate_handshake(); 17 | } 18 | } 19 | 20 | // Type T where T is a Key Exchange and an OPRF 21 | 22 | pub type OpaqueSigma = Opaque; 23 | pub type Opaque3Dh = Opaque; 24 | -------------------------------------------------------------------------------- /ToDo.md: -------------------------------------------------------------------------------- 1 | ## ToDos 2 | 3 | Things to do, loosely held 4 | 5 | - [x] Update README, README driven development. 6 | - [ ] Abstract out KeyExchange 7 | - [ ] SIGMA Impl 8 | - [ ] Add constants for byte arrays. 9 | - [ ] Abstract out OPRF. 10 | - [ ] Zeroize. 11 | - [ ] Add KDF specifically for stretching password pre-computation 12 | - [ ] Pluggable encryptions, make the whole system pluggable. 13 | - [ ] Pluggable key exchange, NOISE, etc 14 | - [ ] Pluggable OPRF 15 | - [ ] VOPRF 16 | - [ ] Pluggable primatives: 17 | - [ ] HKDF: eg. Blake 3 18 | - [ ] KDF: eg. Argon 2, Blake 3 19 | - [ ] AES-GCM-SIV: eg. ChaCha-Poly 20 | - [ ] SHA512: eg. Blake3 21 | - [ ] Varied storage 22 | - [ ] Diesel integration? 23 | - [ ] Storage trait 24 | - [ ] Commit Schemes 25 | 26 | 27 | -------------------------------------------------------------------------------- /tests/opaque_test.rs: -------------------------------------------------------------------------------- 1 | extern crate opaque; 2 | 3 | #[test] 4 | fn test_protocol() { 5 | let username = "jerrg"; 6 | let password = "onelonelyhead"; 7 | 8 | let (alpha, keypair, pub_u, priv_u, r) = 9 | opaque::client::registration_start(&password); 10 | let (beta, v, pub_s) = opaque::registration_start(&username, &alpha); 11 | 12 | let envelope = opaque::client::registration_finalize( 13 | &password, &beta, &v, &pub_u, &pub_s, &priv_u, &r, 14 | ); 15 | opaque::registration_finalize(&username, &pub_u, &envelope); 16 | 17 | let (alpha, ke_1, x, r) = 18 | opaque::client::authenticate_start(&username, &password); 19 | let (beta, v, envelope, ke_2, y) = 20 | opaque::authenticate_start(&username, &alpha, &ke_1); 21 | 22 | let ke_3 = opaque::client::authenticate_finalize( 23 | &password, &keypair, &envelope, &beta, &v, &ke_2, &x, &y, &r, 24 | ); 25 | opaque::authenticate_finalize(&username, &ke_3, &x); 26 | } 27 | -------------------------------------------------------------------------------- /src/key_exchange/triple_diffie.rs: -------------------------------------------------------------------------------- 1 | use crate::key_exchange::KeyExchange; 2 | use crate::key_exchange::KeyExchangeProtocol; 3 | 4 | 5 | pub struct TripleDiffie(); 6 | 7 | impl KeyExchangeProtocol for TripleDiffie { 8 | fn initiate_handshake(&self) { 9 | // SIGMA-I 10 | // sidA, g^x, nA, infoA 11 | // sidA: session identifier chosen by each party for the ongoing session, is returned by B 12 | // g^x: basepoint times random scalar x 13 | // nA: nonce, fresh and anew with each session 14 | // info: any additional info to be carried in the protocol, not required (could be protocol 15 | // name, version, message number, etc) 16 | // -> client sends scalar * base point 17 | } 18 | fn responder_handshake(&self) { 19 | // SIGMA 20 | // KE2 = g^y, Sig(PrivS; g^x, g^y), Mac(Km1; IdS) 21 | 22 | // sidA: 23 | // sidB 24 | // g^y 25 | // nB 26 | // infoB 27 | // server sends, g^y, SIgn(privS; g^x, g^y), Mac(Km1; IdS) 28 | } 29 | fn initiator_response(&self) { 30 | // SIGMA 31 | 32 | // KE3 = Sig(PrivU; g^y, g^x), Mac(Km2; IdU) 33 | // { A, SIGa(g^y, g^x), MAC(Km; A) } Ke 34 | 35 | // client sends -> Sig(PrivU; g^y, g^x), Mac(Km2; IdU) 36 | } 37 | fn responder_response(&self) { 38 | // server verifies request 39 | } 40 | } 41 | 42 | 43 | -------------------------------------------------------------------------------- /DomainModeling.markdown: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Domain Model 4 | 5 | Goal: To abstract out the current procedural style of code, so it's less 6 | like Pascal from back in the 90's. 7 | 8 | ![turbo](https://upload.wikimedia.org/wikipedia/commons/d/df/Turbo_Pascal_7.0_Scrren.png) 9 | 10 | ### Modules 11 | 12 | ## OPRF 13 | 14 | ### Client 15 | 16 | - Registration Start: Calculates Alpha, KeyPair (pub/private), R 17 | - Registration Finalize: Calculates Envelope Ciphered 18 | 19 | Shared between calls: password, pub_u, pub_s, priv_u, R 20 | 21 | - Authenticate Start: Calculates Alpha, R 22 | - Authenticate Final: Calculates 23 | 24 | Shared between calls: password, keypair, R 25 | 26 | ### Server 27 | 28 | - Registration Start: Calculates beta, v, pub_s 29 | - Registration Finalize: Stores result 30 | 31 | - Authentication Start: 32 | - Authentication Finalize: 33 | 34 | Shared between calls: 35 | 36 | ## OPAQUE 37 | 38 | ### Structs 39 | 40 | 41 | ## Key Exchange 42 | 43 | Trait: 44 | * ke_1 - generate the first key for key exchange. 45 | * No inputs 46 | * Returns ke_1 as u8; 32 47 | * ke_2 - derive the second key in the exchange. 48 | * Input: ke_1 49 | * Returns ke_2 as u8; 32 50 | * ke_3 - derive the third key in the exchange. 51 | * Input: ke_2 52 | * Returns ke_3 as u8; 32 53 | 54 | * Sigma: private functions specific to Sigma Key Exchange Implementation to fulfil 55 | the above triats 56 | * SIGa(g^y, g^x) 57 | * MAC(Km; PubS) 58 | 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2020, Gustin Prudner, gustin@mycelium.dev 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | -------------------------------------------------------------------------------- /src/key_exchange/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod sigma; 2 | pub mod triple_diffie; 3 | 4 | use serde::{Deserialize, Serialize}; 5 | 6 | pub const PUBLIC_KEY_SIZE: usize= 32; 7 | pub const SECRET_KEY_SIZE: usize = 32; 8 | 9 | #[derive(Serialize, Deserialize, Clone, Debug)] 10 | pub struct KeyExchange<'a> { 11 | pub identity: [u8; 32], 12 | #[serde(with = "serde_bytes")] 13 | pub signature: &'a [u8], 14 | #[serde(with = "serde_bytes")] 15 | pub mac: Vec, 16 | // nonce, sid, info 17 | } 18 | 19 | pub trait KeyExchangeProtocol { 20 | fn initiate_handshake(&self); 21 | fn responder_handshake(&self); 22 | fn initiator_response(&self); 23 | fn responder_response(&self); 24 | 25 | fn from_bytes(_bytes: &[u8]) -> KeyExchange { 26 | KeyExchange { 27 | identity: [0u8; 32], 28 | signature: &[0u8; 64], 29 | mac: vec![1, 2, 3, 4], 30 | } 31 | } 32 | } 33 | 34 | 35 | impl KeyExchange<'_> { 36 | ///## 37 | /// Construct a KeyExchange from the bytes of a previously formed 38 | /// KeyExchange. 39 | /// 40 | /// # Inputs 41 | /// 42 | /// * `bytes`: an `&[u8]` representing a KeyExchange. 43 | /// 44 | /// # Returns 45 | /// 46 | /// A KeyExchange formed from bytes. 47 | /// 48 | /// # Warning 49 | /// 50 | /// If you give this function bytes which do not represent a KeyExchange 51 | /// it will be broken. 52 | /// 53 | pub fn from_bytes<'a>(_bytes: &'a [u8]) -> KeyExchange { 54 | KeyExchange { 55 | identity: [0u8; 32], 56 | signature: &[0u8; 64], 57 | mac: vec![1, 2, 3, 4], 58 | } 59 | } 60 | } 61 | 62 | 63 | 64 | 65 | #[cfg(test)] 66 | mod tests { 67 | use super::*; 68 | 69 | #[test] 70 | fn key_1() { 71 | let key_bytes: [u8; 32] = [ 72 | 72, 79, 202, 45, 141, 212, 156, 96, 121, 69, 228, 3, 178, 12, 144, 73 | 236, 246, 53, 133, 85, 149, 25, 244, 215, 69, 178, 20, 242, 112, 74 | 154, 116, 41, 75 | ]; 76 | let _key_exchange = KeyExchange::from_bytes(&key_bytes); 77 | } 78 | } 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /src/key_exchange/sigma.rs: -------------------------------------------------------------------------------- 1 | use crate::key_exchange::KeyExchange; 2 | use crate::key_exchange::KeyExchangeProtocol; 3 | 4 | pub struct SigmaI(); 5 | pub struct SigmaR(); 6 | 7 | impl KeyExchangeProtocol for SigmaI { 8 | fn initiate_handshake(&self) { 9 | // SIGMA-I 10 | // sidA, g^x, nA, infoA 11 | // sidA: session identifier chosen by each party for the ongoing session, is returned by B 12 | // g^x: basepoint times random scalar x 13 | // nA: nonce, fresh and anew with each session 14 | // info: any additional info to be carried in the protocol, not required (could be protocol 15 | // name, version, message number, etc) 16 | // -> client sends scalar * base point 17 | } 18 | fn responder_handshake(&self) { 19 | // SIGMA 20 | // KE2 = g^y, Sig(PrivS; g^x, g^y), Mac(Km1; IdS) 21 | 22 | // sidA: 23 | // sidB 24 | // g^y 25 | // nB 26 | // infoB 27 | // server sends, g^y, SIgn(privS; g^x, g^y), Mac(Km1; IdS) 28 | } 29 | fn initiator_response(&self) { 30 | // SIGMA 31 | 32 | // KE3 = Sig(PrivU; g^y, g^x), Mac(Km2; IdU) 33 | // { A, SIGa(g^y, g^x), MAC(Km; A) } Ke 34 | 35 | // client sends -> Sig(PrivU; g^y, g^x), Mac(Km2; IdU) 36 | } 37 | fn responder_response(&self) { 38 | // server verifies request 39 | } 40 | } 41 | 42 | impl KeyExchangeProtocol for SigmaR { 43 | fn initiate_handshake(&self) { 44 | // SIGMA-I 45 | // sidA, g^x, nA, infoA 46 | // sidA: session identifier chosen by each party for the ongoing session, is returned by B 47 | // g^x: basepoint times random scalar x 48 | // nA: nonce, fresh and anew with each session 49 | // info: any additional info to be carried in the protocol, not required (could be protocol 50 | // name, version, message number, etc) 51 | // -> client sends scalar * base point 52 | } 53 | fn responder_handshake(&self) { 54 | // SIGMA 55 | // KE2 = g^y, Sig(PrivS; g^x, g^y), Mac(Km1; IdS) 56 | 57 | // sidA: 58 | // sidB 59 | // g^y 60 | // nB 61 | // infoB 62 | // server sends, g^y, SIgn(privS; g^x, g^y), Mac(Km1; IdS) 63 | } 64 | fn initiator_response(&self) { 65 | // SIGMA 66 | 67 | // KE3 = Sig(PrivU; g^y, g^x), Mac(Km2; IdU) 68 | // { A, SIGa(g^y, g^x), MAC(Km; A) } Ke 69 | 70 | // client sends -> Sig(PrivU; g^y, g^x), Mac(Km2; IdU) 71 | } 72 | fn responder_response(&self) { 73 | // server verifies request 74 | } 75 | } 76 | 77 | -------------------------------------------------------------------------------- /src/envelope.rs: -------------------------------------------------------------------------------- 1 | use crate::key_exchange::PUBLIC_KEY_SIZE; 2 | use crate::key_exchange::SECRET_KEY_SIZE; 3 | use serde::{Deserialize, Serialize}; 4 | 5 | use aes_gcm_siv::aead::{generic_array::GenericArray, Aead, NewAead}; 6 | use aes_gcm_siv::Aes256GcmSiv; 7 | 8 | use hkdf::Hkdf; 9 | use sha2::Sha512; 10 | 11 | #[derive(Serialize, Deserialize, Clone, Debug)] 12 | pub struct Envelope { 13 | pub_u: [u8; PUBLIC_KEY_SIZE], 14 | pub_s: [u8; SECRET_KEY_SIZE], 15 | priv_u: [u8; SECRET_KEY_SIZE], 16 | } 17 | 18 | impl Envelope { 19 | pub fn new( 20 | pub_u: &[u8], 21 | priv_u: &[u8], 22 | pub_s: &[u8], 23 | ) -> Result { 24 | if pub_u.len() != 32 { 25 | return Err(()); 26 | } 27 | if priv_u.len() != 32 { 28 | return Err(()); 29 | } 30 | if pub_s.len() != 32 { 31 | return Err(()); 32 | } 33 | let mut pub_u_env: [u8; PUBLIC_KEY_SIZE] = [0u8; PUBLIC_KEY_SIZE]; 34 | let mut priv_u_env: [u8; SECRET_KEY_SIZE] = [0u8; SECRET_KEY_SIZE]; 35 | let mut pub_s_env: [u8; SECRET_KEY_SIZE] = [0u8; SECRET_KEY_SIZE]; 36 | pub_u_env.copy_from_slice(&pub_u[..PUBLIC_KEY_SIZE]); 37 | priv_u_env.copy_from_slice(&priv_u[..SECRET_KEY_SIZE]); 38 | pub_s_env.copy_from_slice(&pub_s[..SECRET_KEY_SIZE]); 39 | 40 | Ok(Envelope { 41 | pub_u: pub_u_env, 42 | priv_u: priv_u_env, 43 | pub_s: pub_s_env, 44 | }) 45 | } 46 | 47 | pub fn encrypt(&self, rwd_u: &[u8]) -> Vec { 48 | // HKDF: HMAC-based Extract-and-Expand:https://tools.ietf.org/html/rfc5869 49 | // see section on to "salt or not to salt", currently not salting 50 | let hkdf = Hkdf::::new(None, &rwd_u); 51 | let mut output_key_material = [0u8; 44]; // 32 byte key + 96 bit nonce 52 | let info = hex::decode("f0f1f2f3f4f5f6f7f8f9").unwrap(); // domain separation 53 | hkdf.expand(&info, &mut output_key_material).unwrap(); 54 | 55 | let encryption_key: GenericArray = 56 | GenericArray::clone_from_slice(&output_key_material[0..32]); 57 | let aead = Aes256GcmSiv::new(encryption_key); 58 | let nonce: GenericArray = 59 | GenericArray::clone_from_slice(&output_key_material[32..44]); 60 | 61 | let payload: Vec = bincode::serialize(&self).unwrap(); 62 | return aead.encrypt(&nonce, payload.as_slice()).unwrap(); 63 | } 64 | } 65 | 66 | #[cfg(test)] 67 | mod tests { 68 | use ed25519_dalek::{Keypair}; 69 | use rand_os::OsRng; 70 | use sha3::{Digest, Sha3_512}; 71 | 72 | use super::*; 73 | 74 | #[test] 75 | fn build_envelope() { 76 | let key_too_small: [u8; 31] = [0u8; 31]; 77 | let envelope = Envelope::new(&key_too_small, &key_too_small, &key_too_small); 78 | assert_eq!(envelope.is_err(), true); 79 | 80 | let key_too_big: [u8; 33] = [0u8; 33]; 81 | let envelope = Envelope::new(&key_too_big, &key_too_big, &key_too_big); 82 | assert_eq!(envelope.is_err(), true); 83 | } 84 | 85 | #[test] 86 | fn ciphering() { 87 | let mut cspring = OsRng::new().unwrap(); 88 | let keypair: Keypair = Keypair::generate(&mut cspring); 89 | 90 | let priv_u = keypair.secret.to_bytes(); 91 | let pub_u = keypair.public.to_bytes(); 92 | 93 | let keypair: Keypair = Keypair::generate(&mut cspring); 94 | let pub_s = keypair.public.to_bytes(); 95 | 96 | let envelope = Envelope::new(&pub_u, &priv_u, &pub_s).unwrap(); 97 | 98 | let password = "gopro"; 99 | let mut hasher = Sha3_512::new(); 100 | hasher.input(password.as_bytes()); 101 | 102 | let rwd_u = hasher.result(); 103 | 104 | envelope.encrypt(&rwd_u); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OPAQUE Protocol 2 | The OPAQUE protocol is an asymmetric password-authenticated key exchange, PAKE. 3 | 4 | A [PAKE](https://en.wikipedia.org/wiki/Password-authenticated_key_agreement) 5 | is a way to exchange cryptographic keys with the knowledge of a password. 6 | The asymmetric part of this aPAKE means that only one party knows the actual 7 | password; the password does not have to be revealed to both parties taking 8 | part in the exchange. 9 | 10 | OPAQUE was selected by the CFRG as the aPake of choice: 11 | https://github.com/cfrg/pake-selection 12 | 13 | I discovered OPAQUE through Matthew Green's blog post: 14 | [Let's talk about PAKE](https://blog.cryptographyengineering.com/2018/10/19/lets-talk-about-pake/) 15 | 16 | ## Key Exchange 17 | 18 | This package currently uses a custom implementation of the battle-tested 19 | [SIGMA](https://webee.technion.ac.il/~hugo/sigma-pdf.pdf) family of key-exchange 20 | protocols. The full-fledged version, protecting identity. 21 | 22 | ## OPRF 23 | 24 | OPAQUE interleaves an oblivious pseudorandom function (OPRF) and a key-exchange 25 | protocol. 26 | 27 | An (OPRF)[https://tools.ietf.org/html/draft-irtf-cfrg-voprf-03] is a way for 28 | two parties to take part in a computation in which one party provides the input 29 | to the computation, and the other party performs the computation. 30 | 31 | The exciting part is that the party performing the calculation learns nothing 32 | about the inputs provided, and the party providing the actual inputs 33 | only learns the outputs and nothing else about the computation. 34 | 35 | 36 | ### Verifiable 37 | 38 | A verifiable OPRF, a vOPRF, enables each party to prove that the computation 39 | was valid. 40 | 41 | ## Threshold 42 | 43 | OPAQUE lends itself to the ability to a threshold to mitigate a data store being 44 | stolen or accessed. 45 | 46 | A threshold protocol basically distributes a private key amongst a bunch of servers. 47 | A certain a certain number of servers, the threshold, is needed to take part in a protocol. 48 | 49 | NIST is working to standardize the threshold schemes for cryptographic primitives: 50 | https://csrc.nist.gov/Projects/threshold-cryptography 51 | 52 | ### Threshold OPRF 53 | 54 | An OPRF can become a threshold OPRF: 55 | https://eprint.iacr.org/2017/363.pdf 56 | 57 | In the OPRF case, each server acts as a OPRF signer of the blinded salt from the client. 58 | Each server takes part as a share of the larger private key. The output from the OPRF 59 | can then be required to have a certain number of servers take part in its generation. 60 | 61 | Each server runs a [Distributed Key Generation protocol](https://en.wikipedia.org/wiki/Distributed_key_generation) 62 | to generate their share of the private key. 63 | 64 | Torben Pedersen first specified a protocol in 1991: 65 | https://pdfs.semanticscholar.org/642b/d1bbc86c7750cef9fa770e9e4ba86bd49eb9.pdf 66 | 67 | The Feldman VSS (verifiable secret sharing) is a way to take part in the DKG: 68 | https://ieeexplore.ieee.org/abstract/document/4568297/ 69 | 70 | ## Security 71 | 72 | 🎸 There has not been a security audit performed on this package. 🎸 73 | 74 | OPAQUE has been proven to be resilient against pre-computation attacks 75 | in this [whitepaper](https://eprint.iacr.org/2018/163.pdf). 76 | 77 | OPAQUE exhibits forward secrecy. 78 | 79 | It is one of the few PAKEs with a security proof: 80 | https://eprint.iacr.org/2018/163.pdf 81 | 82 | The main attack is a basic brute force, the ability to attempt password 83 | authentication repeatedly. Limiting this attack can easily be accomplished 84 | through standard rate-limiting. 85 | 86 | If the database containing the encrypted envelops was stolen, a brute 87 | force attack, emulating this protocol, could be used. The passwords 88 | are stretched in a way to make this computationally intensive. Cryptographic 89 | ways to mitigate this will be explored. 90 | 91 | 🎸 There has not been a security audit performed on this package. 🎸 92 | 93 | ## Draft Version 94 | 95 | This library is currently built against [draft version 3](https://tools.ietf.org/html/draft-krawczyk-cfrg-opaque-03). 96 | 97 | [Draft 4 of Opaque](https://tools.ietf.org/html/draft-krawczyk-cfrg-opaque-04) was 98 | released on May 15th, 2020. 99 | 100 | The main changes seem to be: 101 | * Details on how to build out the user Envelope: 102 | * specified using AES-CTR and HMAC 103 | * only encrypt-then-MAC is recommended 104 | * GCM is allowed, but only GCM-then-HMAC 105 | * Key exchange protocols is expanded: 106 | * 3DH is added to the already described HMQV and SIGMA-I 107 | * OPRF definition changed 108 | * Does not include `vU = g^kU` when hashing 109 | * This was proven to not be needed anymore 110 | 111 | [Draft 5 of Opaque](https://www.ietf.org/id/draft-krawczyk-cfrg-opaque-05.txt) was 112 | released on May 29th, 2020. 113 | 114 | This will be the last change before a formal specification. 115 | 116 | The main changes for Draft 5 are mostly clarifications and added TODOs to prepare 117 | for specification. 118 | 119 | ## Things to do.. 120 | 121 | A more detailed list of things Todo: 122 | @[:markdown](ToDo.md) 123 | 124 | ## Execute 125 | 126 | cargo run --bin opaque 127 | 128 | ## Testing 129 | 130 | A set of OPAQUE test vectors are tested against: 131 | 132 | cargo test 133 | 134 | 135 | 136 | -------------------------------------------------------------------------------- /src/client.rs: -------------------------------------------------------------------------------- 1 | use aes_gcm_siv::aead::{generic_array::GenericArray, Aead, NewAead}; 2 | use aes_gcm_siv::Aes256GcmSiv; 3 | use crate::envelope::*; 4 | use curve25519_dalek::constants::RISTRETTO_BASEPOINT_POINT; 5 | use curve25519_dalek::ristretto::{CompressedRistretto, RistrettoPoint}; 6 | use curve25519_dalek::scalar::Scalar; 7 | use ed25519_dalek::{Keypair, Signature}; 8 | use hkdf::Hkdf; 9 | use hmac::{Hmac, Mac}; 10 | use rand_os::OsRng; 11 | use sha2::Sha512; 12 | use sha3::{Digest, Sha3_512}; 13 | 14 | use crate::key_exchange::KeyExchange; 15 | 16 | type HmacSha512 = Hmac; 17 | 18 | pub fn registration_start( 19 | password: &str, 20 | ) -> ([u8; 32], [u8; 64], [u8; 32], [u8; 32], [u8; 32]) { 21 | let mut cspring = OsRng::new().unwrap(); 22 | let keypair: Keypair = Keypair::generate(&mut cspring); 23 | 24 | let priv_u = keypair.secret.to_bytes(); 25 | let pub_u = keypair.public.to_bytes(); 26 | 27 | let r = Scalar::random(&mut cspring); 28 | let hash_prime = 29 | RistrettoPoint::hash_from_bytes::(password.as_bytes()); 30 | let alpha_point: RistrettoPoint = hash_prime * r; 31 | let alpha = alpha_point.compress().to_bytes(); 32 | 33 | (alpha, keypair.to_bytes(), pub_u, priv_u, r.to_bytes()) 34 | } 35 | 36 | pub fn registration_finalize( 37 | password: &str, 38 | beta: &[u8; 32], 39 | v: &[u8; 32], 40 | pub_u: &[u8; 32], 41 | pub_s: &[u8; 32], 42 | priv_u: &[u8; 32], 43 | r: &[u8; 32], 44 | ) -> Vec { 45 | let beta_point = CompressedRistretto::from_slice(&beta[..]); 46 | let beta = beta_point.decompress().unwrap(); 47 | let v_point = CompressedRistretto::from_slice(&v[..]); 48 | let v = v_point.decompress().unwrap(); 49 | 50 | let r = Scalar::from_canonical_bytes(*r).unwrap(); 51 | 52 | let inverse_r = r.invert(); 53 | let sub_beta = beta * inverse_r; 54 | 55 | let mut hasher = Sha3_512::new(); 56 | // assuming multiple inputs create a unique hash not just concating, 57 | // verse serializing 58 | hasher.input(password.as_bytes()); 59 | hasher.input(v.compress().as_bytes()); 60 | hasher.input(sub_beta.compress().to_bytes()); 61 | let rwd_u = hasher.result(); 62 | 63 | let envelope = Envelope::new(pub_u, priv_u, pub_s).unwrap(); 64 | envelope.encrypt(&rwd_u) 65 | } 66 | 67 | pub fn authenticate_start( 68 | _username: &str, 69 | password: &str, 70 | ) -> ([u8; 32], [u8; 32], [u8; 32], [u8; 32]) { 71 | let mut cspring = OsRng::new().unwrap(); 72 | 73 | let r = Scalar::random(&mut cspring); 74 | let hash_prime = 75 | RistrettoPoint::hash_from_bytes::(password.as_bytes()); 76 | let alpha_point: RistrettoPoint = hash_prime * r; 77 | 78 | let x = Scalar::random(&mut cspring); 79 | let ke_1_point = RISTRETTO_BASEPOINT_POINT * x; 80 | 81 | let alpha = alpha_point.compress().to_bytes(); 82 | let ke_1 = ke_1_point.compress().to_bytes(); 83 | 84 | (alpha, ke_1, x.to_bytes(), r.to_bytes()) 85 | } 86 | 87 | pub fn authenticate_finalize( 88 | password: &str, 89 | keypair: &[u8; 64], 90 | envelope: &Vec, 91 | beta: &[u8; 32], 92 | v: &[u8; 32], 93 | ke_2: &Vec, 94 | x: &[u8; 32], 95 | y: &[u8; 32], 96 | r: &[u8; 32], 97 | ) -> Vec { 98 | let beta_point = CompressedRistretto::from_slice(&beta[..]); 99 | let beta = beta_point.decompress().unwrap(); 100 | 101 | let v_point = CompressedRistretto::from_slice(&v[..]); 102 | let v = v_point.decompress().unwrap(); 103 | 104 | let y_point = CompressedRistretto::from_slice(&y[..]); 105 | let y = y_point.decompress().unwrap(); 106 | 107 | // OPRF 108 | let keypair = Keypair::from_bytes(keypair).unwrap(); 109 | 110 | // is_canonical 111 | let r = Scalar::from_canonical_bytes(*r).unwrap(); 112 | 113 | let inverse_r = r.invert(); 114 | let sub_beta = beta * inverse_r; 115 | 116 | let mut hasher = Sha3_512::new(); 117 | hasher.input(password.as_bytes()); // NOTE: Harden with a key derivitive, Section 3.4 118 | hasher.input(v.compress().to_bytes()); 119 | hasher.input(sub_beta.compress().to_bytes()); 120 | let rwd_u = hasher.result(); 121 | 122 | // Use rwd_u_a to decrypt envelope 123 | 124 | let hkdf = Hkdf::::new(None, &rwd_u); 125 | let mut okm = [0u8; 44]; // 32 byte key + 96 bit nonce 126 | let info = hex::decode("f0f1f2f3f4f5f6f7f8f9").unwrap(); // make info the domain string, + 127 | hkdf.expand(&info, &mut okm).unwrap(); 128 | 129 | let encryption_key: GenericArray = 130 | GenericArray::clone_from_slice(&okm[0..32]); 131 | let aead = Aes256GcmSiv::new(encryption_key); 132 | let nonce: GenericArray = 133 | GenericArray::clone_from_slice(&okm[32..44]); 134 | 135 | let _envelope_decrypted = aead 136 | .decrypt(&nonce, envelope.as_slice()) 137 | .expect("decryption failure"); 138 | 139 | // SIGMA 140 | 141 | // KE3 = Sig(PrivU; g^y, g^x), Mac(Km2; IdU) 142 | // { A, SIGa(g^y, g^x), MAC(Km; A) } Ke 143 | 144 | // decrypt ke_2 145 | 146 | // #SECURITY: Prove that all scalars are non-zero, init and inverse 147 | let x = Scalar::from_canonical_bytes(*x).unwrap(); 148 | let dh: RistrettoPoint = x * y; 149 | 150 | let hkdf = Hkdf::::new(None, dh.compress().as_bytes()); 151 | let mut okm_dh = [0u8; 108]; // 32 byte key, 96 bit nonce, 64 bytes 152 | let info = hex::decode("f0f1f2f3f4f5f6f7f8f9").unwrap(); 153 | hkdf.expand(&info, &mut okm_dh).unwrap(); 154 | 155 | let encryption_key_dh: GenericArray = 156 | GenericArray::clone_from_slice(&okm_dh[0..32]); 157 | let aead_dh = Aes256GcmSiv::new(encryption_key_dh); 158 | let nonce_dh: GenericArray = 159 | GenericArray::clone_from_slice(&okm_dh[32..44]); 160 | 161 | let _key_2_decrypted = aead_dh 162 | .decrypt(&nonce_dh, ke_2.as_slice()) 163 | .expect("decryption failure"); 164 | 165 | // SIGa(g^y, g^x) 166 | let mut prehashed: Sha3_512 = Sha3_512::new(); 167 | prehashed.input(y.compress().as_bytes()); 168 | prehashed.input(ke_2); 169 | let context: &[u8] = b"SpecificCustomerDomainName"; 170 | let sig: Signature = keypair.sign_prehashed(prehashed, Some(context)); 171 | 172 | // MAC(Km; PubS) 173 | let mut mac = HmacSha512::new_varkey(&okm_dh[44..108]).unwrap(); 174 | mac.input(&keypair.public.to_bytes()); 175 | 176 | let key_exchange_3 = KeyExchange { 177 | identity: keypair.public.to_bytes(), 178 | signature: &sig.to_bytes(), 179 | mac: mac.result().code().as_slice().to_vec(), 180 | }; 181 | 182 | let payload_dh: Vec = bincode::serialize(&key_exchange_3).unwrap(); 183 | let encrypted_ke_3 = 184 | aead_dh.encrypt(&nonce_dh, payload_dh.as_slice()).unwrap(); 185 | 186 | encrypted_ke_3 187 | } 188 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod client; 2 | pub mod envelope; 3 | pub mod key_exchange; 4 | pub mod opaque; 5 | pub mod oprf; 6 | 7 | use crate::key_exchange::KeyExchange; 8 | 9 | use curve25519_dalek::constants::RISTRETTO_BASEPOINT_POINT; 10 | use curve25519_dalek::ristretto::{CompressedRistretto, RistrettoPoint}; 11 | use curve25519_dalek::scalar::Scalar; 12 | 13 | use rand_os::OsRng; 14 | 15 | use ed25519_dalek::{Keypair, PublicKey, Signature}; 16 | 17 | use aes_gcm_siv::aead::{generic_array::GenericArray, Aead, NewAead}; 18 | use aes_gcm_siv::Aes256GcmSiv; 19 | 20 | use lazy_static::lazy_static; 21 | use std::collections::HashMap; 22 | use std::sync::Mutex; 23 | 24 | use hkdf::Hkdf; 25 | use hmac::{Hmac, Mac}; 26 | use sha2::Sha512; // NOTE: Drop sha2/sha3 to Blake for performance 27 | use sha3::{Digest, Sha3_512}; 28 | 29 | pub type HmacSha512 = Hmac; 30 | 31 | #[derive(Clone)] 32 | struct UserRecord { 33 | envelope: Option>, 34 | pub_u: Option<[u8; 32]>, 35 | k_u: Scalar, 36 | v_u: RistrettoPoint, 37 | } 38 | 39 | lazy_static! { 40 | static ref USER_MAP: Mutex> = 41 | { Mutex::new(HashMap::new()) }; 42 | } 43 | 44 | pub fn registration_start( 45 | username: &str, 46 | alpha: &[u8; 32], 47 | ) -> ([u8; 32], [u8; 32], [u8; 32]) { 48 | println!("=> Rusty Registration Start"); 49 | println!("Alpha: {:?}", alpha); 50 | let alpha_point = CompressedRistretto::from_slice(&alpha[..]); 51 | let alpha = alpha_point.decompress().unwrap(); 52 | 53 | // Guard: Ensure alpha is in the Ristretto group 54 | 55 | // S chooses OPRF key kU (random and independent for each user U) and 56 | // sets vU = g^kU; 57 | 58 | // it also chooses its own pair of private-public 59 | // keys PrivS and PubS for use with protocol KE (the server can use 60 | // the same pair of keys with multiple users), and sends PubS to U. 61 | 62 | let mut cspring = OsRng::new().unwrap(); 63 | let keypair: Keypair = Keypair::generate(&mut cspring); 64 | 65 | // CSPRING: just using OS's PRNG for now 66 | // let mut csprng: OsRng = OsRng::new().unwrap(); 67 | // Generate a keypair 68 | // let keypair: Keypair = Keypair::generate(&mut csprng); 69 | // let _public_key: PublicKey = keypair.public; 70 | 71 | // S stores (EnvU, PubS, PrivS, PubU, kU, vU) in a user-specific 72 | // record. If PrivS and PubS are used for multiple users, S can 73 | // store these values separately and omit them from the user's 74 | // record. 75 | 76 | // Note (salt). We note that in OPAQUE the OPRF key acts as the secret 77 | // salt value that ensures the infeasibility of pre-computation attacks. 78 | // No extra salt value is needed. 79 | 80 | // Note (password rules). The above procedure has the significant 81 | // advantage that the user's password is not disclosed to the server 82 | // even during registration. Some sites require learning the user's 83 | // password for enforcing password rules. Doing so voids this important 84 | // security property of OPAQUE and is not recommended. Moving the 85 | // password check procedure to the client side is a more secure 86 | // alternative. 87 | 88 | // S to C: beta=alpha^kU, vU (g^k), EnvU : KE2 89 | println!("*) beta=alpha^kU, vU (g^k)"); 90 | let k = Scalar::random(&mut cspring); // salt, private 91 | let v: RistrettoPoint = RISTRETTO_BASEPOINT_POINT * k; // salt 2, public 92 | let beta = alpha * k; 93 | let user_record = UserRecord { 94 | envelope: None, 95 | pub_u: None, 96 | k_u: k, 97 | v_u: v, 98 | }; 99 | println!("-) kU {:?}:", k); 100 | println!("-) vU {:?}:", v); 101 | println!("-) beta {:?}", beta); 102 | USER_MAP 103 | .lock() 104 | .unwrap() 105 | .insert(username.to_string(), user_record); 106 | 107 | ( 108 | beta.compress().to_bytes(), 109 | v.compress().to_bytes(), 110 | keypair.public.to_bytes(), 111 | ) 112 | } 113 | 114 | pub fn registration_finalize( 115 | username: &str, 116 | pub_u: &[u8; 32], 117 | envelope: &Vec, 118 | ) { 119 | println!("=> Rusty Registration Finalize"); 120 | println!("PubU: {:?}", pub_u); 121 | println!("Envelope: {:?}", envelope); 122 | let mut user_record = 123 | USER_MAP.lock().unwrap().get(username).unwrap().clone(); 124 | user_record.envelope = Some(envelope.to_vec()); 125 | 126 | user_record.pub_u = Some(*pub_u); 127 | USER_MAP 128 | .lock() 129 | .unwrap() 130 | .insert(username.to_string(), user_record); 131 | println!("=) Registered {:?} with envelope {:?}:", username, envelope); 132 | } 133 | 134 | pub fn authenticate_start( 135 | username: &str, 136 | alpha: &[u8; 32], 137 | ke_1: &[u8; 32], 138 | ) -> ([u8; 32], [u8; 32], Vec, Vec, [u8; 32]) { 139 | println!("====> Rusty Authentication Start"); 140 | println!("Alpha: {:?}", alpha); 141 | println!("KE 1: {:?}", ke_1); 142 | let alpha_point = CompressedRistretto::from_slice(&alpha[..]); 143 | let alpha = alpha_point.decompress().unwrap(); 144 | 145 | let ke_1_point = CompressedRistretto::from_slice(&ke_1[..]); 146 | let ke_1 = ke_1_point.decompress().unwrap(); 147 | 148 | let user_record: UserRecord = 149 | USER_MAP.lock().unwrap().get(username).unwrap().clone(); 150 | 151 | // S to C: beta=alpha^kU, vU, EnvU, KE2 152 | println!("*) beta=alpha^kU, vU (g^k)"); 153 | let beta = alpha * user_record.k_u; // DH-OPRF paper recommends rotating 154 | println!("-) kU {:?}:", user_record.k_u); 155 | println!("-) vU {:?}:", user_record.v_u); 156 | println!("-> Envelope {:?}:", user_record.envelope); 157 | println!("-) beta {:?}:", beta); 158 | 159 | // SIGMA 160 | // KE2 = g^y, Sig(PrivS; g^x, g^y), Mac(Km1; IdS) 161 | 162 | // sidA: 163 | // sidB 164 | // g^y 165 | // nB 166 | // infoB 167 | 168 | let ke_2: RistrettoPoint = RISTRETTO_BASEPOINT_POINT * user_record.k_u; 169 | let dh: RistrettoPoint = user_record.k_u * ke_1; 170 | 171 | let hkdf = Hkdf::::new(None, dh.compress().as_bytes()); 172 | let mut okm = [0u8; 108]; // 32 byte key, 96 bit nonce, 64 bytes 173 | let info = hex::decode("f0f1f2f3f4f5f6f7f8f9").unwrap(); 174 | hkdf.expand(&info, &mut okm).unwrap(); 175 | 176 | let mut cspring = OsRng::new().unwrap(); 177 | let keypair: Keypair = Keypair::generate(&mut cspring); 178 | let public_key = keypair.public.to_bytes(); 179 | 180 | // MAC(Km; PubS) 181 | let mut mac = HmacSha512::new_varkey(&okm[44..108]).unwrap(); 182 | mac.input(&public_key); 183 | 184 | // SIG(B; g^x, g^y) 185 | let mut prehashed: Sha3_512 = Sha3_512::new(); 186 | prehashed.input(ke_1.compress().as_bytes()); 187 | prehashed.input(ke_2.compress().as_bytes()); 188 | let context: &[u8] = b"SpecificCustomerDomainName"; 189 | let sig: Signature = keypair.sign_prehashed(prehashed, Some(context)); 190 | 191 | println!("-) KE_2: {:?}", ke_2); 192 | println!("-) Shared Secret: {:?}", dh); 193 | println!("-) SIG(PrivS; g^x, g^y): {:?}", sig); 194 | 195 | let key_exchange = KeyExchange { 196 | identity: public_key, 197 | signature: &sig.to_bytes(), 198 | mac: mac.result().code().as_slice().to_vec(), 199 | }; 200 | 201 | // 202 | let encryption_key: GenericArray = 203 | GenericArray::clone_from_slice(&okm[0..32]); 204 | let aead = Aes256GcmSiv::new(encryption_key); 205 | let nonce: GenericArray = 206 | GenericArray::clone_from_slice(&okm[32..44]); 207 | 208 | let payload: Vec = bincode::serialize(&key_exchange).unwrap(); 209 | let encrypted_ke_2 = aead.encrypt(&nonce, payload.as_slice()).unwrap(); 210 | 211 | println!("-) DH encryption key 32-byte {:?}:", encryption_key); 212 | println!("-) DH nonce 96 bit {:?}:", nonce); 213 | 214 | // sidA, sidB, g^y, nB, info1B 215 | // gy, {B, SigB(g^x, g^y), MAC(Km; B)} Ke 216 | //let message = ke_1 + ke_2; 217 | // let sig = keypair.sign(message.to_bytes()); 218 | 219 | // Mac(Km1; IdS) 220 | // Km1 must be computationally independent from the authentication key 221 | 222 | ( 223 | beta.compress().to_bytes(), 224 | user_record.v_u.compress().to_bytes(), 225 | user_record.envelope.unwrap(), 226 | encrypted_ke_2, 227 | ke_2.compress().to_bytes(), 228 | ) 229 | } 230 | 231 | // NOTE: Think about gaming this function independent of authenticate_start 232 | pub fn authenticate_finalize(username: &str, ke_3: &Vec, x: &[u8; 32]) { 233 | println!("=> Rusty Authenticate Finalize"); 234 | println!("Key 3: {:?}:", ke_3); 235 | println!("X: {:?}:", x); 236 | let x_point = CompressedRistretto::from_slice(&x[..]); 237 | let x = x_point.decompress().unwrap(); 238 | 239 | let user_record: UserRecord = 240 | USER_MAP.lock().unwrap().get(username).unwrap().clone(); 241 | 242 | let ke_2: RistrettoPoint = RISTRETTO_BASEPOINT_POINT * user_record.k_u; 243 | let dh: RistrettoPoint = user_record.k_u * x; 244 | 245 | let hkdf = Hkdf::::new(None, dh.compress().as_bytes()); 246 | let mut okm_dh = [0u8; 108]; // 32 byte key, 96 bit nonce, 64 bytes 247 | let info = hex::decode("f0f1f2f3f4f5f6f7f8f9").unwrap(); 248 | hkdf.expand(&info, &mut okm_dh).unwrap(); 249 | 250 | println!("-) HKDF OKM {}", hex::encode(&okm_dh[..])); 251 | 252 | let encryption_key_dh: GenericArray = 253 | GenericArray::clone_from_slice(&okm_dh[0..32]); 254 | let aead_dh = Aes256GcmSiv::new(encryption_key_dh); 255 | let nonce_dh: GenericArray = 256 | GenericArray::clone_from_slice(&okm_dh[32..44]); 257 | 258 | println!("Encryption Key DH: {:?}", encryption_key_dh); 259 | println!("Nonce DH: {:?}", nonce_dh); 260 | println!("KE 3 Slice: {:?}", ke_3.as_slice()); 261 | println!("Key 3 Size: {}", ke_3.capacity()); 262 | let key_3_decrypted = aead_dh 263 | .decrypt(&nonce_dh, ke_3.as_slice()) 264 | .expect("decryption failure, authorization failed"); 265 | let key_3_for_realz: KeyExchange = 266 | bincode::deserialize(key_3_decrypted.as_slice()).unwrap(); 267 | 268 | let pub_u = PublicKey::from_bytes(&key_3_for_realz.identity).unwrap(); 269 | 270 | let mut prehashed: Sha3_512 = Sha3_512::new(); 271 | prehashed.input(ke_2.compress().as_bytes()); 272 | prehashed.input(x.compress().as_bytes()); 273 | let context: &[u8] = b"SpecificCustomerDomainName"; 274 | let signature: Signature = 275 | Signature::from_bytes(&key_3_for_realz.signature).unwrap(); 276 | let verified = pub_u.verify_prehashed::( 277 | prehashed, 278 | Some(context), 279 | &signature, 280 | ); 281 | 282 | // check Mac on A 283 | 284 | println!("=) {:?}", verified.unwrap()); 285 | 286 | println!("=) Signature Verified KE3 -- {} logged in.", username); 287 | } 288 | 289 | #[cfg(test)] 290 | mod tests { 291 | use super::*; 292 | 293 | #[test] 294 | fn register_and_authenticate() { 295 | let username = "jerryg"; 296 | let alpha: [u8; 32] = [ 297 | 108, 205, 75, 42, 225, 170, 25, 15, 62, 90, 122, 155, 240, 155, 298 | 225, 131, 110, 168, 70, 25, 251, 143, 69, 92, 254, 227, 213, 121, 299 | 165, 35, 195, 29, 300 | ]; 301 | let (_beta, _v, _pub_s) = registration_start(&username, &alpha); 302 | 303 | let pub_u: [u8; 32] = [ 304 | 207, 22, 253, 11, 52, 222, 99, 90, 81, 190, 238, 194, 251, 75, 74, 305 | 2, 16, 68, 164, 9, 40, 186, 224, 222, 166, 173, 192, 76, 53, 86, 306 | 179, 168, 307 | ]; 308 | 309 | let envelope: [u8; 112] = [ 310 | 190, 34, 241, 150, 92, 165, 105, 175, 203, 74, 47, 126, 227, 252, 311 | 3, 129, 68, 56, 16, 172, 107, 199, 253, 60, 9, 226, 1, 159, 124, 312 | 249, 242, 158, 246, 44, 59, 145, 1, 181, 237, 56, 210, 19, 100, 94, 313 | 128, 12, 253, 164, 41, 237, 190, 184, 9, 120, 85, 205, 53, 166, 97, 314 | 68, 137, 77, 174, 45, 249, 77, 175, 59, 143, 31, 14, 12, 111, 159, 315 | 6, 77, 154, 212, 80, 149, 99, 190, 191, 241, 16, 171, 226, 210, 42, 316 | 140, 26, 39, 193, 197, 31, 251, 56, 51, 151, 52, 236, 126, 66, 232, 317 | 191, 57, 69, 94, 53, 5, 163, 119, 318 | ]; 319 | registration_finalize(&username, &pub_u, &envelope.to_vec()); 320 | 321 | let alpha: [u8; 32] = [ 322 | 30, 200, 124, 246, 19, 85, 165, 91, 95, 234, 214, 93, 109, 14, 39, 323 | 114, 185, 129, 141, 141, 7, 234, 47, 147, 219, 183, 145, 117, 9, 324 | 116, 166, 49, 325 | ]; 326 | let key: [u8; 32] = [ 327 | 72, 79, 202, 45, 141, 212, 156, 96, 121, 69, 228, 3, 178, 12, 144, 328 | 236, 246, 53, 133, 85, 149, 25, 244, 215, 69, 178, 20, 242, 112, 329 | 154, 116, 41, 330 | ]; 331 | let (_beta, _v, _envelope, _key, _y) = 332 | authenticate_start(&username, &alpha, &key); 333 | 334 | let key: [u8; 192] = [ 335 | 54, 38, 180, 145, 237, 254, 52, 29, 136, 159, 110, 227, 159, 156, 336 | 188, 54, 70, 43, 111, 115, 25, 220, 33, 164, 92, 15, 222, 159, 100, 337 | 61, 22, 15, 177, 47, 28, 86, 157, 27, 13, 20, 111, 208, 198, 106, 338 | 94, 206, 236, 7, 253, 186, 85, 246, 206, 87, 169, 123, 138, 202, 339 | 59, 241, 204, 32, 69, 126, 65, 170, 247, 83, 248, 16, 248, 172, 74, 340 | 19, 52, 56, 48, 224, 106, 68, 163, 200, 228, 76, 138, 91, 132, 8, 341 | 223, 218, 221, 192, 46, 200, 183, 190, 17, 72, 102, 177, 218, 62, 342 | 255, 229, 102, 221, 60, 14, 125, 164, 225, 89, 156, 25, 82, 49, 343 | 147, 142, 60, 26, 27, 10, 81, 206, 255, 93, 182, 214, 159, 252, 344 | 135, 241, 201, 30, 101, 157, 80, 51, 231, 166, 82, 133, 209, 1, 345 | 250, 131, 7, 135, 245, 126, 197, 111, 141, 68, 244, 220, 202, 199, 346 | 0, 216, 203, 17, 0, 202, 34, 3, 140, 204, 131, 61, 34, 193, 74, 52, 347 | 148, 70, 92, 123, 201, 39, 35, 149, 173, 119, 63, 230, 108, 122, 348 | 158, 88, 19, 78, 349 | ]; 350 | let x: [u8; 32] = [ 351 | 72, 79, 202, 45, 141, 212, 156, 96, 121, 69, 228, 3, 178, 12, 144, 352 | 236, 246, 53, 133, 85, 149, 25, 244, 215, 69, 178, 20, 242, 112, 353 | 154, 116, 41, 354 | ]; 355 | authenticate_finalize(&username, &key.to_vec(), &x); 356 | } 357 | } 358 | -------------------------------------------------------------------------------- /src/bin/opaque.rs: -------------------------------------------------------------------------------- 1 | use crate::key_exchange::KeyExchange; 2 | use ::opaque::*; 3 | 4 | use aes_gcm_siv::aead::{generic_array::GenericArray, Aead, NewAead}; 5 | use aes_gcm_siv::Aes256GcmSiv; 6 | use curve25519_dalek::constants::RISTRETTO_BASEPOINT_POINT; 7 | use curve25519_dalek::ristretto::{CompressedRistretto, RistrettoPoint}; 8 | use curve25519_dalek::scalar::Scalar; 9 | use ed25519_dalek::{Keypair, Signature}; 10 | use hkdf::Hkdf; 11 | use hmac::Mac; 12 | use rand_os::OsRng; 13 | use serde::{Deserialize, Serialize}; 14 | use sha2::Sha512; 15 | use sha3::{Digest, Sha3_512}; 16 | 17 | // Warning: No security audits of the AES-GCM-Siv crate have ever been performed, 18 | // and it has not been thoroughly assessed to ensure its operation is 19 | // constant-time on common CPU architectures. 20 | 21 | // Where possible the implementation uses constant-time hardware intrinsics, 22 | // or otherwise falls back to an implementation which contains no 23 | // secret-dependent branches or table lookups, however it's possible LLVM 24 | // may insert such operations in certain scenarios. 25 | 26 | // When targeting modern x86/x86_64 CPUs, use RUSTFLAGS to take advantage 27 | // of high performance AES-NI and CLMUL CPU intrinsics 28 | 29 | #[derive(Serialize, Deserialize, Clone, Debug)] 30 | pub struct Envelope { 31 | pub priv_u: [u8; 32], 32 | pub pub_u: [u8; 32], 33 | pub pub_s: [u8; 32], 34 | } 35 | 36 | fn _oprf(alpha: &RistrettoPoint, g: &RistrettoPoint) -> RistrettoPoint { 37 | // OPAQUE uses a specific OPRF instantiation, called DH-OPRF, where the 38 | // PRF, denoted F, is defined as follows. 39 | 40 | // ***> Spec 41 | 42 | // Parameters: Hash function H (e.g., a SHA2 or SHA3 function), a cyclic 43 | // group G of prime order q, a generator g of G, and hash function H' 44 | // mapping arbitrary strings into G (where H' is modeled as a random 45 | // oracle). 46 | 47 | // o DH-OPRF domain: Any string 48 | // o DH-OPRF range: The range of the hash function H 49 | // o DH-OPRF key: A random element k in [0..q-1]; denote v=g^k 50 | // o DH-OPRF Operation: F(k; x) = H(x, v, H'(x)^k) 51 | 52 | // ***> Impl 53 | // Parameters: 54 | 55 | // Hash function H (e.g. a SHA2 or SHA3) 56 | // blake::hash(256, b"password", &mut alpha).unwrap(); 57 | 58 | // a cyclic group G of prime order q 59 | // -> G is the Ristretto group of prime order q 60 | 61 | // a generator g of G 62 | // -> g is something that can return a point from the group, 63 | // from the Ed25519 curve 64 | // RistrettoPoint::random(), which generates random points from an RNG; 65 | 66 | // Uses the Ristretto-flavoured Elligator 2 map, so that the discrete 67 | // log of the output point with respect to any other point should be unknown. 68 | // The map is applied twice and the results are added, to ensure a uniform 69 | // distribution. 70 | // let mut cspring: OsRng = OsRng::new().unwrap(); 71 | // let px = RistrettoPoint::random(&mut cspring); 72 | // println!("RistrettoPoint::random(): {:?}", px); 73 | 74 | // a hash function H' mapping arbitrary strings into G 75 | // where H' is modeled as a random oracle 76 | // -> This is the hashing of a string to an elliptical 77 | // curve point. 78 | // RistrettoPoint::from_hash() 79 | // let msg = "plaintext"; 80 | // let hash_prime = RistrettoPoint::hash_from_bytes::<>(msg.as_bytes()); 81 | // println!("Ristretto Point from hash prime function: {:?}", hash_prime); 82 | 83 | // DH-OPRF domain: any string 84 | // -> "plaintext" 85 | let _domain = "plaintext"; 86 | 87 | // DH-OPRF range: The range of the hash function H 88 | // -> what is the range of the blake2? is it 512? 2^512 89 | 90 | // DH-OPRF key: A random element k in [0..q-1]; denote v=g^k 91 | // -> This is the key the server generates to feed the OPRF. 92 | // The salt, v is the DH in the DH-OPRF 93 | 94 | // ***> Spec 95 | 96 | // Protocol for computing DH-OPRF, U with input x and S with input k: 97 | // o U: choose random r in [0..q-1], send alpha=H'(x)*g^r to S 98 | // o S: upon receiving a value alpha, respond with v=g^k and 99 | // beta=alpha^k 100 | // o U: upon receiving values beta and v, set the PRF output to 101 | // H(x, v, beta*v^{-r}) 102 | 103 | // ***> Impl 104 | 105 | // S: upon receiving a value alpha, respond with v=g^k and 106 | // beta=alpha^k 107 | let mut cspring = OsRng::new().unwrap(); 108 | /* Note: Think about feeding this to HDKF after generating with ChaCha */ 109 | let k = Scalar::random(&mut cspring); 110 | let _v = g * k; 111 | let beta = alpha * k; 112 | beta 113 | 114 | // alpha=(H'(x))^r in the first message and set the 115 | // function output to H(x,v,beta^{1/r}) 116 | } 117 | 118 | // https://tools.ietf.org/html/rfc7748 119 | /* 120 | 6. Diffie-Hellman 121 | 6.1. Curve25519 122 | 123 | The X25519 function can be used in an Elliptic Curve Diffie-Hellman 124 | (ECDH) protocol as follows: 125 | 126 | Alice generates 32 random bytes in a[0] to a[31] and transmits K_A = 127 | X25519(a, 9) to Bob, where 9 is the u-coordinate of the base point 128 | and is encoded as a byte with value 9, followed by 31 zero bytes. 129 | 130 | Bob similarly generates 32 random bytes in b[0] to b[31], computes 131 | K_B = X25519(b, 9), and transmits it to Alice. 132 | 133 | Using their generated values and the received input, Alice computes 134 | X25519(a, K_B) and Bob computes X25519(b, K_A). 135 | 136 | Both now share K = X25519(a, X25519(b, 9)) = X25519(b, X25519(a, 9)) 137 | as a shared secret. Both MAY check, without leaking extra 138 | information about the value of K, whether K is the all-zero value and 139 | abort if so (see below). Alice and Bob can then use a key-derivation 140 | function that includes K, K_A, and K_B to derive a symmetric key. 141 | 142 | The check for the all-zero value results from the fact that the 143 | X25519 function produces that value if it operates on an input 144 | corresponding to a point with small order, where the order divides 145 | the cofactor of the curve (see Section 7). The check may be 146 | performed by ORing all the bytes together and checking whether the 147 | result is zero, as this eliminates standard side-channels in software 148 | implementations. 149 | 150 | Test vector: 151 | 152 | Alice's private key, a: 77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a 153 | Alice's public key, X25519(a, 9): 154 | 8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a 155 | Bob's private key, b: 156 | 5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb 157 | Bob's public key, X25519(b, 9): 158 | de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f 159 | Their shared secret, K: 160 | 4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742 161 | */ 162 | fn _dh() { 163 | // instead of transmitting a value “x”, the value “2^x (mod p)” is transmitted, 164 | // where “p” is a large prime number greater than 10⁶⁰⁰. 165 | // 166 | // The “mod p” expression means that all multiples of a prime number “p” 167 | // are removed from the computed value, in order to make the result smaller 168 | // than “p”. 169 | // 170 | // It would be easy for small “x”, but hard for values greater than 10⁶⁰⁰. 171 | // 172 | // In the end, the expression 2^(xy) forms the basis to derive a cipher key. 173 | // (The other user receives 2^y and forms 2^y^x.) 174 | // 175 | // a^(p-1) = 1 (mod p) 176 | // 177 | 178 | /*let mut cspring = OsRng::new().unwrap(); 179 | let alice_secret = EphemeralSecret::new(&mut cspring); 180 | let alice_public = PublicKey::from(&alice_secret); 181 | 182 | let bob_secret = EphemeralSecret::new(&mut cspring); 183 | let bob_public = PublicKey::from(&bob_secret); 184 | 185 | let alice_shared_secret = alice_secret.diffie_hellman(&bob_public); 186 | let bob_shared_secret = bob_secret.diffie_hellman(&alice_public); 187 | println!("Alice's Shared: {:?}", alice_shared_secret.as_bytes()); 188 | println!("Bob's Shared: {:?}", bob_shared_secret.as_bytes());*/ 189 | } 190 | 191 | fn main() { 192 | println!("`~- OPAQUE -~'"); 193 | 194 | // ***> Spec 195 | 196 | // 3.1. Password registration 197 | // https://tools.ietf.org/html/draft-krawczyk-cfrg-opaque-02#section-3.1 198 | // Password registration is run between a user U and a server S. 199 | 200 | // User / Client 201 | 202 | // U chooses password PwdU and a pair of private-public keys PrivU 203 | // and PubU for the given protocol KE 204 | 205 | // CSPRING: just using OS's PRNG for now 206 | let mut cspring = OsRng::new().unwrap(); 207 | let keypair: Keypair = Keypair::generate(&mut cspring); 208 | 209 | let priv_u = keypair.secret.to_bytes(); 210 | let pub_u = keypair.public.to_bytes(); 211 | 212 | // basic password for now 213 | let username = "jerryg"; 214 | let pwd_u = "fizzbangpopdog"; 215 | 216 | println!("-------------------------------------------------------"); 217 | println!("~)-------------------| Registering :-> usename: {} : password: {} <--------", username, pwd_u); 218 | // Protocol for computing DH-OPRF, U with input x and S with input k: 219 | // U: choose random r in [0..q-1], send alpha=H'(x)*g^r to S 220 | 221 | // The simplified form with the base point factor dropped: 222 | // spec: alpha=(H'(x))^r in the first message and set the 223 | // function output to H(x,v,beta^{1/r}) 224 | println!("*) alpha=H'(x)^r"); 225 | let r = Scalar::random(&mut cspring); 226 | let hash_prime = 227 | RistrettoPoint::hash_from_bytes::(pwd_u.as_bytes()); 228 | let alpha: RistrettoPoint = hash_prime * r; 229 | // Guard: alpha should be authenticated when sent 230 | println!("-) r {:?}:", r); 231 | println!("-) H' {:?}:", hash_prime); 232 | println!("-) alpha {:?}:", alpha); 233 | 234 | let alpha = alpha.compress().to_bytes(); 235 | 236 | let (beta, v, pub_s) = registration_start(username, &alpha); 237 | // from u8, 32 to Ristretto Points 238 | let beta_point = CompressedRistretto::from_slice(&beta[..]); 239 | let beta = beta_point.decompress().unwrap(); 240 | 241 | let v_point = CompressedRistretto::from_slice(&v[..]); 242 | let v = v_point.decompress().unwrap(); 243 | 244 | println!("-) beta: {:?} ", beta); 245 | println!("-) v: {:?} ", v); 246 | println!("-) PubS {:?}:", pub_s); 247 | 248 | // Guard: Ensure v and beta are in the Group 249 | 250 | // U: upon receiving values beta and v, set the PRF output to 251 | // H(x, v, beta*v^{-r}) 252 | 253 | // simplified: 254 | // set the function output to H(x,v,beta^{1/r}) 255 | 256 | let inverse_r = r.invert(); 257 | let sub_beta = beta * inverse_r; 258 | 259 | println!("-) {{1/r}} {:?}:", inverse_r); 260 | println!("-) beta^{{1/r}} {:?}:", sub_beta); 261 | 262 | // serialize then hash: 263 | // https://jameshfisher.com/2018/01/09/how-to-hash-multiple-values/ 264 | // attack with non-injectivity of concatenation: 265 | // https://sakurity.com/blog/2015/05/08/pusher.html 266 | 267 | // U and S run OPRF(kU;PwdU) as defined in Section 2 with only U 268 | // learning the result, denoted RwdU (mnemonics for "Randomized 269 | // PwdU"). 270 | 271 | println!("*) H(x, v, beta^{{1/r}}"); 272 | let mut hasher = Sha3_512::new(); 273 | // assuming multiple inputs create a unique hash not just concating, verse serializing 274 | hasher.input(pwd_u.as_bytes()); 275 | hasher.input(v.compress().to_bytes()); 276 | hasher.input(sub_beta.compress().to_bytes()); 277 | let rwd_u = hasher.result(); 278 | 279 | println!("-) RwdU: {:?}:", rwd_u); 280 | 281 | // U generates an "envelope" EnvU defined as 282 | // EnvU = AuthEnc(RwdU; PrivU, PubU, PubS) 283 | 284 | // where AuthEnc is an authenticated encryption function with the 285 | // "key committing" property and is specified in Section 3.1.1 below. 286 | // In EnvU, all values require authentication and PrivU also requires 287 | // encryption. However, for simplicity and to hide the EnvU 288 | // contents, we specify that all values are encrypted (not just 289 | // authenticated). PubU can be omitted from EnvU if it is not needed 290 | // for running the key-exchange protocol by the client or if it can 291 | // be reconstructed from PrivU. 292 | 293 | // Encrypt-then-HMAC: Implement the AuthEnc scheme using any 294 | // encryption function in encrypt-then-pad-then-MAC mode where the 295 | // MAC is implemented with HMAC with a tag size of at least 256 bits (HMAC 296 | // ensures robustness through the collision-resistant property of the 297 | // underlying hash function). This requires two separate keys, one for 298 | // encryption and one for HMAC, which can be derived from RwdU using, 299 | // for example, the HKDF-Expand function from [RFC5869]. 300 | 301 | // Encrypt-then-Mac: https://tools.ietf.org/html/rfc7 302 | // https://signal.org/docs/specifications/doubleratchet/#recommended-cryptographic-algorithms 303 | 304 | let envelope = Envelope { 305 | priv_u, 306 | pub_u, 307 | pub_s, 308 | }; 309 | 310 | println!("=) EnvU {:?}:2", envelope); 311 | // HKDF: HMAC-based Extract-and-Expand:https://tools.ietf.org/html/rfc5869 312 | // see section on to "salt or not to salt", currently not salting 313 | let hkdf = Hkdf::::new(None, &rwd_u); 314 | let mut output_key_material = [0u8; 44]; // 32 byte key + 96 bit nonce 315 | let info = hex::decode("f0f1f2f3f4f5f6f7f8f9").unwrap(); // NOTE: check info value 316 | hkdf.expand(&info, &mut output_key_material).unwrap(); 317 | 318 | println!("-) Hkdf Okm is {}", hex::encode(&output_key_material[..])); 319 | 320 | // AES-GCM-SIV 321 | // Guard: Draft 4 clarifies that GCM can be used but should be HMAC too 322 | // AES-CTR is now the example in the more detailed explanation of AEenv 323 | // standard GCM is not random key robust, since we HKDF should be 324 | 325 | let encryption_key: GenericArray = 326 | GenericArray::clone_from_slice(&output_key_material[0..32]); 327 | let aead = Aes256GcmSiv::new(encryption_key); 328 | let nonce: GenericArray = 329 | GenericArray::clone_from_slice(&output_key_material[32..44]); 330 | 331 | let payload: Vec = bincode::serialize(&envelope).unwrap(); 332 | let env_cipher = aead.encrypt(&nonce, payload.as_slice()).unwrap(); 333 | 334 | println!("-) encryption key 32-byte {:?}:", encryption_key); 335 | println!("-) nonce 96 bit {:?}:", nonce); 336 | println!("-) AuthEnv: AES-GCM-SIV Cipher Envelope {:?} :", env_cipher); 337 | 338 | // Section 3.1.1 Implementing the EnvU envelop 339 | 340 | // U sends EnvU and PubU to S and erases PwdU, RwdU and all keys. 341 | registration_finalize(username, &pub_u, &env_cipher); 342 | 343 | // C to S: Uid, alpha=H'(PwdU)*g^r, KE1 344 | 345 | //let pwd_u_a = "fzzbangpop"; 346 | let pwd_u_a = pwd_u; 347 | 348 | println!("~) ---------------------| Authenticating: -> usename: {} : password: {} <--------", username, pwd_u); 349 | 350 | println!("*) alpha=H'(x)^r"); 351 | let r_a = Scalar::random(&mut cspring); 352 | let hash_prime_a = 353 | RistrettoPoint::hash_from_bytes::(pwd_u_a.as_bytes()); 354 | let alpha_a: RistrettoPoint = hash_prime_a * r_a; 355 | println!("-) r {:?}:", r_a); 356 | println!("-) H' {:?}:", hash_prime_a); 357 | println!("-) alpha {:?}:", alpha_a); 358 | 359 | // S to C: beta=alpha^kU, vU, EnvU, KE2 360 | // spec: alpha=(H'(x))^r in the first message and set the 361 | // function output to H(x,v,beta^{1/r}) 362 | 363 | // SIGMA-I 364 | // sidA, g^x, nA, infoA 365 | // sidA: session identifier chosen by each party for the ongoing session, is returned by B 366 | // g^x: basepoint times random scalar x 367 | // nA: nonce, fresh and anew with each session 368 | // info: any additional info to be carried in the protocol, not required (could be protocol 369 | // name, version, message number, etc) 370 | 371 | // Guard: consider using x25519_dalek ephemeral keys, though no signing 372 | let x = Scalar::random(&mut cspring); 373 | let ke_1 = RISTRETTO_BASEPOINT_POINT * x; 374 | let _n_a = ""; 375 | let _sida = 1; 376 | 377 | println!("-) KE_1: {:?}", ke_1); 378 | let (beta_a, v_a, envelope_a, ke_2, y) = authenticate_start( 379 | username, 380 | &alpha_a.compress().as_bytes(), 381 | &ke_1.compress().as_bytes(), 382 | ); 383 | 384 | let beta_point = CompressedRistretto::from_slice(&beta_a[..]); 385 | let beta_a = beta_point.decompress().unwrap(); 386 | 387 | let v_point = CompressedRistretto::from_slice(&v_a[..]); 388 | let v_a = v_point.decompress().unwrap(); 389 | 390 | let y_point = CompressedRistretto::from_slice(&y[..]); 391 | let y = y_point.decompress().unwrap(); 392 | 393 | // OPRF 394 | 395 | println!("-) beta {:?}:", beta_a); 396 | println!("-) v {:?}:", v_a); 397 | println!("-) KE 2: {:?}", ke_2); 398 | println!("-) KE 2 Size: {:?}", ke_2.capacity()); 399 | println!("-) AuthEnv {:?}:", envelope_a); 400 | println!("-) AuthEnv Size {:?}:", envelope_a.capacity()); 401 | 402 | let inverse_r_a = r_a.invert(); 403 | let sub_beta_a = beta_a * inverse_r_a; 404 | 405 | println!("-) {{1/r}} {:?}:", inverse_r_a); 406 | println!("-) beta^{{1/r}} {:?}:", sub_beta_a); 407 | 408 | println!("*) RwdU = H(x, v, beta^{{1/r}})"); 409 | 410 | let mut hasher_a = Sha3_512::new(); 411 | hasher_a.input(pwd_u_a.as_bytes()); // NOTE: Harden with a key derivitive, Section 3.4 412 | hasher_a.input(v_a.compress().to_bytes()); 413 | hasher_a.input(sub_beta_a.compress().to_bytes()); 414 | let rwd_u_a = hasher_a.result(); 415 | 416 | println!("-) RwdU {:?}:", rwd_u_a); 417 | 418 | // Use rwd_u_a to decrypt envelope 419 | 420 | let hkdf_a = Hkdf::::new(None, &rwd_u_a); 421 | let mut okm_a = [0u8; 44]; // 32 byte key + 96 bit nonce 422 | let info_a = hex::decode("f0f1f2f3f4f5f6f7f8f9").unwrap(); // make info the domain string, + 423 | hkdf_a.expand(&info_a, &mut okm_a).unwrap(); 424 | 425 | println!("-) HKDF OKM {}", hex::encode(&okm_a[..])); 426 | 427 | let encryption_key_a: GenericArray = 428 | GenericArray::clone_from_slice(&okm_a[0..32]); 429 | let aead = Aes256GcmSiv::new(encryption_key_a); 430 | let nonce_a: GenericArray = 431 | GenericArray::clone_from_slice(&okm_a[32..44]); 432 | 433 | println!("-) encryption key 32-byte {:?}:", encryption_key_a); 434 | println!("-) nonce 96 bit {:?}:", nonce_a); 435 | 436 | let envelope_decrypted = aead 437 | .decrypt(&nonce, envelope_a.as_slice()) 438 | .expect("decryption failure"); 439 | let envelope_for_realz: Envelope = 440 | bincode::deserialize(envelope_decrypted.as_slice()).unwrap(); 441 | 442 | println!("=) EnvU (decoded) {:?}:", envelope_for_realz); 443 | 444 | // SIGMA 445 | 446 | // KE3 = Sig(PrivU; g^y, g^x), Mac(Km2; IdU) 447 | // { A, SIGa(g^y, g^x), MAC(Km; A) } Ke 448 | 449 | // decrypt ke_2 450 | let dh: RistrettoPoint = x * y; 451 | println!("-) Shared Secret: {:?}", dh); 452 | 453 | let hkdf = Hkdf::::new(None, dh.compress().as_bytes()); 454 | let mut okm_dh = [0u8; 108]; // 32 byte key, 96 bit nonce, 64 bytes 455 | let info = hex::decode("f0f1f2f3f4f5f6f7f8f9").unwrap(); 456 | hkdf.expand(&info, &mut okm_dh).unwrap(); 457 | 458 | let encryption_key_dh: GenericArray = 459 | GenericArray::clone_from_slice(&okm_dh[0..32]); 460 | let aead_dh = Aes256GcmSiv::new(encryption_key_dh); 461 | let nonce_dh: GenericArray = 462 | GenericArray::clone_from_slice(&okm_dh[32..44]); 463 | 464 | println!("-) DH encryption key 32-byte {:?}:", encryption_key_dh); 465 | println!("-) DH nonce 96 bit {:?}:", nonce_dh); 466 | // Guard: verify HMAC on B 467 | 468 | let mut cspring = OsRng::new().unwrap(); 469 | let keypair: Keypair = Keypair::generate(&mut cspring); 470 | 471 | let _priv_u = keypair.secret.to_bytes(); 472 | let pub_u = keypair.public.to_bytes(); 473 | 474 | let key_2_decrypted = aead_dh 475 | .decrypt(&nonce_dh, ke_2.as_slice()) 476 | .expect("decryption failure"); 477 | let _key_2_for_realz: KeyExchange = 478 | bincode::deserialize(key_2_decrypted.as_slice()).unwrap(); 479 | 480 | // SIGa(g^y, g^x) 481 | let mut prehashed: Sha3_512 = Sha3_512::new(); 482 | prehashed.input(y.compress().as_bytes()); 483 | prehashed.input(ke_1.compress().as_bytes()); 484 | let context: &[u8] = b"SpecificCustomerDomainName"; 485 | let sig: Signature = keypair.sign_prehashed(prehashed, Some(context)); 486 | 487 | // MAC(Km; PubU) 488 | let mut mac = HmacSha512::new_varkey(&okm_dh[44..108]).unwrap(); 489 | mac.input(&pub_u); 490 | 491 | let key_exchange_3 = KeyExchange { 492 | identity: pub_u, 493 | signature: &sig.to_bytes(), 494 | mac: mac.result().code().as_slice().to_vec(), 495 | }; 496 | 497 | let payload_dh: Vec = bincode::serialize(&key_exchange_3).unwrap(); 498 | let encrypted_ke_3 = 499 | aead_dh.encrypt(&nonce_dh, payload_dh.as_slice()).unwrap(); 500 | 501 | // sidA 502 | // sidB 503 | // { infoA, A, sigA(nB, sidA, g^x, infoA, infoA), MAC kM(A)} Ke 504 | 505 | // let ke_3 = ke_2; 506 | 507 | authenticate_finalize( 508 | username, 509 | &encrypted_ke_3, 510 | &ke_1.compress().as_bytes(), 511 | ); 512 | 513 | // run the specified KE protocol using their respective public and 514 | // private keys 515 | 516 | // For the authenticated variant, the same computations are done; but Alice 517 | // and Bob also own asymmetric key pairs usable for digital signatures, and 518 | // they use them: Alice signs whatever she sends to Bob, and Bob verifies that 519 | // signature (using Alice's public key). Similarly, Bob signs what he sends to 520 | // Alice, and Alice verifies that signature (using Bob's public key). 521 | 522 | /* 523 | o C to S: Uid, alpha=H'(PwdU)*g^r, KE1 524 | o S to C: beta=alpha^kU, vU, EnvU, KE2 525 | o C to S: KE3 526 | */ 527 | /* 528 | SIGMA-I can be represented schematically as follows: 529 | o KE1 = g^x 530 | o KE2 = g^y, Sig(PrivS; g^x, g^y), Mac(Km1; IdS) 531 | o KE3 = Sig(PrivU; g^y, g^x), Mac(Km2; IdU) 532 | In this case, the private keys of user and server are signature keys. 533 | Key derivation is based on the DH value g^xy. 534 | */ 535 | } 536 | -------------------------------------------------------------------------------- /tests/sample_run.txt: -------------------------------------------------------------------------------- 1 | `~- OPAQUE -~' 2 | ------------------------------------------------------- 3 | ~)-------------------| Registering :-> usename: jerryg : password: fizzbangpopdog <-------- 4 | *) alpha=H'(x)^r 5 | -) r Scalar{ 6 | bytes: [31, 151, 220, 41, 192, 20, 6, 245, 127, 107, 71, 102, 98, 47, 107, 15, 230, 225, 107, 7, 111, 165, 204, 149, 131, 4, 39, 241, 213, 211, 251, 14], 7 | }: 8 | -) H' RistrettoPoint: coset 9 | EdwardsPoint{ 10 | X: FieldElement51([656038762436102, 1406799287371356, 1786348238230911, 1058876562689887, 11118654170493]), 11 | Y: FieldElement51([959814791126277, 1968753491008071, 312691502780877, 749646495148630, 899946152112758]), 12 | Z: FieldElement51([630423016627832, 1401755870117647, 117469048817406, 403445336527096, 1048475791944265]), 13 | T: FieldElement51([620600270890862, 1632126082154538, 1619817918217783, 1145171700720135, 413618806624035]) 14 | } 15 | EdwardsPoint{ 16 | X: FieldElement51([926859821970023, 2239404452234593, 744550933334533, 2077120592442132, 1627460021181784]), 17 | Y: FieldElement51([1997864973174743, 1468515802607346, 1729650602265619, 1749317835188492, 216729960542505]), 18 | Z: FieldElement51([1466581082851814, 1438021339750375, 1822409516466612, 422749613502192, 1473883459471993]), 19 | T: FieldElement51([294344572748418, 826018863005164, 1419148499208045, 307081474301373, 1142854431936255]) 20 | } 21 | EdwardsPoint{ 22 | X: FieldElement51([907753377086211, 704350107265182, 2183554946857787, 1023907663744292, 39928316127327]), 23 | Y: FieldElement51([2113614720538830, 706792819160827, 1353075996279103, 1787148942634557, 350869282068728]), 24 | Z: FieldElement51([1466581082851814, 1438021339750375, 1822409516466612, 422749613502192, 1473883459471993]), 25 | T: FieldElement51([1957455240936811, 1425780950680083, 832651314477202, 1944718339383874, 1108945381748992]) 26 | } 27 | EdwardsPoint{ 28 | X: FieldElement51([1324939991715206, 12395361450654, 1507248880350714, 174679221243115, 624339792503463]), 29 | Y: FieldElement51([253934840510486, 783284011077901, 522149211419628, 502481978496755, 2035069853142742]), 30 | Z: FieldElement51([1466581082851814, 1438021339750375, 1822409516466612, 422749613502192, 1473883459471993]), 31 | T: FieldElement51([294344572748418, 826018863005164, 1419148499208045, 307081474301373, 1142854431936255]) 32 | }: 33 | -) alpha RistrettoPoint: coset 34 | EdwardsPoint{ 35 | X: FieldElement51([1600652505505316, 830487947397095, 1014386934780472, 831211131496547, 436193473317019]), 36 | Y: FieldElement51([1355428259869953, 1933311700141164, 31840836630386, 761394947982412, 2220564147298807]), 37 | Z: FieldElement51([746002644989909, 1637144893980928, 1173768258925909, 1862841188510406, 1510162270611515]), 38 | T: FieldElement51([979955233218280, 799840812893181, 6638888290137, 394762124734459, 1706267416473270]) 39 | } 40 | EdwardsPoint{ 41 | X: FieldElement51([1324729385404102, 672761565230603, 1107444730328400, 1919744379788999, 2122415223960812]), 42 | Y: FieldElement51([2058837015133295, 2158679897679743, 923953217327857, 1294228390903445, 1822772152270440]), 43 | Z: FieldElement51([605934360686084, 847614908120858, 387043104394771, 63249300154150, 923061288849602]), 44 | T: FieldElement51([957678222533836, 1410339811585857, 1662612735666510, 1192811088414830, 811171977007440]) 45 | } 46 | EdwardsPoint{ 47 | X: FieldElement51([185274360977740, 1933893139444353, 960994401331437, 2057533652787401, 905201074056775]), 48 | Y: FieldElement51([291731429772142, 1834363345217561, 2029276071927923, 461103867703857, 851901789617998]), 49 | Z: FieldElement51([605934360686084, 847614908120858, 387043104394771, 63249300154150, 923061288849602]), 50 | T: FieldElement51([1294121591151393, 841460002099390, 589187078018737, 1058988725270417, 1440627836677807]) 51 | } 52 | EdwardsPoint{ 53 | X: FieldElement51([927070428281127, 1579038248454644, 1144355083356847, 332055433896248, 129384589724435]), 54 | Y: FieldElement51([192962798551934, 93119916005504, 1327846596357390, 957571422781802, 429027661414807]), 55 | Z: FieldElement51([605934360686084, 847614908120858, 387043104394771, 63249300154150, 923061288849602]), 56 | T: FieldElement51([957678222533836, 1410339811585857, 1662612735666510, 1192811088414830, 811171977007440]) 57 | }: 58 | => Rusty Registration Start 59 | Alpha: [88, 162, 223, 153, 58, 23, 143, 49, 125, 24, 69, 244, 198, 191, 210, 64, 45, 194, 101, 205, 212, 79, 146, 54, 5, 92, 83, 90, 125, 255, 199, 40] 60 | *) beta=alpha^kU, vU (g^k) 61 | -) kU Scalar{ 62 | bytes: [72, 219, 30, 174, 136, 224, 221, 154, 129, 179, 224, 240, 176, 80, 51, 89, 142, 115, 121, 93, 100, 240, 161, 72, 201, 179, 17, 192, 253, 253, 168, 15], 63 | }: 64 | -) vU RistrettoPoint: coset 65 | EdwardsPoint{ 66 | X: FieldElement51([141830018985727, 1767058257705176, 582413371802165, 1751169942631293, 456889550964492]), 67 | Y: FieldElement51([1634253091467716, 152699512212120, 348508661982919, 863899673547945, 198889406973460]), 68 | Z: FieldElement51([279626663930087, 1505095676730860, 387793512806261, 1532067127606151, 1522486376402507]), 69 | T: FieldElement51([2167469557821672, 113613036851606, 1301283937963992, 249395150197640, 59212944412185]) 70 | } 71 | EdwardsPoint{ 72 | X: FieldElement51([2069300258955501, 247502068746542, 382495250528636, 706107622929503, 674984118482143]), 73 | Y: FieldElement51([1747219753951041, 916719426590104, 566537028582569, 864211246387401, 1595466952096594]), 74 | Z: FieldElement51([2161322056549397, 1220464154325517, 215985417139774, 2152758900119945, 2121543940471468]), 75 | T: FieldElement51([2197679353142357, 567127916689108, 1523997797022097, 1902900670850672, 675078168201845]) 76 | } 77 | EdwardsPoint{ 78 | X: FieldElement51([496413701249252, 1050111803728136, 1029253815549060, 1160197742710982, 1325753197297868]), 79 | Y: FieldElement51([470035710382352, 1866658756805717, 1606443942245097, 1297349095272561, 1836216790915790]), 80 | Z: FieldElement51([2161322056549397, 1220464154325517, 215985417139774, 2152758900119945, 2121543940471468]), 81 | T: FieldElement51([54120460542872, 1684671896996139, 727802016663150, 348899142834575, 1576721645483402]) 82 | } 83 | EdwardsPoint{ 84 | X: FieldElement51([182499554729728, 2004297744938705, 1869304563156611, 1545692190755744, 1576815695203104]), 85 | Y: FieldElement51([504580059734188, 1335080387095143, 1685262785102678, 1387588567297846, 656332861588653]), 86 | Z: FieldElement51([2161322056549397, 1220464154325517, 215985417139774, 2152758900119945, 2121543940471468]), 87 | T: FieldElement51([2197679353142357, 567127916689108, 1523997797022097, 1902900670850672, 675078168201845]) 88 | }: 89 | -) beta RistrettoPoint: coset 90 | EdwardsPoint{ 91 | X: FieldElement51([811105810847075, 938583071700325, 2145385275025704, 1187401308112980, 1968956564512194]), 92 | Y: FieldElement51([1047186645031593, 12020673541121, 310160036555013, 225115304742322, 119947711248456]), 93 | Z: FieldElement51([1407311596451927, 1399945067367462, 2181717336574160, 1128974160405187, 1590851891589354]), 94 | T: FieldElement51([1818883355894355, 41472109585809, 2208866267115438, 1099015503060749, 471040918708922]) 95 | } 96 | EdwardsPoint{ 97 | X: FieldElement51([520091212783272, 1921542752746579, 2004431328716360, 913864702424300, 1096286898738656]), 98 | Y: FieldElement51([1002753741104772, 1384891013355326, 1260466598653682, 2149065591328799, 337008165495571]), 99 | Z: FieldElement51([1504457662585306, 2093129448234846, 122608910148726, 2208274285061941, 1161042306647760]), 100 | T: FieldElement51([1016657292952053, 1502708070521349, 2213575547224588, 143770231895556, 2153907054934001]) 101 | } 102 | EdwardsPoint{ 103 | X: FieldElement51([1084682336385289, 1730858354018041, 259517108511935, 1116801467463132, 627239623846012]), 104 | Y: FieldElement51([887862158014290, 458272371171288, 236796896104092, 1271028020290410, 456845955377968]), 105 | Z: FieldElement51([1504457662585306, 2093129448234846, 122608910148726, 2208274285061941, 1161042306647760]), 106 | T: FieldElement51([1235142520733176, 749091743163898, 38224266460659, 2108029581789691, 97892758751246]) 107 | } 108 | EdwardsPoint{ 109 | X: FieldElement51([1731708600901957, 330257060938668, 247368484968887, 1337935111260947, 1155512914946591]), 110 | Y: FieldElement51([1249046072580457, 866908800329921, 991333215031565, 102734222356448, 1914791648189676]), 111 | Z: FieldElement51([1504457662585306, 2093129448234846, 122608910148726, 2208274285061941, 1161042306647760]), 112 | T: FieldElement51([1016657292952053, 1502708070521349, 2213575547224588, 143770231895556, 2153907054934001]) 113 | } 114 | -) beta: RistrettoPoint: coset 115 | EdwardsPoint{ 116 | X: FieldElement51([1201448845771174, 1358155948990312, 1688129303612717, 515920638392224, 358280254916972]), 117 | Y: FieldElement51([480210125079371, 1057605275079392, 345904042789727, 2205980594141763, 1279253591168736]), 118 | Z: FieldElement51([1, 0, 0, 0, 0]), 119 | T: FieldElement51([1178741173043872, 1272857694494616, 1896076090732406, 30998299833524, 1152876568941596]) 120 | } 121 | EdwardsPoint{ 122 | X: FieldElement51([1012714850801719, 1773970014857983, 1517939297237611, 1255728019199780, 1035808028531896]), 123 | Y: FieldElement51([288584999399602, 926934435263000, 164671239092964, 323762367995576, 1458928556841378]), 124 | Z: FieldElement51([2251799813685233, 2251799813685247, 2251799813685247, 2251799813685247, 2251799813685247]), 125 | T: FieldElement51([2040434748880199, 1663968663077277, 1422894891811365, 2127806614351148, 2143893165289359]) 126 | } 127 | EdwardsPoint{ 128 | X: FieldElement51([1949604057971029, 1322775645094493, 2882226604873, 188117260116349, 818678794017359]), 129 | Y: FieldElement51([330959313367707, 273178527052927, 868183642526338, 183276878173939, 1638385076380796]), 130 | Z: FieldElement51([2251799813685233, 2251799813685247, 2251799813685247, 2251799813685247, 2251799813685247]), 131 | T: FieldElement51([211365064805030, 587831150607970, 828904921873882, 123993199334099, 107906648395888]) 132 | } 133 | EdwardsPoint{ 134 | X: FieldElement51([1239084962883510, 477829798827264, 733860516447636, 996071794485467, 1215991785153351]), 135 | Y: FieldElement51([1963214814285627, 1324865378422247, 2087128574592283, 1928037445689671, 792871256843869]), 136 | Z: FieldElement51([2251799813685233, 2251799813685247, 2251799813685247, 2251799813685247, 2251799813685247]), 137 | T: FieldElement51([2040434748880199, 1663968663077277, 1422894891811365, 2127806614351148, 2143893165289359]) 138 | } 139 | -) v: RistrettoPoint: coset 140 | EdwardsPoint{ 141 | X: FieldElement51([447099709561120, 2242269474573883, 375815961801930, 1273472360684429, 589520544266284]), 142 | Y: FieldElement51([1750352109427009, 564570672487712, 1346775051008481, 1654630755263711, 945345763357169]), 143 | Z: FieldElement51([1, 0, 0, 0, 0]), 144 | T: FieldElement51([195242660169910, 1005486787525866, 1622538249471461, 371206692635354, 1046436440959547]) 145 | } 146 | EdwardsPoint{ 147 | X: FieldElement51([596428554112178, 659000259389882, 1750196669083677, 1166465300942101, 2022303615995589]), 148 | Y: FieldElement51([797081788406996, 422600318808974, 1993401919640845, 1871081322418192, 368736206911110]), 149 | Z: FieldElement51([2251799813685233, 2251799813685247, 2251799813685247, 2251799813685247, 2251799813685247]), 150 | T: FieldElement51([1470829173005570, 481652477267031, 265246443169898, 766973043143829, 317853863532307]) 151 | } 152 | EdwardsPoint{ 153 | X: FieldElement51([463400975440730, 38121356445459, 748535966477524, 1661509998318027, 2145517450305357]), 154 | Y: FieldElement51([2005790817032918, 2245316937419644, 1368299237021818, 136876420000897, 722216573941817]), 155 | Z: FieldElement51([2251799813685233, 2251799813685247, 2251799813685247, 2251799813685247, 2251799813685247]), 156 | T: FieldElement51([780970640679659, 1770147336418216, 1986553370515349, 1484826770541418, 1933945950152940]) 157 | } 158 | EdwardsPoint{ 159 | X: FieldElement51([1655371259573051, 1592799554295365, 501603144601570, 1085334512743146, 229496197689658]), 160 | Y: FieldElement51([1454718025278233, 1829199494876273, 258397894044402, 380718491267055, 1883063606774137]), 161 | Z: FieldElement51([2251799813685233, 2251799813685247, 2251799813685247, 2251799813685247, 2251799813685247]), 162 | T: FieldElement51([1470829173005570, 481652477267031, 265246443169898, 766973043143829, 317853863532307]) 163 | } 164 | -) PubS [108, 190, 94, 128, 247, 104, 54, 132, 158, 58, 101, 222, 166, 246, 24, 89, 39, 102, 197, 248, 96, 199, 168, 85, 73, 123, 128, 74, 157, 234, 144, 96]: 165 | -) {1/r} Scalar{ 166 | bytes: [97, 85, 184, 147, 186, 252, 45, 66, 184, 168, 80, 252, 11, 89, 222, 243, 92, 196, 175, 154, 48, 123, 206, 113, 49, 243, 19, 150, 21, 119, 107, 7], 167 | }: 168 | -) beta^{1/r} RistrettoPoint: coset 169 | EdwardsPoint{ 170 | X: FieldElement51([821447927599083, 1612487010152781, 1849048668629571, 1407518842023712, 2114182587409594]), 171 | Y: FieldElement51([1756484406584503, 1946424967846516, 2203008844674103, 1355665039398702, 740030508496073]), 172 | Z: FieldElement51([427144811358890, 419634821435427, 1786914308686314, 1829338116259406, 1026005279551549]), 173 | T: FieldElement51([752733198117799, 1136259361829528, 512211815110204, 722390286392503, 2052374905877610]) 174 | } 175 | EdwardsPoint{ 176 | X: FieldElement51([1947900700595116, 442044612304738, 696701124215138, 1494530138651785, 324088829845638]), 177 | Y: FieldElement51([513019210632773, 1826575263435590, 2183283956022117, 472439460966198, 347127751063513]), 178 | Z: FieldElement51([516909798874657, 520574902282016, 995935568209254, 1431354793481376, 1213658785624019]), 179 | T: FieldElement51([460292496653623, 634032438322227, 467272288966113, 1828440168641301, 97857665301540]) 180 | } 181 | EdwardsPoint{ 182 | X: FieldElement51([422654135135503, 945034584534189, 349570977320354, 641893843393160, 722421139343717]), 183 | Y: FieldElement51([989242239079485, 1931438483105574, 689606766112758, 690673964073057, 1565243475662398]), 184 | Z: FieldElement51([516909798874657, 520574902282016, 995935568209254, 1431354793481376, 1213658785624019]), 185 | T: FieldElement51([1791507317031606, 1617767375363020, 1784527524719134, 423359645043946, 2153942148383707]) 186 | } 187 | EdwardsPoint{ 188 | X: FieldElement51([303899113090113, 1809755201380509, 1555098689470109, 757269675033462, 1927710983839609]), 189 | Y: FieldElement51([1738780603052456, 425224550249657, 68515857663130, 1779360352719049, 1904672062621734]), 190 | Z: FieldElement51([516909798874657, 520574902282016, 995935568209254, 1431354793481376, 1213658785624019]), 191 | T: FieldElement51([460292496653623, 634032438322227, 467272288966113, 1828440168641301, 97857665301540]) 192 | }: 193 | *) H(x, v, beta^{1/r} 194 | -) RwdU: [210, 145, 20, 24, 88, 108, 128, 35, 163, 114, 173, 36, 189, 255, 3, 99, 13, 104, 11, 87, 181, 13, 89, 73, 160, 174, 202, 89, 144, 27, 210, 93, 113, 70, 202, 88, 88, 162, 21, 110, 123, 176, 39, 169, 58, 14, 206, 189, 184, 83, 157, 147, 121, 105, 220, 142, 187, 19, 108, 188, 246, 148, 120, 30]: 195 | =) EnvU Envelope { priv_u: [152, 71, 109, 88, 44, 27, 172, 78, 74, 229, 175, 88, 251, 117, 151, 80, 127, 146, 31, 81, 111, 236, 179, 123, 191, 122, 123, 79, 82, 43, 30, 231], pub_u: [15, 40, 236, 9, 144, 197, 121, 138, 241, 164, 221, 132, 238, 57, 120, 204, 235, 68, 214, 132, 174, 212, 243, 139, 111, 1, 94, 218, 12, 136, 89, 18], pub_s: [108, 190, 94, 128, 247, 104, 54, 132, 158, 58, 101, 222, 166, 246, 24, 89, 39, 102, 197, 248, 96, 199, 168, 85, 73, 123, 128, 74, 157, 234, 144, 96] }:2 196 | -) Hkdf Okm is 0330a77b87004f87389cb71394d541a2eb17e68cfe1ee974deed71da60992b4ff119a4a9509cb984513a445b 197 | -) encryption key 32-byte [3, 48, 167, 123, 135, 0, 79, 135, 56, 156, 183, 19, 148, 213, 65, 162, 235, 23, 230, 140, 254, 30, 233, 116, 222, 237, 113, 218, 96, 153, 43, 79]: 198 | -) nonce 96 bit [241, 25, 164, 169, 80, 156, 185, 132, 81, 58, 68, 91]: 199 | -) AuthEnv: AES-GCM-SIV Cipher Envelope [136, 217, 179, 224, 193, 19, 89, 69, 108, 185, 181, 157, 208, 204, 17, 56, 48, 205, 172, 19, 135, 117, 212, 195, 108, 217, 112, 174, 105, 42, 105, 143, 147, 101, 36, 89, 2, 144, 110, 65, 237, 246, 86, 50, 169, 17, 22, 239, 3, 59, 81, 67, 147, 98, 116, 189, 0, 218, 195, 106, 215, 199, 186, 7, 246, 146, 19, 198, 11, 89, 96, 49, 206, 31, 39, 103, 91, 250, 28, 126, 246, 135, 144, 2, 228, 77, 11, 237, 74, 199, 70, 7, 3, 101, 141, 153, 27, 93, 144, 186, 74, 195, 151, 143, 19, 8, 44, 208, 193, 26, 96, 23] : 200 | => Rusty Registration Finalize 201 | PubU: [15, 40, 236, 9, 144, 197, 121, 138, 241, 164, 221, 132, 238, 57, 120, 204, 235, 68, 214, 132, 174, 212, 243, 139, 111, 1, 94, 218, 12, 136, 89, 18] 202 | Envelope: [136, 217, 179, 224, 193, 19, 89, 69, 108, 185, 181, 157, 208, 204, 17, 56, 48, 205, 172, 19, 135, 117, 212, 195, 108, 217, 112, 174, 105, 42, 105, 143, 147, 101, 36, 89, 2, 144, 110, 65, 237, 246, 86, 50, 169, 17, 22, 239, 3, 59, 81, 67, 147, 98, 116, 189, 0, 218, 195, 106, 215, 199, 186, 7, 246, 146, 19, 198, 11, 89, 96, 49, 206, 31, 39, 103, 91, 250, 28, 126, 246, 135, 144, 2, 228, 77, 11, 237, 74, 199, 70, 7, 3, 101, 141, 153, 27, 93, 144, 186, 74, 195, 151, 143, 19, 8, 44, 208, 193, 26, 96, 23] 203 | =) Registered "jerryg" with envelope [136, 217, 179, 224, 193, 19, 89, 69, 108, 185, 181, 157, 208, 204, 17, 56, 48, 205, 172, 19, 135, 117, 212, 195, 108, 217, 112, 174, 105, 42, 105, 143, 147, 101, 36, 89, 2, 144, 110, 65, 237, 246, 86, 50, 169, 17, 22, 239, 3, 59, 81, 67, 147, 98, 116, 189, 0, 218, 195, 106, 215, 199, 186, 7, 246, 146, 19, 198, 11, 89, 96, 49, 206, 31, 39, 103, 91, 250, 28, 126, 246, 135, 144, 2, 228, 77, 11, 237, 74, 199, 70, 7, 3, 101, 141, 153, 27, 93, 144, 186, 74, 195, 151, 143, 19, 8, 44, 208, 193, 26, 96, 23]: 204 | ~) ---------------------| Authenticating: -> usename: jerryg : password: fizzbangpopdog <-------- 205 | *) alpha=H'(x)^r 206 | -) r Scalar{ 207 | bytes: [26, 201, 37, 190, 103, 36, 155, 148, 231, 28, 53, 146, 69, 239, 101, 203, 47, 103, 228, 126, 158, 91, 159, 145, 6, 29, 104, 44, 232, 13, 250, 10], 208 | }: 209 | -) H' RistrettoPoint: coset 210 | EdwardsPoint{ 211 | X: FieldElement51([656038762436102, 1406799287371356, 1786348238230911, 1058876562689887, 11118654170493]), 212 | Y: FieldElement51([959814791126277, 1968753491008071, 312691502780877, 749646495148630, 899946152112758]), 213 | Z: FieldElement51([630423016627832, 1401755870117647, 117469048817406, 403445336527096, 1048475791944265]), 214 | T: FieldElement51([620600270890862, 1632126082154538, 1619817918217783, 1145171700720135, 413618806624035]) 215 | } 216 | EdwardsPoint{ 217 | X: FieldElement51([926859821970023, 2239404452234593, 744550933334533, 2077120592442132, 1627460021181784]), 218 | Y: FieldElement51([1997864973174743, 1468515802607346, 1729650602265619, 1749317835188492, 216729960542505]), 219 | Z: FieldElement51([1466581082851814, 1438021339750375, 1822409516466612, 422749613502192, 1473883459471993]), 220 | T: FieldElement51([294344572748418, 826018863005164, 1419148499208045, 307081474301373, 1142854431936255]) 221 | } 222 | EdwardsPoint{ 223 | X: FieldElement51([907753377086211, 704350107265182, 2183554946857787, 1023907663744292, 39928316127327]), 224 | Y: FieldElement51([2113614720538830, 706792819160827, 1353075996279103, 1787148942634557, 350869282068728]), 225 | Z: FieldElement51([1466581082851814, 1438021339750375, 1822409516466612, 422749613502192, 1473883459471993]), 226 | T: FieldElement51([1957455240936811, 1425780950680083, 832651314477202, 1944718339383874, 1108945381748992]) 227 | } 228 | EdwardsPoint{ 229 | X: FieldElement51([1324939991715206, 12395361450654, 1507248880350714, 174679221243115, 624339792503463]), 230 | Y: FieldElement51([253934840510486, 783284011077901, 522149211419628, 502481978496755, 2035069853142742]), 231 | Z: FieldElement51([1466581082851814, 1438021339750375, 1822409516466612, 422749613502192, 1473883459471993]), 232 | T: FieldElement51([294344572748418, 826018863005164, 1419148499208045, 307081474301373, 1142854431936255]) 233 | }: 234 | -) alpha RistrettoPoint: coset 235 | EdwardsPoint{ 236 | X: FieldElement51([1104201171175049, 1281834163191856, 1424243304452036, 991962165488244, 1879940833175016]), 237 | Y: FieldElement51([428056009113428, 1077328584419961, 834338669719567, 1222255857542047, 2116900704546757]), 238 | Z: FieldElement51([545087434800932, 766447074995478, 1921998533282522, 1084836242144270, 1163046246982648]), 239 | T: FieldElement51([1258183413748963, 822456512189617, 1840986596211478, 1499167087457922, 342293053615964]) 240 | } 241 | EdwardsPoint{ 242 | X: FieldElement51([1561901871054562, 291811541222922, 1549546733143728, 2197623851557320, 836782732262231]), 243 | Y: FieldElement51([1955404302330342, 1273693418789960, 804030857192509, 2066168787887852, 1848894813515967]), 244 | Z: FieldElement51([1772915460544339, 69137173550610, 1904964379441442, 778136239468510, 1517661961218497]), 245 | T: FieldElement51([1902295383797147, 648008207851368, 1393435724795088, 268468527851561, 267531726746600]) 246 | } 247 | EdwardsPoint{ 248 | X: FieldElement51([1738455683019381, 864833278706566, 554893339806519, 532405937987737, 920392160785062]), 249 | Y: FieldElement51([567362709211644, 1372090931703971, 506331485798322, 1076872903658142, 212230176199518]), 250 | Z: FieldElement51([1772915460544339, 69137173550610, 1904964379441442, 778136239468510, 1517661961218497]), 251 | T: FieldElement51([349504429888082, 1603791605833879, 858364088890159, 1983331285833686, 1984268086938647]) 252 | } 253 | EdwardsPoint{ 254 | X: FieldElement51([689897942630667, 1959988272462325, 702253080541519, 54175962127927, 1415017081423016]), 255 | Y: FieldElement51([296395511354887, 978106394895287, 1447768956492738, 185631025797395, 402905000169280]), 256 | Z: FieldElement51([1772915460544339, 69137173550610, 1904964379441442, 778136239468510, 1517661961218497]), 257 | T: FieldElement51([1902295383797147, 648008207851368, 1393435724795088, 268468527851561, 267531726746600]) 258 | }: 259 | -) KE_1: RistrettoPoint: coset 260 | EdwardsPoint{ 261 | X: FieldElement51([1525265270927693, 308931913715819, 104614374928989, 1714961149084361, 968584697894795]), 262 | Y: FieldElement51([774009201233355, 1761249208067278, 1455091699286636, 1502644604040059, 1911630747938897]), 263 | Z: FieldElement51([1219779802639788, 1537193849097684, 990694153970258, 1747203960000169, 602616633591129]), 264 | T: FieldElement51([729916511635893, 29735660608055, 907875952430632, 1202189684689888, 1169281636630165]) 265 | } 266 | EdwardsPoint{ 267 | X: FieldElement51([876206817177072, 869908168850195, 1070707661588353, 159354006265736, 2091112944931312]), 268 | Y: FieldElement51([665334324045457, 1560363754119334, 2190829268595121, 1760767057792298, 554241663007620]), 269 | Z: FieldElement51([443366300027341, 579156596190533, 1234379184285240, 1249012348205104, 94486852498225]), 270 | T: FieldElement51([686773301553109, 1833663948018516, 1412655516875987, 337535305436389, 1681636359461513]) 271 | } 272 | EdwardsPoint{ 273 | X: FieldElement51([1397087356728006, 1184538401085754, 488539507284202, 1391291960608138, 433023182829472]), 274 | Y: FieldElement51([189372424451909, 2049799722168758, 202715652161140, 449400174895022, 1836529678748604]), 275 | Z: FieldElement51([443366300027341, 579156596190533, 1234379184285240, 1249012348205104, 94486852498225]), 276 | T: FieldElement51([1565026512132120, 418135865666731, 839144296809260, 1914264508248858, 570163454223734]) 277 | } 278 | EdwardsPoint{ 279 | X: FieldElement51([1375592996508157, 1381891644835052, 1181092152096894, 2092445807419511, 160686868753935]), 280 | Y: FieldElement51([1586465489639772, 691436059565913, 60970545090126, 491032755892949, 1697558150677627]), 281 | Z: FieldElement51([443366300027341, 579156596190533, 1234379184285240, 1249012348205104, 94486852498225]), 282 | T: FieldElement51([686773301553109, 1833663948018516, 1412655516875987, 337535305436389, 1681636359461513]) 283 | } 284 | ====> Rusty Authentication Start 285 | Alpha: [30, 200, 124, 246, 19, 85, 165, 91, 95, 234, 214, 93, 109, 14, 39, 114, 185, 129, 141, 141, 7, 234, 47, 147, 219, 183, 145, 117, 9, 116, 166, 49] 286 | KE 1: [72, 79, 202, 45, 141, 212, 156, 96, 121, 69, 228, 3, 178, 12, 144, 236, 246, 53, 133, 85, 149, 25, 244, 215, 69, 178, 20, 242, 112, 154, 116, 41] 287 | *) beta=alpha^kU, vU (g^k) 288 | -) kU Scalar{ 289 | bytes: [72, 219, 30, 174, 136, 224, 221, 154, 129, 179, 224, 240, 176, 80, 51, 89, 142, 115, 121, 93, 100, 240, 161, 72, 201, 179, 17, 192, 253, 253, 168, 15], 290 | }: 291 | -) vU RistrettoPoint: coset 292 | EdwardsPoint{ 293 | X: FieldElement51([141830018985727, 1767058257705176, 582413371802165, 1751169942631293, 456889550964492]), 294 | Y: FieldElement51([1634253091467716, 152699512212120, 348508661982919, 863899673547945, 198889406973460]), 295 | Z: FieldElement51([279626663930087, 1505095676730860, 387793512806261, 1532067127606151, 1522486376402507]), 296 | T: FieldElement51([2167469557821672, 113613036851606, 1301283937963992, 249395150197640, 59212944412185]) 297 | } 298 | EdwardsPoint{ 299 | X: FieldElement51([2069300258955501, 247502068746542, 382495250528636, 706107622929503, 674984118482143]), 300 | Y: FieldElement51([1747219753951041, 916719426590104, 566537028582569, 864211246387401, 1595466952096594]), 301 | Z: FieldElement51([2161322056549397, 1220464154325517, 215985417139774, 2152758900119945, 2121543940471468]), 302 | T: FieldElement51([2197679353142357, 567127916689108, 1523997797022097, 1902900670850672, 675078168201845]) 303 | } 304 | EdwardsPoint{ 305 | X: FieldElement51([496413701249252, 1050111803728136, 1029253815549060, 1160197742710982, 1325753197297868]), 306 | Y: FieldElement51([470035710382352, 1866658756805717, 1606443942245097, 1297349095272561, 1836216790915790]), 307 | Z: FieldElement51([2161322056549397, 1220464154325517, 215985417139774, 2152758900119945, 2121543940471468]), 308 | T: FieldElement51([54120460542872, 1684671896996139, 727802016663150, 348899142834575, 1576721645483402]) 309 | } 310 | EdwardsPoint{ 311 | X: FieldElement51([182499554729728, 2004297744938705, 1869304563156611, 1545692190755744, 1576815695203104]), 312 | Y: FieldElement51([504580059734188, 1335080387095143, 1685262785102678, 1387588567297846, 656332861588653]), 313 | Z: FieldElement51([2161322056549397, 1220464154325517, 215985417139774, 2152758900119945, 2121543940471468]), 314 | T: FieldElement51([2197679353142357, 567127916689108, 1523997797022097, 1902900670850672, 675078168201845]) 315 | }: 316 | -> Envelope Some([136, 217, 179, 224, 193, 19, 89, 69, 108, 185, 181, 157, 208, 204, 17, 56, 48, 205, 172, 19, 135, 117, 212, 195, 108, 217, 112, 174, 105, 42, 105, 143, 147, 101, 36, 89, 2, 144, 110, 65, 237, 246, 86, 50, 169, 17, 22, 239, 3, 59, 81, 67, 147, 98, 116, 189, 0, 218, 195, 106, 215, 199, 186, 7, 246, 146, 19, 198, 11, 89, 96, 49, 206, 31, 39, 103, 91, 250, 28, 126, 246, 135, 144, 2, 228, 77, 11, 237, 74, 199, 70, 7, 3, 101, 141, 153, 27, 93, 144, 186, 74, 195, 151, 143, 19, 8, 44, 208, 193, 26, 96, 23]): 317 | -) beta RistrettoPoint: coset 318 | EdwardsPoint{ 319 | X: FieldElement51([1342249293169652, 553137008393913, 943241270606749, 895354469081911, 654310105072033]), 320 | Y: FieldElement51([856792181355926, 194684202299181, 633236351386085, 1088335047435188, 696859181186971]), 321 | Z: FieldElement51([1619083300986705, 1239656676694519, 819986617814566, 199391224298684, 1285006521746265]), 322 | T: FieldElement51([1859035036574843, 1590184125758497, 373966937080736, 213085584944133, 1373818410356516]) 323 | } 324 | EdwardsPoint{ 325 | X: FieldElement51([1299008231545840, 2158145126180089, 2168183544936348, 2158736821093040, 243306258976592]), 326 | Y: FieldElement51([1859790813088873, 656526710418917, 674049789900528, 2009779099506617, 315023945153903]), 327 | Z: FieldElement51([2130222783613089, 1134344611650132, 292967460921499, 550515920476661, 1717275003814244]), 328 | T: FieldElement51([1372335035636319, 2096120839392241, 1345250286371667, 1866558934934424, 1879596724659444]) 329 | } 330 | EdwardsPoint{ 331 | X: FieldElement51([1360760079490900, 1908964530998754, 1005661085980998, 1985213357381223, 2221152210589524]), 332 | Y: FieldElement51([1925278519262655, 156620875351926, 1555847466652159, 2250402975592980, 241038338027246]), 333 | Z: FieldElement51([2130222783613089, 1134344611650132, 292967460921499, 550515920476661, 1717275003814244]), 334 | T: FieldElement51([879464778048910, 155678974293006, 906549527313580, 385240878750823, 372203089025803]) 335 | } 336 | EdwardsPoint{ 337 | X: FieldElement51([952791582139389, 93654687505158, 83616268748899, 93062992592207, 2008493554708655]), 338 | Y: FieldElement51([392009000596356, 1595273103266330, 1577750023784719, 242020714178630, 1936775868531344]), 339 | Z: FieldElement51([2130222783613089, 1134344611650132, 292967460921499, 550515920476661, 1717275003814244]), 340 | T: FieldElement51([1372335035636319, 2096120839392241, 1345250286371667, 1866558934934424, 1879596724659444]) 341 | }: 342 | -) KE_2: RistrettoPoint: coset 343 | EdwardsPoint{ 344 | X: FieldElement51([141830018985727, 1767058257705176, 582413371802165, 1751169942631293, 456889550964492]), 345 | Y: FieldElement51([1634253091467716, 152699512212120, 348508661982919, 863899673547945, 198889406973460]), 346 | Z: FieldElement51([279626663930087, 1505095676730860, 387793512806261, 1532067127606151, 1522486376402507]), 347 | T: FieldElement51([2167469557821672, 113613036851606, 1301283937963992, 249395150197640, 59212944412185]) 348 | } 349 | EdwardsPoint{ 350 | X: FieldElement51([2069300258955501, 247502068746542, 382495250528636, 706107622929503, 674984118482143]), 351 | Y: FieldElement51([1747219753951041, 916719426590104, 566537028582569, 864211246387401, 1595466952096594]), 352 | Z: FieldElement51([2161322056549397, 1220464154325517, 215985417139774, 2152758900119945, 2121543940471468]), 353 | T: FieldElement51([2197679353142357, 567127916689108, 1523997797022097, 1902900670850672, 675078168201845]) 354 | } 355 | EdwardsPoint{ 356 | X: FieldElement51([496413701249252, 1050111803728136, 1029253815549060, 1160197742710982, 1325753197297868]), 357 | Y: FieldElement51([470035710382352, 1866658756805717, 1606443942245097, 1297349095272561, 1836216790915790]), 358 | Z: FieldElement51([2161322056549397, 1220464154325517, 215985417139774, 2152758900119945, 2121543940471468]), 359 | T: FieldElement51([54120460542872, 1684671896996139, 727802016663150, 348899142834575, 1576721645483402]) 360 | } 361 | EdwardsPoint{ 362 | X: FieldElement51([182499554729728, 2004297744938705, 1869304563156611, 1545692190755744, 1576815695203104]), 363 | Y: FieldElement51([504580059734188, 1335080387095143, 1685262785102678, 1387588567297846, 656332861588653]), 364 | Z: FieldElement51([2161322056549397, 1220464154325517, 215985417139774, 2152758900119945, 2121543940471468]), 365 | T: FieldElement51([2197679353142357, 567127916689108, 1523997797022097, 1902900670850672, 675078168201845]) 366 | } 367 | -) Shared Secret: RistrettoPoint: coset 368 | EdwardsPoint{ 369 | X: FieldElement51([1364613908119646, 1063392688075807, 2176348397042309, 2036575485544140, 363587316773456]), 370 | Y: FieldElement51([794270084282582, 2035682075703465, 996189810081630, 1292646088217147, 43033280566779]), 371 | Z: FieldElement51([1904294919095710, 1040507779273355, 866774691500748, 1541423177017379, 1881396088947018]), 372 | T: FieldElement51([1696830289429915, 1841476449681199, 1614208561362630, 1976260091724714, 1486510399144242]) 373 | } 374 | EdwardsPoint{ 375 | X: FieldElement51([1178774395415233, 369477187238234, 1856252136724230, 1899626120410927, 1650780858467291]), 376 | Y: FieldElement51([1427581697884375, 1067354804332427, 941816646823108, 1621420773929163, 249020186341896]), 377 | Z: FieldElement51([498725863646251, 988674597223131, 1347124372434603, 1433214939790898, 1517762842513353]), 378 | T: FieldElement51([81132492183444, 1048463358420726, 801812277808136, 49904926910351, 1101764991578982]) 379 | } 380 | EdwardsPoint{ 381 | X: FieldElement51([99501817825160, 856188591593883, 763556257888248, 1547251321059087, 1809157460764953]), 382 | Y: FieldElement51([225315746292841, 752646226217727, 170309717047764, 1892400623738065, 875408952917642]), 383 | Z: FieldElement51([498725863646251, 988674597223131, 1347124372434603, 1433214939790898, 1517762842513353]), 384 | T: FieldElement51([2170667321501785, 1203336455264521, 1449987535877111, 2201894886774896, 1150034822106265]) 385 | } 386 | EdwardsPoint{ 387 | X: FieldElement51([1073025418269996, 1882322626447013, 395547676961017, 352173693274320, 601018955217956]), 388 | Y: FieldElement51([824218115800854, 1184445009352820, 1309983166862139, 630379039756084, 2002779627343351]), 389 | Z: FieldElement51([498725863646251, 988674597223131, 1347124372434603, 1433214939790898, 1517762842513353]), 390 | T: FieldElement51([81132492183444, 1048463358420726, 801812277808136, 49904926910351, 1101764991578982]) 391 | } 392 | -) SIG(PrivS; g^x, g^y): Signature( R: CompressedEdwardsY: [238, 127, 237, 228, 136, 251, 18, 98, 137, 87, 30, 62, 231, 154, 149, 154, 48, 116, 141, 19, 248, 227, 63, 62, 242, 121, 176, 81, 220, 243, 57, 44], s: Scalar{ 393 | bytes: [112, 47, 64, 205, 108, 124, 237, 195, 58, 136, 73, 146, 129, 93, 185, 97, 142, 29, 122, 110, 188, 151, 64, 6, 203, 18, 238, 103, 183, 159, 221, 8], 394 | } ) 395 | -) DH encryption key 32-byte [40, 10, 15, 21, 180, 90, 155, 121, 190, 160, 131, 62, 82, 35, 227, 166, 174, 132, 108, 4, 237, 50, 197, 71, 156, 16, 195, 250, 34, 74, 134, 189]: 396 | -) DH nonce 96 bit [31, 18, 97, 178, 97, 222, 35, 122, 116, 144, 64, 245]: 397 | -) beta RistrettoPoint: coset 398 | EdwardsPoint{ 399 | X: FieldElement51([1384092945200320, 1232717495517993, 1476163923961761, 43460240910633, 793365713505291]), 400 | Y: FieldElement51([1078691790258516, 512437041573860, 1900614997837126, 1539851761300221, 493284870990513]), 401 | Z: FieldElement51([1, 0, 0, 0, 0]), 402 | T: FieldElement51([1128139856537560, 1701127070121161, 1877748628209063, 1576856650990826, 469735813038975]) 403 | } 404 | EdwardsPoint{ 405 | X: FieldElement51([295195648756642, 59597597391863, 2191770406527061, 1990291333853256, 168165655504203]), 406 | Y: FieldElement51([438223410339894, 2214656399265044, 355702546510780, 1792182462783661, 895804929382367]), 407 | Z: FieldElement51([2251799813685233, 2251799813685247, 2251799813685247, 2251799813685247, 2251799813685247]), 408 | T: FieldElement51([2242840014905485, 2202690974256345, 1496204741904736, 447972837092436, 372856561529345]) 409 | } 410 | EdwardsPoint{ 411 | X: FieldElement51([1219027660254426, 1824529458983769, 850743745208697, 2077958850042713, 1330136773349331]), 412 | Y: FieldElement51([188832466336413, 202051647389806, 1404739263392487, 595992395854856, 278660329723193]), 413 | Z: FieldElement51([2251799813685233, 2251799813685247, 2251799813685247, 2251799813685247, 2251799813685247]), 414 | T: FieldElement51([8959798779744, 49108839428902, 755595071780511, 1803826976592811, 1878943252155902]) 415 | } 416 | EdwardsPoint{ 417 | X: FieldElement51([1956604164928587, 2192202216293384, 60029407158186, 261508479831991, 2083634158181044]), 418 | Y: FieldElement51([1813576403345335, 37143414420203, 1896097267174467, 459617350901586, 1355994884302880]), 419 | Z: FieldElement51([2251799813685233, 2251799813685247, 2251799813685247, 2251799813685247, 2251799813685247]), 420 | T: FieldElement51([2242840014905485, 2202690974256345, 1496204741904736, 447972837092436, 372856561529345]) 421 | }: 422 | -) v RistrettoPoint: coset 423 | EdwardsPoint{ 424 | X: FieldElement51([447099709561120, 2242269474573883, 375815961801930, 1273472360684429, 589520544266284]), 425 | Y: FieldElement51([1750352109427009, 564570672487712, 1346775051008481, 1654630755263711, 945345763357169]), 426 | Z: FieldElement51([1, 0, 0, 0, 0]), 427 | T: FieldElement51([195242660169910, 1005486787525866, 1622538249471461, 371206692635354, 1046436440959547]) 428 | } 429 | EdwardsPoint{ 430 | X: FieldElement51([596428554112178, 659000259389882, 1750196669083677, 1166465300942101, 2022303615995589]), 431 | Y: FieldElement51([797081788406996, 422600318808974, 1993401919640845, 1871081322418192, 368736206911110]), 432 | Z: FieldElement51([2251799813685233, 2251799813685247, 2251799813685247, 2251799813685247, 2251799813685247]), 433 | T: FieldElement51([1470829173005570, 481652477267031, 265246443169898, 766973043143829, 317853863532307]) 434 | } 435 | EdwardsPoint{ 436 | X: FieldElement51([463400975440730, 38121356445459, 748535966477524, 1661509998318027, 2145517450305357]), 437 | Y: FieldElement51([2005790817032918, 2245316937419644, 1368299237021818, 136876420000897, 722216573941817]), 438 | Z: FieldElement51([2251799813685233, 2251799813685247, 2251799813685247, 2251799813685247, 2251799813685247]), 439 | T: FieldElement51([780970640679659, 1770147336418216, 1986553370515349, 1484826770541418, 1933945950152940]) 440 | } 441 | EdwardsPoint{ 442 | X: FieldElement51([1655371259573051, 1592799554295365, 501603144601570, 1085334512743146, 229496197689658]), 443 | Y: FieldElement51([1454718025278233, 1829199494876273, 258397894044402, 380718491267055, 1883063606774137]), 444 | Z: FieldElement51([2251799813685233, 2251799813685247, 2251799813685247, 2251799813685247, 2251799813685247]), 445 | T: FieldElement51([1470829173005570, 481652477267031, 265246443169898, 766973043143829, 317853863532307]) 446 | }: 447 | -) KE 2: [170, 156, 160, 49, 147, 7, 86, 14, 199, 15, 204, 127, 226, 241, 187, 117, 209, 183, 158, 9, 84, 5, 68, 121, 92, 252, 16, 102, 141, 218, 238, 213, 98, 246, 72, 94, 165, 193, 210, 28, 213, 103, 91, 166, 77, 156, 142, 79, 214, 179, 251, 177, 66, 169, 192, 63, 80, 128, 74, 82, 153, 6, 137, 245, 189, 44, 45, 84, 96, 40, 248, 36, 11, 249, 179, 25, 195, 116, 84, 174, 147, 215, 142, 93, 107, 168, 61, 19, 62, 248, 236, 9, 36, 202, 101, 17, 78, 123, 105, 147, 142, 231, 163, 93, 1, 2, 145, 152, 176, 82, 84, 231, 88, 231, 171, 4, 178, 72, 147, 173, 167, 208, 50, 130, 44, 151, 99, 8, 51, 47, 31, 19, 108, 245, 152, 114, 129, 99, 102, 246, 237, 42, 169, 220, 144, 89, 144, 61, 195, 30, 188, 86, 239, 188, 63, 47, 195, 175, 118, 34, 252, 143, 77, 30, 227, 219, 239, 184, 139, 45, 12, 115, 228, 125, 158, 67, 232, 6, 249, 129, 181, 187, 221, 182, 51, 85, 131, 197, 29, 29, 108, 95] 448 | -) KE 2 Size: 192 449 | -) AuthEnv [136, 217, 179, 224, 193, 19, 89, 69, 108, 185, 181, 157, 208, 204, 17, 56, 48, 205, 172, 19, 135, 117, 212, 195, 108, 217, 112, 174, 105, 42, 105, 143, 147, 101, 36, 89, 2, 144, 110, 65, 237, 246, 86, 50, 169, 17, 22, 239, 3, 59, 81, 67, 147, 98, 116, 189, 0, 218, 195, 106, 215, 199, 186, 7, 246, 146, 19, 198, 11, 89, 96, 49, 206, 31, 39, 103, 91, 250, 28, 126, 246, 135, 144, 2, 228, 77, 11, 237, 74, 199, 70, 7, 3, 101, 141, 153, 27, 93, 144, 186, 74, 195, 151, 143, 19, 8, 44, 208, 193, 26, 96, 23]: 450 | -) AuthEnv Size 112: 451 | -) {1/r} Scalar{ 452 | bytes: [103, 63, 17, 166, 80, 218, 54, 72, 116, 217, 179, 89, 138, 201, 16, 36, 28, 61, 126, 11, 119, 121, 35, 153, 3, 0, 158, 181, 72, 61, 170, 10], 453 | }: 454 | -) beta^{1/r} RistrettoPoint: coset 455 | EdwardsPoint{ 456 | X: FieldElement51([1602344580757754, 1486722955231295, 1952454980200341, 293355037110547, 363982385396458]), 457 | Y: FieldElement51([784174446476070, 1424396274122480, 374237401391359, 1272115962701040, 820355542865679]), 458 | Z: FieldElement51([115165684498886, 581371193717146, 1485721101113329, 1866267090242983, 2157906533223806]), 459 | T: FieldElement51([877515343752831, 1784821658984193, 1393958227096116, 344303187724041, 557693533910826]) 460 | } 461 | EdwardsPoint{ 462 | X: FieldElement51([1435772789016015, 2209043870543888, 1578064690074778, 673970967883617, 1304569798544336]), 463 | Y: FieldElement51([1795401978429122, 1894055463737337, 911955443819200, 1631331337830039, 710181371602159]), 464 | Z: FieldElement51([1348396065034756, 1090357908610819, 1529505958032807, 939170969823621, 766419418155805]), 465 | T: FieldElement51([1276445111167685, 1433899380881915, 1557537188079408, 1395469534566016, 1258174513189305]) 466 | } 467 | EdwardsPoint{ 468 | X: FieldElement51([1255738305755272, 1910102522354074, 1984020605543540, 260676506929412, 2152176535286809]), 469 | Y: FieldElement51([962785033133901, 1703235733765466, 946706565311527, 1341388995494591, 683021060697661]), 470 | Z: FieldElement51([1348396065034756, 1090357908610819, 1529505958032807, 939170969823621, 766419418155805]), 471 | T: FieldElement51([975354702517544, 817900432803332, 694262625605839, 856330279119231, 993625300495942]) 472 | } 473 | EdwardsPoint{ 474 | X: FieldElement51([816027024669214, 42755943141359, 673735123610469, 1577828845801630, 947230015140911]), 475 | Y: FieldElement51([456397835256107, 357744349947910, 1339844369866047, 620468475855208, 1541618442083088]), 476 | Z: FieldElement51([1348396065034756, 1090357908610819, 1529505958032807, 939170969823621, 766419418155805]), 477 | T: FieldElement51([1276445111167685, 1433899380881915, 1557537188079408, 1395469534566016, 1258174513189305]) 478 | }: 479 | *) RwdU = H(x, v, beta^{1/r}) 480 | -) RwdU [210, 145, 20, 24, 88, 108, 128, 35, 163, 114, 173, 36, 189, 255, 3, 99, 13, 104, 11, 87, 181, 13, 89, 73, 160, 174, 202, 89, 144, 27, 210, 93, 113, 70, 202, 88, 88, 162, 21, 110, 123, 176, 39, 169, 58, 14, 206, 189, 184, 83, 157, 147, 121, 105, 220, 142, 187, 19, 108, 188, 246, 148, 120, 30]: 481 | -) HKDF OKM 0330a77b87004f87389cb71394d541a2eb17e68cfe1ee974deed71da60992b4ff119a4a9509cb984513a445b 482 | -) encryption key 32-byte [3, 48, 167, 123, 135, 0, 79, 135, 56, 156, 183, 19, 148, 213, 65, 162, 235, 23, 230, 140, 254, 30, 233, 116, 222, 237, 113, 218, 96, 153, 43, 79]: 483 | -) nonce 96 bit [241, 25, 164, 169, 80, 156, 185, 132, 81, 58, 68, 91]: 484 | =) EnvU (decoded) Envelope { priv_u: [152, 71, 109, 88, 44, 27, 172, 78, 74, 229, 175, 88, 251, 117, 151, 80, 127, 146, 31, 81, 111, 236, 179, 123, 191, 122, 123, 79, 82, 43, 30, 231], pub_u: [15, 40, 236, 9, 144, 197, 121, 138, 241, 164, 221, 132, 238, 57, 120, 204, 235, 68, 214, 132, 174, 212, 243, 139, 111, 1, 94, 218, 12, 136, 89, 18], pub_s: [108, 190, 94, 128, 247, 104, 54, 132, 158, 58, 101, 222, 166, 246, 24, 89, 39, 102, 197, 248, 96, 199, 168, 85, 73, 123, 128, 74, 157, 234, 144, 96] }: 485 | -) Shared Secret: RistrettoPoint: coset 486 | EdwardsPoint{ 487 | X: FieldElement51([2057029087328498, 649369084431604, 816486043587346, 2031217887721529, 615195143723254]), 488 | Y: FieldElement51([2069665090446059, 1825348367905538, 1750728351509799, 2245183930964242, 952915275718514]), 489 | Z: FieldElement51([1163575230025787, 2105502361939722, 1558779810795243, 1921089522594600, 1771887844600317]), 490 | T: FieldElement51([830475058987299, 1063846524511619, 1395099107683859, 1203292296559121, 428291349988715]) 491 | } 492 | EdwardsPoint{ 493 | X: FieldElement51([1241435803349840, 387148247609152, 1602141785854179, 2211487036232263, 835530411904084]), 494 | Y: FieldElement51([1922527086621752, 213459995464244, 1314197725898914, 1393442166090255, 2088862249548493]), 495 | Z: FieldElement51([313260149250343, 148276109380860, 2125067161127596, 436773934269919, 77285075814723]), 496 | T: FieldElement51([1384515728662250, 260727713821507, 1655382135749814, 1510083195992015, 789394257599253]) 497 | } 498 | EdwardsPoint{ 499 | X: FieldElement51([1296851045256438, 959272309819692, 188083789216778, 1058247868056939, 713103473679141]), 500 | Y: FieldElement51([1380375700420115, 4923432873694, 2219916894224066, 964957855955214, 430216812356649]), 501 | Z: FieldElement51([313260149250343, 148276109380860, 2125067161127596, 436773934269919, 77285075814723]), 502 | T: FieldElement51([867284085022979, 1991072099863740, 596417677935433, 741716617693232, 1462405556085994]) 503 | } 504 | EdwardsPoint{ 505 | X: FieldElement51([1010364010335389, 1864651566076095, 649658027831068, 40312777452984, 1416269401781163]), 506 | Y: FieldElement51([329272727063477, 2038339818221003, 937602087786333, 858357647594992, 162937564136754]), 507 | Z: FieldElement51([313260149250343, 148276109380860, 2125067161127596, 436773934269919, 77285075814723]), 508 | T: FieldElement51([1384515728662250, 260727713821507, 1655382135749814, 1510083195992015, 789394257599253]) 509 | } 510 | -) DH encryption key 32-byte [40, 10, 15, 21, 180, 90, 155, 121, 190, 160, 131, 62, 82, 35, 227, 166, 174, 132, 108, 4, 237, 50, 197, 71, 156, 16, 195, 250, 34, 74, 134, 189]: 511 | -) DH nonce 96 bit [31, 18, 97, 178, 97, 222, 35, 122, 116, 144, 64, 245]: 512 | => Rusty Authenticate Finalize 513 | Key 3: [54, 38, 180, 145, 237, 254, 52, 29, 136, 159, 110, 227, 159, 156, 188, 54, 70, 43, 111, 115, 25, 220, 33, 164, 92, 15, 222, 159, 100, 61, 22, 15, 177, 47, 28, 86, 514 | 157, 27, 13, 20, 111, 208, 198, 106, 94, 206, 236, 7, 253, 186, 85, 246, 206, 87, 169, 123, 138, 202, 59, 241, 204, 32, 69, 126, 65, 170, 247, 83, 248, 16, 248, 172, 74, 515 | 19, 52, 56, 48, 224, 106, 68, 163, 200, 228, 76, 138, 91, 132, 8, 223, 218, 221, 192, 46, 200, 183, 190, 17, 72, 102, 177, 218, 62, 255, 229, 102, 221, 60, 14, 125, 164, 516 | 225, 89, 156, 25, 82, 49, 147, 142, 60, 26, 27, 10, 81, 206, 255, 93, 182, 214, 159, 252, 135, 241, 201, 30, 101, 157, 80, 51, 231, 166, 82, 133, 209, 1, 250, 131, 7, 517 | 135, 245, 126, 197, 111, 141, 68, 244, 220, 202, 199, 0, 216, 203, 17, 0, 202, 34, 3, 140, 204, 131, 61, 34, 193, 74, 52, 148, 70, 92, 123, 201, 39, 35, 149, 173, 119, 518 | 63, 230, 108, 122, 158, 88, 19, 78]: 519 | X: [72, 79, 202, 45, 141, 212, 156, 96, 121, 69, 228, 3, 178, 12, 144, 236, 246, 53, 133, 85, 149, 25, 244, 215, 69, 178, 20, 242, 112, 154, 116, 41]: 520 | -) HKDF OKM 280a0f15b45a9b79bea0833e5223e3a6ae846c04ed32c5479c10c3fa224a86bd1f1261b261de237a749040f5057b7010994bbb8f330718c55af3d62f2925a85a8c40bcaa02518b41d2420ce5ce76bdf9464192ca78eceef9e4301a1df0f8087f964a42cfb1e0654d3a5f0014 521 | Encryption Key DH: [40, 10, 15, 21, 180, 90, 155, 121, 190, 160, 131, 62, 82, 35, 227, 166, 174, 132, 108, 4, 237, 50, 197, 71, 156, 16, 195, 250, 34, 74, 134, 189] 522 | Nonce DH: [31, 18, 97, 178, 97, 222, 35, 122, 116, 144, 64, 245] 523 | KE 3 Slice: [54, 38, 180, 145, 237, 254, 52, 29, 136, 159, 110, 227, 159, 156, 188, 54, 70, 43, 111, 115, 25, 220, 33, 164, 92, 15, 222, 159, 100, 61, 22, 15, 177, 47, 28, 86, 157, 27, 13, 20, 111, 208, 198, 106, 94, 206, 236, 7, 253, 186, 85, 246, 206, 87, 169, 123, 138, 202, 59, 241, 204, 32, 69, 126, 65, 170, 247, 83, 248, 16, 248, 172, 74, 19, 52, 56, 48, 224, 106, 68, 163, 200, 228, 76, 138, 91, 132, 8, 223, 218, 221, 192, 46, 200, 183, 190, 17, 72, 102, 177, 218, 62, 255, 229, 102, 221, 60, 14, 125, 164, 225, 89, 156, 25, 82, 49, 147, 142, 60, 26, 27, 10, 81, 206, 255, 93, 182, 214, 159, 252, 135, 241, 201, 30, 101, 157, 80, 51, 231, 166, 82, 133, 209, 1, 250, 131, 7, 135, 245, 126, 197, 111, 141, 68, 244, 220, 202, 199, 0, 216, 203, 17, 0, 202, 34, 3, 140, 204, 131, 61, 34, 193, 74, 52, 148, 70, 92, 123, 201, 39, 35, 149, 173, 119, 63, 230, 108, 122, 158, 88, 19, 78] 524 | Key 3 Size: 192 525 | =) () 526 | =) Signature Verified KE3 -- jerryg logged in. 527 | --------------------------------------------------------------------------------