use polyc_proto::proto::polychrome::state::v1 as pb;
use polyc_state::{
command::{CommandEnvelope, CommandMetadata, ResourceBounds},
credentials::{
CredentialActor, CredentialCapabilities, CredentialCommand, CredentialIndex, CredentialKey,
CredentialKeyState, CredentialOperation, CredentialRecord, CredentialRecoveryPremise,
CredentialRequestOutcome, CredentialRequestRecord, credential_scope,
},
digest::ContentDigest,
error::StateError,
id::{Audience, CommandId, NamespaceId, Purpose},
versioned::{EntryExpectation, MAX_MUTATIONS_PER_TRANSACTION, MAX_TRANSACTION_PAYLOAD_BYTES},
};
use crate::wire::{fixed_bytes, malformed, required};
pub(crate) fn key_to_wire(value: &CredentialKey) -> pb::StateCredentialKey {
pb::StateCredentialKey {
key_id: value.key_id().to_owned(),
salt: value.salt().to_owned(),
secret_sha256: value.secret_sha256().to_owned(),
signer_public_key_hex: value.signer_public_key_hex().to_owned(),
state: buffa::EnumValue::Known(match value.state() {
CredentialKeyState::Active => pb::StateCredentialKeyState::Active,
CredentialKeyState::Retiring => pb::StateCredentialKeyState::Retiring,
CredentialKeyState::Revoked => pb::StateCredentialKeyState::Revoked,
}),
activated_at_ms: value.activated_at_ms(),
not_after_ms: value.not_after_ms(),
confirmed_at_ms: value.confirmed_at_ms(),
__buffa_unknown_fields: buffa::UnknownFields::default(),
}
}
pub(crate) fn key_from_wire(value: pb::StateCredentialKey) -> Result<CredentialKey, StateError> {
let state = match value.state {
buffa::EnumValue::Known(pb::StateCredentialKeyState::Active) => CredentialKeyState::Active,
buffa::EnumValue::Known(pb::StateCredentialKeyState::Retiring) => {
CredentialKeyState::Retiring
}
buffa::EnumValue::Known(pb::StateCredentialKeyState::Revoked) => {
CredentialKeyState::Revoked
}
buffa::EnumValue::Known(pb::StateCredentialKeyState::Unspecified)
| buffa::EnumValue::Unknown(_) => {
return Err(malformed(
"key.state",
"credential key lifecycle is recognized",
));
}
};
Ok(CredentialKey::new(
value.key_id,
value.salt,
value.secret_sha256,
value.signer_public_key_hex,
state,
value.activated_at_ms,
value.not_after_ms,
value.confirmed_at_ms,
))
}
pub(crate) fn record_to_wire(value: &CredentialRecord) -> pb::StateCredentialRecord {
pb::StateCredentialRecord {
credential_id: value.credential_id().to_owned(),
principal: value.principal().to_owned(),
keys: value.keys().iter().map(key_to_wire).collect(),
allowed_namespaces: value.allowed_namespaces().to_vec(),
revoked_at_ms: value.revoked_at_ms(),
capabilities: buffa::MessageField::some(pb::StateCredentialCapabilities {
edge: value.capabilities().edge(),
administrator: value.capabilities().administrator(),
attest_email: value.capabilities().attest_email(),
may_assert_direct: value.capabilities().may_assert_direct(),
__buffa_unknown_fields: buffa::UnknownFields::default(),
}),
__buffa_unknown_fields: buffa::UnknownFields::default(),
}
}
pub(crate) fn record_from_wire(
value: pb::StateCredentialRecord,
) -> Result<CredentialRecord, StateError> {
let capabilities = required(
"record.capabilities",
"credential record carries capabilities",
value.capabilities,
)?;
Ok(CredentialRecord::new(
value.credential_id,
value.principal,
value
.keys
.into_iter()
.map(key_from_wire)
.collect::<Result<_, _>>()?,
value.allowed_namespaces,
value.revoked_at_ms,
CredentialCapabilities::new(
capabilities.edge,
capabilities.administrator,
capabilities.attest_email,
capabilities.may_assert_direct,
),
))
}
pub(crate) fn index_to_wire(value: &CredentialIndex) -> pb::StateCredentialIndex {
pb::StateCredentialIndex {
credential_ids: value.credential_ids().to_vec(),
__buffa_unknown_fields: buffa::UnknownFields::default(),
}
}
pub(crate) fn index_from_wire(value: pb::StateCredentialIndex) -> CredentialIndex {
CredentialIndex::new(value.credential_ids)
}
pub(crate) fn request_to_wire(value: &CredentialRequestRecord) -> pb::StateCredentialRequestRecord {
use pb::__buffa::oneof::state_credential_request_outcome::Outcome;
let outcome = match value.outcome() {
CredentialRequestOutcome::Enrolled {
credential_id,
key_id,
} => Outcome::from(pb::StateCredentialEnrolledOutcome {
credential_id: credential_id.clone(),
key_id: key_id.clone(),
__buffa_unknown_fields: buffa::UnknownFields::default(),
}),
CredentialRequestOutcome::Rotated {
credential_id,
key_id,
retiring_key_ids,
} => Outcome::from(pb::StateCredentialRotatedOutcome {
credential_id: credential_id.clone(),
key_id: key_id.clone(),
retiring_key_ids: retiring_key_ids.clone(),
__buffa_unknown_fields: buffa::UnknownFields::default(),
}),
CredentialRequestOutcome::Retired {
credential_id,
key_id,
} => Outcome::from(pb::StateCredentialRetiredOutcome {
credential_id: credential_id.clone(),
key_id: key_id.clone(),
__buffa_unknown_fields: buffa::UnknownFields::default(),
}),
CredentialRequestOutcome::Revoked { credential_id } => {
Outcome::from(pb::StateCredentialRevokedOutcome {
credential_id: credential_id.clone(),
__buffa_unknown_fields: buffa::UnknownFields::default(),
})
}
};
pb::StateCredentialRequestRecord {
operation_id: value.operation_id().to_owned(),
request_digest: value.request_digest().as_bytes().to_vec(),
outcome: buffa::MessageField::some(pb::StateCredentialRequestOutcome {
outcome: Some(outcome),
__buffa_unknown_fields: buffa::UnknownFields::default(),
}),
__buffa_unknown_fields: buffa::UnknownFields::default(),
}
}
pub(crate) fn request_from_wire(
value: pb::StateCredentialRequestRecord,
) -> Result<CredentialRequestRecord, StateError> {
use pb::__buffa::oneof::state_credential_request_outcome::Outcome;
let outcome = required(
"request_record.outcome",
"credential request carries its durable outcome",
value.outcome,
)?;
let outcome = match outcome.outcome {
Some(Outcome::Enrolled(value)) => CredentialRequestOutcome::Enrolled {
credential_id: value.credential_id,
key_id: value.key_id,
},
Some(Outcome::Rotated(value)) => CredentialRequestOutcome::Rotated {
credential_id: value.credential_id,
key_id: value.key_id,
retiring_key_ids: value.retiring_key_ids,
},
Some(Outcome::Retired(value)) => CredentialRequestOutcome::Retired {
credential_id: value.credential_id,
key_id: value.key_id,
},
Some(Outcome::Revoked(value)) => CredentialRequestOutcome::Revoked {
credential_id: value.credential_id,
},
None => {
return Err(malformed(
"request_record.outcome",
"credential request names one durable outcome",
));
}
};
Ok(CredentialRequestRecord::new(
value.operation_id,
ContentDigest::from_bytes(fixed_bytes::<{ ContentDigest::LEN }>(
"request_record.request_digest",
&value.request_digest,
)?),
outcome,
))
}
fn actor_to_wire(value: &CredentialActor) -> pb::StateCredentialActor {
pb::StateCredentialActor {
credential_id: value.credential_id().to_owned(),
key_id: value.key_id().to_owned(),
authenticated_at_ms: value.authenticated_at_ms(),
__buffa_unknown_fields: buffa::UnknownFields::default(),
}
}
fn actor_from_wire(value: pb::StateCredentialActor) -> CredentialActor {
CredentialActor::new(value.credential_id, value.key_id, value.authenticated_at_ms)
}
fn expected_to_wire(value: EntryExpectation) -> pb::StateCredentialExpectedEntry {
use pb::__buffa::oneof::state_credential_expected_entry::Expected;
let expected = match value {
EntryExpectation::Absent => Expected::from(pb::StateCredentialExpectedAbsent::default()),
EntryExpectation::Revision(revision) => {
Expected::from(pb::StateCredentialExpectedRevision {
revision: revision.get(),
__buffa_unknown_fields: buffa::UnknownFields::default(),
})
}
};
pb::StateCredentialExpectedEntry {
expected: Some(expected),
__buffa_unknown_fields: buffa::UnknownFields::default(),
}
}
fn expected_from_wire(
value: pb::StateCredentialExpectedEntry,
) -> Result<EntryExpectation, StateError> {
use pb::__buffa::oneof::state_credential_expected_entry::Expected;
match value.expected {
Some(Expected::Absent(_)) => Ok(EntryExpectation::Absent),
Some(Expected::Revision(value)) if value.revision > 0 => Ok(EntryExpectation::Revision(
polyc_state::revision::Revision::new(value.revision),
)),
Some(Expected::Revision(_)) => {
Err(malformed("expected.revision", "entry revision is nonzero"))
}
None => Err(malformed(
"expected",
"entry premise names absence or a revision",
)),
}
}
#[allow(clippy::too_many_lines)]
pub(crate) fn operation_to_wire(value: &CredentialOperation) -> pb::StateCredentialOperation {
use pb::__buffa::oneof::state_credential_operation::Operation;
let operation = match value {
CredentialOperation::Bootstrap {
records,
index_expected,
} => Operation::from(pb::BootstrapStateCredentials {
records: records.iter().map(record_to_wire).collect(),
index_expected: buffa::MessageField::some(expected_to_wire(*index_expected)),
__buffa_unknown_fields: buffa::UnknownFields::default(),
}),
CredentialOperation::Recover {
observed_at_ms,
records,
existing,
index,
index_expected,
} => Operation::from(pb::RecoverStateCredentials {
observed_at_ms: *observed_at_ms,
records: records.iter().map(record_to_wire).collect(),
existing: existing
.iter()
.map(|premise| pb::StateCredentialRecoveryPremise {
credential_id: premise.credential_id().to_owned(),
expected: buffa::MessageField::some(expected_to_wire(premise.expected())),
__buffa_unknown_fields: buffa::UnknownFields::default(),
})
.collect(),
index: buffa::MessageField::some(index_to_wire(index)),
index_expected: buffa::MessageField::some(expected_to_wire(*index_expected)),
__buffa_unknown_fields: buffa::UnknownFields::default(),
}),
CredentialOperation::Enroll {
actor,
actor_expected,
record,
record_expected,
index,
index_expected,
} => Operation::from(pb::EnrollStateCredential {
actor: buffa::MessageField::some(actor_to_wire(actor)),
actor_expected: buffa::MessageField::some(expected_to_wire(*actor_expected)),
record: buffa::MessageField::some(record_to_wire(record)),
record_expected: buffa::MessageField::some(expected_to_wire(*record_expected)),
index: buffa::MessageField::some(index_to_wire(index)),
index_expected: buffa::MessageField::some(expected_to_wire(*index_expected)),
__buffa_unknown_fields: buffa::UnknownFields::default(),
}),
CredentialOperation::Rotate {
actor,
actor_expected,
record,
record_expected,
} => Operation::from(pb::RotateStateCredential {
actor: buffa::MessageField::some(actor_to_wire(actor)),
actor_expected: buffa::MessageField::some(expected_to_wire(*actor_expected)),
record: buffa::MessageField::some(record_to_wire(record)),
record_expected: buffa::MessageField::some(expected_to_wire(*record_expected)),
__buffa_unknown_fields: buffa::UnknownFields::default(),
}),
CredentialOperation::Retire {
actor,
actor_expected,
record,
record_expected,
} => Operation::from(pb::RetireStateCredentialKey {
actor: buffa::MessageField::some(actor_to_wire(actor)),
actor_expected: buffa::MessageField::some(expected_to_wire(*actor_expected)),
record: buffa::MessageField::some(record_to_wire(record)),
record_expected: buffa::MessageField::some(expected_to_wire(*record_expected)),
__buffa_unknown_fields: buffa::UnknownFields::default(),
}),
CredentialOperation::Revoke {
actor,
actor_expected,
record,
record_expected,
} => Operation::from(pb::RevokeStateCredential {
actor: buffa::MessageField::some(actor_to_wire(actor)),
actor_expected: buffa::MessageField::some(expected_to_wire(*actor_expected)),
record: buffa::MessageField::some(record_to_wire(record)),
record_expected: buffa::MessageField::some(expected_to_wire(*record_expected)),
__buffa_unknown_fields: buffa::UnknownFields::default(),
}),
CredentialOperation::Confirm {
credential_id,
key_id,
confirmed_at_ms,
record,
record_expected,
} => Operation::from(pb::ConfirmStateCredentialKey {
credential_id: credential_id.clone(),
key_id: key_id.clone(),
confirmed_at_ms: *confirmed_at_ms,
record: buffa::MessageField::some(record_to_wire(record)),
record_expected: buffa::MessageField::some(expected_to_wire(*record_expected)),
__buffa_unknown_fields: buffa::UnknownFields::default(),
}),
};
pb::StateCredentialOperation {
operation: Some(operation),
__buffa_unknown_fields: buffa::UnknownFields::default(),
}
}
#[allow(clippy::too_many_lines)]
pub(crate) fn operation_from_wire(
value: pb::StateCredentialOperation,
) -> Result<CredentialOperation, StateError> {
use pb::__buffa::oneof::state_credential_operation::Operation;
Ok(match value.operation {
Some(Operation::Bootstrap(value)) => CredentialOperation::Bootstrap {
records: value
.records
.into_iter()
.map(record_from_wire)
.collect::<Result<_, _>>()?,
index_expected: expected_from_wire(required(
"index_expected",
"bootstrap carries the index premise",
value.index_expected,
)?)?,
},
Some(Operation::Recover(value)) => CredentialOperation::Recover {
observed_at_ms: value.observed_at_ms,
records: value
.records
.into_iter()
.map(record_from_wire)
.collect::<Result<_, _>>()?,
existing: value
.existing
.into_iter()
.map(|premise| {
Ok(CredentialRecoveryPremise::new(
premise.credential_id,
expected_from_wire(required(
"existing.expected",
"recovery premise carries its row revision",
premise.expected,
)?)?,
))
})
.collect::<Result<_, StateError>>()?,
index: index_from_wire(required(
"index",
"recovery carries the complete next index",
value.index,
)?),
index_expected: expected_from_wire(required(
"index_expected",
"recovery carries the index premise",
value.index_expected,
)?)?,
},
Some(Operation::Enroll(value)) => CredentialOperation::Enroll {
actor: actor_from_wire(required(
"actor",
"enrollment carries its actor",
value.actor,
)?),
actor_expected: expected_from_wire(required(
"actor_expected",
"enrollment carries the actor premise",
value.actor_expected,
)?)?,
record: record_from_wire(required(
"record",
"enrollment carries its record",
value.record,
)?)?,
record_expected: expected_from_wire(required(
"record_expected",
"enrollment carries the target premise",
value.record_expected,
)?)?,
index: index_from_wire(required(
"index",
"enrollment carries the next index",
value.index,
)?),
index_expected: expected_from_wire(required(
"index_expected",
"enrollment carries the index premise",
value.index_expected,
)?)?,
},
Some(Operation::Rotate(value)) => CredentialOperation::Rotate {
actor: actor_from_wire(required(
"actor",
"rotation carries its actor",
value.actor,
)?),
actor_expected: expected_from_wire(required(
"actor_expected",
"rotation carries the actor premise",
value.actor_expected,
)?)?,
record: record_from_wire(required(
"record",
"rotation carries its record",
value.record,
)?)?,
record_expected: expected_from_wire(required(
"record_expected",
"rotation carries the target premise",
value.record_expected,
)?)?,
},
Some(Operation::Retire(value)) => CredentialOperation::Retire {
actor: actor_from_wire(required(
"actor",
"retirement carries its actor",
value.actor,
)?),
actor_expected: expected_from_wire(required(
"actor_expected",
"retirement carries the actor premise",
value.actor_expected,
)?)?,
record: record_from_wire(required(
"record",
"retirement carries its record",
value.record,
)?)?,
record_expected: expected_from_wire(required(
"record_expected",
"retirement carries the target premise",
value.record_expected,
)?)?,
},
Some(Operation::Revoke(value)) => CredentialOperation::Revoke {
actor: actor_from_wire(required(
"actor",
"revocation carries its actor",
value.actor,
)?),
actor_expected: expected_from_wire(required(
"actor_expected",
"revocation carries the actor premise",
value.actor_expected,
)?)?,
record: record_from_wire(required(
"record",
"revocation carries its tombstone",
value.record,
)?)?,
record_expected: expected_from_wire(required(
"record_expected",
"revocation carries the target premise",
value.record_expected,
)?)?,
},
Some(Operation::Confirm(value)) => CredentialOperation::Confirm {
credential_id: value.credential_id,
key_id: value.key_id,
confirmed_at_ms: value.confirmed_at_ms,
record: record_from_wire(required(
"record",
"confirmation carries its record",
value.record,
)?)?,
record_expected: expected_from_wire(required(
"record_expected",
"confirmation carries the target premise",
value.record_expected,
)?)?,
},
None => {
return Err(malformed(
"operation",
"credential command names one operation",
));
}
})
}
pub(crate) fn metadata_to_wire(command: &CredentialCommand) -> pb::StateCredentialCommandMetadata {
let value = command.metadata();
pb::StateCredentialCommandMetadata {
command_id: value.command_id().as_str().to_owned(),
namespace: value.scope().namespace().as_str().to_owned(),
purpose: value.envelope().purpose().as_str().to_owned(),
command_audience: value.envelope().audience().as_str().to_owned(),
digest: value.digest().as_bytes().to_vec(),
__buffa_unknown_fields: buffa::UnknownFields::default(),
}
}
pub(crate) fn command_from_wire(
metadata: pb::StateCredentialCommandMetadata,
operation: pb::StateCredentialOperation,
request: Option<pb::StateCredentialRequestRecord>,
) -> Result<CredentialCommand, StateError> {
let namespace = NamespaceId::new(metadata.namespace);
let command = CredentialCommand::new(
CommandMetadata::new(
CommandId::new(metadata.command_id),
polyc_state::versioned::family(),
ContentDigest::from_bytes(fixed_bytes::<{ ContentDigest::LEN }>(
"digest",
&metadata.digest,
)?),
credential_scope(&namespace),
CommandEnvelope::new(
Purpose::new(metadata.purpose),
Audience::new(metadata.command_audience),
ResourceBounds::new(MAX_TRANSACTION_PAYLOAD_BYTES, MAX_MUTATIONS_PER_TRANSACTION),
),
),
operation_from_wire(operation)?,
);
Ok(match request {
Some(request) => command.with_request(request_from_wire(request)?),
None => command,
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn unknown_key_state_is_refused() {
let error = key_from_wire(pb::StateCredentialKey {
state: buffa::EnumValue::Unknown(99),
..Default::default()
})
.expect_err("unknown lifecycle must fail closed");
assert!(matches!(error, StateError::Malformed { .. }));
}
}