Skip to content

Instantly share code, notes, and snippets.

@jedisct1
Created June 14, 2023 13:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jedisct1/6bf24e496e0ee8d31073cbdcf5250fb6 to your computer and use it in GitHub Desktop.
Save jedisct1/6bf24e496e0ee8d31073cbdcf5250fb6 to your computer and use it in GitHub Desktop.
diff --git a/Cargo.toml b/Cargo.toml
index 5f580b6..d0f55d0 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,7 +1,12 @@
[package]
name = "prio"
version = "0.12.2"
-authors = ["Josh Aas <jaas@kflag.net>", "Tim Geoghegan <timg@letsencrypt.org>", "Christopher Patton <cpatton@cloudflare.com", "Karl Tarbe <tarbe@apple.com>"]
+authors = [
+ "Josh Aas <jaas@kflag.net>",
+ "Tim Geoghegan <timg@letsencrypt.org>",
+ "Christopher Patton <cpatton@cloudflare.com",
+ "Karl Tarbe <tarbe@apple.com>",
+]
edition = "2021"
exclude = ["/supply-chain"]
description = "Implementation of the Prio aggregation system core: https://crypto.stanford.edu/prio/"
@@ -35,7 +40,9 @@ rayon = { version = "1.7.0", optional = true }
# dependencies required if feature "prio2" is enabled
aes-gcm = { version = "^0.10", optional = true }
-ring = { version = "0.16.20", optional = true }
+p256 = { version = "0.13.2", features = ["ecdh", "std"], optional = true }
+hmac-sha256 = { version = "1.1.6", optional = true }
+generic-array = { version = "0.14.4", optional = true }
[dev-dependencies]
assert_matches = "1.5.0"
@@ -56,11 +63,17 @@ zipf = "7.0.0"
hex-literal = "0.4.1"
[features]
-default = ["crypto-dependencies"]
+default = ["crypto-dependencies", "prio2"]
experimental = ["bitvec", "fiat-crypto", "fixed"]
test-util = ["rand", "serde_json"]
multithreaded = ["rayon"]
-prio2 = ["crypto-dependencies", "aes-gcm", "ring"]
+prio2 = [
+ "crypto-dependencies",
+ "aes-gcm",
+ "p256",
+ "hmac-sha256",
+ "generic-array",
+]
crypto-dependencies = ["aes", "ctr", "cmac"]
[workspace]
diff --git a/src/encrypt.rs b/src/encrypt.rs
index e69134d..45e0d41 100644
--- a/src/encrypt.rs
+++ b/src/encrypt.rs
@@ -7,7 +7,8 @@ use crate::prng::PrngError;
use aes::{cipher::typenum::U16, Aes128};
use aes_gcm::{aead::generic_array::GenericArray, AeadInPlace, AesGcm, KeyInit};
use base64::{engine::Engine, prelude::BASE64_STANDARD};
-use ring::agreement;
+use hmac_sha256::Hash as Sha256;
+use p256::ecdh::EphemeralSecret;
type Aes128Gcm = AesGcm<Aes128, U16>;
@@ -82,20 +83,14 @@ impl PrivateKey {
/// This uses ECIES with X9.63 key derivation function and AES-GCM for the
/// symmetic encryption and MAC.
pub fn encrypt_share(share: &[u8], key: &PublicKey) -> Result<Vec<u8>, EncryptError> {
- let rng = ring::rand::SystemRandom::new();
- let ephemeral_priv = agreement::EphemeralPrivateKey::generate(&agreement::ECDH_P256, &rng)
- .map_err(|_| EncryptError::KeyAgreement)?;
- let peer_public = agreement::UnparsedPublicKey::new(&agreement::ECDH_P256, &key.0);
- let ephemeral_pub = ephemeral_priv
- .compute_public_key()
- .map_err(|_| EncryptError::KeyAgreement)?;
-
- let symmetric_key_bytes = agreement::agree_ephemeral(
- ephemeral_priv,
- &peer_public,
- EncryptError::KeyAgreement,
- |material| Ok(x963_kdf(material, ephemeral_pub.as_ref())),
- )?;
+ let mut rng = rand::thread_rng();
+ let ephemeral_priv = EphemeralSecret::random(&mut rng);
+ let peer_public =
+ p256::PublicKey::from_sec1_bytes(&key.0).map_err(|_| EncryptError::KeyAgreement)?;
+ let ephemeral_pub: p256::elliptic_curve::PublicKey<p256::NistP256> =
+ ephemeral_priv.public_key();
+ let symmetric_key_bytes = ephemeral_priv.diffie_hellman(&peer_public);
+ let symmetric_key_bytes = symmetric_key_bytes.raw_secret_bytes().as_slice();
let in_out = share.to_owned();
let encrypted = encrypt_aes_gcm(
@@ -103,9 +98,9 @@ pub fn encrypt_share(share: &[u8], key: &PublicKey) -> Result<Vec<u8>, EncryptEr
&symmetric_key_bytes[16..],
in_out,
)?;
-
- let mut output = Vec::with_capacity(encrypted.len() + ephemeral_pub.as_ref().len());
- output.extend_from_slice(ephemeral_pub.as_ref());
+ let ephemeral_pub_bytes = ephemeral_pub.to_sec1_bytes();
+ let mut output = Vec::with_capacity(encrypted.len() + ephemeral_pub_bytes.len());
+ output.extend_from_slice(&ephemeral_pub_bytes);
output.extend_from_slice(&encrypted);
Ok(output)
@@ -121,23 +116,16 @@ pub fn decrypt_share(share: &[u8], key: &PrivateKey) -> Result<Vec<u8>, EncryptE
}
let empheral_pub_bytes: &[u8] = &share[0..PUBLICKEY_LENGTH];
- let ephemeral_pub =
- agreement::UnparsedPublicKey::new(&agreement::ECDH_P256, empheral_pub_bytes);
+ let ephemeral_pub = p256::PublicKey::from_sec1_bytes(empheral_pub_bytes)
+ .map_err(|_| EncryptError::Decryption)?;
- let fake_rng = ring::test::rand::FixedSliceRandom {
- // private key consists of the public key + private scalar
- bytes: &key.0[PUBLICKEY_LENGTH..],
- };
-
- let private_key = agreement::EphemeralPrivateKey::generate(&agreement::ECDH_P256, &fake_rng)
- .map_err(|_| EncryptError::KeyAgreement)?;
-
- let symmetric_key_bytes = agreement::agree_ephemeral(
- private_key,
- &ephemeral_pub,
- EncryptError::KeyAgreement,
- |material| Ok(x963_kdf(material, empheral_pub_bytes)),
- )?;
+ let fk = p256::SecretKey::from_bytes(generic_array::GenericArray::from_slice(
+ &key.0[PUBLICKEY_LENGTH..],
+ ))
+ .map_err(|_| EncryptError::Decryption)?;
+ let private_key: EphemeralSecret = unsafe { std::mem::transmute(fk) }; // Unfortunately, no public API exists to do this
+ let symmetric_key_bytes = private_key.diffie_hellman(&ephemeral_pub);
+ let symmetric_key_bytes = symmetric_key_bytes.raw_secret_bytes();
// in_out is the AES-GCM ciphertext+tag, wihtout the ephemeral EC pubkey
let in_out = share[PUBLICKEY_LENGTH..].to_owned();
@@ -149,13 +137,12 @@ pub fn decrypt_share(share: &[u8], key: &PrivateKey) -> Result<Vec<u8>, EncryptE
}
fn x963_kdf(z: &[u8], shared_info: &[u8]) -> [u8; 32] {
- let mut hasher = ring::digest::Context::new(&ring::digest::SHA256);
+ let mut hasher = Sha256::new();
hasher.update(z);
hasher.update(&1u32.to_be_bytes());
hasher.update(shared_info);
- let digest = hasher.finish();
- // unwrap never fails because SHA256 output len is 32
- digest.as_ref().try_into().unwrap()
+ let digest = hasher.finalize();
+ digest
}
fn decrypt_aes_gcm(key: &[u8], nonce: &[u8], mut data: Vec<u8>) -> Result<Vec<u8>, EncryptError> {
diff --git a/src/prng.rs b/src/prng.rs
index bc98593..19edd01 100644
--- a/src/prng.rs
+++ b/src/prng.rs
@@ -173,7 +173,7 @@ mod tests {
let random_bytes = FieldPrio2::slice_into_byte_vec(&random_data);
- let digest = ring::digest::digest(&ring::digest::SHA256, &random_bytes);
+ let digest = hmac_sha256::Hash::hash(&random_bytes);
assert_eq!(BASE64_STANDARD.encode(digest), hash_base64);
}
diff --git a/src/vdaf/prio2.rs b/src/vdaf/prio2.rs
index 3a154c2..8d8edf2 100644
--- a/src/vdaf/prio2.rs
+++ b/src/vdaf/prio2.rs
@@ -16,7 +16,7 @@ use crate::{
ShareDecodingParameter, Vdaf, VdafError,
},
};
-use ring::hmac;
+use hmac_sha256::HMAC;
use std::{
convert::{TryFrom, TryInto},
io::Cursor,
@@ -218,8 +218,8 @@ impl Aggregator<32, 16> for Prio2 {
// distributed to the Aggregators after they receive their input shares. In a VDAF, shared
// randomness is derived from a nonce selected by the client. For Prio2 we compute the
// query using HMAC-SHA256 evaluated over the nonce.
- let hmac_key = hmac::Key::new(hmac::HMAC_SHA256, agg_key);
- let hmac_tag = hmac::sign(&hmac_key, nonce);
+ let hmac_tag = HMAC::mac(nonce, agg_key);
+
let query_rand = Prng::from_prio2_seed(hmac_tag.as_ref().try_into().unwrap())
.next()
.unwrap();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment