Updating to draft-krawczyk-cfrg-opaque-06, reworking envelope construction and removing AEAD (#14)
Updating to draft-krawczyk-cfrg-opaque-06, reworking envelope construction and removing AEAD
This commit is contained in:
Generated
-126
@@ -8,47 +8,6 @@ dependencies = [
|
||||
"generic-array 0.14.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aes"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"aes-soft 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"aesni 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"block-cipher 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aes-gcm"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"aead 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"aes 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"block-cipher 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ghash 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"subtle 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aes-soft"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"block-cipher 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aesni"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"block-cipher 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.0.0"
|
||||
@@ -88,14 +47,6 @@ dependencies = [
|
||||
"generic-array 0.14.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-cipher"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"generic-array 0.14.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-padding"
|
||||
version = "0.1.5"
|
||||
@@ -119,27 +70,6 @@ name = "cfg-if"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "chacha20"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"stream-cipher 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"zeroize 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "chacha20poly1305"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"aead 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"chacha20 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"poly1305 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"stream-cipher 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"zeroize 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crypto-mac"
|
||||
version = "0.8.0"
|
||||
@@ -214,14 +144,6 @@ dependencies = [
|
||||
"wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ghash"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"polyval 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hex"
|
||||
version = "0.4.2"
|
||||
@@ -278,9 +200,7 @@ name = "opaque-ke"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"aead 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"aes-gcm 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"base64 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"chacha20poly1305 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"curve25519-dalek 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"generic-array 0.14.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hex 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -306,23 +226,6 @@ dependencies = [
|
||||
"crypto-mac 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "poly1305"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"universal-hash 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "polyval"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"universal-hash 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.8"
|
||||
@@ -487,14 +390,6 @@ dependencies = [
|
||||
"opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stream-cipher"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"generic-array 0.14.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
version = "2.2.2"
|
||||
@@ -562,15 +457,6 @@ name = "unicode-xid"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "universal-hash"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"generic-array 0.14.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"subtle 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.2"
|
||||
@@ -639,23 +525,16 @@ dependencies = [
|
||||
|
||||
[metadata]
|
||||
"checksum aead 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3672ba0d12aaf256aabcdab516ebed87b5cec4b602316d7a3035fac9b7a9567b"
|
||||
"checksum aes 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f7001367fde4c768a19d1029f0a8be5abd9308e1119846d5bd9ad26297b8faf5"
|
||||
"checksum aes-gcm 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "86f5007801316299f922a6198d1d09a0bae95786815d066d5880d13f7c45ead1"
|
||||
"checksum aes-soft 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4925647ee64e5056cf231608957ce7c81e12d6d6e316b9ce1404778cc1d35fa7"
|
||||
"checksum aesni 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d050d39b0b7688b3a3254394c3e30a9d66c41dcf9b05b0e2dbdc623f6505d264"
|
||||
"checksum autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
|
||||
"checksum base64 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "53d1ccbaf7d9ec9537465a97bf19edc1a4e158ecb49fc16178202238c569cc42"
|
||||
"checksum bit-set 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6e11e16035ea35e4e5997b393eacbf6f63983188f7a2ad25bfb13465f5ad59de"
|
||||
"checksum bit-vec 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5f0dc55f2d8a1a85650ac47858bb001b4c0dd73d79e3c455a842925e68d29cd3"
|
||||
"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
||||
"checksum block-buffer 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "dbcf92448676f82bb7a334c58bbce8b0d43580fb5362a9d608b18879d12a3d31"
|
||||
"checksum block-cipher 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fa136449e765dc7faa244561ccae839c394048667929af599b5d931ebe7b7f10"
|
||||
"checksum block-padding 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5"
|
||||
"checksum byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7"
|
||||
"checksum byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
|
||||
"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||
"checksum chacha20 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "086c0f07ac275808b7bf9a39f2fd013aae1498be83632814c8c4e0bd53f2dc58"
|
||||
"checksum chacha20poly1305 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "18b0c90556d8e3fec7cf18d84a2f53d27b21288f2fe481b830fadcf809e48205"
|
||||
"checksum crypto-mac 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab"
|
||||
"checksum curve25519-dalek 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5d85653f070353a16313d0046f173f70d1aadd5b42600a14de626f0dfb3473a5"
|
||||
"checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5"
|
||||
@@ -665,7 +544,6 @@ dependencies = [
|
||||
"checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec"
|
||||
"checksum generic-array 0.14.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ac746a5f3bbfdadd6106868134545e684693d54d9d44f6e9588a7d54af0bf980"
|
||||
"checksum getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb"
|
||||
"checksum ghash 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d6e27f0689a6e15944bdce7e45425efb87eaa8ab0c6e87f11d0987a9133e2531"
|
||||
"checksum hex 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "644f9158b2f133fd50f5fb3242878846d9eb792e445c893805ff0e3824006e35"
|
||||
"checksum hkdf 0.9.0-alpha.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e551da9a76291df932270bc2b100d0571588eaa9ef77af7bceee80dba9ace3ad"
|
||||
"checksum hmac 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b87b580bd66811cc2324a27f3587de707cacf7525b96dca8122f7493e6cce0da"
|
||||
@@ -675,8 +553,6 @@ dependencies = [
|
||||
"checksum num-traits 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)" = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611"
|
||||
"checksum opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
|
||||
"checksum pbkdf2 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "216eaa586a190f0a738f2f918511eecfa90f13295abec0e457cdebcceda80cbd"
|
||||
"checksum poly1305 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d9b42192ab143ed7619bf888a7f9c6733a9a2153b218e2cd557cfdb52fbf9bb1"
|
||||
"checksum polyval 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d9a50142b55ab3ed0e9f68dfb3709f1d90d29da24e91033f28b96330643107dc"
|
||||
"checksum ppv-lite86 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea"
|
||||
"checksum proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)" = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa"
|
||||
"checksum proptest 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2520fe6373cf6a3a61e2d200e987c183778ade8d9248ac3e6614ab0edfe4a0c1"
|
||||
@@ -696,7 +572,6 @@ dependencies = [
|
||||
"checksum serde 1.0.111 (registry+https://github.com/rust-lang/crates.io-index)" = "c9124df5b40cbd380080b2cc6ab894c040a3070d995f5c9dc77e18c34a8ae37d"
|
||||
"checksum serde_json 1.0.55 (registry+https://github.com/rust-lang/crates.io-index)" = "ec2c5d7e739bc07a3e73381a39d61fdb5f671c60c1df26a130690665803d8226"
|
||||
"checksum sha2 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "72377440080fd008550fe9b441e854e43318db116f90181eef92e9ae9aedab48"
|
||||
"checksum stream-cipher 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "09f8ed9974042b8c3672ff3030a69fcc03b74c47c3d1ecb7755e8a3626011e88"
|
||||
"checksum subtle 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7c65d530b10ccaeac294f349038a597e435b18fb456aadd0840a623f83b9e941"
|
||||
"checksum syn 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)" = "b5304cfdf27365b7585c25d4af91b35016ed21ef88f17ced89c7093b43dba8b6"
|
||||
"checksum synstructure 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701"
|
||||
@@ -705,7 +580,6 @@ dependencies = [
|
||||
"checksum thiserror-impl 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)" = "893582086c2f98cde18f906265a65b5030a074b1046c674ae898be6519a7f479"
|
||||
"checksum typenum 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33"
|
||||
"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
|
||||
"checksum universal-hash 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8326b2c654932e3e4f9196e69d08fdf7cfd718e1dc6f66b347e6024a0c961402"
|
||||
"checksum version_check 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed"
|
||||
"checksum wait-timeout 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6"
|
||||
"checksum wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)" = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
|
||||
|
||||
@@ -26,9 +26,7 @@ x25519-dalek = "0.6.0"
|
||||
zeroize = "1.1"
|
||||
|
||||
[dev-dependencies]
|
||||
aes-gcm = "0.6.0"
|
||||
base64 = "0.12.1"
|
||||
chacha20poly1305 = "0.5.1"
|
||||
hex = "0.4.2"
|
||||
lazy_static = "1.4.0"
|
||||
serde_json = "1.0.55"
|
||||
|
||||
@@ -15,12 +15,10 @@ use generic_array::typenum::{U32, U64};
|
||||
use rand_core::{CryptoRng, RngCore};
|
||||
|
||||
/// Configures the underlying primitives used in OPAQUE
|
||||
/// * Aead: an authenticated encryption scheme
|
||||
/// * Group: a finite cyclic group along with a point representation
|
||||
/// * KeyFormat: a keypair type composed of public and private components
|
||||
/// * SlowHash: a slow hashing function, typically used for password hashing
|
||||
pub trait CipherSuite {
|
||||
type Aead: aead::NewAead<KeySize = U32> + aead::Aead;
|
||||
type Group: Group<ScalarLen = U32, UniformBytesLen = U64>;
|
||||
type KeyFormat: KeyPair<Repr = Key> + PartialEq;
|
||||
type SlowHash: SlowHash;
|
||||
|
||||
+182
@@ -0,0 +1,182 @@
|
||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
||||
//
|
||||
// This source code is licensed under the MIT license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
|
||||
use crate::errors::InternalPakeError;
|
||||
use generic_array::{
|
||||
typenum::{Unsigned, U32},
|
||||
GenericArray,
|
||||
};
|
||||
use hkdf::Hkdf;
|
||||
use hmac::{Hmac, Mac, NewMac};
|
||||
use rand_core::{CryptoRng, RngCore};
|
||||
use sha2::{Digest, Sha256};
|
||||
|
||||
// Constant string used as salt for HKDF computation
|
||||
const STR_ENVU: &[u8] = b"EnvU";
|
||||
|
||||
/// The length of the "export key" output by the client registration
|
||||
/// and login finish steps
|
||||
pub(crate) type ExportKeySize = <Sha256 as Digest>::OutputSize;
|
||||
|
||||
/// This struct is an instantiation of the envelope as described in
|
||||
/// https://tools.ietf.org/html/draft-krawczyk-cfrg-opaque-06#section-4
|
||||
///
|
||||
/// Note that earlier versions of this specification described an
|
||||
/// implementation of this envelope using an encryption scheme that
|
||||
/// satisfied random-key robustness
|
||||
/// (https://tools.ietf.org/html/draft-krawczyk-cfrg-opaque-05#section-4).
|
||||
/// The specification update has simplified this assumption by taking
|
||||
/// an XOR-based approach without compromising on security, and to avoid
|
||||
/// the confusion around the implementation of an RKR-secure encryption.
|
||||
pub(crate) struct Envelope {
|
||||
nonce: Vec<u8>,
|
||||
ciphertext: Vec<u8>,
|
||||
hmac: Vec<u8>,
|
||||
}
|
||||
|
||||
type NonceLen = U32;
|
||||
|
||||
impl Envelope {
|
||||
/// The additional number of bytes added to the plaintext
|
||||
pub(crate) fn additional_size() -> usize {
|
||||
Self::nonce_size() + <Sha256 as Digest>::OutputSize::to_usize()
|
||||
}
|
||||
|
||||
fn hmac_key_size() -> usize {
|
||||
<Sha256 as Digest>::OutputSize::to_usize()
|
||||
}
|
||||
|
||||
fn hmac_size() -> usize {
|
||||
<Sha256 as Digest>::OutputSize::to_usize()
|
||||
}
|
||||
|
||||
fn nonce_size() -> usize {
|
||||
NonceLen::to_usize()
|
||||
}
|
||||
|
||||
fn export_key_size() -> usize {
|
||||
ExportKeySize::to_usize()
|
||||
}
|
||||
|
||||
pub(crate) fn new(
|
||||
nonce: Vec<u8>,
|
||||
ciphertext: Vec<u8>,
|
||||
hmac: &GenericArray<u8, <Sha256 as Digest>::OutputSize>,
|
||||
) -> Self {
|
||||
Self {
|
||||
nonce,
|
||||
ciphertext,
|
||||
hmac: hmac.to_vec(),
|
||||
}
|
||||
}
|
||||
|
||||
/// The format of the output is:
|
||||
/// nonce | ciphertext | hmac
|
||||
/// nonce_size bytes | variable length | hmac_size bytes
|
||||
pub(crate) fn from_bytes(bytes: &[u8]) -> Result<Self, InternalPakeError> {
|
||||
let ciphertext_start = Self::nonce_size();
|
||||
let ciphertext_end = bytes.len() - Self::hmac_size();
|
||||
|
||||
Ok(Self::new(
|
||||
bytes[..ciphertext_start].to_vec(),
|
||||
bytes[ciphertext_start..ciphertext_end].to_vec(),
|
||||
GenericArray::from_slice(&bytes[ciphertext_end..]),
|
||||
))
|
||||
}
|
||||
|
||||
pub(crate) fn to_bytes(&self) -> Vec<u8> {
|
||||
[&self.nonce[..], &self.ciphertext[..], &self.hmac[..]].concat()
|
||||
}
|
||||
|
||||
/// Uses a key to convert the plaintext into an envelope, authenticated by the aad field.
|
||||
/// Note that a new nonce is sampled for each call to seal.
|
||||
pub(crate) fn seal<R: RngCore + CryptoRng>(
|
||||
key: &[u8],
|
||||
plaintext: &[u8],
|
||||
aad: &[u8],
|
||||
rng: &mut R,
|
||||
) -> Result<(Self, GenericArray<u8, ExportKeySize>), InternalPakeError> {
|
||||
let mut nonce = vec![0u8; Self::nonce_size()];
|
||||
rng.fill_bytes(&mut nonce);
|
||||
|
||||
let h = Hkdf::<Sha256>::new(Some(&nonce), &key);
|
||||
let mut okm = vec![0u8; plaintext.len() + Self::hmac_key_size() + Self::export_key_size()];
|
||||
h.expand(STR_ENVU, &mut okm)
|
||||
.map_err(|_| InternalPakeError::HkdfError)?;
|
||||
let xor_key = &okm[..plaintext.len()];
|
||||
let hmac_key = &okm[plaintext.len()..plaintext.len() + Self::hmac_key_size()];
|
||||
let export_key = &okm[plaintext.len() + Self::hmac_key_size()..];
|
||||
|
||||
let ciphertext: Vec<u8> = xor_key
|
||||
.iter()
|
||||
.zip(plaintext.to_vec().iter())
|
||||
.map(|(&x1, &x2)| x1 ^ x2)
|
||||
.collect();
|
||||
|
||||
let mut hmac =
|
||||
Hmac::<Sha256>::new_varkey(&hmac_key).map_err(|_| InternalPakeError::HmacError)?;
|
||||
hmac.update(&ciphertext);
|
||||
hmac.update(&aad);
|
||||
|
||||
Ok((
|
||||
Self::new(nonce, ciphertext.to_vec(), &hmac.finalize().into_bytes()),
|
||||
*GenericArray::from_slice(&export_key),
|
||||
))
|
||||
}
|
||||
|
||||
/// Attempts to decrypt the envelope using a key, which is successful only if the key and
|
||||
/// aad used to construct the envelope are the same.
|
||||
pub(crate) fn open(
|
||||
&self,
|
||||
key: &[u8],
|
||||
aad: &[u8],
|
||||
) -> Result<(Vec<u8>, GenericArray<u8, ExportKeySize>), InternalPakeError> {
|
||||
let h = Hkdf::<Sha256>::new(Some(&self.nonce), &key);
|
||||
let mut okm =
|
||||
vec![0u8; self.ciphertext.len() + Self::hmac_key_size() + Self::export_key_size()];
|
||||
h.expand(STR_ENVU, &mut okm)
|
||||
.map_err(|_| InternalPakeError::HkdfError)?;
|
||||
let xor_key = &okm[..self.ciphertext.len()];
|
||||
let hmac_key = &okm[self.ciphertext.len()..self.ciphertext.len() + Self::hmac_key_size()];
|
||||
let export_key = &okm[self.ciphertext.len() + Self::hmac_key_size()..];
|
||||
|
||||
let mut hmac =
|
||||
Hmac::<Sha256>::new_varkey(&hmac_key).map_err(|_| InternalPakeError::HmacError)?;
|
||||
hmac.update(&self.ciphertext);
|
||||
hmac.update(aad);
|
||||
if hmac.verify(&self.hmac).is_err() {
|
||||
return Err(InternalPakeError::SealOpenHmacError);
|
||||
}
|
||||
|
||||
let plaintext: Vec<u8> = xor_key
|
||||
.to_vec()
|
||||
.iter()
|
||||
.zip(self.ciphertext.iter())
|
||||
.map(|(&x1, &x2)| x1 ^ x2)
|
||||
.collect();
|
||||
Ok((plaintext, *GenericArray::from_slice(&export_key)))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use rand_core::OsRng;
|
||||
|
||||
#[test]
|
||||
fn seal_and_open() {
|
||||
let mut rng = OsRng;
|
||||
let mut key = [0u8; 32];
|
||||
rng.fill_bytes(&mut key);
|
||||
|
||||
let mut msg = [0u8; 100];
|
||||
rng.fill_bytes(&mut msg);
|
||||
|
||||
let (ciphertext, export_key_1) = Envelope::seal(&key, &msg, b"aad", &mut rng).unwrap();
|
||||
let (plaintext, export_key_2) = ciphertext.open(&key, b"aad").unwrap();
|
||||
assert_eq!(&msg.to_vec(), &plaintext);
|
||||
assert_eq!(&export_key_1.to_vec(), &export_key_2.to_vec());
|
||||
}
|
||||
}
|
||||
+10
-10
@@ -28,6 +28,15 @@ pub enum InternalPakeError {
|
||||
HmacError,
|
||||
#[error("Computing the slow hashing function failed")]
|
||||
SlowHashError,
|
||||
/// This error occurs when the envelope seal fails
|
||||
#[error("Constructing the envelope seal failed.")]
|
||||
SealError,
|
||||
/// This error occurs when the envelope seal open fails
|
||||
#[error("Opening the envelope seal failed.")]
|
||||
SealOpenError,
|
||||
/// This error occurs when the envelope seal open hmac check fails
|
||||
#[error("HMAC check in seal open failed.")]
|
||||
SealOpenHmacError,
|
||||
}
|
||||
|
||||
/// Represents an error in password checking
|
||||
@@ -37,19 +46,10 @@ pub enum PakeError {
|
||||
///
|
||||
#[error("Internal error during PRF verification: {0}")]
|
||||
CryptoError(InternalPakeError),
|
||||
/// This error occurs when the symmetric encryption fails
|
||||
#[error("Symmetric encryption failed.")]
|
||||
EncryptionError,
|
||||
/// This error occurs when the symmetric decryption fails
|
||||
#[error("Symmetric decryption failed.")]
|
||||
DecryptionError,
|
||||
/// This error occurs when the symmetric decryption's hmac check fails
|
||||
#[error("HMAC check in symmetric decryption failed.")]
|
||||
DecryptionHmacError,
|
||||
/// This error occurs when the server object that is being called finish() on is malformed
|
||||
#[error("Incomplete set of keys passed into finish() function")]
|
||||
IncompleteKeysError,
|
||||
#[error("The provided server public key doesn't match the encrypted one")]
|
||||
#[error("The provided server public key doesn't match the sealed one")]
|
||||
IncompatibleServerStaticPublicKeyError,
|
||||
#[error("Error in key exchange protocol when attempting to validate MACs")]
|
||||
KeyExchangeMacValidationError,
|
||||
|
||||
+17
-32
@@ -5,14 +5,13 @@
|
||||
|
||||
//! An implementation of the OPAQUE asymmetric password authentication key exchange protocol
|
||||
//!
|
||||
//! Note: This implementation is in sync with [draft-krawczyk-cfrg-opaque-05](https://tools.ietf.org/html/draft-krawczyk-cfrg-opaque-05),
|
||||
//! Note: This implementation is in sync with [draft-krawczyk-cfrg-opaque-06](https://tools.ietf.org/html/draft-krawczyk-cfrg-opaque-06),
|
||||
//! but this specification is subject to change, until the final version published by the IETF.
|
||||
//!
|
||||
//! # Overview
|
||||
//!
|
||||
//! OPAQUE is a protocol between a client and a server. They must first agree on a collection of primitives
|
||||
//! to be kept consistent throughout protocol execution. These include:
|
||||
//! * an authenticated encryption scheme,
|
||||
//! * a finite cyclic group along with a point representation,
|
||||
//! * a keypair type, and
|
||||
//! * a slow hashing function.
|
||||
@@ -22,7 +21,6 @@
|
||||
//! use opaque_ke::ciphersuite::CipherSuite;
|
||||
//! struct Default;
|
||||
//! impl CipherSuite for Default {
|
||||
//! type Aead = chacha20poly1305::ChaCha20Poly1305;
|
||||
//! type Group = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
//! type KeyFormat = opaque_ke::keypair::X25519KeyPair;
|
||||
//! type SlowHash = opaque_ke::slow_hash::NoOpHash;
|
||||
@@ -43,7 +41,6 @@
|
||||
//! # use opaque_ke::ciphersuite::CipherSuite;
|
||||
//! # struct Default;
|
||||
//! # impl CipherSuite for Default {
|
||||
//! # type Aead = chacha20poly1305::ChaCha20Poly1305;
|
||||
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
//! # type KeyFormat = opaque_ke::keypair::X25519KeyPair;
|
||||
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
|
||||
@@ -75,7 +72,6 @@
|
||||
//! # use opaque_ke::ciphersuite::CipherSuite;
|
||||
//! # struct Default;
|
||||
//! # impl CipherSuite for Default {
|
||||
//! # type Aead = chacha20poly1305::ChaCha20Poly1305;
|
||||
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
//! # type KeyFormat = opaque_ke::keypair::X25519KeyPair;
|
||||
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
|
||||
@@ -104,7 +100,6 @@
|
||||
//! # use opaque_ke::ciphersuite::CipherSuite;
|
||||
//! # struct Default;
|
||||
//! # impl CipherSuite for Default {
|
||||
//! # type Aead = chacha20poly1305::ChaCha20Poly1305;
|
||||
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
//! # type KeyFormat = opaque_ke::keypair::X25519KeyPair;
|
||||
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
|
||||
@@ -125,7 +120,7 @@
|
||||
//!
|
||||
//! In the third step (client registration finish), the client takes as input the `r2` message from the server, along
|
||||
//! with the server's static public key `server_kp.public()`, and uses `client_state` from the first step to run
|
||||
//! `finish` and produce a message `r3` along with the key derivation key `kd_key_registration`:
|
||||
//! `finish` and produce a message `r3` along with the export key `export_key_registration`:
|
||||
//! ```
|
||||
//! # use opaque_ke::{
|
||||
//! # errors::ProtocolError,
|
||||
@@ -136,7 +131,6 @@
|
||||
//! # use opaque_ke::ciphersuite::CipherSuite;
|
||||
//! # struct Default;
|
||||
//! # impl CipherSuite for Default {
|
||||
//! # type Aead = chacha20poly1305::ChaCha20Poly1305;
|
||||
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
//! # type KeyFormat = opaque_ke::keypair::X25519KeyPair;
|
||||
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
|
||||
@@ -151,11 +145,11 @@
|
||||
//! # let mut server_rng = OsRng;
|
||||
//! let (r2, server_state) = ServerRegistration::<Default>::start(r1, &mut server_rng)?;
|
||||
//! # let server_kp = Default::generate_random_keypair(&mut server_rng)?;
|
||||
//! let (r3, kd_key_registration) =
|
||||
//! let (r3, export_key_registration) =
|
||||
//! client_state.finish(r2, server_kp.public(), &mut client_rng)?;
|
||||
//! # Ok::<(), ProtocolError>(())
|
||||
//! ```
|
||||
//! `r3` is sent to the server, and the client can optionally use `kd_key_registration` for applications that choose to
|
||||
//! `r3` is sent to the server, and the client can optionally use `export_key_registration` for applications that choose to
|
||||
//! process user information beyond the OPAQUE functionality (e.g., additional secrets or credentials).
|
||||
//!
|
||||
//! In the fourth step of registration, the server takes as input the `r3` message from the client and uses
|
||||
@@ -170,7 +164,6 @@
|
||||
//! # use opaque_ke::ciphersuite::CipherSuite;
|
||||
//! # struct Default;
|
||||
//! # impl CipherSuite for Default {
|
||||
//! # type Aead = chacha20poly1305::ChaCha20Poly1305;
|
||||
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
//! # type KeyFormat = opaque_ke::keypair::X25519KeyPair;
|
||||
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
|
||||
@@ -185,7 +178,7 @@
|
||||
//! # let mut server_rng = OsRng;
|
||||
//! let (r2, server_state) = ServerRegistration::<Default>::start(r1, &mut server_rng)?;
|
||||
//! # let server_kp = Default::generate_random_keypair(&mut server_rng)?;
|
||||
//! # let (r3, kd_key_registration) = client_state.finish(r2, server_kp.public(), &mut client_rng)?;
|
||||
//! # let (r3, export_key_registration) = client_state.finish(r2, server_kp.public(), &mut client_rng)?;
|
||||
//! let password_file = server_state.finish(r3)?;
|
||||
//! # Ok::<(), ProtocolError>(())
|
||||
//! ```
|
||||
@@ -212,7 +205,6 @@
|
||||
//! # use opaque_ke::ciphersuite::CipherSuite;
|
||||
//! # struct Default;
|
||||
//! # impl CipherSuite for Default {
|
||||
//! # type Aead = chacha20poly1305::ChaCha20Poly1305;
|
||||
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
//! # type KeyFormat = opaque_ke::keypair::X25519KeyPair;
|
||||
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
|
||||
@@ -241,7 +233,6 @@
|
||||
//! # use opaque_ke::ciphersuite::CipherSuite;
|
||||
//! # struct Default;
|
||||
//! # impl CipherSuite for Default {
|
||||
//! # type Aead = chacha20poly1305::ChaCha20Poly1305;
|
||||
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
//! # type KeyFormat = opaque_ke::keypair::X25519KeyPair;
|
||||
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
|
||||
@@ -256,7 +247,7 @@
|
||||
//! # let mut server_rng = OsRng;
|
||||
//! let (r2, server_state) = ServerRegistration::<Default>::start(r1, &mut server_rng)?;
|
||||
//! # let server_kp = Default::generate_random_keypair(&mut server_rng)?;
|
||||
//! # let (r3, kd_key_registration) = client_state.finish(r2, server_kp.public(), &mut client_rng)?;
|
||||
//! # let (r3, export_key_registration) = client_state.finish(r2, server_kp.public(), &mut client_rng)?;
|
||||
//! # let password_file_bytes = server_state.finish(r3)?.to_bytes();
|
||||
//! # let (l1, client_state) = ClientLogin::<Default>::start(
|
||||
//! # b"password",
|
||||
@@ -274,7 +265,7 @@
|
||||
//!
|
||||
//! In the third step (client login finish), the client takes as input the `l2` message from the server, along with the
|
||||
//! server's static public key `server_kp.public()`, and uses `client_state` from the first step to run `finish` and produce
|
||||
//! a message `l3`, the shared secret `client_shared_secret`, and the key derivation key `kd_key_login`:
|
||||
//! a message `l3`, the shared secret `client_shared_secret`, and the export key `export_key_login`:
|
||||
//! ```
|
||||
//! # use opaque_ke::{
|
||||
//! # errors::ProtocolError,
|
||||
@@ -285,7 +276,6 @@
|
||||
//! # use opaque_ke::ciphersuite::CipherSuite;
|
||||
//! # struct Default;
|
||||
//! # impl CipherSuite for Default {
|
||||
//! # type Aead = chacha20poly1305::ChaCha20Poly1305;
|
||||
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
//! # type KeyFormat = opaque_ke::keypair::X25519KeyPair;
|
||||
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
|
||||
@@ -300,7 +290,7 @@
|
||||
//! # let mut server_rng = OsRng;
|
||||
//! let (r2, server_state) = ServerRegistration::<Default>::start(r1, &mut server_rng)?;
|
||||
//! # let server_kp = Default::generate_random_keypair(&mut server_rng)?;
|
||||
//! # let (r3, kd_key_registration) = client_state.finish(r2, server_kp.public(), &mut client_rng)?;
|
||||
//! # let (r3, export_key_registration) = client_state.finish(r2, server_kp.public(), &mut client_rng)?;
|
||||
//! # let password_file_bytes = server_state.finish(r3)?.to_bytes();
|
||||
//! # let (l1, client_state) = ClientLogin::<Default>::start(
|
||||
//! # b"password",
|
||||
@@ -314,20 +304,20 @@
|
||||
//! # )?;
|
||||
//! # let (l2, server_state) =
|
||||
//! # ServerLogin::start(password_file, &server_kp.private(), l1, &mut server_rng)?;
|
||||
//! let (l3, client_shared_secret, kd_key_login) = client_state.finish(
|
||||
//! let (l3, client_shared_secret, export_key_login) = client_state.finish(
|
||||
//! l2,
|
||||
//! &server_kp.public(),
|
||||
//! &mut client_rng,
|
||||
//! )?;
|
||||
//! assert_eq!(kd_key_registration, kd_key_login);
|
||||
//! assert_eq!(export_key_registration, export_key_login);
|
||||
//! # Ok::<(), ProtocolError>(())
|
||||
//! ```
|
||||
//! Note that if the client supplies a tuple (password, pepper, server public key) that does not match the tuple
|
||||
//! used to create the password file, then at this point the `finish` algorithm outputs the error `InvalidLoginError`.
|
||||
//!
|
||||
//! If `finish` completes successfully, then `l3` is sent to the server, and (similarly to registration) the client
|
||||
//! can use `kd_key_login` for applications that can take advantage of the fact that this key is identical to
|
||||
//! `kd_key_registration`.
|
||||
//! can use `export_key_login` for applications that can take advantage of the fact that this key is identical to
|
||||
//! `export_key_registration`.
|
||||
//!
|
||||
//! In the fourth step of login, the server takes as input the `l3` message from the client and uses `server_state` from
|
||||
//! the second step to run `finish`:
|
||||
@@ -341,7 +331,6 @@
|
||||
//! # use opaque_ke::ciphersuite::CipherSuite;
|
||||
//! # struct Default;
|
||||
//! # impl CipherSuite for Default {
|
||||
//! # type Aead = chacha20poly1305::ChaCha20Poly1305;
|
||||
//! # type Group = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
//! # type KeyFormat = opaque_ke::keypair::X25519KeyPair;
|
||||
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
|
||||
@@ -356,7 +345,7 @@
|
||||
//! # let mut server_rng = OsRng;
|
||||
//! let (r2, server_state) = ServerRegistration::<Default>::start(r1, &mut server_rng)?;
|
||||
//! # let server_kp = Default::generate_random_keypair(&mut server_rng)?;
|
||||
//! # let (r3, kd_key) = client_state.finish(r2, server_kp.public(), &mut client_rng)?;
|
||||
//! # let (r3, export_key) = client_state.finish(r2, server_kp.public(), &mut client_rng)?;
|
||||
//! # let password_file_bytes = server_state.finish(r3)?.to_bytes();
|
||||
//! # let (l1, client_state) = ClientLogin::<Default>::start(
|
||||
//! # b"password",
|
||||
@@ -370,7 +359,7 @@
|
||||
//! # )?;
|
||||
//! # let (l2, server_state) =
|
||||
//! # ServerLogin::start(password_file, &server_kp.private(), l1, &mut server_rng)?;
|
||||
//! # let (l3, client_shared_secret, kd_key) = client_state.finish(
|
||||
//! # let (l3, client_shared_secret, export_key) = client_state.finish(
|
||||
//! # l2,
|
||||
//! # &server_kp.public(),
|
||||
//! # &mut client_rng,
|
||||
@@ -385,20 +374,16 @@
|
||||
|
||||
// Error types
|
||||
pub mod errors;
|
||||
|
||||
// High-level API
|
||||
pub mod opaque;
|
||||
|
||||
pub mod ciphersuite;
|
||||
// Your choice of RKR encryption
|
||||
mod rkr_encryption;
|
||||
// Your choice of KE
|
||||
mod envelope;
|
||||
mod group;
|
||||
mod key_exchange;
|
||||
pub mod keypair;
|
||||
// Low-level API contains OPRF stuff
|
||||
mod oprf;
|
||||
// Technical module for your choice of cyclic subgroup to
|
||||
// do the oprf on
|
||||
mod group;
|
||||
pub mod slow_hash;
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
+60
-105
@@ -7,6 +7,7 @@
|
||||
|
||||
use crate::{
|
||||
ciphersuite::CipherSuite,
|
||||
envelope::{Envelope, ExportKeySize},
|
||||
errors::{utils::check_slice_size, InternalPakeError, PakeError, ProtocolError},
|
||||
group::Group,
|
||||
key_exchange::{
|
||||
@@ -16,27 +17,13 @@ use crate::{
|
||||
keypair::{Key, KeyPair, SizedBytes},
|
||||
oprf,
|
||||
oprf::OprfClientBytes,
|
||||
rkr_encryption::{RKRCipher, RKRCiphertext},
|
||||
slow_hash::SlowHash,
|
||||
};
|
||||
use generic_array::{
|
||||
typenum::{Unsigned, U32},
|
||||
GenericArray,
|
||||
};
|
||||
use hkdf::Hkdf;
|
||||
use generic_array::{typenum::Unsigned, GenericArray};
|
||||
use rand_core::{CryptoRng, RngCore};
|
||||
|
||||
use sha2::{Digest, Sha256};
|
||||
use std::{convert::TryFrom, marker::PhantomData};
|
||||
use zeroize::Zeroize;
|
||||
|
||||
// Constant string used as salt for HKDF computation
|
||||
const STR_ENVU: &[u8] = b"EnvU";
|
||||
|
||||
/// The length of the "key-derivation key" output by the client registration
|
||||
/// and login finish steps
|
||||
pub const DERIVED_KEY_LEN: usize = 32;
|
||||
|
||||
// Messages
|
||||
// =========
|
||||
|
||||
@@ -99,19 +86,18 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// The final message from the client, containing encrypted cryptographic
|
||||
/// The final message from the client, containing sealed cryptographic
|
||||
/// identifiers
|
||||
pub struct RegisterThirdMessage<Aead, KeyFormat: KeyPair> {
|
||||
/// The "envelope" generated by the user, containing encrypted
|
||||
pub struct RegisterThirdMessage<KeyFormat: KeyPair> {
|
||||
/// The "envelope" generated by the user, containing sealed
|
||||
/// cryptographic identifiers
|
||||
envelope: RKRCiphertext<Aead>,
|
||||
envelope: Envelope,
|
||||
/// The user's public key
|
||||
client_s_pk: KeyFormat::Repr,
|
||||
}
|
||||
|
||||
impl<Aead, KeyFormat> RegisterThirdMessage<Aead, KeyFormat>
|
||||
impl<KeyFormat> RegisterThirdMessage<KeyFormat>
|
||||
where
|
||||
Aead: aead::Aead + aead::NewAead<KeySize = U32>,
|
||||
KeyFormat: KeyPair,
|
||||
{
|
||||
pub fn to_bytes(&self) -> Vec<u8> {
|
||||
@@ -123,23 +109,25 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<Aead, KeyFormat> TryFrom<&[u8]> for RegisterThirdMessage<Aead, KeyFormat>
|
||||
impl<KeyFormat> TryFrom<&[u8]> for RegisterThirdMessage<KeyFormat>
|
||||
where
|
||||
Aead: aead::Aead + aead::NewAead<KeySize = U32>,
|
||||
KeyFormat: KeyPair,
|
||||
{
|
||||
type Error = ProtocolError;
|
||||
|
||||
fn try_from(third_message_bytes: &[u8]) -> Result<Self, Self::Error> {
|
||||
let rkr_size = RKRCiphertext::<Aead>::rkr_with_nonce_size();
|
||||
let key_len = <KeyFormat::Repr as SizedBytes>::Len::to_usize();
|
||||
let checked_bytes =
|
||||
check_slice_size(third_message_bytes, rkr_size + key_len, "third_message")?;
|
||||
let unchecked_client_s_pk = KeyFormat::Repr::from_bytes(&checked_bytes[rkr_size..])?;
|
||||
let envelope_size = key_len + Envelope::additional_size();
|
||||
let checked_bytes = check_slice_size(
|
||||
third_message_bytes,
|
||||
envelope_size + key_len,
|
||||
"third_message",
|
||||
)?;
|
||||
let unchecked_client_s_pk = KeyFormat::Repr::from_bytes(&checked_bytes[envelope_size..])?;
|
||||
let client_s_pk = KeyFormat::check_public_key(unchecked_client_s_pk)?;
|
||||
|
||||
Ok(Self {
|
||||
envelope: RKRCiphertext::from_bytes(&checked_bytes[..rkr_size])?,
|
||||
envelope: Envelope::from_bytes(&checked_bytes[..envelope_size])?,
|
||||
client_s_pk,
|
||||
})
|
||||
}
|
||||
@@ -178,18 +166,19 @@ impl<Grp: Group> LoginFirstMessage<Grp> {
|
||||
|
||||
/// The answer sent by the server to the user, upon reception of the
|
||||
/// login attempt.
|
||||
pub struct LoginSecondMessage<Aead, Grp> {
|
||||
pub struct LoginSecondMessage<Grp, KeyFormat> {
|
||||
_key_format: PhantomData<KeyFormat>,
|
||||
/// the server's oprf output
|
||||
beta: Grp,
|
||||
/// the user's encrypted information,
|
||||
envelope: RKRCiphertext<Aead>,
|
||||
/// the user's sealed information,
|
||||
envelope: Envelope,
|
||||
ke2_message: KE2Message,
|
||||
}
|
||||
|
||||
impl<Aead, Grp> LoginSecondMessage<Aead, Grp>
|
||||
impl<Grp, KeyFormat> LoginSecondMessage<Grp, KeyFormat>
|
||||
where
|
||||
Aead: aead::NewAead<KeySize = U32> + aead::Aead,
|
||||
Grp: Group,
|
||||
KeyFormat: KeyPair,
|
||||
{
|
||||
pub fn to_bytes(&self) -> Vec<u8> {
|
||||
[
|
||||
@@ -201,18 +190,19 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<Aead, Grp> TryFrom<&[u8]> for LoginSecondMessage<Aead, Grp>
|
||||
impl<Grp, KeyFormat> TryFrom<&[u8]> for LoginSecondMessage<Grp, KeyFormat>
|
||||
where
|
||||
Aead: aead::NewAead<KeySize = U32> + aead::Aead,
|
||||
Grp: Group,
|
||||
KeyFormat: KeyPair,
|
||||
{
|
||||
type Error = ProtocolError;
|
||||
fn try_from(second_message_bytes: &[u8]) -> Result<Self, Self::Error> {
|
||||
let cipher_len = RKRCiphertext::<Aead>::rkr_with_nonce_size();
|
||||
let key_len = <KeyFormat::Repr as SizedBytes>::Len::to_usize();
|
||||
let envelope_size = key_len + Envelope::additional_size();
|
||||
let elem_len = Grp::ElemLen::to_usize();
|
||||
let checked_slice = check_slice_size(
|
||||
second_message_bytes,
|
||||
elem_len + cipher_len + KE2_MESSAGE_LEN,
|
||||
elem_len + envelope_size + KE2_MESSAGE_LEN,
|
||||
"login_second_message_bytes",
|
||||
)?;
|
||||
|
||||
@@ -222,11 +212,11 @@ where
|
||||
let arr = GenericArray::from_slice(beta_bytes);
|
||||
let beta = Grp::from_element_slice(arr)?;
|
||||
|
||||
let envelope =
|
||||
RKRCiphertext::<Aead>::from_bytes(&checked_slice[elem_len..elem_len + cipher_len])?;
|
||||
let ke2_message = KE2Message::try_from(&checked_slice[elem_len + cipher_len..])?;
|
||||
let envelope = Envelope::from_bytes(&checked_slice[elem_len..elem_len + envelope_size])?;
|
||||
let ke2_message = KE2Message::try_from(&checked_slice[elem_len + envelope_size..])?;
|
||||
|
||||
Ok(Self {
|
||||
_key_format: PhantomData,
|
||||
beta,
|
||||
envelope,
|
||||
ke2_message,
|
||||
@@ -235,7 +225,7 @@ where
|
||||
}
|
||||
|
||||
/// The answer sent by the client to the server, upon reception of the
|
||||
/// encrypted envelope
|
||||
/// sealed envelope
|
||||
pub struct LoginThirdMessage {
|
||||
ke3_message: KE3Message,
|
||||
}
|
||||
@@ -260,8 +250,6 @@ impl LoginThirdMessage {
|
||||
|
||||
/// The state elements the client holds to register itself
|
||||
pub struct ClientRegistration<CS: CipherSuite> {
|
||||
/// A choice of symmetric encryption for the envelope
|
||||
_aead: PhantomData<CS::Aead>,
|
||||
/// a blinding factor
|
||||
pub(crate) blinding_factor: <<CS as CipherSuite>::Group as Group>::Scalar,
|
||||
/// the client's password
|
||||
@@ -278,7 +266,6 @@ impl<CS: CipherSuite> TryFrom<&[u8]> for ClientRegistration<CS> {
|
||||
let blinding_factor = CS::Group::from_scalar_slice(blinding_factor_bytes)?;
|
||||
let password = bytes[scalar_len..].to_vec();
|
||||
Ok(Self {
|
||||
_aead: PhantomData,
|
||||
blinding_factor,
|
||||
password,
|
||||
})
|
||||
@@ -311,7 +298,6 @@ impl<CS: CipherSuite> ClientRegistration<CS> {
|
||||
/// use opaque_ke::ciphersuite::CipherSuite;
|
||||
/// struct Default;
|
||||
/// impl CipherSuite for Default {
|
||||
/// type Aead = chacha20poly1305::ChaCha20Poly1305;
|
||||
/// type Group = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
/// type KeyFormat = opaque_ke::keypair::X25519KeyPair;
|
||||
/// type SlowHash = opaque_ke::slow_hash::NoOpHash;
|
||||
@@ -333,7 +319,6 @@ impl<CS: CipherSuite> ClientRegistration<CS> {
|
||||
Ok((
|
||||
RegisterFirstMessage::<CS::Group> { alpha },
|
||||
Self {
|
||||
_aead: PhantomData,
|
||||
blinding_factor,
|
||||
password: password.to_vec(),
|
||||
},
|
||||
@@ -341,9 +326,9 @@ impl<CS: CipherSuite> ClientRegistration<CS> {
|
||||
}
|
||||
}
|
||||
|
||||
type ClientRegistrationFinishResult<Aead, KeyFormat> = (
|
||||
RegisterThirdMessage<Aead, KeyFormat>,
|
||||
GenericArray<u8, <Sha256 as Digest>::OutputSize>,
|
||||
type ClientRegistrationFinishResult<KeyFormat> = (
|
||||
RegisterThirdMessage<KeyFormat>,
|
||||
GenericArray<u8, ExportKeySize>,
|
||||
);
|
||||
|
||||
impl<CS: CipherSuite> ClientRegistration<CS> {
|
||||
@@ -363,7 +348,6 @@ impl<CS: CipherSuite> ClientRegistration<CS> {
|
||||
/// use opaque_ke::ciphersuite::CipherSuite;
|
||||
/// struct Default;
|
||||
/// impl CipherSuite for Default {
|
||||
/// type Aead = chacha20poly1305::ChaCha20Poly1305;
|
||||
/// type Group = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
/// type KeyFormat = opaque_ke::keypair::X25519KeyPair;
|
||||
/// type SlowHash = opaque_ke::slow_hash::NoOpHash;
|
||||
@@ -383,7 +367,7 @@ impl<CS: CipherSuite> ClientRegistration<CS> {
|
||||
r2: RegisterSecondMessage<CS::Group>,
|
||||
server_s_pk: &<<CS as CipherSuite>::KeyFormat as KeyPair>::Repr,
|
||||
rng: &mut R,
|
||||
) -> Result<ClientRegistrationFinishResult<CS::Aead, CS::KeyFormat>, ProtocolError> {
|
||||
) -> Result<ClientRegistrationFinishResult<CS::KeyFormat>, ProtocolError> {
|
||||
let client_static_keypair = CS::KeyFormat::generate_random(rng)?;
|
||||
|
||||
let password_derived_key = get_password_derived_key::<CS::Group, CS::SlowHash>(
|
||||
@@ -391,17 +375,9 @@ impl<CS: CipherSuite> ClientRegistration<CS> {
|
||||
r2.beta,
|
||||
&self.blinding_factor,
|
||||
)?;
|
||||
let h = Hkdf::<Sha256>::new(None, &password_derived_key);
|
||||
let mut okm = [0u8; 3 * DERIVED_KEY_LEN];
|
||||
h.expand(STR_ENVU, &mut okm)
|
||||
.map_err(|_| InternalPakeError::HkdfError)?;
|
||||
let encryption_key = &okm[..DERIVED_KEY_LEN];
|
||||
let hmac_key = &okm[DERIVED_KEY_LEN..2 * DERIVED_KEY_LEN];
|
||||
let kd_key = &okm[2 * DERIVED_KEY_LEN..];
|
||||
|
||||
let envelope = RKRCiphertext::<CS::Aead>::encrypt(
|
||||
&encryption_key,
|
||||
&hmac_key,
|
||||
let (envelope, export_key) = Envelope::seal(
|
||||
&password_derived_key,
|
||||
&client_static_keypair.private().to_arr(),
|
||||
&server_s_pk.to_arr(),
|
||||
rng,
|
||||
@@ -412,7 +388,7 @@ impl<CS: CipherSuite> ClientRegistration<CS> {
|
||||
envelope,
|
||||
client_s_pk: client_static_keypair.public().clone(),
|
||||
},
|
||||
*GenericArray::from_slice(&kd_key),
|
||||
export_key,
|
||||
))
|
||||
}
|
||||
}
|
||||
@@ -447,7 +423,7 @@ impl<CS: CipherSuite> Drop for ClientLogin<CS> {
|
||||
|
||||
/// The state elements the server holds to record a registration
|
||||
pub struct ServerRegistration<CS: CipherSuite> {
|
||||
envelope: Option<RKRCiphertext<CS::Aead>>,
|
||||
envelope: Option<Envelope>,
|
||||
client_s_pk: Option<<<CS as CipherSuite>::KeyFormat as KeyPair>::Repr>,
|
||||
pub(crate) oprf_key: <<CS as CipherSuite>::Group as Group>::Scalar,
|
||||
}
|
||||
@@ -466,7 +442,7 @@ where
|
||||
let key_len =
|
||||
<<<CS as CipherSuite>::KeyFormat as KeyPair>::Repr as SizedBytes>::Len::to_usize();
|
||||
let scalar_len = <<CS as CipherSuite>::Group as Group>::ScalarLen::to_usize();
|
||||
let rkr_size = RKRCiphertext::<CS::Aead>::rkr_with_nonce_size();
|
||||
let envelope_size = key_len + Envelope::additional_size();
|
||||
|
||||
if server_registration_bytes.len() == scalar_len {
|
||||
return Ok(Self {
|
||||
@@ -480,7 +456,7 @@ where
|
||||
|
||||
let checked_bytes = check_slice_size(
|
||||
server_registration_bytes,
|
||||
rkr_size + key_len + scalar_len,
|
||||
envelope_size + key_len + scalar_len,
|
||||
"server_registration_bytes",
|
||||
)?;
|
||||
let oprf_key_bytes = GenericArray::from_slice(&checked_bytes[..scalar_len]);
|
||||
@@ -490,8 +466,8 @@ where
|
||||
)?;
|
||||
let client_s_pk = CS::KeyFormat::check_public_key(unchecked_client_s_pk)?;
|
||||
Ok(Self {
|
||||
envelope: Some(RKRCiphertext::from_bytes(
|
||||
&checked_bytes[checked_bytes.len() - rkr_size..],
|
||||
envelope: Some(Envelope::from_bytes(
|
||||
&checked_bytes[checked_bytes.len() - envelope_size..],
|
||||
)?),
|
||||
client_s_pk: Some(client_s_pk),
|
||||
oprf_key,
|
||||
@@ -536,7 +512,6 @@ where
|
||||
/// use opaque_ke::ciphersuite::CipherSuite;
|
||||
/// struct Default;
|
||||
/// impl CipherSuite for Default {
|
||||
/// type Aead = chacha20poly1305::ChaCha20Poly1305;
|
||||
/// type Group = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
/// type KeyFormat = opaque_ke::keypair::X25519KeyPair;
|
||||
/// type SlowHash = opaque_ke::slow_hash::NoOpHash;
|
||||
@@ -583,7 +558,6 @@ where
|
||||
/// use opaque_ke::ciphersuite::CipherSuite;
|
||||
/// struct Default;
|
||||
/// impl CipherSuite for Default {
|
||||
/// type Aead = chacha20poly1305::ChaCha20Poly1305;
|
||||
/// type Group = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
/// type KeyFormat = opaque_ke::keypair::X25519KeyPair;
|
||||
/// type SlowHash = opaque_ke::slow_hash::NoOpHash;
|
||||
@@ -601,7 +575,7 @@ where
|
||||
/// ```
|
||||
pub fn finish(
|
||||
self,
|
||||
message: RegisterThirdMessage<CS::Aead, CS::KeyFormat>,
|
||||
message: RegisterThirdMessage<CS::KeyFormat>,
|
||||
) -> Result<Self, ProtocolError> {
|
||||
Ok(Self {
|
||||
envelope: Some(message.envelope),
|
||||
@@ -616,8 +590,6 @@ where
|
||||
|
||||
/// The state elements the client holds to perform a login
|
||||
pub struct ClientLogin<CS: CipherSuite> {
|
||||
/// A choice of symmetric encryption for the envelope
|
||||
_aead: PhantomData<CS::Aead>,
|
||||
/// A choice of the keypair type
|
||||
_key_format: PhantomData<CS::KeyFormat>,
|
||||
/// A blinding factor, which is used to mask (and unmask) secret
|
||||
@@ -637,7 +609,6 @@ impl<CS: CipherSuite> TryFrom<&[u8]> for ClientLogin<CS> {
|
||||
let ke1_state = KE1State::try_from(&bytes[scalar_len..scalar_len + KE1_STATE_LEN])?;
|
||||
let password = bytes[scalar_len + KE1_STATE_LEN..].to_vec();
|
||||
Ok(Self {
|
||||
_aead: PhantomData,
|
||||
_key_format: PhantomData,
|
||||
blinding_factor,
|
||||
password,
|
||||
@@ -658,11 +629,7 @@ impl<CS: CipherSuite> ClientLogin<CS> {
|
||||
}
|
||||
}
|
||||
|
||||
type ClientLoginFinishResult = (
|
||||
LoginThirdMessage,
|
||||
Vec<u8>,
|
||||
GenericArray<u8, <Sha256 as Digest>::OutputSize>,
|
||||
);
|
||||
type ClientLoginFinishResult = (LoginThirdMessage, Vec<u8>, GenericArray<u8, ExportKeySize>);
|
||||
|
||||
impl<CS: CipherSuite> ClientLogin<CS> {
|
||||
/// Returns an initial "blinded" password request to send to the server, as well as a ClientLogin
|
||||
@@ -679,7 +646,6 @@ impl<CS: CipherSuite> ClientLogin<CS> {
|
||||
/// use opaque_ke::ciphersuite::CipherSuite;
|
||||
/// struct Default;
|
||||
/// impl CipherSuite for Default {
|
||||
/// type Aead = chacha20poly1305::ChaCha20Poly1305;
|
||||
/// type Group = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
/// type KeyFormat = opaque_ke::keypair::X25519KeyPair;
|
||||
/// type SlowHash = opaque_ke::slow_hash::NoOpHash;
|
||||
@@ -706,7 +672,6 @@ impl<CS: CipherSuite> ClientLogin<CS> {
|
||||
Ok((
|
||||
l1,
|
||||
Self {
|
||||
_aead: PhantomData,
|
||||
_key_format: PhantomData,
|
||||
blinding_factor,
|
||||
password: password.to_vec(),
|
||||
@@ -715,7 +680,7 @@ impl<CS: CipherSuite> ClientLogin<CS> {
|
||||
))
|
||||
}
|
||||
|
||||
/// "Unblinds" the server's answer and returns the decrypted assets from
|
||||
/// "Unblinds" the server's answer and returns the opened assets from
|
||||
/// the server
|
||||
///
|
||||
/// # Arguments
|
||||
@@ -732,7 +697,6 @@ impl<CS: CipherSuite> ClientLogin<CS> {
|
||||
/// use opaque_ke::ciphersuite::CipherSuite;
|
||||
/// struct Default;
|
||||
/// impl CipherSuite for Default {
|
||||
/// type Aead = chacha20poly1305::ChaCha20Poly1305;
|
||||
/// type Group = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
/// type KeyFormat = opaque_ke::keypair::X25519KeyPair;
|
||||
/// type SlowHash = opaque_ke::slow_hash::NoOpHash;
|
||||
@@ -751,7 +715,7 @@ impl<CS: CipherSuite> ClientLogin<CS> {
|
||||
/// ```
|
||||
pub fn finish<R: RngCore + CryptoRng>(
|
||||
self,
|
||||
l2: LoginSecondMessage<CS::Aead, CS::Group>,
|
||||
l2: LoginSecondMessage<CS::Group, CS::KeyFormat>,
|
||||
server_s_pk: &<<CS as CipherSuite>::KeyFormat as KeyPair>::Repr,
|
||||
_client_e_sk_rng: &mut R,
|
||||
) -> Result<ClientLoginFinishResult, ProtocolError> {
|
||||
@@ -762,35 +726,27 @@ impl<CS: CipherSuite> ClientLogin<CS> {
|
||||
l2.beta,
|
||||
&self.blinding_factor,
|
||||
)?;
|
||||
let h = Hkdf::<Sha256>::new(None, &password_derived_key);
|
||||
let mut okm = [0u8; 3 * DERIVED_KEY_LEN];
|
||||
h.expand(STR_ENVU, &mut okm)
|
||||
.map_err(|_| InternalPakeError::HkdfError)?;
|
||||
let encryption_key = &okm[..DERIVED_KEY_LEN];
|
||||
let hmac_key = &okm[DERIVED_KEY_LEN..2 * DERIVED_KEY_LEN];
|
||||
let kd_key = &okm[2 * DERIVED_KEY_LEN..];
|
||||
|
||||
let client_s_sk = Key::from_bytes(
|
||||
&l2.envelope
|
||||
.decrypt(&encryption_key, &hmac_key, &server_s_pk.to_arr())
|
||||
.map_err(|e| match e {
|
||||
PakeError::DecryptionHmacError => PakeError::InvalidLoginError,
|
||||
err => err,
|
||||
})?,
|
||||
)?;
|
||||
let (client_s_sk, export_key) = &l2
|
||||
.envelope
|
||||
.open(&password_derived_key, &server_s_pk.to_arr())
|
||||
.map_err(|e| match e {
|
||||
InternalPakeError::SealOpenHmacError => PakeError::InvalidLoginError,
|
||||
err => PakeError::from(err),
|
||||
})?;
|
||||
|
||||
let (ke3_state, ke3_message) = generate_ke3::<CS::KeyFormat>(
|
||||
l2_bytes,
|
||||
l2.ke2_message,
|
||||
&self.ke1_state,
|
||||
server_s_pk.clone(),
|
||||
client_s_sk,
|
||||
Key::from_bytes(client_s_sk)?,
|
||||
)?;
|
||||
|
||||
Ok((
|
||||
LoginThirdMessage { ke3_message },
|
||||
ke3_state.shared_secret,
|
||||
*GenericArray::from_slice(&kd_key),
|
||||
*export_key,
|
||||
))
|
||||
}
|
||||
}
|
||||
@@ -810,7 +766,7 @@ impl TryFrom<&[u8]> for ServerLogin {
|
||||
}
|
||||
|
||||
type ServerLoginStartResult<CS> = (
|
||||
LoginSecondMessage<<CS as CipherSuite>::Aead, <CS as CipherSuite>::Group>,
|
||||
LoginSecondMessage<<CS as CipherSuite>::Group, <CS as CipherSuite>::KeyFormat>,
|
||||
ServerLogin,
|
||||
);
|
||||
|
||||
@@ -836,7 +792,6 @@ impl ServerLogin {
|
||||
/// use opaque_ke::ciphersuite::CipherSuite;
|
||||
/// struct Default;
|
||||
/// impl CipherSuite for Default {
|
||||
/// type Aead = chacha20poly1305::ChaCha20Poly1305;
|
||||
/// type Group = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
/// type KeyFormat = opaque_ke::keypair::X25519KeyPair;
|
||||
/// type SlowHash = opaque_ke::slow_hash::NoOpHash;
|
||||
@@ -864,8 +819,8 @@ impl ServerLogin {
|
||||
|
||||
let client_s_pk = password_file
|
||||
.client_s_pk
|
||||
.ok_or(PakeError::EncryptionError)?;
|
||||
let envelope = password_file.envelope.ok_or(PakeError::EncryptionError)?;
|
||||
.ok_or(InternalPakeError::SealError)?;
|
||||
let envelope = password_file.envelope.ok_or(InternalPakeError::SealError)?;
|
||||
|
||||
let l2_component: Vec<u8> = [beta.to_bytes().as_slice(), &envelope.to_bytes()].concat();
|
||||
|
||||
@@ -880,6 +835,7 @@ impl ServerLogin {
|
||||
)?;
|
||||
|
||||
let l2 = LoginSecondMessage {
|
||||
_key_format: PhantomData,
|
||||
beta,
|
||||
envelope,
|
||||
ke2_message,
|
||||
@@ -905,7 +861,6 @@ impl ServerLogin {
|
||||
/// use opaque_ke::ciphersuite::CipherSuite;
|
||||
/// struct Default;
|
||||
/// impl CipherSuite for Default {
|
||||
/// type Aead = chacha20poly1305::ChaCha20Poly1305;
|
||||
/// type Group = curve25519_dalek::ristretto::RistrettoPoint;
|
||||
/// type KeyFormat = opaque_ke::keypair::X25519KeyPair;
|
||||
/// type SlowHash = opaque_ke::slow_hash::NoOpHash;
|
||||
|
||||
@@ -7,13 +7,8 @@ use crate::{errors::InternalPakeError, group::Group};
|
||||
use generic_array::{typenum::U64, GenericArray};
|
||||
use hkdf::Hkdf;
|
||||
use rand_core::{CryptoRng, RngCore};
|
||||
|
||||
use sha2::{Digest, Sha256};
|
||||
|
||||
// Low-level API
|
||||
// =============
|
||||
// This file contains an implementation of an oblivious pseudorandom function (OPRF), as well as password hashing and encryption functions.
|
||||
|
||||
pub(crate) struct OprfClientBytes<Grp: Group> {
|
||||
pub(crate) alpha: Grp,
|
||||
pub(crate) blinding_factor: Grp::Scalar,
|
||||
|
||||
@@ -1,202 +0,0 @@
|
||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
||||
//
|
||||
// This source code is licensed under the MIT license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
|
||||
use crate::errors::{utils::check_slice_size, InternalPakeError, PakeError};
|
||||
use aead::{Aead, NewAead};
|
||||
use generic_array::{typenum::Unsigned, GenericArray};
|
||||
use hmac::{Hmac, Mac, NewMac};
|
||||
use rand_core::{CryptoRng, RngCore};
|
||||
use sha2::{Digest, Sha256};
|
||||
|
||||
/// This trait encapsulates an encryption scheme that satisfies random-key robustness (RKR), which is implemented
|
||||
/// through encrypt-then-HMAC -- see Section 3.1.1 of
|
||||
/// https://www.ietf.org/id/draft-krawczyk-cfrg-opaque-03.txt
|
||||
/// We require an Aead implementation with a 32-bit key size, since we
|
||||
/// will derive the symmetric key from pw using Sha256
|
||||
pub trait RKRCipher: Sized {
|
||||
/// The requirement of KeySize = U32 is so that we can use a 32-bit hash
|
||||
/// for key derivation form the user's password
|
||||
type AEAD: NewAead<KeySize = <Sha256 as Digest>::OutputSize> + Aead;
|
||||
|
||||
// Required members
|
||||
fn new(
|
||||
aead_output: Vec<u8>,
|
||||
hmac: &GenericArray<u8, <Sha256 as Digest>::OutputSize>,
|
||||
nonce: &GenericArray<u8, <Self::AEAD as Aead>::NonceSize>,
|
||||
) -> Self;
|
||||
|
||||
fn aead_output(&self) -> &Vec<u8>;
|
||||
fn hmac(&self) -> &GenericArray<u8, <Sha256 as Digest>::OutputSize>;
|
||||
fn nonce(&self) -> &GenericArray<u8, <Self::AEAD as Aead>::NonceSize>;
|
||||
|
||||
fn to_bytes(&self) -> Vec<u8>;
|
||||
|
||||
// Provided members for enc / dec
|
||||
fn key_len() -> usize {
|
||||
<Self::AEAD as NewAead>::KeySize::to_usize()
|
||||
}
|
||||
|
||||
fn nonce_size() -> usize {
|
||||
<Self::AEAD as Aead>::NonceSize::to_usize()
|
||||
}
|
||||
|
||||
fn hmac_size() -> usize {
|
||||
<Sha256 as Digest>::OutputSize::to_usize()
|
||||
}
|
||||
|
||||
/// This estimates the size of the ciphertext once we encode —very specifically—
|
||||
/// the payload we have planned for the protocol's env_u
|
||||
fn ciphertest_size() -> usize {
|
||||
Self::key_len() + <Self::AEAD as Aead>::TagSize::to_usize() + Self::hmac_size()
|
||||
}
|
||||
|
||||
fn rkr_with_nonce_size() -> usize {
|
||||
Self::ciphertest_size() + Self::nonce_size()
|
||||
}
|
||||
|
||||
/// The format of the output ciphertext here is:
|
||||
/// encryption_output | tag | hmac | nonce
|
||||
/// variable length | AEAD_TAG_SIZE bytes | HMAC_SIZE bytes | NONCE_SIZE bytes
|
||||
fn from_bytes(bytes: &[u8]) -> Result<Self, InternalPakeError> {
|
||||
let checked_bytes = check_slice_size(&bytes[..], Self::rkr_with_nonce_size(), "bytes")?;
|
||||
let nonce_start = bytes.len() - Self::nonce_size();
|
||||
let hmac_start = nonce_start - Self::hmac_size();
|
||||
|
||||
Ok(<Self as RKRCipher>::new(
|
||||
bytes[..hmac_start].to_vec(),
|
||||
GenericArray::from_slice(&checked_bytes[hmac_start..nonce_start]),
|
||||
GenericArray::from_slice(&checked_bytes[nonce_start..]),
|
||||
))
|
||||
}
|
||||
|
||||
/// Encrypt with AEAD. Note that this encryption scheme needs to satisfy "random-key robustness" (RKR).
|
||||
fn encrypt<R: RngCore + CryptoRng>(
|
||||
encryption_key: &[u8],
|
||||
hmac_key: &[u8],
|
||||
plaintext: &[u8],
|
||||
aad: &[u8],
|
||||
rng: &mut R,
|
||||
) -> Result<Self, PakeError> {
|
||||
let mut nonce = vec![0u8; Self::nonce_size()];
|
||||
rng.fill_bytes(&mut nonce);
|
||||
let gen_nonce = GenericArray::from_slice(&nonce[..]);
|
||||
|
||||
let ciphertext = <Self::AEAD as NewAead>::new(GenericArray::from_slice(&encryption_key))
|
||||
.encrypt(
|
||||
GenericArray::from_slice(&nonce),
|
||||
aead::Payload {
|
||||
msg: &plaintext,
|
||||
aad: &aad,
|
||||
},
|
||||
)
|
||||
.map_err(|_| PakeError::EncryptionError)?;
|
||||
|
||||
let mut mac =
|
||||
Hmac::<Sha256>::new_varkey(&hmac_key).map_err(|_| InternalPakeError::HmacError)?;
|
||||
mac.update(&ciphertext);
|
||||
|
||||
Ok(<Self as RKRCipher>::new(
|
||||
ciphertext,
|
||||
&mac.finalize().into_bytes(),
|
||||
gen_nonce,
|
||||
))
|
||||
}
|
||||
|
||||
fn decrypt(
|
||||
&self,
|
||||
encryption_key: &[u8],
|
||||
hmac_key: &[u8],
|
||||
aad: &[u8],
|
||||
) -> Result<Vec<u8>, PakeError> {
|
||||
let mut mac =
|
||||
Hmac::<Sha256>::new_varkey(&hmac_key).map_err(|_| InternalPakeError::HmacError)?;
|
||||
mac.update(self.aead_output());
|
||||
if mac.verify(self.hmac()).is_err() {
|
||||
return Err(PakeError::DecryptionHmacError);
|
||||
}
|
||||
|
||||
Aead::decrypt(
|
||||
&<Self::AEAD as NewAead>::new(GenericArray::from_slice(&encryption_key)),
|
||||
self.nonce(),
|
||||
aead::Payload {
|
||||
msg: self.aead_output(),
|
||||
aad: &aad,
|
||||
},
|
||||
)
|
||||
.map_err(|_| PakeError::DecryptionError)
|
||||
}
|
||||
}
|
||||
|
||||
/// This struct is a straightforward instantiation of the trait separating the
|
||||
/// three components in Vecs
|
||||
pub struct RKRCiphertext<T> {
|
||||
aead_choice: std::marker::PhantomData<T>,
|
||||
aead_output: Vec<u8>,
|
||||
hmac: Vec<u8>,
|
||||
nonce: Vec<u8>,
|
||||
}
|
||||
|
||||
impl<T: NewAead<KeySize = <Sha256 as Digest>::OutputSize> + Aead> RKRCipher for RKRCiphertext<T> {
|
||||
type AEAD = T;
|
||||
|
||||
fn new(
|
||||
aead_output: Vec<u8>,
|
||||
hmac: &GenericArray<u8, <Sha256 as Digest>::OutputSize>,
|
||||
nonce: &GenericArray<u8, <Self::AEAD as Aead>::NonceSize>,
|
||||
) -> Self {
|
||||
Self {
|
||||
aead_choice: std::marker::PhantomData,
|
||||
aead_output,
|
||||
hmac: hmac.to_vec(),
|
||||
nonce: nonce.to_vec(),
|
||||
}
|
||||
}
|
||||
|
||||
fn aead_output(&self) -> &Vec<u8> {
|
||||
&self.aead_output
|
||||
}
|
||||
|
||||
fn to_bytes(&self) -> Vec<u8> {
|
||||
[&self.aead_output[..], &self.hmac[..], &self.nonce[..]].concat()
|
||||
}
|
||||
|
||||
fn hmac(&self) -> &GenericArray<u8, <Sha256 as Digest>::OutputSize> {
|
||||
GenericArray::from_slice(&self.hmac[..])
|
||||
}
|
||||
|
||||
fn nonce(&self) -> &GenericArray<u8, <T as Aead>::NonceSize> {
|
||||
GenericArray::from_slice(&self.nonce[..])
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use chacha20poly1305::ChaCha20Poly1305;
|
||||
use rand_core::OsRng;
|
||||
|
||||
#[test]
|
||||
fn encrypt_and_decrypt() {
|
||||
let mut rng = OsRng;
|
||||
let mut encryption_key = [0u8; 32];
|
||||
rng.fill_bytes(&mut encryption_key);
|
||||
let mut hmac_key = [0u8; 32];
|
||||
rng.fill_bytes(&mut hmac_key);
|
||||
|
||||
let mut msg = [0u8; 100];
|
||||
rng.fill_bytes(&mut msg);
|
||||
|
||||
let ciphertext = RKRCiphertext::<ChaCha20Poly1305>::encrypt(
|
||||
&encryption_key,
|
||||
&hmac_key,
|
||||
&msg,
|
||||
b"",
|
||||
&mut rng,
|
||||
)
|
||||
.unwrap();
|
||||
let decrypted = ciphertext.decrypt(&encryption_key, &hmac_key, b"").unwrap();
|
||||
assert_eq!(&msg.to_vec(), &decrypted);
|
||||
}
|
||||
}
|
||||
+58
-64
@@ -8,11 +8,11 @@ use crate::{
|
||||
errors::*,
|
||||
group::Group,
|
||||
key_exchange::NONCE_LEN,
|
||||
keypair::{Key, KeyPair},
|
||||
keypair::{Key, KeyPair, X25519KeyPair},
|
||||
opaque::*,
|
||||
slow_hash::NoOpHash,
|
||||
tests::mock_rng::CycleRng,
|
||||
};
|
||||
use aes_gcm::Aes256Gcm;
|
||||
use curve25519_dalek::edwards::EdwardsPoint;
|
||||
use rand_core::{OsRng, RngCore};
|
||||
use serde_json::Value;
|
||||
@@ -21,12 +21,11 @@ use std::convert::TryFrom;
|
||||
// Tests
|
||||
// =====
|
||||
|
||||
struct AesgcmX255193dhNoSlowHash;
|
||||
impl CipherSuite for AesgcmX255193dhNoSlowHash {
|
||||
type Aead = Aes256Gcm;
|
||||
struct X255193dhNoSlowHash;
|
||||
impl CipherSuite for X255193dhNoSlowHash {
|
||||
type Group = EdwardsPoint;
|
||||
type KeyFormat = crate::keypair::X25519KeyPair;
|
||||
type SlowHash = crate::slow_hash::NoOpHash;
|
||||
type KeyFormat = X25519KeyPair;
|
||||
type SlowHash = NoOpHash;
|
||||
}
|
||||
|
||||
pub struct TestVectorParameters {
|
||||
@@ -63,35 +62,35 @@ pub struct TestVectorParameters {
|
||||
|
||||
static TEST_VECTOR: &str = r#"
|
||||
{
|
||||
"client_s_pk": "f7b150789db3322c8c7b8c4a10ce42baa5ee846de83eaf04c17ffbd0d9e5cd60",
|
||||
"client_s_sk": "601ed276a42ec5795b3471f1a64e312f192e17ff252ce6053c8ecaf210138273",
|
||||
"client_e_pk": "57260d4e231035f0f3e1fb836fe5d9ddb498c956cacb5fab1d6b287e1422376c",
|
||||
"client_e_sk": "e89d0fa4e387a9bd7c26466704ec30e62f58892bf3dfd1fd25133be52f34ea68",
|
||||
"server_s_pk": "a2b4e12d0621ebfb2631e00f5c872ab749e1a33915f16fb11203658b2189cc5e",
|
||||
"server_s_sk": "90b6ca2ea8a37306060c7cd0998d4cdae59e972af7760312f7cf77099e78f940",
|
||||
"server_e_pk": "64ce4a453eb8c27b1d81f6acdc01d36d3ae6cea506432e9509917b195ad90073",
|
||||
"server_e_sk": "883148cc1ba70acb1eb909d99e09493b5d4b3fe6b12c75e2f5aeea6c5d4b267f",
|
||||
"client_s_pk": "4374a0bf4f5f4353c20337574bdc69e6cff18a36e3fd8fe89fe47e02bab06e37",
|
||||
"client_s_sk": "f0804c53de3fce8c9a6bcccb3380a1427c578a2f834de89fc98447c322776955",
|
||||
"client_e_pk": "b45316eaa2d87ac1ed5c28b54ed43578fc54d4beb673b748159cea7536215819",
|
||||
"client_e_sk": "60ff0381b8d8e6913d75e7a3caf2ede4e755d33337a6d904239fa6b00338cb56",
|
||||
"server_s_pk": "c76f269ba1e0fb19597907ddd9e0fc9a4ac11e499e77b5ae480deb825dc2e044",
|
||||
"server_s_sk": "384e6efd4236c76e7e84ae13327b867d85881f27df7f65e409c66a8f1f2c8354",
|
||||
"server_e_pk": "92d4883730b19e767fc2f8224dac4a913f404e6d05a9baaf1f34ec9078dfb648",
|
||||
"server_e_sk": "e88a6ec8d5644b66c20d6a915a94b27a17787196ab9f9bc0ee600d11d739b576",
|
||||
"password": "70617373776f7264",
|
||||
"blinding_factor_raw": "b85e0df2ad0495771edf09a04b1073045e6472e2f86a41e9bab3143ebfb8eb08a3462503eb3750bf006dc82c93b37e07cdf3768018c22b431cf5146a9caeda1c",
|
||||
"blinding_factor": "fac0ed1c38bc8945a91dc4d944af22c466cbffc24fc3d97b8a91798d1ec8b60f",
|
||||
"blinding_factor_raw": "0412c9968a8e6ea19dd42eca36c35ff784d6e83944d833bdb919f8af6166ce00b742fe149fb6d9ba7d130edc983802bf91b510157d221e800e76c3aed1740159",
|
||||
"blinding_factor": "0c9957936385474a1862bc9da3d60d6655b030fd6fc8de1fc7842163007f5a03",
|
||||
"pepper": "706570706572",
|
||||
"oprf_key": "d5cedff72509af4702a985bb31af8dbe88d72c4eee13a09e3f52a76766fa6f0b",
|
||||
"envelope_nonce": "c87e44792a9dfd8858db676e",
|
||||
"client_nonce": "1f023acc6155a06166ee7e5b7ef0360277ed5da3a46adcd4a0a5bce938a67a23",
|
||||
"server_nonce": "d448cb1f58c38605fc29069ac688ec9c667c99d0316b38cd1b2609c1bc14aa90",
|
||||
"r1": "e46efe7d673805b6135a5293ecab13082b322c45f029595efa4b8d1d53ccd897",
|
||||
"r2": "a2a3df89cf85976c4aa5add752736419f728805722571a9646983587ce4c55fb",
|
||||
"r3": "374c49768e4399d4cd46e8b3bc2050e2f6737e3a2f8aee6fddc82e117f340f79a7f10c84445657c6bb4940bd02bc08ca0f107618d810ec94639e8ae43af48ab66f1f75e8bbc169eed0035e347310978bc87e44792a9dfd8858db676ef7b150789db3322c8c7b8c4a10ce42baa5ee846de83eaf04c17ffbd0d9e5cd60",
|
||||
"l1": "e46efe7d673805b6135a5293ecab13082b322c45f029595efa4b8d1d53ccd8971f023acc6155a06166ee7e5b7ef0360277ed5da3a46adcd4a0a5bce938a67a2357260d4e231035f0f3e1fb836fe5d9ddb498c956cacb5fab1d6b287e1422376c",
|
||||
"l2": "a2a3df89cf85976c4aa5add752736419f728805722571a9646983587ce4c55fb374c49768e4399d4cd46e8b3bc2050e2f6737e3a2f8aee6fddc82e117f340f79a7f10c84445657c6bb4940bd02bc08ca0f107618d810ec94639e8ae43af48ab66f1f75e8bbc169eed0035e347310978bc87e44792a9dfd8858db676e883148cc1ba70acb1eb909d99e09493b5d4b3fe6b12c75e2f5aeea6c5d4b267f64ce4a453eb8c27b1d81f6acdc01d36d3ae6cea506432e9509917b195ad90073d81a1104fbd599ef56228bdbe9bf7be4a38ae907a8717ca0883b9d69b2efc529",
|
||||
"l3": "a01332643e8aa7113f6f160205a9b3bd0705f3b33d8e4ea8eab9eae6685a6adb",
|
||||
"client_registration_state": "fac0ed1c38bc8945a91dc4d944af22c466cbffc24fc3d97b8a91798d1ec8b60f70617373776f7264",
|
||||
"client_login_state": "fac0ed1c38bc8945a91dc4d944af22c466cbffc24fc3d97b8a91798d1ec8b60fe89d0fa4e387a9bd7c26466704ec30e62f58892bf3dfd1fd25133be52f34ea681f023acc6155a06166ee7e5b7ef0360277ed5da3a46adcd4a0a5bce938a67a23dd1a7c2b4e9f9be94bd36f3b6c7f23aa9f1e6b3fda9030412a918d1288b4af1970617373776f7264",
|
||||
"server_registration_state": "d5cedff72509af4702a985bb31af8dbe88d72c4eee13a09e3f52a76766fa6f0b",
|
||||
"server_login_state": "809f95143f8f7fc1d0b42f578a83f714f58cfd96d9499aacee730ad296b37b19c18c903396e85da607d02542d4d07456e5357ff2e2eade3aaa42e532d4e9364f66317ab0460307e33d6151e99c7406f2fa1d309f507b46e43f732924d1dc8d0d",
|
||||
"password_file": "d5cedff72509af4702a985bb31af8dbe88d72c4eee13a09e3f52a76766fa6f0bf7b150789db3322c8c7b8c4a10ce42baa5ee846de83eaf04c17ffbd0d9e5cd60374c49768e4399d4cd46e8b3bc2050e2f6737e3a2f8aee6fddc82e117f340f79a7f10c84445657c6bb4940bd02bc08ca0f107618d810ec94639e8ae43af48ab66f1f75e8bbc169eed0035e347310978bc87e44792a9dfd8858db676e",
|
||||
"opaque_key": "682f2868a3e1460fed5a16767bd8778c33b4aecac6607270f848aa61c95a1a68",
|
||||
"shared_secret": "66317ab0460307e33d6151e99c7406f2fa1d309f507b46e43f732924d1dc8d0d"
|
||||
"oprf_key": "764fc3396026a513ede0332f2d0801b7c02516241b473362844a9aea613e3800",
|
||||
"envelope_nonce": "acca14c1d5f7f5843812ad61",
|
||||
"client_nonce": "db2c06ad77d6d6b73170cb26c082c3fea77c64201b021f3d22f477bd5fd4cf9b",
|
||||
"server_nonce": "861dbe0a824fc9a6ebc90a798dd5827888c30c8f8f79c361fb487db5b9a65586",
|
||||
"r1": "3b12967295493838ce743c3fa5e3da39d13589aacad2cb67792df9e99dbefff3",
|
||||
"r2": "4cce303b33ed400fc60cdaa9d314021c9a9a1c29faab56ede9fc580bdb0287c3",
|
||||
"r3": "acca14c1d5f7f5843812ad61f0804c53de3fce8c9a6bcccb3380a1427c578a2f4df43147c5396441be3dee47f441e6cb56eca2cf7e25cc94ef233b2f1d3b0e64142d3d40fc61226627bca32c331dc00b9e9e795a1cfb0377d3a79f565307e1074374a0bf4f5f4353c20337574bdc69e6cff18a36e3fd8fe89fe47e02bab06e37",
|
||||
"l1": "3b12967295493838ce743c3fa5e3da39d13589aacad2cb67792df9e99dbefff3db2c06ad77d6d6b73170cb26c082c3fea77c64201b021f3d22f477bd5fd4cf9bb45316eaa2d87ac1ed5c28b54ed43578fc54d4beb673b748159cea7536215819",
|
||||
"l2": "4cce303b33ed400fc60cdaa9d314021c9a9a1c29faab56ede9fc580bdb0287c3acca14c1d5f7f5843812ad61f0804c53de3fce8c9a6bcccb3380a1427c578a2f4df43147c5396441be3dee47f441e6cb56eca2cf7e25cc94ef233b2f1d3b0e64142d3d40fc61226627bca32c331dc00b9e9e795a1cfb0377d3a79f565307e107e88a6ec8d5644b66c20d6a915a94b27a17787196ab9f9bc0ee600d11d739b57692d4883730b19e767fc2f8224dac4a913f404e6d05a9baaf1f34ec9078dfb648c87b63566267c0941b269f8ad36228ae24a2cd9eb8f90e8f6bc26140c2f93bb4",
|
||||
"l3": "a20bb3efbdbccb23cd6206ef0483cf52a1e3b2f700c4c9aac0c9bab2f4326265",
|
||||
"client_registration_state": "0c9957936385474a1862bc9da3d60d6655b030fd6fc8de1fc7842163007f5a0370617373776f7264",
|
||||
"client_login_state": "0c9957936385474a1862bc9da3d60d6655b030fd6fc8de1fc7842163007f5a0360ff0381b8d8e6913d75e7a3caf2ede4e755d33337a6d904239fa6b00338cb56db2c06ad77d6d6b73170cb26c082c3fea77c64201b021f3d22f477bd5fd4cf9bb898ccd56020538145fc2192532bde9f8da0183a1dc486ae3086aa3b72c6728570617373776f7264",
|
||||
"server_registration_state": "764fc3396026a513ede0332f2d0801b7c02516241b473362844a9aea613e3800",
|
||||
"server_login_state": "75780c16fc843510e2222f195859b3f84c224e378f6afc6685827c30c11add9c3435530551d074a98f30d8e0f078efde0693738808a2c6358a4d34b64004a6c544b77d404067257aab5829b59a5f8a306c586c5a73018a9837c8b9a33d9fed6e",
|
||||
"password_file": "764fc3396026a513ede0332f2d0801b7c02516241b473362844a9aea613e38004374a0bf4f5f4353c20337574bdc69e6cff18a36e3fd8fe89fe47e02bab06e37acca14c1d5f7f5843812ad61f0804c53de3fce8c9a6bcccb3380a1427c578a2f4df43147c5396441be3dee47f441e6cb56eca2cf7e25cc94ef233b2f1d3b0e64142d3d40fc61226627bca32c331dc00b9e9e795a1cfb0377d3a79f565307e107",
|
||||
"opaque_key": "653ec91b97e928868c8977b4339530a5c1298dd9c69f1a1752a56785eca6e82c",
|
||||
"shared_secret": "44b77d404067257aab5829b59a5f8a306c586c5a73018a9837c8b9a33d9fed6e"
|
||||
}
|
||||
"#;
|
||||
|
||||
@@ -219,14 +218,14 @@ fn stringify_test_vectors(p: &TestVectorParameters) -> String {
|
||||
s
|
||||
}
|
||||
|
||||
fn generate_parameters() -> TestVectorParameters {
|
||||
fn generate_parameters<CS: CipherSuite>() -> TestVectorParameters {
|
||||
let mut rng = OsRng;
|
||||
|
||||
// Inputs
|
||||
let server_s_kp = AesgcmX255193dhNoSlowHash::generate_random_keypair(&mut rng).unwrap();
|
||||
let server_e_kp = AesgcmX255193dhNoSlowHash::generate_random_keypair(&mut rng).unwrap();
|
||||
let client_s_kp = AesgcmX255193dhNoSlowHash::generate_random_keypair(&mut rng).unwrap();
|
||||
let client_e_kp = AesgcmX255193dhNoSlowHash::generate_random_keypair(&mut rng).unwrap();
|
||||
let server_s_kp = CS::generate_random_keypair(&mut rng).unwrap();
|
||||
let server_e_kp = CS::generate_random_keypair(&mut rng).unwrap();
|
||||
let client_s_kp = CS::generate_random_keypair(&mut rng).unwrap();
|
||||
let client_e_kp = CS::generate_random_keypair(&mut rng).unwrap();
|
||||
let password = b"password";
|
||||
let pepper = b"pepper";
|
||||
let mut blinding_factor_raw = [0u8; 64];
|
||||
@@ -241,22 +240,21 @@ fn generate_parameters() -> TestVectorParameters {
|
||||
rng.fill_bytes(&mut server_nonce);
|
||||
|
||||
let mut blinding_factor_registration_rng = CycleRng::new(blinding_factor_raw.to_vec());
|
||||
let (r1, client_registration) = ClientRegistration::<AesgcmX255193dhNoSlowHash>::start(
|
||||
let (r1, client_registration) = ClientRegistration::<CS>::start(
|
||||
password,
|
||||
Some(pepper),
|
||||
&mut blinding_factor_registration_rng,
|
||||
)
|
||||
.unwrap();
|
||||
let r1_bytes = r1.to_bytes().to_vec();
|
||||
let blinding_factor_bytes = client_registration.blinding_factor.to_bytes();
|
||||
let blinding_factor_bytes =
|
||||
CS::Group::scalar_as_bytes(&client_registration.blinding_factor).clone();
|
||||
let client_registration_state = client_registration.to_bytes().to_vec();
|
||||
|
||||
let mut oprf_key_rng = CycleRng::new(oprf_key_raw.to_vec());
|
||||
let (r2, server_registration) =
|
||||
ServerRegistration::<AesgcmX255193dhNoSlowHash>::start(r1, &mut oprf_key_rng).unwrap();
|
||||
let (r2, server_registration) = ServerRegistration::<CS>::start(r1, &mut oprf_key_rng).unwrap();
|
||||
let r2_bytes = r2.to_bytes().to_vec();
|
||||
let oprf_key = server_registration.oprf_key;
|
||||
let oprf_key_bytes = EdwardsPoint::scalar_as_bytes(&oprf_key);
|
||||
let oprf_key_bytes = CS::Group::scalar_as_bytes(&server_registration.oprf_key).clone();
|
||||
let server_registration_state = server_registration.to_bytes().to_vec();
|
||||
|
||||
let mut client_s_sk_and_nonce: Vec<u8> = Vec::new();
|
||||
@@ -278,12 +276,8 @@ fn generate_parameters() -> TestVectorParameters {
|
||||
client_login_start.extend_from_slice(&client_nonce);
|
||||
|
||||
let mut client_login_start_rng = CycleRng::new(client_login_start);
|
||||
let (l1, client_login) = ClientLogin::<AesgcmX255193dhNoSlowHash>::start(
|
||||
password,
|
||||
Some(pepper),
|
||||
&mut client_login_start_rng,
|
||||
)
|
||||
.unwrap();
|
||||
let (l1, client_login) =
|
||||
ClientLogin::<CS>::start(password, Some(pepper), &mut client_login_start_rng).unwrap();
|
||||
let l1_bytes = l1.to_bytes().to_vec();
|
||||
let client_login_state = client_login.to_bytes().to_vec();
|
||||
|
||||
@@ -339,7 +333,7 @@ fn generate_parameters() -> TestVectorParameters {
|
||||
|
||||
#[test]
|
||||
fn generate_test_vectors() {
|
||||
let parameters = generate_parameters();
|
||||
let parameters = generate_parameters::<X255193dhNoSlowHash>();
|
||||
println!("{}", stringify_test_vectors(¶meters));
|
||||
}
|
||||
|
||||
@@ -347,7 +341,7 @@ fn generate_test_vectors() {
|
||||
fn test_r1() -> Result<(), PakeError> {
|
||||
let parameters = populate_test_vectors(&serde_json::from_str(TEST_VECTOR).unwrap());
|
||||
let mut blinding_factor_rng = CycleRng::new(parameters.blinding_factor_raw);
|
||||
let (r1, client_registration) = ClientRegistration::<AesgcmX255193dhNoSlowHash>::start(
|
||||
let (r1, client_registration) = ClientRegistration::<X255193dhNoSlowHash>::start(
|
||||
¶meters.password,
|
||||
Some(¶meters.pepper),
|
||||
&mut blinding_factor_rng,
|
||||
@@ -365,7 +359,7 @@ fn test_r1() -> Result<(), PakeError> {
|
||||
fn test_r2() -> Result<(), PakeError> {
|
||||
let parameters = populate_test_vectors(&serde_json::from_str(TEST_VECTOR).unwrap());
|
||||
let mut oprf_key_rng = CycleRng::new(parameters.oprf_key);
|
||||
let (r2, server_registration) = ServerRegistration::<AesgcmX255193dhNoSlowHash>::start(
|
||||
let (r2, server_registration) = ServerRegistration::<X255193dhNoSlowHash>::start(
|
||||
RegisterFirstMessage::try_from(¶meters.r1[..]).unwrap(),
|
||||
&mut oprf_key_rng,
|
||||
)
|
||||
@@ -385,7 +379,7 @@ fn test_r3() -> Result<(), PakeError> {
|
||||
let client_s_sk_and_nonce: Vec<u8> =
|
||||
[parameters.client_s_sk, parameters.envelope_nonce].concat();
|
||||
let mut finish_registration_rng = CycleRng::new(client_s_sk_and_nonce);
|
||||
let (r3, opaque_key_registration) = ClientRegistration::<AesgcmX255193dhNoSlowHash>::try_from(
|
||||
let (r3, opaque_key_registration) = ClientRegistration::<X255193dhNoSlowHash>::try_from(
|
||||
¶meters.client_registration_state[..],
|
||||
)
|
||||
.unwrap()
|
||||
@@ -409,7 +403,7 @@ fn test_r3() -> Result<(), PakeError> {
|
||||
fn test_password_file() -> Result<(), PakeError> {
|
||||
let parameters = populate_test_vectors(&serde_json::from_str(TEST_VECTOR).unwrap());
|
||||
|
||||
let server_registration = ServerRegistration::<AesgcmX255193dhNoSlowHash>::try_from(
|
||||
let server_registration = ServerRegistration::<X255193dhNoSlowHash>::try_from(
|
||||
¶meters.server_registration_state[..],
|
||||
)
|
||||
.unwrap();
|
||||
@@ -435,7 +429,7 @@ fn test_l1() -> Result<(), PakeError> {
|
||||
]
|
||||
.concat();
|
||||
let mut client_login_start_rng = CycleRng::new(client_login_start);
|
||||
let (l1, client_login) = ClientLogin::<AesgcmX255193dhNoSlowHash>::start(
|
||||
let (l1, client_login) = ClientLogin::<X255193dhNoSlowHash>::start(
|
||||
¶meters.password,
|
||||
Some(¶meters.pepper),
|
||||
&mut client_login_start_rng,
|
||||
@@ -454,7 +448,7 @@ fn test_l2() -> Result<(), PakeError> {
|
||||
let parameters = populate_test_vectors(&serde_json::from_str(TEST_VECTOR).unwrap());
|
||||
|
||||
let mut server_e_sk_rng = CycleRng::new(parameters.server_e_sk);
|
||||
let (l2, server_login) = ServerLogin::start::<AesgcmX255193dhNoSlowHash, _>(
|
||||
let (l2, server_login) = ServerLogin::start::<X255193dhNoSlowHash, _>(
|
||||
ServerRegistration::try_from(¶meters.password_file[..]).unwrap(),
|
||||
&Key::try_from(¶meters.server_s_sk[..]).unwrap(),
|
||||
LoginFirstMessage::<EdwardsPoint>::try_from(¶meters.l1[..]).unwrap(),
|
||||
@@ -476,10 +470,10 @@ fn test_l3() -> Result<(), PakeError> {
|
||||
|
||||
let mut client_e_sk_rng = CycleRng::new(parameters.client_e_sk.to_vec());
|
||||
let (l3, shared_secret, opaque_key_login) =
|
||||
ClientLogin::<AesgcmX255193dhNoSlowHash>::try_from(¶meters.client_login_state[..])
|
||||
ClientLogin::<X255193dhNoSlowHash>::try_from(¶meters.client_login_state[..])
|
||||
.unwrap()
|
||||
.finish(
|
||||
LoginSecondMessage::<Aes256Gcm, EdwardsPoint>::try_from(¶meters.l2[..])
|
||||
LoginSecondMessage::<EdwardsPoint, X25519KeyPair>::try_from(¶meters.l2[..])
|
||||
.unwrap(),
|
||||
&Key::try_from(¶meters.server_s_pk[..])?,
|
||||
&mut client_e_sk_rng,
|
||||
@@ -522,19 +516,19 @@ fn test_complete_flow(
|
||||
) -> Result<(), ProtocolError> {
|
||||
let mut client_rng = OsRng;
|
||||
let mut server_rng = OsRng;
|
||||
let server_kp = AesgcmX255193dhNoSlowHash::generate_random_keypair(&mut server_rng)?;
|
||||
let (register_m1, client_state) = ClientRegistration::<AesgcmX255193dhNoSlowHash>::start(
|
||||
let server_kp = X255193dhNoSlowHash::generate_random_keypair(&mut server_rng)?;
|
||||
let (register_m1, client_state) = ClientRegistration::<X255193dhNoSlowHash>::start(
|
||||
registration_password,
|
||||
None,
|
||||
&mut client_rng,
|
||||
)?;
|
||||
let (register_m2, server_state) =
|
||||
ServerRegistration::<AesgcmX255193dhNoSlowHash>::start(register_m1, &mut server_rng)?;
|
||||
ServerRegistration::<X255193dhNoSlowHash>::start(register_m1, &mut server_rng)?;
|
||||
let (register_m3, registration_opaque_key) =
|
||||
client_state.finish(register_m2, server_kp.public(), &mut client_rng)?;
|
||||
let p_file = server_state.finish(register_m3)?;
|
||||
let (login_m1, client_login_state) =
|
||||
ClientLogin::<AesgcmX255193dhNoSlowHash>::start(login_password, None, &mut client_rng)?;
|
||||
ClientLogin::<X255193dhNoSlowHash>::start(login_password, None, &mut client_rng)?;
|
||||
let (login_m2, server_login_state) =
|
||||
ServerLogin::start(p_file, &server_kp.private(), login_m1, &mut server_rng)?;
|
||||
|
||||
|
||||
+13
-23
@@ -5,16 +5,15 @@
|
||||
|
||||
use crate::{
|
||||
ciphersuite::CipherSuite,
|
||||
envelope::Envelope,
|
||||
group::Group,
|
||||
key_exchange::{KE1Message, NONCE_LEN},
|
||||
keypair::{KeyPair, SizedBytes, X25519KeyPair},
|
||||
opaque::*,
|
||||
rkr_encryption::{RKRCipher as _, RKRCiphertext},
|
||||
};
|
||||
|
||||
use curve25519_dalek::ristretto::RistrettoPoint;
|
||||
|
||||
use chacha20poly1305::ChaCha20Poly1305;
|
||||
use generic_array::typenum::Unsigned;
|
||||
use rand_core::{OsRng, RngCore};
|
||||
|
||||
use sha2::{Digest, Sha256};
|
||||
@@ -22,7 +21,6 @@ use std::convert::TryFrom;
|
||||
|
||||
struct Default;
|
||||
impl CipherSuite for Default {
|
||||
type Aead = ChaCha20Poly1305;
|
||||
type Group = RistrettoPoint;
|
||||
type KeyFormat = crate::keypair::X25519KeyPair;
|
||||
type SlowHash = crate::slow_hash::NoOpHash;
|
||||
@@ -67,16 +65,18 @@ fn server_registration_roundtrip() {
|
||||
assert_eq!(reg_bytes, oprf_bytes);
|
||||
// If we do have envelope and client pk, the server registration contains
|
||||
// the whole kit
|
||||
let rkr_size = RKRCiphertext::<ChaCha20Poly1305>::rkr_with_nonce_size();
|
||||
let mut mock_rkr_bytes = vec![0u8; rkr_size];
|
||||
rng.fill_bytes(&mut mock_rkr_bytes);
|
||||
println!("{}", mock_rkr_bytes.len());
|
||||
let key_len =
|
||||
<<<Default as CipherSuite>::KeyFormat as KeyPair>::Repr as SizedBytes>::Len::to_usize();
|
||||
let envelope_size = key_len + Envelope::additional_size();
|
||||
let mut mock_envelope_bytes = vec![0u8; envelope_size];
|
||||
rng.fill_bytes(&mut mock_envelope_bytes);
|
||||
println!("{}", mock_envelope_bytes.len());
|
||||
let mock_client_kp = Default::generate_random_keypair(&mut rng).unwrap();
|
||||
// serialization order: scalar, public key, envelope
|
||||
let mut bytes = Vec::<u8>::new();
|
||||
bytes.extend_from_slice(sc.as_bytes());
|
||||
bytes.extend_from_slice(&mock_client_kp.public().to_arr());
|
||||
bytes.extend_from_slice(&mock_rkr_bytes);
|
||||
bytes.extend_from_slice(&mock_envelope_bytes);
|
||||
let reg = ServerRegistration::<Default>::try_from(&bytes[..]).unwrap();
|
||||
let reg_bytes = reg.to_bytes();
|
||||
assert_eq!(reg_bytes, bytes);
|
||||
@@ -108,26 +108,16 @@ fn register_third_message_roundtrip() {
|
||||
let skp = Default::generate_random_keypair(&mut rng).unwrap();
|
||||
let pubkey_bytes = skp.public().to_arr();
|
||||
|
||||
let mut encryption_key = [0u8; 32];
|
||||
rng.fill_bytes(&mut encryption_key);
|
||||
let mut hmac_key = [0u8; 32];
|
||||
rng.fill_bytes(&mut hmac_key);
|
||||
let mut key = [0u8; 32];
|
||||
rng.fill_bytes(&mut key);
|
||||
|
||||
let mut msg = [0u8; 32];
|
||||
rng.fill_bytes(&mut msg);
|
||||
|
||||
let ciphertext = RKRCiphertext::<ChaCha20Poly1305>::encrypt(
|
||||
&encryption_key,
|
||||
&hmac_key,
|
||||
&msg,
|
||||
&pubkey_bytes,
|
||||
&mut rng,
|
||||
)
|
||||
.unwrap();
|
||||
let (ciphertext, _) = Envelope::seal(&key, &msg, &pubkey_bytes, &mut rng).unwrap();
|
||||
|
||||
let message: Vec<u8> = [&ciphertext.to_bytes(), &pubkey_bytes[..]].concat();
|
||||
let r3 =
|
||||
RegisterThirdMessage::<ChaCha20Poly1305, X25519KeyPair>::try_from(&message[..]).unwrap();
|
||||
let r3 = RegisterThirdMessage::<X25519KeyPair>::try_from(&message[..]).unwrap();
|
||||
let r3_bytes = r3.to_bytes();
|
||||
assert_eq!(message, r3_bytes);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user