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

Implement key sharing #294

Closed
wants to merge 4 commits into from
Closed
Changes from all 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
93 changes: 93 additions & 0 deletions ppoprf/src/ppoprf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,50 @@ impl ServerPublicKey {
}
}

/// Server private key
///
/// Use this to re-create the same PPOPRF Service multiple times.
/// As with any private key, keeping this value secret is essential
/// to the security of the protocol.
#[derive(Debug, Eq, PartialEq, Zeroize, ZeroizeOnDrop)]
pub struct ServerPrivateKey(RistrettoScalar);

impl ServerPrivateKey {
/// Access the private key data as a slice
/// Use `try_from` to turn the result back into a key.
pub fn as_bytes(&self) -> &[u8] {
self.0.as_bytes()
}
}

impl TryFrom<&[u8]> for ServerPrivateKey {
type Error = PPRFError;

/// Attempt to construct a new private key from a slice of 32 bytes.
/// See `ServerPrivateKey::as_bytes` for the reverse operation.
fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
// RistrettoScalar is COMPRESSED_POINT_LEN bytes
// Make sure we have the right input size so copy_from_slice succeeds.
if bytes.len() != 32 {
return Err(PPRFError::BadInputLength {
actual: bytes.len(),
expected: 32,
});
}
let mut raw = [0u8; 32];
raw.copy_from_slice(bytes);
// Converting to a scalar my fail depending on the underlying raw data.
// This happens in constant time so nothing is revealed about the input.
let maybe_scalar = RistrettoScalar::from_canonical_bytes(raw);
// We must convert to a Result. This is not constant time, but the
// different execution paths only reveal if the call was successful,
// which our return value also reveals.
Option::from(maybe_scalar)
.map(ServerPrivateKey)
.ok_or(PPRFError::BadPointEncoding)
}
}

// The wrapper for PPOPRF evaluations (similar to standard OPRFs)
#[derive(Deserialize, Serialize)]
pub struct Evaluation {
Expand Down Expand Up @@ -312,6 +356,13 @@ pub struct Server {
impl Server {
pub fn new(mds: Vec<u8>) -> Result<Self, PPRFError> {
let oprf_key = RistrettoScalar::random(&mut OsRng);
Self::new_with_key(&mds, &ServerPrivateKey(oprf_key))
}

pub fn new_with_key(
mds: &[u8],
key: &ServerPrivateKey,
) -> Result<Self, PPRFError> {
let mut md_pks = BTreeMap::new();
let pprf = GGM::setup();
for &md in mds.iter() {
Expand All @@ -320,6 +371,7 @@ impl Server {
let ts = RistrettoScalar::from_bytes_mod_order(tag);
md_pks.insert(md, Point::from(ts * RISTRETTO_BASEPOINT_POINT));
}
let oprf_key = key.0;
Ok(Self {
oprf_key,
public_key: ServerPublicKey {
Expand Down Expand Up @@ -370,6 +422,10 @@ impl Server {
pub fn get_public_key(&self) -> ServerPublicKey {
self.public_key.clone()
}

pub fn get_private_key(&self) -> ServerPrivateKey {
ServerPrivateKey(self.oprf_key)
}
}

// The `Client` struct is essentially a collection of static functions
Expand Down Expand Up @@ -589,6 +645,43 @@ mod tests {
assert!(ServerPublicKey::load_from_bincode(&[98u8; 10000]).is_err());
}

#[test]
fn private_key() {
// Create a PPOPRF server instance
let mds = vec![0u8];
let server = Server::new(mds.clone()).unwrap();

// Run some random points through it
// In the real protocol these would be blinded to make the
// evaluation oblivious, but we need not test that here.
let inputs: Vec<Point> = (0..12)
.map(|_| RistrettoPoint::random(&mut OsRng))
.map(Point::from)
.collect();
let outputs: Vec<Point> = inputs
.iter()
.map(|p| server.eval(p, mds[0], false).unwrap().output)
.collect();

// Export the private key
let key = server.get_private_key();
let b64_key = base64::encode(key.as_bytes());
// Import the exported key
let bytes = base64::decode(b64_key).unwrap();
let key2 = ServerPrivateKey::try_from(bytes.as_slice()).unwrap();
assert_eq!(key, key2);

// Create a second server instance with the same state
let server_dup = Server::new_with_key(&mds, &key2).unwrap();
// Run the same points through
let outputs_dup: Vec<Point> = inputs
.iter()
.map(|p| server_dup.eval(p, mds[0], false).unwrap().output)
.collect();
// Should produce identical results
assert_eq!(outputs, outputs_dup);
}

#[test]
fn proof_serialization() {
let proof = ProofDLEQ {
Expand Down
Loading