0
0
mirror of https://github.com/signalapp/libsignal.git synced 2024-09-19 19:42:19 +02:00

SVR - plumb new protocol into net.

This commit is contained in:
gram-signal 2024-08-29 14:47:47 -07:00 committed by GitHub
parent e46841ea2c
commit 303d7a6659
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 212 additions and 106 deletions

View File

@ -13,10 +13,11 @@ pub const ENCLAVE_ID_CDSI_STAGING_AND_PROD: &[u8] =
&hex!("0f6fd79cdfdaa5b2e6337f534d3baf999318b0c462a7ac1f41297a3e4b424a57");
pub const ENCLAVE_ID_SVR2_STAGING: &[u8] =
&hex!("acb1973aa0bbbd14b3b4e06f145497d948fd4a98efc500fcce363b3b743ec482");
pub const ENCLAVE_ID_SVR3_SGX_STAGING: &[u8] =
&hex!("29cd5aa268da2412ae14e9de2168608c9b22daadfd7effa2029abac02289e691");
pub const ENCLAVE_ID_SVR3_NITRO_STAGING: &[u8] = b"ffe631d7.52b91975.a4544fb5";
pub const ENCLAVE_ID_SVR3_TPM2SNP_STAGING: &[u8] = b"0.20240703.193412";
&hex!("64fd0ab571f2de73665befed1f34862e17cdd3d58201e335be2f68b3f985180f");
pub const ENCLAVE_ID_SVR3_NITRO_STAGING: &[u8] = b"e651a442.52b91975.5c89712f";
pub const ENCLAVE_ID_SVR3_TPM2SNP_STAGING: &[u8] = b"0.20240824.003942";
pub const ENCLAVE_ID_SVR3_SGX_PROD: &[u8] =
&hex!("0899bf951b57f27b5cd3d2dd4dbe5a144a4a62154853a4e352ac2c93ecfe6a2c");
@ -30,9 +31,9 @@ pub(crate) const NITRO_EXPECTED_PCRS: SmallMap<&'static [u8], nitro::PcrMap, 1>
(
ENCLAVE_ID_SVR3_NITRO_STAGING,
SmallMap::new([
(0, hex!("ffe631d7b726c672480ddde425f3ed9cbdaafa354dc6a85277dde6bfca56e93fafd66052f1dd93bf5f240c5a55fb2cb1")),
(0, hex!("e651a442efc3be65a893c7ea3211d3a826b5c0b2102a224df9b42b6a7c0306b8e67553beed4069db0192224a644d80bc")),
(1, hex!("52b919754e1643f4027eeee8ec39cc4a2cb931723de0c93ce5cc8d407467dc4302e86490c01c0d755acfe10dbf657546")),
(2, hex!("a4544fb5e5f416c08c4aca4c3f14efaf8d16d7ddc39d15f17b3b02605ef6e3a834553a0901fbce8716cc0de8caea028d")),
(2, hex!("5c89712fd49d4cbebfa1c7f7c59e19a2f94203938647348ebd0dfd20ce3e17b9c7447d3e4856348a89d3c96b5a747a35")),
]),
),
]);
@ -47,8 +48,8 @@ pub(crate) const TPM2SNP_EXPECTED_PCRS: SmallMap<&'static [u8], &'static tpm2snp
(3, hex!("3d458cfe55cc03ea1f443f1562beec8df51c75e14a9fcf9a7234a13f198e7969")),
(4, hex!("6038382cdf539eb64d05c804c510e22b81e2c71fb171c9616ab14504f3654bb1")),
(7, hex!("590471a4fbd0c881c4fdc6349bc697e4df18c660c3ae3de9cb29028f8ef77280")),
(8, hex!("7ea60a57d8d6b5c522dabeb55a3fa15aed6df65ae470b43865042e39be26ac06")),
(9, hex!("95abd7aab524ef6b25f18f58a8b7f7e5b6eb285686fa8b7b28ccd39a73ac8d30")),
(8, hex!("de28c40baca9bdb2024cd5e7a0af223396f8459b2bb14b5edce90ff78bc83c93")),
(9, hex!("27117054ad1ca7d2daf5f6dbbc5fb7c3268f460afe6a2933d32d34968a13b12a")),
(11, hex!("0000000000000000000000000000000000000000000000000000000000000000")),
(12, hex!("0000000000000000000000000000000000000000000000000000000000000000")),
(13, hex!("0000000000000000000000000000000000000000000000000000000000000000")),
@ -101,16 +102,16 @@ pub const RAFT_CONFIG_SVR3_SGX_STAGING: &RaftConfig = &RaftConfig {
min_voting_replicas: 3,
max_voting_replicas: 9,
super_majority: 0,
group_id: 15742431669367858463,
group_id: 2598856512118716214,
};
pub const RAFT_CONFIG_SVR3_NITRO_STAGING: &RaftConfig = &RaftConfig {
group_id: 10298929430185113734,
group_id: 12784208226750162631,
min_voting_replicas: 3,
max_voting_replicas: 9,
super_majority: 0,
};
pub const RAFT_CONFIG_SVR3_TPM2SNP_STAGING: &RaftConfig = &RaftConfig {
group_id: 2579018130363863974,
group_id: 15331762113118535803,
min_voting_replicas: 3,
max_voting_replicas: 9,
super_majority: 0,

View File

@ -6,7 +6,7 @@
use std::num::NonZeroU32;
use bincode::Options as _;
use libsignal_svr3::{EvaluationResult, MaskedShareSet};
use libsignal_svr3::{EvaluationResult, MaskedSecret};
use rand_core::CryptoRngCore;
use serde::{Deserialize, Serialize};
use thiserror::Error;
@ -29,31 +29,28 @@ pub struct OpaqueMaskedShareSet {
inner: SerializableMaskedShareSet,
}
// Non pub version of ppss::MaskedShareSet used for serialization
// Non pub version of svr3::MaskedSecret used for serialization
#[derive(Clone, Serialize, Deserialize, Debug)]
#[cfg_attr(test, derive(PartialEq, Eq, Default))]
struct SerializableMaskedShareSet {
server_ids: Vec<u64>,
masked_shares: Vec<[u8; 32]>,
commitment: [u8; 32],
masked_secret: [u8; 32],
}
impl From<MaskedShareSet> for SerializableMaskedShareSet {
fn from(value: MaskedShareSet) -> Self {
impl From<MaskedSecret> for SerializableMaskedShareSet {
fn from(value: MaskedSecret) -> Self {
Self {
server_ids: value.server_ids,
masked_shares: value.masked_shares,
commitment: value.commitment,
masked_secret: value.masked_secret,
}
}
}
impl SerializableMaskedShareSet {
fn into(self) -> MaskedShareSet {
MaskedShareSet {
fn into(self) -> MaskedSecret {
MaskedSecret {
server_ids: self.server_ids,
masked_shares: self.masked_shares,
commitment: self.commitment,
masked_secret: self.masked_secret,
}
}
}
@ -72,12 +69,12 @@ pub enum DeserializeError {
impl LogSafeDisplay for DeserializeError {}
impl OpaqueMaskedShareSet {
fn new(inner: MaskedShareSet) -> Self {
fn new(inner: MaskedSecret) -> Self {
Self {
inner: inner.into(),
}
}
fn into_inner(self) -> MaskedShareSet {
fn into_inner(self) -> MaskedSecret {
self.inner.into()
}
@ -308,8 +305,7 @@ mod test {
OpaqueMaskedShareSet {
inner: SerializableMaskedShareSet {
server_ids: vec![],
masked_shares: vec![],
commitment: [0; 32],
masked_secret: [0u8; 32],
},
}
}

View File

@ -12,7 +12,7 @@ use std::num::NonZeroU32;
use std::sync::Arc;
use futures_util::future::join_all;
use libsignal_svr3::{make_remove_request, Backup, EvaluationResult, Query, Restore};
use libsignal_svr3::{Backup4, EvaluationResult, MaskedSecret, Query4, Remove4, Restore1};
use rand_core::CryptoRngCore;
use super::{Error, OpaqueMaskedShareSet};
@ -31,14 +31,20 @@ pub async fn do_backup<S: AsyncDuplexStream + 'static, Env: PpssSetup<S>>(
let ConnectionContext {
mut connections,
addresses,
mut errors,
errors,
} = ConnectionContext::new(connect_results);
if let Some(err) = errors.pop_front() {
if let Some(err) = errors.into_iter().next() {
return Err(err);
}
let server_ids = Env::server_ids();
let backup = Backup::new(server_ids.as_ref(), password, secret, max_tries, rng)?;
let backup = Backup4::new(
server_ids.as_ref(),
password.as_bytes(),
&secret,
max_tries,
rng,
)?;
let futures = connections
.iter_mut()
.zip(&backup.requests)
@ -47,9 +53,8 @@ pub async fn do_backup<S: AsyncDuplexStream + 'static, Env: PpssSetup<S>>(
.await
.into_iter()
.collect::<Result<Vec<_>, _>>();
let responses = collect_responses(results?, addresses.iter())?;
let share_set = backup.finalize(rng, &responses)?;
Ok(OpaqueMaskedShareSet::new(share_set))
collect_responses(results?, addresses.iter())?;
Ok(OpaqueMaskedShareSet::new(backup.masked_secret))
}
pub async fn do_restore<S: AsyncDuplexStream + 'static>(
@ -61,23 +66,46 @@ pub async fn do_restore<S: AsyncDuplexStream + 'static>(
let ConnectionContext {
mut connections,
addresses,
mut errors,
errors,
} = ConnectionContext::new(connect_results);
if let Some(err) = errors.pop_front() {
if let Some(err) = errors.into_iter().next() {
return Err(err);
}
let restore = Restore::new(password, share_set.into_inner(), rng)?;
let masked_secret: MaskedSecret = share_set.into_inner();
let restore1 = Restore1::new(masked_secret.server_ids.as_ref(), password.as_bytes(), rng);
let responses1 = {
let futures = connections
.iter_mut()
.zip(&restore.requests)
.zip(&restore1.requests)
.map(|(connection, request)| run_attested_interaction(connection, request));
let results = join_all(futures)
.await
.into_iter()
.collect::<Result<Vec<_>, _>>();
let responses = collect_responses(results?, addresses.iter())?;
Ok(restore.finalize(&responses)?)
collect_responses(results?, addresses.iter())?
};
let restore2 = restore1.restore2(&responses1, rng)?;
let tries_remaining = restore2.tries_remaining;
let responses2 = {
let futures = connections
.iter_mut()
.zip(&restore2.requests)
.map(|(connection, request)| run_attested_interaction(connection, request));
let results = join_all(futures)
.await
.into_iter()
.collect::<Result<Vec<_>, _>>();
collect_responses(results?, addresses.iter())?
};
let output = restore2.restore(&responses2)?;
Ok(EvaluationResult {
value: output.unmask_secret(&masked_secret.masked_secret),
tries_remaining,
})
}
pub async fn do_remove<S: AsyncDuplexStream + 'static>(
@ -93,9 +121,11 @@ pub async fn do_remove<S: AsyncDuplexStream + 'static>(
// and proceed to work with the successful connections.
log::debug!("Connection failure '{:?}' will be ignored.", &err);
}
let futures = connections
.iter_mut()
.map(|connection| run_attested_interaction(connection, make_remove_request()));
.zip(Remove4::requests())
.map(|(connection, request)| run_attested_interaction(connection, request));
let results = join_all(futures)
.await
.into_iter()
@ -110,22 +140,22 @@ pub async fn do_query<S: AsyncDuplexStream + 'static>(
let ConnectionContext {
mut connections,
addresses,
mut errors,
errors,
} = ConnectionContext::new(connect_results);
if let Some(err) = errors.pop_front() {
if let Some(err) = errors.into_iter().next() {
return Err(err);
}
let futures = connections
.iter_mut()
.zip(Query::requests())
.zip(Query4::requests())
.map(|(connection, request)| run_attested_interaction(connection, request));
let results = join_all(futures)
.await
.into_iter()
.collect::<Result<Vec<_>, _>>();
let responses = collect_responses(results?, addresses.iter())?;
Ok(Query::finalize(&responses)?)
Ok(Query4::finalize(&responses)?)
}
struct ConnectionContext<S> {

View File

@ -18,12 +18,59 @@ use sha2::{Sha256, Sha512};
use crate::errors::Error;
use crate::proto::svr4;
/// Make a request to remove a record from SVR4.
pub fn make_remove4_request() -> Vec<u8> {
/// Perfrom array XOR: into ^= from
fn arr_xor(from: &[u8], into: &mut [u8]) {
assert_eq!(from.len(), into.len());
for (dst, src) in std::iter::zip(into, from) {
*dst ^= src;
}
}
pub struct Query4 {}
impl Query4 {
// Using `impl Iterator<...>` makes libsignal-net fail to build in Rust 1.72
pub fn requests() -> std::iter::Repeat<Vec<u8>> {
std::iter::repeat(
svr4::Request4 {
inner: Some(svr4::request4::Inner::Query(svr4::request4::Query {})),
}
.encode_to_vec(),
)
}
pub fn finalize(responses: &[Vec<u8>]) -> Result<u32, Error> {
assert!(!responses.is_empty());
responses
.iter()
.map(|b| svr4::Response4::decode(b.as_ref()).map_err(|_| Error::BadData))
.map(|rr| match rr?.inner {
Some(svr4::response4::Inner::Query(r)) => match status_error(r.status) {
Ok(()) => Ok(r.tries_remaining),
Err(e) => Err(e),
},
_ => Err(Error::BadData),
})
// Get the min tries_remaining while short circuiting on errors.
// Should never actually return u32::MAX, since there should be at least one
// response, which will either be an error (returning an error overall)
// or a value less than MAX.
.try_fold(u32::MAX, |acc, tr| Ok(std::cmp::min(acc, tr?)))
}
}
pub struct Remove4 {}
impl Remove4 {
// Using `impl Iterator<...>` makes libsignal-net fail to build in Rust 1.72
pub fn requests() -> std::iter::Repeat<Vec<u8>> {
std::iter::repeat(
svr4::Request4 {
inner: Some(svr4::request4::Inner::Remove(svr4::request4::Remove {})),
}
.encode_to_vec()
.encode_to_vec(),
)
}
}
/// Returns a list of `n` scalars which are chosen randomly but
@ -49,9 +96,7 @@ fn bytes_xoring_to<R: CryptoRngCore>(n: NonZeroUsize, b: &[u8], rng: &mut R) ->
.collect();
let mut xor: Vec<u8> = b.into();
for vec in v.iter() {
for (i, c) in vec.iter().enumerate() {
xor[i] ^= c;
}
arr_xor(vec, &mut xor);
}
v.push(xor);
v
@ -118,19 +163,35 @@ fn to_ristretto_pt(b: &[u8]) -> Option<RistrettoPoint> {
CompressedRistretto::from_slice(b).ok()?.decompress()
}
/// Given a set of password bytes from the client that may be in any
/// format or any length, generate a deterministic 64-byte key
/// for use in Backup/Restore operations.
fn password_to_uniform_input(passwd: &[u8]) -> [u8; 64] {
Kdf::make(b"Signal_SVR_InputFromPassword_20240823", passwd, b"")
}
pub struct Backup4 {
pub requests: Vec<Vec<u8>>,
pub output: Output4,
pub masked_secret: MaskedSecret,
}
/// Contains everything necessary to restore a secret given a password.
pub struct MaskedSecret {
pub server_ids: Vec<u64>,
pub masked_secret: [u8; 32],
}
impl Backup4 {
pub fn new<R: CryptoRngCore>(
server_ids: &[u64],
input: [u8; 64],
password: &[u8],
secret: &[u8; 32],
max_tries: NonZeroU32,
rng: &mut R,
) -> Result<Self, Error> {
assert!(!server_ids.is_empty());
let input = password_to_uniform_input(password);
let k_oprf = Scalar::random(rng);
let mut s_enc = [0u8; 32];
@ -144,7 +205,11 @@ impl Backup4 {
let auth_pt = auth_pt(&input, &k_oprf);
let auth_commitments = auth_commitments(server_ids, &input, &auth_pt);
let version = rng.next_u64();
let version = rng.next_u32();
let output = Output4 {
k_auth: auth_secret(&input, &auth_pt),
s_enc,
};
Ok(Self {
requests: (0usize..server_ids.len())
@ -160,10 +225,11 @@ impl Backup4 {
})
.map(|cr| cr.encode_to_vec())
.collect(),
output: Output4 {
k_auth: auth_secret(&input, &auth_pt),
s_enc,
masked_secret: MaskedSecret {
server_ids: server_ids.to_vec(),
masked_secret: output.mask_secret(secret),
},
output,
})
}
}
@ -182,20 +248,28 @@ impl Output4 {
&self.k_auth,
)
}
pub fn mask_secret(&self, secret: &[u8; 32]) -> [u8; 32] {
let mut out = self.encryption_key();
arr_xor(secret, &mut out);
out
}
pub fn unmask_secret(&self, masked_secret: &[u8; 32]) -> [u8; 32] {
self.mask_secret(masked_secret) // masking and unmasking are the same function
}
}
pub struct Restore1<'a> {
server_ids: &'a [u64],
input: &'a [u8; 64],
input: [u8; 64],
blind: Scalar,
pub requests: Vec<Vec<u8>>,
}
pub struct Restore2<'a> {
server_ids: &'a [u64],
input: &'a [u8; 64],
input: [u8; 64],
auth_pt: RistrettoPoint,
tries_remaining: Option<u32>,
pub tries_remaining: u32,
pub requests: Vec<Vec<u8>>,
}
@ -212,14 +286,15 @@ fn status_errors<I: Iterator<Item = i32>>(statuses: &mut I) -> Result<(), Error>
}
impl<'a> Restore1<'a> {
pub fn new<R: CryptoRngCore>(server_ids: &'a [u64], input: &'a [u8; 64], rng: &mut R) -> Self {
pub fn new<R: CryptoRngCore>(server_ids: &'a [u64], password: &[u8], rng: &mut R) -> Self {
let blind = Scalar::random(rng);
let input = password_to_uniform_input(password);
Restore1 {
requests: server_ids
.iter()
.map(|_| svr4::Request4 {
inner: Some(svr4::request4::Inner::Restore1(svr4::request4::Restore1 {
blinded: (input_hash_pt(input) * blind)
blinded: (input_hash_pt(&input) * blind)
.compress()
.to_bytes()
.to_vec(),
@ -304,7 +379,7 @@ impl<'a> Restore1<'a> {
// Now, we use auth_pt to recompute auth commitments, which we send
// back to the server, proving we have the correct value for `input`.
let auth_commitments = auth_commitments(self.server_ids, self.input, &auth_pt);
let auth_commitments = auth_commitments(self.server_ids, &self.input, &auth_pt);
let rand = Scalar::random(rng);
let proof_pt_bytes = (RISTRETTO_BASEPOINT_TABLE * &rand).compress().to_bytes();
let proof_scalar_base = Scalar::hash_from_bytes::<Sha512>(&proof_pt_bytes);
@ -325,14 +400,15 @@ impl<'a> Restore1<'a> {
server_ids: self.server_ids,
input: self.input,
auth_pt,
tries_remaining,
tries_remaining: tries_remaining
.expect("all responses had to be OK, so we should have this"),
})
}
/// Given a set of responses, each of which have some number of Auths,
/// return a version that is available in all responses, if such a
/// one exists.
fn version_to_use(&self, responses1: &[svr4::response4::Restore1]) -> Option<u64> {
fn version_to_use(&self, responses1: &[svr4::response4::Restore1]) -> Option<u32> {
let mut versions = BTreeMap::new();
for r1 in responses1 {
for auth in &r1.auth {
@ -352,7 +428,7 @@ impl<'a> Restore1<'a> {
/// given version, or an Error::BadResponse if they're not found.
fn auths_with_version<'b>(
&self,
version: u64,
version: u32,
responses1: &'b [svr4::response4::Restore1],
) -> Result<Vec<&'b svr4::response4::restore1::Auth>, Error> {
let mut out = Vec::with_capacity(responses1.len());
@ -394,33 +470,27 @@ impl<'a> Restore2<'a> {
_ => Err(Error::BadResponse),
})
.collect::<Result<Vec<svr4::response4::Restore2>, _>>()?;
status_errors(&mut responses2.iter().map(|r| r.status)).map_err(|e| {
match self.tries_remaining {
Some(tr) => Error::RestoreFailed(tr),
None => e,
}
})?;
status_errors(&mut responses2.iter().map(|r| r.status))
.map_err(|_| Error::RestoreFailed(self.tries_remaining))?;
let mut s_enc = [0u8; 32];
for resp in responses2.iter() {
if resp.encryption_secretshare.len() != s_enc.len() {
return Err(Error::BadResponse);
}
for (i, c) in resp.encryption_secretshare.iter().enumerate() {
s_enc[i] ^= c;
}
arr_xor(&resp.encryption_secretshare, &mut s_enc);
}
Ok(Output4 {
s_enc,
k_auth: auth_secret(self.input, &self.auth_pt),
k_auth: auth_secret(&self.input, &self.auth_pt),
})
}
}
enum RotationAction {
DoNothing,
Commit(u64),
Rollback(u64),
Commit(u32),
Rollback(u32),
}
/// RotationMachineState defines a simple state machine for performing
@ -477,11 +547,11 @@ enum RotationMachineState {
/// Start our actual rotation, using the stored new (random) version
/// number. This creates new key deltas for OPRF and Encryption keys
/// and provides them to the backing servers.
RotateStart(u64),
RotateStart(u32),
/// Commit the version we created with RotateStart, rolling all backends
/// forward to the new version. If this succeeds, all servers will be
/// at the new version.
RotateCommit(u64),
RotateCommit(u32),
/// There's nothing more to do, either because we hit an unrecoverable
/// error or because we've completed successfully.
Done,
@ -569,7 +639,7 @@ impl<'a> RotationMachine<'a> {
.collect::<Vec<_>>()
}
fn rotate_start_requests(&mut self, version: u64) -> Vec<Vec<u8>> {
fn rotate_start_requests(&mut self, version: u32) -> Vec<Vec<u8>> {
let n = NonZeroUsize::new(self.server_ids.len()).unwrap();
let oprf_secretshares = scalars_summing_to(n, &Scalar::ZERO, &mut self.rng);
let mut encryption_secretshares = bytes_xoring_to(n, &[0u8; 32], &mut self.rng);
@ -589,7 +659,7 @@ impl<'a> RotationMachine<'a> {
.collect::<Vec<_>>()
}
fn rotate_commit_requests(&self, version: u64) -> Vec<Vec<u8>> {
fn rotate_commit_requests(&self, version: u32) -> Vec<Vec<u8>> {
self.server_ids
.iter()
.map(|_| svr4::Request4 {
@ -640,7 +710,7 @@ impl<'a> RotationMachine<'a> {
let first = resps[0];
// Utility function to check that a version is in all responses.
let version_in_all_responses = |v: u64| -> bool {
let version_in_all_responses = |v: u32| -> bool {
resps
.iter()
.filter(|r| r.version == v || r.new_version == v)
@ -679,7 +749,7 @@ impl<'a> RotationMachine<'a> {
if action_required {
self.state = RotationMachineState::FixPrevious(actions);
} else {
self.state = RotationMachineState::RotateStart(self.rng.next_u64());
self.state = RotationMachineState::RotateStart(self.rng.next_u32());
}
Ok(())
}
@ -694,11 +764,11 @@ impl<'a> RotationMachine<'a> {
Some(svr4::response4::Inner::RotateRollback(r)) => status_error(r.status),
_ => Err(Error::BadResponse),
})?;
self.state = RotationMachineState::RotateStart(self.rng.next_u64());
self.state = RotationMachineState::RotateStart(self.rng.next_u32());
Ok(())
}
fn rotate_start_responses(&mut self, responses: &[Vec<u8>], version: u64) -> Result<(), Error> {
fn rotate_start_responses(&mut self, responses: &[Vec<u8>], version: u32) -> Result<(), Error> {
let _resps = responses
.iter()
.map(|b| svr4::Response4::decode(b.as_ref()).map_err(|_| Error::BadData))
@ -788,7 +858,7 @@ mod test {
/// TestServer implements the server-side for a single-user interaction.
struct TestServer {
tries: u32,
versions: BTreeMap<u64, TestServerVersion>,
versions: BTreeMap<u32, TestServerVersion>,
}
/// TestServerVersion holds a single version's data for a single user.
@ -930,17 +1000,19 @@ mod test {
.iter()
.map(|_| TestServer::new())
.collect::<Vec<_>>();
let input = [2u8; 64];
let password = [2u8; 67]; // can be arbitrary length
// Create a new backup
let backup =
Backup4::new(&server_ids, input, nonzero!(10u32), &mut rng).expect("create Backup4");
let secret = [3u8; 32];
let backup = Backup4::new(&server_ids, &password, &secret, nonzero!(10u32), &mut rng)
.expect("create Backup4");
for (server, req) in servers.iter_mut().zip(backup.requests) {
server.create(&req);
}
let masked_secret = backup.masked_secret;
// Restoring existing backup.
let restore1 = Restore1::new(&server_ids, &input, &mut rng);
let restore1 = Restore1::new(&server_ids, &password, &mut rng);
let restore1_responses = servers
.iter_mut()
.zip(&restore1.requests)
@ -958,6 +1030,10 @@ mod test {
.restore(&restore2_responses)
.expect("call restored");
assert_eq!(backup.output, got);
assert_eq!(
secret,
backup.output.unmask_secret(&masked_secret.masked_secret)
);
}
/// Deterministic RNG for testing

View File

@ -16,6 +16,7 @@ pub use ppss::{MaskedShareSet, OPRFSession};
mod errors;
pub use errors::{Error, ErrorStatus, OPRFError, PPSSError};
mod proto;
pub use client::{Backup4, MaskedSecret, Query4, Remove4, Restore1, Restore2};
use proto::svr3::{self, create_response, evaluate_response, query_response};
pub use proto::svr4::response4::Status as V4Status;

View File

@ -26,7 +26,7 @@ message Request4 {
bytes auth_commitment = 3; // clienteck^pk
bytes encryption_secretshare = 4; // s_i
bytes zero_secretshare = 5; // z_i
fixed64 version = 6; // cross-backend version # for joins
fixed32 version = 6; // cross-backend version # for joins
}
message Restore1 {
bytes blinded = 1;
@ -34,22 +34,22 @@ message Request4 {
message Restore2 {
bytes auth_point = 1; // R
bytes auth_scalar = 2; // z
fixed64 version = 3;
fixed32 version = 3;
}
message Remove {
}
message Query {
}
message RotateStart {
fixed64 version = 1; // new cross-backend version #
fixed32 version = 1; // new cross-backend version #
bytes oprf_secretshare_delta = 2; // k_i^oprf scalar delta
bytes encryption_secretshare_delta = 3; // s_i XOR delta
}
message RotateCommit {
fixed64 version = 1; // new version #
fixed32 version = 1; // new version #
}
message RotateRollback {
fixed64 version = 1; // new version #
fixed32 version = 1; // new version #
}
}
@ -75,6 +75,8 @@ message Response4 {
VERSION_MISMATCH = 6;
NOT_ROTATING = 7;
ALREADY_ROTATING = 8;
MERKLE_FAILURE = 9;
DUPLICATE_VERSION = 10;
}
message Create {
@ -86,7 +88,7 @@ message Response4 {
Status status = 1;
message Auth {
bytes element = 1;
fixed64 version = 2;
fixed32 version = 2;
}
// There will be either 1 or 2 of these, depending on
// whether we're currently rotating.
@ -105,8 +107,8 @@ message Response4 {
message Query {
Status status = 1;
uint32 tries_remaining = 2;
fixed64 version = 3;
fixed64 new_version = 4;
fixed32 version = 3;
fixed32 new_version = 4;
}
message RotateStart {