use super::{Certificate, CERTIFICATE_FIELD_ENCRYPTION_PROTOCOL};
use crate::primitives::PublicKey;
use crate::wallet::{Counterparty, DecryptArgs, Protocol, SecurityLevel, WalletInterface};
use crate::{Error, Result};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct VerifiableCertificate {
#[serde(flatten)]
pub certificate: Certificate,
#[serde(default)]
pub keyring: HashMap<String, Vec<u8>>,
#[serde(skip)]
decrypted_fields: Option<HashMap<String, String>>,
}
impl VerifiableCertificate {
pub fn new(certificate: Certificate, keyring: HashMap<String, Vec<u8>>) -> Self {
Self {
certificate,
keyring,
decrypted_fields: None,
}
}
pub fn from_certificate(certificate: Certificate) -> Self {
Self {
certificate,
keyring: HashMap::new(),
decrypted_fields: None,
}
}
pub fn has_keyring(&self) -> bool {
!self.keyring.is_empty()
}
pub fn revealable_fields(&self) -> Vec<&String> {
self.keyring.keys().collect()
}
pub async fn decrypt_field<W: WalletInterface>(
&self,
verifier_wallet: &W,
subject: &PublicKey,
field_name: &str,
originator: &str,
) -> Result<String> {
let encrypted_key = self.keyring.get(field_name).ok_or_else(|| {
Error::AuthError(format!(
"Field '{}' not in keyring (not revealed for this verifier)",
field_name
))
})?;
let protocol = Protocol::new(
SecurityLevel::Counterparty,
CERTIFICATE_FIELD_ENCRYPTION_PROTOCOL,
);
let key_id = Certificate::get_field_encryption_key_id_verifiable(
field_name,
&self.certificate.serial_number,
);
let decrypted = verifier_wallet
.decrypt(
DecryptArgs {
ciphertext: encrypted_key.clone(),
protocol_id: protocol,
key_id,
counterparty: Some(Counterparty::Other(subject.clone())),
},
originator,
)
.await?;
String::from_utf8(decrypted.plaintext).map_err(|e| Error::InvalidUtf8(e.to_string()))
}
pub async fn decrypt_fields<W: WalletInterface>(
&mut self,
verifier_wallet: &W,
subject: &PublicKey,
originator: &str,
) -> Result<HashMap<String, String>> {
if let Some(ref cached) = self.decrypted_fields {
return Ok(cached.clone());
}
let mut decrypted = HashMap::new();
for field_name in self.keyring.keys() {
let value = self
.decrypt_field(verifier_wallet, subject, field_name, originator)
.await?;
decrypted.insert(field_name.clone(), value);
}
self.decrypted_fields = Some(decrypted.clone());
Ok(decrypted)
}
pub fn get_decrypted_fields(&self) -> Option<&HashMap<String, String>> {
self.decrypted_fields.as_ref()
}
pub fn clear_decrypted_cache(&mut self) {
self.decrypted_fields = None;
}
pub fn verify(&self) -> Result<bool> {
self.certificate.verify()
}
pub fn subject(&self) -> &PublicKey {
&self.certificate.subject
}
pub fn certifier(&self) -> &PublicKey {
&self.certificate.certifier
}
pub fn cert_type(&self) -> &[u8; 32] {
&self.certificate.cert_type
}
pub fn serial_number(&self) -> &[u8; 32] {
&self.certificate.serial_number
}
pub fn to_json_value(&self) -> serde_json::Value {
serde_json::json!({
"type": self.certificate.type_base64(),
"serialNumber": self.certificate.serial_number_base64(),
"subject": self.certificate.subject.to_hex(),
"certifier": self.certificate.certifier.to_hex(),
"revocationOutpoint": self.certificate.revocation_outpoint.as_ref().map(|o| o.to_string()),
"fields": self.certificate.fields.iter()
.map(|(k, v)| (k.clone(), crate::primitives::to_base64(v)))
.collect::<HashMap<String, String>>(),
"signature": self.certificate.signature.as_ref().map(|s| crate::primitives::to_base64(s)),
"keyring": self.keyring.iter()
.map(|(k, v)| (k.clone(), crate::primitives::to_base64(v)))
.collect::<HashMap<String, String>>(),
})
}
}
impl std::ops::Deref for VerifiableCertificate {
type Target = Certificate;
fn deref(&self) -> &Self::Target {
&self.certificate
}
}
impl From<Certificate> for VerifiableCertificate {
fn from(certificate: Certificate) -> Self {
Self::from_certificate(certificate)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::primitives::PrivateKey;
#[test]
fn test_verifiable_certificate_creation() {
let certifier = PrivateKey::random();
let subject = PrivateKey::random().public_key();
let mut cert = Certificate::new(
[1u8; 32],
[2u8; 32],
subject.clone(),
certifier.public_key(),
);
cert.sign(&certifier).unwrap();
let mut keyring = HashMap::new();
keyring.insert("name".to_string(), vec![1, 2, 3]);
let verifiable = VerifiableCertificate::new(cert, keyring);
assert!(verifiable.verify().unwrap());
assert!(verifiable.has_keyring());
assert_eq!(verifiable.revealable_fields().len(), 1);
}
#[test]
fn test_from_certificate() {
let certifier = PrivateKey::random();
let subject = PrivateKey::random().public_key();
let mut cert = Certificate::new([1u8; 32], [2u8; 32], subject, certifier.public_key());
cert.sign(&certifier).unwrap();
let verifiable = VerifiableCertificate::from_certificate(cert);
assert!(verifiable.verify().unwrap());
assert!(!verifiable.has_keyring());
}
#[test]
fn test_deref_to_certificate() {
let certifier = PrivateKey::random();
let subject = PrivateKey::random().public_key();
let mut cert = Certificate::new(
[1u8; 32],
[2u8; 32],
subject.clone(),
certifier.public_key(),
);
cert.sign(&certifier).unwrap();
let verifiable = VerifiableCertificate::from_certificate(cert);
assert_eq!(verifiable.subject, subject);
assert_eq!(verifiable.cert_type, [1u8; 32]);
}
#[test]
fn test_json_serialization() {
let certifier = PrivateKey::random();
let subject = PrivateKey::random().public_key();
let mut cert = Certificate::new([1u8; 32], [2u8; 32], subject, certifier.public_key());
cert.fields.insert("email".to_string(), vec![4, 5, 6]);
cert.sign(&certifier).unwrap();
let mut keyring = HashMap::new();
keyring.insert("email".to_string(), vec![7, 8, 9]);
let verifiable = VerifiableCertificate::new(cert, keyring);
let json = verifiable.to_json_value();
assert!(json.get("type").is_some());
assert!(json.get("serialNumber").is_some());
assert!(json.get("keyring").is_some());
}
}