This commit is contained in:
Edouard Oger 2019-07-09 14:00:48 -04:00 коммит произвёл GitHub
Родитель 9d414bb360
Коммит 270b0ea2a0
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
86 изменённых файлов: 4981 добавлений и 1021 удалений

Просмотреть файл

@ -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`.

Просмотреть файл

@ -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.

227
Cargo.lock сгенерированный
Просмотреть файл

@ -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

Просмотреть файл

@ -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

10
libs/.gitignore поставляемый
Просмотреть файл

@ -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"])
}
}