Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Framework for variable-size PRSS generation #903

Merged
merged 4 commits into from
Dec 27, 2023

Conversation

andyleiserson
Copy link
Collaborator

There is some relevant background discussion on #884 and #852.

This addresses some, but not all, of the remaining cleanup for #812 -- this makes BA256 not a Field, which happens to be doable because it's not used as one anywhere. We also need to make the smaller BAs not Field, but that requires addressing places where they are used as Fields.

BA256 provides a sample of generating something with PRSS that needs more than 128 random bits, but this will also be needed for vectorization.

This doesn't address unnecessary generation of 128 random bits even when the value being generated is smaller. Once things are vectorized that will be less of an issue.

($modname:ident, $name:ident, $bits:tt) => {
boolean_array_impl!($modname, $name, $bits);

impl Field for $name {
Copy link
Collaborator

Choose a reason for hiding this comment

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

should we add a todo here that we want to remove it later?

@@ -32,7 +32,7 @@ pub async fn integer_add<C, XS, YS>(
where
C: Context,
YS: SharedValue + CustomArray<Element = XS::Element>,
XS: SharedValue + CustomArray + Field,
XS: SharedValue + CustomArray + SharedValue,
Copy link
Collaborator

Choose a reason for hiding this comment

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

👍

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Not sure if this is why you left the comment, but I just noticed this now says SharedValue + CustomArray + SharedValue. Oops.

Copy link
Collaborator

Choose a reason for hiding this comment

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

yea I was impressed that Rust allows to specify that :)

type SourceLength: ArrayLength;

/// Generate a random value of `Self` from `SourceLength` uniformly-distributed u128s.
fn from_random(src: GenericArray<u128, Self::SourceLength>) -> Self;
Copy link
Collaborator

Choose a reason for hiding this comment

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

curious about the choice of u128 for container? why not u8?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I used u128 because it matches what we get from PRSS. I'd be fine with using u8. The only hesitation I have about u8 is that it hides the fact that the randomness is generated in multiples of 128 bits, i.e., an implementation that takes a single u8 is much less efficient than it looks.

Copy link
Collaborator

Choose a reason for hiding this comment

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

agree, it is better to indicate that it is a multiple of 16 bytes

@andyleiserson andyleiserson merged commit 9045800 into private-attribution:main Dec 27, 2023
5 checks passed
@andyleiserson andyleiserson deleted the variable-prss branch December 27, 2023 19:08
// It would be nice for this to be TryFrom, but there's a lot of places where we use u128s as PRSS indexes.
impl From<u128> for PrssIndex {
fn from(value: u128) -> Self {
Self(value.try_into().unwrap())
Copy link
Member

Choose a reason for hiding this comment

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

This would be a great place to use .expect() instead of .unwrap().

impl PrssIndex {
fn offset(self, offset: usize) -> PrssIndex128 {
assert!(offset <= u16::MAX.into());
PrssIndex128::from(u128::from((u64::from(self.0) << 16) + (offset as u64)))
Copy link
Member

Choose a reason for hiding this comment

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

Do you really want to only use 16 bits for the offset, given that this is a 32-bit value?

Copy link
Collaborator Author

@andyleiserson andyleiserson Jan 3, 2024

Choose a reason for hiding this comment

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

Do you think 32 makes more sense? There was also @danielmasny's suggestion of 64 in #884 (comment).

Implementing Display will eliminate one reason for minimizing it, but it also seemed unlikely that any single invocation of PRSS would generate a value with more than 2^23 bits of entropy. I considered as little as 2^15. However, 2^15 bits is a 4,096-byte vector. That is the upper end of the range I foresee using, but I wanted to allow flexibility to go a bit bigger than that.

I don't have a strong opinion on this. 32 seems totally fine. I was prodded not to do the original suggestion of 64/64 when I saw that the existing RecordId and PRSS index are only 32 bits. I do wince a little at some of our use of u128, but this isn't a case of that since we're feeding it to AES anyways.

Copy link
Member

Choose a reason for hiding this comment

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

Keeping the values within a 64-bit range is fine, possibly even a good idea.

@@ -569,7 +638,7 @@ pub mod test {
#[test]
#[cfg(debug_assertions)]
#[should_panic(
expected = "Generated randomness for index '100' twice using the same key 'protocol/test'"
expected = "Generated randomness for index '6553600' twice using the same key 'protocol/test'"
Copy link
Member

Choose a reason for hiding this comment

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

any sense in implementing Display for PrssIndexU128 to make this less of a pain to use?

Comment on lines +148 to +150
for i in 0..N::USIZE {
self.used.insert(index.offset(i));
}
Copy link
Member

Choose a reason for hiding this comment

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

I might suggest that we only need to track against PrssIndex for this. It will greatly reduce the tracking overhead.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It's probably sufficient, but aren't we already planning to disable this tracking in production configurations? In which case, I'd rather be cautious and do the more extensive check, unless we determine it's a huge hit to our dev runtimes.

Vectorization will shift usage from the index bits to the offset bits, but the overall number of entries added to the map will stay the same. i.e. this change does not increase the size requirement for the tracking structure.

Copy link
Member

Choose a reason for hiding this comment

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

Just a thought. The API doesn't let you take an index plus a range of offsets that don't start at zero, so I figured... why not.

You might get similar coverage by only checking 0 and N-1, FWIW.

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.

3 participants