22 Commits
Author SHA1 Message Date
Kevin LewiandKevin Lewi 0c535c0989 Publishing v0.5.1 2021-07-16 00:54:26 -07:00
Kevin LewiandKevin Lewi 9c28c8597a Adding reflected value check on client side
(cherry picked from commit b4882f8810)
2021-07-15 00:46:10 -07:00
Kevin LewiandKevin Lewi e5619d48cd Switch CI to using stable instead of nightly toolchain
(cherry picked from commit 6062bdb5cd)
2021-07-14 22:04:08 -07:00
Kevin LewiandKevin Lewi a69ad9473a Adding identity element checks and ensuring non-zero scalar selection
(cherry picked from commit 98f1821897)
2021-07-14 21:42:32 -07:00
Kevin Lewi f15b37fda4 Adding i2osp error checking condition
(cherry picked from commit e6b5a5dcf6)
2021-07-11 18:23:25 -07:00
Kevin Lewi 27f6975136 Fixing minor nits: conversion to u16 and removing keypair constructor
(cherry picked from commit c8c57785af)
2021-07-07 13:39:03 -07:00
Kevin Lewi 3c4b7ce482 Ensuring mac operations are constant-time
(cherry picked from commit 940d1dcdb2)
2021-07-07 13:35:03 -07:00
Kevin Lewi 05427dd97d Add zeroize on drop for remaining intermediate API states and tests
(cherry picked from commit 8bc5e7dc02)
2021-07-07 13:34:56 -07:00
TonyandKevin Lewi ec8f87944b Zeroize keys on drop (#156)
(cherry picked from commit 468e0690d7)
2021-07-07 13:33:08 -07:00
Kevin LewiandGitHub ba8e940e08 Updating to 0.5 with removing generic_bytes_derive (#150) 2021-03-01 19:23:09 -08:00
Kevin LewiandGitHub ac08801f18 Updating to version 0.4.0 and simplifying transcript computation logic in TripleDH (#148) 2021-02-26 15:18:10 -08:00
Konstantinos ChalkiasandGitHub a49e089e54 Merge pull request #146 from novifinancial/kostas-duplicate-code-v2
Helper generate_nonce to avoid duplicated code + default values for Scrypt
2021-02-21 22:33:12 -08:00
Kostas Chalkias 37f98b3742 Helper function generate_nonce to avoid duplicates + default values for Scrypt 2021-02-21 20:29:26 -08:00
Kevin LewiandGitHub fa0fb48654 Fixing test vector bug with envelope ciphertext serialization and updating VOPRF (#145) 2021-02-21 18:44:36 -08:00
Kevin LewiandGitHub 4da6bbce0c Fixing key sizes and removing serialization (#144) 2021-02-20 03:51:04 -08:00
Kevin LewiandGitHub a457d82793 Updating transcript computation for 3DH (#143) 2021-02-19 12:02:44 -08:00
Kevin LewiandGitHub 782eeb7090 Converting to_bytes and try_from to serialize() and deserialize() (#138) 2021-02-17 02:47:00 -08:00
Kevin LewiandGitHub bb7d17a102 Fixing deny.toml (#142) 2021-02-17 02:28:49 -08:00
Kevin LewiandGitHub 7f84984f61 Making message type parameters set to CipherSuite (#140) 2021-02-17 02:13:03 -08:00
Kevin LewiandGitHub e4636e0a97 Releasing v0.3.1 (#141) 2021-02-11 18:20:11 -08:00
Kevin LewiandGitHub e694a88ef9 Exporting rand (#137) 2021-02-11 18:10:48 -08:00
Kevin LewiandGitHub f596e4bf0e Fixing clippy issues (#139) 2021-02-11 03:41:01 -08:00
28 changed files with 1982 additions and 1282 deletions
+20 -10
View File
@@ -2,7 +2,6 @@ name: Rust CI
on:
push:
branches:
- tls
- master
pull_request:
types: [opened, repoened, synchronize]
@@ -16,16 +15,19 @@ jobs:
backend_feature:
- u64_backend
- u32_backend
toolchain:
- stable
- 1.41.0
name: test
steps:
- name: Checkout sources
uses: actions/checkout@v2
- name: Install nightly toolchain
- name: Install ${{ matrix.toolchain }} toolchain
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: nightly
toolchain: ${{ matrix.toolchain }}
override: true
components: rustfmt, clippy
@@ -75,6 +77,10 @@ jobs:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
toolchain:
- stable
- 1.41.0
name: test simple_login command-line example
steps:
- name: install expect
@@ -85,7 +91,7 @@ jobs:
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: nightly
toolchain: ${{ matrix.toolchain }}
override: true
components: rustfmt, clippy
- name: Run expect (which then runs cargo run)
@@ -95,6 +101,10 @@ jobs:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
toolchain:
- stable
- 1.41.0
name: test digital_locker command-line example
steps:
- name: install expect
@@ -105,7 +115,7 @@ jobs:
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: nightly
toolchain: ${{ matrix.toolchain }}
override: true
components: rustfmt, clippy
- name: Run expect (which then runs cargo run)
@@ -118,7 +128,7 @@ jobs:
- name: Checkout sources
uses: actions/checkout@v2
- name: Install nightly toolchain
- name: Install stable toolchain
uses: actions-rs/toolchain@v1
with:
profile: minimal
@@ -138,11 +148,11 @@ jobs:
- name: Checkout sources
uses: actions/checkout@v2
- name: Install nightly toolchain
- name: Install stable toolchain
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: nightly
toolchain: stable
override: true
components: rustfmt, clippy
@@ -160,11 +170,11 @@ jobs:
- name: Checkout sources
uses: actions/checkout@v2
- name: Install nightly toolchain
- name: Install stable toolchain
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: nightly
toolchain: stable
override: true
components: rustfmt, clippy
+25
View File
@@ -1,5 +1,30 @@
# Changelog
## 0.5.1 (July 16, 2021)
* Various security improvements: non-zero scalars, zeroizing on drop,
constant-time operations, reflected value check, and adding an
i2osp error condition
## 0.5.0 (March 1, 2020)
* Removed dependency on generic-bytes-derive package
## 0.4.0 (February 26, 2020)
* Adherence to protocol format described in
https://tools.ietf.org/html/draft-irtf-cfrg-opaque-03
* Renamed to_bytes() and try_from() to serialize() and deserialize() for
top-level structs
* Conformed all message type parameters to be parameterized in the
Ciphersuite object
## 0.3.1 (February 11, 2020)
* Re-exporting the rand library (and including it as a dependency instead of
just rand_core)
* Exposing a convenience function for converting from byte array to Key type
## 0.3.0 (February 8, 2020)
* General API and documentation improvements, including the support of custom
Generated
+219 -234
View File
@@ -1,5 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "aead"
version = "0.3.2"
@@ -11,21 +13,9 @@ dependencies = [
[[package]]
name = "anyhow"
version = "1.0.35"
version = "1.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c0df63cb2955042487fad3aefd2c6e3ae7389ac5dc1beb28921de0b69f779d4"
[[package]]
name = "arrayref"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544"
[[package]]
name = "arrayvec"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
checksum = "afddf7f520a80dbf76e6f50a35bca42a2331ef227a28b3b6dc5c2e2338d114b1"
[[package]]
name = "atty"
@@ -40,9 +30,9 @@ dependencies = [
[[package]]
name = "autocfg"
version = "1.0.0"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
[[package]]
name = "base64"
@@ -61,9 +51,9 @@ dependencies = [
[[package]]
name = "bit-vec"
version = "0.6.2"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f0dc55f2d8a1a85650ac47858bb001b4c0dd73d79e3c455a842925e68d29cd3"
checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
[[package]]
name = "bitflags"
@@ -71,17 +61,6 @@ version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
[[package]]
name = "blake2b_simd"
version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "afa748e348ad3be8263be728124b24a24f268266f6f5d58af9d75f6a40b5c587"
dependencies = [
"arrayref",
"arrayvec",
"constant_time_eq",
]
[[package]]
name = "block-buffer"
version = "0.9.0"
@@ -93,9 +72,9 @@ dependencies = [
[[package]]
name = "bstr"
version = "0.2.13"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31accafdb70df7871592c058eca3985b71104e15ac32f64706022c58867da931"
checksum = "a40b47ad93e1a5404e6c18dec46b628214fee441c70f4ab5d6942142cc268a3d"
dependencies = [
"lazy_static",
"memchr",
@@ -105,15 +84,15 @@ dependencies = [
[[package]]
name = "bumpalo"
version = "3.4.0"
version = "3.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e8c087f005730276d1096a652e92a8bacee2e2472bcc9715a74d2bec38b5820"
checksum = "099e596ef14349721d9016f6b80dd3419ea1bf289ab9b44df8e4dfd3a005d5d9"
[[package]]
name = "byteorder"
version = "1.3.4"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
checksum = "ae44d1a3d5a19df61dd0c8beb138458ac2a53a7ac09eba97d55592540004306b"
[[package]]
name = "cast"
@@ -126,9 +105,9 @@ dependencies = [
[[package]]
name = "cc"
version = "1.0.61"
version = "1.0.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed67cbde08356238e75fc4656be4749481eeffb09e19f320a25237d5221c985d"
checksum = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48"
[[package]]
name = "cfg-if"
@@ -176,15 +155,21 @@ dependencies = [
[[package]]
name = "clap"
version = "2.33.1"
version = "2.33.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bdfa80d47f954d53a35a64987ca1422f495b8d6483c0fe9f7117b36c2a792129"
checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
dependencies = [
"bitflags",
"textwrap",
"unicode-width",
]
[[package]]
name = "const_fn"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28b9d6de7f49e22cf97ad17fc4036ece69300032f45f78f30b4a4482cdc3f4a6"
[[package]]
name = "constant_time_eq"
version = "0.1.5"
@@ -205,16 +190,16 @@ checksum = "dcb25d077389e53838a8158c8e99174c5a9d902dee4904320db714f3c653ffba"
[[package]]
name = "criterion"
version = "0.3.3"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70daa7ceec6cf143990669a04c7df13391d55fb27bd4079d252fca774ba244d8"
checksum = "ab327ed7354547cc2ef43cbe20ef68b988e70b4b593cbd66a2a61733123a3d23"
dependencies = [
"atty",
"cast",
"clap",
"criterion-plot",
"csv",
"itertools",
"itertools 0.10.0",
"lazy_static",
"num-traits",
"oorandom",
@@ -236,57 +221,44 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e022feadec601fba1649cfa83586381a4ad31c6bf3a9ab7d408118b05dd9889d"
dependencies = [
"cast",
"itertools",
"itertools 0.9.0",
]
[[package]]
name = "crossbeam-channel"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dca26ee1f8d361640700bde38b2c37d8c22b3ce2d360e1fc1c74ea4b0aa7d775"
dependencies = [
"cfg-if 1.0.0",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-deque"
version = "0.7.3"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f02af974daeee82218205558e51ec8768b48cf524bd01d550abe5573a608285"
checksum = "94af6efb46fef72616855b036a624cf27ba656ffc9be1b9a3c931cfc7749a9a9"
dependencies = [
"cfg-if 1.0.0",
"crossbeam-epoch",
"crossbeam-utils 0.7.2",
"maybe-uninit",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-epoch"
version = "0.8.2"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace"
checksum = "a1aaa739f95311c2c7887a76863f500026092fb1dce0161dab577e559ef3569d"
dependencies = [
"autocfg",
"cfg-if 0.1.10",
"crossbeam-utils 0.7.2",
"cfg-if 1.0.0",
"const_fn",
"crossbeam-utils",
"lazy_static",
"maybe-uninit",
"memoffset",
"scopeguard",
]
[[package]]
name = "crossbeam-queue"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "774ba60a54c213d409d5353bda12d49cd68d14e45036a285234c8d6f91f92570"
dependencies = [
"cfg-if 0.1.10",
"crossbeam-utils 0.7.2",
"maybe-uninit",
]
[[package]]
name = "crossbeam-utils"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8"
dependencies = [
"autocfg",
"cfg-if 0.1.10",
"lazy_static",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.1"
@@ -310,9 +282,9 @@ dependencies = [
[[package]]
name = "csv"
version = "1.1.3"
version = "1.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00affe7f6ab566df61b4be3ce8cf16bc2576bca0963ceb0955e45d514bf9a279"
checksum = "f9d58633299b24b515ac72a3f869f8b91306a3cec616a602843a383acd6f9e97"
dependencies = [
"bstr",
"csv-core",
@@ -332,9 +304,9 @@ dependencies = [
[[package]]
name = "curve25519-dalek"
version = "3.0.0"
version = "3.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8492de420e9e60bc9a1d66e2dbb91825390b738a388606600663fc529b4b307"
checksum = "f627126b946c25a4638eec0ea634fc52506dea98db118aae985118ce7c3d723f"
dependencies = [
"byteorder",
"digest",
@@ -353,19 +325,20 @@ dependencies = [
]
[[package]]
name = "dirs"
version = "3.0.1"
name = "dirs-next"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "142995ed02755914747cc6ca76fc7e4583cd18578746716d0508ea6ed558b9ff"
checksum = "cf36e65a80337bea855cd4ef9b8401ffce06a7baedf2e85ec467b1ac3f6e82b6"
dependencies = [
"dirs-sys",
"cfg-if 1.0.0",
"dirs-sys-next",
]
[[package]]
name = "dirs-sys"
version = "0.3.5"
name = "dirs-sys-next"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e93d7f5705de3e49895a2b5e0b8855a1c27f080192ae9c32a6432d50741a57a"
checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d"
dependencies = [
"libc",
"redox_users",
@@ -385,9 +358,9 @@ dependencies = [
[[package]]
name = "either"
version = "1.5.3"
version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3"
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
[[package]]
name = "fnv"
@@ -395,16 +368,6 @@ version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "fs2"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213"
dependencies = [
"libc",
"winapi",
]
[[package]]
name = "generic-array"
version = "0.14.4"
@@ -424,24 +387,13 @@ dependencies = [
"generic-array",
]
[[package]]
name = "generic-bytes-derive"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4257fe99e64e321c197164da8ae22f7a3c074a5b2519072866e2d38bc1ca59e"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "getrandom"
version = "0.1.14"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb"
checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
dependencies = [
"cfg-if 0.1.10",
"cfg-if 1.0.0",
"libc",
"wasi 0.9.0+wasi-snapshot-preview1",
]
@@ -459,15 +411,15 @@ dependencies = [
[[package]]
name = "half"
version = "1.6.0"
version = "1.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d36fab90f82edc3c747f9d438e06cf0a491055896f2a279638bb5beed6c40177"
checksum = "62aca2aba2d62b4a7f5b33f3712cb1b0692779a56fb510499d5c0aa594daeaf3"
[[package]]
name = "hermit-abi"
version = "0.1.15"
version = "0.1.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3deed196b6e7f9e44a2ae8d94225d80302d81208b1bb673fd21fe634645c85a9"
checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c"
dependencies = [
"libc",
]
@@ -508,16 +460,25 @@ dependencies = [
]
[[package]]
name = "itoa"
version = "0.4.6"
name = "itertools"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6"
checksum = "37d572918e350e82412fe766d24b15e6682fb2ed2bbe018280caa810397cb319"
dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"
[[package]]
name = "js-sys"
version = "0.3.42"
version = "0.3.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52732a3d3ad72c58ad2dc70624f9c17b46ecd0943b9a4f1ee37c4c18c5d983e2"
checksum = "5cfb73131c35423a367daf8cbd24100af0d077668c8c2943f0e7dd775fef0f65"
dependencies = [
"wasm-bindgen",
]
@@ -530,57 +491,51 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.81"
version = "0.2.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1482821306169ec4d07f6aca392a4681f66c75c9918aa49641a2595db64053cb"
checksum = "b7282d924be3275cec7f6756ff4121987bc6481325397dde6ba3e7802b1a8b1c"
[[package]]
name = "log"
version = "0.4.11"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b"
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
dependencies = [
"cfg-if 0.1.10",
"cfg-if 1.0.0",
]
[[package]]
name = "maybe-uninit"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00"
[[package]]
name = "memchr"
version = "2.3.3"
version = "2.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400"
checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
[[package]]
name = "memoffset"
version = "0.5.5"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c198b026e1bbf08a937e94c6c60f9ec4a2267f5b0d2eec9c1b21b061ce2be55f"
checksum = "157b4208e3059a8f9e78d559edc658e13df41410cb3ae03979c83130067fdd87"
dependencies = [
"autocfg",
]
[[package]]
name = "nix"
version = "0.19.1"
version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2ccba0cfe4fdf15982d1674c69b1fd80bad427d293849982668dfe454bd61f2"
checksum = "83450fe6a6142ddd95fb064b746083fc4ef1705fe81f64a64e1d4b39f54a1055"
dependencies = [
"bitflags",
"cc",
"cfg-if 1.0.0",
"cfg-if 0.1.10",
"libc",
]
[[package]]
name = "num-traits"
version = "0.2.12"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611"
checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
dependencies = [
"autocfg",
]
@@ -597,9 +552,9 @@ dependencies = [
[[package]]
name = "oorandom"
version = "11.1.2"
version = "11.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a170cebd8021a008ea92e4db85a72f80b35df514ec664b296fdcbb654eac0b2c"
checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
[[package]]
name = "opaque-debug"
@@ -609,25 +564,24 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
[[package]]
name = "opaque-ke"
version = "0.3.0"
version = "0.5.1"
dependencies = [
"anyhow",
"base64",
"chacha20poly1305",
"constant_time_eq",
"criterion",
"curve25519-dalek",
"digest",
"displaydoc",
"generic-array",
"generic-bytes",
"generic-bytes-derive",
"hex",
"hkdf",
"hmac",
"lazy_static",
"proptest",
"rand 0.8.3",
"rand_core 0.6.1",
"rustyline",
"scrypt",
"serde_json",
@@ -648,16 +602,32 @@ dependencies = [
[[package]]
name = "plotters"
version = "0.2.15"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d1685fbe7beba33de0330629da9d955ac75bd54f33d7b79f9a895590124f6bb"
checksum = "45ca0ae5f169d0917a7c7f5a9c1a3d3d9598f18f529dd2b8373ed988efea307a"
dependencies = [
"js-sys",
"num-traits",
"plotters-backend",
"plotters-svg",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "plotters-backend"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b07fffcddc1cb3a1de753caa4e4df03b79922ba43cf882acc1bdd7e8df9f4590"
[[package]]
name = "plotters-svg"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b38a02e23bd9604b842a812063aec4ef702b57989c37b655254bb61c471ad211"
dependencies = [
"plotters-backend",
]
[[package]]
name = "poly1305"
version = "0.6.2"
@@ -670,9 +640,9 @@ dependencies = [
[[package]]
name = "ppv-lite86"
version = "0.2.8"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea"
checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
[[package]]
name = "proc-macro2"
@@ -711,9 +681,9 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
[[package]]
name = "quote"
version = "1.0.7"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37"
checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
dependencies = [
"proc-macro2",
]
@@ -724,7 +694,7 @@ version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
dependencies = [
"getrandom 0.1.14",
"getrandom 0.1.16",
"libc",
"rand_chacha 0.2.2",
"rand_core 0.5.1",
@@ -739,7 +709,7 @@ checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e"
dependencies = [
"libc",
"rand_chacha 0.3.0",
"rand_core 0.6.1",
"rand_core 0.6.2",
"rand_hc 0.3.0",
]
@@ -760,7 +730,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d"
dependencies = [
"ppv-lite86",
"rand_core 0.6.1",
"rand_core 0.6.2",
]
[[package]]
@@ -769,14 +739,14 @@ version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
dependencies = [
"getrandom 0.1.14",
"getrandom 0.1.16",
]
[[package]]
name = "rand_core"
version = "0.6.1"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c026d7df8b298d90ccbbc5190bd04d85e159eaf5576caeacf8741da93ccbd2e5"
checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7"
dependencies = [
"getrandom 0.2.2",
]
@@ -796,7 +766,7 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73"
dependencies = [
"rand_core 0.6.1",
"rand_core 0.6.2",
]
[[package]]
@@ -810,9 +780,9 @@ dependencies = [
[[package]]
name = "rayon"
version = "1.3.1"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62f02856753d04e03e26929f820d0a0a337ebe71f849801eea335d464b349080"
checksum = "8b0d8e0819fadc20c74ea8373106ead0600e3a67ef1fe8da56e39b9ae7275674"
dependencies = [
"autocfg",
"crossbeam-deque",
@@ -822,39 +792,41 @@ dependencies = [
[[package]]
name = "rayon-core"
version = "1.7.1"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e92e15d89083484e11353891f1af602cc661426deb9564c298b270c726973280"
checksum = "9ab346ac5921dc62ffa9f89b7a773907511cdfa5490c572ae9be1be33e8afa4a"
dependencies = [
"crossbeam-channel",
"crossbeam-deque",
"crossbeam-queue",
"crossbeam-utils 0.7.2",
"crossbeam-utils",
"lazy_static",
"num_cpus",
]
[[package]]
name = "redox_syscall"
version = "0.1.57"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
checksum = "94341e4e44e24f6b591b59e47a8a027df12e008d73fd5672dbea9cc22f4507d9"
dependencies = [
"bitflags",
]
[[package]]
name = "redox_users"
version = "0.3.5"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de0737333e7a9502c789a36d7c7fa6092a49895d4faa31ca5df163857ded2e9d"
checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64"
dependencies = [
"getrandom 0.1.14",
"getrandom 0.2.2",
"redox_syscall",
"rust-argon2",
]
[[package]]
name = "regex"
version = "1.3.9"
version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c3780fcf44b193bc4d09f36d2a3c87b251da4a046c87795a0d35f4f927ad8e6"
checksum = "d9251239e129e16308e70d853559389de218ac275b515068abc96829d05b948a"
dependencies = [
"regex-syntax",
]
@@ -870,9 +842,9 @@ dependencies = [
[[package]]
name = "regex-syntax"
version = "0.6.18"
version = "0.6.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26412eb97c6b088a6997e05f69403a802a92d520de2f8e63c2b65f9e0f47c4e8"
checksum = "b5eb417147ba9860a96cfe72a0b93bf88fee1744b5636ec99ab20c1aa9376581"
[[package]]
name = "remove_dir_all"
@@ -883,18 +855,6 @@ dependencies = [
"winapi",
]
[[package]]
name = "rust-argon2"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b18820d944b33caa75a71378964ac46f58517c92b6ae5f762636247c09e78fb"
dependencies = [
"base64",
"blake2b_simd",
"constant_time_eq",
"crossbeam-utils 0.8.1",
]
[[package]]
name = "rustc_version"
version = "0.2.3"
@@ -918,14 +878,12 @@ dependencies = [
[[package]]
name = "rustyline"
version = "7.0.0"
version = "6.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a5f54deba50e65ee4cf786dbc37e8b3c63bdccccbcf9d3a8a9fd0c1bb7e1984"
checksum = "6f0d5e7b0219a3eadd5439498525d4765c59b7c993ef0c12244865cd2d988413"
dependencies = [
"bitflags",
"cfg-if 1.0.0",
"dirs",
"fs2",
"cfg-if 0.1.10",
"dirs-next",
"libc",
"log",
"memchr",
@@ -1000,9 +958,9 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
[[package]]
name = "serde"
version = "1.0.114"
version = "1.0.123"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5317f7588f0a5078ee60ef675ef96735a1442132dc645eb1d12c018620ed8cd3"
checksum = "92d5161132722baa40d802cc70b15262b98258453e85e5d1d365c757c73869ae"
[[package]]
name = "serde_cbor"
@@ -1016,9 +974,9 @@ dependencies = [
[[package]]
name = "serde_derive"
version = "1.0.114"
version = "1.0.123"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a0be94b04690fbaed37cddffc5c134bf537c8e3329d53e982fe04c374978f8e"
checksum = "9391c295d64fc0abb2c556bad848f33cb8296276b1ad2677d1ae1ace4f258f31"
dependencies = [
"proc-macro2",
"quote",
@@ -1027,9 +985,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.60"
version = "1.0.62"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1500e84d27fe482ed1dc791a56eddc2f230046a040fa908c08bda1d9fb615779"
checksum = "ea1c6153794552ea7cf7cf63b1231a25de00ec90db326ba6264440fa08e31486"
dependencies = [
"itoa",
"ryu",
@@ -1038,9 +996,9 @@ dependencies = [
[[package]]
name = "sha2"
version = "0.9.2"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e7aab86fe2149bad8c507606bdb3f4ef5e7b2380eb92350f56122cca72a42a8"
checksum = "fa827a14b29ab7f44778d14a88d3cb76e949c45083f7dbfa507d0cb699dc12de"
dependencies = [
"block-buffer",
"cfg-if 1.0.0",
@@ -1051,15 +1009,15 @@ dependencies = [
[[package]]
name = "subtle"
version = "2.3.0"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "343f3f510c2915908f155e94f17220b19ccfacf2a64a2a5d8004f2c3e311e7fd"
checksum = "1e81da0851ada1f3e9d4312c704aa4f8806f0f9d69faaf8df2f3464b4a9437c2"
[[package]]
name = "syn"
version = "1.0.54"
version = "1.0.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a2af957a63d6bd42255c359c93d9bfdb97076bd3b820897ce55ffbfbf107f44"
checksum = "c700597eca8a5a762beb35753ef6b94df201c81cca676604f547495a0d7f0081"
dependencies = [
"proc-macro2",
"quote",
@@ -1067,14 +1025,26 @@ dependencies = [
]
[[package]]
name = "tempfile"
version = "3.1.0"
name = "synstructure"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9"
checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701"
dependencies = [
"cfg-if 0.1.10",
"proc-macro2",
"quote",
"syn",
"unicode-xid",
]
[[package]]
name = "tempfile"
version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22"
dependencies = [
"cfg-if 1.0.0",
"libc",
"rand 0.7.3",
"rand 0.8.3",
"redox_syscall",
"remove_dir_all",
"winapi",
@@ -1091,18 +1061,18 @@ dependencies = [
[[package]]
name = "thiserror"
version = "1.0.22"
version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e9ae34b84616eedaaf1e9dd6026dbe00dcafa92aa0c8077cb69df1fcfe5e53e"
checksum = "76cc616c6abf8c8928e2fdcc0dbfab37175edd8fb49a4641066ad1364fdab146"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.22"
version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ba20f23e85b10754cd195504aebf6a27e2e6cbe28c17778a0c930724628dd56"
checksum = "9be73a2caec27583d0046ef3796c3794f868a5bc813db689eed00c7631275cd1"
dependencies = [
"proc-macro2",
"quote",
@@ -1111,9 +1081,9 @@ dependencies = [
[[package]]
name = "tinytemplate"
version = "1.1.0"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d3dc76004a03cec1c5932bca4cdc2e39aaa798e3f82363dd94f9adf6098c12f"
checksum = "a2ada8616fad06a2d0c455adc530de4ef57605a8120cc65da9653e0e9623ca74"
dependencies = [
"serde",
"serde_json",
@@ -1127,9 +1097,9 @@ checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33"
[[package]]
name = "unicode-segmentation"
version = "1.6.0"
version = "1.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0"
checksum = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796"
[[package]]
name = "unicode-width"
@@ -1199,19 +1169,19 @@ checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
[[package]]
name = "wasm-bindgen"
version = "0.2.65"
version = "0.2.70"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3edbcc9536ab7eababcc6d2374a0b7bfe13a2b6d562c5e07f370456b1a8f33d"
checksum = "55c0f7123de74f0dab9b7d00fd614e7b19349cd1e2f5252bbe9b1754b59433be"
dependencies = [
"cfg-if 0.1.10",
"cfg-if 1.0.0",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.65"
version = "0.2.70"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89ed2fb8c84bfad20ea66b26a3743f3e7ba8735a69fe7d95118c33ec8fc1244d"
checksum = "7bc45447f0d4573f3d65720f636bbcc3dd6ce920ed704670118650bcd47764c7"
dependencies = [
"bumpalo",
"lazy_static",
@@ -1224,9 +1194,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.65"
version = "0.2.70"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb071268b031a64d92fc6cf691715ca5a40950694d8f683c5bb43db7c730929e"
checksum = "3b8853882eef39593ad4174dd26fc9865a64e84026d223f63bb2c42affcbba2c"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@@ -1234,9 +1204,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.65"
version = "0.2.70"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf592c807080719d1ff2f245a687cbadb3ed28b2077ed7084b47aba8b691f2c6"
checksum = "4133b5e7f2a531fa413b3a1695e925038a05a71cf67e87dafa295cb645a01385"
dependencies = [
"proc-macro2",
"quote",
@@ -1247,15 +1217,15 @@ dependencies = [
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.65"
version = "0.2.70"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b6c0220ded549d63860c78c38f3bcc558d1ca3f4efa74942c536ddbbb55e87"
checksum = "dd4945e4943ae02d15c13962b38a5b1e81eadd4b71214eee75af64a4d6a4fd64"
[[package]]
name = "web-sys"
version = "0.3.42"
version = "0.3.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8be2398f326b7ba09815d0b403095f34dd708579220d099caae89be0b32137b2"
checksum = "c40dc691fc48003eba817c38da7113c15698142da971298003cac3ef175680b3"
dependencies = [
"js-sys",
"wasm-bindgen",
@@ -1294,6 +1264,21 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "zeroize"
version = "1.1.1"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05f33972566adbd2d3588b0491eb94b98b43695c4ef897903470ede4f3f5a28a"
checksum = "81a974bcdd357f0dca4d41677db03436324d45a4c9ed2d0b873a5a360ce41c36"
dependencies = [
"zeroize_derive",
]
[[package]]
name = "zeroize_derive"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2c1e130bebaeab2f23886bf9acbaca14b092408c452543c857f66399cd6dab1"
dependencies = [
"proc-macro2",
"quote",
"syn",
"synstructure",
]
+5 -6
View File
@@ -1,6 +1,6 @@
[package]
name = "opaque-ke"
version = "0.3.0"
version = "0.5.1"
repository = "https://github.com/novifinancial/opaque-ke"
keywords = ["cryptography", "crypto", "opaque", "passwords", "authentication"]
description = "An implementation of the OPAQUE password-authenticated key exchange protocol"
@@ -17,19 +17,19 @@ u64_backend = ["curve25519-dalek/u64_backend"]
u32_backend = ["curve25519-dalek/u32_backend"]
[dependencies]
constant_time_eq = "0.1.5"
curve25519-dalek = { version = "3.0.0", default-features = false, features = ["std"] }
digest = "0.9.0"
displaydoc = "0.1.7"
generic-array = "0.14.4"
generic-bytes = { version = "0.1.0" }
generic-bytes-derive = { version = "0.1.0" }
hkdf = "0.10.0"
hmac = "0.10.1"
rand_core = { version = "0.6.0", features = ["getrandom"] }
rand = "0.8"
scrypt = { version = "0.5.0", optional = true }
subtle = { version = "2.3.0", default-features = false }
thiserror = "1.0.22"
zeroize = "1.1.1"
zeroize = { version = "1.1.1", features = ["zeroize_derive"] }
[dev-dependencies]
anyhow = "1.0.35"
@@ -41,8 +41,7 @@ lazy_static = "1.4.0"
serde_json = "1.0.60"
sha2 = "0.9.2"
proptest = "0.10.1"
rand = "0.8"
rustyline = "7.0.0"
rustyline = "6.3.0"
[[bench]]
name = "oprf"
+2 -2
View File
@@ -22,14 +22,14 @@ Installation
Add the following line to the dependencies of your `Cargo.toml`:
```
opaque-ke = "0.3.0"
opaque-ke = "0.5.1"
```
Resources
---------
- [OPAQUE academic publication](https://eprint.iacr.org/2018/163.pdf), including formal definitions and a proof of security
- [draft-irtf-cfrg-opaque-02](https://www.ietf.org/archive/id/draft-irtf-cfrg-opaque-02.html), containing a detailed (byte-level) specification for OPAQUE
- [draft-irtf-cfrg-opaque-03](https://www.ietf.org/archive/id/draft-irtf-cfrg-opaque-03.html), containing a detailed (byte-level) specification for OPAQUE
- ["Let's talk about PAKE"](https://blog.cryptographyengineering.com/2018/10/19/lets-talk-about-pake/), an introductory blog post written by Matthew Green that covers OPAQUE
Contributors
+3 -3
View File
@@ -11,7 +11,7 @@ use curve25519_dalek::ristretto::RistrettoPoint;
use generic_array::arr;
use opaque_ke::{
group::Group,
oprf::{blind_shim, evaluate_shim, unblind_and_finalize_shim},
oprf::{blind_shim, evaluate_shim, finalize_shim},
};
use rand::{prelude::ThreadRng, thread_rng};
use sha2::Sha512;
@@ -57,9 +57,9 @@ fn oprf3(c: &mut Criterion) {
let salt = RistrettoPoint::from_scalar_slice(&salt_bytes).unwrap();
let beta = evaluate_shim::<RistrettoPoint>(alpha, &salt);
c.bench_function("unblind_and_finalize with Ristretto", move |b| {
c.bench_function("finalize with Ristretto", move |b| {
b.iter(|| {
let _res = unblind_and_finalize_shim::<RistrettoPoint, Sha512>(&token, beta).unwrap();
let _res = finalize_shim::<RistrettoPoint, Sha512>(&token, beta).unwrap();
})
});
}
+2 -2
View File
@@ -34,7 +34,7 @@ targets = [
# The path where the advisory database is cloned/fetched into
db-path = "~/.cargo/advisory-db"
# The url of the advisory database to use
db-url = "https://github.com/rustsec/advisory-db"
db-urls = ["https://github.com/rustsec/advisory-db"]
# The lint level for security vulnerabilities
vulnerability = "deny"
# The lint level for unmaintained crates
@@ -44,7 +44,7 @@ yanked = "warn"
# The lint level for crates with security notices. Note that as of
# 2019-12-17 there are no security notice advisories in
# https://github.com/rustsec/advisory-db
notice = "warn"
notice = "deny"
# A list of advisory IDs to ignore. Note that ignored advisories will still
# output a note when they are encountered.
ignore = [
+12 -11
View File
@@ -26,17 +26,17 @@
use chacha20poly1305::aead::{Aead, NewAead};
use chacha20poly1305::{ChaCha20Poly1305, Key, Nonce};
use rand_core::{OsRng, RngCore};
use rustyline::error::ReadlineError;
use rustyline::Editor;
use std::convert::TryFrom;
use std::process::exit;
use opaque_ke::{
ciphersuite::CipherSuite, ClientLogin, ClientLoginFinishParameters, ClientLoginStartParameters,
ClientRegistration, ClientRegistrationFinishParameters, CredentialFinalization,
CredentialRequest, CredentialResponse, RegistrationRequest, RegistrationResponse,
RegistrationUpload, ServerLogin, ServerLoginStartParameters, ServerRegistration,
ciphersuite::CipherSuite,
rand::{rngs::OsRng, RngCore},
ClientLogin, ClientLoginFinishParameters, ClientLoginStartParameters, ClientRegistration,
ClientRegistrationFinishParameters, CredentialFinalization, CredentialRequest,
CredentialResponse, RegistrationRequest, RegistrationResponse, RegistrationUpload, ServerLogin,
ServerLoginStartParameters, ServerRegistration,
};
// The ciphersuite trait allows to specify the underlying primitives
@@ -128,7 +128,7 @@ fn register_locker(
Locker {
contents: ciphertext,
password_file: password_file.to_bytes(),
password_file: password_file.serialize(),
}
}
@@ -145,11 +145,12 @@ fn open_locker(
ClientLoginStartParameters::default(),
)
.unwrap();
let credential_request_bytes = client_login_start_result.message.serialize();
let credential_request_bytes = client_login_start_result.message.serialize().unwrap();
// Client sends credential_request_bytes to server
let password_file = ServerRegistration::<Default>::try_from(&locker.password_file[..]).unwrap();
let password_file =
ServerRegistration::<Default>::deserialize(&locker.password_file[..]).unwrap();
let mut server_rng = OsRng;
let server_login_start_result = ServerLogin::start(
&mut server_rng,
@@ -159,7 +160,7 @@ fn open_locker(
ServerLoginStartParameters::default(),
)
.unwrap();
let credential_response_bytes = server_login_start_result.message.serialize();
let credential_response_bytes = server_login_start_result.message.serialize().unwrap();
// Server sends credential_response_bytes to client
@@ -173,7 +174,7 @@ fn open_locker(
return Err(String::from("Incorrect password, please try again."));
}
let client_login_finish_result = result.unwrap();
let credential_finalization_bytes = client_login_finish_result.message.serialize();
let credential_finalization_bytes = client_login_finish_result.message.serialize().unwrap();
// Client sends credential_finalization_bytes to server
+10 -11
View File
@@ -20,18 +20,17 @@
//! messages over "the wire" to the server. These bytes are serialized
//! and explicitly annotated in the below functions.
use rand_core::OsRng;
use rustyline::error::ReadlineError;
use rustyline::Editor;
use std::collections::HashMap;
use std::convert::TryFrom;
use std::process::exit;
use opaque_ke::{
ciphersuite::CipherSuite, ClientLogin, ClientLoginFinishParameters, ClientLoginStartParameters,
ClientRegistration, ClientRegistrationFinishParameters, CredentialFinalization,
CredentialRequest, CredentialResponse, RegistrationRequest, RegistrationResponse,
RegistrationUpload, ServerLogin, ServerLoginStartParameters, ServerRegistration,
ciphersuite::CipherSuite, rand::rngs::OsRng, ClientLogin, ClientLoginFinishParameters,
ClientLoginStartParameters, ClientRegistration, ClientRegistrationFinishParameters,
CredentialFinalization, CredentialRequest, CredentialResponse, RegistrationRequest,
RegistrationResponse, RegistrationUpload, ServerLogin, ServerLoginStartParameters,
ServerRegistration,
};
// The ciphersuite trait allows to specify the underlying primitives
@@ -84,7 +83,7 @@ fn account_registration(
.state
.finish(RegistrationUpload::deserialize(&message_bytes[..]).unwrap())
.unwrap();
password_file.to_bytes()
password_file.serialize()
}
// Password-based login between a client and server
@@ -100,11 +99,11 @@ fn account_login(
ClientLoginStartParameters::default(),
)
.unwrap();
let credential_request_bytes = client_login_start_result.message.serialize();
let credential_request_bytes = client_login_start_result.message.serialize().unwrap();
// Client sends credential_request_bytes to server
let password_file = ServerRegistration::<Default>::try_from(password_file_bytes).unwrap();
let password_file = ServerRegistration::<Default>::deserialize(password_file_bytes).unwrap();
let mut server_rng = OsRng;
let server_login_start_result = ServerLogin::start(
&mut server_rng,
@@ -114,7 +113,7 @@ fn account_login(
ServerLoginStartParameters::default(),
)
.unwrap();
let credential_response_bytes = server_login_start_result.message.serialize();
let credential_response_bytes = server_login_start_result.message.serialize().unwrap();
// Server sends credential_response_bytes to client
@@ -128,7 +127,7 @@ fn account_login(
return false;
}
let client_login_finish_result = result.unwrap();
let credential_finalization_bytes = client_login_finish_result.message.serialize();
let credential_finalization_bytes = client_login_finish_result.message.serialize().unwrap();
// Client sends credential_finalization_bytes to server
+1 -1
View File
@@ -11,7 +11,7 @@ use crate::{
};
use digest::Digest;
use rand_core::{CryptoRng, RngCore};
use rand::{CryptoRng, RngCore};
/// Configures the underlying primitives used in OPAQUE
/// * `Group`: a finite cyclic group along with a point representation, along
+70 -42
View File
@@ -6,14 +6,17 @@
use crate::{
errors::{utils::check_slice_size_atleast, InternalPakeError, PakeError, ProtocolError},
hash::Hash,
serialization::{serialize, tokenize},
keypair::Key,
serialization::serialize,
};
use digest::Digest;
use generic_array::{typenum::Unsigned, GenericArray};
use generic_bytes::SizedBytes;
use hkdf::Hkdf;
use hmac::{Hmac, Mac, NewMac};
use rand_core::{CryptoRng, RngCore};
use rand::{CryptoRng, RngCore};
use std::convert::TryFrom;
use zeroize::Zeroize;
// Constant string used as salt for HKDF computation
const STR_PAD: &[u8] = b"Pad";
@@ -22,7 +25,8 @@ const STR_EXPORT_KEY: &[u8] = b"ExportKey";
const NONCE_LEN: usize = 32;
#[derive(Clone, Copy, PartialEq)]
#[derive(Clone, Copy, PartialEq, Zeroize)]
#[zeroize(drop)]
pub(crate) enum InnerEnvelopeMode {
Base = 1,
CustomIdentifier = 2,
@@ -39,6 +43,8 @@ impl TryFrom<u8> for InnerEnvelopeMode {
}
}
#[derive(Clone, Zeroize)]
#[zeroize(drop)]
pub(crate) struct InnerEnvelope {
mode: InnerEnvelopeMode,
nonce: Vec<u8>,
@@ -47,12 +53,7 @@ pub(crate) struct InnerEnvelope {
impl InnerEnvelope {
pub(crate) fn serialize(&self) -> Vec<u8> {
[
&[self.mode as u8],
&self.nonce[..],
&serialize(&self.ciphertext, 2)[..],
]
.concat()
[&[self.mode as u8], &self.nonce[..], &self.ciphertext[..]].concat()
}
pub(crate) fn deserialize(input: &[u8]) -> Result<(Self, Vec<u8>), ProtocolError> {
@@ -63,25 +64,33 @@ impl InnerEnvelope {
}
let mode = InnerEnvelopeMode::try_from(input[0])?;
let key_len = <Key as SizedBytes>::Len::to_usize();
let bytes = &input[1..];
if bytes.len() < NONCE_LEN {
if bytes.len() < NONCE_LEN + key_len {
return Err(ProtocolError::VerificationError(
PakeError::SerializationError,
));
}
let nonce = &bytes[..NONCE_LEN];
let (ciphertext, remainder) = tokenize(&bytes[NONCE_LEN..], 2)?;
Ok((
Self {
mode,
nonce: nonce.to_vec(),
ciphertext,
nonce: bytes[..NONCE_LEN].to_vec(),
ciphertext: bytes[NONCE_LEN..NONCE_LEN + key_len].to_vec(),
},
remainder,
bytes[NONCE_LEN + key_len..].to_vec(),
))
}
#[cfg(test)]
pub fn as_byte_ptrs(&self) -> Vec<(*const u8, usize)> {
vec![
/* Cannot easily get raw pointer of enum value, otherwise would do self.mode.as_ptr() */
(self.nonce.as_ptr(), self.nonce.len()),
(self.ciphertext.as_ptr(), self.ciphertext.len()),
]
}
}
/// This struct is an instantiation of the envelope as described in
@@ -94,6 +103,7 @@ impl InnerEnvelope {
/// 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.
#[derive(Clone)]
pub(crate) struct Envelope<D: Hash> {
inner_envelope: InnerEnvelope,
hmac: GenericArray<u8, <D as Digest>::OutputSize>,
@@ -113,11 +123,6 @@ pub(crate) struct OpenedInnerEnvelope<D: Hash> {
}
impl<D: Hash> Envelope<D> {
/// The additional number of bytes added to the plaintext
pub(crate) fn additional_size() -> usize {
NONCE_LEN + <D as Digest>::OutputSize::to_usize()
}
fn hmac_key_size() -> usize {
<D as Digest>::OutputSize::to_usize()
}
@@ -172,10 +177,9 @@ impl<D: Hash> Envelope<D> {
client_s_sk: &[u8],
server_s_pk: &[u8],
optional_ids: Option<(Vec<u8>, Vec<u8>)>,
) -> Result<(Self, GenericArray<u8, <D as Digest>::OutputSize>), InternalPakeError> {
let plaintext = serialize(&client_s_sk, 2);
let aad = construct_aad(server_s_pk, &optional_ids);
Self::seal_raw(rng, key, &plaintext, &aad, mode_from_ids(&optional_ids))
) -> Result<(Self, GenericArray<u8, <D as Digest>::OutputSize>), ProtocolError> {
let aad = construct_aad(server_s_pk, &optional_ids)?;
Self::seal_raw(rng, key, &client_s_sk, &aad, mode_from_ids(&optional_ids))
}
/// Uses a key to convert the plaintext into an envelope, authenticated by the aad field.
@@ -186,7 +190,7 @@ impl<D: Hash> Envelope<D> {
plaintext: &[u8],
aad: &[u8],
mode: InnerEnvelopeMode,
) -> Result<(Self, GenericArray<u8, <D as Digest>::OutputSize>), InternalPakeError> {
) -> Result<(Self, GenericArray<u8, <D as Digest>::OutputSize>), ProtocolError> {
let mut nonce = vec![0u8; NONCE_LEN];
rng.fill_bytes(&mut nonce);
@@ -235,24 +239,22 @@ impl<D: Hash> Envelope<D> {
key: &[u8],
server_s_pk: &[u8],
optional_ids: &Option<(Vec<u8>, Vec<u8>)>,
) -> Result<OpenedEnvelope<D>, InternalPakeError> {
) -> Result<OpenedEnvelope<D>, ProtocolError> {
// First, check that mode matches
if self.inner_envelope.mode != mode_from_ids(optional_ids) {
return Err(InternalPakeError::IncompatibleEnvelopeModeError);
return Err(InternalPakeError::IncompatibleEnvelopeModeError.into());
}
let aad = construct_aad(server_s_pk, optional_ids);
let aad = construct_aad(server_s_pk, optional_ids)?;
let opened = self.open_raw(key, &aad)?;
let (client_s_sk, remainder) = tokenize(&opened.plaintext, 2)
.map_err(|_| InternalPakeError::UnexpectedEnvelopeContentsError)?;
if !remainder.is_empty() {
// Should not have anything else in plaintext
return Err(InternalPakeError::UnexpectedEnvelopeContentsError);
if opened.plaintext.len() != <Key as SizedBytes>::Len::to_usize() {
// Plaintext should consist of a single key
return Err(InternalPakeError::UnexpectedEnvelopeContentsError.into());
}
Ok(OpenedEnvelope {
client_s_sk,
client_s_sk: opened.plaintext,
export_key: opened.export_key,
})
}
@@ -296,16 +298,42 @@ impl<D: Hash> Envelope<D> {
),
})
}
#[cfg(test)]
pub fn as_byte_ptrs(&self) -> Vec<(*const u8, usize)> {
[
self.inner_envelope.as_byte_ptrs(),
vec![(self.hmac.as_ptr(), self.hmac.len())],
]
.concat()
}
}
// This can't be derived because of the use of a phantom parameter
impl<D: Hash> Zeroize for Envelope<D> {
fn zeroize(&mut self) {
self.inner_envelope.zeroize();
self.hmac.zeroize();
}
}
impl<D: Hash> Drop for Envelope<D> {
fn drop(&mut self) {
self.zeroize();
}
}
// Helper functions
fn construct_aad(server_s_pk: &[u8], optional_ids: &Option<(Vec<u8>, Vec<u8>)>) -> Vec<u8> {
let ids = optional_ids
.iter()
.flat_map(|(l, r)| [serialize(l, 2), serialize(r, 2)].concat())
.collect();
[serialize(server_s_pk, 2), ids].concat()
fn construct_aad(
server_s_pk: &[u8],
optional_ids: &Option<(Vec<u8>, Vec<u8>)>,
) -> Result<Vec<u8>, ProtocolError> {
let ids = match optional_ids {
Some((l, r)) => [serialize(l, 2)?, serialize(r, 2)?].concat(),
None => vec![],
};
Ok([server_s_pk.to_vec(), ids].concat())
}
pub(crate) fn mode_from_ids(optional_ids: &Option<(Vec<u8>, Vec<u8>)>) -> InnerEnvelopeMode {
@@ -318,7 +346,7 @@ pub(crate) fn mode_from_ids(optional_ids: &Option<(Vec<u8>, Vec<u8>)>) -> InnerE
#[cfg(test)]
mod tests {
use super::*;
use rand_core::OsRng;
use rand::rngs::OsRng;
#[test]
fn seal_and_open() {
+5
View File
@@ -73,6 +73,8 @@ pub enum PakeError {
InvalidLoginError,
/// Error with serializing / deserializing protocol messages
SerializationError,
/// Identity group element was encountered during deserialization, which is invalid
IdentityGroupElementError,
}
// This is meant to express future(ly) non-trivial ways of converting the
@@ -99,6 +101,9 @@ pub enum ProtocolError {
/// This error occurs when the client request cannot be handled
/// Client request cannot be handled.
ClientError,
/// This error occurs when the client detects that the server has
/// reflected the OPRF value (beta == alpha)
ReflectedValueError,
}
// This is meant to express future(ly) non-trivial ways of converting the
+40 -16
View File
@@ -12,6 +12,7 @@ use curve25519_dalek::{
constants::RISTRETTO_BASEPOINT_POINT,
ristretto::{CompressedRistretto, RistrettoPoint},
scalar::Scalar,
traits::Identity,
};
use generic_array::{
typenum::{U32, U64},
@@ -19,13 +20,13 @@ use generic_array::{
};
use std::convert::TryInto;
use rand_core::{CryptoRng, RngCore};
use rand::{CryptoRng, RngCore};
use std::ops::Mul;
use zeroize::Zeroize;
/// A prime-order subgroup of a base field (EC, prime-order field ...). This
/// subgroup is noted additively — as in the draft RFC — in this trait.
pub trait Group: Sized + for<'a> Mul<&'a <Self as Group>::Scalar, Output = Self> {
pub trait Group: Copy + Sized + for<'a> Mul<&'a <Self as Group>::Scalar, Output = Self> {
/// The type of base field scalars
type Scalar: Zeroize + Clone;
/// The byte length necessary to represent scalars
@@ -35,7 +36,7 @@ pub trait Group: Sized + for<'a> Mul<&'a <Self as Group>::Scalar, Output = Self>
scalar_bits: &GenericArray<u8, Self::ScalarLen>,
) -> Result<Self::Scalar, InternalPakeError>;
/// picks a scalar at random
fn random_scalar<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Scalar;
fn random_nonzero_scalar<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Scalar;
/// Serializes a scalar to bytes
fn scalar_as_bytes(scalar: &Self::Scalar) -> &GenericArray<u8, Self::ScalarLen>;
/// The multiplicative inverse of this scalar
@@ -64,6 +65,12 @@ pub trait Group: Sized + for<'a> Mul<&'a <Self as Group>::Scalar, Output = Self>
/// Multiply the point by a scalar, represented as a slice
fn mult_by_slice(&self, scalar: &GenericArray<u8, Self::ScalarLen>) -> Self;
/// Returns if the group element is equal to the identity (1)
fn is_identity(&self) -> bool;
/// Compares in constant time if the group elements are equal
fn ct_equal(&self, other: &Self) -> bool;
}
/// The implementation of such a subgroup for Ristretto
@@ -77,20 +84,28 @@ impl Group for RistrettoPoint {
bits.copy_from_slice(scalar_bits);
Ok(Scalar::from_bytes_mod_order(bits))
}
fn random_scalar<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Scalar {
#[cfg(not(test))]
{
let mut scalar_bytes = [0u8; 64];
rng.fill_bytes(&mut scalar_bytes);
Scalar::from_bytes_mod_order_wide(&scalar_bytes)
}
fn random_nonzero_scalar<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Scalar {
loop {
let scalar = {
#[cfg(not(test))]
{
let mut scalar_bytes = [0u8; 64];
rng.fill_bytes(&mut scalar_bytes);
Scalar::from_bytes_mod_order_wide(&scalar_bytes)
}
// Tests need an exact conversion from bytes to scalar, sampling only 32 bytes from rng
#[cfg(test)]
{
let mut scalar_bytes = [0u8; 32];
rng.fill_bytes(&mut scalar_bytes);
Scalar::from_bytes_mod_order(scalar_bytes)
// Tests need an exact conversion from bytes to scalar, sampling only 32 bytes from rng
#[cfg(test)]
{
let mut scalar_bytes = [0u8; 32];
rng.fill_bytes(&mut scalar_bytes);
Scalar::from_bytes_mod_order(scalar_bytes)
}
};
if scalar != Scalar::zero() {
break scalar;
}
}
}
fn scalar_as_bytes(scalar: &Self::Scalar) -> &GenericArray<u8, Self::ScalarLen> {
@@ -134,4 +149,13 @@ impl Group for RistrettoPoint {
let arr: [u8; 32] = scalar.as_slice().try_into().expect("Wrong length");
self * Scalar::from_bits(arr)
}
/// Returns if the group element is equal to the identity (1)
fn is_identity(&self) -> bool {
self == &Self::identity()
}
fn ct_equal(&self, other: &Self) -> bool {
constant_time_eq::constant_time_eq(&self.to_arr(), &other.to_arr())
}
}
+14 -5
View File
@@ -9,15 +9,16 @@ use crate::{
hash::Hash,
keypair::Key,
};
use rand_core::{CryptoRng, RngCore};
use rand::{CryptoRng, RngCore};
use std::convert::TryFrom;
use zeroize::Zeroize;
pub trait KeyExchange<D: Hash, G: Group> {
type KE1State: for<'r> TryFrom<&'r [u8], Error = PakeError> + ToBytes;
type KE2State: for<'r> TryFrom<&'r [u8], Error = PakeError> + ToBytes;
type KE1State: for<'r> TryFrom<&'r [u8], Error = PakeError> + ToBytesWithPointers + Zeroize;
type KE2State: for<'r> TryFrom<&'r [u8], Error = PakeError> + ToBytesWithPointers + Zeroize;
type KE1Message: for<'r> TryFrom<&'r [u8], Error = PakeError> + ToBytes;
type KE2Message: for<'r> TryFrom<&'r [u8], Error = PakeError> + ToBytes;
type KE2Message: for<'r> TryFrom<&'r [u8], Error = PakeError> + ToBytes + Clone;
type KE3Message: for<'r> TryFrom<&'r [u8], Error = PakeError> + ToBytes;
fn generate_ke1<R: RngCore + CryptoRng>(
@@ -60,5 +61,13 @@ pub trait KeyExchange<D: Hash, G: Group> {
}
pub trait ToBytes {
fn to_bytes(&self) -> Vec<u8>;
fn to_bytes(&self) -> Result<Vec<u8>, ProtocolError>;
}
pub trait ToBytesWithPointers {
fn to_bytes(&self) -> Result<Vec<u8>, ProtocolError>;
// Only used for tests to grab raw pointers to data
#[cfg(test)]
fn as_byte_ptrs(&self) -> Vec<(*const u8, usize)>;
}
+159 -136
View File
@@ -11,7 +11,7 @@ use crate::{
},
group::Group,
hash::Hash,
key_exchange::traits::{KeyExchange, ToBytes},
key_exchange::traits::{KeyExchange, ToBytes, ToBytesWithPointers},
keypair::{Key, KeyPair, SizedBytesExt},
serialization::{serialize, tokenize},
};
@@ -23,51 +23,48 @@ use generic_array::{
use generic_bytes::SizedBytes;
use hkdf::Hkdf;
use hmac::{Hmac, Mac, NewMac};
use rand_core::{CryptoRng, RngCore};
use rand::{CryptoRng, RngCore};
use std::convert::TryFrom;
use zeroize::Zeroize;
const KEY_LEN: usize = 32;
pub(crate) type NonceLen = U32;
static STR_3DH: &[u8] = b"3DH keys";
static STR_3DH: &[u8] = b"3DH";
static STR_CLIENT_MAC: &[u8] = b"client mac";
static STR_HANDSHAKE_SECRET: &[u8] = b"handshake secret";
static STR_SERVER_MAC: &[u8] = b"server mac";
static STR_SERVER_ENC: &[u8] = b"handshake enc";
static STR_HANDSHAKE_ENC: &[u8] = b"handshake enc";
static STR_ENCRYPTION_PAD: &[u8] = b"encryption pad";
static STR_SESSION_KEY: &[u8] = b"session secret";
static STR_SESSION_SECRET: &[u8] = b"session secret";
static STR_OPAQUE: &[u8] = b"OPAQUE ";
#[allow(clippy::upper_case_acronyms)]
/// The Triple Diffie-Hellman key exchange implementation
pub struct TripleDH;
impl<D: Hash, G: Group> KeyExchange<D, G> for TripleDH {
type KE1State = KE1State;
type KE2State = KE2State<<D as FixedOutput>::OutputSize>;
type KE1Message = KE1Message;
type KE2Message = KE2Message<<D as FixedOutput>::OutputSize>;
type KE3Message = KE3Message<<D as FixedOutput>::OutputSize>;
type KE1State = Ke1State;
type KE2State = Ke2State<<D as FixedOutput>::OutputSize>;
type KE1Message = Ke1Message;
type KE2Message = Ke2Message<<D as FixedOutput>::OutputSize>;
type KE3Message = Ke3Message<<D as FixedOutput>::OutputSize>;
fn generate_ke1<R: RngCore + CryptoRng>(
info: Vec<u8>,
rng: &mut R,
) -> Result<(Self::KE1State, Self::KE1Message), ProtocolError> {
let client_e_kp = KeyPair::<G>::generate_random(rng);
let client_nonce: GenericArray<u8, NonceLen> = {
let mut client_nonce_bytes = vec![0u8; NonceLen::to_usize()];
rng.fill_bytes(&mut client_nonce_bytes);
GenericArray::clone_from_slice(&client_nonce_bytes)
};
let client_nonce = generate_nonce::<R>(rng);
let ke1_message = KE1Message {
let ke1_message = Ke1Message {
client_nonce,
info,
client_e_pk: client_e_kp.public().clone(),
};
Ok((
KE1State {
Ke1State {
client_e_sk: client_e_kp.private().clone(),
client_nonce,
},
@@ -88,11 +85,16 @@ impl<D: Hash, G: Group> KeyExchange<D, G> for TripleDH {
e_info: Vec<u8>,
) -> Result<(Vec<u8>, Self::KE2State, Self::KE2Message), ProtocolError> {
let server_e_kp = KeyPair::<G>::generate_random(rng);
let server_nonce: GenericArray<u8, NonceLen> = {
let mut server_nonce_bytes = vec![0u8; NonceLen::to_usize()];
rng.fill_bytes(&mut server_nonce_bytes);
GenericArray::clone_from_slice(&server_nonce_bytes)
};
let server_nonce = generate_nonce::<R>(rng);
let mut transcript_hasher = D::new()
.chain(STR_3DH)
.chain(&serialize(&id_u, 2)?)
.chain(&serialized_credential_request[..])
.chain(&serialize(&id_s, 2)?)
.chain(&l2_bytes[..])
.chain(&server_nonce[..])
.chain(&server_e_kp.public().to_arr());
let (session_key, km2, ke2, km3) = derive_3dh_keys::<D, G>(
TripleDHComponents {
@@ -103,10 +105,7 @@ impl<D: Hash, G: Group> KeyExchange<D, G> for TripleDH {
pk3: client_s_pk,
sk3: server_e_kp.private().clone(),
},
&ke1_message.client_nonce,
&server_nonce,
&id_u,
&id_s,
&transcript_hasher.clone().finalize(),
)?;
// Compute encryption of e_info
@@ -120,36 +119,23 @@ impl<D: Hash, G: Group> KeyExchange<D, G> for TripleDH {
.map(|(&x1, &x2)| x1 ^ x2)
.collect();
let transcript2: Vec<u8> = [
&serialized_credential_request[..],
&l2_bytes[..],
&server_nonce[..],
&server_e_kp.public().to_arr(),
&serialize(&ciphertext, 2),
]
.concat();
let mut hasher = D::new();
hasher.update(&transcript2);
let hashed_transcript_without_mac = hasher.finalize_reset();
transcript_hasher.update(&serialize(&ciphertext, 2)?);
let mut mac_hasher =
Hmac::<D>::new_varkey(&km2).map_err(|_| InternalPakeError::HmacError)?;
mac_hasher.update(&hashed_transcript_without_mac);
mac_hasher.update(&transcript_hasher.clone().finalize());
let mac = mac_hasher.finalize().into_bytes();
hasher.update(&transcript2);
hasher.update(&mac);
let hashed_transcript = hasher.finalize();
transcript_hasher.update(&mac);
Ok((
ke1_message.info,
KE2State {
Ke2State {
km3,
hashed_transcript,
hashed_transcript: transcript_hasher.finalize(),
session_key,
},
KE2Message {
Ke2Message {
server_nonce,
server_e_pk: server_e_kp.public().clone(),
e_info: ciphertext,
@@ -169,6 +155,14 @@ impl<D: Hash, G: Group> KeyExchange<D, G> for TripleDH {
id_u: Vec<u8>,
id_s: Vec<u8>,
) -> Result<(Vec<u8>, Vec<u8>, Self::KE3Message), ProtocolError> {
let mut transcript_hasher = D::new()
.chain(STR_3DH)
.chain(&serialize(&id_u, 2)?)
.chain(&serialized_credential_request)
.chain(&serialize(&id_s, 2)?)
.chain(&l2_component[..])
.chain(&ke2_message.to_bytes_without_info_or_mac());
let (session_key, km2, ke2, km3) = derive_3dh_keys::<D, G>(
TripleDHComponents {
pk1: ke2_message.server_e_pk.clone(),
@@ -178,40 +172,26 @@ impl<D: Hash, G: Group> KeyExchange<D, G> for TripleDH {
pk3: ke2_message.server_e_pk.clone(),
sk3: client_s_sk,
},
&ke1_state.client_nonce,
&ke2_message.server_nonce,
&id_u,
&id_s,
&transcript_hasher.clone().finalize(),
)?;
let transcript: Vec<u8> = [
&serialized_credential_request,
&l2_component[..],
&ke2_message.to_bytes_without_mac(),
]
.concat();
let mut hasher = D::new();
hasher.update(&transcript);
let hashed_transcript_without_mac = hasher.finalize_reset();
transcript_hasher.update(&serialize(&ke2_message.e_info[..], 2)?);
let mut server_mac =
Hmac::<D>::new_varkey(&km2).map_err(|_| InternalPakeError::HmacError)?;
server_mac.update(&hashed_transcript_without_mac);
server_mac.update(&transcript_hasher.clone().finalize());
if ke2_message.mac != server_mac.finalize().into_bytes() {
if server_mac.verify(&ke2_message.mac).is_err() {
return Err(ProtocolError::VerificationError(
PakeError::KeyExchangeMacValidationError,
));
}
hasher.update(transcript);
hasher.update(ke2_message.mac.to_vec());
let hashed_transcript = hasher.finalize();
transcript_hasher.update(ke2_message.mac.to_vec());
let mut client_mac =
Hmac::<D>::new_varkey(&km3).map_err(|_| InternalPakeError::HmacError)?;
client_mac.update(&hashed_transcript);
client_mac.update(&transcript_hasher.finalize());
// Compute decryption of e_info
let h = Hkdf::<D>::from_prk(&ke2).map_err(|_| InternalPakeError::HkdfError)?;
@@ -227,7 +207,7 @@ impl<D: Hash, G: Group> KeyExchange<D, G> for TripleDH {
Ok((
plaintext,
session_key.to_vec(),
KE3Message {
Ke3Message {
mac: client_mac.finalize().into_bytes(),
},
))
@@ -242,7 +222,7 @@ impl<D: Hash, G: Group> KeyExchange<D, G> for TripleDH {
Hmac::<D>::new_varkey(&ke2_state.km3).map_err(|_| InternalPakeError::HmacError)?;
client_mac.update(&ke2_state.hashed_transcript);
if ke3_message.mac != client_mac.finalize().into_bytes() {
if client_mac.verify(&ke3_message.mac).is_err() {
return Err(ProtocolError::VerificationError(
PakeError::KeyExchangeMacValidationError,
));
@@ -257,21 +237,22 @@ impl<D: Hash, G: Group> KeyExchange<D, G> for TripleDH {
}
/// The client state produced after the first key exchange message
#[derive(PartialEq, Eq)]
pub struct KE1State {
#[derive(PartialEq, Eq, Zeroize)]
#[zeroize(drop)]
pub struct Ke1State {
client_e_sk: Key,
client_nonce: GenericArray<u8, NonceLen>,
}
/// The first key exchange message
#[derive(PartialEq, Eq)]
pub struct KE1Message {
pub struct Ke1Message {
pub(crate) client_nonce: GenericArray<u8, NonceLen>,
pub(crate) info: Vec<u8>,
pub(crate) client_e_pk: Key,
}
impl TryFrom<&[u8]> for KE1State {
impl TryFrom<&[u8]> for Ke1State {
type Error = PakeError;
fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
@@ -287,25 +268,36 @@ impl TryFrom<&[u8]> for KE1State {
}
}
impl ToBytes for KE1State {
fn to_bytes(&self) -> Vec<u8> {
impl ToBytesWithPointers for Ke1State {
fn to_bytes(&self) -> Result<Vec<u8>, ProtocolError> {
let output: Vec<u8> = [&self.client_e_sk.to_arr(), &self.client_nonce[..]].concat();
output
Ok(output)
}
#[cfg(test)]
fn as_byte_ptrs(&self) -> Vec<(*const u8, usize)> {
vec![
(
self.client_e_sk.as_ptr(),
<Key as SizedBytes>::Len::to_usize(),
),
(self.client_nonce.as_ptr(), NonceLen::to_usize()),
]
}
}
impl ToBytes for KE1Message {
fn to_bytes(&self) -> Vec<u8> {
[
impl ToBytes for Ke1Message {
fn to_bytes(&self) -> Result<Vec<u8>, ProtocolError> {
Ok([
&self.client_nonce[..],
&serialize(&self.info, 2),
&serialize(&self.info, 2)?,
&self.client_e_pk.to_arr(),
]
.concat()
.concat())
}
}
impl TryFrom<&[u8]> for KE1Message {
impl TryFrom<&[u8]> for Ke1Message {
type Error = PakeError;
fn try_from(ke1_message_bytes: &[u8]) -> Result<Self, Self::Error> {
@@ -325,32 +317,57 @@ impl TryFrom<&[u8]> for KE1Message {
}
}
/// The server state produced after the second key exchange message
pub struct KE2State<HashLen: ArrayLength<u8>> {
pub struct Ke2State<HashLen: ArrayLength<u8>> {
km3: GenericArray<u8, HashLen>,
hashed_transcript: GenericArray<u8, HashLen>,
session_key: GenericArray<u8, HashLen>,
}
// This can't be derived because of the use of a phantom parameter
impl<HashLen: ArrayLength<u8>> Zeroize for Ke2State<HashLen> {
fn zeroize(&mut self) {
self.km3.zeroize();
self.hashed_transcript.zeroize();
self.session_key.zeroize();
}
}
impl<HashLen: ArrayLength<u8>> Drop for Ke2State<HashLen> {
fn drop(&mut self) {
self.zeroize();
}
}
impl<HashLen: ArrayLength<u8>> ToBytesWithPointers for Ke2State<HashLen> {
fn to_bytes(&self) -> Result<Vec<u8>, ProtocolError> {
Ok([
&self.km3[..],
&self.hashed_transcript[..],
&self.session_key[..],
]
.concat())
}
#[cfg(test)]
fn as_byte_ptrs(&self) -> Vec<(*const u8, usize)> {
vec![
(self.km3.as_ptr(), HashLen::to_usize()),
(self.hashed_transcript.as_ptr(), HashLen::to_usize()),
(self.session_key.as_ptr(), HashLen::to_usize()),
]
}
}
/// The second key exchange message
pub struct KE2Message<HashLen: ArrayLength<u8>> {
#[derive(Clone)]
pub struct Ke2Message<HashLen: ArrayLength<u8>> {
server_nonce: GenericArray<u8, NonceLen>,
server_e_pk: Key,
e_info: Vec<u8>,
mac: GenericArray<u8, HashLen>,
}
impl<HashLen: ArrayLength<u8>> ToBytes for KE2State<HashLen> {
fn to_bytes(&self) -> Vec<u8> {
[
&self.km3[..],
&self.hashed_transcript[..],
&self.session_key[..],
]
.concat()
}
}
impl<HashLen: ArrayLength<u8>> TryFrom<&[u8]> for KE2State<HashLen> {
impl<HashLen: ArrayLength<u8>> TryFrom<&[u8]> for Ke2State<HashLen> {
type Error = PakeError;
fn try_from(input: &[u8]) -> Result<Self, Self::Error> {
@@ -367,24 +384,24 @@ impl<HashLen: ArrayLength<u8>> TryFrom<&[u8]> for KE2State<HashLen> {
}
}
impl<HashLen: ArrayLength<u8>> ToBytes for KE2Message<HashLen> {
fn to_bytes(&self) -> Vec<u8> {
[&self.to_bytes_without_mac(), &self.mac[..]].concat()
}
}
impl<HashLen: ArrayLength<u8>> KE2Message<HashLen> {
fn to_bytes_without_mac(&self) -> Vec<u8> {
[
&self.server_nonce[..],
&self.server_e_pk.to_arr(),
&serialize(&self.e_info, 2),
impl<HashLen: ArrayLength<u8>> ToBytes for Ke2Message<HashLen> {
fn to_bytes(&self) -> Result<Vec<u8>, ProtocolError> {
Ok([
&self.to_bytes_without_info_or_mac(),
&serialize(&self.e_info, 2)?,
&self.mac[..],
]
.concat()
.concat())
}
}
impl<HashLen: ArrayLength<u8>> TryFrom<&[u8]> for KE2Message<HashLen> {
impl<HashLen: ArrayLength<u8>> Ke2Message<HashLen> {
fn to_bytes_without_info_or_mac(&self) -> Vec<u8> {
[&self.server_nonce[..], &self.server_e_pk.to_arr()].concat()
}
}
impl<HashLen: ArrayLength<u8>> TryFrom<&[u8]> for Ke2Message<HashLen> {
type Error = PakeError;
fn try_from(input: &[u8]) -> Result<Self, Self::Error> {
@@ -407,6 +424,7 @@ impl<HashLen: ArrayLength<u8>> TryFrom<&[u8]> for KE2Message<HashLen> {
}
}
#[allow(clippy::upper_case_acronyms)]
// The triple of public and private components used in the 3DH computation
struct TripleDHComponents {
pk1: Key,
@@ -417,6 +435,7 @@ struct TripleDHComponents {
sk3: Key,
}
#[allow(clippy::upper_case_acronyms)]
// Consists of a session key, followed by two mac keys and an encryption key: (session_key, km2, ke2, km3)
type TripleDHDerivationResult<D> = (
GenericArray<u8, <D as FixedOutput>::OutputSize>,
@@ -426,17 +445,17 @@ type TripleDHDerivationResult<D> = (
);
/// The third key exchange message
pub struct KE3Message<HashLen: ArrayLength<u8>> {
pub struct Ke3Message<HashLen: ArrayLength<u8>> {
mac: GenericArray<u8, HashLen>,
}
impl<HashLen: ArrayLength<u8>> ToBytes for KE3Message<HashLen> {
fn to_bytes(&self) -> Vec<u8> {
self.mac.to_vec()
impl<HashLen: ArrayLength<u8>> ToBytes for Ke3Message<HashLen> {
fn to_bytes(&self) -> Result<Vec<u8>, ProtocolError> {
Ok(self.mac.to_vec())
}
}
impl<HashLen: ArrayLength<u8>> TryFrom<&[u8]> for KE3Message<HashLen> {
impl<HashLen: ArrayLength<u8>> TryFrom<&[u8]> for Ke3Message<HashLen> {
type Error = PakeError;
fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
@@ -454,10 +473,7 @@ impl<HashLen: ArrayLength<u8>> TryFrom<&[u8]> for KE3Message<HashLen> {
// with some auxiliary metadata, to produce the session key and two MAC keys
fn derive_3dh_keys<D: Hash, G: Group>(
dh: TripleDHComponents,
client_nonce: &GenericArray<u8, NonceLen>,
server_nonce: &GenericArray<u8, NonceLen>,
id_u: &[u8],
id_s: &[u8],
hashed_derivation_transcript: &[u8],
) -> Result<TripleDHDerivationResult<D>, ProtocolError> {
let ikm: Vec<u8> = [
&KeyPair::<G>::diffie_hellman(dh.pk1, dh.sk1)?[..],
@@ -466,18 +482,17 @@ fn derive_3dh_keys<D: Hash, G: Group>(
]
.concat();
let info: Vec<u8> = [
STR_3DH,
&serialize(&client_nonce, 2),
&serialize(&server_nonce, 2),
&serialize(id_u, 2),
&serialize(id_s, 2),
]
.concat();
let extracted_ikm = Hkdf::<D>::new(None, &ikm);
let handshake_secret = derive_secrets::<D>(&extracted_ikm, &STR_HANDSHAKE_SECRET, &info)?;
let session_key = derive_secrets::<D>(&extracted_ikm, &STR_SESSION_KEY, &info)?;
let handshake_secret = derive_secrets::<D>(
&extracted_ikm,
&STR_HANDSHAKE_SECRET,
&hashed_derivation_transcript,
)?;
let session_key = derive_secrets::<D>(
&extracted_ikm,
&STR_SESSION_SECRET,
&hashed_derivation_transcript,
)?;
let km2 = hkdf_expand_label::<D>(
&handshake_secret,
@@ -487,7 +502,7 @@ fn derive_3dh_keys<D: Hash, G: Group>(
)?;
let ke2 = hkdf_expand_label::<D>(
&handshake_secret,
&STR_SERVER_ENC,
&STR_HANDSHAKE_ENC,
b"",
<D as Digest>::OutputSize::to_usize(),
)?;
@@ -525,14 +540,16 @@ fn hkdf_expand_label_extracted<D: Hash>(
let mut okm = vec![0u8; length];
let mut hkdf_label: Vec<u8> = Vec::new();
hkdf_label.extend_from_slice(&length.to_be_bytes()[std::mem::size_of::<usize>() - 2..]);
let length_u16: u16 = u16::try_from(length).map_err(|_| PakeError::SerializationError)?;
hkdf_label.extend_from_slice(&length_u16.to_be_bytes());
let mut opaque_label: Vec<u8> = Vec::new();
opaque_label.extend_from_slice(&STR_OPAQUE);
opaque_label.extend_from_slice(&label);
hkdf_label.extend_from_slice(&serialize(&opaque_label, 1));
hkdf_label.extend_from_slice(&serialize(&opaque_label, 1)?);
hkdf_label.extend_from_slice(&serialize(&context, 1));
hkdf_label.extend_from_slice(&serialize(&context, 1)?);
hkdf.expand(&hkdf_label, &mut okm)
.map_err(|_| InternalPakeError::HkdfError)?;
@@ -542,13 +559,19 @@ fn hkdf_expand_label_extracted<D: Hash>(
fn derive_secrets<D: Hash>(
hkdf: &Hkdf<D>,
label: &[u8],
transcript: &[u8],
hashed_derivation_transcript: &[u8],
) -> Result<Vec<u8>, ProtocolError> {
let hashed_transcript = D::digest(transcript);
hkdf_expand_label_extracted::<D>(
hkdf,
label,
&hashed_transcript,
&hashed_derivation_transcript,
<D as Digest>::OutputSize::to_usize(),
)
}
// Generate a random nonce up to NonceLen::to_usize() bytes.
fn generate_nonce<R: RngCore + CryptoRng>(rng: &mut R) -> GenericArray<u8, NonceLen> {
let mut nonce_bytes = vec![0u8; NonceLen::to_usize()];
rng.fill_bytes(&mut nonce_bytes);
GenericArray::clone_from_slice(&nonce_bytes)
}
+75 -19
View File
@@ -5,22 +5,27 @@
//! Contains the keypair types that must be supplied for the OPAQUE API
#![allow(unsafe_code)]
use crate::errors::InternalPakeError;
use crate::group::Group;
#[cfg(test)]
use generic_array::typenum::Unsigned;
use generic_array::{typenum::U32, GenericArray};
use generic_bytes::{SizedBytes, TryFromSizedBytesError};
use generic_bytes_derive::TryFromForSizedBytes;
#[cfg(test)]
use proptest::prelude::*;
#[cfg(test)]
use rand::{rngs::StdRng, SeedableRng};
use rand_core::{CryptoRng, RngCore};
use rand::{CryptoRng, RngCore};
use std::fmt::Debug;
use std::marker::PhantomData;
use std::ops::Deref;
use zeroize::Zeroize;
// Pub(crate) convenience extension trait of SizedBytes for our purposes
pub(crate) trait SizedBytesExt: SizedBytes {
/// Convenience extension trait of SizedBytes
pub trait SizedBytesExt: SizedBytes {
/// Convert from bytes
fn from_bytes(bytes: &[u8]) -> Result<Self, TryFromSizedBytesError> {
<Self as SizedBytes>::from_arr(GenericArray::from_slice(bytes))
}
@@ -37,6 +42,20 @@ pub struct KeyPair<G> {
_g: PhantomData<G>,
}
// This can't be derived because of the use of a phantom parameter
impl<G> Zeroize for KeyPair<G> {
fn zeroize(&mut self) {
self.pk.zeroize();
self.sk.zeroize();
}
}
impl<G> Drop for KeyPair<G> {
fn drop(&mut self) {
self.zeroize();
}
}
impl<G: Group> KeyPair<G> {
/// The public key component
pub fn public(&self) -> &Key {
@@ -48,19 +67,9 @@ impl<G: Group> KeyPair<G> {
&self.sk
}
/// A constructor that receives public and private key independently as
/// bytes
pub fn new(public: Key, private: Key) -> Result<Self, InternalPakeError> {
Ok(Self {
pk: public,
sk: private,
_g: PhantomData,
})
}
/// Generating a random key pair given a cryptographic rng
pub(crate) fn generate_random<R: RngCore + CryptoRng>(rng: &mut R) -> Self {
let sk = G::random_scalar(rng);
let sk = G::random_nonzero_scalar(rng);
let sk_bytes = G::scalar_as_bytes(&sk);
let pk = G::base_point().mult_by_slice(&sk_bytes);
Self {
@@ -97,7 +106,19 @@ impl<G: Group> KeyPair<G> {
pub fn from_private_key_slice(input: &[u8]) -> Result<Self, InternalPakeError> {
let sk = Key::from_arr(GenericArray::from_slice(&input))?;
let pk = Self::public_from_private(&sk);
Self::new(pk, sk)
Ok(Self {
pk,
sk,
_g: PhantomData,
})
}
#[cfg(test)]
pub fn as_byte_ptrs(&self) -> Vec<(*const u8, usize)> {
vec![
(self.pk.as_ptr(), <Key as SizedBytes>::Len::to_usize()),
(self.sk.as_ptr(), <Key as SizedBytes>::Len::to_usize()),
]
}
}
@@ -118,9 +139,10 @@ impl<G: Group + Debug> KeyPair<G> {
}
}
/// A minimalist key type built around [u8;32]
#[derive(Debug, PartialEq, Eq, Clone, TryFromForSizedBytes)]
#[ErrorType = "::generic_bytes::TryFromSizedBytesError"]
/// A minimalist key type built around a \[u8; 32\]
#[derive(Debug, PartialEq, Eq, Clone, Zeroize)]
// Ensure Key material is zeroed after use.
#[zeroize(drop)]
#[repr(transparent)]
pub struct Key(Vec<u8>);
@@ -147,7 +169,41 @@ impl SizedBytes for Key {
#[cfg(test)]
mod tests {
use super::*;
use crate::errors::*;
use curve25519_dalek::ristretto::RistrettoPoint;
use generic_array::typenum::Unsigned;
use rand::rngs::OsRng;
use std::slice::from_raw_parts;
#[test]
fn test_zeroize_key() -> Result<(), ProtocolError> {
let key_len = <Key as SizedBytes>::Len::to_usize();
let mut key = Key(vec![1u8; key_len]);
let ptr = key.as_ptr();
key.zeroize();
let bytes = unsafe { from_raw_parts(ptr, key_len) };
assert!(bytes.iter().all(|&x| x == 0));
Ok(())
}
#[test]
fn test_zeroize_keypair() -> Result<(), ProtocolError> {
let mut rng = OsRng;
let mut keypair = KeyPair::<RistrettoPoint>::generate_random(&mut rng);
let ptrs = keypair.as_byte_ptrs();
keypair.zeroize();
for (ptr, len) in ptrs {
let bytes = unsafe { from_raw_parts(ptr, len) };
assert!(bytes.iter().all(|&x| x == 0));
}
Ok(())
}
proptest! {
#[test]
+32 -37
View File
@@ -5,7 +5,7 @@
//! An implementation of the OPAQUE asymmetric password authentication key exchange protocol
//!
//! Note: This implementation is in sync with [draft-irtf-cfrg-opaque-02](https://www.ietf.org/archive/id/draft-irtf-cfrg-opaque-02.html),
//! Note: This implementation is in sync with [draft-irtf-cfrg-opaque-03](https://www.ietf.org/archive/id/draft-irtf-cfrg-opaque-03.html),
//! but this specification is subject to change, until the final version published by the IETF.
//!
//! # Overview
@@ -47,7 +47,7 @@
//! # type Hash = sha2::Sha512;
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
//! # }
//! use rand_core::{OsRng, RngCore};
//! use rand::{rngs::OsRng, RngCore};
//! let mut rng = OsRng;
//! let server_kp = Default::generate_random_keypair(&mut rng);
//! # Ok::<(), ProtocolError>(())
@@ -80,7 +80,7 @@
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
//! # }
//! use opaque_ke::ClientRegistration;
//! use rand_core::{OsRng, RngCore};
//! use rand::{rngs::OsRng, RngCore};
//! let mut client_rng = OsRng;
//! let client_registration_start_result = ClientRegistration::<Default>::start(
//! &mut client_rng,
@@ -109,7 +109,7 @@
//! # type Hash = sha2::Sha512;
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
//! # }
//! # use rand_core::{OsRng, RngCore};
//! # use rand::{rngs::OsRng, RngCore};
//! # let mut client_rng = OsRng;
//! # let client_registration_start_result = ClientRegistration::<Default>::start(
//! # &mut client_rng,
@@ -146,7 +146,7 @@
//! # type Hash = sha2::Sha512;
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
//! # }
//! # use rand_core::{OsRng, RngCore};
//! # use rand::{rngs::OsRng, RngCore};
//! # let mut client_rng = OsRng;
//! # let client_registration_start_result = ClientRegistration::<Default>::start(
//! # &mut client_rng,
@@ -169,7 +169,7 @@
//! a [ServerRegistration] from the second step.
//! The server runs [ServerRegistration::finish] to produce a finalized [ServerRegistration].
//! At this point, the client can be considered as successfully registered, and the server can invoke
//! [ServerRegistration::to_bytes] to store the password file for use during the login protocol.
//! [ServerRegistration::serialize] to store the password file for use during the login protocol.
//! ```
//! # use opaque_ke::{
//! # errors::ProtocolError,
@@ -184,7 +184,7 @@
//! # type Hash = sha2::Sha512;
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
//! # }
//! # use rand_core::{OsRng, RngCore};
//! # use rand::{rngs::OsRng, RngCore};
//! # let mut client_rng = OsRng;
//! # let client_registration_start_result = ClientRegistration::<Default>::start(
//! # &mut client_rng,
@@ -225,7 +225,7 @@
//! # type Hash = sha2::Sha512;
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
//! # }
//! # use rand_core::{OsRng, RngCore};
//! # use rand::{rngs::OsRng, RngCore};
//! use opaque_ke::{ClientLogin, ClientLoginStartParameters};
//! let mut client_rng = OsRng;
//! let client_login_start_result = ClientLogin::<Default>::start(
@@ -258,7 +258,7 @@
//! # type Hash = sha2::Sha512;
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
//! # }
//! # use rand_core::{OsRng, RngCore};
//! # use rand::{rngs::OsRng, RngCore};
//! # let mut client_rng = OsRng;
//! # let client_registration_start_result = ClientRegistration::<Default>::start(
//! # &mut client_rng,
@@ -268,15 +268,14 @@
//! # let server_kp = Default::generate_random_keypair(&mut server_rng);
//! # let server_registration_start_result = ServerRegistration::<Default>::start(&mut server_rng, client_registration_start_result.message, server_kp.public())?;
//! # let client_registration_finish_result = client_registration_start_result.state.finish(&mut client_rng, server_registration_start_result.message, ClientRegistrationFinishParameters::default())?;
//! # let password_file_bytes = server_registration_start_result.state.finish(client_registration_finish_result.message)?.to_bytes();
//! # let password_file_bytes = server_registration_start_result.state.finish(client_registration_finish_result.message)?.serialize();
//! # let client_login_start_result = ClientLogin::<Default>::start(
//! # &mut client_rng,
//! # b"password",
//! # ClientLoginStartParameters::default(),
//! # )?;
//! use opaque_ke::{ServerLogin, ServerLoginStartParameters};
//! use std::convert::TryFrom;
//! let password_file = ServerRegistration::<Default>::try_from(&password_file_bytes[..])?;
//! let password_file = ServerRegistration::<Default>::deserialize(&password_file_bytes[..])?;
//! let mut server_rng = OsRng;
//! let server_login_start_result = ServerLogin::start(
//! &mut server_rng,
@@ -307,7 +306,7 @@
//! # type Hash = sha2::Sha512;
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
//! # }
//! # use rand_core::{OsRng, RngCore};
//! # use rand::{rngs::OsRng, RngCore};
//! # let mut client_rng = OsRng;
//! # let client_registration_start_result = ClientRegistration::<Default>::start(
//! # &mut client_rng,
@@ -317,15 +316,14 @@
//! # let server_kp = Default::generate_random_keypair(&mut server_rng);
//! # let server_registration_start_result = ServerRegistration::<Default>::start(&mut server_rng, client_registration_start_result.message, server_kp.public())?;
//! # let client_registration_finish_result = client_registration_start_result.state.finish(&mut client_rng, server_registration_start_result.message, ClientRegistrationFinishParameters::default())?;
//! # let password_file_bytes = server_registration_start_result.state.finish(client_registration_finish_result.message)?.to_bytes();
//! # let password_file_bytes = server_registration_start_result.state.finish(client_registration_finish_result.message)?.serialize();
//! # let client_login_start_result = ClientLogin::<Default>::start(
//! # &mut client_rng,
//! # b"password",
//! # ClientLoginStartParameters::default(),
//! # )?;
//! # use std::convert::TryFrom;
//! # let password_file =
//! # ServerRegistration::<Default>::try_from(
//! # ServerRegistration::<Default>::deserialize(
//! # &password_file_bytes[..],
//! # )?;
//! # let server_login_start_result =
@@ -354,7 +352,7 @@
//! # type Hash = sha2::Sha512;
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
//! # }
//! # use rand_core::{OsRng, RngCore};
//! # use rand::{rngs::OsRng, RngCore};
//! # let mut client_rng = OsRng;
//! # let client_registration_start_result = ClientRegistration::<Default>::start(
//! # &mut client_rng,
@@ -364,15 +362,14 @@
//! # let server_kp = Default::generate_random_keypair(&mut server_rng);
//! # let server_registration_start_result = ServerRegistration::<Default>::start(&mut server_rng, client_registration_start_result.message, server_kp.public())?;
//! # let client_registration_finish_result = client_registration_start_result.state.finish(&mut client_rng, server_registration_start_result.message, ClientRegistrationFinishParameters::default())?;
//! # let password_file_bytes = server_registration_start_result.state.finish(client_registration_finish_result.message)?.to_bytes();
//! # let password_file_bytes = server_registration_start_result.state.finish(client_registration_finish_result.message)?.serialize();
//! # let client_login_start_result = ClientLogin::<Default>::start(
//! # &mut client_rng,
//! # b"password",
//! # ClientLoginStartParameters::default(),
//! # )?;
//! # use std::convert::TryFrom;
//! # let password_file =
//! # ServerRegistration::<Default>::try_from(
//! # ServerRegistration::<Default>::deserialize(
//! # &password_file_bytes[..],
//! # )?;
//! # let server_login_start_result =
@@ -430,7 +427,7 @@
//! # type Hash = sha2::Sha512;
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
//! # }
//! # use rand_core::{OsRng, RngCore};
//! # use rand::{rngs::OsRng, RngCore};
//! # let mut client_rng = OsRng;
//! # let client_registration_start_result = ClientRegistration::<Default>::start(
//! # &mut client_rng,
@@ -444,15 +441,14 @@
//! // During setup or registration, the server transmits its static public key to the client
//! let server_s_pk = server_kp.public(); // obtained from the server
//! # let client_registration_finish_result = client_registration_start_result.state.finish(&mut client_rng, server_registration_start_result.message, ClientRegistrationFinishParameters::default())?;
//! # let password_file_bytes = server_registration_start_result.state.finish(client_registration_finish_result.message)?.to_bytes();
//! # let password_file_bytes = server_registration_start_result.state.finish(client_registration_finish_result.message)?.serialize();
//! # let client_login_start_result = ClientLogin::<Default>::start(
//! # &mut client_rng,
//! # b"password",
//! # ClientLoginStartParameters::default(),
//! # )?;
//! # use std::convert::TryFrom;
//! # let password_file =
//! # ServerRegistration::<Default>::try_from(
//! # ServerRegistration::<Default>::deserialize(
//! # &password_file_bytes[..],
//! # )?;
//! # let server_login_start_result =
@@ -507,7 +503,7 @@
//! # type Hash = sha2::Sha512;
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
//! # }
//! # use rand_core::{OsRng, RngCore};
//! # use rand::{rngs::OsRng, RngCore};
//! # let mut client_rng = OsRng;
//! # let client_registration_start_result = ClientRegistration::<Default>::start(
//! # &mut client_rng,
@@ -522,15 +518,14 @@
//! server_registration_start_result.message,
//! ClientRegistrationFinishParameters::default()
//! )?;
//! # let password_file_bytes = server_registration_start_result.state.finish(client_registration_finish_result.message)?.to_bytes();
//! # let password_file_bytes = server_registration_start_result.state.finish(client_registration_finish_result.message)?.serialize();
//! # let client_login_start_result = ClientLogin::<Default>::start(
//! # &mut client_rng,
//! # b"password",
//! # ClientLoginStartParameters::default(),
//! # )?;
//! # use std::convert::TryFrom;
//! # let password_file =
//! # ServerRegistration::<Default>::try_from(
//! # ServerRegistration::<Default>::deserialize(
//! # &password_file_bytes[..],
//! # )?;
//! # let server_login_start_result =
@@ -572,7 +567,7 @@
//! # type Hash = sha2::Sha512;
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
//! # }
//! # use rand_core::{OsRng, RngCore};
//! # use rand::{rngs::OsRng, RngCore};
//! # let mut client_rng = OsRng;
//! # let client_registration_start_result = ClientRegistration::<Default>::start(
//! # &mut client_rng,
@@ -607,7 +602,7 @@
//! # type Hash = sha2::Sha512;
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
//! # }
//! # use rand_core::{OsRng, RngCore};
//! # use rand::{rngs::OsRng, RngCore};
//! # let mut client_rng = OsRng;
//! # let client_registration_start_result = ClientRegistration::<Default>::start(
//! # &mut client_rng,
@@ -617,15 +612,14 @@
//! # let server_kp = Default::generate_random_keypair(&mut server_rng);
//! # let server_registration_start_result = ServerRegistration::<Default>::start(&mut server_rng, client_registration_start_result.message, server_kp.public())?;
//! # let client_registration_finish_result = client_registration_start_result.state.finish(&mut client_rng, server_registration_start_result.message, ClientRegistrationFinishParameters::WithIdentifiers(b"username".to_vec(), b"facebook.com".to_vec()))?;
//! # let password_file_bytes = server_registration_start_result.state.finish(client_registration_finish_result.message)?.to_bytes();
//! # let password_file_bytes = server_registration_start_result.state.finish(client_registration_finish_result.message)?.serialize();
//! # let client_login_start_result = ClientLogin::<Default>::start(
//! # &mut client_rng,
//! # b"password",
//! # ClientLoginStartParameters::default(),
//! # )?;
//! # use opaque_ke::{ServerLogin, ServerLoginStartParameters};
//! # use std::convert::TryFrom;
//! # let password_file = ServerRegistration::<Default>::try_from(&password_file_bytes[..])?;
//! # let password_file = ServerRegistration::<Default>::deserialize(&password_file_bytes[..])?;
//! # let mut server_rng = OsRng;
//! let server_login_start_result = ServerLogin::start(
//! &mut server_rng,
@@ -655,7 +649,7 @@
//! # type Hash = sha2::Sha512;
//! # type SlowHash = opaque_ke::slow_hash::NoOpHash;
//! # }
//! # use rand_core::{OsRng, RngCore};
//! # use rand::{rngs::OsRng, RngCore};
//! # let mut client_rng = OsRng;
//! # let client_registration_start_result = ClientRegistration::<Default>::start(
//! # &mut client_rng,
@@ -665,15 +659,14 @@
//! # let server_kp = Default::generate_random_keypair(&mut server_rng);
//! # let server_registration_start_result = ServerRegistration::<Default>::start(&mut server_rng, client_registration_start_result.message, server_kp.public())?;
//! # let client_registration_finish_result = client_registration_start_result.state.finish(&mut client_rng, server_registration_start_result.message, ClientRegistrationFinishParameters::WithIdentifiers(b"username".to_vec(), b"facebook.com".to_vec()))?;
//! # let password_file_bytes = server_registration_start_result.state.finish(client_registration_finish_result.message)?.to_bytes();
//! # let password_file_bytes = server_registration_start_result.state.finish(client_registration_finish_result.message)?.serialize();
//! # let client_login_start_result = ClientLogin::<Default>::start(
//! # &mut client_rng,
//! # b"password",
//! # ClientLoginStartParameters::default(),
//! # )?;
//! # use std::convert::TryFrom;
//! # let password_file =
//! # ServerRegistration::<Default>::try_from(
//! # ServerRegistration::<Default>::deserialize(
//! # &password_file_bytes[..],
//! # )?;
//! # let server_login_start_result =
@@ -749,6 +742,8 @@ mod tests;
// Exports
pub use rand;
pub use crate::messages::{
CredentialFinalization, CredentialRequest, CredentialResponse, RegistrationRequest,
RegistrationResponse, RegistrationUpload,
+13 -13
View File
@@ -6,7 +6,7 @@
//! Defines the GroupWithMapToCurve trait to specify how to map a password to a
//! curve point
use crate::errors::InternalPakeError;
use crate::errors::{InternalPakeError, ProtocolError};
use crate::group::Group;
use crate::hash::Hash;
use crate::serialization::i2osp;
@@ -22,12 +22,12 @@ pub trait GroupWithMapToCurve: Group {
const SUITE_ID: usize;
/// transforms a password and domain separation tag (DST) into a curve point
fn map_to_curve<H: Hash>(msg: &[u8], dst: &[u8]) -> Result<Self, InternalPakeError>;
fn map_to_curve<H: Hash>(msg: &[u8], dst: &[u8]) -> Result<Self, ProtocolError>;
/// Generates the contextString parameter as defined in
/// <https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-05.txt>
fn get_context_string(mode: u8) -> Vec<u8> {
[i2osp(mode as usize, 1), i2osp(Self::SUITE_ID, 2)].concat()
fn get_context_string(mode: u8) -> Result<Vec<u8>, ProtocolError> {
Ok([i2osp(mode as usize, 1)?, i2osp(Self::SUITE_ID, 2)?].concat())
}
}
@@ -36,7 +36,7 @@ impl GroupWithMapToCurve for RistrettoPoint {
// Implements the hash_to_ristretto255() function from
// https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-10.txt
fn map_to_curve<H: Hash>(msg: &[u8], dst: &[u8]) -> Result<Self, InternalPakeError> {
fn map_to_curve<H: Hash>(msg: &[u8], dst: &[u8]) -> Result<Self, ProtocolError> {
let uniform_bytes =
expand_message_xmd::<H>(msg, dst, <H as Digest>::OutputSize::to_usize())?;
Ok(<Self as Group>::hash_to_curve(
@@ -65,24 +65,24 @@ pub(crate) fn expand_message_xmd<H: Hash>(
msg: &[u8],
dst: &[u8],
len_in_bytes: usize,
) -> Result<Vec<u8>, InternalPakeError> {
) -> Result<Vec<u8>, ProtocolError> {
let b_in_bytes = <H as Digest>::OutputSize::to_usize();
let r_in_bytes = <H as BlockInput>::BlockSize::to_usize();
let ell = div_ceil(len_in_bytes, b_in_bytes);
if ell > 255 {
return Err(InternalPakeError::HashToCurveError);
return Err(InternalPakeError::HashToCurveError.into());
}
let dst_prime = [dst, &i2osp(dst.len(), 1)].concat();
let z_pad = i2osp(0, r_in_bytes);
let l_i_b_str = i2osp(len_in_bytes, 2);
let msg_prime = [&z_pad, msg, &l_i_b_str, &i2osp(0, 1), &dst_prime].concat();
let dst_prime = [dst, &i2osp(dst.len(), 1)?].concat();
let z_pad = i2osp(0, r_in_bytes)?;
let l_i_b_str = i2osp(len_in_bytes, 2)?;
let msg_prime = [&z_pad, msg, &l_i_b_str, &i2osp(0, 1)?, &dst_prime].concat();
let mut b: Vec<Vec<u8>> = vec![H::digest(&msg_prime).to_vec()]; // b[0]
let mut h = H::new();
h.update(&b[0]);
h.update(&i2osp(1, 1));
h.update(&i2osp(1, 1)?);
h.update(&dst_prime);
b.push(h.finalize_reset().to_vec()); // b[1]
@@ -91,7 +91,7 @@ pub(crate) fn expand_message_xmd<H: Hash>(
for i in 2..(ell + 1) {
h.update(xor(&b[0], &b[i - 1])?);
h.update(&i2osp(i, 1));
h.update(&i2osp(i, 1)?);
h.update(&dst_prime);
b.push(h.finalize_reset().to_vec()); // b[i]
uniform_bytes.extend_from_slice(&b[i]);
+112 -155
View File
@@ -13,45 +13,38 @@ use crate::{
PakeError, ProtocolError,
},
group::Group,
hash::Hash,
key_exchange::traits::{KeyExchange, ToBytes},
keypair::{Key, KeyPair, SizedBytesExt},
serialization::{serialize, tokenize},
};
use generic_array::{typenum::Unsigned, GenericArray};
use generic_bytes::SizedBytes;
use std::convert::TryFrom;
use std::marker::PhantomData;
// Messages
// =========
/// The message sent by the client to the server, to initiate registration
pub struct RegistrationRequest<Grp> {
pub struct RegistrationRequest<CS: CipherSuite> {
/// blinded password information
pub(crate) alpha: Grp,
pub(crate) alpha: CS::Group,
}
impl<Grp: Group> TryFrom<&[u8]> for RegistrationRequest<Grp> {
type Error = ProtocolError;
fn try_from(first_message_bytes: &[u8]) -> Result<Self, Self::Error> {
let elem_len = Grp::ElemLen::to_usize();
let checked_slice = check_slice_size(first_message_bytes, elem_len, "first_message_bytes")?;
// Check that the message is actually containing an element of the
// correct subgroup
let arr = GenericArray::from_slice(&checked_slice[checked_slice.len() - elem_len..]);
let alpha = Grp::from_element_slice(arr)?;
Ok(Self { alpha })
impl<CS: CipherSuite> RegistrationRequest<CS> {
/// Only used for testing purposes
#[cfg(test)]
pub fn get_alpha_for_testing(&self) -> CS::Group {
self.alpha
}
}
impl<Grp: Group> RegistrationRequest<Grp> {
/// Byte representation for the registration request
pub fn to_bytes(&self) -> Vec<u8> {
self.alpha.to_arr().to_vec()
// Cannot be derived because it would require for CS to be Clone.
impl<CS: CipherSuite> Clone for RegistrationRequest<CS> {
fn clone(&self) -> Self {
Self { alpha: self.alpha }
}
}
impl<CS: CipherSuite> RegistrationRequest<CS> {
/// Serialization into bytes
pub fn serialize(&self) -> Vec<u8> {
self.alpha.to_arr().to_vec()
@@ -59,130 +52,101 @@ impl<Grp: Group> RegistrationRequest<Grp> {
/// Deserialization from bytes
pub fn deserialize(input: &[u8]) -> Result<Self, ProtocolError> {
let checked_slice =
check_slice_size(&input, Grp::ElemLen::to_usize(), "first_message_bytes")?;
let elem_len = <CS::Group as Group>::ElemLen::to_usize();
let checked_slice = check_slice_size(&input, elem_len, "first_message_bytes")?;
// Check that the message is actually containing an element of the
// correct subgroup
let arr = GenericArray::from_slice(checked_slice);
let alpha = Grp::from_element_slice(arr)?;
let alpha = CS::Group::from_element_slice(arr)?;
// Throw an error if the identity group element is encountered
if alpha.is_identity() {
return Err(PakeError::IdentityGroupElementError.into());
}
Ok(Self { alpha })
}
}
/// The answer sent by the server to the user, upon reception of the
/// registration attempt
pub struct RegistrationResponse<Grp> {
pub struct RegistrationResponse<CS: CipherSuite> {
/// The server's oprf output
pub(crate) beta: Grp,
pub(crate) beta: CS::Group,
/// Server's static public key
pub(crate) server_s_pk: Vec<u8>,
}
impl<Grp> TryFrom<&[u8]> for RegistrationResponse<Grp>
where
Grp: Group,
{
type Error = ProtocolError;
fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
let elem_len = Grp::ElemLen::to_usize();
let checked_slice = check_slice_size_atleast(bytes, elem_len, "second_message_bytes")?;
// Check that the message is actually containing an element of the
// correct subgroup
let arr = GenericArray::from_slice(&checked_slice[..elem_len]);
let beta = Grp::from_element_slice(arr)?;
// FIXME check public key bytes
let server_s_pk = checked_slice[elem_len..].to_vec();
Ok(Self { beta, server_s_pk })
}
}
impl<Grp> RegistrationResponse<Grp>
where
Grp: Group,
{
/// Byte representation for the registration response message. This does not
/// include the envelope credentials format
pub fn to_bytes(&self) -> Vec<u8> {
[&self.beta.to_arr().to_vec()[..], &self.server_s_pk[..]].concat()
}
impl<CS: CipherSuite> RegistrationResponse<CS> {
/// Serialization into bytes
pub fn serialize(&self) -> Vec<u8> {
let mut registration_response: Vec<u8> = Vec::new();
registration_response.extend_from_slice(&self.beta.to_arr());
registration_response.extend_from_slice(&serialize(&self.server_s_pk, 2));
registration_response
[self.beta.to_arr().to_vec(), self.server_s_pk.clone()].concat()
}
/// Deserialization from bytes
pub fn deserialize(input: &[u8]) -> Result<Self, ProtocolError> {
let elem_len = <CS::Group as Group>::ElemLen::to_usize();
let key_len = <Key as SizedBytes>::Len::to_usize();
let checked_slice =
check_slice_size_atleast(&input, Grp::ElemLen::to_usize(), "second_message_bytes")?;
check_slice_size(&input, elem_len + key_len, "registration_response_bytes")?;
// Check that the message is actually containing an element of the
// correct subgroup
let arr = GenericArray::from_slice(&checked_slice[..Grp::ElemLen::to_usize()]);
let beta = Grp::from_element_slice(arr)?;
let arr = GenericArray::from_slice(&checked_slice[..elem_len]);
let beta = CS::Group::from_element_slice(arr)?;
let (server_s_pk, remainder) = tokenize(&checked_slice[Grp::ElemLen::to_usize()..], 2)?;
if !remainder.is_empty() {
return Err(PakeError::SerializationError.into());
// Throw an error if the identity group element is encountered
if beta.is_identity() {
return Err(PakeError::IdentityGroupElementError.into());
}
Ok(Self { server_s_pk, beta })
// Ensure that public key is valid
let server_s_pk =
KeyPair::<CS::Group>::check_public_key(Key::from_bytes(&checked_slice[elem_len..])?)?;
Ok(Self {
beta,
server_s_pk: server_s_pk.to_arr().to_vec(),
})
}
#[cfg(test)]
/// Only used for tests, where we can set the beta value to test for the reflection
/// error case
pub fn set_beta_for_testing(&self, new_beta: CS::Group) -> Self {
Self {
beta: new_beta,
server_s_pk: self.server_s_pk.clone(),
}
}
}
/// The final message from the client, containing sealed cryptographic
/// identifiers
pub struct RegistrationUpload<D: Hash, G: Group> {
pub struct RegistrationUpload<CS: CipherSuite> {
/// The "envelope" generated by the user, containing sealed
/// cryptographic identifiers
pub(crate) envelope: Envelope<D>,
pub(crate) envelope: Envelope<CS::Hash>,
/// The user's public key
pub(crate) client_s_pk: Key,
pub(crate) _g: PhantomData<G>,
}
impl<D: Hash, G: Group> TryFrom<&[u8]> for RegistrationUpload<D, G> {
type Error = ProtocolError;
fn try_from(third_message_bytes: &[u8]) -> Result<Self, Self::Error> {
let key_len = <Key as SizedBytes>::Len::to_usize();
let envelope_size = key_len + Envelope::<D>::additional_size();
let checked_bytes = check_slice_size(
third_message_bytes,
envelope_size + key_len,
"third_message",
)?;
let unchecked_client_s_pk = Key::from_bytes(&checked_bytes[envelope_size..])?;
let client_s_pk = KeyPair::<G>::check_public_key(unchecked_client_s_pk)?;
Ok(Self {
envelope: Envelope::<D>::from_bytes(&checked_bytes[..envelope_size])?,
client_s_pk,
_g: PhantomData,
})
}
}
impl<D: Hash, G: Group> RegistrationUpload<D, G> {
impl<CS: CipherSuite> RegistrationUpload<CS> {
/// Serialization into bytes
pub fn serialize(&self) -> Vec<u8> {
let mut message: Vec<u8> = Vec::new();
message.extend_from_slice(&serialize(&self.client_s_pk.to_arr(), 2));
message.extend_from_slice(&self.envelope.serialize());
message
[
self.client_s_pk.to_arr().to_vec(),
self.envelope.serialize(),
]
.concat()
}
/// Deserialization from bytes
pub fn deserialize(input: &[u8]) -> Result<Self, ProtocolError> {
let (client_s_pk, remainder) = tokenize(&input, 2)?;
let (envelope, remainder) = Envelope::<D>::deserialize(&remainder)?;
let key_len = <Key as SizedBytes>::Len::to_usize();
let checked_slice = check_slice_size_atleast(&input, key_len, "registration_upload_bytes")?;
let (envelope, remainder) = Envelope::<CS::Hash>::deserialize(&checked_slice[key_len..])?;
if !remainder.is_empty() {
return Err(PakeError::SerializationError.into());
@@ -190,8 +154,9 @@ impl<D: Hash, G: Group> RegistrationUpload<D, G> {
Ok(Self {
envelope,
client_s_pk: KeyPair::<G>::check_public_key(Key::from_bytes(&client_s_pk)?)?,
_g: PhantomData,
client_s_pk: KeyPair::<CS::Group>::check_public_key(Key::from_bytes(
&checked_slice[..key_len],
)?)?,
})
}
}
@@ -203,25 +168,13 @@ pub struct CredentialRequest<CS: CipherSuite> {
pub(crate) ke1_message: <CS::KeyExchange as KeyExchange<CS::Hash, CS::Group>>::KE1Message,
}
impl<CS: CipherSuite> TryFrom<&[u8]> for CredentialRequest<CS> {
type Error = ProtocolError;
fn try_from(first_message_bytes: &[u8]) -> Result<Self, Self::Error> {
Self::deserialize(first_message_bytes)
}
}
impl<CS: CipherSuite> CredentialRequest<CS> {
/// byte representation for the login request
pub(crate) fn to_bytes(&self) -> Vec<u8> {
[&self.alpha.to_arr()[..], &self.ke1_message.to_bytes()].concat()
}
/// Serialization into bytes
pub fn serialize(&self) -> Vec<u8> {
pub fn serialize(&self) -> Result<Vec<u8>, ProtocolError> {
let mut credential_request: Vec<u8> = Vec::new();
credential_request.extend_from_slice(&self.alpha.to_arr());
credential_request.extend_from_slice(&self.ke1_message.to_bytes());
credential_request
credential_request.extend_from_slice(&self.ke1_message.to_bytes()?);
Ok(credential_request)
}
/// Deserialization from bytes
@@ -236,6 +189,11 @@ impl<CS: CipherSuite> CredentialRequest<CS> {
let arr = GenericArray::from_slice(&checked_slice[..elem_len]);
let alpha = CS::Group::from_element_slice(arr)?;
// Throw an error if the identity group element is encountered
if alpha.is_identity() {
return Err(PakeError::IdentityGroupElementError.into());
}
let ke1_message =
<CS::KeyExchange as KeyExchange<CS::Hash, CS::Group>>::KE1Message::try_from(
&checked_slice[elem_len..],
@@ -243,6 +201,12 @@ impl<CS: CipherSuite> CredentialRequest<CS> {
Ok(Self { alpha, ke1_message })
}
/// Only used for testing purposes
#[cfg(test)]
pub fn get_alpha_for_testing(&self) -> CS::Group {
self.alpha
}
}
/// The answer sent by the server to the user, upon reception of the
@@ -258,12 +222,12 @@ pub struct CredentialResponse<CS: CipherSuite> {
impl<CS: CipherSuite> CredentialResponse<CS> {
/// Serialization into bytes
pub fn serialize(&self) -> Vec<u8> {
[
pub fn serialize(&self) -> Result<Vec<u8>, ProtocolError> {
Ok([
Self::serialize_without_ke(&self.beta, &self.server_s_pk, &self.envelope),
self.ke2_message.to_bytes(),
self.ke2_message.to_bytes()?,
]
.concat()
.concat())
}
pub(crate) fn serialize_without_ke(
@@ -273,7 +237,7 @@ impl<CS: CipherSuite> CredentialResponse<CS> {
) -> Vec<u8> {
[
&beta.to_arr(),
&serialize(&server_s_pk.to_arr().to_vec(), 2)[..],
&server_s_pk.to_arr()[..],
&envelope.to_bytes(),
]
.concat()
@@ -281,16 +245,10 @@ impl<CS: CipherSuite> CredentialResponse<CS> {
/// Deserialization from bytes
pub fn deserialize(input: &[u8]) -> Result<Self, ProtocolError> {
Self::try_from(input)
}
}
impl<CS: CipherSuite> TryFrom<&[u8]> for CredentialResponse<CS> {
type Error = ProtocolError;
fn try_from(second_message_bytes: &[u8]) -> Result<Self, Self::Error> {
let elem_len = <CS::Group as Group>::ElemLen::to_usize();
let key_len = <Key as SizedBytes>::Len::to_usize();
let checked_slice =
check_slice_size_atleast(second_message_bytes, elem_len, "login_second_message_bytes")?;
check_slice_size_atleast(input, elem_len + key_len, "login_second_message_bytes")?;
// Check that the message is actually containing an element of the
// correct subgroup
@@ -298,16 +256,16 @@ impl<CS: CipherSuite> TryFrom<&[u8]> for CredentialResponse<CS> {
let arr = GenericArray::from_slice(beta_bytes);
let beta = CS::Group::from_element_slice(arr)?;
let (serialized_server_s_pk, remainder) = tokenize(&checked_slice[elem_len..], 2)?;
let sized_server_s_pk = check_slice_size(
&serialized_server_s_pk[..],
<Key as SizedBytes>::Len::to_usize(),
"server_s_pk in credential_response",
)?;
let unchecked_server_s_pk = Key::from_bytes(&sized_server_s_pk[..])?;
// Throw an error if the identity group element is encountered
if beta.is_identity() {
return Err(PakeError::IdentityGroupElementError.into());
}
let unchecked_server_s_pk = Key::from_bytes(&checked_slice[elem_len..elem_len + key_len])?;
let server_s_pk = KeyPair::<CS::Group>::check_public_key(unchecked_server_s_pk)?;
let (envelope, remainder) = Envelope::<CS::Hash>::deserialize(&remainder)?;
let (envelope, remainder) =
Envelope::<CS::Hash>::deserialize(&checked_slice[elem_len + key_len..])?;
let ke2_message_size = CS::KeyExchange::ke2_message_size();
let checked_remainder =
@@ -324,6 +282,18 @@ impl<CS: CipherSuite> TryFrom<&[u8]> for CredentialResponse<CS> {
ke2_message,
})
}
#[cfg(test)]
/// Only used for tests, where we can set the beta value to test for the reflection
/// error case
pub fn set_beta_for_testing(&self, new_beta: CS::Group) -> Self {
Self {
beta: new_beta,
server_s_pk: self.server_s_pk.clone(),
envelope: self.envelope.clone(),
ke2_message: self.ke2_message.clone(),
}
}
}
/// The answer sent by the client to the server, upon reception of the
@@ -332,29 +302,16 @@ pub struct CredentialFinalization<CS: CipherSuite> {
pub(crate) ke3_message: <CS::KeyExchange as KeyExchange<CS::Hash, CS::Group>>::KE3Message,
}
impl<CS: CipherSuite> TryFrom<&[u8]> for CredentialFinalization<CS> {
type Error = ProtocolError;
fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
let ke3_message =
<CS::KeyExchange as KeyExchange<CS::Hash, CS::Group>>::KE3Message::try_from(bytes)?;
Ok(Self { ke3_message })
}
}
impl<CS: CipherSuite> CredentialFinalization<CS> {
/// Serialization into bytes
pub fn serialize(&self) -> Vec<u8> {
pub fn serialize(&self) -> Result<Vec<u8>, ProtocolError> {
self.ke3_message.to_bytes()
}
/// Deserialization from bytes
pub fn deserialize(input: &[u8]) -> Result<Self, ProtocolError> {
Self::try_from(&input[..])
}
/// byte representation for the login finalization
pub fn to_bytes(&self) -> Vec<u8> {
self.ke3_message.to_bytes()
let ke3_message =
<CS::KeyExchange as KeyExchange<CS::Hash, CS::Group>>::KE3Message::try_from(input)?;
Ok(Self { ke3_message })
}
}
+237 -146
View File
@@ -11,7 +11,7 @@ use crate::{
errors::{utils::check_slice_size_atleast, InternalPakeError, PakeError, ProtocolError},
group::Group,
hash::Hash,
key_exchange::traits::{KeyExchange, ToBytes},
key_exchange::traits::{KeyExchange, ToBytesWithPointers},
keypair::{Key, KeyPair, SizedBytesExt},
map_to_curve::GroupWithMapToCurve,
oprf,
@@ -23,25 +23,37 @@ use crate::{
use digest::Digest;
use generic_array::{typenum::Unsigned, GenericArray};
use generic_bytes::SizedBytes;
use rand_core::{CryptoRng, RngCore};
use rand::{CryptoRng, RngCore};
use std::{convert::TryFrom, marker::PhantomData};
use zeroize::Zeroize;
static STR_OPAQUE_VERSION: &[u8] = b"OPAQUE";
// Registration
// ============
/// The state elements the client holds to register itself
pub struct ClientRegistration<CS: CipherSuite> {
alpha: CS::Group,
/// token containing the client's password and the blinding factor
pub(crate) token: oprf::Token<CS::Group>,
}
impl<CS: CipherSuite> TryFrom<&[u8]> for ClientRegistration<CS> {
type Error = ProtocolError;
fn try_from(input: &[u8]) -> Result<Self, Self::Error> {
let min_expected_len = <CS::Group as Group>::ScalarLen::to_usize();
impl<CS: CipherSuite> ClientRegistration<CS> {
/// Serialization into bytes
pub fn serialize(&self) -> Vec<u8> {
let output: Vec<u8> = [
&self.alpha.to_arr().to_vec(),
&CS::Group::scalar_as_bytes(&self.token.blind)[..],
&self.token.data,
]
.concat();
output
}
/// Deserialization from bytes
pub fn deserialize(input: &[u8]) -> Result<Self, ProtocolError> {
let elem_len = <CS::Group as Group>::ElemLen::to_usize();
let scalar_len = <CS::Group as Group>::ScalarLen::to_usize();
let min_expected_len = elem_len + scalar_len;
let checked_slice = (if input.len() <= min_expected_len {
Err(InternalPakeError::SizeError {
name: "client_registration_bytes",
@@ -52,30 +64,31 @@ impl<CS: CipherSuite> TryFrom<&[u8]> for ClientRegistration<CS> {
Ok(input)
})?;
let alpha =
CS::Group::from_element_slice(GenericArray::from_slice(&checked_slice[..elem_len]))?;
// Check that the message is actually containing an element of the
// correct subgroup
let scalar_len = min_expected_len;
let blinding_factor_bytes = GenericArray::from_slice(&checked_slice[..scalar_len]);
let blinding_factor_bytes =
GenericArray::from_slice(&checked_slice[elem_len..elem_len + scalar_len]);
let blinding_factor = CS::Group::from_scalar_slice(blinding_factor_bytes)?;
let password = checked_slice[scalar_len..].to_vec();
let password = checked_slice[elem_len + scalar_len..].to_vec();
Ok(Self {
alpha,
token: oprf::Token {
data: password,
blind: blinding_factor,
},
})
}
}
impl<CS: CipherSuite> ClientRegistration<CS> {
/// byte representation for the client's registration state
pub fn to_bytes(&self) -> Vec<u8> {
let output: Vec<u8> = [
&CS::Group::scalar_as_bytes(&self.token.blind)[..],
&self.token.data,
#[cfg(test)]
pub fn as_byte_ptrs(&self) -> Vec<(*const u8, usize)> {
vec![
(self.token.data.as_ptr(), self.token.data.len()),
/* cannot provide raw pointer to self.token.blind until this is exposed in curve25519_dalek::scalar::Scalar */
]
.concat();
output
}
}
@@ -96,7 +109,7 @@ impl Default for ClientRegistrationFinishParameters {
/// Contains the fields that are returned by a client registration start
pub struct ClientRegistrationStartResult<CS: CipherSuite> {
/// The registration request message to be sent to the server
pub message: RegistrationRequest<CS::Group>,
pub message: RegistrationRequest<CS>,
/// The client state that must be persisted in order to complete registration
pub state: ClientRegistration<CS>,
}
@@ -112,7 +125,7 @@ impl<CS: CipherSuite> ClientRegistration<CS> {
/// ```
/// use opaque_ke::ClientRegistration;
/// # use opaque_ke::errors::ProtocolError;
/// use rand_core::{OsRng, RngCore};
/// use rand::{rngs::OsRng, RngCore};
/// use opaque_ke::ciphersuite::CipherSuite;
/// struct Default;
/// impl CipherSuite for Default {
@@ -132,19 +145,21 @@ impl<CS: CipherSuite> ClientRegistration<CS> {
let (token, alpha) = oprf::blind::<R, CS::Group, CS::Hash>(&password, blinding_factor_rng)?;
Ok(ClientRegistrationStartResult {
message: RegistrationRequest::<CS::Group> { alpha },
state: Self { token },
message: RegistrationRequest::<CS> { alpha },
state: Self { alpha, token },
})
}
}
/// Contains the fields that are returned by a client registration finish
pub struct ClientRegistrationFinishResult<D: Hash, G: Group> {
pub struct ClientRegistrationFinishResult<CS: CipherSuite> {
/// The registration upload message to be sent to the server
pub message: RegistrationUpload<D, G>,
pub message: RegistrationUpload<CS>,
/// The export key output by client registration
pub export_key: GenericArray<u8, <D as Digest>::OutputSize>,
_g: PhantomData<G>,
pub export_key: GenericArray<u8, <CS::Hash as Digest>::OutputSize>,
/// Instance of the ClientRegistration, only used in tests for checking zeroize
#[cfg(test)]
pub state: ClientRegistration<CS>,
}
impl<CS: CipherSuite> ClientRegistration<CS> {
@@ -160,7 +175,7 @@ impl<CS: CipherSuite> ClientRegistration<CS> {
/// use opaque_ke::{ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration};
/// # use opaque_ke::errors::ProtocolError;
/// # use opaque_ke::keypair::KeyPair;
/// use rand_core::{OsRng, RngCore};
/// use rand::{rngs::OsRng, RngCore};
/// use opaque_ke::ciphersuite::CipherSuite;
/// struct Default;
/// impl CipherSuite for Default {
@@ -182,15 +197,20 @@ impl<CS: CipherSuite> ClientRegistration<CS> {
pub fn finish<R: CryptoRng + RngCore>(
self,
rng: &mut R,
r2: RegistrationResponse<CS::Group>,
r2: RegistrationResponse<CS>,
params: ClientRegistrationFinishParameters,
) -> Result<ClientRegistrationFinishResult<CS::Hash, CS::Group>, ProtocolError> {
) -> Result<ClientRegistrationFinishResult<CS>, ProtocolError> {
let optional_ids = match params {
ClientRegistrationFinishParameters::WithIdentifiers(id_u, id_s) => Some((id_u, id_s)),
ClientRegistrationFinishParameters::Default => None,
};
let client_static_keypair = CS::generate_random_keypair(rng);
// Check for reflected value from server and halt if detected
if self.alpha.ct_equal(&r2.beta) {
return Err(ProtocolError::ReflectedValueError);
}
let password_derived_key =
get_password_derived_key::<CS::Group, CS::SlowHash, CS::Hash>(&self.token, r2.beta)?;
@@ -206,46 +226,18 @@ impl<CS: CipherSuite> ClientRegistration<CS> {
message: RegistrationUpload {
envelope,
client_s_pk: client_static_keypair.public().clone(),
_g: PhantomData,
},
export_key,
_g: PhantomData,
#[cfg(test)]
state: self,
})
}
}
// This can't be derived because of the use of a phantom parameter
impl<CS: CipherSuite> Zeroize for ClientRegistration<CS> {
fn zeroize(&mut self) {
self.token.data.zeroize();
self.token.blind.zeroize();
}
}
impl<CS: CipherSuite> Drop for ClientRegistration<CS> {
fn drop(&mut self) {
self.zeroize();
}
}
// This can't be derived because of the use of a phantom parameter
impl<CS: CipherSuite> Zeroize for ClientLogin<CS> {
fn zeroize(&mut self) {
self.token.data.zeroize();
self.token.blind.zeroize();
}
}
impl<CS: CipherSuite> Drop for ClientLogin<CS> {
fn drop(&mut self) {
self.zeroize();
}
}
/// Contains the fields that are returned by a server registration start
pub struct ServerRegistrationStartResult<CS: CipherSuite> {
/// The registration resposne message to send to the client
pub message: RegistrationResponse<CS::Group>,
pub message: RegistrationResponse<CS>,
/// The state that the server must keep in order to complete registration
pub state: ServerRegistration<CS>,
}
@@ -257,12 +249,21 @@ pub struct ServerRegistration<CS: CipherSuite> {
pub(crate) oprf_key: <CS::Group as Group>::Scalar,
}
impl<CS: CipherSuite> TryFrom<&[u8]> for ServerRegistration<CS> {
type Error = ProtocolError;
impl<CS: CipherSuite> ServerRegistration<CS> {
/// Serialization into bytes
pub fn serialize(&self) -> Vec<u8> {
let mut output: Vec<u8> = CS::Group::scalar_as_bytes(&self.oprf_key).to_vec();
self.client_s_pk
.iter()
.for_each(|v| output.extend_from_slice(&v.to_arr()));
self.envelope
.iter()
.for_each(|v| output.extend_from_slice(&v.to_bytes()));
output
}
/// The format of a serialized ServerRegistration object:
/// oprf_key | client_s_pk | envelope
fn try_from(input: &[u8]) -> Result<Self, Self::Error> {
/// Deserialization from bytes
pub fn deserialize(input: &[u8]) -> Result<Self, ProtocolError> {
let scalar_len = <CS::Group as Group>::ScalarLen::to_usize();
if input.len() == scalar_len {
return Ok(Self {
@@ -292,19 +293,20 @@ impl<CS: CipherSuite> TryFrom<&[u8]> for ServerRegistration<CS> {
oprf_key,
})
}
}
impl<CS: CipherSuite> ServerRegistration<CS> {
/// byte representation for the server's registration state
pub fn to_bytes(&self) -> Vec<u8> {
let mut output: Vec<u8> = CS::Group::scalar_as_bytes(&self.oprf_key).to_vec();
self.client_s_pk
.iter()
.for_each(|v| output.extend_from_slice(&v.to_arr()));
self.envelope
.iter()
.for_each(|v| output.extend_from_slice(&v.to_bytes()));
output
#[cfg(test)]
pub fn as_byte_ptrs(&self) -> Vec<(*const u8, usize)> {
[
match &self.envelope {
Some(env) => env.as_byte_ptrs(),
None => vec![],
},
match &self.client_s_pk {
Some(pk) => vec![(pk.as_ptr(), pk.len())],
None => vec![],
},
/* cannot provide raw pointer to self.oprf_key until this is exposed in curve25519_dalek::scalar::Scalar */
].concat()
}
/// From the client's "blinded" password, returns a response to be
@@ -318,7 +320,7 @@ impl<CS: CipherSuite> ServerRegistration<CS> {
/// ```
/// use opaque_ke::*;
/// # use opaque_ke::errors::ProtocolError;
/// use rand_core::{OsRng, RngCore};
/// use rand::{rngs::OsRng, RngCore};
/// use opaque_ke::ciphersuite::CipherSuite;
/// struct Default;
/// impl CipherSuite for Default {
@@ -336,11 +338,11 @@ impl<CS: CipherSuite> ServerRegistration<CS> {
/// ```
pub fn start<R: RngCore + CryptoRng>(
rng: &mut R,
message: RegistrationRequest<CS::Group>,
message: RegistrationRequest<CS>,
server_s_pk: &Key,
) -> Result<ServerRegistrationStartResult<CS>, ProtocolError> {
// RFC: generate oprf_key (salt) and v_u = g^oprf_key
let oprf_key = CS::Group::random_scalar(rng);
let oprf_key = CS::Group::random_nonzero_scalar(rng);
// Compute beta = alpha^oprf_key
let beta = oprf::evaluate::<CS::Group>(message.alpha, &oprf_key);
@@ -369,7 +371,7 @@ impl<CS: CipherSuite> ServerRegistration<CS> {
/// ```
/// use opaque_ke::{*, keypair::KeyPair};
/// # use opaque_ke::errors::ProtocolError;
/// use rand_core::{OsRng, RngCore};
/// use rand::{rngs::OsRng, RngCore};
/// use opaque_ke::ciphersuite::CipherSuite;
/// struct Default;
/// impl CipherSuite for Default {
@@ -388,14 +390,11 @@ impl<CS: CipherSuite> ServerRegistration<CS> {
/// let client_record = server_registration_start_result.state.finish(client_registration_finish_result.message)?;
/// # Ok::<(), ProtocolError>(())
/// ```
pub fn finish(
self,
message: RegistrationUpload<CS::Hash, CS::Group>,
) -> Result<Self, ProtocolError> {
pub fn finish(self, message: RegistrationUpload<CS>) -> Result<Self, ProtocolError> {
Ok(Self {
envelope: Some(message.envelope),
client_s_pk: Some(message.client_s_pk),
oprf_key: self.oprf_key,
oprf_key: self.oprf_key.clone(),
})
}
}
@@ -411,9 +410,21 @@ pub struct ClientLogin<CS: CipherSuite> {
serialized_credential_request: Vec<u8>,
}
impl<CS: CipherSuite> TryFrom<&[u8]> for ClientLogin<CS> {
type Error = ProtocolError;
fn try_from(input: &[u8]) -> Result<Self, Self::Error> {
impl<CS: CipherSuite> ClientLogin<CS> {
/// Serialization into bytes
pub fn serialize(&self) -> Result<Vec<u8>, ProtocolError> {
let output: Vec<u8> = [
&CS::Group::scalar_as_bytes(&self.token.blind)[..],
&serialize(&self.serialized_credential_request, 2)?,
&serialize(&self.ke1_state.to_bytes()?, 2)?,
&self.token.data,
]
.concat();
Ok(output)
}
/// Deserialization from bytes
pub fn deserialize(input: &[u8]) -> Result<Self, ProtocolError> {
let scalar_len = <CS::Group as Group>::ScalarLen::to_usize();
let checked_slice = (if input.len() <= scalar_len {
Err(InternalPakeError::SizeError {
@@ -443,19 +454,17 @@ impl<CS: CipherSuite> TryFrom<&[u8]> for ClientLogin<CS> {
serialized_credential_request,
})
}
}
impl<CS: CipherSuite> ClientLogin<CS> {
/// byte representation for the client's login state
pub fn to_bytes(&self) -> Vec<u8> {
let output: Vec<u8> = [
&CS::Group::scalar_as_bytes(&self.token.blind)[..],
&serialize(&self.serialized_credential_request, 2),
&serialize(&self.ke1_state.to_bytes(), 2),
&self.token.data,
]
.concat();
output
#[cfg(test)]
pub fn as_byte_ptrs(&self) -> Vec<(*const u8, usize)> {
[
vec![
(self.token.data.as_ptr(), self.token.data.len()),
/* cannot provide raw pointer to self.token.blind until this is exposed in curve25519_dalek::scalar::Scalar */
],
self.ke1_state.as_byte_ptrs(),
vec![ (self.serialized_credential_request.as_ptr(), self.serialized_credential_request.len()) ],
].concat()
}
}
@@ -505,6 +514,9 @@ pub struct ClientLoginFinishResult<CS: CipherSuite> {
pub server_s_pk: Key,
/// The confidential info sent by the client
pub confidential_info: Vec<u8>,
/// Instance of the ClientLogin, only used in tests for checking zeroize
#[cfg(test)]
pub state: ClientLogin<CS>,
}
impl<CS: CipherSuite> ClientLogin<CS> {
@@ -518,7 +530,7 @@ impl<CS: CipherSuite> ClientLogin<CS> {
/// ```
/// use opaque_ke::{ClientLogin, ClientLoginStartParameters};
/// # use opaque_ke::errors::ProtocolError;
/// use rand_core::{OsRng, RngCore};
/// use rand::{rngs::OsRng, RngCore};
/// use opaque_ke::ciphersuite::CipherSuite;
/// struct Default;
/// impl CipherSuite for Default {
@@ -543,7 +555,7 @@ impl<CS: CipherSuite> ClientLogin<CS> {
let (ke1_state, ke1_message) = CS::KeyExchange::generate_ke1(info, rng)?;
let credential_request = CredentialRequest { alpha, ke1_message };
let serialized_credential_request = credential_request.serialize();
let serialized_credential_request = credential_request.serialize()?;
Ok(ClientLoginStartResult {
message: credential_request,
@@ -568,7 +580,7 @@ impl<CS: CipherSuite> ClientLogin<CS> {
/// # use opaque_ke::{ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration};
/// # use opaque_ke::errors::ProtocolError;
/// # use opaque_ke::keypair::KeyPair;
/// use rand_core::{OsRng, RngCore};
/// use rand::{rngs::OsRng, RngCore};
/// use opaque_ke::ciphersuite::CipherSuite;
/// struct Default;
/// impl CipherSuite for Default {
@@ -599,7 +611,13 @@ impl<CS: CipherSuite> ClientLogin<CS> {
ClientLoginFinishParameters::WithIdentifiers(id_u, id_s) => Some((id_u, id_s)),
};
let l2_beta_bytes = &l2.beta.to_arr()[..];
// Check if beta value from server is equal to alpha value from client
let credential_request =
CredentialRequest::<CS>::deserialize(&self.serialized_credential_request[..])?;
if credential_request.alpha.ct_equal(&l2.beta) {
return Err(ProtocolError::ReflectedValueError);
}
let server_s_pk_bytes = l2.server_s_pk.to_arr().to_vec();
let password_derived_key =
@@ -608,8 +626,10 @@ impl<CS: CipherSuite> ClientLogin<CS> {
.envelope
.open(&password_derived_key, &server_s_pk_bytes, &optional_ids)
.map_err(|e| match e {
InternalPakeError::SealOpenHmacError => PakeError::InvalidLoginError,
err => PakeError::from(err),
ProtocolError::VerificationError(PakeError::CryptoError(
InternalPakeError::SealOpenHmacError,
)) => ProtocolError::VerificationError(PakeError::InvalidLoginError),
err => err,
})?;
let client_s_sk = Key::from_bytes(&opened_envelope.client_s_sk)?;
@@ -619,20 +639,16 @@ impl<CS: CipherSuite> ClientLogin<CS> {
KeyPair::<CS::Group>::public_from_private(&client_s_sk)
.to_arr()
.to_vec(),
server_s_pk_bytes.clone(),
server_s_pk_bytes,
),
Some((id_u, id_s)) => (id_u, id_s),
};
let l2_bytes: Vec<u8> = [
l2_beta_bytes,
&serialize(&server_s_pk_bytes, 2),
&l2.envelope.to_bytes(),
]
.concat();
let credential_response_component =
CredentialResponse::<CS>::serialize_without_ke(&l2.beta, &l2.server_s_pk, &l2.envelope);
let (confidential_info, session_key, ke3_message) = CS::KeyExchange::generate_ke3(
l2_bytes,
credential_response_component,
l2.ke2_message,
&self.ke1_state,
&self.serialized_credential_request,
@@ -648,6 +664,8 @@ impl<CS: CipherSuite> ClientLogin<CS> {
session_key,
export_key: opened_envelope.export_key.clone(),
server_s_pk: l2.server_s_pk,
#[cfg(test)]
state: self,
})
}
}
@@ -658,18 +676,6 @@ pub struct ServerLogin<CS: CipherSuite> {
_cs: PhantomData<CS>,
}
impl<CS: CipherSuite> TryFrom<&[u8]> for ServerLogin<CS> {
type Error = ProtocolError;
fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
Ok(Self {
_cs: PhantomData,
ke2_state: <CS::KeyExchange as KeyExchange<CS::Hash, CS::Group>>::KE2State::try_from(
bytes,
)?,
})
}
}
/// Optional parameters for server login start
pub enum ServerLoginStartParameters {
/// Specifying a confidential info field that will be sent to the client
@@ -699,17 +705,31 @@ pub struct ServerLoginStartResult<CS: CipherSuite> {
}
/// Contains the fields that are returned by a server login finish
pub struct ServerLoginFinishResult {
pub struct ServerLoginFinishResult<CS: CipherSuite> {
/// The session key between client and server
pub session_key: Vec<u8>,
_cs: PhantomData<CS>,
/// Instance of the ClientRegistration, only used in tests for checking zeroize
#[cfg(test)]
pub state: ServerLogin<CS>,
}
impl<CS: CipherSuite> ServerLogin<CS> {
/// byte representation for the server's login state
pub fn to_bytes(&self) -> Vec<u8> {
/// Serialization into bytes
pub fn serialize(&self) -> Result<Vec<u8>, ProtocolError> {
self.ke2_state.to_bytes()
}
/// Deserialization from bytes
pub fn deserialize(bytes: &[u8]) -> Result<Self, ProtocolError> {
Ok(Self {
_cs: PhantomData,
ke2_state: <CS::KeyExchange as KeyExchange<CS::Hash, CS::Group>>::KE2State::try_from(
bytes,
)?,
})
}
/// From the client's "blinded"" password, returns a challenge to be
/// sent back to the client, as well as a ServerLogin
///
@@ -723,7 +743,7 @@ impl<CS: CipherSuite> ServerLogin<CS> {
/// # use opaque_ke::{ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration};
/// # use opaque_ke::errors::ProtocolError;
/// # use opaque_ke::keypair::KeyPair;
/// use rand_core::{OsRng, RngCore};
/// use rand::{rngs::OsRng, RngCore};
/// use opaque_ke::ciphersuite::CipherSuite;
/// struct Default;
/// impl CipherSuite for Default {
@@ -752,6 +772,7 @@ impl<CS: CipherSuite> ServerLogin<CS> {
) -> Result<ServerLoginStartResult<CS>, ProtocolError> {
let client_s_pk = password_file
.client_s_pk
.clone()
.ok_or(InternalPakeError::SealError)?;
let (e_info, optional_ids) = match params {
@@ -764,7 +785,10 @@ impl<CS: CipherSuite> ServerLogin<CS> {
}
};
let envelope = password_file.envelope.ok_or(InternalPakeError::SealError)?;
let envelope = password_file
.envelope
.clone()
.ok_or(InternalPakeError::SealError)?;
if envelope.get_mode() != mode_from_ids(&optional_ids) {
return Err(InternalPakeError::IncompatibleEnvelopeModeError.into());
}
@@ -779,7 +803,7 @@ impl<CS: CipherSuite> ServerLogin<CS> {
Some((id_u, id_s)) => (id_u, id_s),
};
let l1_bytes = &l1.to_bytes();
let l1_bytes = &l1.serialize()?;
let beta = oprf::evaluate(l1.alpha, &password_file.oprf_key);
let server_s_pk = KeyPair::<CS::Group>::public_from_private(&server_s_sk);
@@ -828,7 +852,7 @@ impl<CS: CipherSuite> ServerLogin<CS> {
/// # use opaque_ke::{ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration};
/// # use opaque_ke::errors::ProtocolError;
/// # use opaque_ke::keypair::KeyPair;
/// use rand_core::{OsRng, RngCore};
/// use rand::{rngs::OsRng, RngCore};
/// use opaque_ke::ciphersuite::CipherSuite;
/// struct Default;
/// impl CipherSuite for Default {
@@ -851,9 +875,9 @@ impl<CS: CipherSuite> ServerLogin<CS> {
/// # Ok::<(), ProtocolError>(())
/// ```
pub fn finish(
&self,
self,
message: CredentialFinalization<CS>,
) -> Result<ServerLoginFinishResult, ProtocolError> {
) -> Result<ServerLoginFinishResult<CS>, ProtocolError> {
let session_key = <CS::KeyExchange as KeyExchange<CS::Hash, CS::Group>>::finish_ke(
message.ke3_message,
&self.ke2_state,
@@ -865,19 +889,86 @@ impl<CS: CipherSuite> ServerLogin<CS> {
err => err,
})?;
Ok(ServerLoginFinishResult { session_key })
Ok(ServerLoginFinishResult {
session_key,
_cs: PhantomData,
#[cfg(test)]
state: self,
})
}
#[cfg(test)]
pub fn as_byte_ptrs(&self) -> Vec<(*const u8, usize)> {
self.ke2_state.as_byte_ptrs()
}
}
// Zeroize on drop implementations
// This can't be derived because of the use of a phantom parameter
impl<CS: CipherSuite> Zeroize for ClientRegistration<CS> {
fn zeroize(&mut self) {
self.token.data.zeroize();
self.token.blind.zeroize();
}
}
impl<CS: CipherSuite> Drop for ClientRegistration<CS> {
fn drop(&mut self) {
self.zeroize();
}
}
// This can't be derived because of the use of a phantom parameter
impl<CS: CipherSuite> Zeroize for ServerRegistration<CS> {
fn zeroize(&mut self) {
self.envelope.zeroize();
self.client_s_pk.zeroize();
self.oprf_key.zeroize();
}
}
impl<CS: CipherSuite> Drop for ServerRegistration<CS> {
fn drop(&mut self) {
self.zeroize();
}
}
// This can't be derived because of the use of a phantom parameter
impl<CS: CipherSuite> Zeroize for ClientLogin<CS> {
fn zeroize(&mut self) {
self.token.data.zeroize();
self.token.blind.zeroize();
self.ke1_state.zeroize();
self.serialized_credential_request.zeroize();
}
}
impl<CS: CipherSuite> Drop for ClientLogin<CS> {
fn drop(&mut self) {
self.zeroize();
}
}
// This can't be derived because of the use of a phantom parameter
impl<CS: CipherSuite> Zeroize for ServerLogin<CS> {
fn zeroize(&mut self) {
self.ke2_state.zeroize();
}
}
impl<CS: CipherSuite> Drop for ServerLogin<CS> {
fn drop(&mut self) {
self.zeroize();
}
}
// Helper functions
fn get_password_derived_key<G: GroupWithMapToCurve, SH: SlowHash<D>, D: Hash>(
token: &oprf::Token<G>,
beta: G,
) -> Result<Vec<u8>, InternalPakeError> {
let oprf_output = oprf::finalize::<G, D>(
&token.data,
&oprf::unblind::<G>(token, beta),
STR_OPAQUE_VERSION,
);
SH::hash(oprf_output)
) -> Result<Vec<u8>, ProtocolError> {
let oprf_output = oprf::finalize::<G, D>(&token.data, &token.blind, beta)?;
SH::hash(oprf_output).map_err(ProtocolError::from)
}
+44 -38
View File
@@ -4,12 +4,12 @@
// LICENSE file in the root directory of this source tree.
use crate::{
errors::InternalPakeError, group::Group, hash::Hash, map_to_curve::GroupWithMapToCurve,
errors::ProtocolError, group::Group, hash::Hash, map_to_curve::GroupWithMapToCurve,
serialization::serialize,
};
use digest::Digest;
use generic_array::GenericArray;
use rand_core::{CryptoRng, RngCore};
use rand::{CryptoRng, RngCore};
/// Used to store the OPRF input and blinding factor
pub struct Token<Grp: Group> {
@@ -28,9 +28,10 @@ static MODE_BASE: u8 = 0x00;
pub(crate) fn blind<R: RngCore + CryptoRng, G: GroupWithMapToCurve, H: Hash>(
input: &[u8],
blinding_factor_rng: &mut R,
) -> Result<(Token<G>, G), InternalPakeError> {
let blind = G::random_scalar(blinding_factor_rng);
let dst = [STR_VOPRF, &G::get_context_string(MODE_BASE)].concat();
) -> Result<(Token<G>, G), ProtocolError> {
// Choose a random scalar that must be non-zero
let blind = G::random_nonzero_scalar(blinding_factor_rng);
let dst = [STR_VOPRF, &G::get_context_string(MODE_BASE)?].concat();
let mapped_point = G::map_to_curve::<H>(input, &dst)?;
let blind_token = mapped_point * &blind;
Ok((
@@ -50,25 +51,27 @@ pub(crate) fn evaluate<G: Group>(point: G, oprf_key: &G::Scalar) -> G {
/// Computes the third step for the multiplicative blinding version of DH-OPRF, in which
/// the client unblinds the server's message.
pub(crate) fn unblind<G: Group>(token: &Token<G>, point: G) -> Vec<u8> {
let unblinded = point * &G::scalar_invert(&token.blind);
unblinded.to_arr().to_vec()
pub(crate) fn finalize<G: GroupWithMapToCurve, H: Hash>(
input: &[u8],
blind: &G::Scalar,
evaluated_element: G,
) -> Result<GenericArray<u8, <H as Digest>::OutputSize>, ProtocolError> {
let unblinded_element = evaluated_element * &G::scalar_invert(blind);
finalize_after_unblind::<G, H>(input, unblinded_element)
}
pub(crate) fn finalize<G: GroupWithMapToCurve, H: Hash>(
token_data: &[u8],
issued_token: &[u8],
info: &[u8],
) -> GenericArray<u8, <H as Digest>::OutputSize> {
let finalize_dst = [STR_VOPRF_FINALIZE, &G::get_context_string(MODE_BASE)].concat();
fn finalize_after_unblind<G: GroupWithMapToCurve, H: Hash>(
input: &[u8],
unblinded_element: G,
) -> Result<GenericArray<u8, <H as Digest>::OutputSize>, ProtocolError> {
let finalize_dst = [STR_VOPRF_FINALIZE, &G::get_context_string(MODE_BASE)?].concat();
let hash_input = [
serialize(token_data, 2),
serialize(issued_token, 2),
serialize(info, 2),
serialize(&finalize_dst, 2),
serialize(input, 2)?,
serialize(&unblinded_element.to_arr().to_vec(), 2)?,
serialize(&finalize_dst, 2)?,
]
.concat();
<H as Digest>::digest(&hash_input)
Ok(<H as Digest>::digest(&hash_input))
}
////////////////////////
@@ -81,7 +84,7 @@ pub(crate) fn finalize<G: GroupWithMapToCurve, H: Hash>(
pub fn blind_shim<R: RngCore + CryptoRng, G: GroupWithMapToCurve, H: Hash>(
input: &[u8],
blinding_factor_rng: &mut R,
) -> Result<(Token<G>, G), InternalPakeError> {
) -> Result<(Token<G>, G), ProtocolError> {
blind::<R, G, H>(input, blinding_factor_rng)
}
@@ -95,15 +98,11 @@ pub fn evaluate_shim<G: Group>(point: G, oprf_key: &G::Scalar) -> G {
#[cfg(feature = "bench")]
#[doc(hidden)]
#[inline]
pub fn unblind_and_finalize_shim<G: GroupWithMapToCurve, H: Hash>(
pub fn finalize_shim<G: GroupWithMapToCurve, H: Hash>(
token: &Token<G>,
point: G,
) -> Result<GenericArray<u8, <H as Digest>::OutputSize>, InternalPakeError> {
Ok(finalize::<G, H>(
&token.data,
&unblind::<G>(token, point),
b"",
))
) -> Result<GenericArray<u8, <H as Digest>::OutputSize>, ProtocolError> {
finalize::<G, H>(&token.data, &token.blind, point)
}
///////////
@@ -117,35 +116,38 @@ mod tests {
use crate::group::Group;
use curve25519_dalek::ristretto::RistrettoPoint;
use generic_array::{arr, GenericArray};
use rand_core::OsRng;
use rand::rngs::OsRng;
use sha2::Sha512;
fn prf(input: &[u8], oprf_key: &[u8; 32]) -> GenericArray<u8, <Sha512 as Digest>::OutputSize> {
let dst = [STR_VOPRF, &RistrettoPoint::get_context_string(MODE_BASE)].concat();
let dst = [
STR_VOPRF,
&RistrettoPoint::get_context_string(MODE_BASE).unwrap(),
]
.concat();
let point = RistrettoPoint::map_to_curve::<Sha512>(input, &dst).unwrap();
let scalar =
RistrettoPoint::from_scalar_slice(GenericArray::from_slice(&oprf_key[..])).unwrap();
let res = point * scalar;
finalize::<RistrettoPoint, sha2::Sha512>(&input, &res.to_arr().to_vec(), b"")
finalize_after_unblind::<RistrettoPoint, sha2::Sha512>(&input, res).unwrap()
}
#[test]
fn oprf_retrieval() -> Result<(), InternalPakeError> {
fn oprf_retrieval() {
let input = b"hunter2";
let mut rng = OsRng;
let (token, alpha) = blind::<_, RistrettoPoint, Sha512>(&input[..], &mut rng)?;
let (token, alpha) = blind::<_, RistrettoPoint, Sha512>(&input[..], &mut rng).unwrap();
let oprf_key_bytes = arr![
u8; 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
24, 25, 26, 27, 28, 29, 30, 31, 32,
];
let oprf_key = RistrettoPoint::from_scalar_slice(&oprf_key_bytes)?;
let oprf_key = RistrettoPoint::from_scalar_slice(&oprf_key_bytes).unwrap();
let beta = evaluate::<RistrettoPoint>(alpha, &oprf_key);
let res =
finalize::<RistrettoPoint, sha2::Sha512>(&token.data, &unblind(&token, beta), b"");
finalize::<RistrettoPoint, sha2::Sha512>(&token.data, &token.blind, beta).unwrap();
let res2 = prf(&input[..], &oprf_key.as_bytes());
assert_eq!(res, res2);
Ok(())
}
#[test]
@@ -155,11 +157,15 @@ mod tests {
rng.fill_bytes(&mut input);
let (token, alpha) = blind::<_, RistrettoPoint, sha2::Sha512>(&input, &mut rng).unwrap();
let res =
finalize::<RistrettoPoint, sha2::Sha512>(&token.data, &unblind(&token, alpha), b"");
finalize::<RistrettoPoint, sha2::Sha512>(&token.data, &token.blind, alpha).unwrap();
let dst = [STR_VOPRF, &RistrettoPoint::get_context_string(MODE_BASE)].concat();
let dst = [
STR_VOPRF,
&RistrettoPoint::get_context_string(MODE_BASE).unwrap(),
]
.concat();
let point = RistrettoPoint::map_to_curve::<Sha512>(&input, &dst).unwrap();
let res2 = finalize::<RistrettoPoint, sha2::Sha512>(&input, &point.to_arr().to_vec(), b"");
let res2 = finalize_after_unblind::<RistrettoPoint, sha2::Sha512>(&input, point).unwrap();
assert_eq!(res, res2);
}
+33 -7
View File
@@ -6,17 +6,24 @@
use crate::errors::PakeError;
// Corresponds to the I2OSP() function from RFC8017
pub(crate) fn i2osp(input: usize, length: usize) -> Vec<u8> {
if length <= std::mem::size_of::<usize>() {
return (&input.to_be_bytes()[std::mem::size_of::<usize>() - length..]).to_vec();
pub(crate) fn i2osp(input: usize, length: usize) -> Result<Vec<u8>, PakeError> {
let sizeof_usize = std::mem::size_of::<usize>();
// Check if input >= 256^length
if (sizeof_usize as u32 - input.leading_zeros() / 8) > length as u32 {
return Err(PakeError::SerializationError);
}
if length <= sizeof_usize {
return Ok((&input.to_be_bytes()[sizeof_usize - length..]).to_vec());
}
let mut output = vec![0u8; length];
output.splice(
length - std::mem::size_of::<usize>()..length,
length - sizeof_usize..length,
input.to_be_bytes().iter().cloned(),
);
output
Ok(output)
}
// Corresponds to the OS2IP() function from RFC8017
@@ -31,8 +38,8 @@ pub(crate) fn os2ip(input: &[u8]) -> Result<usize, PakeError> {
}
// Computes I2OSP(len(input), max_bytes) || input
pub(crate) fn serialize(input: &[u8], max_bytes: usize) -> Vec<u8> {
[&i2osp(input.len(), max_bytes), &input[..]].concat()
pub(crate) fn serialize(input: &[u8], max_bytes: usize) -> Result<Vec<u8>, PakeError> {
Ok([&i2osp(input.len(), max_bytes)?, input].concat())
}
// Tokenizes an input of the format I2OSP(len(input), max_bytes) || input, outputting
@@ -55,3 +62,22 @@ pub(crate) fn tokenize(input: &[u8], size_bytes: usize) -> Result<(Vec<u8>, Vec<
#[cfg(test)]
mod tests;
#[cfg(test)]
mod unit_tests {
use super::*;
// Test the error condition for I2OSP
#[test]
fn test_i2osp_err_check() {
assert!(i2osp(0, 1).is_ok());
assert!(i2osp(255, 1).is_ok());
assert!(i2osp(256, 1).is_err());
assert!(i2osp(257, 1).is_err());
assert!(i2osp(256 * 256 - 1, 2).is_ok());
assert!(i2osp(256 * 256, 2).is_err());
assert!(i2osp(256 * 256 + 1, 2).is_err());
}
}
+111 -64
View File
@@ -6,6 +6,7 @@
use crate::{
ciphersuite::CipherSuite,
envelope::{Envelope, InnerEnvelopeMode},
errors::*,
group::Group,
key_exchange::{
traits::{KeyExchange, ToBytes},
@@ -16,11 +17,11 @@ use crate::{
*,
};
use curve25519_dalek::ristretto::RistrettoPoint;
use curve25519_dalek::{ristretto::RistrettoPoint, traits::Identity};
use generic_array::typenum::Unsigned;
use generic_bytes::SizedBytes;
use proptest::{collection::vec, prelude::*};
use rand_core::{OsRng, RngCore};
use rand::{rngs::OsRng, RngCore};
use sha2::Digest;
use std::convert::TryFrom;
@@ -54,12 +55,13 @@ fn random_ristretto_point() -> RistrettoPoint {
fn client_registration_roundtrip() {
let pw = b"hunter2";
let mut rng = OsRng;
let sc = <RistrettoPoint as Group>::random_scalar(&mut rng);
let sc = <RistrettoPoint as Group>::random_nonzero_scalar(&mut rng);
let elem = <RistrettoPoint as Group>::base_point() * sc;
// serialization order: scalar, password
let bytes: Vec<u8> = [&sc.as_bytes()[..], &pw[..]].concat();
let reg = ClientRegistration::<Default>::try_from(&bytes[..]).unwrap();
let reg_bytes = reg.to_bytes();
// serialization order: scalar, password, group element
let bytes: Vec<u8> = [&elem.to_arr(), &sc.as_bytes()[..], &pw[..]].concat();
let reg = ClientRegistration::<Default>::deserialize(&bytes[..]).unwrap();
let reg_bytes = reg.serialize();
assert_eq!(reg_bytes, bytes);
}
@@ -68,20 +70,21 @@ fn server_registration_roundtrip() {
// If we don't have envelope and client_pk, the server registration just
// contains the prf key
let mut rng = OsRng;
let oprf_key = <RistrettoPoint as Group>::random_scalar(&mut rng);
let oprf_key = <RistrettoPoint as Group>::random_nonzero_scalar(&mut rng);
let mut oprf_bytes: Vec<u8> = vec![];
oprf_bytes.extend_from_slice(oprf_key.as_bytes());
let reg = ServerRegistration::<Default>::try_from(&oprf_bytes[..]).unwrap();
let reg_bytes = reg.to_bytes();
let reg = ServerRegistration::<Default>::deserialize(&oprf_bytes[..]).unwrap();
let reg_bytes = reg.serialize();
assert_eq!(reg_bytes, oprf_bytes);
// If we do have envelope and client pk, the server registration contains
// the whole kit
let mut ciphertext = [0u8; 32];
rng.fill_bytes(&mut ciphertext);
// Construct a mock envelope
let mut mock_envelope_bytes = Vec::new();
mock_envelope_bytes.extend_from_slice(&[1; 1]); // mode = 1
mock_envelope_bytes.extend_from_slice(&vec![0; NonceLen::to_usize()]); // empty nonce
mock_envelope_bytes.extend_from_slice(&[0, 0]); // empty ciphertext
mock_envelope_bytes.extend_from_slice(&ciphertext); // ciphertext which is an encrypted private key
mock_envelope_bytes.extend_from_slice(&[0; MAC_SIZE]); // length-MAC_SIZE hmac
let mock_client_kp = Default::generate_random_keypair(&mut rng);
@@ -90,46 +93,65 @@ fn server_registration_roundtrip() {
bytes.extend_from_slice(oprf_key.as_bytes());
bytes.extend_from_slice(&mock_client_kp.public().to_arr());
bytes.extend_from_slice(&mock_envelope_bytes);
let reg = ServerRegistration::<Default>::try_from(&bytes[..]).unwrap();
let reg_bytes = reg.to_bytes();
let reg = ServerRegistration::<Default>::deserialize(&bytes[..]).unwrap();
let reg_bytes = reg.serialize();
assert_eq!(reg_bytes, bytes);
}
#[test]
fn register_first_message_roundtrip() {
fn registration_request_roundtrip() {
let pt = random_ristretto_point();
let pt_bytes = pt.to_arr().to_vec();
let mut input = Vec::new();
input.extend_from_slice(pt_bytes.as_slice());
let r1 = RegistrationRequest::<RistrettoPoint>::deserialize(input.as_slice()).unwrap();
let r1 = RegistrationRequest::<Default>::deserialize(input.as_slice()).unwrap();
let r1_bytes = r1.serialize();
assert_eq!(input, r1_bytes);
// Assert that identity group element is rejected
let identity = RistrettoPoint::identity();
let identity_bytes = identity.to_arr().to_vec();
assert!(
match RegistrationRequest::<Default>::deserialize(identity_bytes.as_slice()) {
Err(ProtocolError::VerificationError(PakeError::IdentityGroupElementError)) => true,
_ => false,
}
);
}
#[test]
fn register_second_message_roundtrip() {
fn registration_response_roundtrip() {
let pt = random_ristretto_point();
let beta_bytes = pt.to_arr();
let mut rng = OsRng;
let skp = Default::generate_random_keypair(&mut rng);
let pubkey_bytes = skp.public().to_arr();
let pubkey_length: usize = pubkey_bytes.len();
let mut input = Vec::new();
input.extend_from_slice(beta_bytes.as_slice());
input.extend_from_slice(&pubkey_length.to_be_bytes()[std::mem::size_of::<usize>() - 2..]);
input.extend_from_slice(&pubkey_bytes.as_slice());
let r2 = RegistrationResponse::<RistrettoPoint>::deserialize(input.as_slice()).unwrap();
let r2 = RegistrationResponse::<Default>::deserialize(input.as_slice()).unwrap();
let r2_bytes = r2.serialize();
assert_eq!(input, r2_bytes);
// Assert that identity group element is rejected
let identity = RistrettoPoint::identity();
let identity_bytes = identity.to_arr().to_vec();
assert!(match RegistrationResponse::<Default>::deserialize(
&[identity_bytes, pubkey_bytes.to_vec()].concat()
) {
Err(ProtocolError::VerificationError(PakeError::IdentityGroupElementError)) => true,
_ => false,
});
}
#[test]
fn register_third_message_roundtrip() {
fn registration_upload_roundtrip() {
let mut rng = OsRng;
let skp = Default::generate_random_keypair(&mut rng);
let pubkey_bytes = skp.public().to_arr();
@@ -150,20 +172,17 @@ fn register_third_message_roundtrip() {
.unwrap();
let envelope_bytes = envelope.serialize();
let pubkey_length: usize = pubkey_bytes.len();
let mut input = Vec::new();
input.extend_from_slice(&pubkey_length.to_be_bytes()[std::mem::size_of::<usize>() - 2..]);
input.extend_from_slice(&pubkey_bytes[..]);
input.extend_from_slice(&envelope_bytes);
let r3 = RegistrationUpload::<sha2::Sha512, RistrettoPoint>::deserialize(&input[..]).unwrap();
let r3 = RegistrationUpload::<Default>::deserialize(&input[..]).unwrap();
let r3_bytes = r3.serialize();
assert_eq!(input, r3_bytes);
}
#[test]
fn login_first_message_roundtrip() {
fn credential_request_roundtrip() {
let mut rng = OsRng;
let alpha = random_ristretto_point();
let alpha_bytes = alpha.to_arr().to_vec();
@@ -177,7 +196,7 @@ fn login_first_message_roundtrip() {
let ke1m: Vec<u8> = [
&client_nonce[..],
&serialize(&info.to_vec(), 2),
&serialize(&info.to_vec(), 2).unwrap(),
&client_e_kp.public(),
]
.concat();
@@ -187,19 +206,29 @@ fn login_first_message_roundtrip() {
input.extend_from_slice(&ke1m[..]);
let l1 = CredentialRequest::<Default>::deserialize(input.as_slice()).unwrap();
let l1_bytes = l1.serialize();
let l1_bytes = l1.serialize().unwrap();
assert_eq!(input, l1_bytes);
// Assert that identity group element is rejected
let identity = RistrettoPoint::identity();
let identity_bytes = identity.to_arr().to_vec();
assert!(match CredentialRequest::<Default>::deserialize(
&[identity_bytes, ke1m.to_vec()].concat()
) {
Err(ProtocolError::VerificationError(PakeError::IdentityGroupElementError)) => true,
_ => false,
});
}
#[test]
fn login_second_message_roundtrip() {
fn credential_response_roundtrip() {
let pt = random_ristretto_point();
let pt_bytes = pt.to_arr().to_vec();
let mut rng = OsRng;
let skp = Default::generate_random_keypair(&mut rng);
let pubkey_bytes = skp.public().to_arr();
let pubkey_length: usize = pubkey_bytes.len();
let mut key = [0u8; 32];
rng.fill_bytes(&mut key);
@@ -228,21 +257,39 @@ fn login_second_message_roundtrip() {
let ke2m: Vec<u8> = [
&server_nonce[..],
&server_e_kp.public(),
&serialize(&e_info.to_vec(), 2),
&serialize(&e_info.to_vec(), 2).unwrap(),
&mac[..],
]
.concat();
let serialized_envelope = envelope.serialize();
let mut input = Vec::new();
input.extend_from_slice(pt_bytes.as_slice());
input.extend_from_slice(&pubkey_length.to_be_bytes()[std::mem::size_of::<usize>() - 2..]);
input.extend_from_slice(&pubkey_bytes.as_slice());
input.extend_from_slice(&envelope.serialize());
input.extend_from_slice(&serialized_envelope);
input.extend_from_slice(&ke2m[..]);
let l2 = CredentialResponse::<Default>::deserialize(&input).unwrap();
let l2_bytes = l2.serialize();
let l2_bytes = l2.serialize().unwrap();
assert_eq!(input, l2_bytes);
// Assert that identity group element is rejected
let identity = RistrettoPoint::identity();
let identity_bytes = identity.to_arr().to_vec();
assert!(match CredentialResponse::<Default>::deserialize(
&[
identity_bytes,
pubkey_bytes.to_vec(),
serialized_envelope,
ke2m.to_vec()
]
.concat()
) {
Err(ProtocolError::VerificationError(PakeError::IdentityGroupElementError)) => true,
_ => false,
});
}
#[test]
@@ -254,7 +301,7 @@ fn login_third_message_roundtrip() {
let input: Vec<u8> = [&mac[..]].concat();
let l3 = CredentialFinalization::<Default>::deserialize(&input).unwrap();
let l3_bytes = l3.serialize();
let l3_bytes = l3.serialize().unwrap();
assert_eq!(input, l3_bytes);
}
@@ -262,7 +309,7 @@ fn login_third_message_roundtrip() {
fn client_login_roundtrip() {
let pw = b"hunter2";
let mut rng = OsRng;
let sc = <RistrettoPoint as Group>::random_scalar(&mut rng);
let sc = <RistrettoPoint as Group>::random_nonzero_scalar(&mut rng);
let client_e_kp = Default::generate_random_keypair(&mut rng);
let mut client_nonce = vec![0u8; NonceLen::to_usize()];
@@ -274,13 +321,13 @@ fn client_login_roundtrip() {
// serialization order: scalar, credential_request, ke1_state, password
let bytes: Vec<u8> = [
&sc.as_bytes()[..],
&serialize(&serialized_credential_request, 2),
&serialize(&l1_data, 2),
&serialize(&serialized_credential_request, 2).unwrap(),
&serialize(&l1_data, 2).unwrap(),
&pw[..],
]
.concat();
let reg = ClientLogin::<Default>::try_from(&bytes[..]).unwrap();
let reg_bytes = reg.to_bytes();
let reg = ClientLogin::<Default>::deserialize(&bytes[..]).unwrap();
let reg_bytes = reg.serialize().unwrap();
assert_eq!(reg_bytes, bytes);
}
@@ -297,14 +344,14 @@ fn ke1_message_roundtrip() {
let ke1m: Vec<u8> = [
&client_nonce[..],
&serialize(&info.to_vec(), 2),
&serialize(&info.to_vec(), 2).unwrap(),
&client_e_kp.public(),
]
.concat();
let reg =
<TripleDH as KeyExchange<sha2::Sha512, RistrettoPoint>>::KE1Message::try_from(&ke1m[..])
.unwrap();
let reg_bytes = reg.to_bytes();
let reg_bytes = reg.to_bytes().unwrap();
assert_eq!(reg_bytes, ke1m);
}
@@ -323,7 +370,7 @@ fn ke2_message_roundtrip() {
let ke2m: Vec<u8> = [
&server_nonce[..],
&server_e_kp.public(),
&serialize(&e_info.to_vec(), 2),
&serialize(&e_info.to_vec(), 2).unwrap(),
&mac[..],
]
.concat();
@@ -331,7 +378,7 @@ fn ke2_message_roundtrip() {
let reg =
<TripleDH as KeyExchange<sha2::Sha512, RistrettoPoint>>::KE2Message::try_from(&ke2m[..])
.unwrap();
let reg_bytes = reg.to_bytes();
let reg_bytes = reg.to_bytes().unwrap();
assert_eq!(reg_bytes, ke2m);
}
@@ -346,7 +393,7 @@ fn ke3_message_roundtrip() {
let reg =
<TripleDH as KeyExchange<sha2::Sha512, RistrettoPoint>>::KE3Message::try_from(&ke3m[..])
.unwrap();
let reg_bytes = reg.to_bytes();
let reg_bytes = reg.to_bytes().unwrap();
assert_eq!(reg_bytes, ke3m);
}
@@ -354,57 +401,57 @@ proptest! {
#[test]
fn test_i2osp_os2ip(bytes in vec(any::<u8>(), 0..std::mem::size_of::<usize>())) {
assert_eq!(i2osp(os2ip(&bytes)?, bytes.len()), bytes);
assert_eq!(i2osp(os2ip(&bytes)?, bytes.len())?, bytes);
}
#[test]
fn test_nocrash_register_first_message(bytes in vec(any::<u8>(), 0..200)) {
RegistrationRequest::<RistrettoPoint>::try_from(&bytes[..]).map_or(true, |_| true);
fn test_nocrash_registration_request(bytes in vec(any::<u8>(), 0..200)) {
RegistrationRequest::<Default>::deserialize(&bytes[..]).map_or(true, |_| true);
}
#[test]
fn test_nocrash_register_second_message(bytes in vec(any::<u8>(), 0..200)) {
RegistrationResponse::<RistrettoPoint>::try_from(&bytes[..]).map_or(true, |_| true);
fn test_nocrash_registration_response(bytes in vec(any::<u8>(), 0..200)) {
RegistrationResponse::<Default>::deserialize(&bytes[..]).map_or(true, |_| true);
}
#[test]
fn test_nocrash_register_third_message(bytes in vec(any::<u8>(), 0..200)) {
RegistrationUpload::<sha2::Sha512, RistrettoPoint>::try_from(&bytes[..]).map_or(true, |_| true);
fn test_nocrash_registration_upload(bytes in vec(any::<u8>(), 0..200)) {
RegistrationUpload::<Default>::deserialize(&bytes[..]).map_or(true, |_| true);
}
#[test]
fn test_nocrash_login_first_message(bytes in vec(any::<u8>(), 0..500)) {
CredentialRequest::<Default>::try_from(&bytes[..]).map_or(true, |_| true);
fn test_nocrash_credential_request(bytes in vec(any::<u8>(), 0..500)) {
CredentialRequest::<Default>::deserialize(&bytes[..]).map_or(true, |_| true);
}
#[test]
fn test_nocrash_login_second_message(bytes in vec(any::<u8>(), 0..500)) {
CredentialResponse::<Default>::try_from(&bytes[..]).map_or(true, |_| true);
fn test_nocrash_credential_response(bytes in vec(any::<u8>(), 0..500)) {
CredentialResponse::<Default>::deserialize(&bytes[..]).map_or(true, |_| true);
}
#[test]
fn test_nocrash_login_third_message(bytes in vec(any::<u8>(), 0..500)) {
CredentialFinalization::<Default>::try_from(&bytes[..]).map_or(true, |_| true);
fn test_nocrash_credential_finalization(bytes in vec(any::<u8>(), 0..500)) {
CredentialFinalization::<Default>::deserialize(&bytes[..]).map_or(true, |_| true);
}
#[test]
fn test_nocrash_client_registration(bytes in vec(any::<u8>(), 0..700)) {
ClientRegistration::<Default>::try_from(&bytes[..]).map_or(true, |_| true);
ClientRegistration::<Default>::deserialize(&bytes[..]).map_or(true, |_| true);
}
#[test]
fn test_nocrash_server_registration(bytes in vec(any::<u8>(), 0..700)) {
ServerRegistration::<Default>::try_from(&bytes[..]).map_or(true, |_| true);
ServerRegistration::<Default>::deserialize(&bytes[..]).map_or(true, |_| true);
}
#[test]
fn test_nocrash_client_login(bytes in vec(any::<u8>(), 0..700)) {
ClientLogin::<Default>::try_from(&bytes[..]).map_or(true, |_| true);
ClientLogin::<Default>::deserialize(&bytes[..]).map_or(true, |_| true);
}
#[test]
fn test_nocrash_server_login(bytes in vec(any::<u8>(), 0..700)) {
ServerLogin::<Default>::try_from(&bytes[..]).map_or(true, |_| true);
ServerLogin::<Default>::deserialize(&bytes[..]).map_or(true, |_| true);
}
}
+9 -1
View File
@@ -30,13 +30,21 @@ impl<D: Hash> SlowHash<D> for NoOpHash {
}
}
#[cfg(feature = "slow-hash")]
const DEFAULT_SCRYPT_LOG_N: u8 = 15u8;
#[cfg(feature = "slow-hash")]
const DEFAULT_SCRYPT_R: u32 = 8u32;
#[cfg(feature = "slow-hash")]
const DEFAULT_SCRYPT_P: u32 = 1u32;
#[cfg(feature = "slow-hash")]
impl<D: Hash> SlowHash<D> for scrypt::ScryptParams {
fn hash(
input: GenericArray<u8, <D as Digest>::OutputSize>,
) -> Result<Vec<u8>, InternalPakeError> {
let params =
scrypt::ScryptParams::new(15, 8, 1).map_err(|_| InternalPakeError::SlowHashError)?;
scrypt::ScryptParams::new(DEFAULT_SCRYPT_LOG_N, DEFAULT_SCRYPT_R, DEFAULT_SCRYPT_P)
.map_err(|_| InternalPakeError::SlowHashError)?;
let mut output = vec![0u8; <D as Digest>::OutputSize::to_usize()];
scrypt::scrypt(&input, &[], &params, &mut output)
.map_err(|_| InternalPakeError::SlowHashError)?;
+506 -76
View File
@@ -3,23 +3,26 @@
// This source code is licensed under the MIT license found in the
// LICENSE file in the root directory of this source tree.
#![allow(unsafe_code)]
use crate::{
ciphersuite::CipherSuite,
errors::*,
group::Group,
key_exchange::tripledh::{NonceLen, TripleDH},
keypair::Key,
keypair::{Key, SizedBytesExt},
opaque::*,
slow_hash::NoOpHash,
tests::mock_rng::CycleRng,
*,
};
use curve25519_dalek::ristretto::RistrettoPoint;
use curve25519_dalek::{ristretto::RistrettoPoint, traits::Identity};
use generic_array::typenum::Unsigned;
use generic_bytes::SizedBytes;
use rand_core::{OsRng, RngCore};
use rand::{rngs::OsRng, RngCore};
use serde_json::Value;
use std::convert::TryFrom;
use std::slice::from_raw_parts;
use zeroize::Zeroize;
// Tests
// =====
@@ -66,39 +69,41 @@ pub struct TestVectorParameters {
pub session_key: Vec<u8>,
}
static STR_PASSWORD: &str = "password";
static TEST_VECTOR: &str = r#"
{
"client_s_pk": "2c3247e1d7fcf2bef09a0c6e771c44fe922f36deb5726998c89f7816323bc373",
"client_s_sk": "d7dd5397912dc1dce64a26b8d24bee59b7929b15a6b9769ad6357913a13e7f09",
"client_e_pk": "3431e8f57ce0c1ae9da2b4f37a2f877a64b244a86bc1384ba70a9c39985b3729",
"client_e_sk": "8410c0520fd2a37775e54000d9abb5a84825d37649827d8f2721dd655cd64c0c",
"server_s_pk": "6a55f77ffb17741d5dfd404ef7079ef276cb2114a76f507c0ae7172847da647a",
"server_s_sk": "f4433f94dea3f4bf877ab760566efa36340e34d516dc4462b2ad5b9600157a06",
"server_e_pk": "32d56c7e3b882b9ac1a4e38d60456c4942ec6b6aceed91d069ead967a825df74",
"server_e_sk": "256f9cdddb2c4bed42866e37d5dff019b4a9584b7486e5683d52b56842d82b0d",
"client_s_pk": "4275bd003b150aa16dfe06cff83669e2edcf78c18e7608426e5e0d4b706dba18",
"client_s_sk": "f0bc6e351b8354e41566c8c9629a9cdceeca640f7e473adaaa1892d97c80640f",
"client_e_pk": "165f15328fe643007508b702b14888a14a3bae283cfe5f427aac3e4aae4ca844",
"client_e_sk": "c573022221ca5e98bab4556a0d29a8fce930b1b8ac6919f14dc35dececf5c10e",
"server_s_pk": "64b44e3d26748057e3d06cc38711e98121ba0baaf8124e95b27ee9d59feabc5b",
"server_s_sk": "12be4dd74732e5a4d4eb4069ffa8b5ee19c99f3a61607dad29db239fab868007",
"server_e_pk": "d2b56121a97769d578f80d5ffa653b781a7a1db6297a951afd43ef74d132aa53",
"server_e_sk": "1cd12dc39777dd5a3bfd4969de2402d9cb074837faa996425e871458e71b7404",
"id_u": "696455",
"id_s": "696453",
"password": "70617373776f7264",
"blinding_factor": "c0937fddc5b7a8cbb76301cbe588268d52a9efe6a205af21a0a12b1fadf3a008",
"oprf_key": "f386e8710c12c870a0ec74f09364811142050a5266ca53d36c11e369343a5e09",
"envelope_nonce": "2338be69855ad2a280ab5ed67fb04cd96691841241dbb4873ca776adc60d1c93",
"client_nonce": "09f2721189a82d90e891d346aa9be462e003cc6b6824a55352da7df685eca5bf",
"server_nonce": "5c11e9abe0d5d677dd1a66cd1c8e9baa1f1a44c7554ffe61978b100e657f79f4",
"blinding_factor": "f8ab29fa00f1412b78b2f4c993ae3fab4ee644ddf30a784892b3d0d81d838200",
"oprf_key": "ca00ce46525fa67aa5101420f2f0643c3e5c12ac8e283d83d7d28213ca777a08",
"envelope_nonce": "2695cb3afd4df2029b8d3353f7220876619ad8109e27da917114245071613474",
"client_nonce": "8b7a191f297023b5abdf54c4896d57d0c168a2dde7dce00f32886c016298f606",
"server_nonce": "2477b1825fc64764e309facda26b5cb046af668d1c2a4e88aab0138317784825",
"info1": "696e666f31",
"einfo2": "65696e666f32",
"registration_request": "58e629f68bd59f1d5fcee531a5afeedb4e07d2c6aed98d15cab686d063f2f46e",
"registration_response": "a2227384ed34860b7d879d617f7daa802b6a878eaa5eab33669400f30ad0431d00206a55f77ffb17741d5dfd404ef7079ef276cb2114a76f507c0ae7172847da647a",
"registration_upload": "00202c3247e1d7fcf2bef09a0c6e771c44fe922f36deb5726998c89f7816323bc373022338be69855ad2a280ab5ed67fb04cd96691841241dbb4873ca776adc60d1c930022576b449b09abeb68d5f6c56082b0c2f560c636bff4f0af3b44b6377045201b545598d319af3122041f06e3ab86371bff6c0727557c77d20efeb9a0b9f24573e3646735eeebed45e741d11d0120741af27b04c2716eb6bdfe950daff937cfcae8569e",
"credential_request": "58e629f68bd59f1d5fcee531a5afeedb4e07d2c6aed98d15cab686d063f2f46e09f2721189a82d90e891d346aa9be462e003cc6b6824a55352da7df685eca5bf0005696e666f313431e8f57ce0c1ae9da2b4f37a2f877a64b244a86bc1384ba70a9c39985b3729",
"credential_response": "a2227384ed34860b7d879d617f7daa802b6a878eaa5eab33669400f30ad0431d00206a55f77ffb17741d5dfd404ef7079ef276cb2114a76f507c0ae7172847da647a022338be69855ad2a280ab5ed67fb04cd96691841241dbb4873ca776adc60d1c930022576b449b09abeb68d5f6c56082b0c2f560c636bff4f0af3b44b6377045201b545598d319af3122041f06e3ab86371bff6c0727557c77d20efeb9a0b9f24573e3646735eeebed45e741d11d0120741af27b04c2716eb6bdfe950daff937cfcae8569e5c11e9abe0d5d677dd1a66cd1c8e9baa1f1a44c7554ffe61978b100e657f79f432d56c7e3b882b9ac1a4e38d60456c4942ec6b6aceed91d069ead967a825df740006a0c5112e94c86671fd294468658b8a78375f89fca9fb7c3ee1f63871360d5237e57da81719849a8dbccb974742333a1aed034d63d0bdba0beacfe30196912d561e99c1e79ccf",
"credential_finalization": "b1fad859f343f1a15df14bd7782db9ab6ee4493225a470152e53b3b7601dc954bc80d7b7dadfe70f743b1ab1552a054e7a9d192399ea42073a1d1c5839aabfc2",
"client_registration_state": "c0937fddc5b7a8cbb76301cbe588268d52a9efe6a205af21a0a12b1fadf3a00870617373776f7264",
"client_login_state": "c0937fddc5b7a8cbb76301cbe588268d52a9efe6a205af21a0a12b1fadf3a008006758e629f68bd59f1d5fcee531a5afeedb4e07d2c6aed98d15cab686d063f2f46e09f2721189a82d90e891d346aa9be462e003cc6b6824a55352da7df685eca5bf0005696e666f313431e8f57ce0c1ae9da2b4f37a2f877a64b244a86bc1384ba70a9c39985b372900408410c0520fd2a37775e54000d9abb5a84825d37649827d8f2721dd655cd64c0c09f2721189a82d90e891d346aa9be462e003cc6b6824a55352da7df685eca5bf70617373776f7264",
"server_registration_state": "f386e8710c12c870a0ec74f09364811142050a5266ca53d36c11e369343a5e09",
"server_login_state": "761333eea593c396021c3930fb36cf97aad7c7ea00f1ff983e4df9ca002885017161427303fddb4508c9136a67612e01b673ed88b1de49ed6628d0a77e43c590595f53bf0b0766c165b876173c509efc982868d3860df5dad3f0753477ec9bdd103b71dab9540537f2b10964da82032a6339da6d663a409f4e8e5dea03e3653bb6202f265ed814df39f446486197e1c6091ec6b74200f18df5eef59813ff7f245d69b34e8843be496630921601ab784b1f3ccbfe2ff014ea132199c5c76deda7",
"password_file": "f386e8710c12c870a0ec74f09364811142050a5266ca53d36c11e369343a5e092c3247e1d7fcf2bef09a0c6e771c44fe922f36deb5726998c89f7816323bc373022338be69855ad2a280ab5ed67fb04cd96691841241dbb4873ca776adc60d1c930022576b449b09abeb68d5f6c56082b0c2f560c636bff4f0af3b44b6377045201b545598d319af3122041f06e3ab86371bff6c0727557c77d20efeb9a0b9f24573e3646735eeebed45e741d11d0120741af27b04c2716eb6bdfe950daff937cfcae8569e",
"export_key": "96b95891f06c7f02ab9c508f30e1a82ddea25e2f4fc4ffdafeb199ee65e24e418b2648a201e889eaf84301a1a8d0c3b7fea0ec8d4611686e6daee12430f60c05",
"session_key": "b6202f265ed814df39f446486197e1c6091ec6b74200f18df5eef59813ff7f245d69b34e8843be496630921601ab784b1f3ccbfe2ff014ea132199c5c76deda7"
"registration_request": "28259e84c7eb4b776a87481d4761cafac4b5585bf6f7f4939734c422fc82aa5d",
"registration_response": "94a37d9174f7083c44f85017027a6371941ad74eb35b2c666498bd7c5eef3c2a64b44e3d26748057e3d06cc38711e98121ba0baaf8124e95b27ee9d59feabc5b",
"registration_upload": "4275bd003b150aa16dfe06cff83669e2edcf78c18e7608426e5e0d4b706dba18022695cb3afd4df2029b8d3353f7220876619ad8109e27da9171142450716134746a4dba2b7bf8e8763cad213fa81c9511135be6e42c44a2806e9fb416dc760884136277c6644682cddbf03a8472dca2925ceb9d7d804c7b616843bdf4e80ae35672aff300a03dfb6e79c9079509645a790f792caebc9b07003ead9d98d1d179c1",
"credential_request": "28259e84c7eb4b776a87481d4761cafac4b5585bf6f7f4939734c422fc82aa5d8b7a191f297023b5abdf54c4896d57d0c168a2dde7dce00f32886c016298f6060005696e666f31165f15328fe643007508b702b14888a14a3bae283cfe5f427aac3e4aae4ca844",
"credential_response": "94a37d9174f7083c44f85017027a6371941ad74eb35b2c666498bd7c5eef3c2a64b44e3d26748057e3d06cc38711e98121ba0baaf8124e95b27ee9d59feabc5b022695cb3afd4df2029b8d3353f7220876619ad8109e27da9171142450716134746a4dba2b7bf8e8763cad213fa81c9511135be6e42c44a2806e9fb416dc760884136277c6644682cddbf03a8472dca2925ceb9d7d804c7b616843bdf4e80ae35672aff300a03dfb6e79c9079509645a790f792caebc9b07003ead9d98d1d179c12477b1825fc64764e309facda26b5cb046af668d1c2a4e88aab0138317784825d2b56121a97769d578f80d5ffa653b781a7a1db6297a951afd43ef74d132aa530006b71e6bcb7951b448e2b8ad7ab0b358019af909e03090e0f5a65d0f26186e31b10bfc1024e60a5f0c2dbb2648b33d59a3447e9d5618048efe55b7ea9476f90132520d78e3d91f",
"credential_finalization": "0df019bc4652ad1539de43bfdd33761ac0b526ff27c46fd1d7c89c6d46c5d179beeab30c060cf7bf0e17114812c1ac9d90369e76c6284e4a04b43ff9a67afb87",
"client_registration_state": "28259e84c7eb4b776a87481d4761cafac4b5585bf6f7f4939734c422fc82aa5df8ab29fa00f1412b78b2f4c993ae3fab4ee644ddf30a784892b3d0d81d83820070617373776f7264",
"client_login_state": "f8ab29fa00f1412b78b2f4c993ae3fab4ee644ddf30a784892b3d0d81d838200006728259e84c7eb4b776a87481d4761cafac4b5585bf6f7f4939734c422fc82aa5d8b7a191f297023b5abdf54c4896d57d0c168a2dde7dce00f32886c016298f6060005696e666f31165f15328fe643007508b702b14888a14a3bae283cfe5f427aac3e4aae4ca8440040c573022221ca5e98bab4556a0d29a8fce930b1b8ac6919f14dc35dececf5c10e8b7a191f297023b5abdf54c4896d57d0c168a2dde7dce00f32886c016298f60670617373776f7264",
"server_registration_state": "ca00ce46525fa67aa5101420f2f0643c3e5c12ac8e283d83d7d28213ca777a08",
"server_login_state": "b4c7da6422bd9558887022647974a6b391df989ba0f421eec64bdd8e8329f2aec5fab88b8fd55a2071ac02ffd67acb35a3a7d1a3d9dcbb5406542a2a24b1ed6d7378a115cb801e1cd64c152bfbde36b4c22d77e5d98888ec9af51489315ade5f3f34252c9aafc211e7de9703a039def76adef33074be35a978e729ca125aecfed13415079fc276064cb61eada57c6b59041fbd8cdf4ba55cfb8a5ea9c6e100f85b6cfdd85ffbe99e6e2d80fc9c5f8399aa838e384e8451ce78efde5aa8f03c15",
"password_file": "ca00ce46525fa67aa5101420f2f0643c3e5c12ac8e283d83d7d28213ca777a084275bd003b150aa16dfe06cff83669e2edcf78c18e7608426e5e0d4b706dba18022695cb3afd4df2029b8d3353f7220876619ad8109e27da9171142450716134746a4dba2b7bf8e8763cad213fa81c9511135be6e42c44a2806e9fb416dc760884136277c6644682cddbf03a8472dca2925ceb9d7d804c7b616843bdf4e80ae35672aff300a03dfb6e79c9079509645a790f792caebc9b07003ead9d98d1d179c1",
"export_key": "88514619c2a0ec5567cedb69c6d0e5596ace9bf5414b326e3cb80f41a62d55d4343f2c5eb41b030f25cbf9d9d0956a20cf70255f356f143f15a175a3adaa37c2",
"session_key": "d13415079fc276064cb61eada57c6b59041fbd8cdf4ba55cfb8a5ea9c6e100f85b6cfdd85ffbe99e6e2d80fc9c5f8399aa838e384e8451ce78efde5aa8f03c15"
}
"#;
@@ -280,7 +285,7 @@ fn generate_parameters<CS: CipherSuite>() -> TestVectorParameters {
let mut server_nonce = vec![0u8; NonceLen::to_usize()];
rng.fill_bytes(&mut server_nonce);
let blinding_factor = CS::Group::random_scalar(&mut rng);
let blinding_factor = CS::Group::random_nonzero_scalar(&mut rng);
let blinding_factor_bytes = CS::Group::scalar_as_bytes(&blinding_factor).clone();
let info1 = b"info1";
@@ -300,7 +305,7 @@ fn generate_parameters<CS: CipherSuite>() -> TestVectorParameters {
.message
.serialize()
.to_vec();
let client_registration_state = client_registration_start_result.state.to_bytes().to_vec();
let client_registration_state = client_registration_start_result.state.serialize().to_vec();
let mut oprf_key_rng = CycleRng::new(oprf_key_raw.to_vec());
let server_registration_start_result = ServerRegistration::<CS>::start(
@@ -315,7 +320,7 @@ fn generate_parameters<CS: CipherSuite>() -> TestVectorParameters {
.to_vec();
let oprf_key_bytes =
CS::Group::scalar_as_bytes(&server_registration_start_result.state.oprf_key).clone();
let server_registration_state = server_registration_start_result.state.to_bytes().to_vec();
let server_registration_state = server_registration_start_result.state.serialize().to_vec();
let mut client_s_sk_and_nonce: Vec<u8> = Vec::new();
client_s_sk_and_nonce.extend_from_slice(&client_s_kp.private().to_arr());
@@ -339,7 +344,7 @@ fn generate_parameters<CS: CipherSuite>() -> TestVectorParameters {
.state
.finish(client_registration_finish_result.message)
.unwrap();
let password_file_bytes = password_file.to_bytes();
let password_file_bytes = password_file.serialize();
let mut client_login_start: Vec<u8> = Vec::new();
client_login_start.extend_from_slice(&blinding_factor_bytes);
@@ -353,8 +358,16 @@ fn generate_parameters<CS: CipherSuite>() -> TestVectorParameters {
ClientLoginStartParameters::WithInfo(info1.to_vec()),
)
.unwrap();
let credential_request_bytes = client_login_start_result.message.serialize().to_vec();
let client_login_state = client_login_start_result.state.to_bytes().to_vec();
let credential_request_bytes = client_login_start_result
.message
.serialize()
.unwrap()
.to_vec();
let client_login_state = client_login_start_result
.state
.serialize()
.unwrap()
.to_vec();
let mut server_e_sk_and_nonce_rng = CycleRng::new(
[
@@ -375,8 +388,16 @@ fn generate_parameters<CS: CipherSuite>() -> TestVectorParameters {
),
)
.unwrap();
let credential_response_bytes = server_login_start_result.message.serialize().to_vec();
let server_login_state = server_login_start_result.state.to_bytes().to_vec();
let credential_response_bytes = server_login_start_result
.message
.serialize()
.unwrap()
.to_vec();
let server_login_state = server_login_start_result
.state
.serialize()
.unwrap()
.to_vec();
let client_login_finish_result = client_login_start_result
.state
@@ -385,7 +406,7 @@ fn generate_parameters<CS: CipherSuite>() -> TestVectorParameters {
ClientLoginFinishParameters::WithIdentifiers(id_u.to_vec(), id_s.to_vec()),
)
.unwrap();
let credential_finalization_bytes = client_login_finish_result.message.to_bytes().to_vec();
let credential_finalization_bytes = client_login_finish_result.message.serialize().unwrap();
TestVectorParameters {
client_s_pk: client_s_kp.public().to_arr().to_vec(),
@@ -417,7 +438,7 @@ fn generate_parameters<CS: CipherSuite>() -> TestVectorParameters {
server_registration_state,
client_login_state,
server_login_state,
session_key: client_login_finish_result.session_key,
session_key: client_login_finish_result.session_key.clone(),
export_key: client_registration_finish_result.export_key.to_vec(),
}
}
@@ -440,7 +461,7 @@ fn test_registration_request() -> Result<(), ProtocolError> {
);
assert_eq!(
hex::encode(&parameters.client_registration_state),
hex::encode(client_registration_start_result.state.to_bytes())
hex::encode(client_registration_start_result.state.serialize())
);
Ok(())
}
@@ -453,7 +474,7 @@ fn test_registration_response() -> Result<(), ProtocolError> {
ServerRegistration::<RistrettoSha5123dhNoSlowHash>::start(
&mut oprf_key_rng,
RegistrationRequest::deserialize(&parameters.registration_request[..])?,
&Key::try_from(&parameters.server_s_pk[..])?,
&Key::from_bytes(&parameters.server_s_pk[..])?,
)?;
assert_eq!(
hex::encode(parameters.registration_response),
@@ -461,7 +482,7 @@ fn test_registration_response() -> Result<(), ProtocolError> {
);
assert_eq!(
hex::encode(&parameters.server_registration_state),
hex::encode(server_registration_start_result.state.to_bytes())
hex::encode(server_registration_start_result.state.serialize())
);
Ok(())
}
@@ -473,7 +494,7 @@ fn test_registration_upload() -> Result<(), ProtocolError> {
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 result = ClientRegistration::<RistrettoSha5123dhNoSlowHash>::try_from(
let result = ClientRegistration::<RistrettoSha5123dhNoSlowHash>::deserialize(
&parameters.client_registration_state[..],
)?
.finish(
@@ -498,7 +519,7 @@ fn test_registration_upload() -> Result<(), ProtocolError> {
fn test_password_file() -> Result<(), ProtocolError> {
let parameters = populate_test_vectors(&serde_json::from_str(TEST_VECTOR).unwrap());
let server_registration = ServerRegistration::<RistrettoSha5123dhNoSlowHash>::try_from(
let server_registration = ServerRegistration::<RistrettoSha5123dhNoSlowHash>::deserialize(
&parameters.server_registration_state[..],
)?;
let password_file = server_registration.finish(RegistrationUpload::deserialize(
@@ -507,7 +528,7 @@ fn test_password_file() -> Result<(), ProtocolError> {
assert_eq!(
hex::encode(parameters.password_file),
hex::encode(password_file.to_bytes())
hex::encode(password_file.serialize())
);
Ok(())
}
@@ -530,11 +551,11 @@ fn test_credential_request() -> Result<(), ProtocolError> {
)?;
assert_eq!(
hex::encode(&parameters.credential_request),
hex::encode(client_login_start_result.message.serialize())
hex::encode(client_login_start_result.message.serialize().unwrap())
);
assert_eq!(
hex::encode(&parameters.client_login_state),
hex::encode(client_login_start_result.state.to_bytes())
hex::encode(client_login_start_result.state.serialize()?)
);
Ok(())
}
@@ -547,8 +568,8 @@ fn test_credential_response() -> Result<(), ProtocolError> {
CycleRng::new([parameters.server_e_sk, parameters.server_nonce].concat());
let server_login_start_result = ServerLogin::<RistrettoSha5123dhNoSlowHash>::start(
&mut server_e_sk_and_nonce_rng,
ServerRegistration::try_from(&parameters.password_file[..])?,
&Key::try_from(&parameters.server_s_sk[..])?,
ServerRegistration::deserialize(&parameters.password_file[..])?,
&Key::from_bytes(&parameters.server_s_sk[..])?,
CredentialRequest::<RistrettoSha5123dhNoSlowHash>::deserialize(
&parameters.credential_request[..],
)?,
@@ -564,11 +585,11 @@ fn test_credential_response() -> Result<(), ProtocolError> {
);
assert_eq!(
hex::encode(&parameters.credential_response),
hex::encode(server_login_start_result.message.serialize())
hex::encode(server_login_start_result.message.serialize().unwrap())
);
assert_eq!(
hex::encode(&parameters.server_login_state),
hex::encode(server_login_start_result.state.to_bytes())
hex::encode(server_login_start_result.state.serialize().unwrap())
);
Ok(())
}
@@ -577,14 +598,15 @@ fn test_credential_response() -> Result<(), ProtocolError> {
fn test_credential_finalization() -> Result<(), ProtocolError> {
let parameters = populate_test_vectors(&serde_json::from_str(TEST_VECTOR).unwrap());
let client_login_finish_result =
ClientLogin::<RistrettoSha5123dhNoSlowHash>::try_from(&parameters.client_login_state[..])?
.finish(
CredentialResponse::<RistrettoSha5123dhNoSlowHash>::deserialize(
&parameters.credential_response[..],
)?,
ClientLoginFinishParameters::WithIdentifiers(parameters.id_u, parameters.id_s),
)?;
let client_login_finish_result = ClientLogin::<RistrettoSha5123dhNoSlowHash>::deserialize(
&parameters.client_login_state[..],
)?
.finish(
CredentialResponse::<RistrettoSha5123dhNoSlowHash>::deserialize(
&parameters.credential_response[..],
)?,
ClientLoginFinishParameters::WithIdentifiers(parameters.id_u, parameters.id_s),
)?;
assert_eq!(
hex::encode(&parameters.einfo2),
@@ -600,7 +622,7 @@ fn test_credential_finalization() -> Result<(), ProtocolError> {
);
assert_eq!(
hex::encode(&parameters.credential_finalization),
hex::encode(client_login_finish_result.message.to_bytes())
hex::encode(client_login_finish_result.message.serialize().unwrap())
);
assert_eq!(
hex::encode(&parameters.export_key),
@@ -614,15 +636,16 @@ fn test_credential_finalization() -> Result<(), ProtocolError> {
fn test_server_login_finish() -> Result<(), ProtocolError> {
let parameters = populate_test_vectors(&serde_json::from_str(TEST_VECTOR).unwrap());
let server_login_result =
ServerLogin::<RistrettoSha5123dhNoSlowHash>::try_from(&parameters.server_login_state[..])?
.finish(CredentialFinalization::try_from(
&parameters.credential_finalization[..],
)?)?;
let server_login_result = ServerLogin::<RistrettoSha5123dhNoSlowHash>::deserialize(
&parameters.server_login_state[..],
)?
.finish(CredentialFinalization::deserialize(
&parameters.credential_finalization[..],
)?)?;
assert_eq!(
hex::encode(parameters.session_key),
hex::encode(server_login_result.session_key)
hex::encode(&server_login_result.session_key)
);
Ok(())
@@ -679,21 +702,18 @@ fn test_complete_flow(
.finish(client_login_finish_result.message)?;
assert_eq!(
hex::encode(server_login_finish_result.session_key),
hex::encode(client_login_finish_result.session_key)
hex::encode(&server_login_finish_result.session_key),
hex::encode(&client_login_finish_result.session_key)
);
assert_eq!(
hex::encode(client_registration_finish_result.export_key),
hex::encode(client_login_finish_result.export_key)
);
} else {
let res = matches!(
client_login_result,
Err(ProtocolError::VerificationError(
PakeError::InvalidLoginError
))
);
assert!(res);
assert!(match client_login_result {
Err(ProtocolError::VerificationError(PakeError::InvalidLoginError)) => true,
_ => false,
});
}
Ok(())
@@ -708,3 +728,413 @@ fn test_complete_flow_success() -> Result<(), ProtocolError> {
fn test_complete_flow_fail() -> Result<(), ProtocolError> {
test_complete_flow(b"good password", b"bad password")
}
// Zeroize tests
#[test]
fn test_zeroize_client_registration_start() -> Result<(), ProtocolError> {
let mut client_rng = OsRng;
let client_registration_start_result =
ClientRegistration::<RistrettoSha5123dhNoSlowHash>::start(
&mut client_rng,
STR_PASSWORD.as_bytes(),
)?;
let mut state = client_registration_start_result.state;
let ptrs = state.as_byte_ptrs();
state.zeroize();
for (ptr, len) in ptrs {
let bytes = unsafe { from_raw_parts(ptr, len) };
assert!(bytes.iter().all(|&x| x == 0));
}
Ok(())
}
#[test]
fn test_zeroize_server_registration_start() -> Result<(), ProtocolError> {
let mut client_rng = OsRng;
let mut server_rng = OsRng;
let server_kp = RistrettoSha5123dhNoSlowHash::generate_random_keypair(&mut server_rng);
let client_registration_start_result =
ClientRegistration::<RistrettoSha5123dhNoSlowHash>::start(
&mut client_rng,
STR_PASSWORD.as_bytes(),
)?;
let server_registration_start_result =
ServerRegistration::<RistrettoSha5123dhNoSlowHash>::start(
&mut server_rng,
client_registration_start_result.message,
server_kp.public(),
)?;
let mut state = server_registration_start_result.state;
let ptrs = state.as_byte_ptrs();
state.zeroize();
for (ptr, len) in ptrs {
let bytes = unsafe { from_raw_parts(ptr, len) };
assert!(bytes.iter().all(|&x| x == 0));
}
Ok(())
}
#[test]
fn test_zeroize_client_registration_finish() -> Result<(), ProtocolError> {
let mut client_rng = OsRng;
let mut server_rng = OsRng;
let server_kp = RistrettoSha5123dhNoSlowHash::generate_random_keypair(&mut server_rng);
let client_registration_start_result =
ClientRegistration::<RistrettoSha5123dhNoSlowHash>::start(
&mut client_rng,
STR_PASSWORD.as_bytes(),
)?;
let server_registration_start_result =
ServerRegistration::<RistrettoSha5123dhNoSlowHash>::start(
&mut server_rng,
client_registration_start_result.message,
server_kp.public(),
)?;
let client_registration_finish_result = client_registration_start_result.state.finish(
&mut client_rng,
server_registration_start_result.message,
ClientRegistrationFinishParameters::default(),
)?;
let mut state = client_registration_finish_result.state;
let ptrs = state.as_byte_ptrs();
state.zeroize();
for (ptr, len) in ptrs {
let bytes = unsafe { from_raw_parts(ptr, len) };
assert!(bytes.iter().all(|&x| x == 0));
}
Ok(())
}
#[test]
fn test_zeroize_server_registration_finish() -> Result<(), ProtocolError> {
let mut client_rng = OsRng;
let mut server_rng = OsRng;
let server_kp = RistrettoSha5123dhNoSlowHash::generate_random_keypair(&mut server_rng);
let client_registration_start_result =
ClientRegistration::<RistrettoSha5123dhNoSlowHash>::start(
&mut client_rng,
STR_PASSWORD.as_bytes(),
)?;
let server_registration_start_result =
ServerRegistration::<RistrettoSha5123dhNoSlowHash>::start(
&mut server_rng,
client_registration_start_result.message,
server_kp.public(),
)?;
let client_registration_finish_result = client_registration_start_result.state.finish(
&mut client_rng,
server_registration_start_result.message,
ClientRegistrationFinishParameters::default(),
)?;
let p_file = server_registration_start_result
.state
.finish(client_registration_finish_result.message)?;
let mut state = p_file;
let ptrs = state.as_byte_ptrs();
state.zeroize();
for (ptr, len) in ptrs {
let bytes = unsafe { from_raw_parts(ptr, len) };
assert!(bytes.iter().all(|&x| x == 0));
}
Ok(())
}
#[test]
fn test_zeroize_client_login_start() -> Result<(), ProtocolError> {
let mut client_rng = OsRng;
let client_login_start_result = ClientLogin::<RistrettoSha5123dhNoSlowHash>::start(
&mut client_rng,
STR_PASSWORD.as_bytes(),
ClientLoginStartParameters::default(),
)?;
let mut state = client_login_start_result.state;
let ptrs = state.as_byte_ptrs();
state.zeroize();
for (ptr, len) in ptrs {
let bytes = unsafe { from_raw_parts(ptr, len) };
assert!(bytes.iter().all(|&x| x == 0));
}
Ok(())
}
#[test]
fn test_zeroize_server_login_start() -> Result<(), ProtocolError> {
let mut client_rng = OsRng;
let mut server_rng = OsRng;
let server_kp = RistrettoSha5123dhNoSlowHash::generate_random_keypair(&mut server_rng);
let client_registration_start_result =
ClientRegistration::<RistrettoSha5123dhNoSlowHash>::start(
&mut client_rng,
STR_PASSWORD.as_bytes(),
)?;
let server_registration_start_result =
ServerRegistration::<RistrettoSha5123dhNoSlowHash>::start(
&mut server_rng,
client_registration_start_result.message,
server_kp.public(),
)?;
let client_registration_finish_result = client_registration_start_result.state.finish(
&mut client_rng,
server_registration_start_result.message,
ClientRegistrationFinishParameters::default(),
)?;
let p_file = server_registration_start_result
.state
.finish(client_registration_finish_result.message)?;
let client_login_start_result = ClientLogin::<RistrettoSha5123dhNoSlowHash>::start(
&mut client_rng,
STR_PASSWORD.as_bytes(),
ClientLoginStartParameters::default(),
)?;
let server_login_start_result = ServerLogin::<RistrettoSha5123dhNoSlowHash>::start(
&mut server_rng,
p_file,
&server_kp.private(),
client_login_start_result.message,
ServerLoginStartParameters::default(),
)?;
let mut state = server_login_start_result.state;
let ptrs = state.as_byte_ptrs();
state.zeroize();
for (ptr, len) in ptrs {
let bytes = unsafe { from_raw_parts(ptr, len) };
assert!(bytes.iter().all(|&x| x == 0));
}
Ok(())
}
#[test]
fn test_zeroize_client_login_finish() -> Result<(), ProtocolError> {
let mut client_rng = OsRng;
let mut server_rng = OsRng;
let server_kp = RistrettoSha5123dhNoSlowHash::generate_random_keypair(&mut server_rng);
let client_registration_start_result =
ClientRegistration::<RistrettoSha5123dhNoSlowHash>::start(
&mut client_rng,
STR_PASSWORD.as_bytes(),
)?;
let server_registration_start_result =
ServerRegistration::<RistrettoSha5123dhNoSlowHash>::start(
&mut server_rng,
client_registration_start_result.message,
server_kp.public(),
)?;
let client_registration_finish_result = client_registration_start_result.state.finish(
&mut client_rng,
server_registration_start_result.message,
ClientRegistrationFinishParameters::default(),
)?;
let p_file = server_registration_start_result
.state
.finish(client_registration_finish_result.message)?;
let client_login_start_result = ClientLogin::<RistrettoSha5123dhNoSlowHash>::start(
&mut client_rng,
STR_PASSWORD.as_bytes(),
ClientLoginStartParameters::default(),
)?;
let server_login_start_result = ServerLogin::<RistrettoSha5123dhNoSlowHash>::start(
&mut server_rng,
p_file,
&server_kp.private(),
client_login_start_result.message,
ServerLoginStartParameters::default(),
)?;
let client_login_finish_result = client_login_start_result.state.finish(
server_login_start_result.message,
ClientLoginFinishParameters::default(),
)?;
let mut state = client_login_finish_result.state;
let ptrs = state.as_byte_ptrs();
state.zeroize();
for (ptr, len) in ptrs {
let bytes = unsafe { from_raw_parts(ptr, len) };
assert!(bytes.iter().all(|&x| x == 0));
}
Ok(())
}
#[test]
fn test_zeroize_server_login_finish() -> Result<(), ProtocolError> {
let mut client_rng = OsRng;
let mut server_rng = OsRng;
let server_kp = RistrettoSha5123dhNoSlowHash::generate_random_keypair(&mut server_rng);
let client_registration_start_result =
ClientRegistration::<RistrettoSha5123dhNoSlowHash>::start(
&mut client_rng,
STR_PASSWORD.as_bytes(),
)?;
let server_registration_start_result =
ServerRegistration::<RistrettoSha5123dhNoSlowHash>::start(
&mut server_rng,
client_registration_start_result.message,
server_kp.public(),
)?;
let client_registration_finish_result = client_registration_start_result.state.finish(
&mut client_rng,
server_registration_start_result.message,
ClientRegistrationFinishParameters::default(),
)?;
let p_file = server_registration_start_result
.state
.finish(client_registration_finish_result.message)?;
let client_login_start_result = ClientLogin::<RistrettoSha5123dhNoSlowHash>::start(
&mut client_rng,
STR_PASSWORD.as_bytes(),
ClientLoginStartParameters::default(),
)?;
let server_login_start_result = ServerLogin::<RistrettoSha5123dhNoSlowHash>::start(
&mut server_rng,
p_file,
&server_kp.private(),
client_login_start_result.message,
ServerLoginStartParameters::default(),
)?;
let client_login_finish_result = client_login_start_result.state.finish(
server_login_start_result.message,
ClientLoginFinishParameters::default(),
)?;
let server_login_finish_result = server_login_start_result
.state
.finish(client_login_finish_result.message)?;
let mut state = server_login_finish_result.state;
let ptrs = state.as_byte_ptrs();
state.zeroize();
for (ptr, len) in ptrs {
let bytes = unsafe { from_raw_parts(ptr, len) };
assert!(bytes.iter().all(|&x| x == 0));
}
Ok(())
}
#[test]
fn test_scalar_always_nonzero() -> Result<(), ProtocolError> {
// Start out with a bunch of zeros to force resampling of scalar
let mut client_registration_rng = CycleRng::new([vec![0u8; 128], vec![1u8; 128]].concat());
let client_registration_start_result =
ClientRegistration::<RistrettoSha5123dhNoSlowHash>::start(
&mut client_registration_rng,
STR_PASSWORD.as_bytes(),
)?;
assert_ne!(
RistrettoPoint::identity(),
client_registration_start_result
.message
.get_alpha_for_testing()
);
Ok(())
}
#[test]
fn test_reflected_value_error_registration() -> Result<(), ProtocolError> {
let password = b"password";
let mut client_rng = OsRng;
let mut server_rng = OsRng;
let server_kp = RistrettoSha5123dhNoSlowHash::generate_random_keypair(&mut server_rng);
let client_registration_start_result =
ClientRegistration::<RistrettoSha5123dhNoSlowHash>::start(&mut client_rng, password)?;
let alpha = client_registration_start_result
.message
.get_alpha_for_testing();
let server_registration_start_result =
ServerRegistration::<RistrettoSha5123dhNoSlowHash>::start(
&mut server_rng,
client_registration_start_result.message,
server_kp.public(),
)?;
let reflected_registration_response = server_registration_start_result
.message
.set_beta_for_testing(alpha);
let client_registration_finish_result = client_registration_start_result.state.finish(
&mut client_rng,
reflected_registration_response,
ClientRegistrationFinishParameters::default(),
);
assert!(match client_registration_finish_result {
Err(ProtocolError::ReflectedValueError) => true,
_ => false,
});
Ok(())
}
#[test]
fn test_reflected_value_error_login() -> Result<(), ProtocolError> {
let password = b"password";
let mut client_rng = OsRng;
let mut server_rng = OsRng;
let server_kp = RistrettoSha5123dhNoSlowHash::generate_random_keypair(&mut server_rng);
let client_registration_start_result =
ClientRegistration::<RistrettoSha5123dhNoSlowHash>::start(&mut client_rng, password)?;
let server_registration_start_result =
ServerRegistration::<RistrettoSha5123dhNoSlowHash>::start(
&mut server_rng,
client_registration_start_result.message,
server_kp.public(),
)?;
let client_registration_finish_result = client_registration_start_result.state.finish(
&mut client_rng,
server_registration_start_result.message,
ClientRegistrationFinishParameters::default(),
)?;
let p_file = server_registration_start_result
.state
.finish(client_registration_finish_result.message)?;
let client_login_start_result = ClientLogin::<RistrettoSha5123dhNoSlowHash>::start(
&mut client_rng,
password,
ClientLoginStartParameters::default(),
)?;
let alpha = client_login_start_result.message.get_alpha_for_testing();
let server_login_start_result = ServerLogin::<RistrettoSha5123dhNoSlowHash>::start(
&mut server_rng,
p_file,
&server_kp.private(),
client_login_start_result.message,
ServerLoginStartParameters::default(),
)?;
let reflected_credential_response = server_login_start_result
.message
.set_beta_for_testing(alpha.clone());
let client_login_result = client_login_start_result.state.finish(
reflected_credential_response,
ClientLoginFinishParameters::default(),
);
assert!(match client_login_result {
Err(ProtocolError::ReflectedValueError) => true,
_ => false,
});
Ok(())
}
+1 -1
View File
@@ -3,7 +3,7 @@
// This source code is licensed under the MIT license found in the
// LICENSE file in the root directory of this source tree.
use rand_core::{CryptoRng, Error, RngCore};
use rand::{CryptoRng, Error, RngCore};
use std::cmp::min;
/// A simple implementation of `RngCore` for testing purposes.
+204 -195
View File
@@ -4,12 +4,17 @@
// LICENSE file in the root directory of this source tree.
use crate::{
ciphersuite::CipherSuite, errors::*, key_exchange::tripledh::TripleDH, keypair::Key, opaque::*,
slow_hash::NoOpHash, tests::mock_rng::CycleRng, *,
ciphersuite::CipherSuite,
errors::*,
key_exchange::tripledh::TripleDH,
keypair::{Key, SizedBytesExt},
opaque::*,
slow_hash::NoOpHash,
tests::mock_rng::CycleRng,
*,
};
use curve25519_dalek::ristretto::RistrettoPoint;
use serde_json::Value;
use std::convert::TryFrom;
// Tests
// =====
@@ -60,198 +65,202 @@ pub struct TestVectorParameters {
pub session_key: Vec<u8>,
}
// Pulled from "OPAQUE-3DH Test Vector 1" and "OPAQUE-3DH Test Vector 6"
// of https://datatracker.ietf.org/doc/draft-irtf-cfrg-opaque/
static TEST_VECTORS: &[&str] = &[
r#"
Group: ristretto255
EnvelopeMode: 01
OPRF: 0001
SlowHash: Identity
Hash: SHA512
server_nonce: a4997137a8fa0d4baf7052a499bf877057f9404e03c889d641a0d7c
807b6a518
oprf_key: 5ed895206bfc53316d307b23e46ecc6623afb3086da74189a416012be03
7e50b
SlowHash: Identity
EnvelopeMode: 01
Group: ristretto255
Nh: 64
Npk: 32
Nsk: 32
password: 436f7272656374486f72736542617474657279537461706c65
blind_login: ed8366feb6b1d05d1f46acb727061e43aadfafe9c10e5a64e7518d63
e3263503
server_private_keyshare: 31587dff30b8001d9d43584decc22e358fa7f9d6e606
29fb1223081c3bae7103
client_nonce: 75a1ad27ab77578bc08b44c4318f09b31d53145c9ba3b42abf0ea08
a781277a6
server_info: 6772656574696e677320616c696365
envelope_nonce: cc7abb200199d5071c94efa49fb62435d3e70d03cf9573a95da54
20d3eebcd2b
client_private_key: 8bcb0b70dac18de24eef12e737d6b28724d3e37774e0b092f
9f70b255defaf04
client_public_key: 360e716c676cfe4d9968d1a352ed3faf17603863e0a7aa1905
df6ea129343b09
server_private_key: f3a0829898a89239dce29ccc98ec8b449a34b255ba1e6f944
829d18e0d589b0f
server_public_key: 66e130c6eb5b41f851b235b03a0eafeaa883f64147bc62cb74
9c22c762389c3c
client_info: 68656c6c6f20626f62
client_private_keyshare: fbbf4ad24119f08a35bf999f8ae0c779ed7b3e266bf3
3f793f6bf9ebf4578005
envelope_nonce: 6c6bb9021b9833c788dd25994d3ce7f3811338744fd6dc556e037
585783444c0
server_keyshare: 82be40ef93bf7c6edd43d4ed9f52fa19827649b819de39c52a22
43e985b75d62
blind_registration: c604c785ada70d77a5256ae21767de8c3304115237d262134
f5e46e512cf8e03
client_public_key: b2d1b10f3741a8efef3139f4d2889f2b11f776b79b9aa2c3ef
caba4f1c4ae861
client_private_key: f0f56cfb488649fe28691dd9aa5dc9ff4c0e6028075baa3c5
615398a2cb12304
client_keyshare: 484e47e31b3132f4ee512e41805a1690891111a7b885bc526198
22c14cabf360
server_public_key: 5442a6f57333a332b4c6f07308f6fa846bde3ed27425820cdc
eeeb935924a903
server_private_key: d63d709e3a739a128929a9f289ff263fdcbc457f2f47f7c43
ccafbbfee72290c
auth_key: 22198da4ad73b1d35cd8bb875e64ce1a9fc2edeb073d760e114d1d7a2f8
6d47411caf1787907e2ff96cd3190b14d101101d74cff234259d9f19a18f2cfe29d0b
server_mac_key: 9de28e2f7107afe266570934c033dd6a403fb2b09a9f1a1357a81
9fb072d25e6651626638d77bd7f0adf4b2b715d0a0bee2ac531fd0f7da699aba6e9ed
717466
envelope: 016c6bb9021b9833c788dd25994d3ce7f3811338744fd6dc556e0375857
83444c00022e9b28440a1dc99cca7513e8eb754d427caf8515642b45a92c73838c043
fc316deeb83deface81589bc8e5c2c767b5ab79218834fbf4fa4f87cf23775aa54633
ed75665cd88f451e044cac1b282d890269476d1ac18ff9a4c0cb832e1143ffa7d447a
prk: 732303f65e76c39f30876aec31af1f5bcd8861626c922baa2e842209c7d5bf2e
024912db2d6cac00b1260c437d34ec588a664a55fc7a0a40251915d2b15d8ec9
client_mac_key: 1536168d48218d08dcbed3438e897d98eff566894240d8d136be0
4ba46b2c788883fd165ce7614c52a6a9c926bf55d249e6c29bc4a23d5a1775ad294a7
858922
pseudorandom_pad: e99274b5cd27d14aeeaf16e7aa8d7e7a03071d58229c5dc96d0
46ed57a761ddccdbc
handshake_encrypt_key: c646def7a8ca75282ac1a0ad15630834cba2772837d7a3
a26d00b36923e55b3f5d065679c715e4c799654f261c865f1a55bc94cabc29a6e2e72
296dfc494a984
handshake_secret: cc887e128c28064106d4101ac3de633e9096e170f2c4a9913d3
50f306274b1665a5b251f761672c12db4c403a615c22cee96adb3539fa62662a17f2e
18cd5fed
registration_response: 1867301bcc67bdf8e640b7d6edcbe2a65488446417b50d
30cdba66ccb379e57200205442a6f57333a332b4c6f07308f6fa846bde3ed27425820
cdceeeb935924a903
export_key: 6ca2c344763e5bc9e3d2bbfe3d982b826b709da597e28e85f9594ec54
2a20c697d55de277ccce1d1af7c48ab7fea1467ac1e3a99c71dcf6326a909d280bd2f
6f
registration_upload: 0020b2d1b10f3741a8efef3139f4d2889f2b11f776b79b9a
a2c3efcaba4f1c4ae861016c6bb9021b9833c788dd25994d3ce7f3811338744fd6dc5
56e037585783444c00022e9b28440a1dc99cca7513e8eb754d427caf8515642b45a92
c73838c043fc316deeb83deface81589bc8e5c2c767b5ab79218834fbf4fa4f87cf23
775aa54633ed75665cd88f451e044cac1b282d890269476d1ac18ff9a4c0cb832e114
3ffa7d447a
registration_request: 241b621c417c0705b5ea7a8b7cdd5039fd61e6b63effe2a
44418164c4d49003e
session_key: ee9f1ef224d498858f6c9b3a121016a38bad7816055c452b1c7edf3d
d439c42a4cc78cbd672e985a20910df14f8f1af4ce5793303ffe6954ff5f1a264e3fd
515
KE3: e771daf28bc5e8068dead67c3db19f9ad03ee919e52f6c7a6e79cf1085bd7448
1e76512c77f37762578eb2faff8fe98e4185ca2d01957216c556d33a6fba3028
KE2: e83812f06568d57b8cdfdcc90fe91454e21bd25dd2a1c32dd1599a2e4a4b6c35
00205442a6f57333a332b4c6f07308f6fa846bde3ed27425820cdceeeb935924a9030
16c6bb9021b9833c788dd25994d3ce7f3811338744fd6dc556e037585783444c00022
e9b28440a1dc99cca7513e8eb754d427caf8515642b45a92c73838c043fc316deeb83
deface81589bc8e5c2c767b5ab79218834fbf4fa4f87cf23775aa54633ed75665cd88
f451e044cac1b282d890269476d1ac18ff9a4c0cb832e1143ffa7d447aa4997137a8f
a0d4baf7052a499bf877057f9404e03c889d641a0d7c807b6a51882be40ef93bf7c6e
dd43d4ed9f52fa19827649b819de39c52a2243e985b75d62000f13aed85ae30aee2f8
9ac5e1c5dd53609b890267ef2d765bb56000bc704c2ba4e4256107befee6655cdb084
64e6c75d8b251642e0d721b3fce38f245568b50e7f6c98825d54cc1a26d6eafadcc1d
344
KE1: b68e0e356f8490fa9c3bed952e16cc02db21eda686b3c484f3d9d912caa41f76
75a1ad27ab77578bc08b44c4318f09b31d53145c9ba3b42abf0ea08a781277a600096
8656c6c6f20626f62484e47e31b3132f4ee512e41805a1690891111a7b885bc526198
22c14cabf360
server_info: 6772656574696e677320616c696365
server_nonce: 98b8081059f60ffed9336f026fd8e124737205ac73f5348ae5bebdb
49456c70f
client_nonce: 58dc21475ff730342f807bf031c7ae47a11f0d4dfaa63a7feb15d7e
36427ca44
server_keyshare: 5214e3ddc73db786480b79fa2da787f2080b82cbe922c2a9592b
44597d9a702e
client_keyshare: a4084c7296b1a3d5a5e4a24358750489575acfd8fcfa6e787492
b98265a5e651
server_private_keyshare: c4d002aa4cfcf281657cf36fe562bc60d9133e0e72a7
4432f685b2b6a4b42a0c
client_private_keyshare: de2e98f422bf7b99be19f7da7cac62f1599d35a225ec
6340149a0aaff3102003
blind_registration: 7e5bcbf82a46109ee0d24e9bcab41fc830a6ce8b82fc1e921
3a043b743b95800
blind_login: c4d5a15f0d5ffc354e340454ec779f575e4573a3886ab5e57e4da298
4bdd5306
oprf_key: 080d0a4d352de92672ab709b1ae1888cb48dfabc2d6ca5b914b335512fe
70508
auth_key: 7bb7f2b831ee30d3e5cc4012c8f721a4d8f9dd494932d53776e043df9bd
2aa284025b8b006fd8449536446ff50698f46c73fccb53f20d80898f185307d1d39e5
prk: b0aefddbb21d1b97bc40c07b172e0bf172ec740de4f6274f69d46350a447e9b1
b3fb1e4cefc7d8e393ff58a5c45c74d0615ee0eecde116f3d4e744142eb2ee89
pseudorandom_pad: 36a828b3b57bf242c4c47ccd9cb84e5b3cefaffe09629c6b94d
eba0ccec5fa39
envelope: 01cc7abb200199d5071c94efa49fb62435d3e70d03cf9573a95da5420d3
eebcd2bbd6323c36fba7fa08a2b6e2aab6efcdc183c4c897d822cf96d29b129932a55
3d469ffa9999fcbd37a1e8b6c1e579bcf83fed355c9ff413e6158d72d16f3ccd8699e
906027842694b6293b6303bbb7f324e0fccb4ae0f01edb60ee1d32992696e
handshake_secret: 2b041dcf12ac9b75dded88f891c25d76746ce9e2c1a43118ac4
aa5721cdc1bc2f0691e6c012a1ea9eb95ab4899b3e7058d37fe9546c46b0511877e40
f55aac6c
handshake_encrypt_key: ceef10f15d869a4cea8174fa98d0d96c7aaf8602d006fe
0c5274a40173db76cac820138c5890bb63fb974d1e3e925850cc2464e2c10f0a9a776
9a45e80889b1e
server_mac_key: f8fd7fdc349b5ae1339515e05912c89a795f561a117cdc84d8d8b
5f05b05751abfb87fa01c799c5d367244d1e32eab67ff926833c6025c556acffa4af1
f3871a
client_mac_key: 92a30cc82c374c06895aa07e81f0cf5f25309a24b595faefcd225
1f9219b47e47d17da4fe8b572dedefa350ed365f87b217973e90d0b647a2ccf1d796a
8970f6
registration_request: ec9027daa5e9a901d641286a7ded51364142936ac7636e1
42e3f4368b4bd8124
registration_response: 8867d7c8c2c576a6322d49d46078ea32f479aed917c70a
636d3ada4397ea1c0e66e130c6eb5b41f851b235b03a0eafeaa883f64147bc62cb749
c22c762389c3c
registration_upload: 360e716c676cfe4d9968d1a352ed3faf17603863e0a7aa19
05df6ea129343b0901cc7abb200199d5071c94efa49fb62435d3e70d03cf9573a95da
5420d3eebcd2bbd6323c36fba7fa08a2b6e2aab6efcdc183c4c897d822cf96d29b129
932a553d469ffa9999fcbd37a1e8b6c1e579bcf83fed355c9ff413e6158d72d16f3cc
d8699e906027842694b6293b6303bbb7f324e0fccb4ae0f01edb60ee1d32992696e
KE1: e06a32011e1b1704eb686b263e5d132fff4e9f6429cd93b98db107485006792c
58dc21475ff730342f807bf031c7ae47a11f0d4dfaa63a7feb15d7e36427ca4400096
8656c6c6f20626f62a4084c7296b1a3d5a5e4a24358750489575acfd8fcfa6e787492
b98265a5e651
KE2: 66f6b5fa1a4eb6bd7a0c93ed2639a31cba0d02e2df744003641d5a30a4a12364
66e130c6eb5b41f851b235b03a0eafeaa883f64147bc62cb749c22c762389c3c01cc7
abb200199d5071c94efa49fb62435d3e70d03cf9573a95da5420d3eebcd2bbd6323c3
6fba7fa08a2b6e2aab6efcdc183c4c897d822cf96d29b129932a553d469ffa9999fcb
d37a1e8b6c1e579bcf83fed355c9ff413e6158d72d16f3ccd8699e906027842694b62
93b6303bbb7f324e0fccb4ae0f01edb60ee1d32992696e98b8081059f60ffed9336f0
26fd8e124737205ac73f5348ae5bebdb49456c70f5214e3ddc73db786480b79fa2da7
87f2080b82cbe922c2a9592b44597d9a702e000f72f38a0945819089c44c86820c51d
89cc35f77df03d330101bbed3b2f69066112f32529bdda0998657350fc9f8da4cde73
408ad931f4c2ea6237ccae4696483388b174f50cf96d439139b0f8680c3b
KE3: 9f0e4f73455ca9fe06bb52ad02670b09be5a03db11a73be4422f19963be082b0
eb55871022e8d1d87adc3ab50de7c738058eb659866d091648f2fed12e23fd53
export_key: 66c0b72aa829f13a166fb1a1168f1e26023921f0eed1126def4f81ba0
4924ad6012e42b63656ec199ba27670d1e7f23dc0a927714edc140134dde5a5d2063d
fc
session_key: 951c2bb1b876725fa7d3829db791dddd406a688507b47e24101bd0cc
5d071760b6fba59e8758a6ea6d7e5f51a715b49a47c50fee9a7c8a0451243c3ee837f
d30
"#,
r#"
Group: ristretto255
EnvelopeMode: 02
OPRF: 0001
SlowHash: Identity
Hash: SHA512
server_nonce: 0f3a6da8b667bc7a383c987586bee749c5f2787691baca68757e78b
6128b0a0f
oprf_key: 89c61a42c8191a5ca41f2fe959843d333bcf43173b7de4c5c119e0e0d8b
0e707
SlowHash: Identity
EnvelopeMode: 02
Group: ristretto255
Nh: 64
Npk: 32
Nsk: 32
client_identity: 20fa92f2e4b7ea5b5e677ac4930ff3b93b0043481ab70bc613b2
e16a6dde6b05
server_identity: eae9dfa6b8348d34418c32d385e1eac99efbce1af320901f7c8e
de8d6d272c65
password: 436f7272656374486f72736542617474657279537461706c65
blind_login: e6d0f1d89ad552e383d6c6f4e8598cc3037d6e274d22da3089e7afbd
4171ea02
server_private_keyshare: 70c944dcb7f4dddde168ecb48dd9488c62b6fc7e9bb4
2a16d291afca9dd25b07
client_nonce: 480917b09c6720680b4a7a0ba9f54b69d870f640a4a7994b47ad07d
1a95c984f
server_info: 6772656574696e677320616c696365
server_identity: 5442a6f57333a332b4c6f07308f6fa846bde3ed27425820cdcee
eb935924a903
envelope_nonce: f41e8b3c5a999aa946f9b562a150e5c5e36748a31a79feb241809
0438877888c
client_private_key: dc70a99bbabf1ebe98b192e93cedceb9c0164e95b891bd8bc
81721b83d66b00b
client_public_key: 20fa92f2e4b7ea5b5e677ac4930ff3b93b0043481ab70bc613
b2e16a6dde6b05
server_private_key: 709687a36c94592ab76579f42ce1be6961f0700496e71df80
6ebd5320554720d
server_public_key: eae9dfa6b8348d34418c32d385e1eac99efbce1af320901f7c
8ede8d6d272c65
client_info: 68656c6c6f20626f62
client_private_keyshare: a4ba6cb7e16ab76eccdb4c0b9261eedd426d7863f00b
fc4a0e09476d3121e70c
envelope_nonce: e38fb444afe3df13ae05e6876d10eca7661196375518eb66d7dee
b8cfb42d13f
server_public_key: 5442a6f57333a332b4c6f07308f6fa846bde3ed27425820cdc
eeeb935924a903
client_identity: b2d1b10f3741a8efef3139f4d2889f2b11f776b79b9aa2c3efca
ba4f1c4ae861
blind_registration: 019cbd1d7420292528f8cdd62f339fdabb602f04a95dac9db
cec831b8c681a09
client_public_key: b2d1b10f3741a8efef3139f4d2889f2b11f776b79b9aa2c3ef
caba4f1c4ae861
client_private_key: f0f56cfb488649fe28691dd9aa5dc9ff4c0e6028075baa3c5
615398a2cb12304
client_keyshare: 4ae7d50bb80cc8f5034d36c1c27edc30caca0983677a941bc0ac
e5e10b18300a
server_keyshare: a2e9e0809b270a1d5c8208f3498a3188538265e6a9e6b274cb38
c4c5d9b1792d
server_private_key: d63d709e3a739a128929a9f289ff263fdcbc457f2f47f7c43
ccafbbfee72290c
auth_key: 4c7c0ae950ad7e9c518266f953d4a01bd2232695f92fb028aa6c124996e
31a205f621305fd4997edf8a5fce04a51252ba8430227c134b81d7093a58e01c70752
server_mac_key: 974bde939ef1d30ba29f9ec2addcaff1eeb105a1f534e04113f9f
0b5c3b9a0797e3fa5f7006e63dfb2b0ce74d002bd1161767c361507bfb1fa0fed5063
1cdcb7
envelope: 02e38fb444afe3df13ae05e6876d10eca7661196375518eb66d7deeb8cf
b42d13f00227ed4654d4452340f26c6304dceeeb1fddb142fcd91b3b26ecd566d7688
095f9478a721e5a592be9dd3bda76ed97421819aca4a30752813223d33bc7ea443be9
76754605116278c7fffda4de142674ed154f540bd3285080637eb4b929e1378336b11
prk: e3bb74ca5f88a95571578e921489e1b6119b438e4efce6e955ae9b6453f24aa4
e3a34fa22bc5f470cfc134ca0784a9cd7df64be46ff3b325fc19f4009979bf2e
client_mac_key: 1c55ecba64d98dc2db8f45faa72b8fdd63cad8677b8665cc575cf
8ad36ef38ec63c9dfc5552007573215983f55d8f5cfa1651da8417e3f27094dde5254
7f5b89
pseudorandom_pad: 7ef495b828a97c896f381824d3371ba012eb63c3f19bb535676
a3b63b18373255ba3
handshake_encrypt_key: 0852fb826109073be6e7c065b6e2d1f872c16f9977c177
cd2f945b26bb933fe6252f226f40483f7aa52f545f801d1e4430f30f80fd42070494e
a77fdcecd6595
handshake_secret: bfd3bfbe451520880975d0e568cbd3b5155b23c02de504fccb9
dd5a8195266cd94d49d040530b3d6b0a585d542eb24da708a2b6f6dc34dee4652d0c1
c62c4e59
registration_response: 088ac01ebf5700f0c96bc2988509343cb7e2dd6f0df820
d0fb807faa11a26f5600205442a6f57333a332b4c6f07308f6fa846bde3ed27425820
cdceeeb935924a903
export_key: 0effa605dc47ba4fe565c423b782b8b6697b26ee2ede7059b0e17510d
f8b11554ce053409671480a56ffbe77b91edc95205c213caeaf9dcb0841790ff834a0
09
registration_upload: 0020b2d1b10f3741a8efef3139f4d2889f2b11f776b79b9a
a2c3efcaba4f1c4ae86102e38fb444afe3df13ae05e6876d10eca7661196375518eb6
6d7deeb8cfb42d13f00227ed4654d4452340f26c6304dceeeb1fddb142fcd91b3b26e
cd566d7688095f9478a721e5a592be9dd3bda76ed97421819aca4a30752813223d33b
c7ea443be976754605116278c7fffda4de142674ed154f540bd3285080637eb4b929e
1378336b11
registration_request: c8d2e9ba503bf3f8821226653314427edb1ec8a3ecc94a5
dfbbe33d59d07b645
session_key: 8dc21ff264f2774de95955d35544ba314e92d07f4a3b32c89ead5e70
c83ac4c0221deadd34ed11d43fc4d3651aec612d696c63979c96bf1ddd1ee44da5d0c
d68
KE3: c178e45ed9b314653685cdbf5f7730e3e40f8652ceb9b10f47d1c784fdd75dd1
07c4ae3f7683de5a692359178c8f13f41a043fc1dcfc14b1fb7cb411514efc6c
KE2: 5079b16709b195b3b63257b419efb752bd0603170160fa72b828ce9ff9209c0c
00205442a6f57333a332b4c6f07308f6fa846bde3ed27425820cdceeeb935924a9030
2e38fb444afe3df13ae05e6876d10eca7661196375518eb66d7deeb8cfb42d13f0022
7ed4654d4452340f26c6304dceeeb1fddb142fcd91b3b26ecd566d7688095f9478a72
1e5a592be9dd3bda76ed97421819aca4a30752813223d33bc7ea443be976754605116
278c7fffda4de142674ed154f540bd3285080637eb4b929e1378336b110f3a6da8b66
7bc7a383c987586bee749c5f2787691baca68757e78b6128b0a0fa2e9e0809b270a1d
5c8208f3498a3188538265e6a9e6b274cb38c4c5d9b1792d000f5a1a0b34573bf728b
14f53485f3bf62fd91154dc9ca01b21945b2204f96adc87bd80e8283ecf3522b6d893
7527f2e9b9782a94fedab0fa304590d1d4f03d72fb1664fca1523a3be95f6c3a97d0a
2ed
KE1: 7024ca0d5423176294fbb9ca968d8ce3fc879a231f1ceef69e672c89e02ded59
480917b09c6720680b4a7a0ba9f54b69d870f640a4a7994b47ad07d1a95c984f00096
8656c6c6f20626f624ae7d50bb80cc8f5034d36c1c27edc30caca0983677a941bc0ac
e5e10b18300a
server_info: 6772656574696e677320616c696365
server_nonce: ef49d83cef5f1411ea30abb82b08bd85423aadb86e2c19df5930b3c
8498b9f97
client_nonce: 4ab1227db632bc079f79c0f5279df2dfa75cfbd4434ab40dcf844d6
77165cd3b
server_keyshare: 96a9587e233e67f2397f10fec6355b68102534f1f1b115b4ddf7
485840efcd7c
client_keyshare: 54f35db3a52fb0cf2a97918a6987993231d227e28711eaef19a3
e5033632611a
server_private_keyshare: 6650d64df70618a878504ce73dcca27b1af125c67e48
1e7bd49d0b24709b200f
client_private_keyshare: ebb01c59f99bc955df622548e247f7ef180732909ff3
c5f87ff8c7867b8be704
blind_registration: 308f1d3fa1fea402f3c90b04601274050a3c6f467387c2f48
878823949b0e109
blind_login: 141e21373228a44b09d4c00da9a6bbaf9a5e54a1687c07f327833643
4245510b
oprf_key: b7126967aa0cb69c311b71343843ea041bae30e2bde41b548b8fbd8bced
97604
auth_key: 9f761a5a56a74269b382403aeba47c1d24b9e200e1839efcb616fb280da
1b6ba7bd71d6455dd3cd979c545608cfbfc1c4e9ba677e1d40848054a00696c4b2589
prk: 30bc3f37a757890ac17ce46043f3c5ed30c96fb8743205e77e84dc167d98e114
6093150ff7b4d002f793bfe717e88d174ed2669abdd9e96af473a7ac82973b0a
pseudorandom_pad: b909f990c94b9c5949a2b8d0874d602f846b7981b331fd79978
b530cc46c8670
envelope: 02f41e8b3c5a999aa946f9b562a150e5c5e36748a31a79feb2418090438
877888c6579500b73f482e7d1132a39bba0ae96447d37140ba040f25f9c72b4f90a36
7bdb425fa1dd4c49e17780f33b821e1e019668fe7f45520e26996ac8cb08e3d2566cc
439c83030464effecb8350e7b1ca31087d87f6a45ed3910c185a24a89d282
handshake_secret: ff89f264f8c3974238f4c8d736af7b0a55f2e4edc487cbf3e5c
7b4bbf21acd7c1d28354c2c8555fba57c4d4b1fbb4b772bfdf909881f67dd517cc9f4
f6ebaeac
handshake_encrypt_key: 3071b181f639062cf70b74d0ffe5ec8fa695da13cd2f00
e74b8b7ef348ae7a5df9c3a32c9f7aeaad5a28379712cf849b9707e221dce124abfad
d0225a8e8e045
server_mac_key: 7c37b344d189cbbeff80bbb4b78e2703d1a80dc28239923094287
62f7ce2a93b11f6e85dd45c02809afe8583d4aad6377e72788773af92eef33c690692
20ae76
client_mac_key: 2c4aff12ba7aa911a51f9e5b7a7c01439d854c97e4b8ec842a9db
d78345760328fd5a72424e49e25ec8fe1b6d9d42f774516f400948bd5a105d995d000
2fc83b
registration_request: 3c8b89966e261a5aaf7aeb6dcdd94c87ce311bf197221b8
7ef44632d58f18a05
registration_response: caf9243d7ef3e267815632bf79c85a27a23f218a438815
2a523f6a310949807beae9dfa6b8348d34418c32d385e1eac99efbce1af320901f7c8
ede8d6d272c65
registration_upload: 20fa92f2e4b7ea5b5e677ac4930ff3b93b0043481ab70bc6
13b2e16a6dde6b0502f41e8b3c5a999aa946f9b562a150e5c5e36748a31a79feb2418
090438877888c6579500b73f482e7d1132a39bba0ae96447d37140ba040f25f9c72b4
f90a367bdb425fa1dd4c49e17780f33b821e1e019668fe7f45520e26996ac8cb08e3d
2566cc439c83030464effecb8350e7b1ca31087d87f6a45ed3910c185a24a89d282
KE1: 8261a1efd78bea73faf256a23c200d729259886530fa43b875c1ca124b09bc7e
4ab1227db632bc079f79c0f5279df2dfa75cfbd4434ab40dcf844d677165cd3b00096
8656c6c6f20626f6254f35db3a52fb0cf2a97918a6987993231d227e28711eaef19a3
e5033632611a
KE2: fa1f33a43a03123ebe35345ef93aa23b57ea8bfbee7022b05a179d60768ba02e
eae9dfa6b8348d34418c32d385e1eac99efbce1af320901f7c8ede8d6d272c6502f41
e8b3c5a999aa946f9b562a150e5c5e36748a31a79feb2418090438877888c6579500b
73f482e7d1132a39bba0ae96447d37140ba040f25f9c72b4f90a367bdb425fa1dd4c4
9e17780f33b821e1e019668fe7f45520e26996ac8cb08e3d2566cc439c83030464eff
ecb8350e7b1ca31087d87f6a45ed3910c185a24a89d282ef49d83cef5f1411ea30abb
82b08bd85423aadb86e2c19df5930b3c8498b9f9796a9587e233e67f2397f10fec635
5b68102534f1f1b115b4ddf7485840efcd7c000f7ebe71d4ab326006a3aeca802435d
c995a38ac6662221f974cb920992d82b8ef8d147c77e29b628a82b5ccb01ea2f7bb60
af94cd1860e1bd974a11a1c9bd827789f663c4758eb71058c244138de0c2
KE3: d81f93397cdba85a43993d4d9afbdc67f147adfa2b223213b19692cb820eef48
5073eda4c8236b2f47702404ad60d9a875d189626fc7b7cc861825385470ae54
export_key: 03192555940b5b42e64e6200bf55cc701f1bace3d402a2f8d83977843
51a1e3fa1f07a471b783b208acb1d92be47903b6fa3a0df9f4d4b7956ee4f431e2950
f6
session_key: 58a7fa98bf3b7b52da21406abfb11d98734354edd47d7b32462c0513
f0617c89824ea6031d4147a86fc9f6c6837ce640c12fb937d764f296d1a9421ad1b2a
5d5
"#,
];
@@ -347,14 +356,14 @@ fn get_password_file_bytes(parameters: &TestVectorParameters) -> Result<Vec<u8>,
ServerRegistration::<Ristretto255Sha512NoSlowHash>::start(
&mut oprf_key_rng,
RegistrationRequest::deserialize(&parameters.registration_request[..]).unwrap(),
&Key::try_from(&parameters.server_public_key[..]).unwrap(),
&Key::from_bytes(&parameters.server_public_key[..]).unwrap(),
)?;
let password_file = server_registration_start_result
.state
.finish(RegistrationUpload::deserialize(&parameters.registration_upload[..]).unwrap())?;
Ok(password_file.to_bytes())
Ok(password_file.serialize())
}
#[test]
@@ -382,7 +391,7 @@ fn test_registration_response() -> Result<(), ProtocolError> {
ServerRegistration::<Ristretto255Sha512NoSlowHash>::start(
&mut oprf_key_rng,
RegistrationRequest::deserialize(&parameters.registration_request[..]).unwrap(),
&Key::try_from(&parameters.server_public_key[..]).unwrap(),
&Key::from_bytes(&parameters.server_public_key[..]).unwrap(),
)?;
assert_eq!(
hex::encode(parameters.registration_response),
@@ -448,7 +457,7 @@ fn test_ke1() -> Result<(), ProtocolError> {
)?;
assert_eq!(
hex::encode(&parameters.KE1),
hex::encode(client_login_start_result.message.serialize())
hex::encode(client_login_start_result.message.serialize()?)
);
}
Ok(())
@@ -463,8 +472,8 @@ fn test_ke2() -> Result<(), ProtocolError> {
CycleRng::new([parameters.server_private_keyshare, parameters.server_nonce].concat());
let server_login_start_result = ServerLogin::<Ristretto255Sha512NoSlowHash>::start(
&mut server_private_keyshare_and_nonce_rng,
ServerRegistration::try_from(&password_file_bytes[..]).unwrap(),
&Key::try_from(&parameters.server_private_key[..]).unwrap(),
ServerRegistration::deserialize(&password_file_bytes[..]).unwrap(),
&Key::from_bytes(&parameters.server_private_key[..]).unwrap(),
CredentialRequest::<Ristretto255Sha512NoSlowHash>::deserialize(&parameters.KE1[..])
.unwrap(),
if parameters.envelope_mode == EnvelopeMode::CustomIdentifier {
@@ -483,7 +492,7 @@ fn test_ke2() -> Result<(), ProtocolError> {
);
assert_eq!(
hex::encode(&parameters.KE2),
hex::encode(server_login_start_result.message.serialize())
hex::encode(server_login_start_result.message.serialize()?)
);
}
Ok(())
@@ -527,7 +536,7 @@ fn test_ke3() -> Result<(), ProtocolError> {
);
assert_eq!(
hex::encode(&parameters.KE3),
hex::encode(client_login_finish_result.message.to_bytes())
hex::encode(client_login_finish_result.message.serialize()?)
);
assert_eq!(
hex::encode(&parameters.export_key),
@@ -546,8 +555,8 @@ fn test_server_login_finish() -> Result<(), ProtocolError> {
CycleRng::new([parameters.server_private_keyshare, parameters.server_nonce].concat());
let server_login_start_result = ServerLogin::<Ristretto255Sha512NoSlowHash>::start(
&mut server_private_keyshare_and_nonce_rng,
ServerRegistration::try_from(&password_file_bytes[..]).unwrap(),
&Key::try_from(&parameters.server_private_key[..]).unwrap(),
ServerRegistration::deserialize(&password_file_bytes[..]).unwrap(),
&Key::from_bytes(&parameters.server_private_key[..]).unwrap(),
CredentialRequest::<Ristretto255Sha512NoSlowHash>::deserialize(&parameters.KE1[..])
.unwrap(),
if parameters.envelope_mode == EnvelopeMode::CustomIdentifier {
@@ -563,11 +572,11 @@ fn test_server_login_finish() -> Result<(), ProtocolError> {
let server_login_result = server_login_start_result
.state
.finish(CredentialFinalization::try_from(&parameters.KE3[..])?)?;
.finish(CredentialFinalization::deserialize(&parameters.KE3[..])?)?;
assert_eq!(
hex::encode(parameters.session_key),
hex::encode(server_login_result.session_key)
hex::encode(&server_login_result.session_key)
);
}
Ok(())
+18 -51
View File
@@ -16,8 +16,6 @@ struct VOPRFTestVectorParameters {
blind: Vec<u8>,
blinded_element: Vec<u8>,
evaluation_element: Vec<u8>,
unblinded_element: Vec<u8>,
info: Vec<u8>,
output: Vec<u8>,
}
@@ -26,26 +24,22 @@ struct VOPRFTestVectorParameters {
static OPRF_RISTRETTO255_SHA512: &'static [&str] = &[
r#"
{
"sksm": "c604c785ada70d77a5256ae21767de8c3304115237d262134f5e46e512cf8e03",
"sksm": "758cbac0e1eb4265d80f6e6489d9a74d788f7ddeda67d7fb3c08b08f44bda30a",
"input": "00",
"blind": "5ed895206bfc53316d307b23e46ecc6623afb3086da74189a416012be037e50b",
"blinded_element": "5cccd309ec729aebe398c53e19c0ab09c24a29f01036960bdad109852e7bdb44",
"evaluation_element": "86bd5eeabf29a87cb4a5c7207cb3ade5297e65f9b74c979bd3551891f4b21515",
"unblinded_element": "3c7f2d901c0d4f245503a186086fbdf5d8b4408432b25c5163e8b5a19c258348",
"info": "4f505246207465737420766563746f7273",
"output": "0bb570873cc0402ca38f1a2c395301f2a3627616e305f2bc54bb08c3f6ea9871eb71074e52e36b90778ba7c3e3429ef7170245c9e01647f3827fdef84d3ba930"
"blind": "c604c785ada70d77a5256ae21767de8c3304115237d262134f5e46e512cf8e03",
"blinded_element": "3c7f2d901c0d4f245503a186086fbdf5d8b4408432b25c5163e8b5a19c258348",
"evaluation_element": "fc6c2b854553bf1ed6674072ed0bde1a9911e02b4bd64aa02cfb428f30251e77",
"output": "d8ed12382086c74564ae19b7a2b5ed9bdc52656d1fc151faaae51aaba86291e8df0b2143a92f24d44d5efd0892e2e26721d27d88745343493634a66d3a925e3a"
}
"#,
r#"
{
"sksm": "c604c785ada70d77a5256ae21767de8c3304115237d262134f5e46e512cf8e03",
"sksm": "758cbac0e1eb4265d80f6e6489d9a74d788f7ddeda67d7fb3c08b08f44bda30a",
"input": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a",
"blind": "ed8366feb6b1d05d1f46acb727061e43aadfafe9c10e5a64e7518d63e3263503",
"blinded_element": "227d63ca69e93bd062193c1e97fff3d5ebf628f646009d77c4e22ba6429be154",
"evaluation_element": "063b91a12e7cbb98dfeb75d8a7eeb83aacf9fd6df7e0b4197466fb77a27fa631",
"unblinded_element": "804ec6774764ed50a0bbad0a5f477aa04df7323acab8f98ca6e468b7790bca4c",
"info": "4f505246207465737420766563746f7273",
"output": "af7cc264dbc96a6b898ba0fa33bfa9e1407bf1dcfbf8772204d470d4458b8f047806679dbfa251f656b906edf9fa638e268adf979bd0e2380a092047d61f9db9"
"blind": "5ed895206bfc53316d307b23e46ecc6623afb3086da74189a416012be037e50b",
"blinded_element": "28a5e797b710f76d20a52507145fbf320a574ec2c8ab0e33e65dd2c277d0ee56",
"evaluation_element": "345e140b707257ae83d4911f7ead3177891e7a62c54097732802c4c7a98ab25a",
"output": "4d5f4221b5ebfd4d1a9dd54830e1ed0bce5a8f30a792723a6fddfe6cfe9f86bb1d95a3725818aeb725eb0b1b52e01ee9a72f47042372ef66c307770054d674fc"
}
"#,
];
@@ -63,15 +57,13 @@ fn populate_test_vectors(values: &Value) -> VOPRFTestVectorParameters {
blind: decode(&values, "blind").unwrap(),
blinded_element: decode(&values, "blinded_element").unwrap(),
evaluation_element: decode(&values, "evaluation_element").unwrap(),
unblinded_element: decode(&values, "unblinded_element").unwrap(),
info: decode(&values, "info").unwrap(),
output: decode(&values, "output").unwrap(),
}
}
// Tests input -> blind, blinded_element
#[test]
fn test_blind() -> Result<(), PakeError> {
fn test_blind() -> Result<(), ProtocolError> {
for tv in OPRF_RISTRETTO255_SHA512 {
let parameters = populate_test_vectors(&serde_json::from_str(tv).unwrap());
let mut rng = CycleRng::new(parameters.blind.to_vec());
@@ -112,44 +104,19 @@ fn test_evaluate() -> Result<(), PakeError> {
Ok(())
}
// Tests sksm, evaluation_element -> evaluation_element
// Tests input, blind, evaluation_element -> output
#[test]
fn test_unblind() -> Result<(), PakeError> {
for tv in OPRF_RISTRETTO255_SHA512 {
let parameters = populate_test_vectors(&serde_json::from_str(tv).unwrap());
let token = oprf::Token {
data: parameters.input,
blind: RistrettoPoint::from_scalar_slice(GenericArray::from_slice(
&parameters.blind[..],
))
.unwrap(),
};
let unblinded_element = oprf::unblind::<RistrettoPoint>(
&token,
RistrettoPoint::from_element_slice(GenericArray::from_slice(
&parameters.evaluation_element,
))
.unwrap(),
);
assert_eq!(&parameters.unblinded_element, &unblinded_element);
}
Ok(())
}
// Tests input, unblinded_element, info -> output
#[test]
fn test_finalize() -> Result<(), PakeError> {
fn test_finalize() -> Result<(), ProtocolError> {
for tv in OPRF_RISTRETTO255_SHA512 {
let parameters = populate_test_vectors(&serde_json::from_str(tv).unwrap());
let output = oprf::finalize::<RistrettoPoint, Sha512>(
&parameters.input,
&parameters.unblinded_element,
&parameters.info,
);
&RistrettoPoint::from_scalar_slice(GenericArray::from_slice(&parameters.blind))?,
RistrettoPoint::from_element_slice(GenericArray::from_slice(
&parameters.evaluation_element,
))?,
)?;
assert_eq!(&parameters.output, &output.to_vec());
}