Skip to content

Conversation

@raphaelrobert
Copy link
Owner

@raphaelrobert raphaelrobert commented Nov 2, 2025

Fixes #43.

Copy link

@jcjones jcjones left a comment

Choose a reason for hiding this comment

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

A drive-by!

Comment on lines 64 to 68
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.

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.

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?

Comment on lines 78 to 81
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);

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));

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));

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?

}
let attempts_limit = 100;
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.

Comment on lines 174 to 183
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

@raphaelrobert
Copy link
Owner Author

Thanks for the review, @jcjones. Much appreciated!

@raphaelrobert raphaelrobert merged commit c6be7b0 into main Nov 30, 2025
1 check passed
@raphaelrobert raphaelrobert deleted the truncated-key-id-collisions branch November 30, 2025 22:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

OriginKeyStore can't handle collisions of truncated token key IDs

3 participants