use crate::c1_normal_return::DbNormalReturnWorkProof;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum DbServiceTarget {
LinuxX86_64,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct DbDeploymentIdentity {
pub target: DbServiceTarget,
pub environment: [u8; 32],
pub database_server: [u8; 32],
pub network: [u8; 32],
pub sqlx: [u8; 32],
pub target_system: [u8; 32],
pub build: [u8; 32],
pub gate: [u8; 32],
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct DbReturnServiceDomain {
pub max_ping_commands: u64,
pub max_protocol_writes: u64,
pub max_protocol_reads: u64,
pub max_parallel_returns: u64,
pub pool_profile_attested: bool,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct DbReturnServiceRates {
pub ping_command_nanos: u64,
pub protocol_write_nanos: u64,
pub protocol_read_nanos: u64,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct DbReturnCalibration {
pub samples: u64,
pub max_observed_ping_command_nanos: u64,
pub max_observed_protocol_write_nanos: u64,
pub max_observed_protocol_read_nanos: u64,
}
pub trait DeploymentDbServiceAttestation: Sized {
fn target(&self) -> DbServiceTarget;
fn approved(&self) -> bool;
fn reproducible_measurement(&self) -> bool;
fn conservative_upper_bound(&self) -> bool;
fn finite_completion(&self) -> bool;
fn work_identity(&self) -> [u8; 32];
fn work_domain(&self) -> DbReturnServiceDomain;
fn calibration(&self) -> DbReturnCalibration;
fn service_rates(&self) -> DbReturnServiceRates;
fn calibration_identity(&self) -> [u8; 32];
fn environment_identity(&self) -> [u8; 32];
fn database_server_identity(&self) -> [u8; 32];
fn network_identity(&self) -> [u8; 32];
fn sqlx_identity(&self) -> [u8; 32];
fn target_system_identity(&self) -> [u8; 32];
fn service_attestation(&self) -> [u8; 32];
fn build_identity(&self) -> [u8; 32];
fn gate_identity(&self) -> [u8; 32];
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum DbServiceProofError {
Unapproved,
InvalidIdentity,
InvalidDomain,
InvalidService,
DomainMismatch,
Overflow,
}
#[derive(Debug)]
pub struct VerifiedDbServiceProof {
target: DbServiceTarget,
work_identity: [u8; 32],
domain: DbReturnServiceDomain,
calibration: DbReturnCalibration,
rates: DbReturnServiceRates,
calibration_identity: [u8; 32],
environment_identity: [u8; 32],
database_server_identity: [u8; 32],
network_identity: [u8; 32],
sqlx_identity: [u8; 32],
target_system_identity: [u8; 32],
service_attestation: [u8; 32],
build_identity: [u8; 32],
gate_identity: [u8; 32],
}
#[derive(Debug)]
pub struct DbReturnServiceProof {
work_identity: [u8; 32],
max_service_nanos: u64,
calibration_identity: [u8; 32],
environment_identity: [u8; 32],
database_server_identity: [u8; 32],
network_identity: [u8; 32],
sqlx_identity: [u8; 32],
target_system_identity: [u8; 32],
service_attestation: [u8; 32],
build_identity: [u8; 32],
gate_identity: [u8; 32],
}
impl DbReturnServiceProof {
pub const fn work_identity(&self) -> [u8; 32] {
self.work_identity
}
pub const fn max_service_nanos(&self) -> u64 {
self.max_service_nanos
}
pub const fn calibration_identity(&self) -> [u8; 32] {
self.calibration_identity
}
pub const fn environment_identity(&self) -> [u8; 32] {
self.environment_identity
}
pub const fn database_server_identity(&self) -> [u8; 32] {
self.database_server_identity
}
pub const fn network_identity(&self) -> [u8; 32] {
self.network_identity
}
pub const fn sqlx_identity(&self) -> [u8; 32] {
self.sqlx_identity
}
pub const fn target_system_identity(&self) -> [u8; 32] {
self.target_system_identity
}
pub const fn service_attestation(&self) -> [u8; 32] {
self.service_attestation
}
pub const fn build_identity(&self) -> [u8; 32] {
self.build_identity
}
pub const fn gate_identity(&self) -> [u8; 32] {
self.gate_identity
}
}
impl saddle_admission::VerifiedDbTerminationServiceProofOwner for DbReturnServiceProof {
fn work_identity(&self) -> [u8; 32] {
self.work_identity()
}
fn service_attestation(&self) -> [u8; 32] {
self.service_attestation()
}
fn max_service_nanos(&self) -> u64 {
self.max_service_nanos()
}
}
pub fn verify_db_service<A: DeploymentDbServiceAttestation>(
attestation: A,
expected: DbDeploymentIdentity,
work: DbNormalReturnWorkProof,
) -> Result<VerifiedDbServiceProof, DbServiceProofError> {
if !attestation.approved()
|| !attestation.reproducible_measurement()
|| !attestation.conservative_upper_bound()
{
return Err(DbServiceProofError::Unapproved);
}
if !attestation.finite_completion() {
return Err(DbServiceProofError::InvalidService);
}
let target = attestation.target();
if target != DbServiceTarget::LinuxX86_64 || target != expected.target {
return Err(DbServiceProofError::InvalidDomain);
}
let identities = [
attestation.work_identity(),
attestation.calibration_identity(),
attestation.environment_identity(),
attestation.database_server_identity(),
attestation.network_identity(),
attestation.sqlx_identity(),
attestation.target_system_identity(),
attestation.service_attestation(),
attestation.build_identity(),
attestation.gate_identity(),
];
let expected_identities = [
work.identity(),
expected.environment,
expected.database_server,
expected.network,
expected.sqlx,
expected.target_system,
expected.build,
expected.gate,
];
if identities.contains(&[0; 32])
|| expected_identities.contains(&[0; 32])
|| identities[0] != expected_identities[0]
|| identities[2..7] != expected_identities[1..6]
|| identities[8] != expected_identities[6]
|| identities[9] != expected_identities[7]
{
return Err(DbServiceProofError::InvalidIdentity);
}
let domain = attestation.work_domain();
let expected_domain = DbReturnServiceDomain {
max_ping_commands: u64::from(work.max_ping_commands()),
max_protocol_writes: u64::from(work.max_protocol_writes()),
max_protocol_reads: u64::from(work.max_protocol_reads()),
max_parallel_returns: 1,
pool_profile_attested: work.requires_open_pool()
&& work.requires_no_after_release_hook()
&& work.requires_no_max_lifetime()
&& work.requires_zero_min_connections(),
};
if domain.max_ping_commands == 0
|| domain.max_protocol_writes == 0
|| domain.max_protocol_reads == 0
|| domain.max_parallel_returns != 1
|| !domain.pool_profile_attested
{
return Err(DbServiceProofError::InvalidDomain);
}
if domain != expected_domain {
return Err(DbServiceProofError::DomainMismatch);
}
let rates = attestation.service_rates();
let calibration = attestation.calibration();
if calibration.samples == 0
|| calibration.max_observed_ping_command_nanos == 0
|| calibration.max_observed_protocol_write_nanos == 0
|| calibration.max_observed_protocol_read_nanos == 0
{
return Err(DbServiceProofError::InvalidService);
}
if rates.ping_command_nanos == 0
|| rates.protocol_write_nanos == 0
|| rates.protocol_read_nanos == 0
|| rates.ping_command_nanos < calibration.max_observed_ping_command_nanos
|| rates.protocol_write_nanos < calibration.max_observed_protocol_write_nanos
|| rates.protocol_read_nanos < calibration.max_observed_protocol_read_nanos
{
return Err(DbServiceProofError::InvalidService);
}
Ok(VerifiedDbServiceProof {
target,
work_identity: identities[0],
domain,
calibration,
rates,
calibration_identity: identities[1],
environment_identity: identities[2],
database_server_identity: identities[3],
network_identity: identities[4],
sqlx_identity: identities[5],
target_system_identity: identities[6],
service_attestation: identities[7],
build_identity: identities[8],
gate_identity: identities[9],
})
}
pub fn derive_db_return_service(
verified: VerifiedDbServiceProof,
) -> Result<DbReturnServiceProof, DbServiceProofError> {
if verified.target != DbServiceTarget::LinuxX86_64 {
return Err(DbServiceProofError::InvalidDomain);
}
debug_assert!(verified.calibration.samples > 0);
let ping = verified
.domain
.max_ping_commands
.checked_mul(verified.rates.ping_command_nanos)
.ok_or(DbServiceProofError::Overflow)?;
let writes = verified
.domain
.max_protocol_writes
.checked_mul(verified.rates.protocol_write_nanos)
.ok_or(DbServiceProofError::Overflow)?;
let reads = verified
.domain
.max_protocol_reads
.checked_mul(verified.rates.protocol_read_nanos)
.ok_or(DbServiceProofError::Overflow)?;
let max_service_nanos = ping
.checked_add(writes)
.and_then(|sum| sum.checked_add(reads))
.ok_or(DbServiceProofError::Overflow)?;
Ok(DbReturnServiceProof {
work_identity: verified.work_identity,
max_service_nanos,
calibration_identity: verified.calibration_identity,
environment_identity: verified.environment_identity,
database_server_identity: verified.database_server_identity,
network_identity: verified.network_identity,
sqlx_identity: verified.sqlx_identity,
target_system_identity: verified.target_system_identity,
service_attestation: verified.service_attestation,
build_identity: verified.build_identity,
gate_identity: verified.gate_identity,
})
}
#[cfg(test)]
mod tests {
use super::*;
use crate::c1_normal_return::db_normal_return_work_proof;
#[derive(Clone, Copy)]
struct Attestation {
seed: u8,
rate: u64,
finite: bool,
domain: DbReturnServiceDomain,
}
impl DeploymentDbServiceAttestation for Attestation {
fn target(&self) -> DbServiceTarget {
DbServiceTarget::LinuxX86_64
}
fn approved(&self) -> bool {
true
}
fn reproducible_measurement(&self) -> bool {
true
}
fn conservative_upper_bound(&self) -> bool {
true
}
fn finite_completion(&self) -> bool {
self.finite
}
fn work_identity(&self) -> [u8; 32] {
db_normal_return_work_proof().identity()
}
fn work_domain(&self) -> DbReturnServiceDomain {
self.domain
}
fn service_rates(&self) -> DbReturnServiceRates {
DbReturnServiceRates {
ping_command_nanos: self.rate,
protocol_write_nanos: self.rate,
protocol_read_nanos: self.rate,
}
}
fn calibration(&self) -> DbReturnCalibration {
DbReturnCalibration {
samples: 64,
max_observed_ping_command_nanos: self.rate / 2,
max_observed_protocol_write_nanos: self.rate / 2,
max_observed_protocol_read_nanos: self.rate / 2,
}
}
fn calibration_identity(&self) -> [u8; 32] {
[self.seed.wrapping_add(1); 32]
}
fn environment_identity(&self) -> [u8; 32] {
[self.seed; 32]
}
fn database_server_identity(&self) -> [u8; 32] {
[self.seed.wrapping_add(2); 32]
}
fn network_identity(&self) -> [u8; 32] {
[self.seed.wrapping_add(3); 32]
}
fn sqlx_identity(&self) -> [u8; 32] {
[self.seed.wrapping_add(4); 32]
}
fn target_system_identity(&self) -> [u8; 32] {
[self.seed.wrapping_add(5); 32]
}
fn service_attestation(&self) -> [u8; 32] {
[self.seed.wrapping_add(6); 32]
}
fn build_identity(&self) -> [u8; 32] {
[self.seed.wrapping_add(7); 32]
}
fn gate_identity(&self) -> [u8; 32] {
[self.seed.wrapping_add(8); 32]
}
}
fn domain() -> DbReturnServiceDomain {
DbReturnServiceDomain {
max_ping_commands: 1,
max_protocol_writes: 1,
max_protocol_reads: 1,
max_parallel_returns: 1,
pool_profile_attested: true,
}
}
fn expected(seed: u8) -> DbDeploymentIdentity {
DbDeploymentIdentity {
target: DbServiceTarget::LinuxX86_64,
environment: [seed; 32],
database_server: [seed.wrapping_add(2); 32],
network: [seed.wrapping_add(3); 32],
sqlx: [seed.wrapping_add(4); 32],
target_system: [seed.wrapping_add(5); 32],
build: [seed.wrapping_add(7); 32],
gate: [seed.wrapping_add(8); 32],
}
}
fn verify(seed: u8, rate: u64) -> Result<DbReturnServiceProof, DbServiceProofError> {
derive_db_return_service(verify_db_service(
Attestation {
seed,
rate,
finite: true,
domain: domain(),
},
expected(seed),
db_normal_return_work_proof(),
)?)
}
#[test]
fn normal_and_slow_proofs_are_checked_and_monotone() {
let normal = verify(1, 10).unwrap();
let slow = verify(1, 20).unwrap();
assert_eq!(normal.max_service_nanos(), 30);
assert_eq!(slow.max_service_nanos(), 60);
assert!(slow.max_service_nanos() > normal.max_service_nanos());
assert_eq!(
normal.work_identity(),
db_normal_return_work_proof().identity()
);
assert_eq!(normal.environment_identity(), [1; 32]);
}
#[test]
fn unresponsive_foreign_domain_and_overflow_reject() {
let unresponsive = Attestation {
seed: 1,
rate: 10,
finite: false,
domain: domain(),
};
assert_eq!(
verify_db_service(unresponsive, expected(1), db_normal_return_work_proof())
.unwrap_err(),
DbServiceProofError::InvalidService
);
assert_eq!(
verify_db_service(
Attestation {
seed: 1,
rate: 10,
finite: true,
domain: domain(),
},
expected(2),
db_normal_return_work_proof(),
)
.unwrap_err(),
DbServiceProofError::InvalidIdentity
);
assert_eq!(
verify(1, u64::MAX).unwrap_err(),
DbServiceProofError::Overflow
);
let mut oversized = domain();
oversized.max_protocol_reads = 2;
assert_eq!(
verify_db_service(
Attestation {
seed: 1,
rate: 10,
finite: true,
domain: oversized,
},
expected(1),
db_normal_return_work_proof(),
)
.unwrap_err(),
DbServiceProofError::DomainMismatch
);
}
}