use axum::Json;
use axum::extract::State;
use rand_core_06::OsRng;
use serde::{Deserialize, Serialize};
use crate::server::admin_service::error::ApiError;
use crate::server::admin_service::{ValidatorAdminService, decode_hex};
use crate::{PrivateRecordContext, PrivateRecordError};
#[derive(Clone, Debug, Deserialize, Serialize)]
pub(super) struct IssueDecryptionShareRequest {
pub(super) ciphertext: String,
pub(super) decryption_context: String,
}
#[derive(Debug, Deserialize, Serialize)]
pub(super) struct IssueDecryptionShareResponse {
pub(super) decryption_share: String,
}
pub(super) async fn issue_decryption_share(
State(service): State<ValidatorAdminService>,
Json(request): Json<IssueDecryptionShareRequest>,
) -> Result<Json<IssueDecryptionShareResponse>, ApiError> {
let ciphertext = decode_hex("ciphertext", &request.ciphertext)?;
let decryption_context = decode_hex("decryption_context", &request.decryption_context)?;
let context = PrivateRecordContext::try_from_bytes(&decryption_context)
.map_err(|error| map_share_error(&error))?;
if context.key_epoch() != service.operator_key.key_epoch() {
return Err(map_share_error(&PrivateRecordError::KeyEpochMismatch));
}
let validated = service
.reader
.transaction_exists(context.transaction_id())
.await
.map_err(|error| ApiError::internal("failed to look up the transaction", &error))?;
if !validated {
return Err(ApiError::not_found(
"decryption context references a transaction this validator has not validated",
));
}
let decryption_share = service
.operator_key
.issue_decryption_share(&mut OsRng, &ciphertext, &decryption_context)
.map_err(|error| map_share_error(&error))?;
Ok(Json(IssueDecryptionShareResponse {
decryption_share: hex::encode(decryption_share),
}))
}
fn map_share_error(error: &PrivateRecordError) -> ApiError {
match error {
PrivateRecordError::InvalidGoldenEncoding(_)
| PrivateRecordError::InvalidEncryptedRecordKey
| PrivateRecordError::MalformedDecryptionContext
| PrivateRecordError::KeyEpochMismatch
| PrivateRecordError::DecryptionContextMismatch => ApiError::bad_request(error.to_string()),
PrivateRecordError::RecordIdMismatch
| PrivateRecordError::InvalidValidatorId(_)
| PrivateRecordError::SetupContextMismatch
| PrivateRecordError::RecordEncryption
| PrivateRecordError::ContentKeyEncryption(_)
| PrivateRecordError::InvalidCombinerSetup(_)
| PrivateRecordError::InvalidDecryptionShare(_)
| PrivateRecordError::ShareGeneration(_)
| PrivateRecordError::ShareCombination(_)
| PrivateRecordError::UnsupportedFormat(_)
| PrivateRecordError::InvalidNonceLength { .. }
| PrivateRecordError::InvalidRecordCiphertext
| PrivateRecordError::RecordDecryption => {
ApiError::internal("failed to issue Golden decryption share", error)
},
}
}