Remove rc_crypto
This commit is contained in:
Родитель
4347ea9c59
Коммит
08d1f8c3c9
22
COPYRIGHT
22
COPYRIGHT
|
@ -1,25 +1,3 @@
|
|||
Unless otherwise specified (below and/or in individual files), the code in
|
||||
this repository is subject to the Mozilla Public License, version 2.0
|
||||
<LICENSE-MPL> or <https://www.mozilla.org/en-US/MPL/2.0/>.
|
||||
|
||||
The following directories contain some amount of third-party
|
||||
code and are subject to their own license terms.
|
||||
|
||||
* rc_crypto, in components/support/rc_crypto heavily borrows its API
|
||||
and portions of its implementation from the Rust portions of ring,
|
||||
which carries an ISC-style license, reproduced below, and available at
|
||||
<https://github.com/briansmith/ring/blob/master/LICENSE>
|
||||
|
||||
Copyright 2015-2019 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.
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -14,8 +14,6 @@ members = [
|
|||
"components/support/ffi",
|
||||
"components/support/force-viaduct-reqwest",
|
||||
"components/support/interrupt",
|
||||
"components/support/rc_crypto",
|
||||
"components/support/rc_crypto/nss_sys",
|
||||
"components/viaduct",
|
||||
"components/sync15",
|
||||
"components/rc_log",
|
||||
|
|
|
@ -27,7 +27,6 @@ untrusted = "0.6.2"
|
|||
url = "1.7.1"
|
||||
ffi-support = { path = "../support/ffi" }
|
||||
viaduct = { path = "../viaduct" }
|
||||
rc_crypto = { path = "../support/rc_crypto" }
|
||||
error-support = { path = "../support/error" }
|
||||
|
||||
[dev-dependencies]
|
||||
|
|
|
@ -111,9 +111,6 @@ pub enum ErrorKind {
|
|||
info: String,
|
||||
},
|
||||
|
||||
#[fail(display = "Crypto/NSS error: {}", _0)]
|
||||
CryptoError(#[fail(cause)] rc_crypto::Error),
|
||||
|
||||
// Basically reimplement error_chain's foreign_links. (Ugh, this sucks)
|
||||
#[fail(display = "http-ece encryption error: {}", _0)]
|
||||
EceError(#[fail(cause)] ece::Error),
|
||||
|
@ -156,7 +153,6 @@ pub enum ErrorKind {
|
|||
|
||||
error_support::define_error! {
|
||||
ErrorKind {
|
||||
(CryptoError, rc_crypto::Error),
|
||||
(EceError, ece::Error),
|
||||
(HexDecodeError, hex::FromHexError),
|
||||
(Base64Decode, base64::DecodeError),
|
||||
|
|
|
@ -9,7 +9,7 @@ use crate::{
|
|||
Config,
|
||||
};
|
||||
use hawk_request::HawkRequestBuilder;
|
||||
use rc_crypto::{digest, hkdf, hmac};
|
||||
use ring::{digest, hkdf, hmac};
|
||||
use rsa::RSABrowserIDKeyPair;
|
||||
use serde_derive::*;
|
||||
use serde_json::json;
|
||||
|
@ -201,7 +201,7 @@ pub fn derive_sync_key(kb: &[u8]) -> Result<Vec<u8>> {
|
|||
|
||||
pub fn compute_client_state(kb: &[u8]) -> Result<String> {
|
||||
Ok(hex::encode(
|
||||
&digest::digest(&digest::SHA256, &kb)?.as_ref()[0..16],
|
||||
digest::digest(&digest::SHA256, &kb).as_ref()[0..16].to_vec(),
|
||||
))
|
||||
}
|
||||
|
||||
|
@ -228,8 +228,8 @@ fn derive_key_from_session_token(session_token: &[u8]) -> Result<Vec<u8>> {
|
|||
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)
|
||||
hkdf::extract_and_expand(&salt, ikm, info, &mut out);
|
||||
Ok(out.to_vec())
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
|
|
|
@ -7,7 +7,7 @@ use crate::{
|
|||
scoped_keys::{ScopedKey, ScopedKeysFlow},
|
||||
util, FirefoxAccount, RNG,
|
||||
};
|
||||
use rc_crypto::digest;
|
||||
use ring::digest;
|
||||
use serde_derive::*;
|
||||
use std::{
|
||||
collections::HashSet,
|
||||
|
@ -123,9 +123,9 @@ impl FirefoxAccount {
|
|||
}
|
||||
|
||||
fn oauth_flow(&mut self, mut url: Url, scopes: &[&str], wants_keys: bool) -> Result<String> {
|
||||
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 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 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)
|
||||
|
|
|
@ -4,8 +4,7 @@
|
|||
|
||||
use crate::{errors::*, FirefoxAccount};
|
||||
use byteorder::{BigEndian, ByteOrder};
|
||||
use rc_crypto::digest;
|
||||
use ring::{aead, agreement, agreement::EphemeralPrivateKey, rand::SecureRandom};
|
||||
use ring::{aead, agreement, agreement::EphemeralPrivateKey, digest, rand::SecureRandom};
|
||||
use serde_derive::*;
|
||||
use serde_json::{self, json};
|
||||
use untrusted::Input;
|
||||
|
@ -118,7 +117,7 @@ impl ScopedKeysFlow {
|
|||
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)?)
|
||||
Ok(digest::digest(&digest::SHA256, &buf).as_ref()[0..32].to_vec())
|
||||
},
|
||||
)?;
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use crate::errors::*;
|
||||
use rc_crypto::rand;
|
||||
use ring::rand::SecureRandom;
|
||||
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(len: usize) -> Result<String> {
|
||||
pub fn random_base64_url_string(rng: &dyn SecureRandom, len: usize) -> Result<String> {
|
||||
let mut out = vec![0u8; len];
|
||||
rand::fill(&mut out).map_err(|_| ErrorKind::RngFailure)?;
|
||||
rng.fill(&mut out).map_err(|_| ErrorKind::RngFailure)?;
|
||||
Ok(base64::encode_config(&out, base64::URL_SAFE_NO_PAD))
|
||||
}
|
||||
|
||||
|
|
|
@ -26,7 +26,6 @@ viaduct = { path = "../viaduct" }
|
|||
ffi-support = { path = "../support/ffi" }
|
||||
sql-support = { path = "../support/sql" }
|
||||
error-support = { path = "../support/error" }
|
||||
rc_crypto = { path = "../support/rc_crypto" }
|
||||
|
||||
[dev-dependencies]
|
||||
env_logger = "0.6.1"
|
||||
|
|
|
@ -15,7 +15,6 @@ import com.sun.jna.Structure
|
|||
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(
|
||||
|
@ -72,7 +71,6 @@ open class RustError : Structure() {
|
|||
31 -> return TranscodingError(message)
|
||||
32 -> return EncryptionError(message)
|
||||
33 -> return UrlParseError(message)
|
||||
34 -> return CryptoError(message)
|
||||
-1 -> return InternalPanic(message)
|
||||
// Note: `1` is used as a generic catch all, but we
|
||||
// might as well handle the others the same way.
|
||||
|
|
|
@ -19,7 +19,7 @@ use ece::{
|
|||
LocalKeyPairImpl,
|
||||
};
|
||||
use openssl::ec::EcKey;
|
||||
use rc_crypto::rand;
|
||||
use openssl::rand::rand_bytes;
|
||||
|
||||
use crate::error;
|
||||
|
||||
|
@ -172,7 +172,7 @@ pub struct Crypto;
|
|||
|
||||
pub fn get_bytes(size: usize) -> error::Result<Vec<u8>> {
|
||||
let mut bytes = vec![0u8; size];
|
||||
rand::fill(&mut bytes)?;
|
||||
rand_bytes(bytes.as_mut_slice())?;
|
||||
Ok(bytes)
|
||||
}
|
||||
|
||||
|
|
|
@ -25,7 +25,6 @@ impl From<Error> for ffi_support::ExternError {
|
|||
|
||||
error_support::define_error! {
|
||||
ErrorKind {
|
||||
(CryptoError, rc_crypto::Error),
|
||||
(StorageSqlError, rusqlite::Error),
|
||||
(UrlParseError, url::ParseError),
|
||||
}
|
||||
|
@ -41,9 +40,6 @@ pub enum ErrorKind {
|
|||
#[fail(display = "Internal Error: {:?}", _0)]
|
||||
InternalError(String),
|
||||
|
||||
#[fail(display = "Crypto/NSS error: {}", _0)]
|
||||
CryptoError(#[fail(cause)] rc_crypto::Error),
|
||||
|
||||
/// An unknown OpenSSL Cryptography error
|
||||
#[fail(display = "OpenSSL Error: {:?}", _0)]
|
||||
OpenSSLError(String),
|
||||
|
@ -99,7 +95,6 @@ impl ErrorKind {
|
|||
ErrorKind::TranscodingError(_) => 31,
|
||||
ErrorKind::EncryptionError(_) => 32,
|
||||
ErrorKind::UrlParseError(_) => 33,
|
||||
ErrorKind::CryptoError(_) => 34,
|
||||
};
|
||||
ffi_support::ErrorCode::new(code)
|
||||
}
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
[package]
|
||||
name = "rc_crypto"
|
||||
version = "0.1.0"
|
||||
authors = ["Edouard Oger <eoger@fastmail.com>"]
|
||||
edition = "2018"
|
||||
license = "ISC"
|
||||
|
||||
[lib]
|
||||
crate-type = ["lib", "staticlib", "cdylib"]
|
||||
|
||||
[dependencies]
|
||||
failure = "0.1.5"
|
||||
failure_derive = "0.1.5"
|
||||
error-support = { path = "../error" }
|
||||
|
||||
[target.'cfg(not(target_os = "ios"))'.dependencies]
|
||||
nss_sys = { path = "nss_sys" }
|
||||
|
||||
[target.'cfg(target_os = "ios")'.dependencies]
|
||||
ring = "0.14.5"
|
||||
|
||||
[dev-dependencies]
|
||||
hex = "0.3.2"
|
|
@ -1,11 +0,0 @@
|
|||
# rc_crypto
|
||||
|
||||
rc_crypto, like its name infers, handles all of our cryptographic needs.
|
||||
|
||||
It is backed by the Mozilla-sponsored NSS library through the `nss-sys` crate (more information [here](nss_sys/README.md)).
|
||||
|
||||
It pretty much follows the very rust-idiomatic [ring crate API](https://briansmith.org/rustdoc/ring/).
|
||||
|
||||
## License
|
||||
|
||||
This derives its API and portions of its implementation from the the [`ring`](https://github.com/briansmith/ring/) project, which is available under an ISC-style license. See the COPYRIGHT file, or https://github.com/briansmith/ring/blob/master/LICENSE for details.
|
|
@ -1,19 +0,0 @@
|
|||
[package]
|
||||
name = "nss_sys"
|
||||
version = "0.1.0"
|
||||
authors = ["Edouard Oger <eoger@fastmail.com>"]
|
||||
edition = "2018"
|
||||
license = "MPL-2.0"
|
||||
|
||||
[lib]
|
||||
crate-type = ["lib", "staticlib", "cdylib"]
|
||||
|
||||
[dependencies]
|
||||
libloading = "0.5.0"
|
||||
lazy_static = "1.3.0"
|
||||
|
||||
[build-dependencies]
|
||||
bindgen = "0.49.0"
|
||||
serde = "1.0.90"
|
||||
serde_derive = "1.0.90"
|
||||
toml = "0.5.0"
|
|
@ -1,9 +0,0 @@
|
|||
## nss_sys
|
||||
|
||||
NSS bindings for Rust.
|
||||
|
||||
These bindings are implemented using `dlopen`/`dlsym` instead of linking against libnss.
|
||||
This is so we can re-use the NSS library shipped with GeckoView on Fenix and reference-browser.
|
||||
On Lockbox Android, or even in unit tests artifacts, we ship these library files ourselves alongside our compiled Rust library.
|
||||
|
||||
On iOS the situation is different, we dynamically link because Apple [discourages using `dlopen`](https://github.com/nicklockwood/GZIP/issues/24).
|
|
@ -1,57 +0,0 @@
|
|||
# In this file, every section corresponds to a header file.
|
||||
# A corresponding binding file will be created in $OUT_DIR.
|
||||
|
||||
headers = [
|
||||
"blapit.h",
|
||||
"nss.h",
|
||||
"seccomon.h",
|
||||
"secoidt.h",
|
||||
"prtypes.h",
|
||||
"prerror.h",
|
||||
"pk11pub.h",
|
||||
"pkcs11t.h",
|
||||
]
|
||||
enums = [
|
||||
"PK11Origin",
|
||||
"SECOidTag",
|
||||
"SECStatus",
|
||||
"SECItemType",
|
||||
]
|
||||
types = [
|
||||
"PRBool",
|
||||
"PRInt32",
|
||||
"PRUint32",
|
||||
"PRErrorCode",
|
||||
"NSSInitContext",
|
||||
"NSSInitParameters",
|
||||
"SECItem",
|
||||
"SECOidTag",
|
||||
"SECStatus",
|
||||
"CK_NSS_HKDFParams",
|
||||
"CK_ATTRIBUTE_TYPE",
|
||||
"CK_MECHANISM_TYPE",
|
||||
"PK11Context",
|
||||
"PK11SlotInfo",
|
||||
"PK11SymKey",
|
||||
"PK11Origin",
|
||||
]
|
||||
opaque = [
|
||||
"NSSInitContext",
|
||||
"NSSInitParameters",
|
||||
"PK11Context",
|
||||
"PK11SlotInfo",
|
||||
"PK11SymKey",
|
||||
]
|
||||
variables = [
|
||||
"NSS_INIT_FORCEOPEN",
|
||||
"NSS_INIT_NOCERTDB",
|
||||
"NSS_INIT_NOMODDB",
|
||||
"NSS_INIT_OPTIMIZESPACE",
|
||||
"NSS_INIT_READONLY",
|
||||
"CKA_SIGN",
|
||||
"CKA_WRAP",
|
||||
"CKM_SHA256_HMAC",
|
||||
"CKM_SHA512_HMAC",
|
||||
"CKM_NSS_HKDF_SHA256",
|
||||
"HASH_LENGTH_MAX",
|
||||
]
|
|
@ -1,200 +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/. */
|
||||
|
||||
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>,
|
||||
// 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() {
|
||||
let (lib_dir, include_dir) = get_nss();
|
||||
// See https://kazlauskas.me/entries/writing-proper-buildrs-scripts.html.
|
||||
let target_os = env::var("CARGO_CFG_TARGET_OS");
|
||||
// Only iOS dynamically links with NSS. All the other platforms dlopen.
|
||||
if let Ok("ios") = target_os.as_ref().map(|x| &**x) {
|
||||
println!(
|
||||
"cargo:rustc-link-search=native={}",
|
||||
lib_dir.to_string_lossy()
|
||||
);
|
||||
println!("cargo:include={}", include_dir.to_string_lossy());
|
||||
}
|
||||
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 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!")
|
||||
}
|
||||
}
|
|
@ -1,5 +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/. */
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/nss_bindings.rs"));
|
|
@ -1,81 +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/. */
|
||||
|
||||
#![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::*;
|
||||
use std::os::raw::{c_char, c_int, c_uchar, c_uint, c_void};
|
||||
|
||||
// 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 version this crate is claiming to be compatible with.
|
||||
// We check it at runtime using `NSS_VersionCheck`.
|
||||
pub const COMPATIBLE_NSS_VERSION: &str = "3.26";
|
||||
|
||||
// Code adapted from https://stackoverflow.com/a/35591693. I am not this kind of smart.
|
||||
macro_rules! nss_exports {
|
||||
($(unsafe fn $fn_name:ident($($arg:ident: $argty:ty),*)$( -> $ret:ty)?;)*) => {$(
|
||||
#[cfg(not(target_os = "ios"))]
|
||||
lazy_static::lazy_static! {
|
||||
pub static ref $fn_name: libloading::Symbol<'static, unsafe extern fn($($arg: $argty),*)$( -> $ret)?> = {
|
||||
unsafe {
|
||||
LIBNSS3.get(stringify!($fn_name).as_bytes()).expect(stringify!(Could not get $fn_name handle))
|
||||
}
|
||||
};
|
||||
}
|
||||
#[cfg(target_os = "ios")]
|
||||
extern "C" {
|
||||
pub fn $fn_name($($arg: $argty),*)$( -> $ret)?;
|
||||
}
|
||||
)*};
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "ios"))]
|
||||
lazy_static::lazy_static! {
|
||||
// Lib handle.
|
||||
static ref LIBNSS3: libloading::Library = {
|
||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||
const LIB_NAME: &str = "libnss3.dylib";
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
const LIB_NAME: &str = "libnss3.so";
|
||||
#[cfg(target_os = "windows")]
|
||||
const LIB_NAME: &str = "nss3.dll";
|
||||
libloading::Library::new(LIB_NAME).expect("Cannot load libnss3.")
|
||||
};
|
||||
}
|
||||
|
||||
nss_exports! {
|
||||
unsafe fn PR_GetError() -> PRErrorCode;
|
||||
unsafe fn PR_GetErrorTextLength() -> PRInt32;
|
||||
unsafe fn PR_GetErrorText(out: *mut c_uchar) -> PRInt32;
|
||||
unsafe fn NSS_NoDB_Init(configdir: *const c_char) -> SECStatus;
|
||||
unsafe fn NSS_InitContext(configdir: *const c_char, certPrefix: *const c_char, keyPrefix: *const c_char, secmodName: *const c_char, initParams: *mut NSSInitParameters, flags: PRUint32) -> *mut NSSInitContext;
|
||||
unsafe fn NSS_IsInitialized() -> PRBool;
|
||||
unsafe fn NSS_GetVersion() -> *const c_char;
|
||||
unsafe fn NSS_VersionCheck(importedVersion: *const c_char) -> PRBool;
|
||||
unsafe fn NSS_SecureMemcmp(ia: *const c_void, ib: *const c_void, n: usize) -> c_int;
|
||||
unsafe fn PK11_HashBuf(hashAlg: SECOidTag::Type, out: *mut c_uchar, r#in: *const c_uchar, len: PRInt32) -> SECStatus;
|
||||
unsafe fn PK11_FreeSlot(slot: *mut PK11SlotInfo);
|
||||
unsafe fn PK11_FreeSymKey(symKey: *mut PK11SymKey);
|
||||
unsafe fn PK11_DestroyContext(context: *mut PK11Context, freeit: PRBool);
|
||||
unsafe fn PK11_GetInternalSlot() -> *mut PK11SlotInfo;
|
||||
unsafe fn PK11_ImportSymKey(slot: *mut PK11SlotInfo, r#type: CK_MECHANISM_TYPE, origin: PK11Origin::Type, operation: CK_ATTRIBUTE_TYPE, key: *mut SECItem, wincx: *mut c_void) -> *mut PK11SymKey;
|
||||
unsafe fn PK11_CreateContextBySymKey(r#type: CK_MECHANISM_TYPE, operation: CK_ATTRIBUTE_TYPE, symKey: *mut PK11SymKey, param: *mut SECItem) -> *mut PK11Context;
|
||||
unsafe fn PK11_DigestBegin(cx: *mut PK11Context) -> SECStatus;
|
||||
unsafe fn PK11_DigestOp(context: *mut PK11Context, r#in: *const c_uchar, len: c_uint) -> SECStatus;
|
||||
unsafe fn PK11_DigestFinal(context: *mut PK11Context, data: *mut c_uchar, outLen: *mut c_uint, len: c_uint) -> SECStatus;
|
||||
unsafe fn PK11_GenerateRandom(data: *mut c_uchar, len: c_int) -> SECStatus;
|
||||
unsafe fn PK11_Derive(baseKey: *mut PK11SymKey, mechanism: CK_MECHANISM_TYPE, param: *mut SECItem, target: CK_MECHANISM_TYPE, operation: CK_ATTRIBUTE_TYPE, keySize: c_int) -> *mut PK11SymKey;
|
||||
unsafe fn PK11_ExtractKeyValue(symKey: *mut PK11SymKey) -> SECStatus;
|
||||
unsafe fn PK11_GetKeyData(symKey: *mut PK11SymKey) -> *mut SECItem;
|
||||
}
|
|
@ -1,57 +0,0 @@
|
|||
/* 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::*;
|
||||
#[cfg(not(target_os = "ios"))]
|
||||
use crate::util::ensure_nss_initialized;
|
||||
use std::os::raw::c_void;
|
||||
|
||||
/// 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`.
|
||||
#[cfg(not(target_os = "ios"))]
|
||||
pub fn verify_slices_are_equal(a: &[u8], b: &[u8]) -> Result<()> {
|
||||
// NSS_SecureMemcmp will compare N elements fron our slices,
|
||||
// so make sure they are the same length first.
|
||||
if a.len() != b.len() {
|
||||
return Err(ErrorKind::InternalError.into());
|
||||
}
|
||||
ensure_nss_initialized();
|
||||
|
||||
let result = unsafe {
|
||||
nss_sys::NSS_SecureMemcmp(
|
||||
a.as_ptr() as *const c_void,
|
||||
b.as_ptr() as *const c_void,
|
||||
a.len(),
|
||||
)
|
||||
};
|
||||
match result {
|
||||
0 => Ok(()),
|
||||
_ => Err(ErrorKind::InternalError.into()),
|
||||
}
|
||||
}
|
||||
#[cfg(target_os = "ios")]
|
||||
pub fn verify_slices_are_equal(a: &[u8], b: &[u8]) -> Result<()> {
|
||||
ring::constant_time::verify_slices_are_equal(a, b).map_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());
|
||||
}
|
||||
}
|
|
@ -1,102 +0,0 @@
|
|||
/* 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::*;
|
||||
#[cfg(not(target_os = "ios"))]
|
||||
use crate::util::{ensure_nss_initialized, map_nss_secstatus};
|
||||
use std::convert::TryFrom;
|
||||
|
||||
pub enum Algorithm {
|
||||
SHA256,
|
||||
}
|
||||
pub use Algorithm::*;
|
||||
|
||||
impl Algorithm {
|
||||
fn result_len(&self) -> usize {
|
||||
match self {
|
||||
Algorithm::SHA256 => 32,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "ios"))]
|
||||
impl From<&Algorithm> for nss_sys::SECOidTag::Type {
|
||||
fn from(alg: &Algorithm) -> Self {
|
||||
match alg {
|
||||
Algorithm::SHA256 => nss_sys::SECOidTag::SEC_OID_SHA256,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A calculated digest value.
|
||||
#[derive(Clone)]
|
||||
pub struct Digest {
|
||||
pub(crate) value: Vec<u8>,
|
||||
pub(crate) algorithm: &'static Algorithm,
|
||||
}
|
||||
|
||||
impl Digest {
|
||||
pub fn algorithm(&self) -> &'static 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.
|
||||
#[cfg(not(target_os = "ios"))]
|
||||
pub fn digest(algorithm: &'static Algorithm, data: &[u8]) -> Result<Digest> {
|
||||
let mut out_buf = vec![0u8; algorithm.result_len()];
|
||||
ensure_nss_initialized();
|
||||
let data_len = i32::try_from(data.len())?;
|
||||
map_nss_secstatus(|| unsafe {
|
||||
nss_sys::PK11_HashBuf(
|
||||
algorithm.into(),
|
||||
out_buf.as_mut_ptr(),
|
||||
data.as_ptr(),
|
||||
data_len,
|
||||
)
|
||||
})?;
|
||||
Ok(Digest {
|
||||
value: out_buf,
|
||||
algorithm,
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(target_os = "ios")]
|
||||
pub fn digest(algorithm: &'static Algorithm, data: &[u8]) -> Result<Digest> {
|
||||
let ring_alg = match algorithm {
|
||||
Algorithm::SHA256 => &ring::digest::SHA256,
|
||||
};
|
||||
let ring_digest = ring::digest::digest(&ring_alg, data);
|
||||
Ok(Digest {
|
||||
value: ring_digest.as_ref().to_vec(),
|
||||
algorithm,
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use hex;
|
||||
#[test]
|
||||
fn sha256_digest() {
|
||||
assert_eq!(
|
||||
hex::encode(&digest(&SHA256, b"bobo").unwrap()),
|
||||
"bf0c97708b849de696e7373508b13c5ea92bafa972fc941d694443e494a4b84d"
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
/* 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 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 error")]
|
||||
InternalError,
|
||||
#[fail(display = "Conversion error: {}", _0)]
|
||||
ConversionError(#[fail(cause)] std::num::TryFromIntError),
|
||||
}
|
||||
|
||||
error_support::define_error! {
|
||||
ErrorKind {
|
||||
(ConversionError, std::num::TryFromIntError),
|
||||
}
|
||||
}
|
|
@ -1,123 +0,0 @@
|
|||
/* 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::{digest, error::*, hmac};
|
||||
#[cfg(not(target_os = "ios"))]
|
||||
use crate::{
|
||||
p11,
|
||||
util::{ensure_nss_initialized, map_nss_secstatus},
|
||||
};
|
||||
#[cfg(not(target_os = "ios"))]
|
||||
use nss_sys::*;
|
||||
use std::{
|
||||
convert::TryFrom,
|
||||
mem,
|
||||
os::raw::{c_uchar, c_ulong},
|
||||
ptr,
|
||||
};
|
||||
|
||||
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()))
|
||||
}
|
||||
|
||||
#[cfg(target_os = "ios")]
|
||||
pub fn expand(prk: &hmac::SigningKey, info: &[u8], out: &mut [u8]) -> Result<()> {
|
||||
let ring_digest = match prk.digest_alg {
|
||||
digest::Algorithm::SHA256 => &ring::digest::SHA256,
|
||||
};
|
||||
let ring_prk = ring::hmac::SigningKey::new(&ring_digest, &prk.key_value);
|
||||
ring::hkdf::expand(&ring_prk, info, out);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "ios"))]
|
||||
pub fn expand(prk: &hmac::SigningKey, info: &[u8], out: &mut [u8]) -> Result<()> {
|
||||
let mech = match prk.digest_algorithm() {
|
||||
digest::Algorithm::SHA256 => CKM_NSS_HKDF_SHA256,
|
||||
};
|
||||
ensure_nss_initialized();
|
||||
// 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 bellow.
|
||||
let mut hkdf_params = CK_NSS_HKDFParams {
|
||||
bExtract: CK_FALSE,
|
||||
pSalt: ptr::null_mut(),
|
||||
ulSaltLen: 0,
|
||||
bExpand: CK_TRUE,
|
||||
pInfo: info.as_ptr() as *mut u8,
|
||||
ulInfoLen: c_ulong::try_from(info.len())?,
|
||||
};
|
||||
let mut params = SECItem {
|
||||
type_: SECItemType::siBuffer,
|
||||
data: &mut hkdf_params as *mut _ as *mut c_uchar,
|
||||
len: u32::try_from(mem::size_of::<CK_NSS_HKDFParams>())?,
|
||||
};
|
||||
let base_key = p11::import_sym_key(mech.into(), CKA_WRAP.into(), &prk.key_value)?;
|
||||
let len = i32::try_from(out.len())?;
|
||||
let sym_key = p11::SymKey::from_ptr(unsafe {
|
||||
// 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.
|
||||
PK11_Derive(
|
||||
base_key.as_mut_ptr(),
|
||||
mech.into(),
|
||||
&mut params,
|
||||
CKM_SHA512_HMAC.into(),
|
||||
CKA_SIGN.into(),
|
||||
len,
|
||||
)
|
||||
})?;
|
||||
|
||||
map_nss_secstatus(|| unsafe { 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 `symKey` which we copy into `out`.
|
||||
let key_data = unsafe { *PK11_GetKeyData(sym_key.as_mut_ptr()) };
|
||||
if u32::try_from(out.len())? > key_data.len {
|
||||
return Err(ErrorKind::InternalError.into());
|
||||
}
|
||||
let key_data_len = usize::try_from(key_data.len)?;
|
||||
let buf = unsafe { std::slice::from_raw_parts(key_data.data, key_data_len) };
|
||||
out.copy_from_slice(&buf[0..out.len()]);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use hex;
|
||||
#[test]
|
||||
fn hkdf_extract_expand() {
|
||||
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);
|
||||
}
|
||||
}
|
|
@ -1,143 +0,0 @@
|
|||
/* 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::*};
|
||||
#[cfg(not(target_os = "ios"))]
|
||||
use crate::{
|
||||
p11,
|
||||
util::{ensure_nss_initialized, map_nss_secstatus},
|
||||
};
|
||||
use std::convert::TryFrom;
|
||||
|
||||
/// A calculated signature value.
|
||||
#[derive(Clone)]
|
||||
pub struct Signature(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 correspond 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`.
|
||||
#[cfg(not(target_os = "ios"))]
|
||||
pub fn sign(key: &SigningKey, data: &[u8]) -> Result<Signature> {
|
||||
let mech = match key.digest_alg {
|
||||
digest::Algorithm::SHA256 => nss_sys::CKM_SHA256_HMAC,
|
||||
};
|
||||
ensure_nss_initialized();
|
||||
let sym_key = p11::import_sym_key(mech.into(), nss_sys::CKA_SIGN.into(), &key.key_value)?;
|
||||
let context = p11::create_context_by_sym_key(mech.into(), nss_sys::CKA_SIGN.into(), &sym_key)?;
|
||||
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(Signature(digest::Digest {
|
||||
value: out,
|
||||
algorithm: key.digest_alg,
|
||||
}))
|
||||
}
|
||||
|
||||
#[cfg(target_os = "ios")]
|
||||
pub fn sign(key: &SigningKey, data: &[u8]) -> Result<Signature> {
|
||||
let ring_digest = match key.digest_alg {
|
||||
digest::Algorithm::SHA256 => &ring::digest::SHA256,
|
||||
};
|
||||
let ring_key = ring::hmac::SigningKey::new(&ring_digest, &key.key_value);
|
||||
let ring_signature = ring::hmac::sign(&ring_key, data);
|
||||
Ok(Signature(digest::Digest {
|
||||
value: ring_signature.as_ref().to_vec(),
|
||||
algorithm: key.digest_alg,
|
||||
}))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use hex;
|
||||
#[test]
|
||||
fn hmac_sign_verify() {
|
||||
let key = VerificationKey::new(&digest::SHA256, b"key");
|
||||
let expected_signature =
|
||||
hex::decode("f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8")
|
||||
.unwrap();
|
||||
assert!(verify(
|
||||
&key,
|
||||
b"The quick brown fox jumps over the lazy dog",
|
||||
&expected_signature
|
||||
)
|
||||
.is_ok());
|
||||
}
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
/* 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. */
|
||||
|
||||
/// 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
|
||||
/// (https://briansmith.org/rustdoc/ring/) as it is well thought.
|
||||
pub mod constant_time;
|
||||
pub mod digest;
|
||||
mod error;
|
||||
pub mod hkdf;
|
||||
pub mod hmac;
|
||||
#[cfg(not(target_os = "ios"))]
|
||||
mod p11;
|
||||
pub mod rand;
|
||||
#[cfg(not(target_os = "ios"))]
|
||||
mod util;
|
||||
|
||||
pub use crate::error::{Error, ErrorKind, Result};
|
|
@ -1,119 +0,0 @@
|
|||
/* 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 nss_sys::*;
|
||||
use std::{
|
||||
convert::TryFrom,
|
||||
os::raw::{c_uchar, c_uint},
|
||||
ptr,
|
||||
};
|
||||
|
||||
// The macro defines a wrapper around pointers refering to
|
||||
// types allocated by NSS and calling their NSS destructor
|
||||
// method when they go out of scope avoiding memory leaks.
|
||||
// The `as_ptr`/`as_mut_ptr` are provided to retrieve the
|
||||
// raw pointers for the NSS functions consuming them.
|
||||
macro_rules! scoped_ptr {
|
||||
($scoped:ident, $target:ty, $dtor:path) => {
|
||||
pub struct $scoped {
|
||||
ptr: *mut $target,
|
||||
}
|
||||
|
||||
impl $scoped {
|
||||
pub fn from_ptr(ptr: *mut $target) -> Result<$scoped> {
|
||||
if !ptr.is_null() {
|
||||
Ok($scoped { ptr: ptr })
|
||||
} else {
|
||||
Err(ErrorKind::InternalError.into())
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[allow(dead_code)]
|
||||
pub const fn as_ptr(&self) -> *const $target {
|
||||
self.ptr
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn as_mut_ptr(&self) -> *mut $target {
|
||||
self.ptr
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for $scoped {
|
||||
fn drop(&mut self) {
|
||||
unsafe { $dtor(self.ptr) };
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
scoped_ptr!(Context, PK11Context, pk11_destroy_context_true);
|
||||
scoped_ptr!(SymKey, PK11SymKey, PK11_FreeSymKey);
|
||||
scoped_ptr!(Slot, PK11SlotInfo, PK11_FreeSlot);
|
||||
|
||||
#[inline]
|
||||
unsafe fn pk11_destroy_context_true(context: *mut PK11Context) {
|
||||
PK11_DestroyContext(context, PR_TRUE);
|
||||
}
|
||||
|
||||
/// Safe wrapper around `PK11_GetInternalSlot` that
|
||||
/// de-allocates memory when the slot goes out of
|
||||
/// scope.
|
||||
pub(crate) fn get_internal_slot() -> Result<Slot> {
|
||||
Slot::from_ptr(unsafe { PK11_GetInternalSlot() })
|
||||
}
|
||||
|
||||
/// Safe wrapper around PK11_ImportSymKey that
|
||||
/// de-allocates memory when the key goes out of
|
||||
/// scope.
|
||||
pub(crate) fn import_sym_key(
|
||||
mechanism: CK_MECHANISM_TYPE,
|
||||
operation: CK_ATTRIBUTE_TYPE,
|
||||
buf: &[u8],
|
||||
) -> Result<SymKey> {
|
||||
let mut item = SECItem {
|
||||
type_: SECItemType::siBuffer,
|
||||
data: buf.as_ptr() as *mut c_uchar,
|
||||
len: c_uint::try_from(buf.len())?,
|
||||
};
|
||||
let slot = get_internal_slot()?;
|
||||
SymKey::from_ptr(unsafe {
|
||||
PK11_ImportSymKey(
|
||||
slot.as_mut_ptr(),
|
||||
mechanism,
|
||||
PK11Origin::PK11_OriginUnwrap,
|
||||
operation,
|
||||
&mut item,
|
||||
ptr::null_mut(),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/// Safe wrapper around PK11_CreateContextBySymKey that
|
||||
/// de-allocates memory when the context goes out of
|
||||
/// scope.
|
||||
pub(crate) fn create_context_by_sym_key(
|
||||
mechanism: CK_MECHANISM_TYPE,
|
||||
operation: CK_ATTRIBUTE_TYPE,
|
||||
sym_key: &SymKey,
|
||||
) -> Result<Context> {
|
||||
let mut param = SECItem {
|
||||
type_: SECItemType::siBuffer,
|
||||
data: ptr::null_mut(),
|
||||
len: 0,
|
||||
};
|
||||
Context::from_ptr(unsafe {
|
||||
PK11_CreateContextBySymKey(mechanism, operation, sym_key.as_mut_ptr(), &mut param)
|
||||
})
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
/* 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::*;
|
||||
#[cfg(not(target_os = "ios"))]
|
||||
use crate::util::{ensure_nss_initialized, map_nss_secstatus};
|
||||
use std::convert::TryFrom;
|
||||
|
||||
/// Fill a buffer with cryptographically secure pseudo-random data.
|
||||
#[cfg(not(target_os = "ios"))]
|
||||
pub fn fill(dest: &mut [u8]) -> Result<()> {
|
||||
// `NSS_Init` will initialize the RNG with data from `/dev/urandom`.
|
||||
ensure_nss_initialized();
|
||||
let len = i32::try_from(dest.len())?;
|
||||
map_nss_secstatus(|| unsafe { nss_sys::PK11_GenerateRandom(dest.as_mut_ptr(), len) })?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(target_os = "ios")]
|
||||
pub fn fill(dest: &mut [u8]) -> Result<()> {
|
||||
use ring::rand::SecureRandom;
|
||||
let rng = ring::rand::SystemRandom::new();
|
||||
rng.fill(dest).map_err(|_| ErrorKind::InternalError.into())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
#[test]
|
||||
fn random_fill() {
|
||||
let mut out = vec![0u8; 64];
|
||||
assert!(fill(&mut out).is_ok());
|
||||
assert_ne!(out, vec![0u8; 64]);
|
||||
}
|
||||
}
|
|
@ -1,75 +0,0 @@
|
|||
/* 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 nss_sys::*;
|
||||
use std::{convert::TryFrom, ffi::CString, 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!")
|
||||
}
|
||||
if unsafe { NSS_IsInitialized() } == PR_FALSE {
|
||||
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.
|
||||
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()) };
|
||||
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()
|
||||
}
|
|
@ -21,7 +21,6 @@ log = "0.4"
|
|||
lazy_static = "1.0"
|
||||
base16 = "0.1.1"
|
||||
failure = "0.1.3"
|
||||
rc_crypto = { path = "../support/rc_crypto" }
|
||||
viaduct = { path = "../viaduct" }
|
||||
interrupt = { path = "../support/interrupt" }
|
||||
error-support = { path = "../support/error" }
|
||||
|
|
|
@ -71,9 +71,6 @@ pub enum ErrorKind {
|
|||
#[fail(display = "Store error: {}", _0)]
|
||||
StoreError(#[fail(cause)] failure::Error),
|
||||
|
||||
#[fail(display = "Crypto/NSS error: {}", _0)]
|
||||
CryptoError(#[fail(cause)] rc_crypto::Error),
|
||||
|
||||
// Basically reimplement error_chain's foreign_links. (Ugh, this sucks)
|
||||
#[fail(display = "OpenSSL error: {}", _0)]
|
||||
OpensslError(#[fail(cause)] openssl::error::ErrorStack),
|
||||
|
@ -105,7 +102,6 @@ pub enum ErrorKind {
|
|||
|
||||
error_support::define_error! {
|
||||
ErrorKind {
|
||||
(CryptoError, rc_crypto::Error),
|
||||
(OpensslError, openssl::error::ErrorStack),
|
||||
(Base64Decode, base64::DecodeError),
|
||||
(JsonError, serde_json::Error),
|
||||
|
|
|
@ -3,12 +3,10 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use crate::error::{ErrorKind, Result};
|
||||
use openssl::symm;
|
||||
use rc_crypto::{
|
||||
digest,
|
||||
hmac::{self, Signature, SigningKey, VerificationKey},
|
||||
rand,
|
||||
};
|
||||
use openssl::hash::MessageDigest;
|
||||
use openssl::pkey::PKey;
|
||||
use openssl::sign::Signer;
|
||||
use openssl::{self, symm};
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub struct KeyBundle {
|
||||
|
@ -36,7 +34,7 @@ impl KeyBundle {
|
|||
|
||||
pub fn new_random() -> Result<KeyBundle> {
|
||||
let mut buffer = [0u8; 64];
|
||||
rand::fill(&mut buffer)?;
|
||||
openssl::rand::rand_bytes(&mut buffer)?;
|
||||
KeyBundle::from_ksync_bytes(&buffer)
|
||||
}
|
||||
|
||||
|
@ -77,9 +75,20 @@ impl KeyBundle {
|
|||
[base64::encode(&self.enc_key), base64::encode(&self.mac_key)]
|
||||
}
|
||||
|
||||
fn hmac(&self, ciphertext: &[u8]) -> Result<Signature> {
|
||||
let key = SigningKey::new(&digest::SHA256, self.hmac_key());
|
||||
Ok(hmac::sign(&key, ciphertext)?)
|
||||
/// 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`!
|
||||
|
@ -87,25 +96,32 @@ impl KeyBundle {
|
|||
Ok(base16::encode_lower(&self.hmac(ciphertext)?))
|
||||
}
|
||||
|
||||
pub fn verify_hmac(&self, expected_hmac: &[u8], ciphertext_base64: &str) -> Result<()> {
|
||||
let key = VerificationKey::new(&digest::SHA256, self.hmac_key());
|
||||
Ok(hmac::verify(
|
||||
&key,
|
||||
ciphertext_base64.as_bytes(),
|
||||
expected_hmac,
|
||||
)?)
|
||||
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<()> {
|
||||
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);
|
||||
}
|
||||
// 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() {
|
||||
log::warn!("Garbage HMAC verification string: contained non base16 characters");
|
||||
return Err(ErrorKind::HmacMismatch.into());
|
||||
return Ok(false);
|
||||
}
|
||||
self.verify_hmac(&decoded_hmac, ciphertext_base64)
|
||||
|
||||
Ok(openssl::memcmp::eq(&decoded_hmac, &computed_hmac))
|
||||
}
|
||||
|
||||
/// Decrypt the provided ciphertext with the given iv, and decodes the
|
||||
|
@ -136,7 +152,7 @@ impl KeyBundle {
|
|||
/// and the generated iv.
|
||||
pub fn encrypt_bytes_rand_iv(&self, cleartext_bytes: &[u8]) -> Result<(Vec<u8>, [u8; 16])> {
|
||||
let mut iv = [0u8; 16];
|
||||
rand::fill(&mut iv)?;
|
||||
openssl::rand::rand_bytes(&mut iv)?;
|
||||
let ciphertext = self.encrypt_bytes_with_iv(cleartext_bytes, &iv)?;
|
||||
Ok((ciphertext, iv))
|
||||
}
|
||||
|
@ -184,7 +200,7 @@ mod test {
|
|||
let ciphertext_base64 = CIPHERTEXT_B64_PIECES.join("");
|
||||
assert!(key_bundle
|
||||
.verify_hmac_string(HMAC_B16, &ciphertext_base64)
|
||||
.is_ok());
|
||||
.unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
Загрузка…
Ссылка в новой задаче