rc_crypto initial commit (#1349)
This commit is contained in:
Родитель
9d414bb360
Коммит
270b0ea2a0
|
@ -68,9 +68,6 @@ commands:
|
|||
install-clang: # Used by bindgen for nss_sys.
|
||||
steps:
|
||||
- run: sudo apt-get install clang
|
||||
install-system-nss:
|
||||
steps:
|
||||
- run: sudo apt-get install libnss3-dev
|
||||
build-libs:
|
||||
parameters:
|
||||
platform:
|
||||
|
@ -93,8 +90,25 @@ commands:
|
|||
build-desktop-libs:
|
||||
steps:
|
||||
- run: sudo apt-get install tcl
|
||||
- run:
|
||||
name: Install NSS build system dependencies
|
||||
command: sudo apt-get install ninja-build gyp mercurial zlib1g-dev
|
||||
- build-libs:
|
||||
platform: desktop
|
||||
build-ios-libs:
|
||||
steps:
|
||||
- run:
|
||||
name: Install NSS build system dependencies
|
||||
command: |
|
||||
brew install ninja mercurial
|
||||
pushd ..
|
||||
git clone https://chromium.googlesource.com/external/gyp.git
|
||||
pushd gyp
|
||||
python setup.py install
|
||||
popd
|
||||
popd
|
||||
- build-libs:
|
||||
platform: ios
|
||||
test-setup:
|
||||
parameters:
|
||||
rust-version:
|
||||
|
@ -102,7 +116,6 @@ commands:
|
|||
default: "stable"
|
||||
steps:
|
||||
- checkout
|
||||
- install-system-nss
|
||||
- build-desktop-libs
|
||||
- setup-rust-toolchain:
|
||||
rust-version: <<parameters.rust-version>>
|
||||
|
@ -268,8 +281,7 @@ jobs:
|
|||
- setup-rust-toolchain
|
||||
- checkout
|
||||
- setup-sccache
|
||||
- build-libs:
|
||||
platform: ios
|
||||
- build-ios-libs
|
||||
# We do not use the ssccache cache helper commands as
|
||||
# the macOS cache is in a different folder.
|
||||
- restore_cache:
|
||||
|
|
|
@ -4,6 +4,11 @@
|
|||
|
||||
[Full Changelog](https://github.com/mozilla/application-services/compare/v0.33.2...master)
|
||||
|
||||
## General
|
||||
|
||||
- All of our cryptographic primitives are now backed by NSS. This change should be transparent our customers.
|
||||
If you build application-services, it is recommended to delete the `libs/{desktop, ios, android}` folders and start over using `./build-all.sh [android|desktop|ios]`.
|
||||
|
||||
## Places
|
||||
|
||||
### What's New
|
||||
|
@ -19,3 +24,9 @@
|
|||
- Android only: The addition of `acceptResult` to `WritableHistoryConnection` is
|
||||
a breaking change for any custom implementations of `WritableHistoryConnection`
|
||||
([#1332](https://github.com/mozilla/application-services/pull/1332))
|
||||
|
||||
## Push
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
- `OpenSSLError` has been renamed to the more general `CryptoError`.
|
||||
|
|
17
COPYRIGHT
17
COPYRIGHT
|
@ -7,3 +7,20 @@ this repository is subject to the Mozilla Public License, version 2.0
|
|||
<http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
<components/support/ffi/LICENSE-MIT> or <http://opensource.org/licenses/MIT>,
|
||||
at your option.
|
||||
|
||||
- Some code in the rc_crypto crate (`components/support/rc_crypto`) is derived from the
|
||||
ring crate which is under the ISC license, reproduced below:
|
||||
|
||||
Copyright 2015-2017 Brian Smith.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
|
||||
SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
|
||||
OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
|
|
@ -98,15 +98,6 @@ name = "base16"
|
|||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.9.3"
|
||||
|
@ -134,6 +125,28 @@ dependencies = [
|
|||
"serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bindgen"
|
||||
version = "0.49.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cexpr 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"clang-sys 0.28.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"peeking_take_while 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 1.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"shlex 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"which 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.1.0"
|
||||
|
@ -226,6 +239,14 @@ name = "cc"
|
|||
version = "1.0.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "cexpr"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "0.1.9"
|
||||
|
@ -252,6 +273,16 @@ dependencies = [
|
|||
"time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clang-sys"
|
||||
version = "0.28.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libloading 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "2.33.0"
|
||||
|
@ -610,14 +641,6 @@ dependencies = [
|
|||
"termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "error-chain"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"backtrace 0.3.32 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "error-chain"
|
||||
version = "0.12.1"
|
||||
|
@ -786,12 +809,10 @@ dependencies = [
|
|||
"bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cli-support 0.1.0",
|
||||
"dialoguer 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ece 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"error-support 0.1.0",
|
||||
"failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ffi-support 0.3.4",
|
||||
"force-viaduct-reqwest 0.1.0",
|
||||
"hawk 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -800,12 +821,12 @@ dependencies = [
|
|||
"prost 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"prost-build 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"prost-derive 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rc_crypto 0.1.0",
|
||||
"ring 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"sync15 0.1.0",
|
||||
"untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"viaduct 0.1.0",
|
||||
"webbrowser 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -825,6 +846,14 @@ dependencies = [
|
|||
"viaduct 0.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fxhash"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.12.3"
|
||||
|
@ -842,6 +871,11 @@ dependencies = [
|
|||
"libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "glob"
|
||||
version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "h2"
|
||||
version = "0.1.25"
|
||||
|
@ -861,27 +895,13 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "hawk"
|
||||
version = "1.0.5"
|
||||
version = "3.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"base64 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.3.23 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ring 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hawk"
|
||||
version = "2.0.0"
|
||||
source = "git+https://github.com/eoger/rust-hawk?branch=use-openssl#721c7d72fb3c34d855c8563ca453e77f8e1ee9a9"
|
||||
dependencies = [
|
||||
"base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"openssl 0.10.23 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"once_cell 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
|
@ -952,7 +972,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "0.12.31"
|
||||
version = "0.12.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -986,7 +1006,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
dependencies = [
|
||||
"bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hyper 0.12.31 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hyper 0.12.32 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"native-tls 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
@ -1057,6 +1077,15 @@ name = "libc"
|
|||
version = "0.2.59"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libsqlite3-sys"
|
||||
version = "0.15.0"
|
||||
|
@ -1365,6 +1394,38 @@ name = "nodrop"
|
|||
version = "0.1.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "4.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nss"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"error-support 0.1.0",
|
||||
"failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"nss_sys 0.1.0",
|
||||
"serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nss_sys"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bindgen 0.49.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"toml 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.41"
|
||||
|
@ -1403,6 +1464,14 @@ name = "numtoa"
|
|||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "opaque-debug"
|
||||
version = "0.2.2"
|
||||
|
@ -1500,6 +1569,11 @@ dependencies = [
|
|||
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "peeking_take_while"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "1.0.1"
|
||||
|
@ -1719,7 +1793,6 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bincode 1.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ece 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"error-support 0.1.0",
|
||||
"failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -1730,7 +1803,7 @@ dependencies = [
|
|||
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"mockito 0.17.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"openssl 0.10.23 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rc_crypto 0.1.0",
|
||||
"rusqlite 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -1770,15 +1843,6 @@ dependencies = [
|
|||
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.3.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.4.6"
|
||||
|
@ -1968,6 +2032,22 @@ dependencies = [
|
|||
"num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rc_crypto"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ece 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"error-support 0.1.0",
|
||||
"failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hawk 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libsqlite3-sys 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"nss 0.1.0",
|
||||
"openssl-sys 0.9.47 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rc_log_ffi"
|
||||
version = "0.1.0"
|
||||
|
@ -2071,7 +2151,7 @@ dependencies = [
|
|||
"flate2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"http 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hyper 0.12.31 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hyper 0.12.32 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hyper-tls 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"mime 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -2145,11 +2225,6 @@ name = "ryu"
|
|||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "safemem"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "safemem"
|
||||
version = "0.3.0"
|
||||
|
@ -2269,6 +2344,11 @@ dependencies = [
|
|||
"opaque-debug 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "siphasher"
|
||||
version = "0.2.3"
|
||||
|
@ -2383,11 +2463,10 @@ dependencies = [
|
|||
"error-support 0.1.0",
|
||||
"failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ffi-support 0.3.4",
|
||||
"hawk 2.0.0 (git+https://github.com/eoger/rust-hawk?branch=use-openssl)",
|
||||
"interrupt 0.1.0",
|
||||
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"openssl 0.10.23 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rc_crypto 0.1.0",
|
||||
"serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -2623,6 +2702,14 @@ dependencies = [
|
|||
"tokio-executor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "try-lock"
|
||||
version = "0.2.2"
|
||||
|
@ -2893,9 +2980,9 @@ dependencies = [
|
|||
"checksum backtrace-sys 0.1.30 (registry+https://github.com/rust-lang/crates.io-index)" = "5b3a000b9c543553af61bc01cbfc403b04b5caa9e421033866f2e98061eb3e61"
|
||||
"checksum base16 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d27c3610c36aee21ce8ac510e6224498de4228ad772a171ed65643a24693a5a8"
|
||||
"checksum base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e"
|
||||
"checksum base64 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "96434f987501f0ed4eb336a411e0631ecd1afa11574fe148587adc4ff96143c9"
|
||||
"checksum base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643"
|
||||
"checksum bincode 1.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "9f04a5e50dc80b3d5d35320889053637d15011aed5e66b66b37ae798c65da6f7"
|
||||
"checksum bindgen 0.49.2 (registry+https://github.com/rust-lang/crates.io-index)" = "846a1fba6535362a01487ef6b10f0275faa12e5c5d835c5c1c627aabc46ccbd6"
|
||||
"checksum bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d155346769a6855b86399e9bc3814ab343cd3d62c7e985113d46a0ec3c281fd"
|
||||
"checksum blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "5d6d530bdd2d52966a6d03b7a964add7ae1a288d25214066fd4b600f0f796400"
|
||||
"checksum block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b"
|
||||
|
@ -2908,9 +2995,11 @@ dependencies = [
|
|||
"checksum caseless 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "808dab3318747be122cb31d36de18d4d1c81277a76f8332a02b81a3d73463d7f"
|
||||
"checksum cast 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "926013f2860c46252efceabb19f4a6b308197505082c609025aa6706c011d427"
|
||||
"checksum cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)" = "39f75544d7bbaf57560d2168f28fd649ff9c76153874db88bdbdfd839b1a7e7d"
|
||||
"checksum cexpr 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "a7fa24eb00d5ffab90eaeaf1092ac85c04c64aaf358ea6f84505b8116d24c6af"
|
||||
"checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33"
|
||||
"checksum cgmath 0.16.1 (registry+https://github.com/rust-lang/crates.io-index)" = "64a4b57c8f4e3a2e9ac07e0f6abc9c24b6fc9e1b54c3478cfb598f3d0023e51c"
|
||||
"checksum chrono 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "77d81f58b7301084de3b958691458a53c3f7e0b1d702f77e550b6a88e3a88abe"
|
||||
"checksum clang-sys 0.28.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4227269cec09f5f83ff160be12a1e9b0262dd1aa305302d5ba296c2ebd291055"
|
||||
"checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9"
|
||||
"checksum clicolors-control 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "73abfd4c73d003a674ce5d2933fca6ce6c42480ea84a5ffe0a2dc39ed56300f9"
|
||||
"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
|
||||
|
@ -2946,7 +3035,6 @@ dependencies = [
|
|||
"checksum encode_unicode 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "90b2c9496c001e8cb61827acdefad780795c42264c137744cae6f7d9e3450abd"
|
||||
"checksum encoding_rs 0.8.17 (registry+https://github.com/rust-lang/crates.io-index)" = "4155785c79f2f6701f185eb2e6b4caf0555ec03477cb4c70db67b465311620ed"
|
||||
"checksum env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "aafcde04e90a5226a6443b7aabdb016ba2f8307c847d524724bd9b346dd1a2d3"
|
||||
"checksum error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff511d5dc435d703f4971bc399647c9bc38e20cb41452e3b9feb4765419ed3f3"
|
||||
"checksum error-chain 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3ab49e9dcb602294bc42f9a7dfc9bc6e936fca4418ea300dbfb84fe16de0b7d9"
|
||||
"checksum failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "795bd83d3abeb9220f257e597aa0080a508b27533824adf336529648f6abf7e2"
|
||||
"checksum failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea1063915fd7ef4309e222a5a07cf9c319fb9c7836b1f89b85458672dbb127e1"
|
||||
|
@ -2964,11 +3052,12 @@ dependencies = [
|
|||
"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
|
||||
"checksum futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)" = "45dc39533a6cae6da2b56da48edae506bb767ec07370f86f70fc062e9d435869"
|
||||
"checksum futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4"
|
||||
"checksum fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c"
|
||||
"checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec"
|
||||
"checksum getrandom 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "e65cce4e5084b14874c4e7097f38cab54f47ee554f9194673456ea379dcc4c55"
|
||||
"checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb"
|
||||
"checksum h2 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)" = "a539b63339fbbb00e081e84b6e11bd1d9634a82d91da2984a18ac74a8823f392"
|
||||
"checksum hawk 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ccb26fc177705af37f278bb3e31643650176d265cb4d233d651dde0660c17b8c"
|
||||
"checksum hawk 2.0.0 (git+https://github.com/eoger/rust-hawk?branch=use-openssl)" = "<none>"
|
||||
"checksum hawk 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7afdba594720e9250e3d3609ec309f8adb26fa18ce39834aeb6724a30bc2431c"
|
||||
"checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205"
|
||||
"checksum hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77"
|
||||
"checksum hkdf 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1a89c4638cf4e02d9db29750659d2af13d9001b508716f77d4693ec8a1f8bda8"
|
||||
|
@ -2977,7 +3066,7 @@ dependencies = [
|
|||
"checksum http-body 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6741c859c1b2463a423a1dbce98d418e6c3c3fc720fb0d45528657320920292d"
|
||||
"checksum httparse 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9"
|
||||
"checksum humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ca7e5f2e110db35f93b837c81797f3714500b81d517bf20c431b16d3ca4f114"
|
||||
"checksum hyper 0.12.31 (registry+https://github.com/rust-lang/crates.io-index)" = "6481fff8269772d4463253ca83c788104a7305cb3fb9136bc651a6211e46e03f"
|
||||
"checksum hyper 0.12.32 (registry+https://github.com/rust-lang/crates.io-index)" = "a64d71c1e77d39da024f06f5821ee00ad9c38febb90370bad1f07a94e0bc8793"
|
||||
"checksum hyper-tls 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3a800d6aa50af4b5850b2b0f659625ce9504df908e9733b635720483be26174f"
|
||||
"checksum idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e"
|
||||
"checksum indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7e81a7c05f79578dbc15793d8b619db9ba32b4577003ef3af1a91c416798c58d"
|
||||
|
@ -2987,6 +3076,7 @@ dependencies = [
|
|||
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
|
||||
"checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14"
|
||||
"checksum libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)" = "3262021842bf00fe07dbd6cf34ff25c99d7a7ebef8deea84db72be3ea3bb0aff"
|
||||
"checksum libloading 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f2b111a074963af1d37a139918ac6d49ad1d0d5e47f72fd55388619691a7d753"
|
||||
"checksum libsqlite3-sys 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "72b1e07fcc60484f42e246f0cf1f133940c98117c81b2cefcdf71be288069680"
|
||||
"checksum linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83"
|
||||
"checksum lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "62ebf1391f6acad60e5c8b43706dde4582df75c06698ab44511d15016bc2442c"
|
||||
|
@ -3014,11 +3104,13 @@ dependencies = [
|
|||
"checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88"
|
||||
"checksum nix 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6c722bee1037d430d0f8e687bbdbf222f27cc6e4e68d5caf630857bb2b6dbdce"
|
||||
"checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945"
|
||||
"checksum nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6"
|
||||
"checksum num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "b85e541ef8255f6cf42bbfe4ef361305c6c135d10919ecc26126c4e5ae94bc09"
|
||||
"checksum num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31"
|
||||
"checksum num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32"
|
||||
"checksum num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bcef43580c035376c0705c42792c294b66974abbfd2789b511784023f71f3273"
|
||||
"checksum numtoa 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef"
|
||||
"checksum once_cell 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "532c29a261168a45ce28948f9537ddd7a5dd272cc513b3017b1e82a88f962c37"
|
||||
"checksum opaque-debug 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "93f5bb2e8e8dec81642920ccff6b61f1eb94fa3020c5a325c9851ff604152409"
|
||||
"checksum openssl 0.10.23 (registry+https://github.com/rust-lang/crates.io-index)" = "97c140cbb82f3b3468193dd14c1b88def39f341f68257f8a7fe8ed9ed3f628a5"
|
||||
"checksum openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de"
|
||||
|
@ -3029,6 +3121,7 @@ dependencies = [
|
|||
"checksum parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fa7767817701cce701d5585b9c4db3cdd02086398322c1d7e8bf5094a96a2ce7"
|
||||
"checksum parking_lot_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "94c8c7923936b28d546dfd14d4472eaf34c99b14e1c973a32b3e6d4eb04298c9"
|
||||
"checksum parking_lot_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cb88cb1cb3790baa6776844f968fea3be44956cf184fa1be5a03341f5491278c"
|
||||
"checksum peeking_take_while 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
|
||||
"checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831"
|
||||
"checksum petgraph 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)" = "9c3659d1ee90221741f65dd128d9998311b0e40c5d3c23a62445938214abce4f"
|
||||
"checksum phf 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "b3da44b85f8e8dfaec21adae67f95d93244b2ecf6ad2a692320598dcc8e6dd18"
|
||||
|
@ -3047,7 +3140,6 @@ dependencies = [
|
|||
"checksum publicsuffix 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5afecba86dcf1e4fd610246f89899d1924fe12e1e89f555eb7c7f710f3c5ad1d"
|
||||
"checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0"
|
||||
"checksum quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "faf4799c5d274f3868a4aae320a0a182cbd2baee377b378f080e16a23e9d80db"
|
||||
"checksum rand 0.3.23 (registry+https://github.com/rust-lang/crates.io-index)" = "64ac302d8f83c0c1974bf758f6b041c6c8ada916fbb44a609158ca8b064cc76c"
|
||||
"checksum rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293"
|
||||
"checksum rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c618c47cd3ebd209790115ab837de41425723956ad3ce2e6a7f09890947cacb9"
|
||||
"checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca"
|
||||
|
@ -3083,7 +3175,6 @@ dependencies = [
|
|||
"checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda"
|
||||
"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
|
||||
"checksum ryu 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c92464b447c0ee8c4fb3824ecc8383b81717b9f1e74ba2e72540aef7b9f82997"
|
||||
"checksum safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e27a8b19b835f7aea908818e871f5cc3a5a186550c30773be987e155e8163d8f"
|
||||
"checksum safemem 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8dca453248a96cb0749e36ccdfe2b0b4e54a61bfef89fb97ec621eb8e0a93dd9"
|
||||
"checksum same-file 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8f20c4be53a8a1ff4c1f1b2bd14570d2f634628709752f0702ecdd2b3f9a5267"
|
||||
"checksum schannel 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "f2f6abf258d99c3c1c5c2131d99d064e94b7b3dd5f416483057f308fea253339"
|
||||
|
@ -3099,6 +3190,7 @@ dependencies = [
|
|||
"checksum serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)" = "051c49229f282f7c6f3813f8286cc1e3323e8051823fce42c7ea80fe13521704"
|
||||
"checksum serde_urlencoded 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "642dd69105886af2efd227f75a520ec9b44a820d65bc133a9131f7d229fd165a"
|
||||
"checksum sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b4d8bfd0e469f417657573d8451fb33d16cfe0989359b93baf3a1ffc639543d"
|
||||
"checksum shlex 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2"
|
||||
"checksum siphasher 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac"
|
||||
"checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
|
||||
"checksum smallbitvec 2.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b17a69077f717ddbf4945f16c34df16a0a251909c6b7c4a45bccdb4d377bf02b"
|
||||
|
@ -3132,6 +3224,7 @@ dependencies = [
|
|||
"checksum tokio-tcp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1d14b10654be682ac43efee27401d792507e30fd8d26389e1da3b185de2e4119"
|
||||
"checksum tokio-threadpool 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "90ca01319dea1e376a001e8dc192d42ebde6dd532532a5bad988ac37db365b19"
|
||||
"checksum tokio-timer 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "f2106812d500ed25a4f38235b9cae8f78a09edf43203e16e59c3b769a342a60e"
|
||||
"checksum toml 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b8c96d7873fa7ef8bdeb3a9cda3ac48389b4154f32b9803b4bc26220b677b039"
|
||||
"checksum try-lock 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e604eb7b43c06650e854be16a2a03155743d3752dd1c943f6829e26b7a36e382"
|
||||
"checksum try_from 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "283d3b89e1368717881a9d51dad843cc435380d8109c9e47d38780a324698d8b"
|
||||
"checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169"
|
||||
|
|
|
@ -14,6 +14,9 @@ members = [
|
|||
"components/support/ffi",
|
||||
"components/support/force-viaduct-reqwest",
|
||||
"components/support/interrupt",
|
||||
"components/support/rc_crypto",
|
||||
"components/support/rc_crypto/nss",
|
||||
"components/support/rc_crypto/nss/nss_sys",
|
||||
"components/viaduct",
|
||||
"components/sync15",
|
||||
"components/rc_log",
|
||||
|
|
|
@ -1,3 +1,23 @@
|
|||
* Some code in the rc_crypto crate (`components/support/rc_crypto`) is derived from the
|
||||
ring crate which is under the ISC license, reproduced below:
|
||||
|
||||
Copyright 2015-2017 Brian Smith.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
|
||||
SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
|
||||
OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
* The rest of the code in this repository is under Mozilla Public license,
|
||||
reproduced below:
|
||||
|
||||
Mozilla Public License Version 2.0
|
||||
==================================
|
||||
|
|
@ -54,7 +54,15 @@ RUN apt-get update -qq \
|
|||
tclsh \
|
||||
patch \
|
||||
file \
|
||||
libnss3-dev \
|
||||
# NSS build dependencies
|
||||
gyp \
|
||||
ninja-build \
|
||||
zlib1g-dev \
|
||||
# Delete mercurial once `libs/build-all.sh` gets NSS through a zip file.
|
||||
mercurial \
|
||||
# Delete p7zip once NSS windows is actually compiled instead of downloaded.
|
||||
p7zip-full \
|
||||
# End of NSS build dependencies
|
||||
&& apt-get clean
|
||||
|
||||
RUN pip install --upgrade pip
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euvx
|
||||
|
||||
# This script patches our NSS .dylibs to load each other using @rpath instead of
|
||||
# @executable_path which is incorrect.
|
||||
|
||||
# It should be called inside XCode as it reads env variables set by it
|
||||
|
||||
pushd "${TARGET_BUILD_DIR}/${TARGET_NAME}.framework"
|
||||
for binary in *.dylib; do
|
||||
install_name_tool -id "@rpath/${binary}" "${binary}"
|
||||
for lib in *.dylib; do
|
||||
install_name_tool -change "@executable_path/${lib}" "@rpath/${lib}" "${binary}"
|
||||
done
|
||||
done
|
||||
popd
|
21
build.gradle
21
build.gradle
|
@ -226,27 +226,6 @@ subprojects {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: NativeLibsPlugin
|
||||
nativeLibs {
|
||||
libsRoot = "${libsRootDir}/libs"
|
||||
libs {
|
||||
nss {
|
||||
lib "libplc4.*"
|
||||
lib "libplds4.*"
|
||||
lib "libnspr4.*"
|
||||
lib "*freebl3.*"
|
||||
lib "*nss3.*"
|
||||
lib "*nssckbi.*"
|
||||
lib "*nssutil3.*"
|
||||
lib "*softokn3.*"
|
||||
lib "*smime3.*"
|
||||
lib "*sqlite3.*"
|
||||
// We don't need SSL so we might as well save space.
|
||||
// lib "libssl3.*"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Configure some environment variables, per toolchain, that will apply during
|
||||
|
|
|
@ -1,99 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/*
|
||||
* This gradle plugin generates a `copyNativeLibs` task in the project it is
|
||||
* applied to.
|
||||
* This task copies library files in the application-services libs/ folder
|
||||
* inside the build dir nativeLibs/ folder.
|
||||
* It is the responsability of the consumer of this plugin to add `nativeLibs`
|
||||
* to their sourceSets and to depend on that newly defined task,
|
||||
* for example in the `generateDebugAssets` task.
|
||||
* Example of usage:
|
||||
* <pre>
|
||||
* apply plugin: NativeLibsPlugin
|
||||
* nativeLibs {
|
||||
* libsRoot = "${rootProject.rootDir}/libs"
|
||||
* libs {
|
||||
* nss {
|
||||
* lib "libnss3.*" // Wildcards are supported, just like the Copy task `include` method.
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* android {
|
||||
* sourceSets {
|
||||
* main.jniLibs.srcDirs += "$buildDir/nativeLibs/android"
|
||||
* }
|
||||
* }
|
||||
* tasks["generateDebugAssets"].dependsOn(tasks["copyNativeLibs"])
|
||||
* </pre>
|
||||
*/
|
||||
|
||||
import groovy.lang.Closure
|
||||
import org.gradle.api.tasks.Copy
|
||||
import org.gradle.api.NamedDomainObjectContainer
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.kotlin.dsl.delegateClosureOf
|
||||
// Needed to be able to call `DomainObjectCollection.all` instead of Kotlin's built-in `all` method.
|
||||
import kotlin.collections.all as ktAll // ktlint-disable no-unused-imports
|
||||
|
||||
val ARCHS_FOLDERS = arrayOf(
|
||||
"android/armeabi-v7a",
|
||||
"android/arm64-v8a",
|
||||
"android/x86",
|
||||
"android/x86_64",
|
||||
"desktop/linux-x86-64",
|
||||
"desktop/darwin",
|
||||
"desktop/win32-x86-64"
|
||||
)
|
||||
|
||||
data class NativeLib(
|
||||
val name: String,
|
||||
var libs: List<String>
|
||||
) {
|
||||
constructor(name: String) : this(name, listOf<String>())
|
||||
|
||||
public fun lib(libName: String): NativeLib {
|
||||
this.libs += libName
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
||||
const val EXTENSION_NAME = "nativeLibs"
|
||||
open class NativeLibsExtension(nativeLibs: NamedDomainObjectContainer<NativeLib>) {
|
||||
lateinit var libsRoot: String
|
||||
val libs: NamedDomainObjectContainer<NativeLib> = nativeLibs
|
||||
|
||||
fun libs(config: Closure<*>) {
|
||||
this.libs.configure(config)
|
||||
}
|
||||
}
|
||||
|
||||
open class NativeLibsPlugin : Plugin<Project> {
|
||||
override fun apply(project: Project) {
|
||||
with(project) {
|
||||
val nativeLibs = container(NativeLib::class.java)
|
||||
val extension = extensions.create(EXTENSION_NAME, NativeLibsExtension::class.java, nativeLibs)
|
||||
|
||||
nativeLibs.all(delegateClosureOf<NativeLib>({
|
||||
val nativeLib = this
|
||||
afterEvaluate {
|
||||
var copyNativeLibsTask = tasks.maybeCreate("copyNativeLibs")
|
||||
ARCHS_FOLDERS.forEach { archFolder ->
|
||||
val taskName = archFolder.replace("/", "-")
|
||||
val copyLibsTask = tasks.maybeCreate("copyNativeLibs-$taskName", Copy::class.java).apply {
|
||||
from("${extension.libsRoot}/$archFolder/${nativeLib.name}/lib/")
|
||||
into("$buildDir/nativeLibs/$archFolder")
|
||||
nativeLib.libs.forEach {
|
||||
include(it)
|
||||
}
|
||||
}
|
||||
copyNativeLibsTask.dependsOn(copyLibsTask)
|
||||
}
|
||||
}
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -9,27 +9,25 @@ license = "MPL-2.0"
|
|||
base64 = "0.10.1"
|
||||
byteorder = "1.3.2"
|
||||
bytes = "0.4"
|
||||
ece = "1.0.0"
|
||||
failure = "0.1.3"
|
||||
hawk = "1.0.5"
|
||||
hex = "0.3.2"
|
||||
lazy_static = "1.0.0"
|
||||
log = "0.4"
|
||||
openssl = { version = "0.10.23", optional = true }
|
||||
prost = "0.5"
|
||||
prost-derive = "0.5"
|
||||
ring = "0.14.5"
|
||||
serde = { version = "1.0.94", features = ["rc"] }
|
||||
serde_derive = "1.0.94"
|
||||
serde_json = "1.0.40"
|
||||
sync15 = { path = "../sync15" }
|
||||
untrusted = "0.6.2"
|
||||
url = "1.7.1"
|
||||
ffi-support = { path = "../support/ffi" }
|
||||
viaduct = { path = "../viaduct" }
|
||||
rc_crypto = { path = "../support/rc_crypto", features = ["ece"] }
|
||||
error-support = { path = "../support/error" }
|
||||
|
||||
[dev-dependencies]
|
||||
ring = "0.14.5"
|
||||
cli-support = { path = "../support/cli" }
|
||||
force-viaduct-reqwest = { path = "../support/force-viaduct-reqwest" }
|
||||
dialoguer = "0.4.0"
|
||||
|
@ -40,6 +38,6 @@ mockiato = "0.8.0"
|
|||
prost-build = "0.5"
|
||||
|
||||
[features]
|
||||
browserid = ["openssl"]
|
||||
browserid = ["openssl", "rc_crypto/hawk"]
|
||||
reqwest = ["viaduct/reqwest"]
|
||||
default = []
|
||||
|
|
|
@ -151,12 +151,10 @@ afterEvaluate {
|
|||
def buildType = "${variant.buildType.name.capitalize()}"
|
||||
if (variant.buildType.name != 'withoutLib') {
|
||||
tasks["generate${productFlavor}${buildType}Assets"].dependsOn(tasks["cargoBuild"])
|
||||
tasks["generate${productFlavor}${buildType}Assets"].dependsOn(tasks["copyNativeLibs"])
|
||||
}
|
||||
|
||||
// For unit tests.
|
||||
tasks["process${productFlavor}${buildType}UnitTestJavaRes"].dependsOn(tasks["cargoBuild"])
|
||||
tasks["process${productFlavor}${buildType}UnitTestJavaRes"].dependsOn(tasks["copyNativeLibs"])
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,11 +14,11 @@
|
|||
/// contains the tab to send and finally forms the `EncryptedSendTabPayload` that is
|
||||
/// then sent to the target device.
|
||||
use crate::{device::Device, error::*, scoped_keys::ScopedKey, scopes};
|
||||
use ece::{
|
||||
Aes128GcmEceWebPushImpl, EcKeyComponents, LocalKeyPair, LocalKeyPairImpl, RemotePublicKey,
|
||||
RemotePublicKeyImpl, WebPushParams,
|
||||
};
|
||||
use hex;
|
||||
use rc_crypto::ece::{
|
||||
self, Aes128GcmEceWebPushImpl, EcKeyComponents, LocalKeyPair, LocalKeyPairImpl,
|
||||
RemotePublicKey, RemotePublicKeyImpl, WebPushParams,
|
||||
};
|
||||
use serde_derive::*;
|
||||
use sync15::{EncryptedPayload, KeyBundle};
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use failure::Fail;
|
||||
use failure::SyncFailure;
|
||||
use rc_crypto::hawk;
|
||||
use std::string;
|
||||
|
||||
#[derive(Debug, Fail)]
|
||||
|
@ -77,9 +77,6 @@ pub enum ErrorKind {
|
|||
#[fail(display = "Public key computation failed")]
|
||||
PublicKeyComputationFailed,
|
||||
|
||||
#[fail(display = "Key agreement failed")]
|
||||
KeyAgreementFailed,
|
||||
|
||||
#[fail(display = "Remote key and local key mismatch")]
|
||||
MismatchedKeys,
|
||||
|
||||
|
@ -110,9 +107,12 @@ pub enum ErrorKind {
|
|||
info: String,
|
||||
},
|
||||
|
||||
// Basically reimplement error_chain's foreign_links. (Ugh, this sucks)
|
||||
// Basically reimplement error_chain's foreign_links. (Ugh, this sucks).
|
||||
#[fail(display = "Crypto/NSS error: {}", _0)]
|
||||
CryptoError(#[fail(cause)] rc_crypto::Error),
|
||||
|
||||
#[fail(display = "http-ece encryption error: {}", _0)]
|
||||
EceError(#[fail(cause)] ece::Error),
|
||||
EceError(#[fail(cause)] rc_crypto::ece::Error),
|
||||
|
||||
#[fail(display = "Hex decode error: {}", _0)]
|
||||
HexDecodeError(#[fail(cause)] hex::FromHexError),
|
||||
|
@ -143,7 +143,7 @@ pub enum ErrorKind {
|
|||
SyncError(#[fail(cause)] sync15::Error),
|
||||
|
||||
#[fail(display = "HAWK error: {}", _0)]
|
||||
HawkError(#[fail(cause)] SyncFailure<hawk::Error>),
|
||||
HawkError(#[fail(cause)] hawk::Error),
|
||||
|
||||
#[fail(display = "Protobuf decode error: {}", _0)]
|
||||
ProtobufDecodeError(#[fail(cause)] prost::DecodeError),
|
||||
|
@ -151,7 +151,8 @@ pub enum ErrorKind {
|
|||
|
||||
error_support::define_error! {
|
||||
ErrorKind {
|
||||
(EceError, ece::Error),
|
||||
(CryptoError, rc_crypto::Error),
|
||||
(EceError, rc_crypto::ece::Error),
|
||||
(HexDecodeError, hex::FromHexError),
|
||||
(Base64Decode, base64::DecodeError),
|
||||
(JsonError, serde_json::Error),
|
||||
|
@ -164,24 +165,15 @@ error_support::define_error! {
|
|||
}
|
||||
}
|
||||
|
||||
error_support::define_error_conversions! {
|
||||
ErrorKind {
|
||||
(HawkError, hawk::Error),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "browserid")]
|
||||
error_support::define_error_conversions! {
|
||||
ErrorKind {
|
||||
(OpensslError, openssl::error::ErrorStack),
|
||||
}
|
||||
}
|
||||
|
||||
// These can got away when we update to the next version of Hawk,
|
||||
// in https://github.com/mozilla/application-services/pull/1050
|
||||
impl From<hawk::Error> for ErrorKind {
|
||||
#[inline]
|
||||
fn from(e: hawk::Error) -> ErrorKind {
|
||||
ErrorKind::HawkError(SyncFailure::new(e))
|
||||
}
|
||||
}
|
||||
impl From<hawk::Error> for Error {
|
||||
#[inline]
|
||||
fn from(e: hawk::Error) -> Error {
|
||||
ErrorKind::from(e).into()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ use crate::{
|
|||
util::Xorable,
|
||||
Config,
|
||||
};
|
||||
use ring::{digest, hkdf, hmac};
|
||||
use rc_crypto::{digest, hkdf, hmac};
|
||||
#[cfg(feature = "browserid")]
|
||||
use rsa::RSABrowserIDKeyPair;
|
||||
#[cfg(feature = "browserid")]
|
||||
|
@ -216,7 +216,7 @@ pub(crate) fn derive_sync_key(kb: &[u8]) -> Result<Vec<u8>> {
|
|||
#[cfg(feature = "browserid")]
|
||||
pub(crate) fn compute_client_state(kb: &[u8]) -> Result<String> {
|
||||
Ok(hex::encode(
|
||||
digest::digest(&digest::SHA256, &kb).as_ref()[0..16].to_vec(),
|
||||
&digest::digest(&digest::SHA256, &kb)?.as_ref()[0..16],
|
||||
))
|
||||
}
|
||||
|
||||
|
@ -244,8 +244,8 @@ pub(crate) fn derive_hawk_auth_key_from_session_token(session_token: &[u8]) -> R
|
|||
fn derive_hkdf_sha256_key(ikm: &[u8], salt: &[u8], info: &[u8], len: usize) -> Result<Vec<u8>> {
|
||||
let salt = hmac::SigningKey::new(&digest::SHA256, salt);
|
||||
let mut out = vec![0u8; len];
|
||||
hkdf::extract_and_expand(&salt, ikm, info, &mut out);
|
||||
Ok(out.to_vec())
|
||||
hkdf::extract_and_expand(&salt, ikm, info, &mut out)?;
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
#[cfg(feature = "browserid")]
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use crate::error::*;
|
||||
use hawk::{Credentials, Key, PayloadHasher, RequestBuilder, SHA256};
|
||||
use rc_crypto::hawk::{Credentials, Key, PayloadHasher, RequestBuilder, SHA256};
|
||||
use url::Url;
|
||||
use viaduct::{header_names, Method, Request};
|
||||
|
||||
|
@ -18,6 +18,7 @@ pub struct HawkRequestBuilder<'a> {
|
|||
|
||||
impl<'a> HawkRequestBuilder<'a> {
|
||||
pub fn new(method: Method, url: Url, hkdf_sha256_key: &'a [u8]) -> Self {
|
||||
rc_crypto::ensure_initialized();
|
||||
HawkRequestBuilder {
|
||||
url,
|
||||
method,
|
||||
|
@ -39,7 +40,7 @@ impl<'a> HawkRequestBuilder<'a> {
|
|||
let method = format!("{}", self.method);
|
||||
let mut hawk_request_builder = RequestBuilder::from_url(method.as_str(), &self.url)?;
|
||||
if let Some(ref body) = self.body {
|
||||
hash = PayloadHasher::hash("application/json", &SHA256, &body);
|
||||
hash = PayloadHasher::hash("application/json", SHA256, &body)?;
|
||||
hawk_request_builder = hawk_request_builder.hash(&hash[..]);
|
||||
}
|
||||
let hawk_request = hawk_request_builder.request();
|
||||
|
@ -47,7 +48,7 @@ impl<'a> HawkRequestBuilder<'a> {
|
|||
let hmac_key = &self.hkdf_sha256_key[KEY_LENGTH..(2 * KEY_LENGTH)];
|
||||
let hawk_credentials = Credentials {
|
||||
id: token_id,
|
||||
key: Key::new(hmac_key, &SHA256),
|
||||
key: Key::new(hmac_key, SHA256)?,
|
||||
};
|
||||
let header = hawk_request.make_header(&hawk_credentials)?;
|
||||
Ok(format!("Hawk {}", header))
|
||||
|
|
|
@ -17,8 +17,6 @@ use crate::{
|
|||
scoped_keys::ScopedKey,
|
||||
};
|
||||
pub use crate::{config::Config, oauth::AccessTokenInfo, profile::Profile};
|
||||
use lazy_static::lazy_static;
|
||||
use ring::rand::SystemRandom;
|
||||
use serde_derive::*;
|
||||
use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
|
@ -49,10 +47,6 @@ pub mod send_tab;
|
|||
mod state_persistence;
|
||||
mod util;
|
||||
|
||||
lazy_static! {
|
||||
pub static ref RNG: SystemRandom = SystemRandom::new();
|
||||
}
|
||||
|
||||
#[cfg(feature = "browserid")]
|
||||
type FxAClient = dyn http_client::browser_id::FxABrowserIDClient + Sync + Send;
|
||||
#[cfg(not(feature = "browserid"))]
|
||||
|
|
|
@ -6,9 +6,9 @@ use crate::{
|
|||
error::*,
|
||||
http_client::OAuthTokenResponse,
|
||||
scoped_keys::{ScopedKey, ScopedKeysFlow},
|
||||
util, FirefoxAccount, RNG,
|
||||
util, FirefoxAccount,
|
||||
};
|
||||
use ring::digest;
|
||||
use rc_crypto::digest;
|
||||
use serde_derive::*;
|
||||
use std::{
|
||||
collections::HashSet,
|
||||
|
@ -125,9 +125,9 @@ impl FirefoxAccount {
|
|||
|
||||
fn oauth_flow(&mut self, mut url: Url, scopes: &[&str], wants_keys: bool) -> Result<String> {
|
||||
self.clear_access_token_cache();
|
||||
let state = util::random_base64_url_string(&*RNG, 16)?;
|
||||
let code_verifier = util::random_base64_url_string(&*RNG, 43)?;
|
||||
let code_challenge = digest::digest(&digest::SHA256, &code_verifier.as_bytes());
|
||||
let state = util::random_base64_url_string(16)?;
|
||||
let code_verifier = util::random_base64_url_string(43)?;
|
||||
let code_challenge = digest::digest(&digest::SHA256, &code_verifier.as_bytes())?;
|
||||
let code_challenge = base64::encode_config(&code_challenge, base64::URL_SAFE_NO_PAD);
|
||||
url.query_pairs_mut()
|
||||
.append_pair("client_id", &self.state.config.client_id)
|
||||
|
@ -138,7 +138,7 @@ impl FirefoxAccount {
|
|||
.append_pair("code_challenge", &code_challenge)
|
||||
.append_pair("access_type", "offline");
|
||||
let scoped_keys_flow = if wants_keys {
|
||||
let flow = ScopedKeysFlow::with_random_key(&*RNG)?;
|
||||
let flow = ScopedKeysFlow::with_random_key()?;
|
||||
let jwk_json = flow.generate_keys_jwk()?;
|
||||
let keys_jwk = base64::encode_config(&jwk_json, base64::URL_SAFE_NO_PAD);
|
||||
url.query_pairs_mut().append_pair("keys_jwk", &keys_jwk);
|
||||
|
|
|
@ -4,10 +4,13 @@
|
|||
|
||||
use crate::{error::*, FirefoxAccount};
|
||||
use byteorder::{BigEndian, ByteOrder};
|
||||
use ring::{aead, agreement, agreement::EphemeralPrivateKey, digest, rand::SecureRandom};
|
||||
use rc_crypto::{
|
||||
aead, agreement,
|
||||
agreement::{Ephemeral, KeyPair},
|
||||
digest,
|
||||
};
|
||||
use serde_derive::*;
|
||||
use serde_json::{self, json};
|
||||
use untrusted::Input;
|
||||
|
||||
impl FirefoxAccount {
|
||||
pub(crate) fn get_scoped_key(&self, scope: &str) -> Result<&ScopedKey> {
|
||||
|
@ -44,7 +47,7 @@ impl std::fmt::Debug for ScopedKey {
|
|||
}
|
||||
|
||||
pub struct ScopedKeysFlow {
|
||||
private_key: EphemeralPrivateKey,
|
||||
key_pair: KeyPair<Ephemeral>,
|
||||
}
|
||||
|
||||
/// Theorically, everything done in this file could and should be done in a JWT library.
|
||||
|
@ -53,18 +56,22 @@ pub struct ScopedKeysFlow {
|
|||
/// In the past, we chose cjose to do that job, but it added three C dependencies to build and link
|
||||
/// against: jansson, openssl and cjose itself.
|
||||
impl ScopedKeysFlow {
|
||||
pub fn with_random_key(rng: &dyn SecureRandom) -> Result<ScopedKeysFlow> {
|
||||
let private_key = EphemeralPrivateKey::generate(&agreement::ECDH_P256, rng)
|
||||
pub fn with_random_key() -> Result<Self> {
|
||||
let key_pair = KeyPair::<Ephemeral>::generate(&agreement::ECDH_P256)
|
||||
.map_err(|_| ErrorKind::KeyGenerationFailed)?;
|
||||
Ok(ScopedKeysFlow { private_key })
|
||||
Ok(Self { key_pair })
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn from_static_key_pair(key_pair: KeyPair<agreement::Static>) -> Result<Self> {
|
||||
let (private_key, _) = key_pair.split();
|
||||
let ephemeral_prv_key = private_key._tests_only_dangerously_convert_to_ephemeral();
|
||||
let key_pair = KeyPair::from_private_key(ephemeral_prv_key)?;
|
||||
Ok(Self { key_pair })
|
||||
}
|
||||
|
||||
pub fn generate_keys_jwk(&self) -> Result<String> {
|
||||
let pub_key = &self
|
||||
.private_key
|
||||
.compute_public_key()
|
||||
.map_err(|_| ErrorKind::PublicKeyComputationFailed)?;
|
||||
let pub_key_bytes = pub_key.as_ref();
|
||||
let pub_key_bytes = self.key_pair.public_key().to_bytes()?;
|
||||
// Uncompressed form (see SECG SEC1 section 2.3.3).
|
||||
// First byte is 4, then 32 bytes for x, and 32 bytes for y.
|
||||
assert_eq!(pub_key_bytes.len(), 1 + 32 + 32);
|
||||
|
@ -86,66 +93,83 @@ impl ScopedKeysFlow {
|
|||
let segments: Vec<&str> = jwe.split('.').collect();
|
||||
let header = base64::decode_config(&segments[0], base64::URL_SAFE_NO_PAD)?;
|
||||
let protected_header: serde_json::Value = serde_json::from_slice(&header)?;
|
||||
assert_eq!(protected_header["epk"]["kty"], "EC");
|
||||
assert_eq!(protected_header["epk"]["crv"], "P-256");
|
||||
if protected_header["epk"]["kty"] != "EC" {
|
||||
return Err(ErrorKind::UnrecoverableServerError("Only EC keys are supported.").into());
|
||||
}
|
||||
if protected_header["epk"]["crv"] != "P-256" {
|
||||
return Err(
|
||||
ErrorKind::UnrecoverableServerError("Only P-256 curves are supported.").into(),
|
||||
);
|
||||
}
|
||||
let alg = protected_header["enc"]
|
||||
.as_str()
|
||||
.ok_or_else(|| ErrorKind::UnrecoverableServerError("enc is not a string."))?;
|
||||
let apu = protected_header["apu"].as_str().unwrap_or("");
|
||||
let apv = protected_header["apv"].as_str().unwrap_or("");
|
||||
|
||||
// Part 1: Grab the x/y from the other party and construct the secret.
|
||||
let x = base64::decode_config(
|
||||
&protected_header["epk"]["x"].as_str().unwrap(),
|
||||
&protected_header["epk"]["x"]
|
||||
.as_str()
|
||||
.ok_or_else(|| ErrorKind::UnrecoverableServerError("x is not a string."))?,
|
||||
base64::URL_SAFE_NO_PAD,
|
||||
)?;
|
||||
let y = base64::decode_config(
|
||||
&protected_header["epk"]["y"].as_str().unwrap(),
|
||||
&protected_header["epk"]["y"]
|
||||
.as_str()
|
||||
.ok_or_else(|| ErrorKind::UnrecoverableServerError("y is not a string."))?,
|
||||
base64::URL_SAFE_NO_PAD,
|
||||
)?;
|
||||
assert_eq!(x.len(), 256 / 8);
|
||||
assert_eq!(y.len(), 256 / 8);
|
||||
if x.len() != (256 / 8) {
|
||||
return Err(ErrorKind::UnrecoverableServerError("X must be 32 bytes long.").into());
|
||||
}
|
||||
if y.len() != (256 / 8) {
|
||||
return Err(ErrorKind::UnrecoverableServerError("Y must be 32 bytes long.").into());
|
||||
}
|
||||
let mut peer_pub_key: Vec<u8> = vec![0x04];
|
||||
peer_pub_key.extend_from_slice(&x);
|
||||
peer_pub_key.extend_from_slice(&y);
|
||||
let peer_pub_key = Input::from(&peer_pub_key);
|
||||
let secret = agreement::agree_ephemeral(
|
||||
self.private_key,
|
||||
&agreement::ECDH_P256,
|
||||
peer_pub_key,
|
||||
ErrorKind::KeyAgreementFailed,
|
||||
|z| {
|
||||
// ConcatKDF (1 iteration since keyLen <= hashLen).
|
||||
// See rfc7518 section 4.6 for reference.
|
||||
let counter = 1;
|
||||
let alg = protected_header["enc"].as_str().unwrap();
|
||||
let apu = protected_header["apu"].as_str().unwrap_or("");
|
||||
let apv = protected_header["apv"].as_str().unwrap_or("");
|
||||
let mut buf: Vec<u8> = vec![];
|
||||
buf.extend_from_slice(&to_32b_buf(counter));
|
||||
buf.extend_from_slice(&z);
|
||||
// otherinfo
|
||||
buf.extend_from_slice(&to_32b_buf(alg.len() as u32));
|
||||
buf.extend_from_slice(alg.as_bytes());
|
||||
buf.extend_from_slice(&to_32b_buf(apu.len() as u32));
|
||||
buf.extend_from_slice(apu.as_bytes());
|
||||
buf.extend_from_slice(&to_32b_buf(apv.len() as u32));
|
||||
buf.extend_from_slice(apv.as_bytes());
|
||||
buf.extend_from_slice(&to_32b_buf(256));
|
||||
Ok(digest::digest(&digest::SHA256, &buf).as_ref()[0..32].to_vec())
|
||||
},
|
||||
)?;
|
||||
let (private_key, _) = self.key_pair.split();
|
||||
let ikm = private_key.agree(&agreement::ECDH_P256, &peer_pub_key)?;
|
||||
let secret = ikm.derive(|z| {
|
||||
// ConcatKDF (1 iteration since keyLen <= hashLen).
|
||||
// See rfc7518 section 4.6 for reference.
|
||||
let counter = 1;
|
||||
let mut buf: Vec<u8> = vec![];
|
||||
buf.extend_from_slice(&to_32b_buf(counter));
|
||||
buf.extend_from_slice(&z);
|
||||
// otherinfo
|
||||
buf.extend_from_slice(&to_32b_buf(alg.len() as u32));
|
||||
buf.extend_from_slice(alg.as_bytes());
|
||||
buf.extend_from_slice(&to_32b_buf(apu.len() as u32));
|
||||
buf.extend_from_slice(apu.as_bytes());
|
||||
buf.extend_from_slice(&to_32b_buf(apv.len() as u32));
|
||||
buf.extend_from_slice(apv.as_bytes());
|
||||
buf.extend_from_slice(&to_32b_buf(256));
|
||||
digest::digest(&digest::SHA256, &buf)
|
||||
})?;
|
||||
|
||||
// Part 2: decrypt the payload with the obtained secret
|
||||
assert_eq!(segments[1].len(), 0); // Encrypted Key is zero-length.
|
||||
if !segments[1].is_empty() {
|
||||
return Err(
|
||||
ErrorKind::UnrecoverableServerError("The Encrypted Key must be empty.").into(),
|
||||
);
|
||||
}
|
||||
let iv = base64::decode_config(&segments[2], base64::URL_SAFE_NO_PAD)?;
|
||||
let ciphertext = base64::decode_config(&segments[3], base64::URL_SAFE_NO_PAD)?;
|
||||
let auth_tag = base64::decode_config(&segments[4], base64::URL_SAFE_NO_PAD)?;
|
||||
assert_eq!(auth_tag.len(), 128 / 8);
|
||||
assert_eq!(iv.len(), 96 / 8);
|
||||
if auth_tag.len() != (128 / 8) {
|
||||
return Err(
|
||||
ErrorKind::UnrecoverableServerError("The auth tag must be 16 bytes long.").into(),
|
||||
);
|
||||
}
|
||||
let opening_key = aead::OpeningKey::new(&aead::AES_256_GCM, &secret.as_ref())
|
||||
.map_err(|_| ErrorKind::KeyImportFailed)?;
|
||||
let mut in_out = ciphertext.to_vec();
|
||||
in_out.append(&mut auth_tag.to_vec());
|
||||
// We have already asserted that iv is 12 bytes long.
|
||||
let nonce = aead::Nonce::try_assume_unique_for_key(&iv).expect("iv was not 12 bytes long.");
|
||||
let mut ciphertext_and_tag = ciphertext.to_vec();
|
||||
ciphertext_and_tag.extend(&auth_tag.to_vec());
|
||||
let nonce = aead::Nonce::try_assume_unique_for_key(&aead::AES_256_GCM, &iv)?;
|
||||
let aad = aead::Aad::from(segments[0].as_bytes());
|
||||
let plaintext = aead::open_in_place(&opening_key, nonce, aad, 0, &mut in_out)
|
||||
let plaintext = aead::open(&opening_key, nonce, aad, &ciphertext_and_tag)
|
||||
.map_err(|_| ErrorKind::AEADOpenFailure)?;
|
||||
String::from_utf8(plaintext.to_vec()).map_err(Into::into)
|
||||
}
|
||||
|
@ -160,17 +184,30 @@ fn to_32b_buf(n: u32) -> Vec<u8> {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use ring::test::rand::FixedSliceRandom;
|
||||
use rc_crypto::agreement::PrivateKey;
|
||||
|
||||
#[test]
|
||||
fn test_flow() {
|
||||
let fake_rng = FixedSliceRandom {
|
||||
bytes: &[
|
||||
81, 172, 131, 226, 73, 255, 225, 1, 239, 46, 242, 203, 73, 38, 128, 53, 240, 212,
|
||||
167, 208, 28, 66, 119, 80, 187, 244, 232, 133, 2, 168, 202, 127,
|
||||
],
|
||||
};
|
||||
let flow = ScopedKeysFlow::with_random_key(&fake_rng).unwrap();
|
||||
let x = base64::decode_config(
|
||||
"ARvGIPJ5eIFdp6YTM-INVDqwfun2R9FfCUvXbH7QCIU",
|
||||
base64::URL_SAFE_NO_PAD,
|
||||
)
|
||||
.unwrap();
|
||||
let y = base64::decode_config(
|
||||
"hk8gP0Po8nBh-WSiTsvsyesC5c1L6fGOEVuX8FHsvTs",
|
||||
base64::URL_SAFE_NO_PAD,
|
||||
)
|
||||
.unwrap();
|
||||
let d = base64::decode_config(
|
||||
"UayD4kn_4QHvLvLLSSaANfDUp9AcQndQu_TohQKoyn8",
|
||||
base64::URL_SAFE_NO_PAD,
|
||||
)
|
||||
.unwrap();
|
||||
let ec_key =
|
||||
agreement::EcKey::from_coordinates(agreement::Curve::P256, &d, &x, &y).unwrap();
|
||||
let private_key = PrivateKey::<rc_crypto::agreement::Static>::import(&ec_key).unwrap();
|
||||
let key_pair = KeyPair::from(private_key).unwrap();
|
||||
let flow = ScopedKeysFlow::from_static_key_pair(key_pair).unwrap();
|
||||
let json = flow.generate_keys_jwk().unwrap();
|
||||
assert_eq!(json, "{\"crv\":\"P-256\",\"kty\":\"EC\",\"x\":\"ARvGIPJ5eIFdp6YTM-INVDqwfun2R9FfCUvXbH7QCIU\",\"y\":\"hk8gP0Po8nBh-WSiTsvsyesC5c1L6fGOEVuX8FHsvTs\"}");
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use crate::error::*;
|
||||
use ring::rand::SecureRandom;
|
||||
use rc_crypto::rand;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
// Gets the unix epoch in ms.
|
||||
|
@ -21,9 +21,9 @@ pub fn now_secs() -> u64 {
|
|||
since_epoch.as_secs()
|
||||
}
|
||||
|
||||
pub fn random_base64_url_string(rng: &dyn SecureRandom, len: usize) -> Result<String> {
|
||||
pub fn random_base64_url_string(len: usize) -> Result<String> {
|
||||
let mut out = vec![0u8; len];
|
||||
rng.fill(&mut out).map_err(|_| ErrorKind::RngFailure)?;
|
||||
rand::fill(&mut out).map_err(|_| ErrorKind::RngFailure)?;
|
||||
Ok(base64::encode_config(&out, base64::URL_SAFE_NO_PAD))
|
||||
}
|
||||
|
||||
|
|
|
@ -15,9 +15,7 @@ serde_derive = "1.0.94"
|
|||
serde_json = "1.0.40"
|
||||
bincode = "1.1.4"
|
||||
lazy_static = "1.3.0"
|
||||
openssl = "0.10.23"
|
||||
base64 = "0.10.1"
|
||||
ece = "1.0.0"
|
||||
failure = "0.1.5"
|
||||
failure_derive = "0.1.5"
|
||||
log = "0.4.6"
|
||||
|
@ -27,6 +25,7 @@ viaduct = { path = "../viaduct" }
|
|||
ffi-support = { path = "../support/ffi" }
|
||||
sql-support = { path = "../support/sql" }
|
||||
error-support = { path = "../support/error" }
|
||||
rc_crypto = { path = "../support/rc_crypto", features = ["ece"] }
|
||||
|
||||
[dev-dependencies]
|
||||
env_logger = "0.6.2"
|
||||
|
|
|
@ -14,7 +14,7 @@ import com.sun.jna.Structure
|
|||
// Mirror the Rust errors from push/error/lib.rs
|
||||
open class PushError(msg: String) : Exception(msg)
|
||||
open class InternalPanic(msg: String) : PushError(msg)
|
||||
open class OpenSSLError(msg: String) : PushError(msg)
|
||||
open class CryptoError(msg: String) : PushError(msg)
|
||||
open class CommunicationError(msg: String) : PushError(msg)
|
||||
open class CommunicationServerError(msg: String) : PushError(msg)
|
||||
open class AlreadyRegisteredError : PushError(
|
||||
|
@ -24,7 +24,6 @@ open class MissingRegistrationTokenError : PushError(
|
|||
"Missing Registration Token. Please register with OS first.")
|
||||
open class StorageSqlError(msg: String) : PushError(msg)
|
||||
open class TranscodingError(msg: String) : PushError(msg)
|
||||
open class EncryptionError(msg: String) : PushError(msg)
|
||||
open class UrlParseError(msg: String) : PushError(msg)
|
||||
|
||||
/**
|
||||
|
@ -61,7 +60,7 @@ open class RustError : Structure() {
|
|||
}
|
||||
val message = this.consumeErrorMessage()
|
||||
when (code) {
|
||||
24 -> return OpenSSLError(message)
|
||||
24 -> return CryptoError(message)
|
||||
25 -> return CommunicationError(message)
|
||||
26 -> return CommunicationServerError(message)
|
||||
27 -> return AlreadyRegisteredError()
|
||||
|
@ -69,7 +68,6 @@ open class RustError : Structure() {
|
|||
29 -> return StorageSqlError(message)
|
||||
30 -> return MissingRegistrationTokenError()
|
||||
31 -> return TranscodingError(message)
|
||||
32 -> return EncryptionError(message)
|
||||
33 -> return UrlParseError(message)
|
||||
-1 -> return InternalPanic(message)
|
||||
// Note: `1` is used as a generic catch all, but we
|
||||
|
|
|
@ -2,20 +2,12 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
//! Handles cryptographic functions.
|
||||
//!
|
||||
//! Depending on platform, this may call various libraries or have other dependencies.
|
||||
//!
|
||||
//! This uses prime256v1 EC encryption that should come from internal crypto calls. The "application-services"
|
||||
//! module compiles openssl, however, so might be enough to tie into that.
|
||||
|
||||
use crate::error;
|
||||
use ece::{
|
||||
use rc_crypto::ece::{
|
||||
Aes128GcmEceWebPushImpl, AesGcmEceWebPushImpl, AesGcmEncryptedBlock, EcKeyComponents,
|
||||
LocalKeyPair, LocalKeyPairImpl,
|
||||
};
|
||||
use log;
|
||||
use openssl::rand::rand_bytes;
|
||||
use rc_crypto::rand;
|
||||
use serde_derive::*;
|
||||
|
||||
pub const SER_AUTH_LENGTH: usize = 16;
|
||||
|
@ -60,7 +52,7 @@ impl Key {
|
|||
|
||||
pub fn key_pair(&self) -> error::Result<LocalKeyPairImpl> {
|
||||
LocalKeyPairImpl::from_raw_components(&self.p256key).map_err(|e| {
|
||||
error::ErrorKind::EncryptionError(format!(
|
||||
error::ErrorKind::CryptoError(format!(
|
||||
"Could not re-create key from components: {:?}",
|
||||
e
|
||||
))
|
||||
|
@ -111,7 +103,9 @@ pub struct Crypto;
|
|||
|
||||
pub fn get_bytes(size: usize) -> error::Result<Vec<u8>> {
|
||||
let mut bytes = vec![0u8; size];
|
||||
rand_bytes(bytes.as_mut_slice())?;
|
||||
rand::fill(&mut bytes).map_err(|e| {
|
||||
error::ErrorKind::CryptoError(format!("Could not generate random bytes: {:?}", e))
|
||||
})?;
|
||||
Ok(bytes)
|
||||
}
|
||||
|
||||
|
@ -146,10 +140,10 @@ impl Cryptography for Crypto {
|
|||
/// Generate a new cryptographic Key
|
||||
fn generate_key() -> error::Result<Key> {
|
||||
let key = LocalKeyPairImpl::generate_random().map_err(|e| {
|
||||
error::ErrorKind::EncryptionError(format!("Could not generate key: {:?}", e))
|
||||
error::ErrorKind::CryptoError(format!("Could not generate key: {:?}", e))
|
||||
})?;
|
||||
let components = key.raw_components().map_err(|e| {
|
||||
error::ErrorKind::EncryptionError(format!("Could not extract key components: {:?}", e))
|
||||
error::ErrorKind::CryptoError(format!("Could not extract key components: {:?}", e))
|
||||
})?;
|
||||
let auth = get_bytes(SER_AUTH_LENGTH)?;
|
||||
Ok(Key {
|
||||
|
@ -189,9 +183,7 @@ impl Cryptography for Crypto {
|
|||
match encoding.to_lowercase().as_str() {
|
||||
"aesgcm" => Self::decrypt_aesgcm(&key, &d_body, d_salt, d_dh),
|
||||
"aes128gcm" => Self::decrypt_aes128gcm(&key, &d_body),
|
||||
_ => Err(
|
||||
error::ErrorKind::EncryptionError("Unknown Content Encoding".to_string()).into(),
|
||||
),
|
||||
_ => Err(error::ErrorKind::CryptoError("Unknown Content Encoding".to_string()).into()),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -205,21 +197,19 @@ impl Cryptography for Crypto {
|
|||
let dh = match crypto_key {
|
||||
Some(v) => v,
|
||||
None => {
|
||||
return Err(
|
||||
error::ErrorKind::EncryptionError("Missing public key".to_string()).into(),
|
||||
);
|
||||
return Err(error::ErrorKind::CryptoError("Missing public key".to_string()).into());
|
||||
}
|
||||
};
|
||||
let salt = match salt {
|
||||
Some(v) => v,
|
||||
None => {
|
||||
return Err(error::ErrorKind::EncryptionError("Missing salt".to_string()).into());
|
||||
return Err(error::ErrorKind::CryptoError("Missing salt".to_string()).into());
|
||||
}
|
||||
};
|
||||
let block = match AesGcmEncryptedBlock::new(&dh, &salt, 4096, content.to_vec()) {
|
||||
Ok(b) => b,
|
||||
Err(e) => {
|
||||
return Err(error::ErrorKind::EncryptionError(format!(
|
||||
return Err(error::ErrorKind::CryptoError(format!(
|
||||
"Could not create block: {}",
|
||||
e
|
||||
))
|
||||
|
@ -227,12 +217,12 @@ impl Cryptography for Crypto {
|
|||
}
|
||||
};
|
||||
AesGcmEceWebPushImpl::decrypt(&key.key_pair()?, &key.auth, &block)
|
||||
.map_err(|e| error::ErrorKind::OpenSSLError(format!("{:?}", e)).into())
|
||||
.map_err(|_| error::ErrorKind::CryptoError("Decryption error".to_owned()).into())
|
||||
}
|
||||
|
||||
fn decrypt_aes128gcm(key: &Key, content: &[u8]) -> error::Result<Vec<u8>> {
|
||||
Aes128GcmEceWebPushImpl::decrypt(&key.key_pair()?, &key.auth, &content)
|
||||
.map_err(|e| error::ErrorKind::OpenSSLError(format!("{:?}", e)).into())
|
||||
.map_err(|_| error::ErrorKind::CryptoError("Decryption error".to_owned()).into())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,19 +4,6 @@
|
|||
|
||||
use failure::Fail;
|
||||
|
||||
impl Error {
|
||||
pub fn internal(msg: &str) -> Self {
|
||||
ErrorKind::InternalError(msg.to_owned()).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<openssl::error::ErrorStack> for Error {
|
||||
#[inline]
|
||||
fn from(inner: openssl::error::ErrorStack) -> Error {
|
||||
Error::from(ErrorKind::OpenSSLError(format!("{:?}", inner)))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Error> for ffi_support::ExternError {
|
||||
fn from(e: Error) -> ffi_support::ExternError {
|
||||
ffi_support::ExternError::new_error(e.kind().error_code(), format!("{:?}", e))
|
||||
|
@ -36,13 +23,8 @@ pub enum ErrorKind {
|
|||
#[fail(display = "General Error: {:?}", _0)]
|
||||
GeneralError(String),
|
||||
|
||||
/// An unspecifed Internal processing error has occurred
|
||||
#[fail(display = "Internal Error: {:?}", _0)]
|
||||
InternalError(String),
|
||||
|
||||
/// An unknown OpenSSL Cryptography error
|
||||
#[fail(display = "OpenSSL Error: {:?}", _0)]
|
||||
OpenSSLError(String),
|
||||
#[fail(display = "Crypto error: {}", _0)]
|
||||
CryptoError(String),
|
||||
|
||||
/// A Client communication error
|
||||
#[fail(display = "Communication Error: {:?}", _0)]
|
||||
|
@ -70,9 +52,6 @@ pub enum ErrorKind {
|
|||
#[fail(display = "Transcoding Error: {}", _0)]
|
||||
TranscodingError(String),
|
||||
|
||||
#[fail(display = "Encryption Error: {}", _0)]
|
||||
EncryptionError(String),
|
||||
|
||||
/// A failure to parse a URL.
|
||||
#[fail(display = "URL parse error: {:?}", _0)]
|
||||
UrlParseError(#[fail(cause)] url::ParseError),
|
||||
|
@ -84,8 +63,7 @@ impl ErrorKind {
|
|||
pub fn error_code(&self) -> ffi_support::ErrorCode {
|
||||
let code = match self {
|
||||
ErrorKind::GeneralError(_) => 22,
|
||||
ErrorKind::InternalError(_) => 23,
|
||||
ErrorKind::OpenSSLError(_) => 24,
|
||||
ErrorKind::CryptoError(_) => 24,
|
||||
ErrorKind::CommunicationError(_) => 25,
|
||||
ErrorKind::CommunicationServerError(_) => 26,
|
||||
ErrorKind::AlreadyRegisteredError => 27,
|
||||
|
@ -93,7 +71,6 @@ impl ErrorKind {
|
|||
ErrorKind::StorageSqlError(_) => 29,
|
||||
ErrorKind::MissingRegistrationTokenError => 30,
|
||||
ErrorKind::TranscodingError(_) => 31,
|
||||
ErrorKind::EncryptionError(_) => 32,
|
||||
ErrorKind::UrlParseError(_) => 33,
|
||||
};
|
||||
ffi_support::ErrorCode::new(code)
|
||||
|
|
|
@ -155,7 +155,7 @@ impl PushManager {
|
|||
})?;
|
||||
let key = Key::deserialize(&val.key)?;
|
||||
let decrypted = Crypto::decrypt(&key, body, encoding, salt, dh)
|
||||
.map_err(|e| ErrorKind::EncryptionError(format!("{:?}", e)))?;
|
||||
.map_err(|e| ErrorKind::CryptoError(format!("{:?}", e)))?;
|
||||
serde_json::to_string(&decrypted)
|
||||
.map_err(|e| ErrorKind::TranscodingError(format!("{:?}", e)).into())
|
||||
}
|
||||
|
@ -195,7 +195,6 @@ impl PushManager {
|
|||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use openssl::rand::rand_bytes;
|
||||
|
||||
#[test]
|
||||
fn basic() -> Result<()> {
|
||||
|
@ -225,7 +224,7 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn full() -> Result<()> {
|
||||
use ece;
|
||||
use rc_crypto::ece;
|
||||
use serde_json;
|
||||
|
||||
let data_string = b"Mary had a little lamb, with some nice mint jelly";
|
||||
|
@ -240,8 +239,8 @@ mod test {
|
|||
// Act like a subscription provider, so create a "local" key to encrypt the data
|
||||
let mut auth_secret = vec![0u8; 16];
|
||||
let mut salt = vec![0u8; 16];
|
||||
rand_bytes(&mut auth_secret)?;
|
||||
rand_bytes(&mut salt)?;
|
||||
rc_crypto::rand::fill(&mut auth_secret).unwrap();
|
||||
rc_crypto::rand::fill(&mut salt).unwrap();
|
||||
let ciphertext = ece::encrypt(&key.public_key(), &key.auth, &salt, data_string).unwrap();
|
||||
let body = base64::encode_config(&ciphertext, base64::URL_SAFE_NO_PAD);
|
||||
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
[package]
|
||||
name = "rc_crypto"
|
||||
version = "0.1.0"
|
||||
authors = ["application-services@mozilla.com"]
|
||||
edition = "2018"
|
||||
license = "MPL-2.0"
|
||||
|
||||
[lib]
|
||||
crate-type = ["lib"]
|
||||
|
||||
[dependencies]
|
||||
base64 = "0.10.1"
|
||||
failure = "0.1.5"
|
||||
failure_derive = "0.1.5"
|
||||
error-support = { path = "../error" }
|
||||
nss = { path = "nss" }
|
||||
libsqlite3-sys = { version = "0.15.0", features = ["bundled"] }
|
||||
hawk = { version = "3.0.0", default-features = false, optional = true }
|
||||
ece = { version = "1.0.1", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
hex = "0.3.2"
|
||||
openssl-sys = "0.9.46"
|
|
@ -0,0 +1,21 @@
|
|||
# rc_crypto
|
||||
|
||||
The `rc_crypto` crate, like its name implies, handles all of our cryptographic needs.
|
||||
|
||||
For consumers, it pretty much follows the very rust-idiomatic [ring crate API](https://briansmith.org/rustdoc/ring/) and
|
||||
offers the following functionality:
|
||||
|
||||
* Cryptographically secure [pseudorandom number generation](./src/rand.rs).
|
||||
* Cryptographic [digests](./src/digest.rs), [hmac](./src/hmac.rs), and [hkdf](./src/hkdf.rs).
|
||||
* Authenticated encryption ([AEAD](./src/aead.rs)) routines.
|
||||
* ECDH [key agreement](./src/agreement.rs).
|
||||
* Constant-time [string comparison](./src/constant_time.rs).
|
||||
* HTTP [Hawk Authentication](./src/hawk_crypto.rs) through the [rust-hawk crate](https://github.com/taskcluster/rust-hawk/).
|
||||
* HTTP [Encrypted Content-Encoding](./src/ece.rs) through the [ece crate](https://github.com/mozilla/rust-ece).
|
||||
|
||||
Under the hood, it is backed by Mozilla's [NSS](https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS) library,
|
||||
through bindings in the [nss](./nss/) crate. This has a number of advantages for our use-case:
|
||||
|
||||
* Uses Mozilla-owned-and-audited crypto primitives.
|
||||
* Decouples us from ring's fast-moving [versioning and stability
|
||||
policy](https://github.com/briansmith/ring#versioning--stability).
|
|
@ -0,0 +1,18 @@
|
|||
[package]
|
||||
name = "nss"
|
||||
version = "0.1.0"
|
||||
authors = ["application-services@mozilla.com"]
|
||||
edition = "2018"
|
||||
license = "MPL-2.0"
|
||||
|
||||
[lib]
|
||||
crate-type = ["lib"]
|
||||
|
||||
[dependencies]
|
||||
base64 = "0.10.1"
|
||||
error-support = { path = "../../error" }
|
||||
failure = "0.1.5"
|
||||
failure_derive = "0.1.5"
|
||||
nss_sys = { path = "nss_sys" }
|
||||
serde = "1.0.91"
|
||||
serde_derive = "1.0.91"
|
|
@ -0,0 +1,21 @@
|
|||
## nss
|
||||
|
||||
This crate provides various cryptographic routines backed by
|
||||
[NSS](https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS).
|
||||
|
||||
The API is designed to operate at approximately the same level of abstraction as the
|
||||
[`crypto.subtle`](https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto) API, although the details are obviously
|
||||
different given the different host language. It provides:
|
||||
|
||||
* Cryptographically secure [pseudorandom number generation](./src/pk11/slot.rs).
|
||||
* Cryptographic [digests](./src/pk11/context.rs) and [hkdf](./src/pk11/sym_key.rs).
|
||||
* [AES encryption and decryption](./src/aes.rs) in various modes.
|
||||
* Generation, import and export of [elliptic-curve keys](./src/ec.rs).
|
||||
* ECDH [key agreement](./src/ecdh.rs).
|
||||
* Constant-time [string comparison](./src/secport.rs).
|
||||
|
||||
Like the `crypto.subtle` API, these primitives are quite low-level and involve some subtlety in order to use correctly.
|
||||
Consumers should prefer the higher-level abstractions offered by the [rc_crypto](../) crate where possible.
|
||||
|
||||
These features are in turn built on even-lower-level bindings to the raw NSS API, provided by the [nss_sys](./nss_sys)
|
||||
crate.
|
|
@ -0,0 +1,15 @@
|
|||
[package]
|
||||
name = "nss_sys"
|
||||
version = "0.1.0"
|
||||
authors = ["application-services@mozilla.com"]
|
||||
edition = "2018"
|
||||
license = "MPL-2.0"
|
||||
|
||||
[lib]
|
||||
crate-type = ["lib"]
|
||||
|
||||
[build-dependencies]
|
||||
bindgen = "0.49.0"
|
||||
serde = "1.0.91"
|
||||
serde_derive = "1.0.91"
|
||||
toml = "0.5.0"
|
|
@ -0,0 +1,6 @@
|
|||
## nss_sys
|
||||
|
||||
Low-level NSS bindings for Rust.
|
||||
|
||||
Much of the mechanics of the binding definitions are auto-generated from NSS header files using
|
||||
[bindgen](https://docs.rs/bindgen/); see [bindings.toml](./bindings.toml) and [build.rs](./build.rs) for details.
|
|
@ -0,0 +1,135 @@
|
|||
# In this file, every section corresponds to a header file.
|
||||
# A corresponding binding file will be created in $OUT_DIR.
|
||||
|
||||
headers = [
|
||||
"blapit.h",
|
||||
"keyhi.h",
|
||||
"keythi.h",
|
||||
"nss.h",
|
||||
"pk11pub.h",
|
||||
"pkcs11t.h",
|
||||
"prerror.h",
|
||||
"prtypes.h",
|
||||
"secasn1t.h",
|
||||
"seccomon.h",
|
||||
"secoidt.h",
|
||||
]
|
||||
enums = [
|
||||
"KeyType",
|
||||
"PK11ObjectType",
|
||||
"PK11Origin",
|
||||
"SECItemType",
|
||||
"SECOidTag",
|
||||
"SECStatus",
|
||||
]
|
||||
functions = [
|
||||
"NSS_GetVersion",
|
||||
"NSS_InitContext",
|
||||
"NSS_SecureMemcmp",
|
||||
"NSS_VersionCheck",
|
||||
"PK11_CreateContextBySymKey",
|
||||
"PK11_CreateGenericObject",
|
||||
"PK11_Decrypt",
|
||||
"PK11_Derive",
|
||||
"PK11_DestroyContext",
|
||||
"PK11_DestroyGenericObject",
|
||||
"PK11_DigestBegin",
|
||||
"PK11_DigestFinal",
|
||||
"PK11_DigestOp",
|
||||
"PK11_Encrypt",
|
||||
"PK11_ExtractKeyValue",
|
||||
"PK11_FindKeyByKeyID",
|
||||
"PK11_FreeSlot",
|
||||
"PK11_FreeSymKey",
|
||||
"PK11_GenerateKeyPair",
|
||||
"PK11_GenerateRandom",
|
||||
"PK11_GetInternalSlot",
|
||||
"PK11_GetKeyData",
|
||||
"PK11_HashBuf",
|
||||
"PK11_ImportSymKey",
|
||||
"PK11_PubDeriveWithKDF",
|
||||
"PK11_ReadRawAttribute",
|
||||
"PORT_FreeArena",
|
||||
"PR_GetError",
|
||||
"PR_GetErrorText",
|
||||
"PR_GetErrorTextLength",
|
||||
"SECITEM_FreeItem",
|
||||
"SECKEY_ConvertToPublicKey",
|
||||
"SECKEY_CopyPublicKey",
|
||||
"SECKEY_DestroyPrivateKey",
|
||||
"SECKEY_DestroyPublicKey",
|
||||
"SECOID_FindOIDByTag",
|
||||
]
|
||||
types = [
|
||||
"CK_ATTRIBUTE",
|
||||
"CK_ATTRIBUTE_TYPE",
|
||||
"CK_BBOOL",
|
||||
"CK_GCM_PARAMS",
|
||||
"CK_KEY_TYPE",
|
||||
"CK_MECHANISM_TYPE",
|
||||
"CK_NSS_HKDFParams",
|
||||
"CK_OBJECT_CLASS",
|
||||
"KeyType",
|
||||
"NSSInitContext",
|
||||
"NSSInitParameters",
|
||||
"PK11Context",
|
||||
"PK11GenericObject",
|
||||
"PK11ObjectType",
|
||||
"PK11Origin",
|
||||
"PK11SlotInfo",
|
||||
"PK11SymKey",
|
||||
"PLArenaPool",
|
||||
"PRBool",
|
||||
"PRErrorCode",
|
||||
"PRInt32",
|
||||
"PRUint32",
|
||||
"SECItem",
|
||||
"SECKEYPrivateKey",
|
||||
"SECKEYPublicKey",
|
||||
"SECOidData",
|
||||
"SECOidTag",
|
||||
"SECStatus",
|
||||
]
|
||||
opaque = [
|
||||
"NSSInitContext",
|
||||
"NSSInitParameters",
|
||||
"PK11Context",
|
||||
"PK11SlotInfo",
|
||||
"PK11SymKey",
|
||||
]
|
||||
variables = [
|
||||
"AES_BLOCK_SIZE",
|
||||
"CKA_CLASS",
|
||||
"CKA_EC_PARAMS",
|
||||
"CKA_EC_POINT",
|
||||
"CKA_ENCRYPT",
|
||||
"CKA_ID",
|
||||
"CKA_KEY_TYPE",
|
||||
"CKA_PRIVATE",
|
||||
"CKA_SENSITIVE",
|
||||
"CKA_SIGN",
|
||||
"CKA_TOKEN",
|
||||
"CKA_VALUE",
|
||||
"CKA_WRAP",
|
||||
"CKD_NULL",
|
||||
"CKK_EC",
|
||||
"CKM_AES_CBC_PAD",
|
||||
"CKM_AES_GCM",
|
||||
"CKM_ECDH1_DERIVE",
|
||||
"CKM_EC_KEY_PAIR_GEN",
|
||||
"CKM_NSS_HKDF_SHA256",
|
||||
"CKM_SHA256_HMAC",
|
||||
"CKM_SHA512_HMAC",
|
||||
"CKO_PRIVATE_KEY",
|
||||
"CK_INVALID_HANDLE",
|
||||
"EC_POINT_FORM_UNCOMPRESSED",
|
||||
"HASH_LENGTH_MAX",
|
||||
"KU_ALL",
|
||||
"NSS_INIT_FORCEOPEN",
|
||||
"NSS_INIT_NOCERTDB",
|
||||
"NSS_INIT_NOMODDB",
|
||||
"NSS_INIT_OPTIMIZESPACE",
|
||||
"NSS_INIT_READONLY",
|
||||
"SEC_ASN1_OBJECT_ID",
|
||||
"SHA256_LENGTH",
|
||||
]
|
|
@ -0,0 +1,225 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use bindgen::Builder;
|
||||
use serde_derive::Deserialize;
|
||||
use std::{
|
||||
env,
|
||||
ffi::OsString,
|
||||
fs,
|
||||
path::{Path, PathBuf},
|
||||
process::Command,
|
||||
};
|
||||
use toml;
|
||||
|
||||
const BINDINGS_CONFIG: &'static str = "bindings.toml";
|
||||
|
||||
// This is the format of a single section of the configuration file.
|
||||
#[derive(Deserialize)]
|
||||
struct Bindings {
|
||||
// The .h header files to generate from.
|
||||
headers: Vec<String>,
|
||||
// functions that are explicitly included
|
||||
functions: Option<Vec<String>>,
|
||||
// types that are explicitly included
|
||||
types: Option<Vec<String>>,
|
||||
// (un-used) functions that are explicitly included
|
||||
// functions: Option<Vec<String>>,
|
||||
// variables (and `#define`s) that are explicitly included
|
||||
variables: Option<Vec<String>>,
|
||||
// types that should be explicitly marked as opaque
|
||||
opaque: Option<Vec<String>>,
|
||||
// enumerations that are turned into a module (without this, the enum is
|
||||
// mapped using the default, which means that the individual values are
|
||||
// formed with an underscore as <enum_type>_<enum_value_name>).
|
||||
enums: Option<Vec<String>>,
|
||||
|
||||
// Any item that is specifically excluded; if none of the types, functions,
|
||||
// or variables fields are specified, everything defined will be mapped,
|
||||
// so this can be used to limit that.
|
||||
exclude: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
fn env(name: &str) -> Option<OsString> {
|
||||
println!("cargo:rerun-if-env-changed={}", name);
|
||||
env::var_os(name)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// 1. NSS linking.
|
||||
let (lib_dir, include_dir) = get_nss();
|
||||
println!(
|
||||
"cargo:rustc-link-search=native={}",
|
||||
lib_dir.to_string_lossy()
|
||||
);
|
||||
println!("cargo:include={}", include_dir.to_string_lossy());
|
||||
let mut static_libs = vec![
|
||||
"certdb",
|
||||
"certhi",
|
||||
"cryptohi",
|
||||
"freebl_static",
|
||||
"hw-acc-crypto",
|
||||
"nspr4",
|
||||
"nss_static",
|
||||
"nssb",
|
||||
"nssdev",
|
||||
"nsspki",
|
||||
"nssutil",
|
||||
"pk11wrap_static",
|
||||
"plc4",
|
||||
"plds4",
|
||||
"softokn_static",
|
||||
];
|
||||
// Hardware specific libs.
|
||||
let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap();
|
||||
if target_arch == "x86_64" || target_arch == "x86" {
|
||||
static_libs.push("gcm-aes-x86_c_lib");
|
||||
}
|
||||
// Emit -L flags
|
||||
for lib in static_libs {
|
||||
println!("cargo:rustc-link-lib=static={}", lib);
|
||||
}
|
||||
// 2. Bindings.
|
||||
let config_file = PathBuf::from(BINDINGS_CONFIG);
|
||||
println!("cargo:rerun-if-changed={}", config_file.to_str().unwrap());
|
||||
let config = fs::read_to_string(config_file).expect("unable to read binding configuration");
|
||||
let bindings: Bindings = toml::from_str(&config).unwrap();
|
||||
build_bindings(&bindings, &include_dir.join("nss"));
|
||||
}
|
||||
|
||||
fn get_nss() -> (PathBuf, PathBuf) {
|
||||
let nss_dir = env("NSS_DIR").expect("To build nss_sys, NSS_DIR must be set!");
|
||||
let nss_dir = Path::new(&nss_dir);
|
||||
let lib_dir = nss_dir.join("lib");
|
||||
let include_dir = nss_dir.join("include");
|
||||
(lib_dir, include_dir)
|
||||
}
|
||||
|
||||
fn build_bindings(bindings: &Bindings, include_dir: &PathBuf) {
|
||||
let out = PathBuf::from(env::var("OUT_DIR").unwrap()).join("nss_bindings.rs");
|
||||
let mut builder = Builder::default().generate_comments(false);
|
||||
|
||||
for h in bindings.headers.iter().cloned() {
|
||||
builder = builder.header(include_dir.join(h).to_str().unwrap());
|
||||
}
|
||||
|
||||
// Fix our cross-compilation include directories.
|
||||
builder = fix_include_dirs(builder);
|
||||
|
||||
// Apply the configuration.
|
||||
let empty: Vec<String> = vec![];
|
||||
for v in bindings.types.as_ref().unwrap_or_else(|| &empty).iter() {
|
||||
builder = builder.whitelist_type(v);
|
||||
}
|
||||
for v in bindings.functions.as_ref().unwrap_or_else(|| &empty).iter() {
|
||||
builder = builder.whitelist_function(v);
|
||||
}
|
||||
for v in bindings.variables.as_ref().unwrap_or_else(|| &empty).iter() {
|
||||
builder = builder.whitelist_var(v);
|
||||
}
|
||||
for v in bindings.exclude.as_ref().unwrap_or_else(|| &empty).iter() {
|
||||
builder = builder.blacklist_item(v);
|
||||
}
|
||||
for v in bindings.opaque.as_ref().unwrap_or_else(|| &empty).iter() {
|
||||
builder = builder.opaque_type(v);
|
||||
}
|
||||
for v in bindings.enums.as_ref().unwrap_or_else(|| &empty).iter() {
|
||||
builder = builder.constified_enum_module(v);
|
||||
}
|
||||
|
||||
let bindings = builder.generate().expect("unable to generate bindings");
|
||||
bindings
|
||||
.write_to_file(out)
|
||||
.expect("couldn't write bindings");
|
||||
}
|
||||
|
||||
fn fix_include_dirs(mut builder: Builder) -> Builder {
|
||||
let target_os = env::var("CARGO_CFG_TARGET_OS");
|
||||
let target_arch = env::var("CARGO_CFG_TARGET_ARCH");
|
||||
match target_os.as_ref().map(|x| &**x) {
|
||||
Ok("macos") => {
|
||||
// Cheap and dirty way to detect that we are cross-compiling.
|
||||
if env::var_os("CI").is_some() {
|
||||
builder = builder
|
||||
.detect_include_paths(false)
|
||||
.clang_arg("-isysroot/tmp/MacOSX10.11.sdk");
|
||||
}
|
||||
}
|
||||
Ok("windows") => {
|
||||
if env::var_os("CI").is_some() {
|
||||
builder = builder.clang_arg("-D_M_X64");
|
||||
}
|
||||
}
|
||||
Ok("ios") => {
|
||||
let sdk_root;
|
||||
match target_arch.as_ref().map(|x| &**x).unwrap() {
|
||||
"aarch64" => {
|
||||
sdk_root = get_ios_sdk_root("iphoneos");
|
||||
builder = builder.clang_arg("--target=arm64-apple-ios") // See https://github.com/rust-lang/rust-bindgen/issues/1211
|
||||
}
|
||||
"x86_64" => {
|
||||
sdk_root = get_ios_sdk_root("iphonesimulator");
|
||||
}
|
||||
_ => panic!("Unknown iOS architecture."),
|
||||
}
|
||||
builder = builder
|
||||
.detect_include_paths(false)
|
||||
.clang_arg(format!("-isysroot{}", &sdk_root));
|
||||
}
|
||||
Ok("android") => {
|
||||
let (android_api_version, _ndk_root, toolchain_dir) = get_android_env();
|
||||
let mut toolchain = target_arch.as_ref().map(|x| &**x).unwrap();
|
||||
// The other architectures map perfectly to what libs/setup_toolchains_local.sh produces.
|
||||
if toolchain == "aarch64" {
|
||||
toolchain = "arm64";
|
||||
}
|
||||
builder = builder
|
||||
.detect_include_paths(false)
|
||||
.clang_arg(format!(
|
||||
"--sysroot={}",
|
||||
&toolchain_dir
|
||||
.join(format!("{}-{}/sysroot", toolchain, android_api_version))
|
||||
.to_str()
|
||||
.unwrap()
|
||||
))
|
||||
.clang_arg(format!("-D__ANDROID_API__={}", android_api_version))
|
||||
// stddef.h isn't defined otherwise.
|
||||
.clang_arg(format!(
|
||||
"-I{}",
|
||||
toolchain_dir
|
||||
.join(format!(
|
||||
"{}-{}/lib64/clang/5.0/include/",
|
||||
toolchain, android_api_version
|
||||
))
|
||||
.to_str()
|
||||
.unwrap()
|
||||
))
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
|
||||
fn get_android_env() -> (String, PathBuf, PathBuf) {
|
||||
return (
|
||||
// This variable is not mandatory for building yet, so fall back to 21.
|
||||
env::var("ANDROID_NDK_API_VERSION").unwrap_or("21".to_string()),
|
||||
PathBuf::from(env::var("ANDROID_NDK_ROOT").unwrap()),
|
||||
PathBuf::from(env::var("ANDROID_NDK_TOOLCHAIN_DIR").unwrap()),
|
||||
);
|
||||
}
|
||||
|
||||
fn get_ios_sdk_root(sdk_name: &str) -> String {
|
||||
let output = Command::new("xcrun")
|
||||
.arg("--show-sdk-path")
|
||||
.arg("-sdk")
|
||||
.arg(sdk_name)
|
||||
.output()
|
||||
.unwrap();
|
||||
if output.status.success() {
|
||||
String::from_utf8(output.stdout).unwrap().trim().to_string()
|
||||
} else {
|
||||
panic!("Could not get iOS SDK root!")
|
||||
}
|
||||
}
|
|
@ -2,10 +2,4 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
plugins {
|
||||
`kotlin-dsl`
|
||||
}
|
||||
|
||||
repositories {
|
||||
jcenter()
|
||||
}
|
||||
include!(concat!(env!("OUT_DIR"), "/nss_bindings.rs"));
|
|
@ -0,0 +1,24 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#![allow(unknown_lints)]
|
||||
#![warn(rust_2018_idioms)]
|
||||
#![allow(non_camel_case_types, non_upper_case_globals, non_snake_case)]
|
||||
|
||||
#[cfg_attr(feature = "cargo-clippy", allow(clippy::all))]
|
||||
mod bindings;
|
||||
|
||||
pub use bindings::*;
|
||||
|
||||
// Remap some constants.
|
||||
pub const SECSuccess: SECStatus = _SECStatus_SECSuccess;
|
||||
pub const SECFailure: SECStatus = _SECStatus_SECFailure;
|
||||
pub const PR_FALSE: PRBool = 0;
|
||||
pub const PR_TRUE: PRBool = 1;
|
||||
pub const CK_FALSE: CK_BBOOL = 0;
|
||||
pub const CK_TRUE: CK_BBOOL = 1;
|
||||
|
||||
// This is the NSS version that this crate is claiming to be compatible with.
|
||||
// We check it at runtime using `NSS_VersionCheck`.
|
||||
pub const COMPATIBLE_NSS_VERSION: &str = "3.26";
|
|
@ -0,0 +1,114 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use crate::{
|
||||
error::*,
|
||||
pk11::sym_key::import_sym_key,
|
||||
util::{ensure_nss_initialized, map_nss_secstatus, ScopedPtr},
|
||||
};
|
||||
use std::{
|
||||
convert::TryFrom,
|
||||
mem,
|
||||
os::raw::{c_uchar, c_uint},
|
||||
};
|
||||
|
||||
const AES_GCM_TAG_LENGTH: usize = 16;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub enum Operation {
|
||||
Encrypt,
|
||||
Decrypt,
|
||||
}
|
||||
|
||||
pub fn aes_gcm_crypt(
|
||||
key: &[u8],
|
||||
nonce: &[u8],
|
||||
aad: &[u8],
|
||||
data: &[u8],
|
||||
operation: Operation,
|
||||
) -> Result<Vec<u8>> {
|
||||
let mut gcm_params = nss_sys::CK_GCM_PARAMS {
|
||||
pIv: nonce.as_ptr() as nss_sys::CK_BYTE_PTR,
|
||||
ulIvLen: nss_sys::CK_ULONG::try_from(nonce.len())?,
|
||||
pAAD: aad.as_ptr() as nss_sys::CK_BYTE_PTR,
|
||||
ulAADLen: nss_sys::CK_ULONG::try_from(aad.len())?,
|
||||
ulTagBits: nss_sys::CK_ULONG::try_from(AES_GCM_TAG_LENGTH * 8)?,
|
||||
};
|
||||
let mut params = nss_sys::SECItem {
|
||||
type_: nss_sys::SECItemType::siBuffer,
|
||||
data: &mut gcm_params as *mut _ as *mut c_uchar,
|
||||
len: c_uint::try_from(mem::size_of::<nss_sys::CK_GCM_PARAMS>())?,
|
||||
};
|
||||
common_crypt(
|
||||
nss_sys::CKM_AES_GCM.into(),
|
||||
key,
|
||||
data,
|
||||
AES_GCM_TAG_LENGTH,
|
||||
&mut params,
|
||||
operation,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn aes_cbc_crypt(
|
||||
key: &[u8],
|
||||
nonce: &[u8],
|
||||
data: &[u8],
|
||||
operation: Operation,
|
||||
) -> Result<Vec<u8>> {
|
||||
let mut params = nss_sys::SECItem {
|
||||
type_: nss_sys::SECItemType::siBuffer,
|
||||
data: nonce.as_ptr() as *mut c_uchar,
|
||||
len: c_uint::try_from(nonce.len())?,
|
||||
};
|
||||
common_crypt(
|
||||
nss_sys::CKM_AES_CBC_PAD.into(),
|
||||
key,
|
||||
data,
|
||||
usize::try_from(nss_sys::AES_BLOCK_SIZE)?, // CBC mode might pad the result.
|
||||
&mut params,
|
||||
operation,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn common_crypt(
|
||||
mech: nss_sys::CK_MECHANISM_TYPE,
|
||||
key: &[u8],
|
||||
data: &[u8],
|
||||
extra_data_len: usize,
|
||||
params: &mut nss_sys::SECItem,
|
||||
operation: Operation,
|
||||
) -> Result<Vec<u8>> {
|
||||
ensure_nss_initialized();
|
||||
// Most of the following code is inspired by the Firefox WebCrypto implementation:
|
||||
// https://searchfox.org/mozilla-central/rev/f46e2bf881d522a440b30cbf5cf8d76fc212eaf4/dom/crypto/WebCryptoTask.cpp#566
|
||||
// CKA_ENCRYPT always is fine.
|
||||
let sym_key = import_sym_key(mech, nss_sys::CKA_ENCRYPT.into(), &key)?;
|
||||
// Initialize the output buffer (enough space for padding / a full tag).
|
||||
let result_max_len = data
|
||||
.len()
|
||||
.checked_add(extra_data_len)
|
||||
.ok_or_else(|| ErrorKind::InternalError)?;
|
||||
let mut out_len: c_uint = 0;
|
||||
let mut out = vec![0u8; result_max_len];
|
||||
let result_max_len_uint = c_uint::try_from(result_max_len)?;
|
||||
let data_len = c_uint::try_from(data.len())?;
|
||||
let f = match operation {
|
||||
Operation::Decrypt => nss_sys::PK11_Decrypt,
|
||||
Operation::Encrypt => nss_sys::PK11_Encrypt,
|
||||
};
|
||||
map_nss_secstatus(|| unsafe {
|
||||
f(
|
||||
sym_key.as_mut_ptr(),
|
||||
mech,
|
||||
params,
|
||||
out.as_mut_ptr(),
|
||||
&mut out_len,
|
||||
result_max_len_uint,
|
||||
data.as_ptr(),
|
||||
data_len,
|
||||
)
|
||||
})?;
|
||||
out.truncate(usize::try_from(out_len)?);
|
||||
Ok(out)
|
||||
}
|
|
@ -0,0 +1,378 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use crate::{
|
||||
error::*,
|
||||
pk11::{
|
||||
slot,
|
||||
types::{Pkcs11Object, PrivateKey as PK11PrivateKey, PublicKey as PK11PublicKey},
|
||||
},
|
||||
util::{ensure_nss_initialized, sec_item_as_slice, ScopedPtr},
|
||||
};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::{
|
||||
convert::TryFrom,
|
||||
mem,
|
||||
ops::Deref,
|
||||
os::raw::{c_uchar, c_uint, c_void},
|
||||
ptr,
|
||||
};
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq)]
|
||||
#[repr(u8)]
|
||||
pub enum Curve {
|
||||
P256,
|
||||
}
|
||||
const CRV_P256: &str = "P-256";
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
||||
pub struct EcKey {
|
||||
curve: String,
|
||||
// The `d` value of the EC Key.
|
||||
private_key: Vec<u8>,
|
||||
// The uncompressed x,y-representation of the public component of the EC Key.
|
||||
public_key: Vec<u8>,
|
||||
}
|
||||
|
||||
impl EcKey {
|
||||
pub fn new(curve: Curve, private_key: &[u8], public_key: &[u8]) -> Self {
|
||||
let curve = match curve {
|
||||
Curve::P256 => CRV_P256,
|
||||
};
|
||||
Self {
|
||||
curve: curve.to_owned(),
|
||||
private_key: private_key.to_vec(),
|
||||
public_key: public_key.to_vec(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_coordinates(curve: Curve, d: &[u8], x: &[u8], y: &[u8]) -> Result<Self> {
|
||||
let ec_point = create_ec_point_for_coordinates(x, y)?;
|
||||
Ok(EcKey::new(curve, d, &ec_point))
|
||||
}
|
||||
|
||||
pub fn curve(&self) -> Curve {
|
||||
if self.curve == CRV_P256 {
|
||||
return Curve::P256;
|
||||
}
|
||||
unimplemented!("It is impossible to create a curve object with a different CRV.")
|
||||
}
|
||||
|
||||
pub fn public_key(&self) -> &[u8] {
|
||||
&self.public_key
|
||||
}
|
||||
|
||||
pub fn private_key(&self) -> &[u8] {
|
||||
&self.private_key
|
||||
}
|
||||
}
|
||||
|
||||
fn create_ec_point_for_coordinates(x: &[u8], y: &[u8]) -> Result<Vec<u8>> {
|
||||
if x.len() != y.len() {
|
||||
return Err(ErrorKind::InternalError.into());
|
||||
}
|
||||
let mut buf = vec![0u8; x.len() + y.len() + 1];
|
||||
buf[0] = u8::try_from(nss_sys::EC_POINT_FORM_UNCOMPRESSED)?;
|
||||
let mut offset = 1;
|
||||
buf[offset..offset + x.len()].copy_from_slice(x);
|
||||
offset += x.len();
|
||||
buf[offset..offset + y.len()].copy_from_slice(y);
|
||||
Ok(buf)
|
||||
}
|
||||
|
||||
pub fn generate_keypair(curve: Curve) -> Result<(PrivateKey, PublicKey)> {
|
||||
ensure_nss_initialized();
|
||||
// 1. Create EC params
|
||||
let params_buf = create_ec_params_for_curve(curve)?;
|
||||
let mut params = nss_sys::SECItem {
|
||||
type_: nss_sys::SECItemType::siBuffer,
|
||||
data: params_buf.as_ptr() as *mut c_uchar,
|
||||
len: c_uint::try_from(params_buf.len())?,
|
||||
};
|
||||
|
||||
// 2. Generate the key pair
|
||||
// The following code is adapted from:
|
||||
// https://searchfox.org/mozilla-central/rev/f46e2bf881d522a440b30cbf5cf8d76fc212eaf4/dom/crypto/WebCryptoTask.cpp#2389
|
||||
let mech = match curve {
|
||||
Curve::P256 => nss_sys::CKM_EC_KEY_PAIR_GEN,
|
||||
};
|
||||
let slot = slot::get_internal_slot()?;
|
||||
let mut pub_key: *mut nss_sys::SECKEYPublicKey = ptr::null_mut();
|
||||
let prv_key = PrivateKey::from(curve, unsafe {
|
||||
PK11PrivateKey::from_ptr(nss_sys::PK11_GenerateKeyPair(
|
||||
slot.as_mut_ptr(),
|
||||
mech.into(),
|
||||
&mut params as *mut _ as *mut c_void,
|
||||
&mut pub_key,
|
||||
nss_sys::PR_FALSE,
|
||||
nss_sys::PR_FALSE,
|
||||
ptr::null_mut(),
|
||||
))?
|
||||
});
|
||||
let pub_key = PublicKey::from(curve, unsafe { PK11PublicKey::from_ptr(pub_key)? });
|
||||
Ok((prv_key, pub_key))
|
||||
}
|
||||
|
||||
pub struct PrivateKey {
|
||||
curve: Curve,
|
||||
wrapped: PK11PrivateKey,
|
||||
}
|
||||
|
||||
impl Deref for PrivateKey {
|
||||
type Target = PK11PrivateKey;
|
||||
#[inline]
|
||||
fn deref(&self) -> &PK11PrivateKey {
|
||||
&self.wrapped
|
||||
}
|
||||
}
|
||||
|
||||
impl PrivateKey {
|
||||
pub fn convert_to_public_key(&self) -> Result<PublicKey> {
|
||||
let mut pub_key = self.wrapped.convert_to_public_key()?;
|
||||
|
||||
// Workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=1562046.
|
||||
let field_len = match self.curve {
|
||||
Curve::P256 => 32,
|
||||
};
|
||||
let expected_len = 2 * field_len + 1;
|
||||
let mut pub_value = unsafe { (*pub_key.as_ptr()).u.ec.publicValue };
|
||||
if pub_value.len == expected_len - 2 {
|
||||
let old_pub_value_raw = unsafe { sec_item_as_slice(&mut pub_value)?.to_vec() };
|
||||
let mut new_pub_value_raw = vec![0u8; usize::try_from(expected_len)?];
|
||||
new_pub_value_raw[0] = u8::try_from(nss_sys::EC_POINT_FORM_UNCOMPRESSED)?;
|
||||
new_pub_value_raw[1] = u8::try_from(old_pub_value_raw.len())?;
|
||||
new_pub_value_raw[2..].copy_from_slice(&old_pub_value_raw);
|
||||
pub_key = PublicKey::from_bytes(self.curve, &new_pub_value_raw)?.wrapped;
|
||||
}
|
||||
Ok(PublicKey {
|
||||
wrapped: pub_key,
|
||||
curve: self.curve,
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn from(curve: Curve, key: PK11PrivateKey) -> Self {
|
||||
Self {
|
||||
curve,
|
||||
wrapped: key,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn curve(&self) -> Curve {
|
||||
self.curve
|
||||
}
|
||||
|
||||
pub fn private_value(&self) -> Result<Vec<u8>> {
|
||||
let mut private_value = self.read_raw_attribute(nss_sys::CKA_VALUE.into()).unwrap();
|
||||
let private_key = unsafe { sec_item_as_slice(private_value.as_mut_ref())?.to_vec() };
|
||||
Ok(private_key)
|
||||
}
|
||||
|
||||
fn from_nss_params(
|
||||
curve: Curve,
|
||||
ec_params: &[u8],
|
||||
ec_point: &[u8],
|
||||
private_value: &[u8],
|
||||
) -> Result<Self> {
|
||||
// The following code is adapted from:
|
||||
// https://searchfox.org/mozilla-central/rev/444ee13e14fe30451651c0f62b3979c76766ada4/dom/crypto/CryptoKey.cpp#322
|
||||
// These explicit variable type declarations are *VERY* important, as we pass to NSS a pointer to them
|
||||
// and we need these variables to be of the right size!
|
||||
let mut private_key_value: nss_sys::CK_OBJECT_CLASS = nss_sys::CKO_PRIVATE_KEY.into();
|
||||
let mut false_value: nss_sys::CK_BBOOL = nss_sys::CK_FALSE;
|
||||
let mut ec_value: nss_sys::CK_KEY_TYPE = nss_sys::CKK_EC.into();
|
||||
let bbool_size = mem::size_of::<nss_sys::CK_BBOOL>();
|
||||
let key_template = vec![
|
||||
ck_attribute(
|
||||
nss_sys::CKA_CLASS.into(),
|
||||
&mut private_key_value as *mut _ as *mut c_void,
|
||||
mem::size_of::<nss_sys::CK_OBJECT_CLASS>(),
|
||||
)?,
|
||||
ck_attribute(
|
||||
nss_sys::CKA_KEY_TYPE.into(),
|
||||
&mut ec_value as *mut _ as *mut c_void,
|
||||
mem::size_of::<nss_sys::CK_KEY_TYPE>(),
|
||||
)?,
|
||||
ck_attribute(
|
||||
nss_sys::CKA_TOKEN.into(),
|
||||
&mut false_value as *mut _ as *mut c_void,
|
||||
bbool_size,
|
||||
)?,
|
||||
ck_attribute(
|
||||
nss_sys::CKA_SENSITIVE.into(),
|
||||
&mut false_value as *mut _ as *mut c_void,
|
||||
bbool_size,
|
||||
)?,
|
||||
ck_attribute(
|
||||
nss_sys::CKA_PRIVATE.into(),
|
||||
&mut false_value as *mut _ as *mut c_void,
|
||||
bbool_size,
|
||||
)?,
|
||||
// PrivateKeyFromPrivateKeyTemplate sets the ID.
|
||||
ck_attribute(nss_sys::CKA_ID.into(), ptr::null_mut(), 0)?,
|
||||
ck_attribute(
|
||||
nss_sys::CKA_EC_PARAMS.into(),
|
||||
ec_params.as_ptr() as *mut c_void,
|
||||
ec_params.len(),
|
||||
)?,
|
||||
ck_attribute(
|
||||
nss_sys::CKA_EC_POINT.into(),
|
||||
ec_point.as_ptr() as *mut c_void,
|
||||
ec_point.len(),
|
||||
)?,
|
||||
ck_attribute(
|
||||
nss_sys::CKA_VALUE.into(),
|
||||
private_value.as_ptr() as *mut c_void,
|
||||
private_value.len(),
|
||||
)?,
|
||||
];
|
||||
Ok(Self::from(
|
||||
curve,
|
||||
PK11PrivateKey::from_private_key_template(key_template)?,
|
||||
))
|
||||
}
|
||||
|
||||
pub fn import(ec_key: &EcKey) -> Result<Self> {
|
||||
// The following code is adapted from:
|
||||
// https://searchfox.org/mozilla-central/rev/66086345467c69685434dd1c5177b30a7511b1a5/dom/crypto/CryptoKey.cpp#652
|
||||
ensure_nss_initialized();
|
||||
let curve = ec_key.curve();
|
||||
let ec_params = create_ec_params_for_curve(curve)?;
|
||||
Self::from_nss_params(curve, &ec_params, &ec_key.public_key, &ec_key.private_key)
|
||||
}
|
||||
|
||||
pub fn export(&self) -> Result<EcKey> {
|
||||
let public_key = self.convert_to_public_key()?;
|
||||
let public_key_bytes = public_key.to_bytes()?;
|
||||
let private_key_bytes = self.private_value()?;
|
||||
Ok(EcKey::new(
|
||||
self.curve,
|
||||
&private_key_bytes,
|
||||
&public_key_bytes,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn ck_attribute(
|
||||
r#type: nss_sys::CK_ATTRIBUTE_TYPE,
|
||||
p_value: nss_sys::CK_VOID_PTR,
|
||||
value_len: usize,
|
||||
) -> Result<nss_sys::CK_ATTRIBUTE> {
|
||||
Ok(nss_sys::CK_ATTRIBUTE {
|
||||
type_: r#type,
|
||||
pValue: p_value,
|
||||
ulValueLen: nss_sys::CK_ULONG::try_from(value_len)?,
|
||||
})
|
||||
}
|
||||
|
||||
pub struct PublicKey {
|
||||
curve: Curve,
|
||||
wrapped: PK11PublicKey,
|
||||
}
|
||||
|
||||
impl Deref for PublicKey {
|
||||
type Target = PK11PublicKey;
|
||||
#[inline]
|
||||
fn deref(&self) -> &PK11PublicKey {
|
||||
&self.wrapped
|
||||
}
|
||||
}
|
||||
|
||||
impl PublicKey {
|
||||
#[inline]
|
||||
pub(crate) fn from(curve: Curve, key: PK11PublicKey) -> Self {
|
||||
Self {
|
||||
curve,
|
||||
wrapped: key,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn curve(&self) -> Curve {
|
||||
self.curve
|
||||
}
|
||||
|
||||
pub fn to_bytes(&self) -> Result<Vec<u8>> {
|
||||
// Some public keys we create do not have an associated PCKS#11 slot
|
||||
// therefore we cannot use `read_raw_attribute(CKA_EC_POINT)`
|
||||
// so we read the `publicValue` field directly instead.
|
||||
let mut ec_point = unsafe { (*self.as_ptr()).u.ec.publicValue };
|
||||
let public_key = unsafe { sec_item_as_slice(&mut ec_point)?.to_vec() };
|
||||
check_pub_key_bytes(&public_key, self.curve)?;
|
||||
Ok(public_key)
|
||||
}
|
||||
|
||||
pub fn from_bytes(curve: Curve, bytes: &[u8]) -> Result<PublicKey> {
|
||||
// The following code is adapted from:
|
||||
// https://searchfox.org/mozilla-central/rev/ec489aa170b6486891cf3625717d6fa12bcd11c1/dom/crypto/CryptoKey.cpp#1078
|
||||
check_pub_key_bytes(bytes, curve)?;
|
||||
let key_data = nss_sys::SECItem {
|
||||
type_: nss_sys::SECItemType::siBuffer,
|
||||
data: bytes.as_ptr() as *mut c_uchar,
|
||||
len: c_uint::try_from(bytes.len())?,
|
||||
};
|
||||
let params_buf = create_ec_params_for_curve(curve)?;
|
||||
let params = nss_sys::SECItem {
|
||||
type_: nss_sys::SECItemType::siBuffer,
|
||||
data: params_buf.as_ptr() as *mut c_uchar,
|
||||
len: c_uint::try_from(params_buf.len())?,
|
||||
};
|
||||
|
||||
let pub_key = nss_sys::SECKEYPublicKey {
|
||||
arena: ptr::null_mut(),
|
||||
keyType: nss_sys::KeyType::ecKey,
|
||||
pkcs11Slot: ptr::null_mut(),
|
||||
pkcs11ID: nss_sys::CK_INVALID_HANDLE.into(),
|
||||
u: nss_sys::SECKEYPublicKeyStr__bindgen_ty_1 {
|
||||
ec: nss_sys::SECKEYECPublicKey {
|
||||
DEREncodedParams: params,
|
||||
publicValue: key_data,
|
||||
encoding: nss_sys::ECPointEncoding_ECPoint_Uncompressed,
|
||||
size: 0,
|
||||
},
|
||||
},
|
||||
};
|
||||
Ok(Self::from(curve, unsafe {
|
||||
PK11PublicKey::from_ptr(nss_sys::SECKEY_CopyPublicKey(&pub_key))?
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
fn check_pub_key_bytes(bytes: &[u8], curve: Curve) -> Result<()> {
|
||||
let field_len = match curve {
|
||||
Curve::P256 => 32,
|
||||
};
|
||||
// Check length of uncompressed point coordinates. There are 2 field elements
|
||||
// and a leading "point form" octet (which must be EC_POINT_FORM_UNCOMPRESSED).
|
||||
if bytes.len() != (2 * field_len + 1) {
|
||||
return Err(ErrorKind::InternalError.into());
|
||||
}
|
||||
// No support for compressed points.
|
||||
if bytes[0] != u8::try_from(nss_sys::EC_POINT_FORM_UNCOMPRESSED)? {
|
||||
return Err(ErrorKind::InternalError.into());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn create_ec_params_for_curve(curve: Curve) -> Result<Vec<u8>> {
|
||||
// The following code is adapted from:
|
||||
// https://searchfox.org/mozilla-central/rev/ec489aa170b6486891cf3625717d6fa12bcd11c1/dom/crypto/WebCryptoCommon.h#299
|
||||
let curve_oid_tag = match curve {
|
||||
Curve::P256 => nss_sys::SECOidTag::SEC_OID_ANSIX962_EC_PRIME256V1,
|
||||
};
|
||||
// Retrieve curve data by OID tag.
|
||||
let oid_data = unsafe { nss_sys::SECOID_FindOIDByTag(curve_oid_tag) };
|
||||
if oid_data.is_null() {
|
||||
return Err(ErrorKind::InternalError.into());
|
||||
}
|
||||
// Set parameters
|
||||
let oid_data_len = unsafe { (*oid_data).oid.len };
|
||||
let mut buf = vec![0u8; usize::try_from(oid_data_len)? + 2];
|
||||
buf[0] = c_uchar::try_from(nss_sys::SEC_ASN1_OBJECT_ID)?;
|
||||
buf[1] = c_uchar::try_from(oid_data_len)?;
|
||||
let oid_data_data =
|
||||
unsafe { std::slice::from_raw_parts((*oid_data).oid.data, usize::try_from(oid_data_len)?) };
|
||||
buf[2..].copy_from_slice(oid_data_data);
|
||||
Ok(buf)
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use crate::{
|
||||
ec::{PrivateKey, PublicKey},
|
||||
error::*,
|
||||
pk11::types::SymKey,
|
||||
util::{ensure_nss_initialized, map_nss_secstatus, sec_item_as_slice, ScopedPtr},
|
||||
};
|
||||
|
||||
pub fn ecdh_agreement(priv_key: &PrivateKey, pub_key: &PublicKey) -> Result<Vec<u8>> {
|
||||
ensure_nss_initialized();
|
||||
if priv_key.curve() != pub_key.curve() {
|
||||
return Err(ErrorKind::InternalError.into());
|
||||
}
|
||||
// The following code is adapted from:
|
||||
// https://searchfox.org/mozilla-central/rev/444ee13e14fe30451651c0f62b3979c76766ada4/dom/crypto/WebCryptoTask.cpp#2835
|
||||
|
||||
// CKM_SHA512_HMAC and CKA_SIGN are key type and usage attributes of the
|
||||
// derived symmetric key and don't matter because we ignore them anyway.
|
||||
let sym_key = unsafe {
|
||||
SymKey::from_ptr(nss_sys::PK11_PubDeriveWithKDF(
|
||||
priv_key.as_mut_ptr(),
|
||||
pub_key.as_mut_ptr(),
|
||||
nss_sys::PR_FALSE,
|
||||
std::ptr::null_mut(),
|
||||
std::ptr::null_mut(),
|
||||
nss_sys::CKM_ECDH1_DERIVE.into(),
|
||||
nss_sys::CKM_SHA512_HMAC.into(),
|
||||
nss_sys::CKA_SIGN.into(),
|
||||
0,
|
||||
nss_sys::CKD_NULL.into(),
|
||||
std::ptr::null_mut(),
|
||||
std::ptr::null_mut(),
|
||||
))?
|
||||
};
|
||||
|
||||
map_nss_secstatus(|| unsafe { nss_sys::PK11_ExtractKeyValue(sym_key.as_mut_ptr()) })?;
|
||||
|
||||
// This doesn't leak, because the SECItem* returned by PK11_GetKeyData
|
||||
// just refers to a buffer managed by `sym_key` which we copy into `buf`.
|
||||
let mut key_data = unsafe { *nss_sys::PK11_GetKeyData(sym_key.as_mut_ptr()) };
|
||||
let buf = unsafe { sec_item_as_slice(&mut key_data)? };
|
||||
Ok(buf.to_vec())
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use failure::Fail;
|
||||
|
||||
#[derive(Debug, Fail)]
|
||||
pub enum ErrorKind {
|
||||
#[fail(display = "NSS could not be initialized")]
|
||||
NSSInitFailure,
|
||||
#[fail(display = "NSS error: {} {}", _0, _1)]
|
||||
NSSError(i32, String),
|
||||
#[fail(display = "Internal crypto error")]
|
||||
InternalError,
|
||||
#[fail(display = "Conversion error: {}", _0)]
|
||||
ConversionError(#[fail(cause)] std::num::TryFromIntError),
|
||||
#[fail(display = "Base64 decode error: {}", _0)]
|
||||
Base64Decode(#[fail(cause)] base64::DecodeError),
|
||||
}
|
||||
|
||||
error_support::define_error! {
|
||||
ErrorKind {
|
||||
(Base64Decode, base64::DecodeError),
|
||||
(ConversionError, std::num::TryFromIntError),
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#![allow(unknown_lints)]
|
||||
#![warn(rust_2018_idioms)]
|
||||
#[macro_use]
|
||||
mod util;
|
||||
pub mod aes;
|
||||
pub mod ec;
|
||||
pub mod ecdh;
|
||||
mod error;
|
||||
pub mod pk11;
|
||||
pub mod secport;
|
||||
pub use crate::error::{Error, ErrorKind, Result};
|
||||
pub use util::ensure_nss_initialized as ensure_initialized;
|
|
@ -0,0 +1,113 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use crate::{
|
||||
error::*,
|
||||
pk11::{
|
||||
sym_key::import_sym_key,
|
||||
types::{Context, SymKey},
|
||||
},
|
||||
util::{ensure_nss_initialized, map_nss_secstatus, ScopedPtr},
|
||||
};
|
||||
use std::{convert::TryFrom, ptr};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[repr(u8)]
|
||||
pub enum HashAlgorithm {
|
||||
SHA256,
|
||||
}
|
||||
|
||||
impl HashAlgorithm {
|
||||
fn result_len(&self) -> u32 {
|
||||
match self {
|
||||
HashAlgorithm::SHA256 => nss_sys::SHA256_LENGTH,
|
||||
}
|
||||
}
|
||||
|
||||
fn as_hmac_mechanism(&self) -> u32 {
|
||||
match self {
|
||||
HashAlgorithm::SHA256 => nss_sys::CKM_SHA256_HMAC,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn as_hkdf_mechanism(&self) -> u32 {
|
||||
match self {
|
||||
HashAlgorithm::SHA256 => nss_sys::CKM_NSS_HKDF_SHA256,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&HashAlgorithm> for nss_sys::SECOidTag::Type {
|
||||
fn from(alg: &HashAlgorithm) -> Self {
|
||||
match alg {
|
||||
HashAlgorithm::SHA256 => nss_sys::SECOidTag::SEC_OID_SHA256,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hash_buf(algorithm: &HashAlgorithm, data: &[u8]) -> Result<Vec<u8>> {
|
||||
ensure_nss_initialized();
|
||||
let result_len = usize::try_from(algorithm.result_len())?;
|
||||
let mut out = vec![0u8; result_len];
|
||||
let data_len = i32::try_from(data.len())?;
|
||||
map_nss_secstatus(|| unsafe {
|
||||
nss_sys::PK11_HashBuf(algorithm.into(), out.as_mut_ptr(), data.as_ptr(), data_len)
|
||||
})?;
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
pub fn hmac_sign(digest_alg: &HashAlgorithm, sym_key_bytes: &[u8], data: &[u8]) -> Result<Vec<u8>> {
|
||||
let mech = digest_alg.as_hmac_mechanism();
|
||||
let sym_key = import_sym_key(mech.into(), nss_sys::CKA_SIGN.into(), sym_key_bytes)?;
|
||||
let context = create_context_by_sym_key(mech.into(), nss_sys::CKA_SIGN.into(), &sym_key)?;
|
||||
Ok(hash_buf_with_context(&context, data)?)
|
||||
}
|
||||
|
||||
/// Similar to hash_buf except the consumer has to provide the digest context.
|
||||
fn hash_buf_with_context(context: &Context, data: &[u8]) -> Result<Vec<u8>> {
|
||||
ensure_nss_initialized();
|
||||
map_nss_secstatus(|| unsafe { nss_sys::PK11_DigestBegin(context.as_mut_ptr()) })?;
|
||||
let data_len = u32::try_from(data.len())?;
|
||||
map_nss_secstatus(|| unsafe {
|
||||
nss_sys::PK11_DigestOp(context.as_mut_ptr(), data.as_ptr(), data_len)
|
||||
})?;
|
||||
// We allocate the maximum possible length for the out buffer then we'll
|
||||
// slice it after nss fills `out_len`.
|
||||
let mut out_len: u32 = 0;
|
||||
let mut out = vec![0u8; nss_sys::HASH_LENGTH_MAX as usize];
|
||||
map_nss_secstatus(|| unsafe {
|
||||
nss_sys::PK11_DigestFinal(
|
||||
context.as_mut_ptr(),
|
||||
out.as_mut_ptr(),
|
||||
&mut out_len,
|
||||
nss_sys::HASH_LENGTH_MAX,
|
||||
)
|
||||
})?;
|
||||
out.truncate(usize::try_from(out_len)?);
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
/// Safe wrapper around PK11_CreateContextBySymKey that
|
||||
/// de-allocates memory when the context goes out of
|
||||
/// scope.
|
||||
pub fn create_context_by_sym_key(
|
||||
mechanism: nss_sys::CK_MECHANISM_TYPE,
|
||||
operation: nss_sys::CK_ATTRIBUTE_TYPE,
|
||||
sym_key: &SymKey,
|
||||
) -> Result<Context> {
|
||||
ensure_nss_initialized();
|
||||
let mut param = nss_sys::SECItem {
|
||||
type_: nss_sys::SECItemType::siBuffer,
|
||||
data: ptr::null_mut(),
|
||||
len: 0,
|
||||
};
|
||||
unsafe {
|
||||
Context::from_ptr(nss_sys::PK11_CreateContextBySymKey(
|
||||
mechanism,
|
||||
operation,
|
||||
sym_key.as_mut_ptr(),
|
||||
&mut param,
|
||||
))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
pub mod context;
|
||||
pub mod slot;
|
||||
pub mod sym_key;
|
||||
pub mod types;
|
|
@ -0,0 +1,25 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use crate::{
|
||||
error::*,
|
||||
pk11::types::Slot,
|
||||
util::{ensure_nss_initialized, map_nss_secstatus, ScopedPtr},
|
||||
};
|
||||
use std::convert::TryFrom;
|
||||
|
||||
pub fn generate_random(data: &mut [u8]) -> Result<()> {
|
||||
// `NSS_Init` will initialize the RNG with data from `/dev/urandom`.
|
||||
ensure_nss_initialized();
|
||||
let len = i32::try_from(data.len())?;
|
||||
map_nss_secstatus(|| unsafe { nss_sys::PK11_GenerateRandom(data.as_mut_ptr(), len) })?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Safe wrapper around `PK11_GetInternalSlot` that
|
||||
/// de-allocates memory when the slot goes out of
|
||||
/// scope.
|
||||
pub(crate) fn get_internal_slot() -> Result<Slot> {
|
||||
unsafe { Slot::from_ptr(nss_sys::PK11_GetInternalSlot()) }
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use crate::{
|
||||
error::*,
|
||||
pk11::{context::HashAlgorithm, slot, types::SymKey},
|
||||
util::{ensure_nss_initialized, map_nss_secstatus, sec_item_as_slice, ScopedPtr},
|
||||
};
|
||||
use std::{
|
||||
convert::TryFrom,
|
||||
mem,
|
||||
os::raw::{c_uchar, c_uint, c_ulong},
|
||||
ptr,
|
||||
};
|
||||
|
||||
pub fn hkdf_expand(
|
||||
digest_alg: &HashAlgorithm,
|
||||
key_bytes: &[u8],
|
||||
info: &[u8],
|
||||
len: usize,
|
||||
) -> Result<Vec<u8>> {
|
||||
ensure_nss_initialized();
|
||||
let mech = digest_alg.as_hkdf_mechanism();
|
||||
// Most of the following code is inspired by the Firefox WebCrypto implementation:
|
||||
// https://searchfox.org/mozilla-central/rev/ee3905439acbf81e9c829ece0b46d09d2fa26c5c/dom/crypto/WebCryptoTask.cpp#2530-2597
|
||||
// Except that we only do the expand part, which explains why we use null pointers below.
|
||||
let mut hkdf_params = nss_sys::CK_NSS_HKDFParams {
|
||||
bExtract: nss_sys::CK_FALSE,
|
||||
pSalt: ptr::null_mut(),
|
||||
ulSaltLen: 0,
|
||||
bExpand: nss_sys::CK_TRUE,
|
||||
pInfo: info.as_ptr() as *mut u8,
|
||||
ulInfoLen: c_ulong::try_from(info.len())?,
|
||||
};
|
||||
let mut params = nss_sys::SECItem {
|
||||
type_: nss_sys::SECItemType::siBuffer,
|
||||
data: &mut hkdf_params as *mut _ as *mut c_uchar,
|
||||
len: u32::try_from(mem::size_of::<nss_sys::CK_NSS_HKDFParams>())?,
|
||||
};
|
||||
let base_key = import_sym_key(mech.into(), nss_sys::CKA_WRAP.into(), key_bytes)?;
|
||||
let derived_len = i32::try_from(len)?;
|
||||
let sym_key = unsafe {
|
||||
SymKey::from_ptr(
|
||||
// CKM_SHA512_HMAC and CKA_SIGN are key type and usage attributes of the
|
||||
// derived symmetric key and don't matter because we ignore them anyway.
|
||||
nss_sys::PK11_Derive(
|
||||
base_key.as_mut_ptr(),
|
||||
mech.into(),
|
||||
&mut params,
|
||||
nss_sys::CKM_SHA512_HMAC.into(),
|
||||
nss_sys::CKA_SIGN.into(),
|
||||
derived_len,
|
||||
),
|
||||
)?
|
||||
};
|
||||
map_nss_secstatus(|| unsafe { nss_sys::PK11_ExtractKeyValue(sym_key.as_mut_ptr()) })?;
|
||||
// This doesn't leak, because the SECItem* returned by PK11_GetKeyData
|
||||
// just refers to a buffer managed by `sym_key` which we copy into `out`.
|
||||
let mut key_data = unsafe { *nss_sys::PK11_GetKeyData(sym_key.as_mut_ptr()) };
|
||||
if u32::try_from(len)? > key_data.len {
|
||||
return Err(ErrorKind::InternalError.into());
|
||||
}
|
||||
let buf = unsafe { sec_item_as_slice(&mut key_data)? };
|
||||
Ok(buf.to_vec())
|
||||
}
|
||||
|
||||
/// Safe wrapper around PK11_ImportSymKey that
|
||||
/// de-allocates memory when the key goes out of
|
||||
/// scope.
|
||||
pub(crate) fn import_sym_key(
|
||||
mechanism: nss_sys::CK_MECHANISM_TYPE,
|
||||
operation: nss_sys::CK_ATTRIBUTE_TYPE,
|
||||
buf: &[u8],
|
||||
) -> Result<SymKey> {
|
||||
ensure_nss_initialized();
|
||||
let mut item = nss_sys::SECItem {
|
||||
type_: nss_sys::SECItemType::siBuffer,
|
||||
data: buf.as_ptr() as *mut c_uchar,
|
||||
len: c_uint::try_from(buf.len())?,
|
||||
};
|
||||
let slot = slot::get_internal_slot()?;
|
||||
unsafe {
|
||||
SymKey::from_ptr(nss_sys::PK11_ImportSymKey(
|
||||
slot.as_mut_ptr(),
|
||||
mechanism,
|
||||
nss_sys::PK11Origin::PK11_OriginUnwrap,
|
||||
operation,
|
||||
&mut item,
|
||||
ptr::null_mut(),
|
||||
))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,213 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use crate::{
|
||||
error::*,
|
||||
pk11::slot::{generate_random, get_internal_slot},
|
||||
util::{map_nss_secstatus, ScopedPtr},
|
||||
};
|
||||
use std::{
|
||||
convert::TryFrom,
|
||||
ops::Deref,
|
||||
os::raw::{c_int, c_uchar, c_uint, c_void},
|
||||
ptr,
|
||||
};
|
||||
|
||||
scoped_ptr!(SymKey, nss_sys::PK11SymKey, nss_sys::PK11_FreeSymKey);
|
||||
scoped_ptr!(
|
||||
PrivateKey,
|
||||
nss_sys::SECKEYPrivateKey,
|
||||
nss_sys::SECKEY_DestroyPrivateKey
|
||||
);
|
||||
scoped_ptr!(
|
||||
PublicKey,
|
||||
nss_sys::SECKEYPublicKey,
|
||||
nss_sys::SECKEY_DestroyPublicKey
|
||||
);
|
||||
scoped_ptr!(
|
||||
GenericObject,
|
||||
nss_sys::PK11GenericObject,
|
||||
nss_sys::PK11_DestroyGenericObject
|
||||
);
|
||||
scoped_ptr!(Context, nss_sys::PK11Context, pk11_destroy_context_true);
|
||||
scoped_ptr!(Slot, nss_sys::PK11SlotInfo, nss_sys::PK11_FreeSlot);
|
||||
|
||||
#[inline]
|
||||
unsafe fn pk11_destroy_context_true(context: *mut nss_sys::PK11Context) {
|
||||
nss_sys::PK11_DestroyContext(context, nss_sys::PR_TRUE);
|
||||
}
|
||||
|
||||
// Trait for types that have PCKS#11 attributes that are readable. See
|
||||
// https://searchfox.org/mozilla-central/rev/8ed8474757695cdae047150a0eaf94a5f1c96dbe/security/nss/lib/pk11wrap/pk11pub.h#842-864
|
||||
pub(crate) unsafe trait Pkcs11Object: ScopedPtr {
|
||||
const PK11_OBJECT_TYPE: nss_sys::PK11ObjectType::Type;
|
||||
fn read_raw_attribute(
|
||||
&self,
|
||||
attribute_type: nss_sys::CK_ATTRIBUTE_TYPE,
|
||||
) -> Result<ScopedSECItem> {
|
||||
let mut out_sec = ScopedSECItem::empty(nss_sys::SECItemType::siBuffer);
|
||||
map_nss_secstatus(|| unsafe {
|
||||
nss_sys::PK11_ReadRawAttribute(
|
||||
Self::PK11_OBJECT_TYPE,
|
||||
self.as_mut_ptr() as *mut c_void,
|
||||
attribute_type,
|
||||
out_sec.as_mut_ref(),
|
||||
)
|
||||
})?;
|
||||
Ok(out_sec)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Pkcs11Object for GenericObject {
|
||||
const PK11_OBJECT_TYPE: nss_sys::PK11ObjectType::Type =
|
||||
nss_sys::PK11ObjectType::PK11_TypeGeneric;
|
||||
}
|
||||
unsafe impl Pkcs11Object for PrivateKey {
|
||||
const PK11_OBJECT_TYPE: nss_sys::PK11ObjectType::Type =
|
||||
nss_sys::PK11ObjectType::PK11_TypePrivKey;
|
||||
}
|
||||
unsafe impl Pkcs11Object for PublicKey {
|
||||
const PK11_OBJECT_TYPE: nss_sys::PK11ObjectType::Type =
|
||||
nss_sys::PK11ObjectType::PK11_TypePubKey;
|
||||
}
|
||||
unsafe impl Pkcs11Object for SymKey {
|
||||
const PK11_OBJECT_TYPE: nss_sys::PK11ObjectType::Type =
|
||||
nss_sys::PK11ObjectType::PK11_TypeSymKey;
|
||||
}
|
||||
|
||||
// From https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/NSS_API_Guidelines#Thread_Safety:
|
||||
// "Data structures that are read only, like SECKEYPublicKeys or PK11SymKeys, need not be protected."
|
||||
unsafe impl Send for PrivateKey {}
|
||||
unsafe impl Send for PublicKey {}
|
||||
|
||||
impl PrivateKey {
|
||||
pub fn convert_to_public_key(&self) -> Result<PublicKey> {
|
||||
Ok(unsafe { PublicKey::from_ptr(nss_sys::SECKEY_ConvertToPublicKey(self.as_mut_ptr()))? })
|
||||
}
|
||||
|
||||
// To protect against key ID collisions, PrivateKeyFromPrivateKeyTemplate
|
||||
// generates a random ID for each key. The given template must contain an
|
||||
// attribute slot for a key ID, but it must consist of a null pointer and have a
|
||||
// length of 0.
|
||||
pub(crate) fn from_private_key_template(
|
||||
mut template: Vec<nss_sys::CK_ATTRIBUTE>,
|
||||
) -> Result<Self> {
|
||||
// Generate a random 160-bit object ID. This ID must be unique.
|
||||
let mut obj_id_buf = vec![0u8; 160 / 8];
|
||||
generate_random(&mut obj_id_buf)?;
|
||||
let mut obj_id = nss_sys::SECItem {
|
||||
type_: nss_sys::SECItemType::siBuffer,
|
||||
data: obj_id_buf.as_ptr() as *mut c_uchar,
|
||||
len: c_uint::try_from(obj_id_buf.len())?,
|
||||
};
|
||||
let slot = get_internal_slot()?;
|
||||
let mut pre_existing_key = unsafe {
|
||||
nss_sys::PK11_FindKeyByKeyID(slot.as_mut_ptr(), &mut obj_id, std::ptr::null_mut())
|
||||
};
|
||||
if !pre_existing_key.is_null() {
|
||||
// Note that we can't just call SECKEY_DestroyPrivateKey here because that
|
||||
// will destroy the PKCS#11 object that is backing a preexisting key (that
|
||||
// we still have a handle on somewhere else in memory). If that object were
|
||||
// destroyed, cryptographic operations performed by that other key would
|
||||
// fail.
|
||||
unsafe {
|
||||
destroy_private_key_without_destroying_pkcs11_object(pre_existing_key);
|
||||
}
|
||||
// Try again with a new ID (but only once - collisions are very unlikely).
|
||||
generate_random(&mut obj_id_buf)?;
|
||||
pre_existing_key = unsafe {
|
||||
nss_sys::PK11_FindKeyByKeyID(slot.as_mut_ptr(), &mut obj_id, std::ptr::null_mut())
|
||||
};
|
||||
if !pre_existing_key.is_null() {
|
||||
unsafe {
|
||||
destroy_private_key_without_destroying_pkcs11_object(pre_existing_key);
|
||||
}
|
||||
return Err(ErrorKind::InternalError.into());
|
||||
}
|
||||
}
|
||||
let template_len = c_int::try_from(template.len())?;
|
||||
let mut id_attr: &mut nss_sys::CK_ATTRIBUTE = template
|
||||
.iter_mut()
|
||||
.find(|&&mut attr| {
|
||||
attr.type_ == nss_sys::CKA_ID.into()
|
||||
&& attr.pValue.is_null()
|
||||
&& attr.ulValueLen == 0
|
||||
})
|
||||
.ok_or_else(|| ErrorKind::InternalError)?;
|
||||
id_attr.pValue = obj_id_buf.as_mut_ptr() as *mut c_void;
|
||||
id_attr.ulValueLen = nss_sys::CK_ULONG::try_from(obj_id_buf.len())?;
|
||||
// We use `PK11_CreateGenericObject` instead of `PK11_CreateManagedGenericObject`
|
||||
// to leak the reference on purpose because `PK11_FindKeyByKeyID` will take
|
||||
// ownership of it.
|
||||
let _obj = unsafe {
|
||||
GenericObject::from_ptr(nss_sys::PK11_CreateGenericObject(
|
||||
slot.as_mut_ptr(),
|
||||
template.as_mut_ptr(),
|
||||
template_len,
|
||||
nss_sys::PR_FALSE,
|
||||
))?
|
||||
};
|
||||
// Have NSS translate the object to a private key.
|
||||
Ok(unsafe {
|
||||
PrivateKey::from_ptr(nss_sys::PK11_FindKeyByKeyID(
|
||||
slot.as_mut_ptr(),
|
||||
&mut obj_id,
|
||||
std::ptr::null_mut(),
|
||||
))?
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// This is typically used by functions receiving a pointer to an `out SECItem`,
|
||||
// where we allocate the struct, but NSS allocates the elements it points to.
|
||||
pub(crate) struct ScopedSECItem {
|
||||
wrapped: nss_sys::SECItem,
|
||||
}
|
||||
|
||||
impl ScopedSECItem {
|
||||
pub(crate) fn empty(r#type: nss_sys::SECItemType::Type) -> Self {
|
||||
ScopedSECItem {
|
||||
wrapped: nss_sys::SECItem {
|
||||
type_: r#type,
|
||||
data: ptr::null_mut(),
|
||||
len: 0,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn as_mut_ref(&mut self) -> &mut nss_sys::SECItem {
|
||||
&mut self.wrapped
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for ScopedSECItem {
|
||||
type Target = nss_sys::SECItem;
|
||||
#[inline]
|
||||
fn deref(&self) -> &nss_sys::SECItem {
|
||||
&self.wrapped
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for ScopedSECItem {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
// PR_FALSE asks the NSS allocator not to free the SECItem
|
||||
// itself, and just the pointee of `self.wrapped.data`.
|
||||
nss_sys::SECITEM_FreeItem(&mut self.wrapped, nss_sys::PR_FALSE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This helper function will release the memory backing a SECKEYPrivateKey and
|
||||
// any resources acquired in its creation. It will leave the backing PKCS#11
|
||||
// object untouched, however. This should only be called from
|
||||
// PrivateKeyFromPrivateKeyTemplate.
|
||||
// From: https://searchfox.org/mozilla-central/rev/444ee13e14fe30451651c0f62b3979c76766ada4/dom/crypto/CryptoKey.cpp#80
|
||||
unsafe fn destroy_private_key_without_destroying_pkcs11_object(
|
||||
key: *mut nss_sys::SECKEYPrivateKey,
|
||||
) {
|
||||
assert!(!key.is_null());
|
||||
nss_sys::PK11_FreeSlot((*key).pkcs11Slot);
|
||||
nss_sys::PORT_FreeArena((*key).arena, nss_sys::PR_TRUE);
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use crate::util::ensure_nss_initialized;
|
||||
use std::os::raw::c_void;
|
||||
|
||||
pub fn secure_memcmp(a: &[u8], b: &[u8]) -> bool {
|
||||
ensure_nss_initialized();
|
||||
// NSS_SecureMemcmp will compare N elements fron our slices,
|
||||
// so make sure they are the same length first.
|
||||
if a.len() != b.len() {
|
||||
return false;
|
||||
}
|
||||
let result = unsafe {
|
||||
nss_sys::NSS_SecureMemcmp(
|
||||
a.as_ptr() as *const c_void,
|
||||
b.as_ptr() as *const c_void,
|
||||
a.len(),
|
||||
)
|
||||
};
|
||||
result == 0
|
||||
}
|
|
@ -0,0 +1,125 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use crate::error::*;
|
||||
use nss_sys::*;
|
||||
use std::{convert::TryFrom, ffi::CString, os::raw::c_char, sync::Once};
|
||||
|
||||
static NSS_INIT: Once = Once::new();
|
||||
|
||||
pub fn ensure_nss_initialized() {
|
||||
NSS_INIT.call_once(|| {
|
||||
let version_ptr = CString::new(nss_sys::COMPATIBLE_NSS_VERSION).unwrap();
|
||||
if unsafe { NSS_VersionCheck(version_ptr.as_ptr()) == PR_FALSE } {
|
||||
panic!("Incompatible NSS version!")
|
||||
}
|
||||
let empty = CString::default();
|
||||
let flags = NSS_INIT_READONLY
|
||||
| NSS_INIT_NOCERTDB
|
||||
| NSS_INIT_NOMODDB
|
||||
| NSS_INIT_FORCEOPEN
|
||||
| NSS_INIT_OPTIMIZESPACE;
|
||||
let context = unsafe {
|
||||
NSS_InitContext(
|
||||
empty.as_ptr(),
|
||||
empty.as_ptr(),
|
||||
empty.as_ptr(),
|
||||
empty.as_ptr(),
|
||||
std::ptr::null_mut(),
|
||||
flags,
|
||||
)
|
||||
};
|
||||
if context.is_null() {
|
||||
let error = get_last_error();
|
||||
panic!("Could not initialize NSS: {}", error);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn map_nss_secstatus<F>(callback: F) -> Result<()>
|
||||
where
|
||||
F: FnOnce() -> SECStatus,
|
||||
{
|
||||
if callback() == SECSuccess {
|
||||
return Ok(());
|
||||
}
|
||||
Err(get_last_error())
|
||||
}
|
||||
|
||||
/// Retrieve and wrap the last NSS/NSPR error in the current thread.
|
||||
#[cold]
|
||||
pub fn get_last_error() -> Error {
|
||||
let error_code = unsafe { PR_GetError() };
|
||||
let error_text: String = usize::try_from(unsafe { PR_GetErrorTextLength() })
|
||||
.map(|error_text_len| {
|
||||
let mut out_str = vec![0u8; error_text_len + 1];
|
||||
unsafe { PR_GetErrorText(out_str.as_mut_ptr() as *mut c_char) };
|
||||
CString::new(&out_str[0..error_text_len])
|
||||
.unwrap_or_else(|_| CString::default())
|
||||
.to_str()
|
||||
.unwrap_or_else(|_| "")
|
||||
.to_owned()
|
||||
})
|
||||
.unwrap_or_else(|_| "".to_string());
|
||||
ErrorKind::NSSError(error_code, error_text).into()
|
||||
}
|
||||
|
||||
pub(crate) trait ScopedPtr
|
||||
where
|
||||
Self: std::marker::Sized,
|
||||
{
|
||||
type RawType;
|
||||
unsafe fn from_ptr(ptr: *mut Self::RawType) -> Result<Self>;
|
||||
fn as_ptr(&self) -> *const Self::RawType;
|
||||
fn as_mut_ptr(&self) -> *mut Self::RawType;
|
||||
}
|
||||
|
||||
// The macro defines a wrapper around pointers refering to types allocated by NSS,
|
||||
// calling their NSS destructor method when they go out of scope to avoid memory leaks.
|
||||
// The `as_ptr`/`as_mut_ptr` are provided to retrieve the raw pointers to pass to
|
||||
// NSS functions that consume them.
|
||||
#[macro_export]
|
||||
macro_rules! scoped_ptr {
|
||||
($scoped:ident, $target:ty, $dtor:path) => {
|
||||
pub struct $scoped {
|
||||
ptr: *mut $target,
|
||||
}
|
||||
|
||||
impl crate::util::ScopedPtr for $scoped {
|
||||
type RawType = $target;
|
||||
|
||||
#[allow(dead_code)]
|
||||
unsafe fn from_ptr(ptr: *mut $target) -> crate::error::Result<$scoped> {
|
||||
if !ptr.is_null() {
|
||||
Ok($scoped { ptr: ptr })
|
||||
} else {
|
||||
Err(crate::error::ErrorKind::InternalError.into())
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn as_ptr(&self) -> *const $target {
|
||||
self.ptr
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn as_mut_ptr(&self) -> *mut $target {
|
||||
self.ptr
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for $scoped {
|
||||
fn drop(&mut self) {
|
||||
assert!(!self.ptr.is_null());
|
||||
unsafe { $dtor(self.ptr) };
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn sec_item_as_slice(sec_item: &mut SECItem) -> Result<&mut [u8]> {
|
||||
let sec_item_buf_len = usize::try_from(sec_item.len)?;
|
||||
let buf = std::slice::from_raw_parts_mut(sec_item.data, sec_item_buf_len);
|
||||
Ok(buf)
|
||||
}
|
|
@ -0,0 +1,317 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// This file contains code that was copied from the ring crate which is under
|
||||
// the ISC license, reproduced below:
|
||||
|
||||
// Copyright 2015-2017 Brian Smith.
|
||||
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
|
||||
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
|
||||
// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
mod aes_cbc;
|
||||
mod aes_gcm;
|
||||
|
||||
use crate::error::*;
|
||||
pub use aes_cbc::LEGACY_SYNC_AES_256_CBC_HMAC_SHA256;
|
||||
pub use aes_gcm::{AES_128_GCM, AES_256_GCM};
|
||||
use nss::aes;
|
||||
|
||||
pub fn open(
|
||||
key: &OpeningKey,
|
||||
nonce: Nonce,
|
||||
aad: Aad<'_>,
|
||||
ciphertext_and_tag: &[u8],
|
||||
) -> Result<Vec<u8>> {
|
||||
(key.algorithm().open)(&key.key, nonce, &aad, ciphertext_and_tag)
|
||||
}
|
||||
|
||||
pub fn seal(key: &SealingKey, nonce: Nonce, aad: Aad<'_>, plaintext: &[u8]) -> Result<Vec<u8>> {
|
||||
(key.algorithm().seal)(&key.key, nonce, &aad, plaintext)
|
||||
}
|
||||
|
||||
/// The additional authenticated data (AAD) for an opening or sealing
|
||||
/// operation. This data is authenticated but is **not** encrypted.
|
||||
/// This is a type-safe wrapper around the raw bytes designed to encourage
|
||||
/// correct use of the API.
|
||||
#[repr(transparent)]
|
||||
pub struct Aad<'a>(&'a [u8]);
|
||||
|
||||
impl<'a> Aad<'a> {
|
||||
/// Construct the `Aad` by borrowing a contiguous sequence of bytes.
|
||||
#[inline]
|
||||
pub fn from(aad: &'a [u8]) -> Self {
|
||||
Aad(aad)
|
||||
}
|
||||
}
|
||||
|
||||
impl Aad<'static> {
|
||||
/// Construct an empty `Aad`.
|
||||
pub fn empty() -> Self {
|
||||
Self::from(&[])
|
||||
}
|
||||
}
|
||||
|
||||
/// The nonce for an opening or sealing operation.
|
||||
/// This is a type-safe wrapper around the raw bytes designed to encourage
|
||||
/// correct use of the API.
|
||||
pub struct Nonce(Vec<u8>);
|
||||
|
||||
impl Nonce {
|
||||
#[inline]
|
||||
pub fn try_assume_unique_for_key(algorithm: &'static Algorithm, value: &[u8]) -> Result<Self> {
|
||||
if value.len() != algorithm.nonce_len() {
|
||||
return Err(ErrorKind::InternalError.into());
|
||||
}
|
||||
Ok(Self(value.to_vec()))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct OpeningKey {
|
||||
key: Key,
|
||||
}
|
||||
|
||||
impl OpeningKey {
|
||||
/// Create a new opening key.
|
||||
///
|
||||
/// `key_bytes` must be exactly `algorithm.key_len` bytes long.
|
||||
#[inline]
|
||||
pub fn new(algorithm: &'static Algorithm, key_bytes: &[u8]) -> Result<Self> {
|
||||
Ok(Self {
|
||||
key: Key::new(algorithm, key_bytes)?,
|
||||
})
|
||||
}
|
||||
|
||||
/// The key's AEAD algorithm.
|
||||
#[inline]
|
||||
pub fn algorithm(&self) -> &'static Algorithm {
|
||||
self.key.algorithm()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SealingKey {
|
||||
key: Key,
|
||||
}
|
||||
|
||||
impl SealingKey {
|
||||
/// Create a new sealing key.
|
||||
///
|
||||
/// `key_bytes` must be exactly `algorithm.key_len` bytes long.
|
||||
#[inline]
|
||||
pub fn new(algorithm: &'static Algorithm, key_bytes: &[u8]) -> Result<Self> {
|
||||
Ok(Self {
|
||||
key: Key::new(algorithm, key_bytes)?,
|
||||
})
|
||||
}
|
||||
|
||||
/// The key's AEAD algorithm.
|
||||
#[inline]
|
||||
pub fn algorithm(&self) -> &'static Algorithm {
|
||||
self.key.algorithm()
|
||||
}
|
||||
}
|
||||
|
||||
/// `OpeningKey` and `SealingKey` are type-safety wrappers around `Key`.
|
||||
pub(crate) struct Key {
|
||||
key_value: Vec<u8>,
|
||||
algorithm: &'static Algorithm,
|
||||
}
|
||||
|
||||
impl Key {
|
||||
fn new(algorithm: &'static Algorithm, key_bytes: &[u8]) -> Result<Self> {
|
||||
if key_bytes.len() != algorithm.key_len() {
|
||||
return Err(ErrorKind::InternalError.into());
|
||||
}
|
||||
Ok(Key {
|
||||
key_value: key_bytes.to_vec(),
|
||||
algorithm,
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn algorithm(&self) -> &'static Algorithm {
|
||||
self.algorithm
|
||||
}
|
||||
}
|
||||
|
||||
// An AEAD algorithm.
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub struct Algorithm {
|
||||
tag_len: usize,
|
||||
key_len: usize,
|
||||
nonce_len: usize,
|
||||
open: fn(key: &Key, nonce: Nonce, aad: &Aad<'_>, ciphertext_and_tag: &[u8]) -> Result<Vec<u8>>,
|
||||
seal: fn(key: &Key, nonce: Nonce, aad: &Aad<'_>, plaintext: &[u8]) -> Result<Vec<u8>>,
|
||||
}
|
||||
|
||||
impl Algorithm {
|
||||
/// The length of the key.
|
||||
#[inline]
|
||||
pub const fn key_len(&self) -> usize {
|
||||
self.key_len
|
||||
}
|
||||
|
||||
/// The length of a tag.
|
||||
#[inline]
|
||||
pub const fn tag_len(&self) -> usize {
|
||||
self.tag_len
|
||||
}
|
||||
|
||||
/// The length of the nonces.
|
||||
#[inline]
|
||||
pub const fn nonce_len(&self) -> usize {
|
||||
self.nonce_len
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) enum Direction {
|
||||
Opening,
|
||||
Sealing,
|
||||
}
|
||||
|
||||
impl Direction {
|
||||
fn to_nss_operation(&self) -> aes::Operation {
|
||||
match self {
|
||||
Direction::Opening => aes::Operation::Decrypt,
|
||||
Direction::Sealing => aes::Operation::Encrypt,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
static ALL_ALGORITHMS: &[&Algorithm] = &[
|
||||
&LEGACY_SYNC_AES_256_CBC_HMAC_SHA256,
|
||||
&AES_128_GCM,
|
||||
&AES_256_GCM,
|
||||
];
|
||||
static ALL_ALGORITHMS_THAT_SUPPORT_AAD: &[&Algorithm] = &[&AES_128_GCM, &AES_256_GCM];
|
||||
|
||||
#[test]
|
||||
fn test_roundtrip() {
|
||||
for algorithm in ALL_ALGORITHMS {
|
||||
let mut cleartext_bytes = vec![0u8; 127];
|
||||
crate::rand::fill(&mut cleartext_bytes).unwrap();
|
||||
|
||||
let mut key_bytes = vec![0u8; algorithm.key_len()];
|
||||
crate::rand::fill(&mut key_bytes).unwrap();
|
||||
|
||||
let nonce_bytes = vec![0u8; algorithm.nonce_len()];
|
||||
|
||||
let key = SealingKey::new(algorithm, &key_bytes).unwrap();
|
||||
let nonce = Nonce::try_assume_unique_for_key(algorithm, &nonce_bytes).unwrap();
|
||||
let ciphertext_bytes = seal(&key, nonce, Aad::empty(), &cleartext_bytes).unwrap();
|
||||
|
||||
let key = OpeningKey::new(algorithm, &key_bytes).unwrap();
|
||||
let nonce = Nonce::try_assume_unique_for_key(algorithm, &nonce_bytes).unwrap();
|
||||
let roundtriped_cleartext_bytes =
|
||||
open(&key, nonce, Aad::empty(), &ciphertext_bytes).unwrap();
|
||||
assert_eq!(roundtriped_cleartext_bytes, cleartext_bytes);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cant_open_with_mismatched_key() {
|
||||
let mut key_bytes_1 = vec![0u8; AES_256_GCM.key_len()];
|
||||
crate::rand::fill(&mut key_bytes_1).unwrap();
|
||||
|
||||
let mut key_bytes_2 = vec![0u8; AES_128_GCM.key_len()];
|
||||
crate::rand::fill(&mut key_bytes_2).unwrap();
|
||||
|
||||
let nonce_bytes = vec![0u8; AES_256_GCM.nonce_len()];
|
||||
|
||||
let key = SealingKey::new(&AES_256_GCM, &key_bytes_1).unwrap();
|
||||
let nonce = Nonce::try_assume_unique_for_key(&AES_256_GCM, &nonce_bytes).unwrap();
|
||||
let ciphertext_bytes = seal(&key, nonce, Aad::empty(), &[0u8; 0]).unwrap();
|
||||
|
||||
let key = OpeningKey::new(&AES_128_GCM, &key_bytes_2).unwrap();
|
||||
let nonce = Nonce::try_assume_unique_for_key(&AES_128_GCM, &nonce_bytes).unwrap();
|
||||
let result = open(&key, nonce, Aad::empty(), &ciphertext_bytes);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cant_open_modified_ciphertext() {
|
||||
for algorithm in ALL_ALGORITHMS {
|
||||
let mut key_bytes = vec![0u8; algorithm.key_len()];
|
||||
crate::rand::fill(&mut key_bytes).unwrap();
|
||||
|
||||
let nonce_bytes = vec![0u8; algorithm.nonce_len()];
|
||||
|
||||
let key = SealingKey::new(algorithm, &key_bytes).unwrap();
|
||||
let nonce = Nonce::try_assume_unique_for_key(algorithm, &nonce_bytes).unwrap();
|
||||
let ciphertext_bytes = seal(&key, nonce, Aad::empty(), &[0u8; 0]).unwrap();
|
||||
|
||||
for i in 0..ciphertext_bytes.len() {
|
||||
let mut modified_ciphertext = ciphertext_bytes.clone();
|
||||
modified_ciphertext[i] = modified_ciphertext[i].wrapping_add(1);
|
||||
|
||||
let key = OpeningKey::new(algorithm, &key_bytes).unwrap();
|
||||
let nonce = Nonce::try_assume_unique_for_key(algorithm, &nonce_bytes).unwrap();
|
||||
let result = open(&key, nonce, Aad::empty(), &modified_ciphertext);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cant_open_with_incorrect_associated_data() {
|
||||
for algorithm in ALL_ALGORITHMS_THAT_SUPPORT_AAD {
|
||||
let mut key_bytes = vec![0u8; algorithm.key_len()];
|
||||
crate::rand::fill(&mut key_bytes).unwrap();
|
||||
|
||||
let nonce_bytes = vec![0u8; algorithm.nonce_len()];
|
||||
|
||||
let key = SealingKey::new(algorithm, &key_bytes).unwrap();
|
||||
let nonce = Nonce::try_assume_unique_for_key(algorithm, &nonce_bytes).unwrap();
|
||||
let ciphertext_bytes = seal(&key, nonce, Aad::from(&[1, 2, 3]), &[0u8; 0]).unwrap();
|
||||
|
||||
let key = OpeningKey::new(algorithm, &key_bytes).unwrap();
|
||||
let nonce = Nonce::try_assume_unique_for_key(algorithm, &nonce_bytes).unwrap();
|
||||
let result = open(&key, nonce, Aad::empty(), &ciphertext_bytes);
|
||||
assert!(result.is_err());
|
||||
|
||||
let nonce = Nonce::try_assume_unique_for_key(&AES_256_GCM, &nonce_bytes).unwrap();
|
||||
let result = open(&key, nonce, Aad::from(&[2, 3, 4]), &ciphertext_bytes);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cant_use_incorrectly_sized_key() {
|
||||
for algorithm in ALL_ALGORITHMS {
|
||||
let key_bytes = vec![0u8; algorithm.key_len() - 1];
|
||||
let result = Key::new(&algorithm, &key_bytes);
|
||||
assert!(result.is_err());
|
||||
|
||||
let key_bytes = vec![0u8; algorithm.key_len() + 1];
|
||||
let result = Key::new(&algorithm, &key_bytes);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cant_use_incorrectly_sized_nonce() {
|
||||
for algorithm in ALL_ALGORITHMS {
|
||||
let nonce_bytes = vec![0u8; algorithm.nonce_len() - 1];
|
||||
let result = Nonce::try_assume_unique_for_key(&algorithm, &nonce_bytes);
|
||||
assert!(result.is_err());
|
||||
|
||||
let nonce_bytes = vec![0u8; algorithm.nonce_len() + 1];
|
||||
let result = Nonce::try_assume_unique_for_key(&algorithm, &nonce_bytes);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,262 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// This file contains code that was copied from the ring crate which is under
|
||||
// the ISC license, reproduced below:
|
||||
|
||||
// Copyright 2015-2017 Brian Smith.
|
||||
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
|
||||
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
|
||||
// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
use crate::{aead, digest, error::*, hmac};
|
||||
use nss::aes;
|
||||
|
||||
/// AES-256 in CBC mode with HMAC-SHA256 tags and 128 bit nonces.
|
||||
/// This is a Sync 1.5 specific encryption scheme, do not use for new
|
||||
/// applications, there are better options out there nowadays.
|
||||
/// Important note: The HMAC tag verification is done against the
|
||||
/// base64 representation of the ciphertext.
|
||||
/// More details here: https://mozilla-services.readthedocs.io/en/latest/sync/storageformat5.html#record-encryption
|
||||
pub static LEGACY_SYNC_AES_256_CBC_HMAC_SHA256: aead::Algorithm = aead::Algorithm {
|
||||
key_len: 64, // 32 bytes for the AES key, 32 bytes for the HMAC key.
|
||||
tag_len: 32,
|
||||
nonce_len: 128 / 8,
|
||||
open,
|
||||
seal,
|
||||
};
|
||||
|
||||
// Warning: This does not run in constant time (which is fine for our usage).
|
||||
pub(crate) fn open(
|
||||
key: &aead::Key,
|
||||
nonce: aead::Nonce,
|
||||
aad: &aead::Aad<'_>,
|
||||
ciphertext_and_tag: &[u8],
|
||||
) -> Result<Vec<u8>> {
|
||||
let ciphertext_len = ciphertext_and_tag
|
||||
.len()
|
||||
.checked_sub(key.algorithm().tag_len())
|
||||
.ok_or_else(|| ErrorKind::InternalError)?;
|
||||
let (ciphertext, hmac_signature) = ciphertext_and_tag.split_at(ciphertext_len);
|
||||
let (aes_key, hmac_key_bytes) = extract_keys(&key);
|
||||
// 1. Tag (HMAC signature) check.
|
||||
let hmac_key = hmac::VerificationKey::new(&digest::SHA256, &hmac_key_bytes);
|
||||
let hmac_res = hmac::verify(
|
||||
&hmac_key,
|
||||
base64::encode(ciphertext).as_bytes(),
|
||||
hmac_signature,
|
||||
);
|
||||
// 2. Decryption.
|
||||
let cbc_res = aes_cbc(aes_key, nonce, aad, ciphertext, aead::Direction::Opening);
|
||||
// To make this function as constant-time as possible, we always try to run both
|
||||
// the hmac and the decryption operation.
|
||||
hmac_res.and(cbc_res)
|
||||
}
|
||||
|
||||
pub(crate) fn seal(
|
||||
key: &aead::Key,
|
||||
nonce: aead::Nonce,
|
||||
aad: &aead::Aad<'_>,
|
||||
plaintext: &[u8],
|
||||
) -> Result<Vec<u8>> {
|
||||
let (aes_key, hmac_key_bytes) = extract_keys(&key);
|
||||
// 1. Encryption.
|
||||
let mut ciphertext = aes_cbc(aes_key, nonce, aad, plaintext, aead::Direction::Sealing)?;
|
||||
// 2. Tag (HMAC signature) generation.
|
||||
let hmac_key = hmac::SigningKey::new(&digest::SHA256, &hmac_key_bytes);
|
||||
let signature = hmac::sign(&hmac_key, base64::encode(&ciphertext).as_bytes())?;
|
||||
ciphertext.extend(&signature.0.value);
|
||||
Ok(ciphertext)
|
||||
}
|
||||
|
||||
fn extract_keys(key: &aead::Key) -> (&[u8], &[u8]) {
|
||||
// Always split at 32 since we only do AES 256 w/ HMAC 256 tag.
|
||||
let (aes_key, hmac_key_bytes) = key.key_value.split_at(32);
|
||||
(aes_key, hmac_key_bytes)
|
||||
}
|
||||
|
||||
fn aes_cbc(
|
||||
aes_key: &[u8],
|
||||
nonce: aead::Nonce,
|
||||
aad: &aead::Aad<'_>,
|
||||
data: &[u8],
|
||||
direction: aead::Direction,
|
||||
) -> Result<Vec<u8>> {
|
||||
if !aad.0.is_empty() {
|
||||
// CBC mode does not support AAD.
|
||||
return Err(ErrorKind::InternalError.into());
|
||||
}
|
||||
Ok(aes::aes_cbc_crypt(
|
||||
aes_key,
|
||||
&nonce.0,
|
||||
data,
|
||||
direction.to_nss_operation(),
|
||||
)?)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
// These are the test vectors used by the sync15 crate, but concatenated
|
||||
// together rather than split into individual pieces.
|
||||
const IV_B64: &str = "GX8L37AAb2FZJMzIoXlX8w==";
|
||||
|
||||
const KEY_B64: &str = "9K/wLdXdw+nrTtXo4ZpECyHFNr4d7aYHqeg3KW9+m6Qwye0R+62At\
|
||||
NzwWVMtAWazz/Ew+YKV2o+Wr9BBcSPHvQ==";
|
||||
|
||||
const CIPHERTEXT_AND_TAG_B64: &str =
|
||||
"NMsdnRulLwQsVcwxKW9XwaUe7ouJk5Wn80QhbD80l0HEcZGCynh45qIbeYBik0lgcHbKm\
|
||||
lIxTJNwU+OeqipN+/j7MqhjKOGIlvbpiPQQLC6/ffF2vbzL0nzMUuSyvaQzyGGkSYM2xU\
|
||||
Ft06aNivoQTvU2GgGmUK6MvadoY38hhW2LCMkoZcNfgCqJ26lO1O0sEO6zHsk3IVz6vsK\
|
||||
iJ2Hq6VCo7hu123wNegmujHWQSGyf8JeudZjKzfi0OFRRvvm4QAKyBWf0MgrW1F8SFDnV\
|
||||
fkq8amCB7NhdwhgLWbN+21NitNwWYknoEWe1m6hmGZDgDT32uxzWxCV8QqqrpH/ZggViE\
|
||||
r9uMgoy4lYaWqP7G5WKvvechc62aqnsNEYhH26A5QgzmlNyvB+KPFvPsYzxDnSCjOoRSL\
|
||||
x7GG86wT59QZyx5sGKww3rcCNrwNZaRvek3OO4sOAs+SGCuRTjr6XuvA==";
|
||||
|
||||
const CLEARTEXT_B64: &str =
|
||||
"eyJpZCI6IjVxUnNnWFdSSlpYciIsImhpc3RVcmkiOiJmaWxlOi8vL1VzZXJzL2phc29u\
|
||||
L0xpYnJhcnkvQXBwbGljYXRpb24lMjBTdXBwb3J0L0ZpcmVmb3gvUHJvZmlsZXMva3Nn\
|
||||
ZDd3cGsuTG9jYWxTeW5jU2VydmVyL3dlYXZlL2xvZ3MvIiwidGl0bGUiOiJJbmRleCBv\
|
||||
ZiBmaWxlOi8vL1VzZXJzL2phc29uL0xpYnJhcnkvQXBwbGljYXRpb24gU3VwcG9ydC9G\
|
||||
aXJlZm94L1Byb2ZpbGVzL2tzZ2Q3d3BrLkxvY2FsU3luY1NlcnZlci93ZWF2ZS9sb2dz\
|
||||
LyIsInZpc2l0cyI6W3siZGF0ZSI6MTMxOTE0OTAxMjM3MjQyNSwidHlwZSI6MX1dfQ==";
|
||||
|
||||
#[test]
|
||||
fn test_decrypt() {
|
||||
let key_bytes = base64::decode(KEY_B64).unwrap();
|
||||
let key = aead::Key::new(&LEGACY_SYNC_AES_256_CBC_HMAC_SHA256, &key_bytes).unwrap();
|
||||
let ciphertext_and_tag = base64::decode(&CIPHERTEXT_AND_TAG_B64).unwrap();
|
||||
|
||||
let iv = base64::decode(IV_B64).unwrap();
|
||||
let nonce =
|
||||
aead::Nonce::try_assume_unique_for_key(&LEGACY_SYNC_AES_256_CBC_HMAC_SHA256, &iv)
|
||||
.unwrap();
|
||||
let cleartext_bytes = open(&key, nonce, &aead::Aad::empty(), &ciphertext_and_tag).unwrap();
|
||||
|
||||
let expected_cleartext_bytes = base64::decode(&CLEARTEXT_B64).unwrap();
|
||||
assert_eq!(&expected_cleartext_bytes, &cleartext_bytes);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_encrypt() {
|
||||
let key_bytes = base64::decode(KEY_B64).unwrap();
|
||||
let key = aead::Key::new(&LEGACY_SYNC_AES_256_CBC_HMAC_SHA256, &key_bytes).unwrap();
|
||||
let cleartext = base64::decode(&CLEARTEXT_B64).unwrap();
|
||||
|
||||
let iv = base64::decode(IV_B64).unwrap();
|
||||
let nonce =
|
||||
aead::Nonce::try_assume_unique_for_key(&LEGACY_SYNC_AES_256_CBC_HMAC_SHA256, &iv)
|
||||
.unwrap();
|
||||
let ciphertext_bytes = seal(&key, nonce, &aead::Aad::empty(), &cleartext).unwrap();
|
||||
|
||||
let expected_ciphertext_bytes = base64::decode(&CIPHERTEXT_AND_TAG_B64).unwrap();
|
||||
assert_eq!(&expected_ciphertext_bytes, &ciphertext_bytes);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_roundtrip() {
|
||||
let key_bytes = base64::decode(KEY_B64).unwrap();
|
||||
let key = aead::Key::new(&LEGACY_SYNC_AES_256_CBC_HMAC_SHA256, &key_bytes).unwrap();
|
||||
let cleartext = base64::decode(&CLEARTEXT_B64).unwrap();
|
||||
|
||||
let iv = base64::decode(IV_B64).unwrap();
|
||||
let nonce =
|
||||
aead::Nonce::try_assume_unique_for_key(&LEGACY_SYNC_AES_256_CBC_HMAC_SHA256, &iv)
|
||||
.unwrap();
|
||||
let ciphertext_bytes = seal(&key, nonce, &aead::Aad::empty(), &cleartext).unwrap();
|
||||
let nonce =
|
||||
aead::Nonce::try_assume_unique_for_key(&LEGACY_SYNC_AES_256_CBC_HMAC_SHA256, &iv)
|
||||
.unwrap();
|
||||
let roundtriped_cleartext_bytes =
|
||||
open(&key, nonce, &aead::Aad::empty(), &ciphertext_bytes).unwrap();
|
||||
assert_eq!(roundtriped_cleartext_bytes, cleartext);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decrypt_fails_with_wrong_aes_key() {
|
||||
let mut key_bytes = base64::decode(KEY_B64).unwrap();
|
||||
key_bytes[1] = b'X';
|
||||
|
||||
let key = aead::Key::new(&LEGACY_SYNC_AES_256_CBC_HMAC_SHA256, &key_bytes).unwrap();
|
||||
let ciphertext_and_tag = base64::decode(&CIPHERTEXT_AND_TAG_B64).unwrap();
|
||||
let iv = base64::decode(IV_B64).unwrap();
|
||||
let nonce =
|
||||
aead::Nonce::try_assume_unique_for_key(&LEGACY_SYNC_AES_256_CBC_HMAC_SHA256, &iv)
|
||||
.unwrap();
|
||||
|
||||
let err = open(&key, nonce, &aead::Aad::empty(), &ciphertext_and_tag).unwrap_err();
|
||||
match err.kind() {
|
||||
ErrorKind::NSSError(_) | ErrorKind::InternalError => {}
|
||||
_ => panic!("unexpected error kind"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decrypt_fails_with_wrong_hmac_key() {
|
||||
let mut key_bytes = base64::decode(KEY_B64).unwrap();
|
||||
key_bytes[60] = b'X';
|
||||
|
||||
let key = aead::Key::new(&LEGACY_SYNC_AES_256_CBC_HMAC_SHA256, &key_bytes).unwrap();
|
||||
let ciphertext_and_tag = base64::decode(&CIPHERTEXT_AND_TAG_B64).unwrap();
|
||||
let iv = base64::decode(IV_B64).unwrap();
|
||||
let nonce =
|
||||
aead::Nonce::try_assume_unique_for_key(&LEGACY_SYNC_AES_256_CBC_HMAC_SHA256, &iv)
|
||||
.unwrap();
|
||||
|
||||
let err = open(&key, nonce, &aead::Aad::empty(), &ciphertext_and_tag).unwrap_err();
|
||||
match err.kind() {
|
||||
ErrorKind::InternalError => {}
|
||||
_ => panic!("unexpected error kind"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decrypt_fails_with_modified_ciphertext() {
|
||||
let key_bytes = base64::decode(KEY_B64).unwrap();
|
||||
let key = aead::Key::new(&LEGACY_SYNC_AES_256_CBC_HMAC_SHA256, &key_bytes).unwrap();
|
||||
let iv = base64::decode(IV_B64).unwrap();
|
||||
let nonce =
|
||||
aead::Nonce::try_assume_unique_for_key(&LEGACY_SYNC_AES_256_CBC_HMAC_SHA256, &iv)
|
||||
.unwrap();
|
||||
|
||||
let mut ciphertext_and_tag = base64::decode(&CIPHERTEXT_AND_TAG_B64).unwrap();
|
||||
ciphertext_and_tag[4] = b'Z';
|
||||
|
||||
let err = open(&key, nonce, &aead::Aad::empty(), &ciphertext_and_tag).unwrap_err();
|
||||
match err.kind() {
|
||||
ErrorKind::InternalError => {}
|
||||
_ => panic!("unexpected error kind"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decrypt_fails_with_modified_tag() {
|
||||
let key_bytes = base64::decode(KEY_B64).unwrap();
|
||||
let key = aead::Key::new(&LEGACY_SYNC_AES_256_CBC_HMAC_SHA256, &key_bytes).unwrap();
|
||||
let iv = base64::decode(IV_B64).unwrap();
|
||||
let nonce =
|
||||
aead::Nonce::try_assume_unique_for_key(&LEGACY_SYNC_AES_256_CBC_HMAC_SHA256, &iv)
|
||||
.unwrap();
|
||||
|
||||
let mut ciphertext_and_tag = base64::decode(&CIPHERTEXT_AND_TAG_B64).unwrap();
|
||||
let end = ciphertext_and_tag.len();
|
||||
ciphertext_and_tag[end - 4] = b'Z';
|
||||
|
||||
let err = open(&key, nonce, &aead::Aad::empty(), &ciphertext_and_tag).unwrap_err();
|
||||
match err.kind() {
|
||||
ErrorKind::InternalError => {}
|
||||
_ => panic!("unexpected error kind"),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,146 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// This file contains code that was copied from the ring crate which is under
|
||||
// the ISC license, reproduced below:
|
||||
|
||||
// Copyright 2015-2017 Brian Smith.
|
||||
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
|
||||
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
|
||||
// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
use crate::{aead, error::*};
|
||||
use nss::aes;
|
||||
|
||||
/// AES-128 in GCM mode with 128-bit tags and 96 bit nonces.
|
||||
pub static AES_128_GCM: aead::Algorithm = aead::Algorithm {
|
||||
key_len: 16,
|
||||
tag_len: 16,
|
||||
nonce_len: 96 / 8,
|
||||
open,
|
||||
seal,
|
||||
};
|
||||
|
||||
/// AES-256 in GCM mode with 128-bit tags and 96 bit nonces.
|
||||
pub static AES_256_GCM: aead::Algorithm = aead::Algorithm {
|
||||
key_len: 32,
|
||||
tag_len: 16,
|
||||
nonce_len: 96 / 8,
|
||||
open,
|
||||
seal,
|
||||
};
|
||||
|
||||
pub(crate) fn open(
|
||||
key: &aead::Key,
|
||||
nonce: aead::Nonce,
|
||||
aad: &aead::Aad<'_>,
|
||||
ciphertext_and_tag: &[u8],
|
||||
) -> Result<Vec<u8>> {
|
||||
aes_gcm(
|
||||
key,
|
||||
nonce,
|
||||
aad,
|
||||
ciphertext_and_tag,
|
||||
aead::Direction::Opening,
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn seal(
|
||||
key: &aead::Key,
|
||||
nonce: aead::Nonce,
|
||||
aad: &aead::Aad<'_>,
|
||||
plaintext: &[u8],
|
||||
) -> Result<Vec<u8>> {
|
||||
aes_gcm(key, nonce, aad, plaintext, aead::Direction::Sealing)
|
||||
}
|
||||
|
||||
fn aes_gcm(
|
||||
key: &aead::Key,
|
||||
nonce: aead::Nonce,
|
||||
aad: &aead::Aad<'_>,
|
||||
data: &[u8],
|
||||
direction: aead::Direction,
|
||||
) -> Result<Vec<u8>> {
|
||||
Ok(aes::aes_gcm_crypt(
|
||||
&key.key_value,
|
||||
&nonce.0,
|
||||
&aad.0,
|
||||
data,
|
||||
direction.to_nss_operation(),
|
||||
)?)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
// Test vector from the AES-GCM spec.
|
||||
const NONCE_HEX: &str = "cafebabefacedbaddecaf888";
|
||||
const KEY_HEX: &str = "feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308";
|
||||
const AAD_HEX: &str = "feedfacedeadbeeffeedfacedeadbeefabaddad2";
|
||||
const TAG_HEX: &str = "76fc6ece0f4e1768cddf8853bb2d551b";
|
||||
const CIPHERTEXT_HEX: &str =
|
||||
"522dc1f099567d07f47f37a32a84427d643a8cdcbfe5c0c97598a2bd2555d1aa8cb08e48590dbb3da7b08b1056828838c5f61e6393ba7a0abcc9f662";
|
||||
const CLEARTEXT_HEX: &str =
|
||||
"d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39";
|
||||
|
||||
#[test]
|
||||
fn test_decrypt() {
|
||||
let key_bytes = hex::decode(KEY_HEX).unwrap();
|
||||
let key = aead::Key::new(&AES_256_GCM, &key_bytes).unwrap();
|
||||
let mut ciphertext_and_tag = hex::decode(&CIPHERTEXT_HEX).unwrap();
|
||||
let tag = hex::decode(&TAG_HEX).unwrap();
|
||||
ciphertext_and_tag.extend(&tag);
|
||||
|
||||
let iv = hex::decode(NONCE_HEX).unwrap();
|
||||
let nonce = aead::Nonce::try_assume_unique_for_key(&AES_256_GCM, &iv).unwrap();
|
||||
let aad_bytes = hex::decode(AAD_HEX).unwrap();
|
||||
let aad = aead::Aad::from(&aad_bytes);
|
||||
let cleartext_bytes = open(&key, nonce, &aad, &ciphertext_and_tag).unwrap();
|
||||
let encoded_cleartext = hex::encode(cleartext_bytes);
|
||||
assert_eq!(&CLEARTEXT_HEX, &encoded_cleartext);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_encrypt() {
|
||||
let key_bytes = hex::decode(KEY_HEX).unwrap();
|
||||
let key = aead::Key::new(&AES_256_GCM, &key_bytes).unwrap();
|
||||
let cleartext = hex::decode(&CLEARTEXT_HEX).unwrap();
|
||||
|
||||
let iv = hex::decode(NONCE_HEX).unwrap();
|
||||
let nonce = aead::Nonce::try_assume_unique_for_key(&AES_256_GCM, &iv).unwrap();
|
||||
let aad_bytes = hex::decode(AAD_HEX).unwrap();
|
||||
let aad = aead::Aad::from(&aad_bytes);
|
||||
let ciphertext_bytes = seal(&key, nonce, &aad, &cleartext).unwrap();
|
||||
|
||||
let expected_tag = hex::decode(&TAG_HEX).unwrap();
|
||||
let mut expected_ciphertext = hex::decode(&CIPHERTEXT_HEX).unwrap();
|
||||
expected_ciphertext.extend(&expected_tag);
|
||||
assert_eq!(&expected_ciphertext, &ciphertext_bytes);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_roundtrip() {
|
||||
let key_bytes = hex::decode(KEY_HEX).unwrap();
|
||||
let key = aead::Key::new(&AES_256_GCM, &key_bytes).unwrap();
|
||||
let cleartext = hex::decode(&CLEARTEXT_HEX).unwrap();
|
||||
|
||||
let iv = hex::decode(NONCE_HEX).unwrap();
|
||||
let nonce = aead::Nonce::try_assume_unique_for_key(&AES_256_GCM, &iv).unwrap();
|
||||
let ciphertext_bytes = seal(&key, nonce, &aead::Aad::empty(), &cleartext).unwrap();
|
||||
let nonce = aead::Nonce::try_assume_unique_for_key(&AES_256_GCM, &iv).unwrap();
|
||||
let roundtriped_cleartext_bytes =
|
||||
open(&key, nonce, &aead::Aad::empty(), &ciphertext_bytes).unwrap();
|
||||
assert_eq!(roundtriped_cleartext_bytes, cleartext);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,387 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// This file contains code that was copied from the ring crate which is under
|
||||
// the ISC license, reproduced below:
|
||||
|
||||
// Copyright 2015-2017 Brian Smith.
|
||||
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
|
||||
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
|
||||
// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
use crate::error::*;
|
||||
use core::marker::PhantomData;
|
||||
pub use ec::{Curve, EcKey};
|
||||
use nss::{ec, ecdh};
|
||||
|
||||
/// A key agreement algorithm.
|
||||
#[derive(PartialEq)]
|
||||
pub struct Algorithm {
|
||||
pub(crate) curve_id: ec::Curve,
|
||||
}
|
||||
|
||||
pub static ECDH_P256: Algorithm = Algorithm {
|
||||
curve_id: ec::Curve::P256,
|
||||
};
|
||||
|
||||
/// How many times the key may be used.
|
||||
pub trait Lifetime {}
|
||||
|
||||
/// The key may be used at most once.
|
||||
pub struct Ephemeral {}
|
||||
impl Lifetime for Ephemeral {}
|
||||
|
||||
/// The key may be used more than once.
|
||||
pub struct Static {}
|
||||
impl Lifetime for Static {}
|
||||
|
||||
/// A key pair for key agreement.
|
||||
pub struct KeyPair<U: Lifetime> {
|
||||
private_key: PrivateKey<U>,
|
||||
public_key: PublicKey,
|
||||
}
|
||||
|
||||
impl<U: Lifetime> KeyPair<U> {
|
||||
/// Generate a new key pair for the given algorithm.
|
||||
pub fn generate(alg: &'static Algorithm) -> Result<Self> {
|
||||
let (prv_key, pub_key) = ec::generate_keypair(alg.curve_id)?;
|
||||
Ok(Self {
|
||||
private_key: PrivateKey {
|
||||
alg,
|
||||
wrapped: prv_key,
|
||||
usage: PhantomData,
|
||||
},
|
||||
public_key: PublicKey {
|
||||
alg,
|
||||
wrapped: pub_key,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
pub fn from_private_key(private_key: PrivateKey<U>) -> Result<Self> {
|
||||
let public_key = private_key
|
||||
.compute_public_key()
|
||||
.map_err(|_| ErrorKind::InternalError)?;
|
||||
Ok(Self {
|
||||
private_key,
|
||||
public_key,
|
||||
})
|
||||
}
|
||||
|
||||
/// The private key.
|
||||
pub fn private_key(&self) -> &PrivateKey<U> {
|
||||
&self.private_key
|
||||
}
|
||||
|
||||
/// The public key.
|
||||
pub fn public_key(&self) -> &PublicKey {
|
||||
&self.public_key
|
||||
}
|
||||
|
||||
/// Split the key pair apart.
|
||||
pub fn split(self) -> (PrivateKey<U>, PublicKey) {
|
||||
(self.private_key, self.public_key)
|
||||
}
|
||||
}
|
||||
|
||||
impl KeyPair<Static> {
|
||||
pub fn from(private_key: PrivateKey<Static>) -> Result<Self> {
|
||||
Self::from_private_key(private_key)
|
||||
}
|
||||
}
|
||||
|
||||
/// A public key for key agreement.
|
||||
pub struct PublicKey {
|
||||
wrapped: ec::PublicKey,
|
||||
alg: &'static Algorithm,
|
||||
}
|
||||
|
||||
impl PublicKey {
|
||||
#[inline]
|
||||
pub fn to_bytes(&self) -> Result<Vec<u8>> {
|
||||
Ok(self.wrapped.to_bytes()?)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn algorithm(&self) -> &'static Algorithm {
|
||||
self.alg
|
||||
}
|
||||
}
|
||||
|
||||
/// A private key for key agreement.
|
||||
pub struct PrivateKey<U: Lifetime> {
|
||||
wrapped: ec::PrivateKey,
|
||||
alg: &'static Algorithm,
|
||||
usage: PhantomData<U>,
|
||||
}
|
||||
|
||||
impl<U: Lifetime> PrivateKey<U> {
|
||||
#[inline]
|
||||
pub fn algorithm(&self) -> &'static Algorithm {
|
||||
self.alg
|
||||
}
|
||||
|
||||
pub fn compute_public_key(&self) -> Result<PublicKey> {
|
||||
let pub_key = self.wrapped.convert_to_public_key()?;
|
||||
Ok(PublicKey {
|
||||
wrapped: pub_key,
|
||||
alg: self.alg,
|
||||
})
|
||||
}
|
||||
|
||||
/// Ephemeral agreement.
|
||||
/// This consumes `self`, ensuring that the private key can
|
||||
/// only be used for a single agreement operation.
|
||||
pub fn agree(
|
||||
self,
|
||||
peer_public_key_alg: &Algorithm,
|
||||
peer_public_key: &[u8],
|
||||
) -> Result<InputKeyMaterial> {
|
||||
agree_(
|
||||
&self.wrapped,
|
||||
self.alg,
|
||||
peer_public_key_alg,
|
||||
peer_public_key,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl PrivateKey<Static> {
|
||||
/// Static agreement.
|
||||
/// This borrows `self`, allowing the private key to
|
||||
/// be used for a multiple agreement operations.
|
||||
pub fn agree_static(
|
||||
&self,
|
||||
peer_public_key_alg: &Algorithm,
|
||||
peer_public_key: &[u8],
|
||||
) -> Result<InputKeyMaterial> {
|
||||
agree_(
|
||||
&self.wrapped,
|
||||
self.alg,
|
||||
peer_public_key_alg,
|
||||
peer_public_key,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn import(ec_key: &EcKey) -> Result<Self> {
|
||||
// XXX: we should just let ec::PrivateKey own alg.
|
||||
let alg = match ec_key.curve() {
|
||||
Curve::P256 => &ECDH_P256,
|
||||
};
|
||||
let private_key = ec::PrivateKey::import(ec_key)?;
|
||||
Ok(Self {
|
||||
wrapped: private_key,
|
||||
alg,
|
||||
usage: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn export(&self) -> Result<EcKey> {
|
||||
Ok(self.wrapped.export()?)
|
||||
}
|
||||
|
||||
/// The whole point of having `Ephemeral` and `Static` lifetimes is to use the type
|
||||
/// system to avoid re-using the same ephemeral key. However for tests we might need
|
||||
/// to create a "static" ephemeral key.
|
||||
pub fn _tests_only_dangerously_convert_to_ephemeral(self) -> PrivateKey<Ephemeral> {
|
||||
PrivateKey::<Ephemeral> {
|
||||
wrapped: self.wrapped,
|
||||
alg: self.alg,
|
||||
usage: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn agree_(
|
||||
my_private_key: &ec::PrivateKey,
|
||||
my_alg: &Algorithm,
|
||||
peer_public_key_alg: &Algorithm,
|
||||
peer_public_key: &[u8],
|
||||
) -> Result<InputKeyMaterial> {
|
||||
let alg = &my_alg;
|
||||
if peer_public_key_alg != *alg {
|
||||
return Err(ErrorKind::InternalError.into());
|
||||
}
|
||||
let pub_key = ec::PublicKey::from_bytes(my_private_key.curve(), peer_public_key)?;
|
||||
let value = ecdh::ecdh_agreement(my_private_key, &pub_key)?;
|
||||
Ok(InputKeyMaterial { value })
|
||||
}
|
||||
|
||||
/// The result of a key agreement operation, to be fed into a KDF.
|
||||
#[must_use]
|
||||
pub struct InputKeyMaterial {
|
||||
value: Vec<u8>,
|
||||
}
|
||||
|
||||
impl InputKeyMaterial {
|
||||
/// Calls `kdf` with the raw key material and then returns what `kdf`
|
||||
/// returns, consuming `Self` so that the key material can only be used
|
||||
/// once.
|
||||
pub fn derive<F, R>(self, kdf: F) -> R
|
||||
where
|
||||
F: FnOnce(&[u8]) -> R,
|
||||
{
|
||||
kdf(&self.value)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
// Test vectors copied from:
|
||||
// https://chromium.googlesource.com/chromium/src/+/56f1232/components/test/data/webcrypto/ecdh.json#5
|
||||
|
||||
const PUB_KEY_1_B64: &str =
|
||||
"BLunVoWkR67xRdAohVblFBWn1Oosb3kH_baxw1yfIYFfthSm4LIY35vDD-5LE454eB7TShn919DVVGZ_7tWdjTE";
|
||||
const PRIV_KEY_1_JWK_D: &str = "CQ8uF_-zB1NftLO6ytwKM3Cnuol64PQw5qOuCzQJeFU";
|
||||
const PRIV_KEY_1_JWK_X: &str = "u6dWhaRHrvFF0CiFVuUUFafU6ixveQf9trHDXJ8hgV8";
|
||||
const PRIV_KEY_1_JWK_Y: &str = "thSm4LIY35vDD-5LE454eB7TShn919DVVGZ_7tWdjTE";
|
||||
|
||||
const PRIV_KEY_2_JWK_D: &str = "uN2YSQvxuxhQQ9Y1XXjYi1vr2ZTdzuoDX18PYu4LU-0";
|
||||
const PRIV_KEY_2_JWK_X: &str = "S2S3tjygMB0DkM-N9jYUgGLt_9_H6km5P9V6V_KS4_4";
|
||||
const PRIV_KEY_2_JWK_Y: &str = "03j8Tyqgrc4R4FAUV2C7-im96yMmfmO_5Om6Kr8YP3o";
|
||||
|
||||
const SHARED_SECRET_HEX: &str =
|
||||
"163FAA3FC4815D47345C8E959F707B2F1D3537E7B2EA1DAEC23CA8D0A242CFF3";
|
||||
|
||||
fn load_priv_key_1() -> PrivateKey<Static> {
|
||||
let private_key = base64::decode_config(PRIV_KEY_1_JWK_D, base64::URL_SAFE_NO_PAD).unwrap();
|
||||
let x = base64::decode_config(PRIV_KEY_1_JWK_X, base64::URL_SAFE_NO_PAD).unwrap();
|
||||
let y = base64::decode_config(PRIV_KEY_1_JWK_Y, base64::URL_SAFE_NO_PAD).unwrap();
|
||||
PrivateKey::<Static>::import(
|
||||
&EcKey::from_coordinates(Curve::P256, &private_key, &x, &y).unwrap(),
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn load_priv_key_2() -> PrivateKey<Static> {
|
||||
let private_key = base64::decode_config(PRIV_KEY_2_JWK_D, base64::URL_SAFE_NO_PAD).unwrap();
|
||||
let x = base64::decode_config(PRIV_KEY_2_JWK_X, base64::URL_SAFE_NO_PAD).unwrap();
|
||||
let y = base64::decode_config(PRIV_KEY_2_JWK_Y, base64::URL_SAFE_NO_PAD).unwrap();
|
||||
PrivateKey::<Static>::import(
|
||||
&EcKey::from_coordinates(Curve::P256, &private_key, &x, &y).unwrap(),
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_static_agreement() {
|
||||
let pub_key = base64::decode_config(PUB_KEY_1_B64, base64::URL_SAFE_NO_PAD).unwrap();
|
||||
let prv_key = load_priv_key_2();
|
||||
let ikm = prv_key.agree_static(&ECDH_P256, &pub_key).unwrap();
|
||||
let secret = ikm
|
||||
.derive(|z| -> Result<Vec<u8>> { Ok(z.to_vec()) })
|
||||
.unwrap();
|
||||
let secret_b64 = hex::encode_upper(&secret);
|
||||
assert_eq!(secret_b64, *SHARED_SECRET_HEX);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ephemeral_agreement_roundtrip() {
|
||||
let (our_prv_key, our_pub_key) =
|
||||
KeyPair::<Ephemeral>::generate(&ECDH_P256).unwrap().split();
|
||||
let (their_prv_key, their_pub_key) =
|
||||
KeyPair::<Ephemeral>::generate(&ECDH_P256).unwrap().split();
|
||||
let ikm_1 = our_prv_key
|
||||
.agree(&ECDH_P256, &their_pub_key.to_bytes().unwrap())
|
||||
.unwrap();
|
||||
let secret_1 = ikm_1
|
||||
.derive(|z| -> Result<Vec<u8>> { Ok(z.to_vec()) })
|
||||
.unwrap();
|
||||
let ikm_2 = their_prv_key
|
||||
.agree(&ECDH_P256, &our_pub_key.to_bytes().unwrap())
|
||||
.unwrap();
|
||||
let secret_2 = ikm_2
|
||||
.derive(|z| -> Result<Vec<u8>> { Ok(z.to_vec()) })
|
||||
.unwrap();
|
||||
assert_eq!(secret_1, secret_2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_compute_public_key() {
|
||||
let (prv_key, pub_key) = KeyPair::<Static>::generate(&ECDH_P256).unwrap().split();
|
||||
let computed_pub_key = prv_key.compute_public_key().unwrap();
|
||||
assert_eq!(
|
||||
computed_pub_key.to_bytes().unwrap(),
|
||||
pub_key.to_bytes().unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_compute_public_key_known_values() {
|
||||
let prv_key = load_priv_key_1();
|
||||
let pub_key = base64::decode_config(PUB_KEY_1_B64, base64::URL_SAFE_NO_PAD).unwrap();
|
||||
let computed_pub_key = prv_key.compute_public_key().unwrap();
|
||||
assert_eq!(computed_pub_key.to_bytes().unwrap(), pub_key.as_slice());
|
||||
|
||||
let prv_key = load_priv_key_2();
|
||||
let computed_pub_key = prv_key.compute_public_key().unwrap();
|
||||
assert_ne!(computed_pub_key.to_bytes().unwrap(), pub_key.as_slice());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_keys_byte_representations_roundtrip() {
|
||||
let key_pair = KeyPair::<Static>::generate(&ECDH_P256).unwrap();
|
||||
let prv_key = key_pair.private_key;
|
||||
let extracted_pub_key = prv_key.compute_public_key().unwrap();
|
||||
let ec_key = prv_key.export().unwrap();
|
||||
let prv_key_reconstructed = PrivateKey::<Static>::import(&ec_key).unwrap();
|
||||
let extracted_pub_key_reconstructed = prv_key.compute_public_key().unwrap();
|
||||
let ec_key_reconstructed = prv_key_reconstructed.export().unwrap();
|
||||
assert_eq!(ec_key.curve(), ec_key_reconstructed.curve());
|
||||
assert_eq!(ec_key.public_key(), ec_key_reconstructed.public_key());
|
||||
assert_eq!(ec_key.private_key(), ec_key_reconstructed.private_key());
|
||||
assert_eq!(
|
||||
extracted_pub_key.to_bytes().unwrap(),
|
||||
extracted_pub_key_reconstructed.to_bytes().unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_agreement_rejects_invalid_pubkeys() {
|
||||
let prv_key = load_priv_key_2();
|
||||
|
||||
let mut invalid_pub_key =
|
||||
base64::decode_config(PUB_KEY_1_B64, base64::URL_SAFE_NO_PAD).unwrap();
|
||||
invalid_pub_key[0] = invalid_pub_key[0].wrapping_add(1);
|
||||
assert!(prv_key.agree_static(&ECDH_P256, &invalid_pub_key).is_err());
|
||||
|
||||
let mut invalid_pub_key =
|
||||
base64::decode_config(PUB_KEY_1_B64, base64::URL_SAFE_NO_PAD).unwrap();
|
||||
invalid_pub_key[0] = 0x02;
|
||||
assert!(prv_key.agree_static(&ECDH_P256, &invalid_pub_key).is_err());
|
||||
|
||||
let mut invalid_pub_key =
|
||||
base64::decode_config(PUB_KEY_1_B64, base64::URL_SAFE_NO_PAD).unwrap();
|
||||
invalid_pub_key[64] = invalid_pub_key[0].wrapping_add(1);
|
||||
assert!(prv_key.agree_static(&ECDH_P256, &invalid_pub_key).is_err());
|
||||
|
||||
let mut invalid_pub_key = [0u8; 65];
|
||||
assert!(prv_key.agree_static(&ECDH_P256, &invalid_pub_key).is_err());
|
||||
invalid_pub_key[0] = 0x04;
|
||||
|
||||
let mut invalid_pub_key = base64::decode_config(PUB_KEY_1_B64, base64::URL_SAFE_NO_PAD)
|
||||
.unwrap()
|
||||
.to_vec();
|
||||
invalid_pub_key = invalid_pub_key[0..64].to_vec();
|
||||
assert!(prv_key.agree_static(&ECDH_P256, &invalid_pub_key).is_err());
|
||||
|
||||
// From FxA tests at https://github.com/mozilla/fxa-crypto-relier/blob/04f61dc/test/deriver/DeriverUtils.js#L78
|
||||
// We trust that NSS will do the right thing here, but it seems worthwhile to confirm for completeness.
|
||||
let invalid_pub_key_b64 = "BEogZ-rnm44oJkKsOE6Tc7NwFMgmntf7Btm_Rc4atxcqq99Xq1RWNTFpk99pdQOSjUvwELss51PkmAGCXhLfMV0";
|
||||
let invalid_pub_key =
|
||||
base64::decode_config(invalid_pub_key_b64, base64::URL_SAFE_NO_PAD).unwrap();
|
||||
assert!(prv_key.agree_static(&ECDH_P256, &invalid_pub_key).is_err());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// This file contains code that was copied from the ring crate which is under
|
||||
// the ISC license, reproduced below:
|
||||
|
||||
// Copyright 2015-2017 Brian Smith.
|
||||
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
|
||||
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
|
||||
// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
use crate::error::*;
|
||||
|
||||
/// Returns `Ok(())` if `a == b` and `Error` otherwise.
|
||||
/// The comparison of `a` and `b` is done in constant time with respect to the
|
||||
/// contents of each, but NOT in constant time with respect to the lengths of
|
||||
/// `a` and `b`.
|
||||
pub fn verify_slices_are_equal(a: &[u8], b: &[u8]) -> Result<()> {
|
||||
if nss::secport::secure_memcmp(a, b) {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ErrorKind::InternalError.into())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
#[test]
|
||||
fn does_compare() {
|
||||
assert!(verify_slices_are_equal(b"bobo", b"bobo").is_ok());
|
||||
assert!(verify_slices_are_equal(b"bobo", b"obob").is_err());
|
||||
assert!(verify_slices_are_equal(b"bobo", b"notbobo").is_err());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// This file contains code that was copied from the ring crate which is under
|
||||
// the ISC license, reproduced below:
|
||||
|
||||
// Copyright 2015-2017 Brian Smith.
|
||||
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
|
||||
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
|
||||
// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
use crate::error::*;
|
||||
|
||||
pub use nss::pk11::context::HashAlgorithm::{self as Algorithm, *};
|
||||
|
||||
/// A calculated digest value.
|
||||
#[derive(Clone)]
|
||||
pub struct Digest {
|
||||
pub(crate) value: Vec<u8>,
|
||||
pub(crate) algorithm: Algorithm,
|
||||
}
|
||||
|
||||
impl Digest {
|
||||
pub fn algorithm(&self) -> &Algorithm {
|
||||
&self.algorithm
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for Digest {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
self.value.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the digest of data using the given digest algorithm.
|
||||
pub fn digest(algorithm: &Algorithm, data: &[u8]) -> Result<Digest> {
|
||||
let value = nss::pk11::context::hash_buf(algorithm, data)?;
|
||||
Ok(Digest {
|
||||
value,
|
||||
algorithm: algorithm.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use hex;
|
||||
|
||||
const MESSAGE: &[u8] = b"bobo";
|
||||
const DIGEST_HEX: &str = "bf0c97708b849de696e7373508b13c5ea92bafa972fc941d694443e494a4b84d";
|
||||
|
||||
#[test]
|
||||
fn sha256_digest() {
|
||||
assert_eq!(hex::encode(&digest(&SHA256, MESSAGE).unwrap()), DIGEST_HEX);
|
||||
assert_ne!(
|
||||
hex::encode(&digest(&SHA256, b"notbobo").unwrap()),
|
||||
DIGEST_HEX
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn digest_cleanly_rejects_gigantic_messages() {
|
||||
let message = vec![0; (std::i32::MAX as usize) + 1];
|
||||
assert!(digest(&SHA256, &message).is_err());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,491 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use crate::{
|
||||
aead,
|
||||
agreement::{self, Curve, EcKey},
|
||||
digest, hkdf, hmac, rand,
|
||||
};
|
||||
use ece::{
|
||||
Aes128GcmEceWebPush, AesGcmEceWebPush, Crypto, ErrorKind, Result,
|
||||
ECE_WEBPUSH_AUTH_SECRET_LENGTH,
|
||||
};
|
||||
pub use ece::{
|
||||
AesGcmEncryptedBlock, EcKeyComponents, Error, LocalKeyPair, RemotePublicKey, WebPushParams,
|
||||
};
|
||||
|
||||
// Can't `impl From<crate::Result<T>> for ece::Result<T>`
|
||||
// because we don't own either struct :-(
|
||||
fn convert<T>(r: crate::Result<T>) -> Result<T> {
|
||||
match r {
|
||||
Ok(v) => Ok(v),
|
||||
Err(_) => Err(ErrorKind::CryptoError.into()),
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RemotePublicKeyImpl {
|
||||
raw: Vec<u8>,
|
||||
}
|
||||
|
||||
impl RemotePublicKeyImpl {
|
||||
fn _from_raw(bytes: &[u8]) -> crate::Result<RemotePublicKeyImpl> {
|
||||
Ok(RemotePublicKeyImpl {
|
||||
raw: bytes.to_owned(),
|
||||
})
|
||||
}
|
||||
|
||||
fn _as_raw(&self) -> crate::Result<Vec<u8>> {
|
||||
Ok(self.raw.to_vec())
|
||||
}
|
||||
}
|
||||
|
||||
impl ece::crypto_backend::RemotePublicKey for RemotePublicKeyImpl {
|
||||
fn from_raw(bytes: &[u8]) -> Result<Self> {
|
||||
convert(Self::_from_raw(bytes))
|
||||
}
|
||||
|
||||
fn as_raw(&self) -> Result<Vec<u8>> {
|
||||
convert(self._as_raw())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LocalKeyPairImpl {
|
||||
wrapped: agreement::KeyPair<agreement::Static>,
|
||||
}
|
||||
|
||||
impl LocalKeyPairImpl {
|
||||
fn _from_raw_components(components: &EcKeyComponents) -> crate::Result<Self> {
|
||||
let ec_key = EcKey::new(
|
||||
Curve::P256,
|
||||
components.private_key(),
|
||||
components.public_key(),
|
||||
);
|
||||
let priv_key = agreement::PrivateKey::<agreement::Static>::import(&ec_key)?;
|
||||
let wrapped = agreement::KeyPair::<agreement::Static>::from_private_key(priv_key)?;
|
||||
Ok(LocalKeyPairImpl { wrapped })
|
||||
}
|
||||
|
||||
fn _raw_components(&self) -> crate::Result<EcKeyComponents> {
|
||||
let ec_key = self.wrapped.private_key().export()?;
|
||||
Ok(EcKeyComponents::new(
|
||||
ec_key.private_key(),
|
||||
ec_key.public_key(),
|
||||
))
|
||||
}
|
||||
|
||||
fn agree(&self, peer: &RemotePublicKeyImpl) -> crate::Result<Vec<u8>> {
|
||||
self.wrapped
|
||||
.private_key()
|
||||
.agree_static(&agreement::ECDH_P256, &peer._as_raw()?)?
|
||||
.derive(|z| Ok(z.to_vec()))
|
||||
}
|
||||
|
||||
fn _generate_random() -> crate::Result<Self> {
|
||||
let wrapped = agreement::KeyPair::<agreement::Static>::generate(&agreement::ECDH_P256)?;
|
||||
Ok(LocalKeyPairImpl { wrapped })
|
||||
}
|
||||
|
||||
fn _pub_as_raw(&self) -> crate::Result<Vec<u8>> {
|
||||
let bytes = self.wrapped.public_key().to_bytes()?;
|
||||
Ok(bytes.to_vec())
|
||||
}
|
||||
}
|
||||
|
||||
impl ece::crypto_backend::LocalKeyPair for LocalKeyPairImpl {
|
||||
fn generate_random() -> Result<Self> {
|
||||
convert(Self::_generate_random())
|
||||
}
|
||||
|
||||
fn from_raw_components(components: &EcKeyComponents) -> Result<Self> {
|
||||
convert(Self::_from_raw_components(components))
|
||||
}
|
||||
|
||||
fn raw_components(&self) -> Result<EcKeyComponents> {
|
||||
convert(self._raw_components())
|
||||
}
|
||||
|
||||
fn pub_as_raw(&self) -> Result<Vec<u8>> {
|
||||
convert(self._pub_as_raw())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CryptoImpl;
|
||||
|
||||
impl CryptoImpl {
|
||||
fn _generate_ephemeral_keypair() -> crate::Result<LocalKeyPairImpl> {
|
||||
Ok(LocalKeyPairImpl::_generate_random()?)
|
||||
}
|
||||
|
||||
fn _compute_ecdh_secret(
|
||||
remote: &RemotePublicKeyImpl,
|
||||
local: &LocalKeyPairImpl,
|
||||
) -> crate::Result<Vec<u8>> {
|
||||
Ok(local.agree(&remote)?)
|
||||
}
|
||||
|
||||
fn _hkdf_sha256(salt: &[u8], secret: &[u8], info: &[u8], len: usize) -> crate::Result<Vec<u8>> {
|
||||
let salt = hmac::SigningKey::new(&digest::SHA256, &salt);
|
||||
let mut out = vec![0u8; len];
|
||||
hkdf::extract_and_expand(&salt, &secret, &info, &mut out)?;
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
fn _aes_gcm_128_encrypt(
|
||||
key: &[u8],
|
||||
iv: &[u8],
|
||||
data: &[u8],
|
||||
tag_len: usize,
|
||||
) -> crate::Result<Vec<u8>> {
|
||||
let key = aead::SealingKey::new(&aead::AES_128_GCM, key)?;
|
||||
let nonce = aead::Nonce::try_assume_unique_for_key(&aead::AES_128_GCM, iv)?;
|
||||
// XXX TODO: tag_len is always fixed.
|
||||
assert!(tag_len == aead::AES_128_GCM.tag_len());
|
||||
Ok(aead::seal(&key, nonce, aead::Aad::empty(), data)?)
|
||||
}
|
||||
|
||||
fn _aes_gcm_128_decrypt(
|
||||
key: &[u8],
|
||||
iv: &[u8],
|
||||
data: &[u8],
|
||||
tag: &[u8],
|
||||
) -> crate::Result<Vec<u8>> {
|
||||
let key = aead::OpeningKey::new(&aead::AES_128_GCM, key)?;
|
||||
let nonce = aead::Nonce::try_assume_unique_for_key(&aead::AES_128_GCM, iv)?;
|
||||
// XXX TODO: is it necessary to receive `data` and `tag` separately?
|
||||
let mut ciphertext = vec![0u8; data.len() + tag.len()];
|
||||
ciphertext[0..data.len()].copy_from_slice(&data);
|
||||
ciphertext[data.len()..].copy_from_slice(&tag);
|
||||
Ok(aead::open(&key, nonce, aead::Aad::empty(), &ciphertext)?)
|
||||
}
|
||||
|
||||
fn _random(dest: &mut [u8]) -> crate::Result<()> {
|
||||
Ok(rand::fill(dest)?)
|
||||
}
|
||||
}
|
||||
|
||||
impl ece::crypto_backend::Crypto for CryptoImpl {
|
||||
type RemotePublicKey = RemotePublicKeyImpl;
|
||||
type LocalKeyPair = LocalKeyPairImpl;
|
||||
|
||||
fn generate_ephemeral_keypair() -> Result<Self::LocalKeyPair> {
|
||||
convert(Self::_generate_ephemeral_keypair())
|
||||
}
|
||||
|
||||
fn compute_ecdh_secret(
|
||||
remote: &Self::RemotePublicKey,
|
||||
local: &Self::LocalKeyPair,
|
||||
) -> Result<Vec<u8>> {
|
||||
convert(Self::_compute_ecdh_secret(remote, local))
|
||||
}
|
||||
|
||||
fn hkdf_sha256(salt: &[u8], secret: &[u8], info: &[u8], len: usize) -> Result<Vec<u8>> {
|
||||
convert(Self::_hkdf_sha256(salt, secret, info, len))
|
||||
}
|
||||
|
||||
fn aes_gcm_128_encrypt(key: &[u8], iv: &[u8], data: &[u8], tag_len: usize) -> Result<Vec<u8>> {
|
||||
convert(Self::_aes_gcm_128_encrypt(key, iv, data, tag_len))
|
||||
}
|
||||
|
||||
fn aes_gcm_128_decrypt(key: &[u8], iv: &[u8], data: &[u8], tag: &[u8]) -> Result<Vec<u8>> {
|
||||
convert(Self::_aes_gcm_128_decrypt(key, iv, data, tag))
|
||||
}
|
||||
|
||||
fn random(dest: &mut [u8]) -> ece::Result<()> {
|
||||
convert(Self::_random(dest))
|
||||
}
|
||||
}
|
||||
|
||||
pub type Aes128GcmEceWebPushImpl = Aes128GcmEceWebPush<CryptoImpl>;
|
||||
pub type AesGcmEceWebPushImpl = AesGcmEceWebPush<CryptoImpl>;
|
||||
|
||||
/// Generate a local ECE key pair and auth nonce.
|
||||
pub fn generate_keypair_and_auth_secret(
|
||||
) -> Result<(LocalKeyPairImpl, [u8; ECE_WEBPUSH_AUTH_SECRET_LENGTH])> {
|
||||
let local_key_pair = LocalKeyPairImpl::generate_random()?;
|
||||
let mut auth_secret = [0u8; ECE_WEBPUSH_AUTH_SECRET_LENGTH];
|
||||
CryptoImpl::random(&mut auth_secret)?;
|
||||
Ok((local_key_pair, auth_secret))
|
||||
}
|
||||
|
||||
/// Encrypt a block using default AES128GCM encoding.
|
||||
///
|
||||
/// param remote_pub &[u8] - The remote public key
|
||||
/// param remote_auth &u8 - The remote authorization token
|
||||
/// param salt &[u8] - The locally generated random salt
|
||||
/// param data &[u8] - The data to encrypt
|
||||
///
|
||||
pub fn encrypt(remote_pub: &[u8], remote_auth: &[u8], salt: &[u8], data: &[u8]) -> Result<Vec<u8>> {
|
||||
let remote_key = RemotePublicKeyImpl::from_raw(remote_pub)?;
|
||||
let local_key = CryptoImpl::generate_ephemeral_keypair()?;
|
||||
// Pick a random size to pad to.
|
||||
// It's a sampled random, endianness doesn't really matter here.
|
||||
// XXX TODO: could we pick too much padding here, and fail with EncryptPadding error?
|
||||
let mut padr = [0u8; 2];
|
||||
CryptoImpl::random(&mut padr)?;
|
||||
let pad = ((usize::from(padr[0]) + (usize::from(padr[1]) << 8)) % 4095) + 1;
|
||||
let params = WebPushParams::new(4096, pad, Vec::from(salt));
|
||||
Ok(Aes128GcmEceWebPushImpl::encrypt_with_keys(
|
||||
&local_key,
|
||||
&remote_key,
|
||||
&remote_auth,
|
||||
data,
|
||||
params,
|
||||
)?)
|
||||
}
|
||||
|
||||
/// Decrypt a block using default AES128GCM encoding.
|
||||
///
|
||||
/// param components &str - The locally generated private key components.
|
||||
/// param auth &str - The locally generated auth token (this value was shared with the encryptor)
|
||||
/// param data &[u8] - The encrypted data block
|
||||
///
|
||||
pub fn decrypt(components: &EcKeyComponents, auth: &[u8], data: &[u8]) -> Result<Vec<u8>> {
|
||||
let priv_key = LocalKeyPairImpl::from_raw_components(components).unwrap();
|
||||
Aes128GcmEceWebPushImpl::decrypt(&priv_key, &auth, data)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
// Copy-pasta from the tests in the ECE crate.
|
||||
|
||||
fn generate_keys() -> Result<(LocalKeyPairImpl, LocalKeyPairImpl)> {
|
||||
let local_key = LocalKeyPairImpl::generate_random()?;
|
||||
let remote_key = LocalKeyPairImpl::generate_random()?;
|
||||
Ok((local_key, remote_key))
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn try_encrypt(
|
||||
private_key: &str,
|
||||
public_key: &str,
|
||||
remote_pub_key: &str,
|
||||
auth_secret: &str,
|
||||
salt: &str,
|
||||
pad_length: usize,
|
||||
rs: u32,
|
||||
plaintext: &str,
|
||||
) -> Result<String> {
|
||||
let private_key = hex::decode(private_key).unwrap();
|
||||
let public_key = hex::decode(public_key).unwrap();
|
||||
let ec_key = EcKeyComponents::new(private_key, public_key);
|
||||
let local_key_pair = LocalKeyPairImpl::from_raw_components(&ec_key)?;
|
||||
let remote_pub_key = hex::decode(remote_pub_key).unwrap();
|
||||
let remote_pub_key = RemotePublicKeyImpl::from_raw(&remote_pub_key)?;
|
||||
let auth_secret = hex::decode(auth_secret).unwrap();
|
||||
let salt = hex::decode(salt).unwrap();
|
||||
let plaintext = plaintext.as_bytes();
|
||||
let params = WebPushParams::new(rs, pad_length, salt);
|
||||
let ciphertext = Aes128GcmEceWebPushImpl::encrypt_with_keys(
|
||||
&local_key_pair,
|
||||
&remote_pub_key,
|
||||
&auth_secret,
|
||||
&plaintext,
|
||||
params,
|
||||
)?;
|
||||
Ok(hex::encode(ciphertext))
|
||||
}
|
||||
|
||||
fn try_decrypt(
|
||||
private_key: &str,
|
||||
public_key: &str,
|
||||
auth_secret: &str,
|
||||
payload: &str,
|
||||
) -> Result<String> {
|
||||
let private_key = hex::decode(private_key).unwrap();
|
||||
let public_key = hex::decode(public_key).unwrap();
|
||||
let ec_key = EcKeyComponents::new(private_key, public_key);
|
||||
let plaintext = decrypt(
|
||||
&ec_key,
|
||||
&hex::decode(auth_secret).unwrap(),
|
||||
&hex::decode(payload).unwrap(),
|
||||
)?;
|
||||
Ok(String::from_utf8(plaintext).unwrap())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_e2e() {
|
||||
let (local_key, remote_key) = generate_keys().unwrap();
|
||||
let plaintext = b"When I grow up, I want to be a watermelon";
|
||||
let mut auth_secret = vec![0u8; 16];
|
||||
CryptoImpl::random(&mut auth_secret).unwrap();
|
||||
let remote_public =
|
||||
RemotePublicKeyImpl::from_raw(&remote_key.pub_as_raw().unwrap()).unwrap();
|
||||
let params = WebPushParams::default();
|
||||
let ciphertext = Aes128GcmEceWebPushImpl::encrypt_with_keys(
|
||||
&local_key,
|
||||
&remote_public,
|
||||
&auth_secret,
|
||||
plaintext,
|
||||
params,
|
||||
)
|
||||
.unwrap();
|
||||
let decrypted =
|
||||
Aes128GcmEceWebPushImpl::decrypt(&remote_key, &auth_secret, &ciphertext).unwrap();
|
||||
assert_eq!(decrypted, plaintext.to_vec());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_conv_fn() -> Result<()> {
|
||||
let (local_key, auth) = generate_keypair_and_auth_secret()?;
|
||||
let plaintext = b"Mary had a little lamb, with some nice mint jelly";
|
||||
let mut salt = vec![0u8; 16];
|
||||
CryptoImpl::random(&mut salt)?;
|
||||
let encoded = encrypt(&local_key.pub_as_raw()?, &auth, &salt, plaintext)?;
|
||||
let decoded = decrypt(&local_key.raw_components()?, &auth, &encoded)?;
|
||||
assert_eq!(decoded, plaintext.to_vec());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn try_encrypt_ietf_rfc() {
|
||||
let ciphertext = try_encrypt(
|
||||
"c9f58f89813e9f8e872e71f42aa64e1757c9254dcc62b72ddc010bb4043ea11c",
|
||||
"04fe33f4ab0dea71914db55823f73b54948f41306d920732dbb9a59a53286482200e597a7b7bc260ba1c227998580992e93973002f3012a28ae8f06bbb78e5ec0f",
|
||||
"042571b2becdfde360551aaf1ed0f4cd366c11cebe555f89bcb7b186a53339173168ece2ebe018597bd30479b86e3c8f8eced577ca59187e9246990db682008b0e",
|
||||
"05305932a1c7eabe13b6cec9fda48882",
|
||||
"0c6bfaadad67958803092d454676f397",
|
||||
0,
|
||||
4096,
|
||||
"When I grow up, I want to be a watermelon",
|
||||
).unwrap();
|
||||
assert_eq!(ciphertext, "0c6bfaadad67958803092d454676f397000010004104fe33f4ab0dea71914db55823f73b54948f41306d920732dbb9a59a53286482200e597a7b7bc260ba1c227998580992e93973002f3012a28ae8f06bbb78e5ec0ff297de5b429bba7153d3a4ae0caa091fd425f3b4b5414add8ab37a19c1bbb05cf5cb5b2a2e0562d558635641ec52812c6c8ff42e95ccb86be7cd");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn try_encrypt_rs_24_pad_6() {
|
||||
let ciphertext = try_encrypt(
|
||||
"0f28beaf7e27793c03638dc2973a15b0016e1b367cbffda8861ab175f31bce02",
|
||||
"0430efcb1eb043b805e4e44bab35f82513c33fedb28700f7e568ac8b61e8d835665a51eb6679b2db228a10c0c3fe5077062848d9bb3d60279f93ce35484728aa1f",
|
||||
"04c0d1a812b291291dd7beee358713c126c589f3633c26d1a201311de036dc10931e4ee142f61921a3ea5864e872a93841a52944e5b3f6accecce8c828fb04a4cd",
|
||||
"9d7735d8de1962b98394b07ffe287e20",
|
||||
"ff805030a108e114e6c17fad6186a1a6",
|
||||
6,
|
||||
24,
|
||||
"I am the very model of a modern Major-General, I've information vegetable, animal, and mineral",
|
||||
).unwrap();
|
||||
assert_eq!(ciphertext, "ff805030a108e114e6c17fad6186a1a600000018410430efcb1eb043b805e4e44bab35f82513c33fedb28700f7e568ac8b61e8d835665a51eb6679b2db228a10c0c3fe5077062848d9bb3d60279f93ce35484728aa1fd2c1713949aec98f05096c7298fd3f51c4f818fafa1fe615d8447b3a05406031f6401ac24f2a775ca52456a921b83b9e0042c3a63e1afa1ae012774d9d775be8d19419451d37ff59ff592e84f07440a63fc17f5cabcb9a50eddaf75370db647f94447d3f166269d8711df0f57e56049576e1130a5a5e1f94ba8a5d0b0007c6c0fd2998429e7d63d4ef919798f46ecf5f0b28fb80f5b2439de26b8a52200bc7d6af7a4840721fe8be8524a691b6ef0edae90bb6f5927894819b831b45b53f8401fe022dbb64ed7565350904ac0b517135d7f8abbc98127fb163864d4d4a307425b2cd43db22af267d71c37146994a8c4805adc341bfba27af09fd80bd5eff51d877282a2fbfbfeb10199e7879e4b9d13a46d57fb7d786824853e1cc89cafbaf14de1e924c944feb8b626ce0207d6f9fa9d849eecac69b42d6e7a23bd5124d49622b44b35c5b15fb0e6a7781a503f1a4e062e015d557d95d44d9d8b0799b3aafce83d5d4");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn try_encrypt_rs_18_pad_31() {
|
||||
// This test is also interesting because the data length (54) is a
|
||||
// multiple of rs (18). We'll allocate memory to hold 4 records, but only
|
||||
// write 3.
|
||||
let ciphertext = try_encrypt(
|
||||
"7830577bafcfc45828da0c40aab09fb227bfeae068aab8c064222acbe6effd34",
|
||||
"0400b833e481a99aa330dcb277922d5f84af2e9ce611ad2ad3ed0f5b431912d35ea72fc5bf76b769d9526778f5abfa058650988da5e531ff82d1a7043794c71706",
|
||||
"04c3d714cb42e2b0a1d6f98599e2f186b8c2ba6f6fab5e09a2abca865c0805892b2c3729330ef83dc9df4b44362b039a0609d36beb9321a431ec123506ddd90f24",
|
||||
"e4d7b79decdede12c3e9d90d3e05730f",
|
||||
"e49888d2b28f277f847bc5de96f0f81b",
|
||||
31,
|
||||
18,
|
||||
"Push the button, Frank!",
|
||||
).unwrap();
|
||||
assert_eq!(ciphertext, "e49888d2b28f277f847bc5de96f0f81b00000012410400b833e481a99aa330dcb277922d5f84af2e9ce611ad2ad3ed0f5b431912d35ea72fc5bf76b769d9526778f5abfa058650988da5e531ff82d1a7043794c717063aeb958bf116bccf50742fd4d69bd0ea7e3f611c709bf2cdf5cd47c6426cb8323b5398c43c0d0b92cc982da1c24ce5fee2b203f7ad78ca44f0490f3407f5fee883266ee47035195de0fe6d8a75e487df256db597a75e45ae4fb55b8259cb0b2d19e7b05714267eb560ae072b7a665951917a068732df309be256f90f2adda32f05feaa5e9b0695bca2ccf22aaefc7da9ceebc5d40c12d32adb5c84cb320af944016095362febba4ffa4a99830e4958ea2bba508cb683a58d2027d4b74726a853b24b47ccba751abe9d9ab2da9ec2ba9c7ccf0cf17305bae314d38a687618b0772fcb71d4419027a4bf435cb721aad74efc179981b7169604bf97ecac41e73884456933734818132923b56c152d6c9e59aef995aca59de0bf2c803a07180889670a08e64a20d2bfa853e0112872947baaaffb510cc9e75d6310ed6aacbd2e0ba3a29be42c6532ea4e3346e1f0571646371c71665e3fac9d76faee1f122e64d490dd2a3e31816eab583f172841a075d205f318714a8c70ce0f327f4d92b8c9dcb813e6d24fe85633f1a9c7c1e4a1fb314dd5fe3e280e3908f36c8cbfb80b7d9243abaffa65c216cf1aa8b8d626a630dfe8186ce977a5b8f3649d3753b9176c367e4e07f220a175806138e88825a2f3498420582b96209658bbfa8f2ba6933a83c25edb269187796542e2ac49b8078636bddc268e11625e8bff9f0a343d3a4c06080ef0803b8dcd8e841d0e2759e483ea19b903324d9ec4d52f491acef3eeff441c37881c7593eac31621337a5e8659f93e20079b0e26ebfe56c10455d10971130bd2a2c159c74f48b2e526530a76f64cca2efb246e793d11fb75a668018e70c3107100f81ba3b16ae40a838f18d4c47f1d7132f174688ec5382394e0119921731a16879b858ff38f72851ea3d9f5263fec5a606d1271a89b84cca53ed73c5254e245bf8f2f27c2c1c87f39eea78c7017c8c6b5ab01663032b58da31057285e56c203f4e48d6789c66b2695a900e00482bd846559ecddd40264b38e279647d1ec0fccdc1881838bbe0c835e2690ef058b8f6a03e29cd9eb9584e97fbc309773c3688e5e03f9d38e3e4548738a5f569c59147d3e823cccac71d5e8825d5134ce9813cd0b8f9627a3dbfa45b83a59c83d2b4d3ad437778a3cb1bc77ba16c92306f4261a2a1f0d5c7edaecf926f92d7c9dfcae87513a68b8c7ef7c63264b858767c11aaa41d27c636f52e28551e93a969cdc96d43867b7cbd68fe0357bd33415faf22aaeebc957f4b5737a04ab7277b4ed4008f09edaff5a6db69f6cb06f3d0b76688906b2f53b27e63f3728ba2eda505fb1b32f81dddc6d305fd5949edd05490cb1618f0ce1430e9f5edf50012dc3");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decrypt_rs_24_pad_0() {
|
||||
let plaintext = try_decrypt(
|
||||
"c899d11d32e2b7e6fe7498786f50f23b98ace5397ad261de39ba6449ecc12cad",
|
||||
"04b3fc72e4365cbeb5c78862396eb5e66fd905b483a1b3eac04695f4b802e5b493c5e3b70eb427b6c728b2b204fc255fa218cb45f34d235242705e0d1ea87236e0",
|
||||
"996fad8b50aa2d02b83f26412b2e2aee",
|
||||
"495ce6c8de93a4539e862e8634993cbb0000001841043c3378a2c0ab954e1498718e85f08bb723fb7d25e135a663fe385884eb8192336bf90a54ed720f1c045c0b405e9bbc3a2142b16c89086734c374ebaf7099e6427e2d32c8ada5018703c54b10b481e1027d7209d8c6b43553fa133afa597f2ddc45a5ba8140944e6490bb8d6d99ba1d02e60d95f48ce644477c17231d95b97a4f95dd"
|
||||
).unwrap();
|
||||
assert_eq!(plaintext, "I am the walrus");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decrypt_rs_49_pad_84_ciphertext_len_falls_on_record_boundary() {
|
||||
let plaintext = try_decrypt(
|
||||
"67004a4ea820deed8e49db5e9480e63d3ea3cce1ae8e1a60609713d527d001ef",
|
||||
"04014e8f14b92da07ce083b93f96367e87b217a47f7ef2ee93a9d343aa063e575a9f30d59c690c6a39b3fc815b150ca7dd149601741337b53507a51f41b173a721",
|
||||
"95f17570e508ef6a2b2ad1b4f5cade33",
|
||||
"fb2883cec1c4fcadd6d1371f6ea491e00000003141042d441ee7f9ff6a0329a64927d0524fdbe7b22c6fb65e10ab4fdc038f94420a0ca3fa28dad36c84ec91a162eae078faad2c1ced78de8113e19602b20e894f4976b973e2fcf682fa0c8ccd9af3d5bff1ede16fad5a31ce19d38b5e1fe1f78a4fad842bbc10254c2c6cdd96a2b55284d972c53cad8c3bacb10f5f57eb0d4a4333b604102ba117cae29108fbd9f629a8ba6960dd01945b39ed37ba706c434a10fd2bd2094ff9249bcdad45135f5fe45fcd38071f8b2d3941afda439810d77aacaf7ce50b54325bf58c9503337d073785a323dfa343"
|
||||
).unwrap();
|
||||
assert_eq!(plaintext, "Hello, world");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decrypt_ietf_rfc() {
|
||||
let plaintext = try_decrypt(
|
||||
"ab5757a70dd4a53e553a6bbf71ffefea2874ec07a6b379e3c48f895a02dc33de",
|
||||
"042571b2becdfde360551aaf1ed0f4cd366c11cebe555f89bcb7b186a53339173168ece2ebe018597bd30479b86e3c8f8eced577ca59187e9246990db682008b0e",
|
||||
"05305932a1c7eabe13b6cec9fda48882",
|
||||
"0c6bfaadad67958803092d454676f397000010004104fe33f4ab0dea71914db55823f73b54948f41306d920732dbb9a59a53286482200e597a7b7bc260ba1c227998580992e93973002f3012a28ae8f06bbb78e5ec0ff297de5b429bba7153d3a4ae0caa091fd425f3b4b5414add8ab37a19c1bbb05cf5cb5b2a2e0562d558635641ec52812c6c8ff42e95ccb86be7cd"
|
||||
).unwrap();
|
||||
assert_eq!(plaintext, "When I grow up, I want to be a watermelon");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decrypt_rs_18_pad_0() {
|
||||
let plaintext = try_decrypt(
|
||||
"27433fab8970b3cb5284b61183efb46286562cd2a7330d8cae960911a5571d0c",
|
||||
"04515d4326355652399da24b2be9241e633b5cf14faf0cf3a6fd60317b954c0a2f4848548004b27b0cf7480bc810c6bec03a8fb79c8ea00fc8b05e00f8834563ef",
|
||||
"d65a04df95f2db5e604839f717dcde79",
|
||||
"7caebdbc20938ee340a946f1bd4f68f100000012410437cfdb5223d9f95eaa02f6ed940ff22eaf05b3622e949dc3ce9f335e6ef9b26aeaacca0f74080a8b364592f2ccc6d5eddd43004b70b91887d144d9fa93f16c3bc7ea68f4fd547a94eca84b16e138a6080177"
|
||||
).unwrap();
|
||||
assert_eq!(plaintext, "1");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decrypt_missing_header_block() {
|
||||
let err = try_decrypt(
|
||||
"1be83f38332ef09681faf3f307b1ff2e10cab78cc7cdab683ac0ee92ac3f6ee1",
|
||||
"04dba991ca215343f36bdd3e857cafde3d18bf57f1835b2833bad414f0884162051ac96a0b24490037d07cf528e4e18e100a1a64eb744748544bf1e220dabacf2c",
|
||||
"3471bb98481e02533bf39542bcf3dba4",
|
||||
"45b74d2b69be9b074de3b35aa87e7c15611d",
|
||||
)
|
||||
.unwrap_err();
|
||||
match err.kind() {
|
||||
ErrorKind::HeaderTooShort => {}
|
||||
_ => panic!("Expected HeaderTooShort error"),
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decrypt_truncated_sender_key() {
|
||||
let err = try_decrypt(
|
||||
"ce88e8e0b3057a4752eb4c8fa931eb621c302da5ad03b81af459cf6735560cae",
|
||||
"04a325d99084c40de0ce722a042c448d94a32691721ca79e3cf745e78c69886194b02cea19224176795a9d4dbbb2073af2ccd6fa6f0a4c7c4968556be502a3ba81",
|
||||
"5c31e0d96d9a139899ac0969d359f740",
|
||||
"de5b696b87f1a15cb6adebdd79d6f99e000000120100b6bc1826c37c9f73dd6b4859c2b505181952",
|
||||
)
|
||||
.unwrap_err();
|
||||
match err.kind() {
|
||||
ErrorKind::InvalidKeyLength => {}
|
||||
_ => panic!("Expected InvalidKeyLength error"),
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decrypt_truncated_auth_secret() {
|
||||
let err = try_decrypt(
|
||||
"60c7636a517de7039a0ac2d0e3064400794c78e7e049398129a227cee0f9a801",
|
||||
"04fdd04128a85c05896d7f81fe118bdcb887b9f3c1ff4183adc4c824d128607300e986b2dfb5a610e5af43e408a00730584f93e3dfddfc44737d5f08fb2d6f8916",
|
||||
"355a38cd6d9bef15990e2d3308dbd600",
|
||||
"8115f4988b8c392a7bacb43c8f1ac5650000001241041994483c541e9bc39a6af03ff713aa7745c284e138a42a2435b797b20c4b698cf5118b4f8555317c190eabebfab749c164d3f6bdebe0d441719131a357d8890a13c4dbd4b16ff3dd5a83f7c91ad6e040ac42730a7f0b3cd3245e9f8d6ff31c751d410cfd"
|
||||
).unwrap_err();
|
||||
match err.kind() {
|
||||
ErrorKind::CryptoError => {}
|
||||
_ => panic!("Expected CryptoError"),
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decrypt_early_final_record() {
|
||||
let err = try_decrypt(
|
||||
"5dda1d918bc407ba3cda12cb8014d49aa7e0269002820304466bc80034ca9240",
|
||||
"04c95c6520dad11e8f6a1bf8031a40c2a4ee1045c1903be06a1dfa7f829cceb2de02481ae6bd0476121b12c5532d0b231788077efa0683a5bfe0d62339b251cb35",
|
||||
"40c241fde4269ee1e6d725592d982718",
|
||||
"dbe215507d1ad3d2eaeabeae6e874d8f0000001241047bc4343f34a8348cdc4e462ffc7c40aa6a8c61a739c4c41d45125505f70e9fc5f9efa86852dd488dcf8e8ea2cafb75e07abd5ee7c9d5c038bafef079571b0bda294411ce98c76dd031c0e580577a4980a375e45ed30429be0e2ee9da7e6df8696d01b8ec"
|
||||
).unwrap_err();
|
||||
match err.kind() {
|
||||
ErrorKind::DecryptPadding => {}
|
||||
_ => panic!("Expected DecryptPadding error"),
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use failure::Fail;
|
||||
|
||||
#[derive(Debug, Fail)]
|
||||
pub enum ErrorKind {
|
||||
#[fail(display = "NSS error: {}", _0)]
|
||||
NSSError(#[fail(cause)] nss::Error),
|
||||
#[fail(display = "Internal crypto error")]
|
||||
InternalError,
|
||||
#[fail(display = "Conversion error: {}", _0)]
|
||||
ConversionError(#[fail(cause)] std::num::TryFromIntError),
|
||||
}
|
||||
|
||||
error_support::define_error! {
|
||||
ErrorKind {
|
||||
(ConversionError, std::num::TryFromIntError),
|
||||
(NSSError, nss::Error),
|
||||
}
|
||||
}
|
|
@ -0,0 +1,157 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use crate::{digest, hmac, rand};
|
||||
use hawk::crypto as hc;
|
||||
|
||||
impl From<crate::Error> for hc::CryptoError {
|
||||
// Our errors impl `Fail`, so we can do this.
|
||||
fn from(e: crate::Error) -> Self {
|
||||
hc::CryptoError::Other(e.into())
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct RcCryptoCryptographer;
|
||||
|
||||
impl hc::HmacKey for crate::hmac::SigningKey {
|
||||
fn sign(&self, data: &[u8]) -> Result<Vec<u8>, hc::CryptoError> {
|
||||
let digest = hmac::sign(&self, data)?;
|
||||
Ok(digest.as_ref().into())
|
||||
}
|
||||
}
|
||||
|
||||
// I don't really see a reason to bother doing incremental hashing here. A
|
||||
// one-shot is going to be faster in many cases anyway, and the higher memory
|
||||
// usage probably doesn't matter given our usage.
|
||||
struct NssHasher {
|
||||
buffer: Vec<u8>,
|
||||
algorithm: &'static digest::Algorithm,
|
||||
}
|
||||
|
||||
impl hc::Hasher for NssHasher {
|
||||
fn update(&mut self, data: &[u8]) -> Result<(), hc::CryptoError> {
|
||||
self.buffer.extend_from_slice(data);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn finish(&mut self) -> Result<Vec<u8>, hc::CryptoError> {
|
||||
let digest = digest::digest(self.algorithm, &self.buffer)?;
|
||||
let bytes: &[u8] = digest.as_ref();
|
||||
Ok(bytes.to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
impl hc::Cryptographer for RcCryptoCryptographer {
|
||||
fn rand_bytes(&self, output: &mut [u8]) -> Result<(), hc::CryptoError> {
|
||||
rand::fill(output)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn new_key(
|
||||
&self,
|
||||
algorithm: hawk::DigestAlgorithm,
|
||||
key: &[u8],
|
||||
) -> Result<Box<dyn hc::HmacKey>, hc::CryptoError> {
|
||||
let k = hmac::SigningKey::new(to_rc_crypto_algorithm(algorithm)?, key);
|
||||
Ok(Box::new(k))
|
||||
}
|
||||
|
||||
fn constant_time_compare(&self, a: &[u8], b: &[u8]) -> bool {
|
||||
crate::constant_time::verify_slices_are_equal(a, b).is_ok()
|
||||
}
|
||||
|
||||
fn new_hasher(
|
||||
&self,
|
||||
algorithm: hawk::DigestAlgorithm,
|
||||
) -> Result<Box<dyn hc::Hasher>, hc::CryptoError> {
|
||||
Ok(Box::new(NssHasher {
|
||||
algorithm: to_rc_crypto_algorithm(algorithm)?,
|
||||
buffer: vec![],
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
fn to_rc_crypto_algorithm(
|
||||
algorithm: hawk::DigestAlgorithm,
|
||||
) -> Result<&'static digest::Algorithm, hc::CryptoError> {
|
||||
match algorithm {
|
||||
hawk::DigestAlgorithm::Sha256 => Ok(&digest::SHA256),
|
||||
algo => Err(hc::CryptoError::UnsupportedDigest(algo)),
|
||||
}
|
||||
}
|
||||
|
||||
// Note: this doesn't initialize NSS!
|
||||
pub(crate) fn init() {
|
||||
hawk::crypto::set_cryptographer(&crate::hawk_crypto::RcCryptoCryptographer)
|
||||
.expect("Failed to initialize `hawk` cryptographer!")
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
|
||||
// Based on rust-hawk's hash_consistency. This fails if we've messed up the hashing.
|
||||
#[test]
|
||||
fn test_hawk_hashing() {
|
||||
crate::ensure_initialized();
|
||||
let mut hasher1 = hawk::PayloadHasher::new("text/plain", hawk::SHA256).unwrap();
|
||||
hasher1.update("pày").unwrap();
|
||||
hasher1.update("load").unwrap();
|
||||
let hash1 = hasher1.finish().unwrap();
|
||||
|
||||
let mut hasher2 = hawk::PayloadHasher::new("text/plain", hawk::SHA256).unwrap();
|
||||
hasher2.update("pàyload").unwrap();
|
||||
let hash2 = hasher2.finish().unwrap();
|
||||
|
||||
let hash3 = hawk::PayloadHasher::hash("text/plain", hawk::SHA256, "pàyload").unwrap();
|
||||
|
||||
let hash4 = // "pàyload" as utf-8 bytes
|
||||
hawk::PayloadHasher::hash("text/plain", hawk::SHA256, &[112, 195, 160, 121, 108, 111, 97, 100]).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
hash1,
|
||||
&[
|
||||
228, 238, 241, 224, 235, 114, 158, 112, 211, 254, 118, 89, 25, 236, 87, 176, 181,
|
||||
54, 61, 135, 42, 223, 188, 103, 194, 59, 83, 36, 136, 31, 198, 50
|
||||
]
|
||||
);
|
||||
assert_eq!(hash2, hash1);
|
||||
assert_eq!(hash3, hash1);
|
||||
assert_eq!(hash4, hash1);
|
||||
}
|
||||
|
||||
// Based on rust-hawk's test_make_mac. This fails if we've messed up the signing.
|
||||
#[test]
|
||||
fn test_hawk_signing() {
|
||||
crate::ensure_initialized();
|
||||
let key = hawk::Key::new(
|
||||
&[
|
||||
11u8, 19, 228, 209, 79, 189, 200, 59, 166, 47, 86, 254, 235, 184, 120, 197, 75,
|
||||
152, 201, 79, 115, 61, 111, 242, 219, 187, 173, 14, 227, 108, 60, 232,
|
||||
],
|
||||
hawk::SHA256,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let mac = hawk::mac::Mac::new(
|
||||
hawk::mac::MacType::Header,
|
||||
&key,
|
||||
std::time::UNIX_EPOCH + std::time::Duration::new(1000, 100),
|
||||
"nonny",
|
||||
"POST",
|
||||
"mysite.com",
|
||||
443,
|
||||
"/v1/api",
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
mac.as_ref(),
|
||||
&[
|
||||
192, 227, 235, 121, 157, 185, 197, 79, 189, 214, 235, 139, 9, 232, 99, 55, 67, 30,
|
||||
68, 0, 150, 187, 192, 238, 21, 200, 209, 107, 245, 159, 243, 178
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,132 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// This file contains code that was copied from the ring crate which is under
|
||||
// the ISC license, reproduced below:
|
||||
|
||||
// Copyright 2015-2017 Brian Smith.
|
||||
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
|
||||
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
|
||||
// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
use crate::{error::*, hmac};
|
||||
|
||||
pub fn extract_and_expand(
|
||||
salt: &hmac::SigningKey,
|
||||
secret: &[u8],
|
||||
info: &[u8],
|
||||
out: &mut [u8],
|
||||
) -> Result<()> {
|
||||
let prk = extract(salt, secret)?;
|
||||
expand(&prk, info, out)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn extract(salt: &hmac::SigningKey, secret: &[u8]) -> Result<hmac::SigningKey> {
|
||||
let prk = hmac::sign(salt, secret)?;
|
||||
Ok(hmac::SigningKey::new(salt.digest_algorithm(), prk.as_ref()))
|
||||
}
|
||||
|
||||
pub fn expand(prk: &hmac::SigningKey, info: &[u8], out: &mut [u8]) -> Result<()> {
|
||||
let mut derived =
|
||||
nss::pk11::sym_key::hkdf_expand(prk.digest_alg, &prk.key_value, info, out.len())?;
|
||||
out.swap_with_slice(&mut derived[0..out.len()]);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::digest;
|
||||
use hex;
|
||||
|
||||
// NSS limits the size of derived key material to 576 bytes due to fixed-size `key_block` buffer here:
|
||||
// https://dxr.mozilla.org/mozilla-central/rev/3c0f78074b727fbae112b6eda111d4c4d30cc3ec/security/nss/lib/softoken/pkcs11c.c#7758
|
||||
const NSS_MAX_DERIVED_KEY_MATERIAL: usize = 576;
|
||||
|
||||
#[test]
|
||||
fn hkdf_produces_correct_result() {
|
||||
let secret = hex::decode("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b").unwrap();
|
||||
let salt = hex::decode("000102030405060708090a0b0c").unwrap();
|
||||
let info = hex::decode("f0f1f2f3f4f5f6f7f8f9").unwrap();
|
||||
let expected_out = hex::decode(
|
||||
"3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf34007208d5b887185865",
|
||||
)
|
||||
.unwrap();
|
||||
let salt = hmac::SigningKey::new(&digest::SHA256, &salt);
|
||||
let mut out = vec![0u8; expected_out.len()];
|
||||
extract_and_expand(&salt, &secret, &info, &mut out).unwrap();
|
||||
assert_eq!(out, expected_out);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hkdf_rejects_gigantic_salt() {
|
||||
if (std::u32::MAX as usize) < std::usize::MAX {
|
||||
let salt_bytes = vec![0; (std::u32::MAX as usize) + 1];
|
||||
let salt = hmac::SigningKey {
|
||||
digest_alg: &digest::SHA256,
|
||||
key_value: salt_bytes,
|
||||
};
|
||||
let mut out = vec![0u8; 8];
|
||||
assert!(extract_and_expand(&salt, b"secret", b"info", &mut out).is_err());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hkdf_rejects_gigantic_secret() {
|
||||
if (std::u32::MAX as usize) < std::usize::MAX {
|
||||
let salt = hmac::SigningKey::new(&digest::SHA256, b"salt");
|
||||
let secret = vec![0; (std::u32::MAX as usize) + 1];
|
||||
let mut out = vec![0u8; 8];
|
||||
assert!(extract_and_expand(&salt, secret.as_slice(), b"info", &mut out).is_err());
|
||||
}
|
||||
}
|
||||
|
||||
// N.B. the `info `parameter is a `c_ulong`, and I can't figure out how to check whether
|
||||
// `c_ulong` is smaller than `usize` in order to write a `hkdf_rejects_gigantic_info` test.
|
||||
|
||||
#[test]
|
||||
fn hkdf_rejects_gigantic_output_buffers() {
|
||||
let salt = hmac::SigningKey::new(&digest::SHA256, b"salt");
|
||||
let mut out = vec![0u8; NSS_MAX_DERIVED_KEY_MATERIAL + 1];
|
||||
assert!(extract_and_expand(&salt, b"secret", b"info", &mut out).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hkdf_rejects_zero_length_output_buffer() {
|
||||
let salt = hmac::SigningKey::new(&digest::SHA256, b"salt");
|
||||
let mut out = vec![0u8; 0];
|
||||
assert!(extract_and_expand(&salt, b"secret", b"info", &mut out).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hkdf_can_produce_small_output() {
|
||||
let salt = hmac::SigningKey::new(&digest::SHA256, b"salt");
|
||||
let mut out = vec![0u8; 1];
|
||||
assert!(extract_and_expand(&salt, b"secret", b"info", &mut out).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hkdf_accepts_zero_length_info() {
|
||||
let salt = hmac::SigningKey::new(&digest::SHA256, b"salt");
|
||||
let mut out = vec![0u8; 32];
|
||||
assert!(extract_and_expand(&salt, b"secret", b"", &mut out).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hkdf_expand_rejects_short_prk() {
|
||||
let prk = hmac::SigningKey::new(&digest::SHA256, b"too short"); // must be >= HashLen
|
||||
let mut out = vec![0u8; 8];
|
||||
assert!(expand(&prk, b"info", &mut out).is_ok());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,200 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// This file contains code that was copied from the ring crate which is under
|
||||
// the ISC license, reproduced below:
|
||||
|
||||
// Copyright 2015-2017 Brian Smith.
|
||||
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
|
||||
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
|
||||
// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
use crate::{constant_time, digest, error::*};
|
||||
|
||||
/// A calculated signature value.
|
||||
/// This is a type-safe wrappper that discourages attempts at comparing signatures
|
||||
/// for equality, which might naively be done using a non-constant-time comparison.
|
||||
#[derive(Clone)]
|
||||
pub struct Signature(pub(crate) digest::Digest);
|
||||
|
||||
impl AsRef<[u8]> for Signature {
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
self.0.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
/// A key to use for HMAC signing.
|
||||
pub struct SigningKey {
|
||||
pub(crate) digest_alg: &'static digest::Algorithm,
|
||||
pub(crate) key_value: Vec<u8>,
|
||||
}
|
||||
|
||||
impl SigningKey {
|
||||
pub fn new(digest_alg: &'static digest::Algorithm, key_value: &[u8]) -> Self {
|
||||
SigningKey {
|
||||
digest_alg,
|
||||
key_value: key_value.to_vec(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn digest_algorithm(&self) -> &'static digest::Algorithm {
|
||||
self.digest_alg
|
||||
}
|
||||
}
|
||||
|
||||
/// A key to use for HMAC authentication.
|
||||
pub struct VerificationKey {
|
||||
wrapped: SigningKey,
|
||||
}
|
||||
|
||||
impl VerificationKey {
|
||||
pub fn new(digest_alg: &'static digest::Algorithm, key_value: &[u8]) -> Self {
|
||||
VerificationKey {
|
||||
wrapped: SigningKey::new(digest_alg, key_value),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn digest_algorithm(&self) -> &'static digest::Algorithm {
|
||||
self.wrapped.digest_algorithm()
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculate the HMAC of `data` using `key` and verify it corresponds to the provided signature.
|
||||
pub fn verify(key: &VerificationKey, data: &[u8], signature: &[u8]) -> Result<()> {
|
||||
verify_with_own_key(&key.wrapped, data, signature)
|
||||
}
|
||||
|
||||
/// Equivalent to `verify` but allows the consumer to pass a `SigningKey`.
|
||||
pub fn verify_with_own_key(key: &SigningKey, data: &[u8], signature: &[u8]) -> Result<()> {
|
||||
constant_time::verify_slices_are_equal(sign(key, data)?.as_ref(), signature)
|
||||
}
|
||||
|
||||
/// Calculate the HMAC of `data` using `key`.
|
||||
pub fn sign(key: &SigningKey, data: &[u8]) -> Result<Signature> {
|
||||
let value = nss::pk11::context::hmac_sign(key.digest_alg, &key.key_value, data)?;
|
||||
Ok(Signature(digest::Digest {
|
||||
value,
|
||||
algorithm: key.digest_alg.clone(),
|
||||
}))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use hex;
|
||||
|
||||
const KEY: &[u8] = b"key";
|
||||
const MESSAGE: &[u8] = b"The quick brown fox jumps over the lazy dog";
|
||||
const SIGNATURE_HEX: &str = "f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8";
|
||||
|
||||
#[test]
|
||||
fn hmac_sign() {
|
||||
let key = SigningKey::new(&digest::SHA256, KEY);
|
||||
let signature = sign(&key, MESSAGE).unwrap();
|
||||
let expected_signature = hex::decode(SIGNATURE_HEX).unwrap();
|
||||
assert_eq!(signature.as_ref(), expected_signature.as_slice());
|
||||
assert!(verify_with_own_key(&key, MESSAGE, &expected_signature).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hmac_sign_gives_different_signatures_for_different_keys() {
|
||||
let key = SigningKey::new(&digest::SHA256, b"another key");
|
||||
let signature = sign(&key, MESSAGE).unwrap();
|
||||
let expected_signature = hex::decode(SIGNATURE_HEX).unwrap();
|
||||
assert_ne!(signature.as_ref(), expected_signature.as_slice());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hmac_sign_gives_different_signatures_for_different_messages() {
|
||||
let key = SigningKey::new(&digest::SHA256, KEY);
|
||||
let signature = sign(&key, b"a different message").unwrap();
|
||||
let expected_signature = hex::decode(SIGNATURE_HEX).unwrap();
|
||||
assert_ne!(signature.as_ref(), expected_signature.as_slice());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hmac_verify() {
|
||||
let key = VerificationKey::new(&digest::SHA256, KEY);
|
||||
let expected_signature = hex::decode(SIGNATURE_HEX).unwrap();
|
||||
assert!(verify(&key, MESSAGE, &expected_signature).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hmac_verify_fails_with_incorrect_signature() {
|
||||
let key = VerificationKey::new(&digest::SHA256, KEY);
|
||||
let signature = hex::decode(SIGNATURE_HEX).unwrap();
|
||||
for i in 0..signature.len() {
|
||||
let mut wrong_signature = signature.clone();
|
||||
wrong_signature[i] = wrong_signature[i].wrapping_add(1);
|
||||
assert!(verify(&key, MESSAGE, &wrong_signature).is_err());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hmac_verify_fails_with_incorrect_key() {
|
||||
let key = VerificationKey::new(&digest::SHA256, b"wrong key");
|
||||
let signature = hex::decode(SIGNATURE_HEX).unwrap();
|
||||
assert!(verify(&key, MESSAGE, &signature).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hmac_sign_cleanly_rejects_gigantic_keys() {
|
||||
if (std::u32::MAX as usize) < std::usize::MAX {
|
||||
let key_bytes = vec![0; (std::u32::MAX as usize) + 1];
|
||||
// Direct construction of SigningKey to avoid instantiating the array.
|
||||
let key = SigningKey {
|
||||
digest_alg: &digest::SHA256,
|
||||
key_value: key_bytes,
|
||||
};
|
||||
assert!(sign(&key, MESSAGE).is_err());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hmac_verify_cleanly_rejects_gigantic_keys() {
|
||||
if (std::u32::MAX as usize) < std::usize::MAX {
|
||||
let key_bytes = vec![0; (std::u32::MAX as usize) + 1];
|
||||
// Direct construction of VerificationKey to avoid instantiating the array.
|
||||
let key = VerificationKey {
|
||||
wrapped: SigningKey {
|
||||
digest_alg: &digest::SHA256,
|
||||
key_value: key_bytes,
|
||||
},
|
||||
};
|
||||
let signature = hex::decode(SIGNATURE_HEX).unwrap();
|
||||
assert!(verify(&key, MESSAGE, &signature).is_err());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hmac_sign_cleanly_rejects_gigantic_messages() {
|
||||
if (std::u32::MAX as usize) < std::usize::MAX {
|
||||
let key = SigningKey::new(&digest::SHA256, KEY);
|
||||
let message = vec![0; (std::u32::MAX as usize) + 1];
|
||||
assert!(sign(&key, &message).is_err());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hmac_verify_cleanly_rejects_gigantic_messages() {
|
||||
if (std::u32::MAX as usize) < std::usize::MAX {
|
||||
let key = VerificationKey::new(&digest::SHA256, KEY);
|
||||
let signature = hex::decode(SIGNATURE_HEX).unwrap();
|
||||
let message = vec![0; (std::u32::MAX as usize) + 1];
|
||||
assert!(verify(&key, &message, &signature).is_err());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// This file contains code that was copied from the ring crate which is under
|
||||
// the ISC license, reproduced below:
|
||||
|
||||
// Copyright 2015-2017 Brian Smith.
|
||||
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
|
||||
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
|
||||
// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
#![allow(unknown_lints)]
|
||||
#![warn(rust_2018_idioms)]
|
||||
/// This crate provides all the cryptographic primitives required by
|
||||
/// this workspace, backed by the NSS library.
|
||||
/// The exposed API is pretty much the same as the `ring` crate.
|
||||
pub mod aead;
|
||||
pub mod agreement;
|
||||
pub mod constant_time;
|
||||
pub mod digest;
|
||||
#[cfg(feature = "ece")]
|
||||
pub mod ece;
|
||||
mod error;
|
||||
#[cfg(feature = "hawk")]
|
||||
mod hawk_crypto;
|
||||
pub mod hkdf;
|
||||
pub mod hmac;
|
||||
pub mod rand;
|
||||
|
||||
// Expose `hawk` if the hawk feature is on. This avoids consumers needing to
|
||||
// configure this separately, which is more or less trivial to do incorrectly.
|
||||
#[cfg(feature = "hawk")]
|
||||
pub use hawk;
|
||||
|
||||
pub use crate::error::{Error, ErrorKind, Result};
|
||||
|
||||
// So we link against the SQLite lib imported by parent crates
|
||||
// such as places and logins.
|
||||
#[allow(unused_extern_crates)]
|
||||
extern crate libsqlite3_sys;
|
||||
// For some reason when running `cargo test --all` we use
|
||||
// the sqlcipher version of `libsqlite3_sys`, which has a
|
||||
// dependency on OpenSSL.
|
||||
#[cfg(test)]
|
||||
#[allow(unused_extern_crates)]
|
||||
extern crate openssl_sys;
|
||||
|
||||
/// Only required to be called if you intend to use this library in conjunction
|
||||
/// with the `hawk` crate.
|
||||
pub fn ensure_initialized() {
|
||||
nss::ensure_initialized();
|
||||
#[cfg(feature = "hawk")]
|
||||
{
|
||||
static INIT_ONCE: std::sync::Once = std::sync::Once::new();
|
||||
INIT_ONCE.call_once(crate::hawk_crypto::init);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// This file contains code that was copied from the ring crate which is under
|
||||
// the ISC license, reproduced below:
|
||||
|
||||
// Copyright 2015-2017 Brian Smith.
|
||||
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
|
||||
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
|
||||
// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
use crate::error::*;
|
||||
|
||||
/// Fill a buffer with cryptographically secure pseudo-random data.
|
||||
pub fn fill(dest: &mut [u8]) -> Result<()> {
|
||||
Ok(nss::pk11::slot::generate_random(dest)?)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn random_fill() {
|
||||
let mut out = vec![0u8; 64];
|
||||
assert!(fill(&mut out).is_ok());
|
||||
// This check could *in theory* fail if we randomly generate all zeroes
|
||||
// but we're treating that probability as negligible in practice.
|
||||
assert_ne!(out, vec![0u8; 64]);
|
||||
|
||||
let mut out2 = vec![0u8; 64];
|
||||
assert!(fill(&mut out2).is_ok());
|
||||
assert_ne!(out, vec![0u8; 64]);
|
||||
assert_ne!(out2, out);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn random_fill_empty() {
|
||||
let mut out = vec![0u8; 0];
|
||||
assert!(fill(&mut out).is_ok());
|
||||
assert_eq!(out, vec![0u8; 0]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn random_fill_oddly_sized_arrays() {
|
||||
let sizes: [usize; 4] = [61, 63, 65, 67];
|
||||
for size in &sizes {
|
||||
let mut out = vec![0u8; *size];
|
||||
assert!(fill(&mut out).is_ok());
|
||||
assert_ne!(out, vec![0u8; *size]);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn random_fill_rejects_attempts_to_fill_gigantic_arrays() {
|
||||
let max_size: usize = std::i32::MAX as usize;
|
||||
let mut out = vec![0u8; max_size + 1];
|
||||
assert!(fill(&mut out).is_err());
|
||||
}
|
||||
}
|
|
@ -16,12 +16,11 @@ serde = "1.0.94"
|
|||
serde_derive = "1.0.94"
|
||||
serde_json = "1.0.40"
|
||||
url = "1.7.1"
|
||||
openssl = "0.10.23"
|
||||
hawk = { git = "https://github.com/eoger/rust-hawk", branch = "use-openssl" }
|
||||
log = "0.4"
|
||||
lazy_static = "1.0"
|
||||
base16 = "0.2.1"
|
||||
failure = "0.1.3"
|
||||
rc_crypto = { path = "../support/rc_crypto", features = ["hawk"] }
|
||||
viaduct = { path = "../viaduct" }
|
||||
interrupt = { path = "../support/interrupt" }
|
||||
error-support = { path = "../support/error" }
|
||||
|
|
|
@ -370,17 +370,7 @@ impl EncryptedPayload {
|
|||
where
|
||||
for<'a> T: Deserialize<'a>,
|
||||
{
|
||||
let matches = key
|
||||
.verify_hmac_string(&self.hmac, &self.ciphertext)
|
||||
.map_err(|_| error::ErrorKind::HmacMismatch)?;
|
||||
if !matches {
|
||||
return Err(error::ErrorKind::HmacMismatch.into());
|
||||
}
|
||||
|
||||
let iv = base64::decode(&self.iv)?;
|
||||
let ciphertext = base64::decode(&self.ciphertext)?;
|
||||
let cleartext = key.decrypt(&ciphertext, &iv)?;
|
||||
|
||||
let cleartext = key.decrypt(&self.ciphertext, &self.iv, &self.hmac)?;
|
||||
Ok(serde_json::from_str(&cleartext)?)
|
||||
}
|
||||
|
||||
|
@ -389,13 +379,11 @@ impl EncryptedPayload {
|
|||
cleartext_payload: &T,
|
||||
) -> error::Result<Self> {
|
||||
let cleartext = serde_json::to_string(cleartext_payload)?;
|
||||
let (enc_bytes, iv) = key.encrypt_bytes_rand_iv(&cleartext.as_bytes())?;
|
||||
let iv_base64 = base64::encode(&iv);
|
||||
let enc_base64 = base64::encode(&enc_bytes);
|
||||
let hmac = key.hmac_string(enc_base64.as_bytes())?;
|
||||
let (enc_base64, iv_base64, hmac_base16) =
|
||||
key.encrypt_bytes_rand_iv(&cleartext.as_bytes())?;
|
||||
Ok(EncryptedPayload {
|
||||
iv: iv_base64,
|
||||
hmac,
|
||||
hmac: hmac_base16,
|
||||
ciphertext: enc_base64,
|
||||
})
|
||||
}
|
||||
|
@ -508,10 +496,6 @@ mod tests {
|
|||
|
||||
let encrypted = orig_record.clone().encrypt(&keybundle).unwrap();
|
||||
|
||||
assert!(keybundle
|
||||
.verify_hmac_string(&encrypted.payload.hmac, &encrypted.payload.ciphertext)
|
||||
.unwrap());
|
||||
|
||||
// While we're here, check on EncryptedPayload::serialized_len
|
||||
let val_rec =
|
||||
serde_json::from_str::<JsonValue>(&serde_json::to_string(&encrypted).unwrap()).unwrap();
|
||||
|
@ -539,10 +523,6 @@ mod tests {
|
|||
|
||||
let encrypted = orig_record.clone().encrypt(&keybundle).unwrap();
|
||||
|
||||
assert!(keybundle
|
||||
.verify_hmac_string(&encrypted.payload.hmac, &encrypted.payload.ciphertext)
|
||||
.unwrap());
|
||||
|
||||
// While we're here, check on EncryptedPayload::serialized_len
|
||||
let val_rec =
|
||||
serde_json::from_str::<JsonValue>(&serde_json::to_string(&encrypted).unwrap()).unwrap();
|
||||
|
@ -575,10 +555,6 @@ mod tests {
|
|||
let keybundle = KeyBundle::new_random().unwrap();
|
||||
let encrypted = bso.clone().encrypt(&keybundle).unwrap();
|
||||
|
||||
assert!(keybundle
|
||||
.verify_hmac_string(&encrypted.payload.hmac, &encrypted.payload.ciphertext)
|
||||
.unwrap());
|
||||
|
||||
let decrypted = encrypted.decrypt(&keybundle).unwrap();
|
||||
// We add auto fields during decryption.
|
||||
assert_eq!(decrypted.payload.data["sortindex"], 100);
|
||||
|
@ -604,11 +580,11 @@ mod tests {
|
|||
|
||||
// Note: ErrorKind isn't PartialEq, so.
|
||||
match e.kind() {
|
||||
error::ErrorKind::HmacMismatch => {
|
||||
error::ErrorKind::CryptoError(_) => {
|
||||
// yay.
|
||||
}
|
||||
other => {
|
||||
panic!("Expected HMAC mismatch, got {:?}", other);
|
||||
panic!("Expected Crypto Error, got {:?}", other);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -173,6 +173,7 @@ impl SetupStorageClient for Sync15StorageClient {
|
|||
|
||||
impl Sync15StorageClient {
|
||||
pub fn new(init_params: Sync15StorageClientInit) -> error::Result<Sync15StorageClient> {
|
||||
rc_crypto::ensure_initialized();
|
||||
let tsc = token::TokenProvider::new(
|
||||
init_params.tokenserver_url,
|
||||
init_params.access_token,
|
||||
|
|
|
@ -2,8 +2,9 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use failure::{Fail, SyncFailure};
|
||||
use failure::Fail;
|
||||
use interrupt::Interrupted;
|
||||
use rc_crypto::hawk;
|
||||
use std::string;
|
||||
use std::time::SystemTime;
|
||||
|
||||
|
@ -79,9 +80,8 @@ pub enum ErrorKind {
|
|||
#[fail(display = "Store error: {}", _0)]
|
||||
StoreError(#[fail(cause)] failure::Error),
|
||||
|
||||
// Basically reimplement error_chain's foreign_links. (Ugh, this sucks)
|
||||
#[fail(display = "OpenSSL error: {}", _0)]
|
||||
OpensslError(#[fail(cause)] openssl::error::ErrorStack),
|
||||
#[fail(display = "Crypto/NSS error: {}", _0)]
|
||||
CryptoError(#[fail(cause)] rc_crypto::Error),
|
||||
|
||||
#[fail(display = "Base64 decode error: {}", _0)]
|
||||
Base64Decode(#[fail(cause)] base64::DecodeError),
|
||||
|
@ -99,7 +99,7 @@ pub enum ErrorKind {
|
|||
UnexpectedStatus(#[fail(cause)] viaduct::UnexpectedStatus),
|
||||
|
||||
#[fail(display = "HAWK error: {}", _0)]
|
||||
HawkError(#[fail(cause)] SyncFailure<hawk::Error>),
|
||||
HawkError(#[fail(cause)] hawk::Error),
|
||||
|
||||
#[fail(display = "URL parse error: {}", _0)]
|
||||
MalformedUrl(#[fail(cause)] url::ParseError),
|
||||
|
@ -110,7 +110,7 @@ pub enum ErrorKind {
|
|||
|
||||
error_support::define_error! {
|
||||
ErrorKind {
|
||||
(OpensslError, openssl::error::ErrorStack),
|
||||
(CryptoError, rc_crypto::Error),
|
||||
(Base64Decode, base64::DecodeError),
|
||||
(JsonError, serde_json::Error),
|
||||
(BadCleartextUtf8, std::string::FromUtf8Error),
|
||||
|
@ -120,21 +120,6 @@ error_support::define_error! {
|
|||
// A bit dubious, since we only want this to happen inside `synchronize`
|
||||
(StoreError, failure::Error),
|
||||
(Interrupted, Interrupted),
|
||||
}
|
||||
}
|
||||
|
||||
// These can got away when we update to the next version of Hawk,
|
||||
// in https://github.com/mozilla/application-services/pull/1050
|
||||
impl From<hawk::Error> for ErrorKind {
|
||||
#[cold]
|
||||
fn from(e: hawk::Error) -> ErrorKind {
|
||||
ErrorKind::HawkError(SyncFailure::new(e))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<hawk::Error> for Error {
|
||||
#[cold]
|
||||
fn from(e: hawk::Error) -> Self {
|
||||
ErrorKind::from(e).into()
|
||||
(HawkError, hawk::Error),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use crate::error::{ErrorKind, Result};
|
||||
use openssl::hash::MessageDigest;
|
||||
use openssl::pkey::PKey;
|
||||
use openssl::sign::Signer;
|
||||
use openssl::{self, symm};
|
||||
use rc_crypto::{
|
||||
aead::{self, OpeningKey, SealingKey},
|
||||
rand,
|
||||
};
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||
pub struct KeyBundle {
|
||||
|
@ -40,7 +40,7 @@ impl KeyBundle {
|
|||
|
||||
pub fn new_random() -> Result<KeyBundle> {
|
||||
let mut buffer = [0u8; 64];
|
||||
openssl::rand::rand_bytes(&mut buffer)?;
|
||||
rand::fill(&mut buffer)?;
|
||||
KeyBundle::from_ksync_bytes(&buffer)
|
||||
}
|
||||
|
||||
|
@ -81,93 +81,68 @@ impl KeyBundle {
|
|||
[base64::encode(&self.enc_key), base64::encode(&self.mac_key)]
|
||||
}
|
||||
|
||||
/// Returns the 32 byte digest by value since it's small enough to be passed
|
||||
/// around cheaply, and easily convertable into a slice or vec if you want.
|
||||
fn hmac(&self, ciphertext: &[u8]) -> Result<[u8; 32]> {
|
||||
let mut out = [0u8; 32];
|
||||
let key = PKey::hmac(self.hmac_key())?;
|
||||
let mut signer = Signer::new(MessageDigest::sha256(), &key)?;
|
||||
signer.update(ciphertext)?;
|
||||
let size = signer.sign(&mut out)?;
|
||||
// This isn't an Err since it really should not be possible.
|
||||
assert!(
|
||||
size == 32,
|
||||
"Somehow the 256 bits from sha256 do not add up into 32 bytes..."
|
||||
);
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
/// Important! Don't compare against this directly! use `verify_hmac` or `verify_hmac_string`!
|
||||
pub fn hmac_string(&self, ciphertext: &[u8]) -> Result<String> {
|
||||
Ok(base16::encode_lower(&self.hmac(ciphertext)?))
|
||||
}
|
||||
|
||||
pub fn verify_hmac(&self, expected_hmac: &[u8], ciphertext_base64: &str) -> Result<bool> {
|
||||
let computed_hmac = self.hmac(ciphertext_base64.as_bytes())?;
|
||||
// I suspect this is unnecessary for our case, but the rust-openssl docs
|
||||
// want us to use this over == to avoid sidechannels, and who am I to argue?
|
||||
Ok(openssl::memcmp::eq(&expected_hmac, &computed_hmac))
|
||||
}
|
||||
|
||||
pub fn verify_hmac_string(&self, expected_hmac: &str, ciphertext_base64: &str) -> Result<bool> {
|
||||
let computed_hmac = self.hmac(ciphertext_base64.as_bytes())?;
|
||||
// Note: openssl::memcmp::eq panics if the sizes aren't the same. Desktop returns that it
|
||||
// was a verification failure, so we will too.
|
||||
if expected_hmac.len() != 64 {
|
||||
log::warn!("Garbage HMAC verification string: Wrong length");
|
||||
return Ok(false);
|
||||
}
|
||||
/// Decrypt the provided ciphertext with the given iv, and decodes the
|
||||
/// result as a utf8 string.
|
||||
pub fn decrypt(&self, enc_base64: &str, iv_base64: &str, hmac_base16: &str) -> Result<String> {
|
||||
// Decode the expected_hmac into bytes to avoid issues if a client happens to encode
|
||||
// this as uppercase. This shouldn't happen in practice, but doing it this way is more
|
||||
// robust and avoids an allocation.
|
||||
let mut decoded_hmac = [0u8; 32];
|
||||
|
||||
if base16::decode_slice(expected_hmac, &mut decoded_hmac).is_err() {
|
||||
let mut decoded_hmac = vec![0u8; 32];
|
||||
if base16::decode_slice(hmac_base16, &mut decoded_hmac).is_err() {
|
||||
log::warn!("Garbage HMAC verification string: contained non base16 characters");
|
||||
return Ok(false);
|
||||
return Err(ErrorKind::HmacMismatch.into());
|
||||
}
|
||||
|
||||
Ok(openssl::memcmp::eq(&decoded_hmac, &computed_hmac))
|
||||
}
|
||||
|
||||
/// Decrypt the provided ciphertext with the given iv, and decodes the
|
||||
/// result as a utf8 string. Important: Caller must check verify_hmac first!
|
||||
pub fn decrypt(&self, ciphertext: &[u8], iv: &[u8]) -> Result<String> {
|
||||
let cleartext_bytes = symm::decrypt(
|
||||
symm::Cipher::aes_256_cbc(),
|
||||
self.encryption_key(),
|
||||
Some(iv),
|
||||
ciphertext,
|
||||
let iv = base64::decode(iv_base64)?;
|
||||
let ciphertext_bytes = base64::decode(enc_base64)?;
|
||||
let key_bytes = [self.encryption_key(), self.hmac_key()].concat();
|
||||
let key = OpeningKey::new(&aead::LEGACY_SYNC_AES_256_CBC_HMAC_SHA256, &key_bytes)?;
|
||||
let nonce = aead::Nonce::try_assume_unique_for_key(
|
||||
&aead::LEGACY_SYNC_AES_256_CBC_HMAC_SHA256,
|
||||
&iv,
|
||||
)?;
|
||||
let ciphertext_and_hmac = [ciphertext_bytes, decoded_hmac].concat();
|
||||
let cleartext_bytes = aead::open(&key, nonce, aead::Aad::empty(), &ciphertext_and_hmac)?;
|
||||
let cleartext = String::from_utf8(cleartext_bytes)?;
|
||||
Ok(cleartext)
|
||||
}
|
||||
|
||||
/// Encrypt using the provided IV.
|
||||
pub fn encrypt_bytes_with_iv(&self, cleartext_bytes: &[u8], iv: &[u8]) -> Result<Vec<u8>> {
|
||||
let ciphertext = symm::encrypt(
|
||||
symm::Cipher::aes_256_cbc(),
|
||||
self.encryption_key(),
|
||||
Some(iv),
|
||||
cleartext_bytes,
|
||||
)?;
|
||||
Ok(ciphertext)
|
||||
pub fn encrypt_bytes_with_iv(
|
||||
&self,
|
||||
cleartext_bytes: &[u8],
|
||||
iv: &[u8],
|
||||
) -> Result<(String, String)> {
|
||||
let key_bytes = [self.encryption_key(), self.hmac_key()].concat();
|
||||
let key = SealingKey::new(&aead::LEGACY_SYNC_AES_256_CBC_HMAC_SHA256, &key_bytes)?;
|
||||
let nonce =
|
||||
aead::Nonce::try_assume_unique_for_key(&aead::LEGACY_SYNC_AES_256_CBC_HMAC_SHA256, iv)?;
|
||||
let ciphertext_and_hmac = aead::seal(&key, nonce, aead::Aad::empty(), cleartext_bytes)?;
|
||||
let ciphertext_len = ciphertext_and_hmac.len() - key.algorithm().tag_len();
|
||||
// Do the string conversions here so we don't have to split and copy to 2 vectors.
|
||||
let (ciphertext, hmac_signature) = ciphertext_and_hmac.split_at(ciphertext_len);
|
||||
let enc_base64 = base64::encode(&ciphertext);
|
||||
let hmac_base16 = base16::encode_lower(&hmac_signature);
|
||||
Ok((enc_base64, hmac_base16))
|
||||
}
|
||||
|
||||
/// Generate a random iv and encrypt with it. Return both the encrypted bytes
|
||||
/// and the generated iv.
|
||||
pub fn encrypt_bytes_rand_iv(&self, cleartext_bytes: &[u8]) -> Result<(Vec<u8>, [u8; 16])> {
|
||||
pub fn encrypt_bytes_rand_iv(
|
||||
&self,
|
||||
cleartext_bytes: &[u8],
|
||||
) -> Result<(String, String, String)> {
|
||||
let mut iv = [0u8; 16];
|
||||
openssl::rand::rand_bytes(&mut iv)?;
|
||||
let ciphertext = self.encrypt_bytes_with_iv(cleartext_bytes, &iv)?;
|
||||
Ok((ciphertext, iv))
|
||||
rand::fill(&mut iv)?;
|
||||
let (enc_base64, hmac_base16) = self.encrypt_bytes_with_iv(cleartext_bytes, &iv)?;
|
||||
let iv_base64 = base64::encode(&iv);
|
||||
Ok((enc_base64, iv_base64, hmac_base16))
|
||||
}
|
||||
|
||||
pub fn encrypt_with_iv(&self, cleartext: &str, iv: &[u8]) -> Result<Vec<u8>> {
|
||||
pub fn encrypt_with_iv(&self, cleartext: &str, iv: &[u8]) -> Result<(String, String)> {
|
||||
self.encrypt_bytes_with_iv(cleartext.as_bytes(), iv)
|
||||
}
|
||||
|
||||
pub fn encrypt_rand_iv(&self, cleartext: &str) -> Result<(Vec<u8>, [u8; 16])> {
|
||||
pub fn encrypt_rand_iv(&self, cleartext: &str) -> Result<(String, String, String)> {
|
||||
self.encrypt_bytes_rand_iv(cleartext.as_bytes())
|
||||
}
|
||||
}
|
||||
|
@ -200,21 +175,11 @@ mod test {
|
|||
"LyIsInZpc2l0cyI6W3siZGF0ZSI6MTMxOTE0OTAxMjM3MjQyNSwidHlwZSI6MX1dfQ==",
|
||||
];
|
||||
|
||||
#[test]
|
||||
fn test_hmac() {
|
||||
let key_bundle = KeyBundle::from_base64(ENC_KEY_B64, HMAC_KEY_B64).unwrap();
|
||||
let ciphertext_base64 = CIPHERTEXT_B64_PIECES.join("");
|
||||
assert!(key_bundle
|
||||
.verify_hmac_string(HMAC_B16, &ciphertext_base64)
|
||||
.unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decrypt() {
|
||||
let key_bundle = KeyBundle::from_base64(ENC_KEY_B64, HMAC_KEY_B64).unwrap();
|
||||
let ciphertext = base64::decode(&CIPHERTEXT_B64_PIECES.join("")).unwrap();
|
||||
let iv = base64::decode(IV_B64).unwrap();
|
||||
let s = key_bundle.decrypt(&ciphertext, &iv).unwrap();
|
||||
let ciphertext = CIPHERTEXT_B64_PIECES.join("");
|
||||
let s = key_bundle.decrypt(&ciphertext, IV_B64, HMAC_B16).unwrap();
|
||||
|
||||
let cleartext =
|
||||
String::from_utf8(base64::decode(&CLEARTEXT_B64_PIECES.join("")).unwrap()).unwrap();
|
||||
|
@ -227,18 +192,21 @@ mod test {
|
|||
let iv = base64::decode(IV_B64).unwrap();
|
||||
|
||||
let cleartext_bytes = base64::decode(&CLEARTEXT_B64_PIECES.join("")).unwrap();
|
||||
let encrypted_bytes = key_bundle
|
||||
let (enc_base64, _hmac_base16) = key_bundle
|
||||
.encrypt_bytes_with_iv(&cleartext_bytes, &iv)
|
||||
.unwrap();
|
||||
|
||||
let expect_ciphertext = base64::decode(&CIPHERTEXT_B64_PIECES.join("")).unwrap();
|
||||
let expect_ciphertext = CIPHERTEXT_B64_PIECES.join("");
|
||||
|
||||
assert_eq!(&encrypted_bytes, &expect_ciphertext);
|
||||
assert_eq!(&enc_base64, &expect_ciphertext);
|
||||
|
||||
let (enc_bytes2, iv2) = key_bundle.encrypt_bytes_rand_iv(&cleartext_bytes).unwrap();
|
||||
assert_ne!(&enc_bytes2, &expect_ciphertext);
|
||||
let (enc_base64_2, iv_base64_2, hmac_base16_2) =
|
||||
key_bundle.encrypt_bytes_rand_iv(&cleartext_bytes).unwrap();
|
||||
assert_ne!(&enc_base64_2, &expect_ciphertext);
|
||||
|
||||
let s = key_bundle.decrypt(&enc_bytes2, &iv2).unwrap();
|
||||
let s = key_bundle
|
||||
.decrypt(&enc_base64_2, &iv_base64_2, &hmac_base16_2)
|
||||
.unwrap();
|
||||
assert_eq!(&cleartext_bytes, &s.as_bytes());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,8 +49,7 @@ impl ServiceStatus {
|
|||
},
|
||||
|
||||
// Network errors.
|
||||
ErrorKind::OpensslError(_)
|
||||
| ErrorKind::RequestError(_)
|
||||
ErrorKind::RequestError(_)
|
||||
| ErrorKind::UnexpectedStatus(_)
|
||||
| ErrorKind::HawkError(_) => ServiceStatus::NetworkError,
|
||||
|
||||
|
|
|
@ -272,7 +272,7 @@ impl<'a> From<&'a Error> for SyncFailure {
|
|||
ErrorResponse::ServerError { status, .. } => SyncFailure::Http { code: *status },
|
||||
ErrorResponse::RequestFailed { status, .. } => SyncFailure::Http { code: *status },
|
||||
},
|
||||
ErrorKind::OpensslError(ref e) => SyncFailure::Unexpected {
|
||||
ErrorKind::CryptoError(ref e) => SyncFailure::Unexpected {
|
||||
error: e.to_string(),
|
||||
},
|
||||
ErrorKind::RequestError(ref e) => SyncFailure::Unexpected {
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
use crate::error::{self, ErrorKind, Result};
|
||||
use crate::util::ServerTimestamp;
|
||||
use rc_crypto::hawk;
|
||||
use serde_derive::*;
|
||||
use std::borrow::{Borrow, Cow};
|
||||
use std::cell::RefCell;
|
||||
|
@ -241,6 +242,9 @@ struct TokenProviderImpl<TF: TokenFetcher> {
|
|||
|
||||
impl<TF: TokenFetcher> TokenProviderImpl<TF> {
|
||||
fn new(fetcher: TF) -> Self {
|
||||
// We check this at the real entrypoint of the application, but tests
|
||||
// can/do bypass that, so check this here too.
|
||||
rc_crypto::ensure_initialized();
|
||||
TokenProviderImpl {
|
||||
fetcher,
|
||||
current_state: RefCell::new(TokenState::NoToken),
|
||||
|
@ -256,7 +260,7 @@ impl<TF: TokenFetcher> TokenProviderImpl<TF> {
|
|||
|
||||
let credentials = hawk::Credentials {
|
||||
id: token.id.clone(),
|
||||
key: hawk::Key::new(token.key.as_bytes(), hawk::Digest::sha256())?,
|
||||
key: hawk::Key::new(token.key.as_bytes(), hawk::SHA256)?,
|
||||
};
|
||||
|
||||
Ok(TokenContext::new(
|
||||
|
|
|
@ -9,9 +9,9 @@ use std::str::FromStr;
|
|||
use std::time::Duration;
|
||||
use std::{fmt, num};
|
||||
|
||||
pub fn random_guid() -> Result<String, openssl::error::ErrorStack> {
|
||||
let mut bytes = vec![0u8; 9];
|
||||
openssl::rand::rand_bytes(&mut bytes)?;
|
||||
pub fn random_guid() -> Result<String, rc_crypto::Error> {
|
||||
let mut bytes = [0u8; 9];
|
||||
rc_crypto::rand::fill(&mut bytes)?;
|
||||
Ok(base64::encode_config(&bytes, base64::URL_SAFE_NO_PAD))
|
||||
}
|
||||
|
||||
|
|
|
@ -21,20 +21,6 @@ To work on the code in this repo you will need to be familiar with
|
|||
the [Rust](https://www.rust-lang.org/) programming language.
|
||||
You can get a working rust compiler and toolchain via [rustup](https://rustup.rs/).
|
||||
|
||||
The Network Security Suite (NSS) libraries must be available on your system for
|
||||
tests to run correctly.
|
||||
You can install them by doing the following:
|
||||
|
||||
* On MacOS (using the [Homebrew package manager](https://brew.sh/)):
|
||||
```
|
||||
brew install nss
|
||||
brew link --force nss
|
||||
```
|
||||
* On Debian/Ubuntu:
|
||||
```
|
||||
apt-get install libnss3-dev
|
||||
```
|
||||
|
||||
Some components also require `openssl` and `sqlcipher` in order to build correctly.
|
||||
You may be able to install these via your OS package manager, but for consistency
|
||||
we recommend using the versions included in this repo by doing the following from
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
# Downloaded sources tarballs.
|
||||
# Downloaded sources archives.
|
||||
*.tar.gz
|
||||
*.7z
|
||||
|
||||
# Intermediate folders leftover on build failures.
|
||||
openssl-*
|
||||
sqlcipher-*
|
||||
nss-*
|
||||
openssl*
|
||||
sqlcipher*
|
||||
nss*
|
||||
dist
|
||||
|
||||
# Final artifacts
|
||||
/android
|
||||
|
|
|
@ -22,36 +22,6 @@ else
|
|||
./build-all.sh desktop
|
||||
popd
|
||||
fi;
|
||||
|
||||
# NSS system libs check.
|
||||
pushd "$(mktemp -d)"
|
||||
echo '
|
||||
use std::{ffi::*, os::raw::*, process::exit};
|
||||
extern "C" {
|
||||
fn dlopen(filename: *const c_char, flags: c_int) -> *mut c_void;
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
const LIB_NAME: &str = "libnss3.dylib";
|
||||
#[cfg(target_os = "linux")]
|
||||
const LIB_NAME: &str = "libnss3.so";
|
||||
const RTLD_LAZY: c_int = 0x01;
|
||||
fn main() {
|
||||
let result = unsafe { dlopen(CString::new(LIB_NAME).unwrap().as_ptr(), RTLD_LAZY) };
|
||||
if result.is_null() { println!("Could not dlopen nss"); exit(1); }
|
||||
}
|
||||
' > nsslibcheck.rs
|
||||
rustc nsslibcheck.rs && ./nsslibcheck
|
||||
LIB_CHECK_RES="${?}"
|
||||
popd
|
||||
if [[ "${LIB_CHECK_RES}" != 0 ]]; then
|
||||
echo "It looks like the NSS libraries are not installed system-wide on your computer. Tests might fail to run."
|
||||
echo "* On MacOS:"
|
||||
echo "brew install nss"
|
||||
echo "brew link --force nss"
|
||||
echo "* On Debian/Ubuntu:"
|
||||
echo "apt-get install libnss3-dev"
|
||||
fi
|
||||
if [ "$(uname -s)" == "Darwin" ] && [ ! -f "/usr/include/pthread.h" ]; then
|
||||
# rustc does not include the macOS SDK headers in its include list yet
|
||||
# (see https://developer.apple.com/documentation/xcode_release_notes/xcode_10_release_notes)
|
||||
|
|
|
@ -41,7 +41,7 @@ echo "# Building openssl"
|
|||
for i in "${!TARGET_ARCHS[@]}"; do
|
||||
ARCH=${TARGET_ARCHS[${i}]}
|
||||
DIST=${TARGET_ARCHS_DISTS[${i}]}
|
||||
DIST_DIR=$(abspath "android/""${DIST}""/openssl")
|
||||
DIST_DIR=$(abspath "android/${DIST}/openssl")
|
||||
if [ -d "${DIST_DIR}" ]; then
|
||||
echo "${DIST_DIR} already exists. Skipping building openssl."
|
||||
else
|
||||
|
@ -53,11 +53,11 @@ echo "# Building sqlcipher"
|
|||
for i in "${!TARGET_ARCHS[@]}"; do
|
||||
ARCH=${TARGET_ARCHS[${i}]}
|
||||
DIST=${TARGET_ARCHS_DISTS[${i}]}
|
||||
OPENSSL_DIR=$(abspath "android/""${DIST}""/openssl")
|
||||
DIST_DIR=$(abspath "android/""${DIST}""/sqlcipher")
|
||||
NSS_DIR=$(abspath "android/${DIST}/nss")
|
||||
DIST_DIR=$(abspath "android/${DIST}/sqlcipher")
|
||||
if [ -d "${DIST_DIR}" ]; then
|
||||
echo "${DIST_DIR} already exists. Skipping building sqlcipher."
|
||||
else
|
||||
./build-sqlcipher-android.sh "${SQLCIPHER_SRC_PATH}" "${DIST_DIR}" "${ANDROID_NDK_TOOLCHAIN_DIR}/${ARCH}-${ANDROID_NDK_API_VERSION}" "${TARGET_ARCHS_TOOLCHAINS[${i}]}" "${ANDROID_NDK_API_VERSION}" "${OPENSSL_DIR}" || exit 1
|
||||
./build-sqlcipher-android.sh "${SQLCIPHER_SRC_PATH}" "${DIST_DIR}" "${ANDROID_NDK_TOOLCHAIN_DIR}/${ARCH}-${ANDROID_NDK_API_VERSION}" "${TARGET_ARCHS_TOOLCHAINS[${i}]}" "${ANDROID_NDK_API_VERSION}" "${NSS_DIR}" || exit 1
|
||||
fi
|
||||
done
|
||||
|
|
|
@ -18,14 +18,15 @@ SQLCIPHER_SRC_PATH=${2}
|
|||
NSS_SRC_PATH=${3}
|
||||
|
||||
function universal_lib() {
|
||||
DIR_NAME=${1}
|
||||
LIB_NAME=${2}
|
||||
DIR_NAME="${1}"
|
||||
LIB_NAME="${2}"
|
||||
shift; shift
|
||||
UNIVERSAL_DIR="ios/universal/${DIR_NAME}"
|
||||
LIB_PATH="${UNIVERSAL_DIR}/lib/${LIB_NAME}"
|
||||
if [ ! -e "${LIB_PATH}" ]; then
|
||||
mkdir -p "${UNIVERSAL_DIR}/lib"
|
||||
CMD="lipo"
|
||||
for ARCH in "${TARGET_ARCHS[@]}"; do
|
||||
for ARCH in "${@}"; do
|
||||
CMD="${CMD} -arch ${ARCH} ios/${ARCH}/${DIR_NAME}/lib/${LIB_NAME}"
|
||||
done
|
||||
CMD="${CMD} -output ${LIB_PATH} -create"
|
||||
|
@ -33,31 +34,6 @@ function universal_lib() {
|
|||
fi
|
||||
}
|
||||
|
||||
echo "# Building openssl"
|
||||
for i in "${!TARGET_ARCHS[@]}"; do
|
||||
ARCH=${TARGET_ARCHS[${i}]}
|
||||
DIST_DIR=$(abspath "ios/""${ARCH}""/openssl")
|
||||
if [ -d "${DIST_DIR}" ]; then
|
||||
echo "${DIST_DIR} already exists. Skipping building openssl."
|
||||
else
|
||||
./build-openssl-ios.sh "${OPENSSL_SRC_PATH}" "${DIST_DIR}" "${ARCH}" "${IOS_MIN_SDK_VERSION}" || exit 1
|
||||
fi
|
||||
done
|
||||
universal_lib "openssl" "libssl.a"
|
||||
universal_lib "openssl" "libcrypto.a"
|
||||
|
||||
echo "# Building sqlcipher"
|
||||
for i in "${!TARGET_ARCHS[@]}"; do
|
||||
ARCH=${TARGET_ARCHS[${i}]}
|
||||
DIST_DIR=$(abspath "ios/""${ARCH}""/sqlcipher")
|
||||
if [ -d "${DIST_DIR}" ]; then
|
||||
echo "${DIST_DIR} already exists. Skipping building sqlcipher."
|
||||
else
|
||||
./build-sqlcipher-ios.sh "${SQLCIPHER_SRC_PATH}" "${DIST_DIR}" "${ARCH}" "${IOS_MIN_SDK_VERSION}" || exit 1
|
||||
fi
|
||||
done
|
||||
universal_lib "sqlcipher" "libsqlcipher.a"
|
||||
|
||||
echo "# Building NSS"
|
||||
for i in "${!TARGET_ARCHS[@]}"; do
|
||||
ARCH=${TARGET_ARCHS[${i}]}
|
||||
|
@ -68,16 +44,51 @@ for i in "${!TARGET_ARCHS[@]}"; do
|
|||
./build-nss-ios.sh "${NSS_SRC_PATH}" "${DIST_DIR}" "${ARCH}" "${IOS_MIN_SDK_VERSION}" || exit 1
|
||||
fi
|
||||
done
|
||||
universal_lib "nss" "libplc4.dylib"
|
||||
universal_lib "nss" "libplds4.dylib"
|
||||
universal_lib "nss" "libnspr4.dylib"
|
||||
universal_lib "nss" "libfreebl3.dylib"
|
||||
universal_lib "nss" "libnss3.dylib"
|
||||
universal_lib "nss" "libnssckbi.dylib"
|
||||
universal_lib "nss" "libnssutil3.dylib"
|
||||
universal_lib "nss" "libsmime3.dylib"
|
||||
universal_lib "nss" "libsoftokn3.dylib"
|
||||
universal_lib "nss" "libssl3.dylib"
|
||||
universal_lib "nss" "libcertdb.a" "${TARGET_ARCHS[@]}"
|
||||
universal_lib "nss" "libfreebl_static.a" "${TARGET_ARCHS[@]}"
|
||||
universal_lib "nss" "libnssb.a" "${TARGET_ARCHS[@]}"
|
||||
universal_lib "nss" "libnssutil.a" "${TARGET_ARCHS[@]}"
|
||||
universal_lib "nss" "libpkcs7.a" "${TARGET_ARCHS[@]}"
|
||||
universal_lib "nss" "libsmime.a" "${TARGET_ARCHS[@]}"
|
||||
universal_lib "nss" "libcerthi.a" "${TARGET_ARCHS[@]}"
|
||||
universal_lib "nss" "libnspr4.a" "${TARGET_ARCHS[@]}"
|
||||
universal_lib "nss" "libnssdev.a" "${TARGET_ARCHS[@]}"
|
||||
universal_lib "nss" "libpk11wrap_static.a" "${TARGET_ARCHS[@]}"
|
||||
universal_lib "nss" "libplc4.a" "${TARGET_ARCHS[@]}"
|
||||
universal_lib "nss" "libsoftokn_static.a" "${TARGET_ARCHS[@]}"
|
||||
universal_lib "nss" "libcryptohi.a" "${TARGET_ARCHS[@]}"
|
||||
universal_lib "nss" "libnss_static.a" "${TARGET_ARCHS[@]}"
|
||||
universal_lib "nss" "libnsspki.a" "${TARGET_ARCHS[@]}"
|
||||
universal_lib "nss" "libpkcs12.a" "${TARGET_ARCHS[@]}"
|
||||
universal_lib "nss" "libplds4.a" "${TARGET_ARCHS[@]}"
|
||||
universal_lib "nss" "libssl.a" "${TARGET_ARCHS[@]}"
|
||||
universal_lib "nss" "libhw-acc-crypto.a" "${TARGET_ARCHS[@]}"
|
||||
universal_lib "nss" "libgcm-aes-x86_c_lib.a" "x86_64"
|
||||
|
||||
echo "# Building openssl"
|
||||
for i in "${!TARGET_ARCHS[@]}"; do
|
||||
ARCH=${TARGET_ARCHS[${i}]}
|
||||
DIST_DIR=$(abspath "ios/${ARCH}/openssl")
|
||||
if [ -d "${DIST_DIR}" ]; then
|
||||
echo "${DIST_DIR} already exists. Skipping building openssl."
|
||||
else
|
||||
./build-openssl-ios.sh "${OPENSSL_SRC_PATH}" "${DIST_DIR}" "${ARCH}" "${IOS_MIN_SDK_VERSION}" || exit 1
|
||||
fi
|
||||
done
|
||||
universal_lib "openssl" "libssl.a" "${TARGET_ARCHS[@]}"
|
||||
universal_lib "openssl" "libcrypto.a" "${TARGET_ARCHS[@]}"
|
||||
|
||||
echo "# Building sqlcipher"
|
||||
for i in "${!TARGET_ARCHS[@]}"; do
|
||||
ARCH=${TARGET_ARCHS[${i}]}
|
||||
DIST_DIR=$(abspath "ios/${ARCH}/sqlcipher")
|
||||
if [ -d "${DIST_DIR}" ]; then
|
||||
echo "${DIST_DIR} already exists. Skipping building sqlcipher."
|
||||
else
|
||||
./build-sqlcipher-ios.sh "${SQLCIPHER_SRC_PATH}" "${DIST_DIR}" "${ARCH}" "${IOS_MIN_SDK_VERSION}" || exit 1
|
||||
fi
|
||||
done
|
||||
universal_lib "sqlcipher" "libsqlcipher.a" "${TARGET_ARCHS[@]}"
|
||||
|
||||
HEADER_DIST_DIR="ios/universal/openssl/include/openssl"
|
||||
if [ ! -e "${HEADER_DIST_DIR}" ]; then
|
||||
|
|
|
@ -5,13 +5,13 @@ set -euvx
|
|||
OPENSSL_VERSION="1.1.1a"
|
||||
OPENSSL_SHA256="fc20130f8b7cbd2fb918b2f14e2f429e109c31ddd0fb38fc5d71d9ffed3f9f41"
|
||||
|
||||
SQLCIPHER_VERSION="4.1.0"
|
||||
SQLCIPHER_SHA256="65144ca3ba4c0f9cd4bae8c20bb42f2b84424bf29d1ebcf04c44a728903b1faa"
|
||||
# SQLCIPHER_VERSION="4.1.0"
|
||||
# SQLCIPHER_SHA256="65144ca3ba4c0f9cd4bae8c20bb42f2b84424bf29d1ebcf04c44a728903b1faa"
|
||||
|
||||
NSS="nss-3.43"
|
||||
NSS_ARCHIVE="nss-3.43-with-nspr-4.21.tar.gz"
|
||||
NSS_URL="http://ftp.mozilla.org/pub/security/nss/releases/NSS_3_43_RTM/src/${NSS_ARCHIVE}"
|
||||
NSS_SHA256="fb2d54d507ceb185bac73f492cce7086a462d41977c2378aba9dd10e04448cf3"
|
||||
NSS="nss-3.44"
|
||||
# NSS_ARCHIVE="nss-3.44-with-nspr-4.21.tar.gz"
|
||||
# NSS_URL="http://ftp.mozilla.org/pub/security/nss/releases/NSS_3_44_RTM/src/${NSS_ARCHIVE}"
|
||||
# NSS_SHA256="298d86e18e96660d3c98476274b5857b48c135d809a10d6528d8661bdf834a49"
|
||||
|
||||
# End of configuration.
|
||||
|
||||
|
@ -45,29 +45,69 @@ echo "${OPENSSL_SHA256} ${OPENSSL}.tar.gz" | shasum -a 256 -c - || exit 2
|
|||
tar xfz "${OPENSSL}.tar.gz"
|
||||
OPENSSL_SRC_PATH=$(abspath ${OPENSSL})
|
||||
|
||||
SQLCIPHER="v${SQLCIPHER_VERSION}"
|
||||
rm -rf "${SQLCIPHER}"
|
||||
if [ ! -e "${SQLCIPHER}.tar.gz" ]; then
|
||||
echo "Downloading ${SQLCIPHER}.tar.gz"
|
||||
curl -L -O "https://github.com/sqlcipher/sqlcipher/archive/${SQLCIPHER}.tar.gz"
|
||||
else
|
||||
echo "Using ${SQLCIPHER}.tar.gz"
|
||||
fi
|
||||
echo "${SQLCIPHER_SHA256} ${SQLCIPHER}.tar.gz" | shasum -a 256 -c - || exit 2
|
||||
tar xfz "${SQLCIPHER}.tar.gz"
|
||||
SQLCIPHER_SRC_PATH=$(abspath "sqlcipher-${SQLCIPHER_VERSION}")
|
||||
# Delete the following...
|
||||
rm -rf sqlcipher
|
||||
git clone --single-branch --branch nss-crypto-impl --depth 1 "https://github.com/eoger/sqlcipher.git"
|
||||
SQLCIPHER_SRC_PATH=$(abspath "sqlcipher")
|
||||
# ... and uncomment the following once SQLCipher has an NSS crypto backend.
|
||||
# SQLCIPHER="v${SQLCIPHER_VERSION}"
|
||||
# rm -rf "${SQLCIPHER}"
|
||||
# if [ ! -e "${SQLCIPHER}.tar.gz" ]; then
|
||||
# echo "Downloading ${SQLCIPHER}.tar.gz"
|
||||
# curl -L -O "https://github.com/sqlcipher/sqlcipher/archive/${SQLCIPHER}.tar.gz"
|
||||
# else
|
||||
# echo "Using ${SQLCIPHER}.tar.gz"
|
||||
# fi
|
||||
# echo "${SQLCIPHER_SHA256} ${SQLCIPHER}.tar.gz" | shasum -a 256 -c - || exit 2
|
||||
# tar xfz "${SQLCIPHER}.tar.gz"
|
||||
# SQLCIPHER_SRC_PATH=$(abspath "sqlcipher-${SQLCIPHER_VERSION}")
|
||||
|
||||
rm -rf "${NSS}"
|
||||
if [ ! -e "${NSS_ARCHIVE}" ]; then
|
||||
echo "Downloading ${NSS_ARCHIVE}"
|
||||
curl -L -O "${NSS_URL}"
|
||||
else
|
||||
echo "Using ${NSS_ARCHIVE}"
|
||||
fi
|
||||
echo "${NSS_SHA256} ${NSS_ARCHIVE}" | shasum -a 256 -c - || exit 2
|
||||
tar xfz "${NSS_ARCHIVE}"
|
||||
# Delete the following...
|
||||
hg clone https://hg.mozilla.org/projects/nss/ -r 0c5d37301637ed024de8c2cbdbecf144aae12163 "${NSS}"/nss
|
||||
# Temporary fix for bug 1561953
|
||||
git clone --single-branch --branch without-versions https://github.com/eoger/nspr.git "${NSS}"/nspr
|
||||
# hg clone https://hg.mozilla.org/projects/nspr/ -r cc73b6c7dab2e8053533e1f2c0c23dc721e10b76 "${NSS}"/nspr
|
||||
# ... and uncomment the following once NSS 3.45 and NSPR 4.22 are out.
|
||||
# if [ ! -e "${NSS_ARCHIVE}" ]; then
|
||||
# echo "Downloading ${NSS_ARCHIVE}"
|
||||
# curl -L -O "${NSS_URL}"
|
||||
# else
|
||||
# echo "Using ${NSS_ARCHIVE}"
|
||||
# fi
|
||||
# echo "${NSS_SHA256} ${NSS_ARCHIVE}" | shasum -a 256 -c - || exit 2
|
||||
# tar xfz "${NSS_ARCHIVE}"
|
||||
NSS_SRC_PATH=$(abspath "${NSS}")
|
||||
./patch-nss-src.sh "${NSS_SRC_PATH}"
|
||||
|
||||
# Some NSS symbols clash with OpenSSL symbols, rename them using
|
||||
# C preprocessor define macros.
|
||||
echo $'\
|
||||
diff -r 65efa74ef84a coreconf/config.gypi
|
||||
--- a/coreconf/config.gypi Thu May 16 09:43:04 2019 +0000
|
||||
+++ b/coreconf/config.gypi Thu May 23 19:46:44 2019 -0400
|
||||
@@ -138,6 +138,21 @@
|
||||
\'<(nspr_include_dir)\',
|
||||
\'<(nss_dist_dir)/private/<(module)\',
|
||||
],
|
||||
+ \'defines\': [
|
||||
+ \'HMAC_Update=NSS_HMAC_Update\',
|
||||
+ \'HMAC_Init=NSS_HMAC_Init\',
|
||||
+ \'MD5_Update=NSS_MD5_Update\',
|
||||
+ \'SHA1_Update=NSS_SHA1_Update\',
|
||||
+ \'SHA256_Update=NSS_SHA256_Update\',
|
||||
+ \'SHA224_Update=NSS_SHA224_Update\',
|
||||
+ \'SHA512_Update=NSS_SHA512_Update\',
|
||||
+ \'SHA384_Update=NSS_SHA384_Update\',
|
||||
+ \'SEED_set_key=NSS_SEED_set_key\',
|
||||
+ \'SEED_encrypt=NSS_SEED_encrypt\',
|
||||
+ \'SEED_decrypt=NSS_SEED_decrypt\',
|
||||
+ \'SEED_ecb_encrypt=NSS_SEED_ecb_encrypt\',
|
||||
+ \'SEED_cbc_encrypt=NSS_SEED_cbc_encrypt\',
|
||||
+ ],
|
||||
\'conditions\': [
|
||||
[ \'mozpkix_only==1 and OS=="linux"\', {
|
||||
\'include_dirs\': [
|
||||
' | patch "${NSS_SRC_PATH}/nss/coreconf/config.gypi"
|
||||
|
||||
if [ "${PLATFORM}" == "ios" ]
|
||||
then
|
||||
|
|
|
@ -19,38 +19,32 @@ TOOLCHAIN=${5}
|
|||
ANDROID_NDK_API_VERSION=${6}
|
||||
|
||||
if [ -d "${DIST_DIR}" ]; then
|
||||
echo "${DIST_DIR}"" folder already exists. Skipping build."
|
||||
echo "${DIST_DIR} folder already exists. Skipping build."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
PLATFORM_PATH="${ANDROID_NDK_ROOT}/platforms/android-${ANDROID_NDK_API_VERSION}/arch-${ARCH}"
|
||||
USE_64=""
|
||||
if [ "${TOOLCHAIN}" == "x86_64-linux-android" ]
|
||||
then
|
||||
CPU_ARCH="x86_64"
|
||||
GYP_ARCH="x64"
|
||||
LDFLAGS="-L${PLATFORM_PATH}/usr/lib64"
|
||||
USE_64=1
|
||||
NSPR_64="--enable-64bit"
|
||||
elif [ "${TOOLCHAIN}" == "i686-linux-android" ]
|
||||
then
|
||||
CPU_ARCH="x86"
|
||||
GYP_ARCH="ia32"
|
||||
elif [ "${TOOLCHAIN}" == "aarch64-linux-android" ]
|
||||
then
|
||||
CPU_ARCH="arm"
|
||||
USE_64=1
|
||||
GYP_ARCH="arm64"
|
||||
NSPR_64="--enable-64bit"
|
||||
elif [ "${TOOLCHAIN}" == "arm-linux-androideabi" ]
|
||||
then
|
||||
CPU_ARCH="arm"
|
||||
GYP_ARCH="arm"
|
||||
else
|
||||
echo "Unknown toolchain"
|
||||
exit 1
|
||||
fi
|
||||
NSPR_64=""
|
||||
NSS_64=""
|
||||
if [[ -n "${USE_64}" ]]; then
|
||||
NSPR_64="--enable-64bit"
|
||||
NSS_64="USE_64=1"
|
||||
fi
|
||||
LDFLAGS=${LDFLAGS:-}
|
||||
LDFLAGS="${LDFLAGS:-}"
|
||||
NSPR_64="${NSPR_64:-""}"
|
||||
|
||||
# Build NSPR
|
||||
NSPR_BUILD_DIR=$(mktemp -d)
|
||||
|
@ -69,45 +63,61 @@ popd
|
|||
|
||||
# Build NSS
|
||||
BUILD_DIR=$(mktemp -d)
|
||||
# The ANDROID_ vars are just set so the Makefile doesn't complain.
|
||||
make \
|
||||
CROSS_COMPILE=1 \
|
||||
ANDROID_NDK="${ANDROID_NDK_ROOT}" \
|
||||
ANDROID_TOOLCHAIN_VERSION="${ANDROID_NDK_API_VERSION}" \
|
||||
CC="${TOOLCHAIN_PATH}/bin/${TOOLCHAIN}-clang" \
|
||||
CCC="${TOOLCHAIN_PATH}/bin/${TOOLCHAIN}-clang++" \
|
||||
RANLIB="${TOOLCHAIN_PATH}/bin/${TOOLCHAIN}-ranlib" \
|
||||
OS_TARGET=Android \
|
||||
${NSS_64} \
|
||||
LDFLAGS="${LDFLAGS}" \
|
||||
CPU_ARCH="${CPU_ARCH}" \
|
||||
ARCHFLAG="-D__ANDROID_API__=${ANDROID_NDK_API_VERSION}" \
|
||||
BUILD_OPT=1 \
|
||||
NSS_DISABLE_CHACHAPOLY=1 \
|
||||
NSS_DISABLE_DBM=1 \
|
||||
BUILD_TREE="${BUILD_DIR}" \
|
||||
SOURCE_PREFIX="${BUILD_DIR}/dist" \
|
||||
SOURCE_MD_DIR="${BUILD_DIR}/dist" \
|
||||
DIST="${BUILD_DIR}/dist" \
|
||||
SOURCE_MDHEADERS_DIR="${NSPR_BUILD_DIR}/dist/include/nspr" \
|
||||
NSPR_INCLUDE_DIR="${NSPR_BUILD_DIR}/dist/include/nspr" \
|
||||
NSPR_LIB_DIR="${NSPR_BUILD_DIR}/dist/lib" \
|
||||
NSINSTALL="${NSPR_BUILD_DIR}/config/nsinstall" \
|
||||
-C "${NSS_SRC_DIR}/nss"
|
||||
export AR="${TOOLCHAIN_PATH}/bin/${TOOLCHAIN}-ar"
|
||||
export CC="${TOOLCHAIN_PATH}/bin/${TOOLCHAIN}-clang"
|
||||
export CXX="${TOOLCHAIN_PATH}/bin/${TOOLCHAIN}-clang++"
|
||||
export LD="${TOOLCHAIN_PATH}/bin/${TOOLCHAIN}-ld"
|
||||
export NM="${TOOLCHAIN_PATH}/bin/${TOOLCHAIN}-nm"
|
||||
export RANLIB="${TOOLCHAIN_PATH}/bin/${TOOLCHAIN}-ranlib"
|
||||
export READELF="${TOOLCHAIN_PATH}/bin/${TOOLCHAIN}-readelf"
|
||||
|
||||
BUILD_DIR=$(mktemp -d)
|
||||
rm -rf "${NSS_SRC_DIR}/nss/out"
|
||||
gyp -f ninja-android "${NSS_SRC_DIR}/nss/nss.gyp" \
|
||||
--depth "${NSS_SRC_DIR}/nss/" \
|
||||
--generator-output=. \
|
||||
-DOS=android \
|
||||
-Dnspr_lib_dir="${NSPR_BUILD_DIR}/dist/lib" \
|
||||
-Dnspr_include_dir="${NSPR_BUILD_DIR}/dist/include/nspr" \
|
||||
-Dnss_dist_dir="${BUILD_DIR}" \
|
||||
-Dnss_dist_obj_dir="${BUILD_DIR}" \
|
||||
-Dhost_arch="${GYP_ARCH}" \
|
||||
-Dtarget_arch="${GYP_ARCH}" \
|
||||
-Dstatic_libs=1 \
|
||||
-Ddisable_dbm=1 \
|
||||
-Dsign_libs=0 \
|
||||
-Denable_sslkeylogfile=0 \
|
||||
-Ddisable_tests=1 \
|
||||
-Ddisable_libpkix=1
|
||||
|
||||
GENERATED_DIR="${NSS_SRC_DIR}/nss/out/Release"
|
||||
ninja -C "${GENERATED_DIR}"
|
||||
|
||||
mkdir -p "${DIST_DIR}/include/nss"
|
||||
mkdir -p "${DIST_DIR}/lib"
|
||||
cp -p -L "${BUILD_DIR}/dist"/lib/libfreebl3.so "${DIST_DIR}/lib"
|
||||
cp -p -L "${BUILD_DIR}/dist"/lib/libnss3.so "${DIST_DIR}/lib"
|
||||
cp -p -L "${BUILD_DIR}/dist"/lib/libnssckbi.so "${DIST_DIR}/lib"
|
||||
cp -p -L "${BUILD_DIR}/dist"/lib/libnssutil3.so "${DIST_DIR}/lib"
|
||||
cp -p -L "${BUILD_DIR}/dist"/lib/libsmime3.so "${DIST_DIR}/lib"
|
||||
cp -p -L "${BUILD_DIR}/dist"/lib/libsoftokn3.so "${DIST_DIR}/lib"
|
||||
cp -p -L "${BUILD_DIR}/dist"/lib/libssl3.so "${DIST_DIR}/lib"
|
||||
cp -p -L "${BUILD_DIR}/dist"/lib/libsqlite3.so "${DIST_DIR}/lib"
|
||||
cp -p -L "${NSPR_BUILD_DIR}/dist"/lib/libplc4.so "${DIST_DIR}/lib"
|
||||
cp -p -L "${NSPR_BUILD_DIR}/dist"/lib/libplds4.so "${DIST_DIR}/lib"
|
||||
cp -p -L "${NSPR_BUILD_DIR}/dist"/lib/libnspr4.so "${DIST_DIR}/lib"
|
||||
cp -p -L "${BUILD_DIR}/lib/libcertdb.a" "${DIST_DIR}/lib"
|
||||
cp -p -L "${BUILD_DIR}/lib/libcerthi.a" "${DIST_DIR}/lib"
|
||||
cp -p -L "${BUILD_DIR}/lib/libcryptohi.a" "${DIST_DIR}/lib"
|
||||
cp -p -L "${BUILD_DIR}/lib/libfreebl_static.a" "${DIST_DIR}/lib"
|
||||
cp -p -L "${BUILD_DIR}/lib/libnss_static.a" "${DIST_DIR}/lib"
|
||||
cp -p -L "${BUILD_DIR}/lib/libnssb.a" "${DIST_DIR}/lib"
|
||||
cp -p -L "${BUILD_DIR}/lib/libnssdev.a" "${DIST_DIR}/lib"
|
||||
cp -p -L "${BUILD_DIR}/lib/libnsspki.a" "${DIST_DIR}/lib"
|
||||
cp -p -L "${BUILD_DIR}/lib/libnssutil.a" "${DIST_DIR}/lib"
|
||||
cp -p -L "${BUILD_DIR}/lib/libpk11wrap_static.a" "${DIST_DIR}/lib"
|
||||
cp -p -L "${BUILD_DIR}/lib/libpkcs12.a" "${DIST_DIR}/lib"
|
||||
cp -p -L "${BUILD_DIR}/lib/libpkcs7.a" "${DIST_DIR}/lib"
|
||||
cp -p -L "${BUILD_DIR}/lib/libsmime.a" "${DIST_DIR}/lib"
|
||||
cp -p -L "${BUILD_DIR}/lib/libsoftokn_static.a" "${DIST_DIR}/lib"
|
||||
cp -p -L "${BUILD_DIR}/lib/libssl.a" "${DIST_DIR}/lib"
|
||||
cp -p -L "${BUILD_DIR}/lib/libhw-acc-crypto.a" "${DIST_DIR}/lib"
|
||||
# HW specific.
|
||||
if [[ "${TOOLCHAIN}" == "i686-linux-android" ]] || [[ "${TOOLCHAIN}" == "x86_64-linux-android" ]]; then
|
||||
cp -p -L "${BUILD_DIR}/lib/libgcm-aes-x86_c_lib.a" "${DIST_DIR}/lib"
|
||||
fi
|
||||
cp -p -L "${NSPR_BUILD_DIR}/dist/lib/libplc4.a" "${DIST_DIR}/lib"
|
||||
cp -p -L "${NSPR_BUILD_DIR}/dist/lib/libplds4.a" "${DIST_DIR}/lib"
|
||||
cp -p -L "${NSPR_BUILD_DIR}/dist/lib/libnspr4.a" "${DIST_DIR}/lib"
|
||||
|
||||
cp -p -L "${BUILD_DIR}/dist"/public/nss/* "${DIST_DIR}/include/nss"
|
||||
cp -p -L -R "${NSPR_BUILD_DIR}/dist"/include/nspr/* "${DIST_DIR}/include/nss"
|
||||
cp -p -L -R "${BUILD_DIR}/public/nss/"* "${DIST_DIR}/include/nss"
|
||||
cp -p -L -R "${NSPR_BUILD_DIR}/dist/include/nspr/"* "${DIST_DIR}/include/nss"
|
||||
|
|
|
@ -7,11 +7,11 @@ set -euvx
|
|||
if [ "${#}" -lt 1 ] || [ "${#}" -gt 2 ]
|
||||
then
|
||||
echo "Usage:"
|
||||
echo "./build-nss-desktop.sh <NSS_SRC_PATH> [CROSS_COMPILE_TARGET]"
|
||||
echo "./build-nss-desktop.sh <ABSOLUTE_SRC_DIR> [CROSS_COMPILE_TARGET]"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
NSS_SRC_PATH=${1}
|
||||
NSS_SRC_DIR=${1}
|
||||
# Whether to cross compile from Linux to a different target. Really
|
||||
# only intended for automation.
|
||||
CROSS_COMPILE_TARGET=${2-}
|
||||
|
@ -22,117 +22,88 @@ if [ -n "${CROSS_COMPILE_TARGET}" ] && [ "$(uname -s)" != "Linux" ]; then
|
|||
fi
|
||||
|
||||
if [[ "${CROSS_COMPILE_TARGET}" =~ "win32-x86-64" ]]; then
|
||||
NSS_DIR=$(abspath "desktop/win32-x86-64/nss")
|
||||
DIST_DIR=$(abspath "desktop/win32-x86-64/nss")
|
||||
elif [[ "${CROSS_COMPILE_TARGET}" =~ "darwin" ]]; then
|
||||
NSS_DIR=$(abspath "desktop/darwin/nss")
|
||||
DIST_DIR=$(abspath "desktop/darwin/nss")
|
||||
elif [ -n "${CROSS_COMPILE_TARGET}" ]; then
|
||||
echo "Cannot build NSS for unrecognized target OS ${CROSS_COMPILE_TARGET}"
|
||||
exit 1
|
||||
elif [ "$(uname -s)" == "Darwin" ]; then
|
||||
NSS_DIR=$(abspath "desktop/darwin/nss")
|
||||
DIST_DIR=$(abspath "desktop/darwin/nss")
|
||||
elif [ "$(uname -s)" == "Linux" ]; then
|
||||
# This is a JNA weirdness: "x86-64" rather than "x86_64".
|
||||
NSS_DIR=$(abspath "desktop/linux-x86-64/nss")
|
||||
DIST_DIR=$(abspath "desktop/linux-x86-64/nss")
|
||||
else
|
||||
echo "Cannot build NSS on unrecognized host OS $(uname -s)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -d "${NSS_DIR}" ]; then
|
||||
echo "${NSS_DIR} folder already exists. Skipping build."
|
||||
if [ -d "${DIST_DIR}" ]; then
|
||||
echo "${DIST_DIR} folder already exists. Skipping build."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
NSPR_BUILD_DIR=$(mktemp -d)
|
||||
BUILD_DIR=$(mktemp -d)
|
||||
|
||||
EXTRA_MAKE_ARGS=()
|
||||
# Build NSPR.
|
||||
pushd "${NSPR_BUILD_DIR}"
|
||||
# TODO compile on macOS/windows machines once `chainOfTrust` is supported on macOS (1499051).
|
||||
if [[ "${CROSS_COMPILE_TARGET}" =~ "darwin" ]]; then
|
||||
# TODO cross compile ourselves, I lost my sanity over this and gave up.
|
||||
curl -L -O "https://s3-us-west-2.amazonaws.com/fxa-dev-bucket/nss/nss-dist.tar.bz2"
|
||||
SHA256="e744a4e0ea7daad75b28eef63d6ced0acd8a993a850998018916e0cad82dc382"
|
||||
echo "${SHA256} nss-dist.tar.bz2" | shasum -a 256 -c - || exit 2
|
||||
tar xvjf nss-dist.tar.bz2
|
||||
mkdir -p "${NSS_DIR}/include/nss"
|
||||
mkdir -p "${NSS_DIR}/lib"
|
||||
cp -p -L dist/Debug/lib/libnss3.dylib "${NSS_DIR}/lib"
|
||||
cp -p -L dist/Debug/lib/libnssutil3.dylib "${NSS_DIR}/lib"
|
||||
cp -p -L dist/Debug/lib/libfreebl3.dylib "${NSS_DIR}/lib"
|
||||
cp -p -L dist/Debug/lib/libnssckbi.dylib "${NSS_DIR}/lib"
|
||||
cp -p -L dist/Debug/lib/libsmime3.dylib "${NSS_DIR}/lib"
|
||||
cp -p -L dist/Debug/lib/libsoftokn3.dylib "${NSS_DIR}/lib"
|
||||
cp -p -L dist/Debug/lib/libssl3.dylib "${NSS_DIR}/lib"
|
||||
cp -p -L dist/Debug/lib/libplc4.dylib "${NSS_DIR}/lib"
|
||||
cp -p -L dist/Debug/lib/libplds4.dylib "${NSS_DIR}/lib"
|
||||
cp -p -L dist/Debug/lib/libnspr4.dylib "${NSS_DIR}/lib"
|
||||
cp -p -L dist/public/nss/*.h "${NSS_DIR}/include/nss"
|
||||
cp -p -L -R dist/Debug/include/nspr/* "${NSS_DIR}/include/nss"
|
||||
rm -rf dist && rm -f nss-dist.tar.bz2
|
||||
exit 0
|
||||
|
||||
# Generated from nss-try@0c5d37301637ed024de8c2cbdbecf144aae12163.
|
||||
curl -L -O "https://fxa-dev-bucket.s3-us-west-2.amazonaws.com/a-s/nss_nspr_static_libs_darwin.tar.bz2"
|
||||
SHA256="b25d6d057d39213aeb5426dfbb0223a1d33f1706a1fcde1b3547fd7895c922f7"
|
||||
echo "${SHA256} nss_nspr_static_libs_darwin.tar.bz2" | shasum -a 256 -c - || exit 2
|
||||
tar xvjf nss_nspr_static_libs_darwin.tar.bz2 && rm -rf nss_nspr_static_libs_darwin.tar.bz2
|
||||
NSS_DIST_DIR=$(abspath "dist")
|
||||
elif [[ "${CROSS_COMPILE_TARGET}" =~ "win32-x86-64" ]]; then
|
||||
# Build NSPR.
|
||||
"${NSS_SRC_PATH}"/nspr/configure \
|
||||
--target x86_64-w64-mingw32 \
|
||||
--enable-64bit \
|
||||
--disable-debug \
|
||||
--enable-optimize
|
||||
EXTRA_MAKE_ARGS+=('OS_ARCH=WINNT')
|
||||
EXTRA_MAKE_ARGS+=('OS_TARGET=WIN95')
|
||||
EXTRA_MAKE_ARGS+=('NS_USE_GCC=1')
|
||||
EXTRA_MAKE_ARGS+=('CC=x86_64-w64-mingw32-gcc')
|
||||
EXTRA_MAKE_ARGS+=('CCC=x86_64-w64-mingw32-gcc')
|
||||
EXTRA_MAKE_ARGS+=('RC=x86_64-w64-mingw32-windres -O coff --use-temp-file')
|
||||
# Generated from nss-try@0c5d37301637ed024de8c2cbdbecf144aae12163.
|
||||
curl -L -O "https://fxa-dev-bucket.s3-us-west-2.amazonaws.com/a-s/nss_nspr_static_libs_win32.7z"
|
||||
SHA256="cdafb89f727f7a5d6cf1c6c01b58af150f780a4438863d3a1f98b6aa50809ded"
|
||||
echo "${SHA256} nss_nspr_static_libs_win32.7z" | shasum -a 256 -c - || exit 2
|
||||
7z x nss_nspr_static_libs_win32.7z -aoa && rm -rf nss_nspr_static_libs_win32.7z
|
||||
NSS_DIST_DIR=$(abspath "dist")
|
||||
elif [ "$(uname -s)" == "Darwin" ] || [ "$(uname -s)" == "Linux" ]; then
|
||||
"${NSS_SRC_PATH}"/nspr/configure \
|
||||
--enable-64bit \
|
||||
--disable-debug \
|
||||
--enable-optimize
|
||||
"${NSS_SRC_DIR}"/nss/build.sh \
|
||||
--opt \
|
||||
--static \
|
||||
--disable-tests \
|
||||
-Ddisable_dbm=1 \
|
||||
-Dsign_libs=0 \
|
||||
-Ddisable_libpkix=1
|
||||
NSS_DIST_DIR="${NSS_SRC_DIR}/dist"
|
||||
fi
|
||||
make
|
||||
popd
|
||||
|
||||
# Build NSS.
|
||||
make \
|
||||
${EXTRA_MAKE_ARGS[@]+"${EXTRA_MAKE_ARGS[@]}"} \
|
||||
USE_64=1 \
|
||||
BUILD_OPT=1 \
|
||||
NSS_DISABLE_CHACHAPOLY=1 \
|
||||
NSS_DISABLE_DBM=1 \
|
||||
SOURCE_MDHEADERS_DIR="${NSPR_BUILD_DIR}/dist/include/nspr" \
|
||||
NSPR_INCLUDE_DIR="${NSPR_BUILD_DIR}/dist/include/nspr" \
|
||||
NSPR_LIB_DIR="${NSPR_BUILD_DIR}/dist/lib" \
|
||||
NSINSTALL="${NSPR_BUILD_DIR}/config/nsinstall" \
|
||||
BUILD_TREE="${BUILD_DIR}" \
|
||||
SOURCE_PREFIX="${BUILD_DIR}/dist" \
|
||||
SOURCE_MD_DIR="${BUILD_DIR}/dist" \
|
||||
DIST="${BUILD_DIR}/dist" \
|
||||
-C "${NSS_SRC_PATH}/nss"
|
||||
|
||||
mkdir -p "${NSS_DIR}/include/nss"
|
||||
mkdir -p "${NSS_DIR}/lib"
|
||||
|
||||
if [[ "${CROSS_COMPILE_TARGET}" =~ "win32-x86-64" ]]; then
|
||||
EXT="dll"
|
||||
EXT="lib"
|
||||
PREFIX=""
|
||||
elif [ "$(uname -s)" == "Darwin" ] || [ "$(uname -s)" == "Linux" ]; then
|
||||
[[ "$(uname -s)" == "Darwin" ]] && EXT="dylib" || EXT="so"
|
||||
EXT="a"
|
||||
PREFIX="lib"
|
||||
fi
|
||||
|
||||
cp -p -L "${BUILD_DIR}/dist/lib/${PREFIX}freebl3.${EXT}" "${NSS_DIR}/lib"
|
||||
cp -p -L "${BUILD_DIR}/dist/lib/${PREFIX}nss3.${EXT}" "${NSS_DIR}/lib"
|
||||
cp -p -L "${BUILD_DIR}/dist/lib/${PREFIX}nssckbi.${EXT}" "${NSS_DIR}/lib"
|
||||
cp -p -L "${BUILD_DIR}/dist/lib/${PREFIX}nssutil3.${EXT}" "${NSS_DIR}/lib"
|
||||
cp -p -L "${BUILD_DIR}/dist/lib/${PREFIX}smime3.${EXT}" "${NSS_DIR}/lib"
|
||||
cp -p -L "${BUILD_DIR}/dist/lib/${PREFIX}softokn3.${EXT}" "${NSS_DIR}/lib"
|
||||
cp -p -L "${BUILD_DIR}/dist/lib/${PREFIX}ssl3.${EXT}" "${NSS_DIR}/lib"
|
||||
mkdir -p "${DIST_DIR}/include/nss"
|
||||
mkdir -p "${DIST_DIR}/lib"
|
||||
NSS_DIST_OBJ_DIR="${NSS_DIST_DIR}/Release"
|
||||
cp -p -L "${NSS_DIST_OBJ_DIR}/lib/${PREFIX}certdb.${EXT}" "${DIST_DIR}/lib"
|
||||
cp -p -L "${NSS_DIST_OBJ_DIR}/lib/${PREFIX}certhi.${EXT}" "${DIST_DIR}/lib"
|
||||
cp -p -L "${NSS_DIST_OBJ_DIR}/lib/${PREFIX}cryptohi.${EXT}" "${DIST_DIR}/lib"
|
||||
cp -p -L "${NSS_DIST_OBJ_DIR}/lib/${PREFIX}freebl_static.${EXT}" "${DIST_DIR}/lib"
|
||||
cp -p -L "${NSS_DIST_OBJ_DIR}/lib/${PREFIX}nss_static.${EXT}" "${DIST_DIR}/lib"
|
||||
cp -p -L "${NSS_DIST_OBJ_DIR}/lib/${PREFIX}nssb.${EXT}" "${DIST_DIR}/lib"
|
||||
cp -p -L "${NSS_DIST_OBJ_DIR}/lib/${PREFIX}nssdev.${EXT}" "${DIST_DIR}/lib"
|
||||
cp -p -L "${NSS_DIST_OBJ_DIR}/lib/${PREFIX}nsspki.${EXT}" "${DIST_DIR}/lib"
|
||||
cp -p -L "${NSS_DIST_OBJ_DIR}/lib/${PREFIX}nssutil.${EXT}" "${DIST_DIR}/lib"
|
||||
cp -p -L "${NSS_DIST_OBJ_DIR}/lib/${PREFIX}pk11wrap_static.${EXT}" "${DIST_DIR}/lib"
|
||||
cp -p -L "${NSS_DIST_OBJ_DIR}/lib/${PREFIX}pkcs12.${EXT}" "${DIST_DIR}/lib"
|
||||
cp -p -L "${NSS_DIST_OBJ_DIR}/lib/${PREFIX}pkcs7.${EXT}" "${DIST_DIR}/lib"
|
||||
cp -p -L "${NSS_DIST_OBJ_DIR}/lib/${PREFIX}smime.${EXT}" "${DIST_DIR}/lib"
|
||||
cp -p -L "${NSS_DIST_OBJ_DIR}/lib/${PREFIX}softokn_static.${EXT}" "${DIST_DIR}/lib"
|
||||
cp -p -L "${NSS_DIST_OBJ_DIR}/lib/${PREFIX}ssl.${EXT}" "${DIST_DIR}/lib"
|
||||
cp -p -L "${NSS_DIST_OBJ_DIR}/lib/${PREFIX}hw-acc-crypto.${EXT}" "${DIST_DIR}/lib"
|
||||
# HW specific.
|
||||
cp -p -L "${NSS_DIST_OBJ_DIR}/lib/${PREFIX}gcm-aes-x86_c_lib.${EXT}" "${DIST_DIR}/lib"
|
||||
# For some reason the NSPR libs always have the "lib" prefix even on Windows.
|
||||
cp -p -L "${NSPR_BUILD_DIR}/dist/lib/libplc4.${EXT}" "${NSS_DIR}/lib"
|
||||
cp -p -L "${NSPR_BUILD_DIR}/dist/lib/libplds4.${EXT}" "${NSS_DIR}/lib"
|
||||
cp -p -L "${NSPR_BUILD_DIR}/dist/lib/libnspr4.${EXT}" "${NSS_DIR}/lib"
|
||||
cp -p -L "${NSS_DIST_OBJ_DIR}/lib/libplc4.${EXT}" "${DIST_DIR}/lib/${PREFIX}plc4.${EXT}"
|
||||
cp -p -L "${NSS_DIST_OBJ_DIR}/lib/libplds4.${EXT}" "${DIST_DIR}/lib/${PREFIX}plds4.${EXT}"
|
||||
cp -p -L "${NSS_DIST_OBJ_DIR}/lib/libnspr4.${EXT}" "${DIST_DIR}/lib/${PREFIX}nspr4.${EXT}"
|
||||
|
||||
cp -p -L "${BUILD_DIR}/dist"/public/nss/* "${NSS_DIR}/include/nss"
|
||||
cp -p -L -R "${NSPR_BUILD_DIR}/dist"/include/nspr/* "${NSS_DIR}/include/nss"
|
||||
cp -p -L -R "${NSS_DIST_DIR}/public/nss/"* "${DIST_DIR}/include/nss"
|
||||
cp -p -L -R "${NSS_DIST_OBJ_DIR}/include/nspr/"* "${DIST_DIR}/include/nss"
|
||||
|
||||
rm -rf "${NSS_DIST_DIR}"
|
||||
|
|
|
@ -17,14 +17,18 @@ ARCH=${3}
|
|||
IOS_MIN_SDK_VERSION=${4}
|
||||
|
||||
if [ -d "${DIST_DIR}" ]; then
|
||||
echo "${DIST_DIR}"" folder already exists. Skipping build."
|
||||
echo "${DIST_DIR} folder already exists. Skipping build."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ "${ARCH}" == "i386" || "${ARCH}" == "x86_64" ]]; then
|
||||
if [[ "${ARCH}" == "x86_64" ]]; then
|
||||
OS_COMPILER="iPhoneSimulator"
|
||||
elif [[ "${ARCH}" == "armv7" || "${ARCH}" == "arm64" ]]; then
|
||||
TARGET="x86_64-apple-darwin"
|
||||
GYP_ARCH="x64"
|
||||
elif [[ "${ARCH}" == "arm64" ]]; then
|
||||
OS_COMPILER="iPhoneOS"
|
||||
TARGET="aarch64-apple-darwin"
|
||||
GYP_ARCH="arm64"
|
||||
else
|
||||
echo "Unsupported architecture"
|
||||
exit 1
|
||||
|
@ -35,8 +39,7 @@ CROSS_TOP="${DEVELOPER}/Platforms/${OS_COMPILER}.platform/Developer"
|
|||
CROSS_SDK="${OS_COMPILER}.sdk"
|
||||
TOOLCHAIN_BIN="${DEVELOPER}/Toolchains/XcodeDefault.xctoolchain/usr/bin"
|
||||
ISYSROOT="${CROSS_TOP}/SDKs/${CROSS_SDK}"
|
||||
CC="${TOOLCHAIN_BIN}/clang -arch ${ARCH} -isysroot ${ISYSROOT} -lc++ -mios-version-min=${IOS_MIN_SDK_VERSION}"
|
||||
CPU_ARCH="arm" # Static on purpose as NSS's Makefiles don't try to do anything funny when CPU_ARCH == "arm".
|
||||
CC="${TOOLCHAIN_BIN}/clang -arch ${ARCH} -isysroot ${ISYSROOT} -mios-version-min=${IOS_MIN_SDK_VERSION}"
|
||||
|
||||
# Build NSPR
|
||||
NSPR_BUILD_DIR=$(mktemp -d)
|
||||
|
@ -46,10 +49,10 @@ pushd "${NSPR_BUILD_DIR}"
|
|||
RANLIB="${TOOLCHAIN_BIN}/ranlib" \
|
||||
AR="${TOOLCHAIN_BIN}/ar" \
|
||||
AS="${TOOLCHAIN_BIN}/as" \
|
||||
LD="${TOOLCHAIN_BIN}/ld -arch arm64" \
|
||||
LD="${TOOLCHAIN_BIN}/ld" \
|
||||
CC="${CC}" \
|
||||
CCC="${CC}" \
|
||||
--target aarch64-apple-darwin \
|
||||
--target "${TARGET}" \
|
||||
--enable-64bit \
|
||||
--disable-debug \
|
||||
--enable-optimize
|
||||
|
@ -58,44 +61,53 @@ popd
|
|||
|
||||
# Build NSS
|
||||
BUILD_DIR=$(mktemp -d)
|
||||
make \
|
||||
CROSS_COMPILE=1 \
|
||||
STRIP="${TOOLCHAIN_BIN}/strip" \
|
||||
RANLIB="${TOOLCHAIN_BIN}/ranlib" \
|
||||
AR="${TOOLCHAIN_BIN}/ar cr"' $@' \
|
||||
AS="${TOOLCHAIN_BIN}/as -arch ${ARCH} -isysroot ${ISYSROOT}" \
|
||||
LINK="${TOOLCHAIN_BIN}/ld -arch ${ARCH}" \
|
||||
CC="${CC}" \
|
||||
CCC="${CC}" \
|
||||
OS_ARCH=Darwin \
|
||||
OS_TEST="${CPU_ARCH}" \
|
||||
CPU_ARCH="${CPU_ARCH}" \
|
||||
USE_64=1 \
|
||||
BUILD_OPT=1 \
|
||||
NSS_DISABLE_CHACHAPOLY=1 \
|
||||
NSS_DISABLE_DBM=1 \
|
||||
BUILD_TREE="${BUILD_DIR}" \
|
||||
SOURCE_PREFIX="${BUILD_DIR}/dist" \
|
||||
SOURCE_MD_DIR="${BUILD_DIR}/dist" \
|
||||
DIST="${BUILD_DIR}/dist" \
|
||||
SOURCE_MDHEADERS_DIR="${NSPR_BUILD_DIR}/dist/include/nspr" \
|
||||
NSPR_INCLUDE_DIR="${NSPR_BUILD_DIR}/dist/include/nspr" \
|
||||
NSPR_LIB_DIR="${NSPR_BUILD_DIR}/dist/lib" \
|
||||
NSINSTALL="${NSPR_BUILD_DIR}/config/nsinstall" \
|
||||
-C "${NSS_SRC_DIR}/nss"
|
||||
rm -rf "${NSS_SRC_DIR}/nss/out"
|
||||
gyp -f ninja "${NSS_SRC_DIR}/nss/nss.gyp" \
|
||||
--depth "${NSS_SRC_DIR}/nss/" \
|
||||
--generator-output=. \
|
||||
-DOS=ios \
|
||||
-Dnspr_lib_dir="${NSPR_BUILD_DIR}/dist/lib" \
|
||||
-Dnspr_include_dir="${NSPR_BUILD_DIR}/dist/include/nspr" \
|
||||
-Dnss_dist_dir="${BUILD_DIR}" \
|
||||
-Dnss_dist_obj_dir="${BUILD_DIR}" \
|
||||
-Dhost_arch="${GYP_ARCH}" \
|
||||
-Dtarget_arch="${GYP_ARCH}" \
|
||||
-Dstatic_libs=1 \
|
||||
-Ddisable_dbm=1 \
|
||||
-Dsign_libs=0 \
|
||||
-Denable_sslkeylogfile=0 \
|
||||
-Ddisable_tests=1 \
|
||||
-Ddisable_libpkix=1 \
|
||||
-Diphone_deployment_target="${IOS_MIN_SDK_VERSION}"
|
||||
|
||||
GENERATED_DIR="${NSS_SRC_DIR}/nss/out/Release-$(echo ${OS_COMPILER} | tr '[:upper:]' '[:lower:]')/"
|
||||
ninja -C "${GENERATED_DIR}"
|
||||
|
||||
mkdir -p "${DIST_DIR}/include/nss"
|
||||
mkdir -p "${DIST_DIR}/lib"
|
||||
cp -p -L "${BUILD_DIR}/dist"/lib/libfreebl3.dylib "${DIST_DIR}/lib"
|
||||
cp -p -L "${BUILD_DIR}/dist"/lib/libnss3.dylib "${DIST_DIR}/lib"
|
||||
cp -p -L "${BUILD_DIR}/dist"/lib/libnssckbi.dylib "${DIST_DIR}/lib"
|
||||
cp -p -L "${BUILD_DIR}/dist"/lib/libnssutil3.dylib "${DIST_DIR}/lib"
|
||||
cp -p -L "${BUILD_DIR}/dist"/lib/libsmime3.dylib "${DIST_DIR}/lib"
|
||||
cp -p -L "${BUILD_DIR}/dist"/lib/libsoftokn3.dylib "${DIST_DIR}/lib"
|
||||
cp -p -L "${BUILD_DIR}/dist"/lib/libssl3.dylib "${DIST_DIR}/lib"
|
||||
cp -p -L "${NSPR_BUILD_DIR}/dist"/lib/libplc4.dylib "${DIST_DIR}/lib"
|
||||
cp -p -L "${NSPR_BUILD_DIR}/dist"/lib/libplds4.dylib "${DIST_DIR}/lib"
|
||||
cp -p -L "${NSPR_BUILD_DIR}/dist"/lib/libnspr4.dylib "${DIST_DIR}/lib"
|
||||
cp -p -L "${BUILD_DIR}/lib/libcertdb.a" "${DIST_DIR}/lib"
|
||||
cp -p -L "${BUILD_DIR}/lib/libcerthi.a" "${DIST_DIR}/lib"
|
||||
cp -p -L "${BUILD_DIR}/lib/libcryptohi.a" "${DIST_DIR}/lib"
|
||||
cp -p -L "${BUILD_DIR}/lib/libfreebl_static.a" "${DIST_DIR}/lib"
|
||||
cp -p -L "${BUILD_DIR}/lib/libnss_static.a" "${DIST_DIR}/lib"
|
||||
cp -p -L "${BUILD_DIR}/lib/libnssb.a" "${DIST_DIR}/lib"
|
||||
cp -p -L "${BUILD_DIR}/lib/libnssdev.a" "${DIST_DIR}/lib"
|
||||
cp -p -L "${BUILD_DIR}/lib/libnsspki.a" "${DIST_DIR}/lib"
|
||||
cp -p -L "${BUILD_DIR}/lib/libnssutil.a" "${DIST_DIR}/lib"
|
||||
cp -p -L "${BUILD_DIR}/lib/libpk11wrap_static.a" "${DIST_DIR}/lib"
|
||||
cp -p -L "${BUILD_DIR}/lib/libpkcs12.a" "${DIST_DIR}/lib"
|
||||
cp -p -L "${BUILD_DIR}/lib/libpkcs7.a" "${DIST_DIR}/lib"
|
||||
cp -p -L "${BUILD_DIR}/lib/libsmime.a" "${DIST_DIR}/lib"
|
||||
cp -p -L "${BUILD_DIR}/lib/libsoftokn_static.a" "${DIST_DIR}/lib"
|
||||
cp -p -L "${BUILD_DIR}/lib/libssl.a" "${DIST_DIR}/lib"
|
||||
cp -p -L "${BUILD_DIR}/lib/libhw-acc-crypto.a" "${DIST_DIR}/lib"
|
||||
# HW specific.
|
||||
if [[ "${ARCH}" == "x86_64" ]]; then
|
||||
cp -p -L "${BUILD_DIR}/lib/libgcm-aes-x86_c_lib.a" "${DIST_DIR}/lib"
|
||||
fi
|
||||
cp -p -L "${NSPR_BUILD_DIR}/dist/lib/libplc4.a" "${DIST_DIR}/lib"
|
||||
cp -p -L "${NSPR_BUILD_DIR}/dist/lib/libplds4.a" "${DIST_DIR}/lib"
|
||||
cp -p -L "${NSPR_BUILD_DIR}/dist/lib/libnspr4.a" "${DIST_DIR}/lib"
|
||||
|
||||
cp -p -L "${BUILD_DIR}/dist"/public/nss/* "${DIST_DIR}/include/nss"
|
||||
cp -p -L -R "${NSPR_BUILD_DIR}/dist"/include/nspr/* "${DIST_DIR}/include/nss"
|
||||
cp -p -L -R "${BUILD_DIR}/public/nss/"* "${DIST_DIR}/include/nss"
|
||||
cp -p -L -R "${NSPR_BUILD_DIR}/dist/include/nspr/"* "${DIST_DIR}/include/nss"
|
||||
|
|
|
@ -7,35 +7,30 @@ set -euvx
|
|||
if [ "${#}" -ne 6 ]
|
||||
then
|
||||
echo "Usage:"
|
||||
echo "./build-sqlcipher-android.sh <ABSOLUTE_SRC_DIR> <DIST_DIR> <TOOLCHAIN_PATH> <TOOLCHAIN> <ANDROID_NDK_API_VERSION> <OPENSSL_DIR>"
|
||||
echo "./build-sqlcipher-android.sh <ABSOLUTE_SRC_DIR> <DIST_DIR> <TOOLCHAIN_PATH> <TOOLCHAIN> <ANDROID_NDK_API_VERSION> <NSS_DIR>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
SQLCIPHER_DIR=${1}
|
||||
SQLCIPHER_SRC_DIR=${1}
|
||||
DIST_DIR=${2}
|
||||
TOOLCHAIN_PATH=${3}
|
||||
TOOLCHAIN=${4}
|
||||
ANDROID_NDK_API_VERSION=${5}
|
||||
OPENSSL_DIR=${6}
|
||||
NSS_DIR=${6}
|
||||
|
||||
if [ -d "${DIST_DIR}" ]; then
|
||||
echo "${DIST_DIR}"" folder already exists. Skipping build."
|
||||
echo "${DIST_DIR} folder already exists. Skipping build."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
cd "${SQLCIPHER_DIR}"
|
||||
|
||||
export TOOLCHAIN_BIN="${TOOLCHAIN_PATH}""/bin/"
|
||||
export CC="${TOOLCHAIN_BIN}""${TOOLCHAIN}""-gcc"
|
||||
export CXX="${TOOLCHAIN_BIN}""${TOOLCHAIN}""-g++"
|
||||
export RANLIB="${TOOLCHAIN_BIN}""${TOOLCHAIN}""-ranlib"
|
||||
export LD="${TOOLCHAIN_BIN}""${TOOLCHAIN}""-ld"
|
||||
export AR="${TOOLCHAIN_BIN}""${TOOLCHAIN}""-ar"
|
||||
export TOOLCHAIN_BIN="${TOOLCHAIN_PATH}/bin"
|
||||
export CC="${TOOLCHAIN_BIN}/${TOOLCHAIN}-gcc"
|
||||
export CXX="${TOOLCHAIN_BIN}/${TOOLCHAIN}-g++"
|
||||
export RANLIB="${TOOLCHAIN_BIN}/${TOOLCHAIN}-ranlib"
|
||||
export LD="${TOOLCHAIN_BIN}/${TOOLCHAIN}-ld"
|
||||
export AR="${TOOLCHAIN_BIN}/${TOOLCHAIN}-ar"
|
||||
export CFLAGS="-D__ANDROID_API__=${ANDROID_NDK_API_VERSION}"
|
||||
|
||||
SQLCIPHER_OUTPUT_PATH="/tmp/sqlcipher-""${TOOLCHAIN}_${$}"
|
||||
mkdir -p "${SQLCIPHER_OUTPUT_PATH}"
|
||||
|
||||
if [ "${TOOLCHAIN}" == "x86_64-linux-android" ]
|
||||
then
|
||||
HOST="x86_64-linux"
|
||||
|
@ -76,30 +71,63 @@ SQLCIPHER_CFLAGS=" \
|
|||
-DSQLITE_ENABLE_FTS3_PARENTHESIS \
|
||||
-DSQLITE_ENABLE_FTS4 \
|
||||
-DSQLITE_ENABLE_FTS5 \
|
||||
-DSQLCIPHER_CRYPTO_OPENSSL \
|
||||
-DSQLCIPHER_CRYPTO_NSS \
|
||||
-DSQLITE_ENABLE_DBSTAT_VTAB \
|
||||
-DSQLITE_SECURE_DELETE \
|
||||
-DSQLITE_DEFAULT_PAGE_SIZE=32768 \
|
||||
-DSQLITE_MAX_DEFAULT_PAGE_SIZE=32768 \
|
||||
-I${NSS_DIR}/include \
|
||||
"
|
||||
|
||||
make clean || true
|
||||
LIBS="\
|
||||
-lcertdb \
|
||||
-lcerthi \
|
||||
-lcryptohi \
|
||||
-lfreebl_static \
|
||||
-lhw-acc-crypto \
|
||||
-lnspr4 \
|
||||
-lnss_static \
|
||||
-lnssb \
|
||||
-lnssdev \
|
||||
-lnsspki \
|
||||
-lnssutil \
|
||||
-lpk11wrap_static \
|
||||
-lplc4 \
|
||||
-lplds4 \
|
||||
-lsoftokn_static \
|
||||
"
|
||||
|
||||
./configure --prefix="${SQLCIPHER_OUTPUT_PATH}" \
|
||||
if [[ "${TOOLCHAIN}" == "x86_64-linux-android" ]] || [[ "${TOOLCHAIN}" == "i686-linux-android" ]]; then
|
||||
LIBS="${LIBS} -lgcm-aes-x86_c_lib"
|
||||
fi
|
||||
|
||||
BUILD_DIR=$(mktemp -d)
|
||||
pushd "${BUILD_DIR}"
|
||||
|
||||
"${SQLCIPHER_SRC_DIR}/configure" \
|
||||
--host="${HOST}" \
|
||||
--enable-tempstore=always \
|
||||
CFLAGS="${CFLAGS} ${SQLCIPHER_CFLAGS} -I${OPENSSL_DIR}/include -L${OPENSSL_DIR}/lib" \
|
||||
LIBS="-lcrypto -llog -lm" \
|
||||
LDFLAGS="${OPENSSL_DIR}/lib/libcrypto.a "
|
||||
--with-pic \
|
||||
--verbose \
|
||||
--disable-shared \
|
||||
--with-crypto-lib=none \
|
||||
--disable-tcl \
|
||||
--enable-tempstore=yes \
|
||||
CFLAGS="${CFLAGS} ${SQLCIPHER_CFLAGS}" \
|
||||
LDFLAGS="-L${NSS_DIR}/lib" \
|
||||
LIBS="${LIBS} -llog -lm"
|
||||
|
||||
make -j6
|
||||
make install
|
||||
make sqlite3.h
|
||||
make sqlite3ext.h
|
||||
make libsqlcipher.la
|
||||
|
||||
mkdir -p "${DIST_DIR}""/include/sqlcipher"
|
||||
mkdir -p "${DIST_DIR}""/lib"
|
||||
mkdir -p "${DIST_DIR}/include/sqlcipher"
|
||||
mkdir -p "${DIST_DIR}/lib"
|
||||
|
||||
cp -p "${SQLCIPHER_OUTPUT_PATH}"/lib/libsqlcipher.a "${DIST_DIR}"/lib/libsqlcipher.a
|
||||
cp -p "${BUILD_DIR}/sqlite3.h" "${DIST_DIR}/include/sqlcipher"
|
||||
cp -p "${BUILD_DIR}/sqlite3ext.h" "${DIST_DIR}/include/sqlcipher"
|
||||
cp -p "${BUILD_DIR}/.libs/libsqlcipher.a" "${DIST_DIR}/lib"
|
||||
|
||||
# Just in case, ensure that the created binaries are not -w.
|
||||
chmod +w "${DIST_DIR}"/lib/libsqlcipher.a
|
||||
rm -rf "${SQLCIPHER_OUTPUT_PATH}"
|
||||
chmod +w "${DIST_DIR}/lib/libsqlcipher.a"
|
||||
|
||||
popd
|
||||
|
|
|
@ -5,11 +5,11 @@ set -euvx
|
|||
if [ "${#}" -lt 1 ] || [ "${#}" -gt 2 ]
|
||||
then
|
||||
echo "Usage:"
|
||||
echo "./build-sqlcipher-desktop.sh <SQLCIPHER_SRC_PATH> [CROSS_COMPILE_TARGET]"
|
||||
echo "./build-sqlcipher-desktop.sh <ABSOLUTE_SRC_DIR> [CROSS_COMPILE_TARGET]"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
SQLCIPHER_SRC_PATH=${1}
|
||||
SQLCIPHER_SRC_DIR=${1}
|
||||
# Whether to cross compile from Linux to a different target. Really
|
||||
# only intended for automation.
|
||||
CROSS_COMPILE_TARGET=${2-}
|
||||
|
@ -20,28 +20,28 @@ if [ -n "${CROSS_COMPILE_TARGET}" ] && [ "$(uname -s)" != "Linux" ]; then
|
|||
fi
|
||||
|
||||
if [[ "${CROSS_COMPILE_TARGET}" =~ "win32-x86-64" ]]; then
|
||||
SQLCIPHER_DIR=$(abspath "desktop/win32-x86-64/sqlcipher")
|
||||
OPENSSL_DIR=$(abspath "desktop/win32-x86-64/openssl")
|
||||
DIST_DIR=$(abspath "desktop/win32-x86-64/sqlcipher")
|
||||
NSS_DIR=$(abspath "desktop/win32-x86-64/nss")
|
||||
elif [[ "${CROSS_COMPILE_TARGET}" =~ "darwin" ]]; then
|
||||
SQLCIPHER_DIR=$(abspath "desktop/darwin/sqlcipher")
|
||||
OPENSSL_DIR=$(abspath "desktop/darwin/openssl")
|
||||
DIST_DIR=$(abspath "desktop/darwin/sqlcipher")
|
||||
NSS_DIR=$(abspath "desktop/darwin/nss")
|
||||
elif [ -n "${CROSS_COMPILE_TARGET}" ]; then
|
||||
echo "Cannot build SQLCipher for unrecognized target OS ${CROSS_COMPILE_TARGET}"
|
||||
exit 1
|
||||
elif [ "$(uname -s)" == "Darwin" ]; then
|
||||
SQLCIPHER_DIR=$(abspath "desktop/darwin/sqlcipher")
|
||||
OPENSSL_DIR=$(abspath "desktop/darwin/openssl")
|
||||
DIST_DIR=$(abspath "desktop/darwin/sqlcipher")
|
||||
NSS_DIR=$(abspath "desktop/darwin/nss")
|
||||
elif [ "$(uname -s)" == "Linux" ]; then
|
||||
# This is a JNA weirdness: "x86-64" rather than "x86_64".
|
||||
SQLCIPHER_DIR=$(abspath "desktop/linux-x86-64/sqlcipher")
|
||||
OPENSSL_DIR=$(abspath "desktop/linux-x86-64/openssl")
|
||||
DIST_DIR=$(abspath "desktop/linux-x86-64/sqlcipher")
|
||||
NSS_DIR=$(abspath "desktop/linux-x86-64/nss")
|
||||
else
|
||||
echo "Cannot build SQLcipher on unrecognized host OS $(uname -s)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -d "${SQLCIPHER_DIR}" ]; then
|
||||
echo "${SQLCIPHER_DIR} folder already exists. Skipping build."
|
||||
if [ -d "${DIST_DIR}" ]; then
|
||||
echo "${DIST_DIR} folder already exists. Skipping build."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
|
@ -70,18 +70,35 @@ SQLCIPHER_CFLAGS=" \
|
|||
-DSQLITE_ENABLE_FTS3_PARENTHESIS \
|
||||
-DSQLITE_ENABLE_FTS4 \
|
||||
-DSQLITE_ENABLE_FTS5 \
|
||||
-DSQLCIPHER_CRYPTO_OPENSSL \
|
||||
-DSQLCIPHER_CRYPTO_NSS \
|
||||
-DSQLITE_ENABLE_DBSTAT_VTAB \
|
||||
-DSQLITE_SECURE_DELETE=1 \
|
||||
-DSQLITE_DEFAULT_PAGE_SIZE=32768 \
|
||||
-DSQLITE_MAX_DEFAULT_PAGE_SIZE=32768 \
|
||||
-I${NSS_DIR}/include \
|
||||
"
|
||||
|
||||
rm -rf "${SQLCIPHER_SRC_PATH}/build-desktop"
|
||||
mkdir -p "${SQLCIPHER_SRC_PATH}/build-desktop/install-prefix"
|
||||
pushd "${SQLCIPHER_SRC_PATH}/build-desktop"
|
||||
LIBS="\
|
||||
-lcertdb \
|
||||
-lcerthi \
|
||||
-lcryptohi \
|
||||
-lfreebl_static \
|
||||
-lhw-acc-crypto \
|
||||
-lnspr4 \
|
||||
-lnss_static \
|
||||
-lnssb \
|
||||
-lnssdev \
|
||||
-lnsspki \
|
||||
-lnssutil \
|
||||
-lpk11wrap_static \
|
||||
-lplc4 \
|
||||
-lplds4 \
|
||||
-lsoftokn_static \
|
||||
-lgcm-aes-x86_c_lib \
|
||||
"
|
||||
|
||||
make clean || true
|
||||
BUILD_DIR=$(mktemp -d)
|
||||
pushd "${BUILD_DIR}"
|
||||
|
||||
# Why `--with-pic --enable-shared`? We're doing unusual things. By
|
||||
# default, libtool builds a static library (.a) with a non-PIC .o, and
|
||||
|
@ -111,19 +128,19 @@ if [[ "${CROSS_COMPILE_TARGET}" =~ "darwin" ]]; then
|
|||
# See https://searchfox.org/mozilla-central/rev/8848b9741fc4ee4e9bc3ae83ea0fc048da39979f/build/macosx/cross-mozconfig.common#12-13.
|
||||
export LD_LIBRARY_PATH=/tmp/clang/lib
|
||||
|
||||
../configure --prefix="${PWD}/install-prefix" \
|
||||
"${SQLCIPHER_SRC_DIR}/configure" \
|
||||
--with-pic \
|
||||
--disable-shared \
|
||||
--host=x86_64-apple-darwin \
|
||||
--with-crypto-lib=none \
|
||||
--disable-tcl \
|
||||
--enable-tempstore=yes \
|
||||
CFLAGS="${CFLAGS} ${SQLCIPHER_CFLAGS} -I${OPENSSL_DIR}/include -L${OPENSSL_DIR}/lib" \
|
||||
LDFLAGS="${LDFLAGS} -L${OPENSSL_DIR}/lib" \
|
||||
LIBS="-lcrypto"
|
||||
CFLAGS="${CFLAGS} ${SQLCIPHER_CFLAGS}" \
|
||||
LDFLAGS="${LDFLAGS} -L${NSS_DIR}/lib" \
|
||||
LIBS="${LIBS}"
|
||||
elif [[ "${CROSS_COMPILE_TARGET}" =~ "win32-x86-64" ]]; then
|
||||
|
||||
pushd ..
|
||||
pushd "${SQLCIPHER_SRC_DIR}"
|
||||
|
||||
# From https://github.com/qTox/qTox/blob/9525505bff8719c84b6193174ea5e7ec097c54b8/windows/cross-compile/build.sh#L390-L446.
|
||||
# shellcheck disable=SC2016
|
||||
|
@ -152,7 +169,7 @@ EOF
|
|||
patch --forward --ignore-whitespace < Makefile.in-patch
|
||||
popd
|
||||
|
||||
../configure --prefix="${PWD}/install-prefix" \
|
||||
"${SQLCIPHER_SRC_DIR}/configure" \
|
||||
--with-pic \
|
||||
--disable-shared \
|
||||
--build=x86_64 \
|
||||
|
@ -161,42 +178,46 @@ popd
|
|||
--enable-tempstore=yes \
|
||||
--with-crypto-lib=none \
|
||||
--disable-tcl \
|
||||
CFLAGS="${SQLCIPHER_CFLAGS} -I${OPENSSL_DIR}/include -L${OPENSSL_DIR}/lib" \
|
||||
LDFLAGS="-L${OPENSSL_DIR}/lib" \
|
||||
LIBS="-llibcrypto -lgdi32 -lws2_32"
|
||||
CFLAGS="${SQLCIPHER_CFLAGS}" \
|
||||
LDFLAGS="-L${NSS_DIR}/lib" \
|
||||
LIBS="${LIBS}"
|
||||
elif [ "$(uname -s)" == "Darwin" ]; then
|
||||
../configure --prefix="${PWD}/install-prefix" \
|
||||
"${SQLCIPHER_SRC_DIR}/configure" \
|
||||
--with-pic \
|
||||
--disable-shared \
|
||||
--enable-tempstore=yes \
|
||||
--with-crypto-lib=none \
|
||||
--disable-tcl \
|
||||
CFLAGS="${SQLCIPHER_CFLAGS} -I${OPENSSL_DIR}/include -L${OPENSSL_DIR}/lib" \
|
||||
LDFLAGS="-L${OPENSSL_DIR}/lib" \
|
||||
LIBS="-lcrypto"
|
||||
CFLAGS="${SQLCIPHER_CFLAGS}" \
|
||||
LDFLAGS="-L${NSS_DIR}/lib" \
|
||||
LIBS="${LIBS}"
|
||||
elif [ "$(uname -s)" == "Linux" ]; then
|
||||
../configure --prefix="${PWD}/install-prefix" \
|
||||
"${SQLCIPHER_SRC_DIR}/configure" \
|
||||
--with-pic \
|
||||
--disable-shared \
|
||||
--enable-tempstore=yes \
|
||||
--with-crypto-lib=none \
|
||||
--disable-tcl \
|
||||
CFLAGS="${SQLCIPHER_CFLAGS} -I${OPENSSL_DIR}/include -L${OPENSSL_DIR}/lib" \
|
||||
LDFLAGS="-L${OPENSSL_DIR}/lib" \
|
||||
LIBS="-lcrypto -ldl -lm -lpthread"
|
||||
CFLAGS="${SQLCIPHER_CFLAGS}" \
|
||||
LDFLAGS="-L${NSS_DIR}/lib" \
|
||||
LIBS="${LIBS}"
|
||||
fi
|
||||
|
||||
make -j6
|
||||
make install
|
||||
make sqlite3.h
|
||||
make sqlite3ext.h
|
||||
make libsqlcipher.la
|
||||
|
||||
mkdir -p "${SQLCIPHER_DIR}/lib"
|
||||
cp -r "install-prefix/include" "${SQLCIPHER_DIR}"
|
||||
cp -p "install-prefix/lib/libsqlcipher.a" "${SQLCIPHER_DIR}/lib/libsqlcipher.a"
|
||||
mkdir -p "${DIST_DIR}/include/sqlcipher"
|
||||
mkdir -p "${DIST_DIR}/lib"
|
||||
|
||||
chmod +w "${SQLCIPHER_DIR}/lib/libsqlcipher.a"
|
||||
cp -p "${BUILD_DIR}/sqlite3.h" "${DIST_DIR}/include/sqlcipher"
|
||||
cp -p "${BUILD_DIR}/sqlite3ext.h" "${DIST_DIR}/include/sqlcipher"
|
||||
cp -p "${BUILD_DIR}/.libs/libsqlcipher.a" "${DIST_DIR}/lib"
|
||||
|
||||
chmod +w "${DIST_DIR}/lib/libsqlcipher.a"
|
||||
|
||||
if [[ "${CROSS_COMPILE_TARGET}" =~ "win32-x86-64" ]]; then
|
||||
mv "${SQLCIPHER_DIR}/lib/libsqlcipher.a" "${SQLCIPHER_DIR}/lib/sqlcipher.lib"
|
||||
mv "${DIST_DIR}/lib/libsqlcipher.a" "${DIST_DIR}/lib/sqlcipher.lib"
|
||||
fi
|
||||
|
||||
popd
|
||||
|
|
|
@ -17,20 +17,18 @@ ARCH=${3}
|
|||
IOS_MIN_SDK_VERSION=${4}
|
||||
|
||||
if [ -d "${DIST_DIR}" ]; then
|
||||
echo "${DIST_DIR}"" folder already exists. Skipping build."
|
||||
echo "${DIST_DIR} folder already exists. Skipping build."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
SQLCIPHER_IOS="${SQLCIPHER_SRC_DIR}/build-ios-""${ARCH}_${$}"
|
||||
mkdir -p "${SQLCIPHER_IOS}"
|
||||
pushd "${SQLCIPHER_IOS}"
|
||||
|
||||
if [[ "${ARCH}" == "x86_64" ]]; then
|
||||
OS_COMPILER="iPhoneSimulator"
|
||||
HOST="x86_64-apple-darwin"
|
||||
NSS_DIR=$(abspath "ios/x86_64/nss")
|
||||
elif [[ "${ARCH}" == "arm64" ]]; then
|
||||
OS_COMPILER="iPhoneOS"
|
||||
HOST="arm-apple-darwin"
|
||||
NSS_DIR=$(abspath "ios/arm64/nss")
|
||||
else
|
||||
echo "Unsupported architecture"
|
||||
exit 1
|
||||
|
@ -72,7 +70,10 @@ SQLCIPHER_CFLAGS=" \
|
|||
-DSQLITE_ENABLE_FTS4 \
|
||||
-DSQLITE_ENABLE_FTS5 \
|
||||
-DHAVE_USLEEP=1 \
|
||||
-DSQLCIPHER_CRYPTO_NSS \
|
||||
-I${NSS_DIR}/include \
|
||||
"
|
||||
|
||||
# These additional options are used on desktop, but are not currently
|
||||
# used on iOS until we can performance test them:
|
||||
# -DSQLITE_SOUNDEX \
|
||||
|
@ -93,17 +94,43 @@ SQLCIPHER_CFLAGS=" \
|
|||
# at runtime with `PRAGMA secure_delete`:
|
||||
# -DSQLITE_SECURE_DELETE \
|
||||
|
||||
../configure \
|
||||
LIBS="\
|
||||
-lcertdb \
|
||||
-lcerthi \
|
||||
-lcryptohi \
|
||||
-lfreebl_static \
|
||||
-lhw-acc-crypto \
|
||||
-lnspr4 \
|
||||
-lnss_static \
|
||||
-lnssb \
|
||||
-lnssdev \
|
||||
-lnsspki \
|
||||
-lnssutil \
|
||||
-lpk11wrap_static \
|
||||
-lplc4 \
|
||||
-lplds4 \
|
||||
-lsoftokn_static \
|
||||
"
|
||||
|
||||
if [[ "${ARCH}" == "x86_64" ]]; then
|
||||
LIBS="${LIBS} -lgcm-aes-x86_c_lib"
|
||||
fi
|
||||
|
||||
BUILD_DIR=$(mktemp -d)
|
||||
pushd "${BUILD_DIR}"
|
||||
|
||||
"${SQLCIPHER_SRC_DIR}/configure" \
|
||||
--with-pic \
|
||||
--disable-tcl \
|
||||
--host="${HOST}" \
|
||||
--verbose \
|
||||
--with-crypto-lib=commoncrypto \
|
||||
--with-crypto-lib=none \
|
||||
--enable-tempstore=yes \
|
||||
--enable-threadsafe=yes \
|
||||
--disable-editline \
|
||||
CFLAGS="${CFLAGS} ${SQLCIPHER_CFLAGS}" \
|
||||
LDFLAGS="-framework Security -framework Foundation"
|
||||
LDFLAGS="-L${NSS_DIR}/lib" \
|
||||
LIBS="${LIBS}"
|
||||
|
||||
# Make all fails because it tries to build the command line program.
|
||||
# Can't find a way around this so we'll build what we need... Sort of.
|
||||
|
@ -120,10 +147,8 @@ make libsqlcipher.la
|
|||
mkdir -p "${DIST_DIR}/include/sqlcipher"
|
||||
mkdir -p "${DIST_DIR}/lib"
|
||||
|
||||
cp -p "${SQLCIPHER_IOS}/sqlite3.h" "${DIST_DIR}/include/sqlcipher"
|
||||
cp -p "${SQLCIPHER_IOS}/sqlite3ext.h" "${DIST_DIR}/include/sqlcipher"
|
||||
cp -p "${SQLCIPHER_IOS}/.libs/libsqlcipher.a" "${DIST_DIR}/lib"
|
||||
cp -p "${BUILD_DIR}/sqlite3.h" "${DIST_DIR}/include/sqlcipher"
|
||||
cp -p "${BUILD_DIR}/sqlite3ext.h" "${DIST_DIR}/include/sqlcipher"
|
||||
cp -p "${BUILD_DIR}/.libs/libsqlcipher.a" "${DIST_DIR}/lib"
|
||||
|
||||
popd
|
||||
|
||||
rm -rf "${SQLCIPHER_IOS}"
|
||||
|
|
|
@ -1,83 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# This script patches the NSS/NSPR source code so we can cross-compile
|
||||
# them properly until the fixes are merged upstream and released.
|
||||
|
||||
set -euvx
|
||||
|
||||
if [ "${#}" -ne 1 ]
|
||||
then
|
||||
echo "Usage:"
|
||||
echo "./patch-nss-src.sh <NSS_SRC_PATH>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
NSS_SRC_PATH=${1}
|
||||
|
||||
# Remove once NSS 3.44 is out (see bug 1540205 for context).
|
||||
echo '
|
||||
--- chacha20poly1305.c 2019-03-15 20:25:08.000000000 -0400
|
||||
+++ chacha20poly1305.c.patched 2019-03-29 17:24:37.000000000 -0400
|
||||
@@ -157,6 +157,7 @@
|
||||
#endif
|
||||
}
|
||||
|
||||
+#ifndef NSS_DISABLE_CHACHAPOLY
|
||||
void
|
||||
ChaCha20Xor(uint8_t *output, uint8_t *block, uint32_t len, uint8_t *k,
|
||||
uint8_t *nonce, uint32_t ctr)
|
||||
@@ -167,6 +168,7 @@
|
||||
Hacl_Chacha20_chacha20(output, block, len, k, nonce, ctr);
|
||||
}
|
||||
}
|
||||
+#endif
|
||||
|
||||
SECStatus
|
||||
ChaCha20Poly1305_Seal(const ChaCha20Poly1305Context *ctx, unsigned char *output,
|
||||
' | patch "${NSS_SRC_PATH}/nss/lib/freebl/chacha20poly1305.c"
|
||||
|
||||
# TODO: file bug to get this upstream.
|
||||
# shellcheck disable=SC2016
|
||||
echo '
|
||||
--- configure 2019-03-07 05:04:05.000000000 -0500
|
||||
+++ configure.patched 2019-04-02 15:04:27.000000000 -0400
|
||||
@@ -2641,6 +2641,12 @@
|
||||
|
||||
|
||||
case "$target" in
|
||||
+x86_64-linux*-android*)
|
||||
+ android_tool_prefix="x86_64-linux-android"
|
||||
+ ;;
|
||||
+aarch64-linux*-android*)
|
||||
+ android_tool_prefix="aarch64-linux-android"
|
||||
+ ;;
|
||||
arm-linux*-android*|*-linuxandroid*)
|
||||
android_tool_prefix="arm-linux-androideabi"
|
||||
;;
|
||||
' | patch "${NSS_SRC_PATH}/nspr/configure"
|
||||
|
||||
# TODO: file bug to get this upstream.
|
||||
# shellcheck disable=SC2016
|
||||
echo '
|
||||
--- Linux.mk 2019-04-02 14:55:31.000000000 -0400
|
||||
+++ Linux.mk.patched 2019-04-02 14:55:32.000000000 -0400
|
||||
@@ -135,6 +135,10 @@
|
||||
endif
|
||||
OS_LIBS = $(OS_PTHREAD) -ldl -lc
|
||||
|
||||
+ifeq ($(OS_TARGET),Android)
|
||||
+ OS_LIBS += -llog
|
||||
+endif
|
||||
+
|
||||
ifdef USE_PTHREADS
|
||||
DEFINES += -D_REENTRANT
|
||||
endif
|
||||
' | patch "${NSS_SRC_PATH}/nss/coreconf/Linux.mk"
|
||||
|
||||
# TODO: file bug to get this upstream.
|
||||
# This param makes mingw32 gcc trip.
|
||||
sed -i -e '/w44996/d' "${NSS_SRC_PATH}/nss/lib/sqlite/Makefile"
|
||||
|
||||
# We probably don't want to upstream this.
|
||||
# Only build NSS lib and skip tests
|
||||
sed -i -e '/^DIRS = /s/ cmd cpputil gtests$//' "${NSS_SRC_PATH}/nss/manifest.mn"
|
|
@ -96,11 +96,9 @@ afterEvaluate {
|
|||
}
|
||||
def buildType = "${variant.buildType.name.capitalize()}"
|
||||
tasks["generate${productFlavor}${buildType}Assets"].dependsOn(tasks["cargoBuild"])
|
||||
// Don't depend on copyNativeLibs here as NSS libs are shipped with GeckoView.
|
||||
|
||||
// For unit tests.
|
||||
tasks["process${productFlavor}${buildType}UnitTestJavaRes"].dependsOn(tasks["cargoBuild"])
|
||||
tasks["process${productFlavor}${buildType}UnitTestJavaRes"].dependsOn(tasks["copyNativeLibs"])
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -97,11 +97,9 @@ afterEvaluate {
|
|||
}
|
||||
def buildType = "${variant.buildType.name.capitalize()}"
|
||||
tasks["generate${productFlavor}${buildType}Assets"].dependsOn(tasks["cargoBuild"])
|
||||
tasks["generate${productFlavor}${buildType}Assets"].dependsOn(tasks["copyNativeLibs"])
|
||||
|
||||
// For unit tests.
|
||||
tasks["process${productFlavor}${buildType}UnitTestJavaRes"].dependsOn(tasks["cargoBuild"])
|
||||
tasks["process${productFlavor}${buildType}UnitTestJavaRes"].dependsOn(tasks["copyNativeLibs"])
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -98,11 +98,9 @@ afterEvaluate {
|
|||
}
|
||||
def buildType = "${variant.buildType.name.capitalize()}"
|
||||
tasks["generate${productFlavor}${buildType}Assets"].dependsOn(tasks["cargoBuild"])
|
||||
// Don't depend on copyNativeLibs here as NSS libs are shipped with GeckoView.
|
||||
|
||||
// For unit tests.
|
||||
tasks["process${productFlavor}${buildType}UnitTestJavaRes"].dependsOn(tasks["cargoBuild"])
|
||||
tasks["process${productFlavor}${buildType}UnitTestJavaRes"].dependsOn(tasks["copyNativeLibs"])
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -59,10 +59,6 @@ ext.configurePublish = { jnaForTestConfiguration = null,
|
|||
}
|
||||
|
||||
project.afterEvaluate {
|
||||
def copyNativeLibsTask = tasks.findByName("copyNativeLibs")
|
||||
if (copyNativeLibsTask != null) {
|
||||
forUnitTestsJarTask.dependsOn(copyNativeLibsTask)
|
||||
}
|
||||
forUnitTestsJarTask.dependsOn(tasks["cargoBuild"])
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче