Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 28 additions & 24 deletions src/amortized_tokens/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@ pub struct Server<CS: PrivateCipherSuite> {
}

impl<CS: PrivateCipherSuite> Server<CS> {
fn server_from_seed(seed: &[u8], info: &[u8]) -> Result<VoprfServer<CS>, CreateKeypairError>
where
<CS::Group as Group>::Scalar: Send + Sync,
<CS::Group as Group>::Elem: Send + Sync,
{
VoprfServer::<CS>::new_from_seed(seed, info).map_err(|_| CreateKeypairError::SeedError)
}

/// Create a new server. The new server does not contain any key material.
#[must_use]
pub const fn new() -> Self {
Expand All @@ -45,14 +53,27 @@ impl<CS: PrivateCipherSuite> Server<CS> {
<CS::Group as Group>::Scalar: Send + Sync,
<CS::Group as Group>::Elem: Send + Sync,
{
let mut seed = GenericArray::<_, <CS::Group as Group>::ScalarLen>::default();
OsRng.fill_bytes(&mut seed);
self.create_keypair_internal(key_store, &seed, b"PrivacyPass")
.await
loop {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the other servers have a loop limit, this one does not.

let mut seed = GenericArray::<_, <CS::Group as Group>::ScalarLen>::default();
OsRng.fill_bytes(&mut seed);
let server = Self::server_from_seed(&seed, b"PrivacyPass")?;
let public_key = server.get_public_key();
let truncated_token_key_id =
truncate_token_key_id(&public_key_to_token_key_id::<CS>(&server.get_public_key()));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
truncate_token_key_id(&public_key_to_token_key_id::<CS>(&server.get_public_key()));
truncate_token_key_id(&public_key_to_token_key_id::<CS>(&public_key));


if key_store.get(&truncated_token_key_id).await.is_some() {
continue;
}

key_store.insert(truncated_token_key_id, server).await;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it's critical (like, could just document it), but this is a Time-of-Check-Time-of-Use race condition, and a fix would be to write an atomic insert if absent method on the key_stores.

return Ok(public_key);
}
}

/// Creates a new keypair and inserts it into the key store.
async fn create_keypair_internal<BKS: PrivateKeyStore<CS = CS>>(
/// Creates a new keypair with explicit parameters and inserts it into the
/// key store.
#[cfg(feature = "kat")]
pub async fn create_keypair_with_params<BKS: PrivateKeyStore<CS = CS>>(
&self,
key_store: &BKS,
seed: &[u8],
Expand All @@ -62,31 +83,14 @@ impl<CS: PrivateCipherSuite> Server<CS> {
<CS::Group as Group>::Scalar: Send + Sync,
<CS::Group as Group>::Elem: Send + Sync,
{
let server = VoprfServer::<CS>::new_from_seed(seed, info)
.map_err(|_| CreateKeypairError::SeedError)?;
let server = Self::server_from_seed(seed, info)?;
let public_key = server.get_public_key();
let truncated_token_key_id =
truncate_token_key_id(&public_key_to_token_key_id::<CS>(&server.get_public_key()));
key_store.insert(truncated_token_key_id, server).await;
Ok(public_key)
}

/// Creates a new keypair with explicit parameters and inserts it into the
/// key store.
#[cfg(feature = "kat")]
pub async fn create_keypair_with_params<BKS: PrivateKeyStore<CS = CS>>(
&self,
key_store: &BKS,
seed: &[u8],
info: &[u8],
) -> Result<PublicKey<CS>, CreateKeypairError>
where
<CS::Group as Group>::Scalar: Send + Sync,
<CS::Group as Group>::Elem: Send + Sync,
{
self.create_keypair_internal(key_store, seed, info).await
}

/// Issues a token response.
///
/// # Errors
Expand Down
46 changes: 26 additions & 20 deletions src/private_tokens/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ pub struct Server<CS: PrivateCipherSuite> {
}

impl<CS: PrivateCipherSuite> Server<CS> {
fn server_from_seed(seed: &[u8], info: &[u8]) -> Result<VoprfServer<CS>, CreateKeypairError> {
VoprfServer::<CS>::new_from_seed(seed, info).map_err(|_| CreateKeypairError::SeedError)
}

/// Creates a new server.
#[must_use]
pub const fn new() -> Self {
Expand All @@ -42,26 +46,23 @@ impl<CS: PrivateCipherSuite> Server<CS> {
&self,
key_store: &PKS,
) -> Result<PublicKey<CS>, CreateKeypairError> {
let mut seed = vec![0u8; <<CS::Group as Group>::ScalarLen as Unsigned>::USIZE];
OsRng.fill_bytes(&mut seed);
self.create_keypair_internal(key_store, &seed, b"PrivacyPass")
.await
}
let attempts_limit = 100;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe make this a constant somewhere, and use the same one for all three servers?

for _ in 0..attempts_limit {
let mut seed = vec![0u8; <<CS::Group as Group>::ScalarLen as Unsigned>::USIZE];
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You use GenericArray in Amortized Tokens, which puts the seed on the stack. That seems good, but I'd suggest just doing the same thing everywhere, whichever you like.

OsRng.fill_bytes(&mut seed);
let server = Self::server_from_seed(&seed, b"PrivacyPass")?;
let public_key = server.get_public_key();
let truncated_token_key_id =
truncate_token_key_id(&public_key_to_token_key_id::<CS>(&server.get_public_key()));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
truncate_token_key_id(&public_key_to_token_key_id::<CS>(&server.get_public_key()));
truncate_token_key_id(&public_key_to_token_key_id::<CS>(&public_key));


/// Creates a new keypair and inserts it into the key store.
async fn create_keypair_internal<PKS: PrivateKeyStore<CS = CS>>(
&self,
key_store: &PKS,
seed: &[u8],
info: &[u8],
) -> Result<PublicKey<CS>, CreateKeypairError> {
let server = VoprfServer::<CS>::new_from_seed(seed, info)
.map_err(|_| CreateKeypairError::SeedError)?;
let public_key = server.get_public_key();
let truncated_token_key_id =
truncate_token_key_id(&public_key_to_token_key_id::<CS>(&server.get_public_key()));
key_store.insert(truncated_token_key_id, server).await;
Ok(public_key)
if key_store.get(&truncated_token_key_id).await.is_some() {
continue;
}

key_store.insert(truncated_token_key_id, server).await;
return Ok(public_key);
}
Err(CreateKeypairError::SeedError)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#[error("Seed is too long")] seems sufficiently incorrect that you might want to make a CollisionExhausted maybe?

}

/// Creates a new keypair with explicit parameters and inserts it into the
Expand All @@ -73,7 +74,12 @@ impl<CS: PrivateCipherSuite> Server<CS> {
seed: &[u8],
info: &[u8],
) -> Result<PublicKey<CS>, CreateKeypairError> {
self.create_keypair_internal(key_store, seed, info).await
let server = Self::server_from_seed(seed, info)?;
let public_key = server.get_public_key();
let truncated_token_key_id =
truncate_token_key_id(&public_key_to_token_key_id::<CS>(&server.get_public_key()));
key_store.insert(truncated_token_key_id, server).await;
Ok(public_key)
}

/// Issues a token response.
Expand Down
57 changes: 40 additions & 17 deletions src/public_tokens/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ pub trait IssuerKeyStore: Send + Sync {
pub trait OriginKeyStore {
/// Inserts a keypair with a given `truncated_token_key_id` into the key store.
async fn insert(&self, truncated_token_key_id: TruncatedTokenKeyId, server: PublicKey);
/// Returns a keypair with a given `truncated_token_key_id` from the key store.
async fn get(&self, truncated_token_key_id: &TruncatedTokenKeyId) -> Option<PublicKey>;
/// Returns all public keys with a given `truncated_token_key_id` from the key store.
async fn get(&self, truncated_token_key_id: &TruncatedTokenKeyId) -> Vec<PublicKey>;
}

/// Serializes a keypair into a DER-encoded PKCS#8 document.
Expand Down Expand Up @@ -64,14 +64,23 @@ impl IssuerServer {
rng: &mut R,
key_store: &IKS,
) -> Result<PublicKey, CreateKeypairError> {
let key_pair =
KeyPair::generate(rng, KEYSIZE_IN_BITS).map_err(|_| CreateKeypairError::SeedError)?;
let truncated_token_key_id =
truncate_token_key_id(&public_key_to_token_key_id(&key_pair.pk));
key_store
.insert(truncated_token_key_id, key_pair.clone())
.await;
Ok(key_pair.pk)
let attempts_limit = 100;
for _ in 0..attempts_limit {
let key_pair = KeyPair::generate(rng, KEYSIZE_IN_BITS)
.map_err(|_| CreateKeypairError::SeedError)?;
let truncated_token_key_id =
truncate_token_key_id(&public_key_to_token_key_id(&key_pair.pk));

if key_store.get(&truncated_token_key_id).await.is_some() {
continue;
}

key_store
.insert(truncated_token_key_id, key_pair.clone())
.await;
return Ok(key_pair.pk);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of cloning the privkey as well, we can just clone out the pubkey

Suggested change
key_store
.insert(truncated_token_key_id, key_pair.clone())
.await;
return Ok(key_pair.pk);
let public_key = key_pair.pk.clone();
key_store
.insert(truncated_token_key_id, key_pair)
.await;
return Ok(public_key);

}
Err(CreateKeypairError::SeedError)
}

/// Issues a new token response.
Expand Down Expand Up @@ -152,17 +161,31 @@ impl OriginServer {
*token.token_key_id(),
);

let public_key = key_store
.get(&truncate_token_key_id(token.token_key_id()))
.await
.ok_or(RedeemTokenError::KeyIdNotFound)?;
let truncated_token_key_id = truncate_token_key_id(token.token_key_id());
let public_keys = key_store.get(&truncated_token_key_id).await;
if public_keys.is_empty() {
return Err(RedeemTokenError::KeyIdNotFound);
}

let options = Options::default();
let signature = Signature(token.authenticator().to_vec());
let token_input_bytes = token_input.serialize();

let mut verified = false;
for public_key in public_keys {
if signature
.verify(&public_key, None, token_input_bytes.clone(), &options)
.is_ok()
{
verified = true;
break;
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this is an opportunity to use iter().any()? Something like:

Suggested change
let mut verified = false;
for public_key in public_keys {
if signature
.verify(&public_key, None, token_input_bytes.clone(), &options)
.is_ok()
{
verified = true;
break;
}
}
let verified = public_keys.iter().any(|public_key| {
signature.verify(public_key, None, token_input_bytes.clone(), &options).is_ok()
});

perhaps we can get rid of the .clone() on each iteration, too? hmmm


if !verified {
return Err(RedeemTokenError::InvalidToken);
}

signature
.verify(&public_key, None, token_input.serialize(), &options)
.map_err(|_| RedeemTokenError::InvalidToken)?;
nonce_store.insert(token.nonce()).await;
Ok(())
}
Expand Down
15 changes: 11 additions & 4 deletions src/test_utils/public_memory_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,17 +27,24 @@ impl IssuerKeyStore for IssuerMemoryKeyStore {
/// Public key store that stores keys in memory.
#[derive(Default, Debug)]
pub struct OriginMemoryKeyStore {
keys: Mutex<HashMap<TruncatedTokenKeyId, PublicKey>>,
keys: Mutex<HashMap<TruncatedTokenKeyId, Vec<PublicKey>>>,
}

#[async_trait]
impl OriginKeyStore for OriginMemoryKeyStore {
async fn insert(&self, truncated_token_key_id: TruncatedTokenKeyId, public_key: PublicKey) {
let mut keys = self.keys.lock().await;
keys.insert(truncated_token_key_id, public_key);
keys.entry(truncated_token_key_id)
.or_default()
.push(public_key);
}

async fn get(&self, truncated_token_key_id: &TruncatedTokenKeyId) -> Option<PublicKey> {
self.keys.lock().await.get(truncated_token_key_id).cloned()
async fn get(&self, truncated_token_key_id: &TruncatedTokenKeyId) -> Vec<PublicKey> {
self.keys
.lock()
.await
.get(truncated_token_key_id)
.cloned()
.unwrap_or_default()
}
}
Loading