summaryrefslogtreecommitdiffstats
path: root/third_party/rust/rc_crypto/src
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /third_party/rust/rc_crypto/src
parentInitial commit. (diff)
downloadfirefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz
firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/rc_crypto/src')
-rw-r--r--third_party/rust/rc_crypto/src/aead.rs317
-rw-r--r--third_party/rust/rc_crypto/src/aead/aes_cbc.rs265
-rw-r--r--third_party/rust/rc_crypto/src/aead/aes_gcm.rs146
-rw-r--r--third_party/rust/rc_crypto/src/agreement.rs414
-rw-r--r--third_party/rust/rc_crypto/src/constant_time.rs45
-rw-r--r--third_party/rust/rc_crypto/src/digest.rs75
-rw-r--r--third_party/rust/rc_crypto/src/ece_crypto.rs437
-rw-r--r--third_party/rust/rc_crypto/src/error.rs20
-rw-r--r--third_party/rust/rc_crypto/src/hawk_crypto.rs157
-rw-r--r--third_party/rust/rc_crypto/src/hkdf.rs127
-rw-r--r--third_party/rust/rc_crypto/src/hmac.rs199
-rw-r--r--third_party/rust/rc_crypto/src/lib.rs68
-rw-r--r--third_party/rust/rc_crypto/src/pbkdf2.rs196
-rw-r--r--third_party/rust/rc_crypto/src/rand.rs70
-rw-r--r--third_party/rust/rc_crypto/src/signature.rs111
15 files changed, 2647 insertions, 0 deletions
diff --git a/third_party/rust/rc_crypto/src/aead.rs b/third_party/rust/rc_crypto/src/aead.rs
new file mode 100644
index 0000000000..d0e60e5f61
--- /dev/null
+++ b/third_party/rust/rc_crypto/src/aead.rs
@@ -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());
+ }
+ }
+}
diff --git a/third_party/rust/rc_crypto/src/aead/aes_cbc.rs b/third_party/rust/rc_crypto/src/aead/aes_cbc.rs
new file mode 100644
index 0000000000..93e3d8a1d7
--- /dev/null
+++ b/third_party/rust/rc_crypto/src/aead/aes_cbc.rs
@@ -0,0 +1,265 @@
+/* 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);
+ hmac::verify(
+ &hmac_key,
+ base64::encode(ciphertext).as_bytes(),
+ hmac_signature,
+ )?;
+ // 2. Decryption.
+ Ok(aes_cbc(
+ aes_key,
+ nonce,
+ aad,
+ ciphertext,
+ aead::Direction::Opening,
+ )?)
+}
+
+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"),
+ }
+ }
+}
diff --git a/third_party/rust/rc_crypto/src/aead/aes_gcm.rs b/third_party/rust/rc_crypto/src/aead/aes_gcm.rs
new file mode 100644
index 0000000000..8bcdbe6803
--- /dev/null
+++ b/third_party/rust/rc_crypto/src/aead/aes_gcm.rs
@@ -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);
+ }
+}
diff --git a/third_party/rust/rc_crypto/src/agreement.rs b/third_party/rust/rc_crypto/src/agreement.rs
new file mode 100644
index 0000000000..ae609b9c9c
--- /dev/null
+++ b/third_party/rust/rc_crypto/src/agreement.rs
@@ -0,0 +1,414 @@
+/* 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};
+
+pub type EphemeralKeyPair = KeyPair<Ephemeral>;
+
+/// 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,
+};
+
+pub static ECDH_P384: Algorithm = Algorithm {
+ curve_id: ec::Curve::P384,
+};
+
+/// 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
+ }
+}
+
+/// An unparsed public key for key agreement.
+pub struct UnparsedPublicKey<'a> {
+ alg: &'static Algorithm,
+ bytes: &'a [u8],
+}
+
+impl<'a> UnparsedPublicKey<'a> {
+ pub fn new(algorithm: &'static Algorithm, bytes: &'a [u8]) -> Self {
+ Self {
+ alg: algorithm,
+ bytes,
+ }
+ }
+
+ pub fn algorithm(&self) -> &'static Algorithm {
+ self.alg
+ }
+
+ pub fn bytes(&self) -> &'a [u8] {
+ &self.bytes
+ }
+}
+
+/// 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: &UnparsedPublicKey<'_>) -> Result<InputKeyMaterial> {
+ agree_(&self.wrapped, self.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: &UnparsedPublicKey<'_>,
+ ) -> Result<InputKeyMaterial> {
+ agree_(&self.wrapped, self.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,
+ Curve::P384 => &ECDH_P384,
+ };
+ 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: &UnparsedPublicKey<'_>,
+) -> Result<InputKeyMaterial> {
+ let alg = &my_alg;
+ if peer_public_key.algorithm() != *alg {
+ return Err(ErrorKind::InternalError.into());
+ }
+ let pub_key = ec::PublicKey::from_bytes(my_private_key.curve(), peer_public_key.bytes())?;
+ 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_raw = base64::decode_config(PUB_KEY_1_B64, base64::URL_SAFE_NO_PAD).unwrap();
+ let peer_pub_key = UnparsedPublicKey::new(&ECDH_P256, &pub_key_raw);
+ let prv_key = load_priv_key_2();
+ let ikm = prv_key.agree_static(&peer_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 their_pub_key_raw = their_pub_key.to_bytes().unwrap();
+ let peer_public_key_1 = UnparsedPublicKey::new(&ECDH_P256, &their_pub_key_raw);
+ let ikm_1 = our_prv_key.agree(&peer_public_key_1).unwrap();
+ let secret_1 = ikm_1
+ .derive(|z| -> Result<Vec<u8>> { Ok(z.to_vec()) })
+ .unwrap();
+ let our_pub_key_raw = our_pub_key.to_bytes().unwrap();
+ let peer_public_key_2 = UnparsedPublicKey::new(&ECDH_P256, &our_pub_key_raw);
+ let ikm_2 = their_prv_key.agree(&peer_public_key_2).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(&UnparsedPublicKey::new(&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(&UnparsedPublicKey::new(&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(&UnparsedPublicKey::new(&ECDH_P256, &invalid_pub_key))
+ .is_err());
+
+ let mut invalid_pub_key = [0u8; 65];
+ assert!(prv_key
+ .agree_static(&UnparsedPublicKey::new(&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(&UnparsedPublicKey::new(&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(&UnparsedPublicKey::new(&ECDH_P256, &invalid_pub_key))
+ .is_err());
+ }
+}
diff --git a/third_party/rust/rc_crypto/src/constant_time.rs b/third_party/rust/rc_crypto/src/constant_time.rs
new file mode 100644
index 0000000000..64dfe568b8
--- /dev/null
+++ b/third_party/rust/rc_crypto/src/constant_time.rs
@@ -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());
+ }
+}
diff --git a/third_party/rust/rc_crypto/src/digest.rs b/third_party/rust/rc_crypto/src/digest.rs
new file mode 100644
index 0000000000..4dc451a55c
--- /dev/null
+++ b/third_party/rust/rc_crypto/src/digest.rs
@@ -0,0 +1,75 @@
+/* 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,
+ })
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ 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());
+ }
+}
diff --git a/third_party/rust/rc_crypto/src/ece_crypto.rs b/third_party/rust/rc_crypto/src/ece_crypto.rs
new file mode 100644
index 0000000000..44cf59d231
--- /dev/null
+++ b/third_party/rust/rc_crypto/src/ece_crypto.rs
@@ -0,0 +1,437 @@
+/* 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, UnparsedPublicKey},
+ digest, hkdf, hmac, rand,
+};
+use ece::crypto::{Cryptographer, EcKeyComponents, LocalKeyPair, RemotePublicKey};
+
+impl From<crate::Error> for ece::Error {
+ fn from(_: crate::Error) -> Self {
+ ece::Error::CryptoError
+ }
+}
+
+pub struct RcCryptoLocalKeyPair {
+ wrapped: agreement::KeyPair<agreement::Static>,
+}
+// SECKEYPrivateKeyStr and SECKEYPublicKeyStr are Sync.
+unsafe impl Sync for RcCryptoLocalKeyPair {}
+
+impl RcCryptoLocalKeyPair {
+ pub fn from_raw_components(components: &EcKeyComponents) -> Result<Self, ece::Error> {
+ 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(RcCryptoLocalKeyPair { wrapped })
+ }
+
+ pub fn generate_random() -> Result<Self, ece::Error> {
+ let wrapped = agreement::KeyPair::<agreement::Static>::generate(&agreement::ECDH_P256)?;
+ Ok(RcCryptoLocalKeyPair { wrapped })
+ }
+
+ fn agree(&self, peer: &RcCryptoRemotePublicKey) -> Result<Vec<u8>, ece::Error> {
+ let peer_public_key_raw_bytes = &peer.as_raw()?;
+ let peer_public_key =
+ UnparsedPublicKey::new(&agreement::ECDH_P256, &peer_public_key_raw_bytes);
+ self.wrapped
+ .private_key()
+ .agree_static(&peer_public_key)?
+ .derive(|z| Ok(z.to_vec()))
+ }
+}
+
+impl LocalKeyPair for RcCryptoLocalKeyPair {
+ fn raw_components(&self) -> Result<EcKeyComponents, ece::Error> {
+ let ec_key = self.wrapped.private_key().export()?;
+ Ok(EcKeyComponents::new(
+ ec_key.private_key(),
+ ec_key.public_key(),
+ ))
+ }
+
+ fn pub_as_raw(&self) -> Result<Vec<u8>, ece::Error> {
+ let bytes = self.wrapped.public_key().to_bytes()?;
+ Ok(bytes.to_vec())
+ }
+
+ fn as_any(&self) -> &dyn std::any::Any {
+ self
+ }
+}
+pub struct RcCryptoRemotePublicKey {
+ raw: Vec<u8>,
+}
+
+impl RcCryptoRemotePublicKey {
+ pub fn from_raw(bytes: &[u8]) -> Result<RcCryptoRemotePublicKey, ece::Error> {
+ Ok(RcCryptoRemotePublicKey {
+ raw: bytes.to_owned(),
+ })
+ }
+}
+
+impl RemotePublicKey for RcCryptoRemotePublicKey {
+ fn as_raw(&self) -> Result<Vec<u8>, ece::Error> {
+ Ok(self.raw.to_vec())
+ }
+
+ fn as_any(&self) -> &dyn std::any::Any {
+ self
+ }
+}
+
+pub(crate) struct RcCryptoCryptographer;
+
+impl Cryptographer for RcCryptoCryptographer {
+ fn generate_ephemeral_keypair(&self) -> Result<Box<dyn LocalKeyPair>, ece::Error> {
+ Ok(Box::new(RcCryptoLocalKeyPair::generate_random()?))
+ }
+
+ fn import_key_pair(
+ &self,
+ components: &EcKeyComponents,
+ ) -> Result<Box<dyn LocalKeyPair>, ece::Error> {
+ Ok(Box::new(RcCryptoLocalKeyPair::from_raw_components(
+ components,
+ )?))
+ }
+
+ fn import_public_key(&self, raw: &[u8]) -> Result<Box<dyn RemotePublicKey>, ece::Error> {
+ Ok(Box::new(RcCryptoRemotePublicKey::from_raw(raw)?))
+ }
+
+ fn compute_ecdh_secret(
+ &self,
+ remote: &dyn RemotePublicKey,
+ local: &dyn LocalKeyPair,
+ ) -> Result<Vec<u8>, ece::Error> {
+ let local_any = local.as_any();
+ let local = local_any.downcast_ref::<RcCryptoLocalKeyPair>().unwrap();
+ let remote_any = remote.as_any();
+ let remote = remote_any
+ .downcast_ref::<RcCryptoRemotePublicKey>()
+ .unwrap();
+ Ok(local.agree(&remote)?)
+ }
+
+ fn hkdf_sha256(
+ &self,
+ salt: &[u8],
+ secret: &[u8],
+ info: &[u8],
+ len: usize,
+ ) -> Result<Vec<u8>, ece::Error> {
+ 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(
+ &self,
+ key: &[u8],
+ iv: &[u8],
+ data: &[u8],
+ ) -> Result<Vec<u8>, ece::Error> {
+ let key = aead::SealingKey::new(&aead::AES_128_GCM, key)?;
+ let nonce = aead::Nonce::try_assume_unique_for_key(&aead::AES_128_GCM, iv)?;
+ Ok(aead::seal(&key, nonce, aead::Aad::empty(), data)?)
+ }
+
+ fn aes_gcm_128_decrypt(
+ &self,
+ key: &[u8],
+ iv: &[u8],
+ ciphertext_and_tag: &[u8],
+ ) -> Result<Vec<u8>, ece::Error> {
+ let key = aead::OpeningKey::new(&aead::AES_128_GCM, key)?;
+ let nonce = aead::Nonce::try_assume_unique_for_key(&aead::AES_128_GCM, iv)?;
+ Ok(aead::open(
+ &key,
+ nonce,
+ aead::Aad::empty(),
+ &ciphertext_and_tag,
+ )?)
+ }
+
+ fn random_bytes(&self, dest: &mut [u8]) -> Result<(), ece::Error> {
+ Ok(rand::fill(dest)?)
+ }
+}
+
+// Please call `rc_crypto::ensure_initialized()` instead of calling
+// this function directly.
+pub(crate) fn init() {
+ ece::crypto::set_cryptographer(&crate::ece_crypto::RcCryptoCryptographer)
+ .expect("Failed to initialize `ece` cryptographer!")
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use ece::*;
+
+ // Copy-pasta from the tests in the ECE crate.
+ fn generate_keys() -> Result<(Box<dyn LocalKeyPair>, Box<dyn LocalKeyPair>)> {
+ let local_key = RcCryptoLocalKeyPair::generate_random()?;
+ let remote_key = RcCryptoLocalKeyPair::generate_random()?;
+ Ok((Box::new(local_key), Box::new(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 = RcCryptoLocalKeyPair::from_raw_components(&ec_key)?;
+ let remote_pub_key = hex::decode(remote_pub_key).unwrap();
+ let remote_pub_key = RcCryptoRemotePublicKey::from_raw(&remote_pub_key).unwrap();
+ 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 = Aes128GcmEceWebPush::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() {
+ crate::ensure_initialized();
+ 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];
+ RcCryptoCryptographer
+ .random_bytes(&mut auth_secret)
+ .unwrap();
+ let remote_public = RcCryptoCryptographer
+ .import_public_key(&remote_key.pub_as_raw().unwrap())
+ .unwrap();
+ let params = WebPushParams::default();
+ let ciphertext = Aes128GcmEceWebPush::encrypt_with_keys(
+ &*local_key,
+ &*remote_public,
+ &auth_secret,
+ plaintext,
+ params,
+ )
+ .unwrap();
+ let decrypted =
+ Aes128GcmEceWebPush::decrypt(&*remote_key, &auth_secret, &ciphertext).unwrap();
+ assert_eq!(decrypted, plaintext.to_vec());
+ }
+
+ #[test]
+ fn test_conv_fn() -> Result<()> {
+ crate::ensure_initialized();
+ 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];
+ RcCryptoCryptographer.random_bytes(&mut salt)?;
+ let encoded = encrypt(&local_key.pub_as_raw()?, &auth, &salt, plaintext).unwrap();
+ let decoded = decrypt(&local_key.raw_components()?, &auth, &encoded)?;
+ assert_eq!(decoded, plaintext.to_vec());
+ Ok(())
+ }
+
+ #[test]
+ fn try_encrypt_ietf_rfc() {
+ crate::ensure_initialized();
+ 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() {
+ crate::ensure_initialized();
+ 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() {
+ crate::ensure_initialized();
+ // 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() {
+ crate::ensure_initialized();
+ 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() {
+ crate::ensure_initialized();
+ let plaintext = try_decrypt(
+ "67004a4ea820deed8e49db5e9480e63d3ea3cce1ae8e1a60609713d527d001ef",
+ "04014e8f14b92da07ce083b93f96367e87b217a47f7ef2ee93a9d343aa063e575a9f30d59c690c6a39b3fc815b150ca7dd149601741337b53507a51f41b173a721",
+ "95f17570e508ef6a2b2ad1b4f5cade33",
+ "fb2883cec1c4fcadd6d1371f6ea491e00000003141042d441ee7f9ff6a0329a64927d0524fdbe7b22c6fb65e10ab4fdc038f94420a0ca3fa28dad36c84ec91a162eae078faad2c1ced78de8113e19602b20e894f4976b973e2fcf682fa0c8ccd9af3d5bff1ede16fad5a31ce19d38b5e1fe1f78a4fad842bbc10254c2c6cdd96a2b55284d972c53cad8c3bacb10f5f57eb0d4a4333b604102ba117cae29108fbd9f629a8ba6960dd01945b39ed37ba706c434a10fd2bd2094ff9249bcdad45135f5fe45fcd38071f8b2d3941afda439810d77aacaf7ce50b54325bf58c9503337d073785a323dfa343"
+ ).unwrap();
+ assert_eq!(plaintext, "Hello, world");
+ }
+
+ #[test]
+ fn test_decrypt_ietf_rfc() {
+ crate::ensure_initialized();
+ 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() {
+ crate::ensure_initialized();
+ let plaintext = try_decrypt(
+ "27433fab8970b3cb5284b61183efb46286562cd2a7330d8cae960911a5571d0c",
+ "04515d4326355652399da24b2be9241e633b5cf14faf0cf3a6fd60317b954c0a2f4848548004b27b0cf7480bc810c6bec03a8fb79c8ea00fc8b05e00f8834563ef",
+ "d65a04df95f2db5e604839f717dcde79",
+ "7caebdbc20938ee340a946f1bd4f68f100000012410437cfdb5223d9f95eaa02f6ed940ff22eaf05b3622e949dc3ce9f335e6ef9b26aeaacca0f74080a8b364592f2ccc6d5eddd43004b70b91887d144d9fa93f16c3bc7ea68f4fd547a94eca84b16e138a6080177"
+ ).unwrap();
+ assert_eq!(plaintext, "1");
+ }
+
+ #[test]
+ fn test_decrypt_missing_header_block() {
+ crate::ensure_initialized();
+ let err = try_decrypt(
+ "1be83f38332ef09681faf3f307b1ff2e10cab78cc7cdab683ac0ee92ac3f6ee1",
+ "04dba991ca215343f36bdd3e857cafde3d18bf57f1835b2833bad414f0884162051ac96a0b24490037d07cf528e4e18e100a1a64eb744748544bf1e220dabacf2c",
+ "3471bb98481e02533bf39542bcf3dba4",
+ "45b74d2b69be9b074de3b35aa87e7c15611d",
+ )
+ .unwrap_err();
+ match err {
+ Error::HeaderTooShort => {}
+ _ => panic!("Unexpected error type!"),
+ };
+ }
+
+ #[test]
+ fn test_decrypt_truncated_sender_key() {
+ crate::ensure_initialized();
+ let err = try_decrypt(
+ "ce88e8e0b3057a4752eb4c8fa931eb621c302da5ad03b81af459cf6735560cae",
+ "04a325d99084c40de0ce722a042c448d94a32691721ca79e3cf745e78c69886194b02cea19224176795a9d4dbbb2073af2ccd6fa6f0a4c7c4968556be502a3ba81",
+ "5c31e0d96d9a139899ac0969d359f740",
+ "de5b696b87f1a15cb6adebdd79d6f99e000000120100b6bc1826c37c9f73dd6b4859c2b505181952",
+ )
+ .unwrap_err();
+ match err {
+ Error::InvalidKeyLength => {}
+ _ => panic!("Unexpected error type!"),
+ };
+ }
+
+ #[test]
+ fn test_decrypt_truncated_auth_secret() {
+ crate::ensure_initialized();
+ let err = try_decrypt(
+ "60c7636a517de7039a0ac2d0e3064400794c78e7e049398129a227cee0f9a801",
+ "04fdd04128a85c05896d7f81fe118bdcb887b9f3c1ff4183adc4c824d128607300e986b2dfb5a610e5af43e408a00730584f93e3dfddfc44737d5f08fb2d6f8916",
+ "355a38cd6d9bef15990e2d3308dbd600",
+ "8115f4988b8c392a7bacb43c8f1ac5650000001241041994483c541e9bc39a6af03ff713aa7745c284e138a42a2435b797b20c4b698cf5118b4f8555317c190eabebfab749c164d3f6bdebe0d441719131a357d8890a13c4dbd4b16ff3dd5a83f7c91ad6e040ac42730a7f0b3cd3245e9f8d6ff31c751d410cfd"
+ ).unwrap_err();
+ match err {
+ Error::CryptoError => {}
+ _ => panic!("Unexpected error type!"),
+ };
+ }
+
+ #[test]
+ fn test_decrypt_early_final_record() {
+ crate::ensure_initialized();
+ let err = try_decrypt(
+ "5dda1d918bc407ba3cda12cb8014d49aa7e0269002820304466bc80034ca9240",
+ "04c95c6520dad11e8f6a1bf8031a40c2a4ee1045c1903be06a1dfa7f829cceb2de02481ae6bd0476121b12c5532d0b231788077efa0683a5bfe0d62339b251cb35",
+ "40c241fde4269ee1e6d725592d982718",
+ "dbe215507d1ad3d2eaeabeae6e874d8f0000001241047bc4343f34a8348cdc4e462ffc7c40aa6a8c61a739c4c41d45125505f70e9fc5f9efa86852dd488dcf8e8ea2cafb75e07abd5ee7c9d5c038bafef079571b0bda294411ce98c76dd031c0e580577a4980a375e45ed30429be0e2ee9da7e6df8696d01b8ec"
+ ).unwrap_err();
+ match err {
+ Error::DecryptPadding => {}
+ _ => panic!("Unexpected error type!"),
+ };
+ }
+}
diff --git a/third_party/rust/rc_crypto/src/error.rs b/third_party/rust/rc_crypto/src/error.rs
new file mode 100644
index 0000000000..0edc97bef4
--- /dev/null
+++ b/third_party/rust/rc_crypto/src/error.rs
@@ -0,0 +1,20 @@
+/* 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/. */
+
+#[derive(Debug, thiserror::Error)]
+pub enum ErrorKind {
+ #[error("NSS error: {0}")]
+ NSSError(#[from] nss::Error),
+ #[error("Internal crypto error")]
+ InternalError,
+ #[error("Conversion error: {0}")]
+ ConversionError(#[from] std::num::TryFromIntError),
+}
+
+error_support::define_error! {
+ ErrorKind {
+ (ConversionError, std::num::TryFromIntError),
+ (NSSError, nss::Error),
+ }
+}
diff --git a/third_party/rust/rc_crypto/src/hawk_crypto.rs b/third_party/rust/rc_crypto/src/hawk_crypto.rs
new file mode 100644
index 0000000000..92f4725ca8
--- /dev/null
+++ b/third_party/rust/rc_crypto/src/hawk_crypto.rs
@@ -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
+ ]
+ );
+ }
+}
diff --git a/third_party/rust/rc_crypto/src/hkdf.rs b/third_party/rust/rc_crypto/src/hkdf.rs
new file mode 100644
index 0000000000..a87a652d28
--- /dev/null
+++ b/third_party/rust/rc_crypto/src/hkdf.rs
@@ -0,0 +1,127 @@
+/* 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;
+
+ #[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; 8160 + 1]; // RFC maximum (hashlen * 255) + 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());
+ }
+}
diff --git a/third_party/rust/rc_crypto/src/hmac.rs b/third_party/rust/rc_crypto/src/hmac.rs
new file mode 100644
index 0000000000..cc3decb128
--- /dev/null
+++ b/third_party/rust/rc_crypto/src/hmac.rs
@@ -0,0 +1,199 @@
+/* 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,
+ }))
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ 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());
+ }
+ }
+}
diff --git a/third_party/rust/rc_crypto/src/lib.rs b/third_party/rust/rc_crypto/src/lib.rs
new file mode 100644
index 0000000000..923817423f
--- /dev/null
+++ b/third_party/rust/rc_crypto/src/lib.rs
@@ -0,0 +1,68 @@
+/* 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_crypto;
+mod error;
+#[cfg(feature = "hawk")]
+mod hawk_crypto;
+pub mod hkdf;
+pub mod hmac;
+pub mod pbkdf2;
+pub mod rand;
+pub mod signature;
+
+// 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;
+
+// Expose `ece` if the ece feature is on. This avoids consumers needing to
+// configure this separately, which is more or less trivial to do incorrectly.
+#[cfg(feature = "ece")]
+pub use ece;
+
+pub use crate::error::{Error, ErrorKind, Result};
+
+/// Only required to be called if you intend to use this library in conjunction
+/// with the `hawk` or the `ece` crate.
+pub fn ensure_initialized() {
+ nss::ensure_initialized();
+ #[cfg(any(feature = "hawk", feature = "ece"))]
+ {
+ static INIT_ONCE: std::sync::Once = std::sync::Once::new();
+ INIT_ONCE.call_once(|| {
+ #[cfg(feature = "hawk")]
+ crate::hawk_crypto::init();
+ #[cfg(feature = "ece")]
+ crate::ece_crypto::init();
+ });
+ }
+}
diff --git a/third_party/rust/rc_crypto/src/pbkdf2.rs b/third_party/rust/rc_crypto/src/pbkdf2.rs
new file mode 100644
index 0000000000..37d8ea8497
--- /dev/null
+++ b/third_party/rust/rc_crypto/src/pbkdf2.rs
@@ -0,0 +1,196 @@
+/* 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::pbkdf2::pbkdf2_key_derive;
+pub use nss::pbkdf2::HashAlgorithm;
+/// Extend passwords using pbkdf2, based on the following [rfc](https://www.ietf.org/rfc/rfc2898.txt) it runs the NSS implementation
+/// # Arguments
+///
+/// * `passphrase` - The password to stretch
+/// * `salt` - A salt to use in the generation process
+/// * `iterations` - The number of iterations the hashing algorithm will run on each section of the key
+/// * `hash_algorithm` - The hash algorithm to use
+/// * `out` - The slice the algorithm will populate
+///
+/// # Examples
+///
+/// ```
+/// use rc_crypto::pbkdf2;
+/// let password = b"password";
+/// let salt = b"salt";
+/// let mut out = vec![0u8; 32];
+/// let iterations = 2; // Real code should have a MUCH higher number of iterations (Think 1000+)
+/// pbkdf2::derive(password, salt, iterations, pbkdf2::HashAlgorithm::SHA256, &mut out).unwrap(); // Oh oh should handle the error!
+/// assert_eq!(hex::encode(out), "ae4d0c95af6b46d32d0adff928f06dd02a303f8ef3c251dfd6e2d85a95474c43");
+//
+///```
+///
+/// # Errors
+///
+/// Could possibly return an error if the HMAC algorithm fails, or if the NSS algorithm returns an error
+pub fn derive(
+ passphrase: &[u8],
+ salt: &[u8],
+ iterations: u32,
+ hash_algorithm: HashAlgorithm,
+ out: &mut [u8],
+) -> Result<()> {
+ pbkdf2_key_derive(passphrase, salt, iterations, hash_algorithm, out)?;
+ Ok(())
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ #[test]
+ fn test_generate_correct_out() {
+ let expected = "120fb6cffcf8b32c43e7225256c4f837a86548c92ccc35480805987cb70be17b";
+ let mut out = vec![0u8; 32];
+ let password = b"password";
+ let salt = b"salt";
+ derive(password, salt, 1, HashAlgorithm::SHA256, &mut out).unwrap();
+ assert_eq!(expected, hex::encode(out));
+ }
+
+ #[test]
+ fn test_longer_key() {
+ let expected = "120fb6cffcf8b32c43e7225256c4f837a86548c92ccc35480805987cb70be17b4dbf3a2f3dad3377264bb7b8e8330d4efc7451418617dabef683735361cdc18c";
+ let password = b"password";
+ let salt = b"salt";
+ let mut out = vec![0u8; 64];
+ derive(password, salt, 1, HashAlgorithm::SHA256, &mut out).unwrap();
+ assert_eq!(expected, hex::encode(out));
+ }
+
+ #[test]
+ fn test_more_iterations() {
+ let expected = "ae4d0c95af6b46d32d0adff928f06dd02a303f8ef3c251dfd6e2d85a95474c43";
+ let password = b"password";
+ let salt = b"salt";
+ let mut out = vec![0u8; 32];
+ derive(password, salt, 2, HashAlgorithm::SHA256, &mut out).unwrap();
+ assert_eq!(expected, hex::encode(out));
+ }
+
+ #[test]
+ fn test_odd_length() {
+ let expected = "ad35240ac683febfaf3cd49d845473fbbbaa2437f5f82d5a415ae00ac76c6bfccf";
+ let password = b"password";
+ let salt = b"salt";
+ let mut out = vec![0u8; 33];
+ derive(password, salt, 3, HashAlgorithm::SHA256, &mut out).unwrap();
+ assert_eq!(expected, hex::encode(out));
+ }
+
+ #[test]
+ fn test_nulls() {
+ let expected = "e25d526987819f966e324faa4a";
+ let password = b"passw\x00rd";
+ let salt = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
+ let mut out = vec![0u8; 13];
+ derive(password, salt, 5, HashAlgorithm::SHA256, &mut out).unwrap();
+ assert_eq!(expected, hex::encode(out));
+ }
+
+ #[test]
+ fn test_password_null() {
+ let expected = "62384466264daadc4144018c6bd864648272b34da8980d31521ffcce92ae003b";
+ let password = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
+ let salt = b"salt";
+ let mut out = vec![0u8; 32];
+ derive(password, salt, 2, HashAlgorithm::SHA256, &mut out).unwrap();
+ assert_eq!(expected, hex::encode(out));
+ }
+
+ #[test]
+ fn test_empty_password() {
+ let expected = "f135c27993baf98773c5cdb40a5706ce6a345cde61b000a67858650cd6a324d7";
+ let mut out = vec![0u8; 32];
+ let password = b"";
+ let salt = b"salt";
+ derive(password, salt, 1, HashAlgorithm::SHA256, &mut out).unwrap();
+ assert_eq!(expected, hex::encode(out));
+ }
+
+ #[test]
+ fn test_empty_salt() {
+ let expected = "c1232f10f62715fda06ae7c0a2037ca19b33cf103b727ba56d870c11f290a2ab";
+ let mut out = vec![0u8; 32];
+ let password = b"password";
+ let salt = b"";
+ derive(password, salt, 1, HashAlgorithm::SHA256, &mut out).unwrap();
+ assert_eq!(expected, hex::encode(out));
+ }
+
+ #[test]
+ fn test_tiny_out() {
+ let expected = "12";
+ let mut out = vec![0u8; 1];
+ let password = b"password";
+ let salt = b"salt";
+ derive(password, salt, 1, HashAlgorithm::SHA256, &mut out).unwrap();
+ assert_eq!(expected, hex::encode(out));
+ }
+
+ #[test]
+ fn test_rejects_zero_iterations() {
+ let mut out = vec![0u8; 32];
+ let password = b"password";
+ let salt = b"salt";
+ assert!(derive(password, salt, 0, HashAlgorithm::SHA256, &mut out).is_err());
+ }
+
+ #[test]
+ fn test_rejects_empty_out() {
+ let mut out = vec![0u8; 0];
+ let password = b"password";
+ let salt = b"salt";
+ assert!(derive(password, salt, 1, HashAlgorithm::SHA256, &mut out).is_err());
+ }
+
+ #[test]
+ fn test_rejects_gaigantic_salt() {
+ if (std::u32::MAX as usize) < std::usize::MAX {
+ let salt = vec![0; (std::u32::MAX as usize) + 1];
+ let mut out = vec![0u8; 1];
+ let password = b"password";
+ assert!(derive(password, &salt, 1, HashAlgorithm::SHA256, &mut out).is_err());
+ }
+ }
+ #[test]
+ fn test_rejects_gaigantic_password() {
+ if (std::u32::MAX as usize) < std::usize::MAX {
+ let password = vec![0; (std::u32::MAX as usize) + 1];
+ let mut out = vec![0u8; 1];
+ let salt = b"salt";
+ assert!(derive(&password, salt, 1, HashAlgorithm::SHA256, &mut out).is_err());
+ }
+ }
+
+ #[test]
+ fn test_rejects_gaigantic_out() {
+ if (std::u32::MAX as usize) < std::usize::MAX {
+ let password = b"password";
+ let mut out = vec![0; (std::u32::MAX as usize) + 1];
+ let salt = b"salt";
+ assert!(derive(password, salt, 1, HashAlgorithm::SHA256, &mut out).is_err());
+ }
+ }
+
+ #[test]
+ fn test_rejects_gaigantic_iterations() {
+ let password = b"password";
+ let mut out = vec![0; 32];
+ let salt = b"salt";
+ assert!(derive(
+ password,
+ salt,
+ std::u32::MAX,
+ HashAlgorithm::SHA256,
+ &mut out
+ )
+ .is_err());
+ }
+}
diff --git a/third_party/rust/rc_crypto/src/rand.rs b/third_party/rust/rc_crypto/src/rand.rs
new file mode 100644
index 0000000000..4d3dc5df4c
--- /dev/null
+++ b/third_party/rust/rc_crypto/src/rand.rs
@@ -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());
+ }
+}
diff --git a/third_party/rust/rc_crypto/src/signature.rs b/third_party/rust/rc_crypto/src/signature.rs
new file mode 100644
index 0000000000..8ad15fb0f0
--- /dev/null
+++ b/third_party/rust/rc_crypto/src/signature.rs
@@ -0,0 +1,111 @@
+/* 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::Result;
+use nss::{ec::Curve, ec::PublicKey, pbkdf2::HashAlgorithm};
+
+/// A signature verification algorithm.
+pub struct VerificationAlgorithm {
+ curve: Curve,
+ digest_alg: HashAlgorithm,
+}
+
+pub static ECDSA_P256_SHA256: VerificationAlgorithm = VerificationAlgorithm {
+ curve: Curve::P256,
+ digest_alg: HashAlgorithm::SHA256,
+};
+
+pub static ECDSA_P384_SHA384: VerificationAlgorithm = VerificationAlgorithm {
+ curve: Curve::P384,
+ digest_alg: HashAlgorithm::SHA384,
+};
+
+/// An unparsed public key for signature operations.
+pub struct UnparsedPublicKey<'a> {
+ alg: &'static VerificationAlgorithm,
+ bytes: &'a [u8],
+}
+
+impl<'a> UnparsedPublicKey<'a> {
+ pub fn new(algorithm: &'static VerificationAlgorithm, bytes: &'a [u8]) -> Self {
+ Self {
+ alg: algorithm,
+ bytes,
+ }
+ }
+
+ pub fn verify(&self, message: &[u8], signature: &[u8]) -> Result<()> {
+ let pub_key = PublicKey::from_bytes(self.alg.curve, self.bytes)?;
+ Ok(pub_key.verify(message, signature, self.alg.digest_alg)?)
+ }
+
+ pub fn algorithm(&self) -> &'static VerificationAlgorithm {
+ self.alg
+ }
+
+ pub fn bytes(&self) -> &'a [u8] {
+ &self.bytes
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_ecdsa_p384_sha384_verify() {
+ // Test generated with JS DOM's WebCrypto.
+ let pub_key_bytes = base64::decode_config(
+ "BMZj_xHOfLQn5DIEQcYUkyASDWo8O30gWdkWXHHHWN5owKhGWplYHEb4PLf3DkFTg_smprr-ApdULy3NV10x8IZ0EfVaUZdXvTquH1kiw2PxD7fhqiozMXUaSuZI5KBE6w",
+ base64::URL_SAFE_NO_PAD
+ ).unwrap();
+ let message = base64::decode_config(
+ "F9MQDmEEdvOfm-NkCRrXqG-aVA9kq0xqtjvtWLndmmt6bO2gfLE2CVDDLzJYds0n88uz27c5JkzdsLpm5HP3aLFgD8bgnGm-EgdBpm99CRiIm7mAMbb0-NRAyUxeoGmdgJPVQLWFNoHRwzKV2wZ0Bk-Bq7jkeDHmDfnx-CJKVMQ",
+ base64::URL_SAFE_NO_PAD,
+ )
+ .unwrap();
+ let signature = base64::decode_config(
+ "XLZmtJweW4qx0u0l6EpfmB5z-S-CNj4mrl9d7U0MuftdNPhmlNacV4AKR-i4uNn0TUIycU7GsfIjIqxuiL9WdAnfq_KH_SJ95mduqXgWNKlyt8JgMLd4h-jKOllh4erh",
+ base64::URL_SAFE_NO_PAD,
+ )
+ .unwrap();
+ let public_key =
+ crate::signature::UnparsedPublicKey::new(&ECDSA_P384_SHA384, &pub_key_bytes);
+
+ // Failure case: Wrong key algorithm.
+ let public_key_wrong_alg =
+ crate::signature::UnparsedPublicKey::new(&ECDSA_P256_SHA256, &pub_key_bytes);
+ assert!(public_key_wrong_alg.verify(&message, &signature).is_err());
+
+ // Failure case: Add garbage to signature.
+ let mut garbage_signature = signature.clone();
+ garbage_signature.push(42);
+ assert!(public_key.verify(&message, &garbage_signature).is_err());
+
+ // Failure case: Flip a bit in message.
+ let mut garbage_message = message.clone();
+ garbage_message[42] = 42;
+ assert!(public_key.verify(&garbage_message, &signature).is_err());
+
+ // Happy case.
+ assert!(public_key.verify(&message, &signature).is_ok());
+ }
+}