use super::{Certificate, CERTIFICATE_FIELD_ENCRYPTION_PROTOCOL};
use crate::primitives::PublicKey;
use crate::wallet::{
Counterparty, DecryptArgs, EncryptArgs, 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 MasterCertificate {
#[serde(flatten)]
pub certificate: Certificate,
#[serde(default)]
pub master_keyring: HashMap<String, Vec<u8>>,
}
impl MasterCertificate {
pub fn new(certificate: Certificate, master_keyring: HashMap<String, Vec<u8>>) -> Self {
Self {
certificate,
master_keyring,
}
}
pub async fn create_certificate_fields<W: WalletInterface>(
creator_wallet: &W,
subject: &PublicKey,
fields: HashMap<String, String>,
_serial_number: &[u8; 32],
originator: &str,
) -> Result<(HashMap<String, Vec<u8>>, HashMap<String, Vec<u8>>)> {
let protocol = Protocol::new(
SecurityLevel::Counterparty,
CERTIFICATE_FIELD_ENCRYPTION_PROTOCOL,
);
let mut encrypted_fields = HashMap::new();
let mut master_keyring = HashMap::new();
for (field_name, plain_value) in fields {
let key_id = Certificate::get_field_encryption_key_id_master(&field_name);
let encrypt_result = creator_wallet
.encrypt(
EncryptArgs {
plaintext: plain_value.as_bytes().to_vec(),
protocol_id: protocol.clone(),
key_id: key_id.clone(),
counterparty: Some(Counterparty::Other(subject.clone())),
},
originator,
)
.await?;
encrypted_fields.insert(field_name.clone(), encrypt_result.ciphertext.clone());
master_keyring.insert(field_name, encrypt_result.ciphertext);
}
Ok((encrypted_fields, master_keyring))
}
pub async fn create_keyring_for_verifier<W: WalletInterface>(
subject_wallet: &W,
certifier: &PublicKey,
verifier: &PublicKey,
fields_to_reveal: &[String],
encrypted_fields: &HashMap<String, Vec<u8>>,
serial_number: &[u8; 32],
originator: &str,
) -> Result<HashMap<String, Vec<u8>>> {
let protocol = Protocol::new(
SecurityLevel::Counterparty,
CERTIFICATE_FIELD_ENCRYPTION_PROTOCOL,
);
let mut keyring = HashMap::new();
for field_name in fields_to_reveal {
let encrypted_value = encrypted_fields.get(field_name).ok_or_else(|| {
Error::AuthError(format!("Field '{}' not found in certificate", field_name))
})?;
let master_key_id = Certificate::get_field_encryption_key_id_master(field_name);
let decrypted = subject_wallet
.decrypt(
DecryptArgs {
ciphertext: encrypted_value.clone(),
protocol_id: protocol.clone(),
key_id: master_key_id,
counterparty: Some(Counterparty::Other(certifier.clone())),
},
originator,
)
.await?;
let verifiable_key_id =
Certificate::get_field_encryption_key_id_verifiable(field_name, serial_number);
let re_encrypted = subject_wallet
.encrypt(
EncryptArgs {
plaintext: decrypted.plaintext,
protocol_id: protocol.clone(),
key_id: verifiable_key_id,
counterparty: Some(Counterparty::Other(verifier.clone())),
},
originator,
)
.await?;
keyring.insert(field_name.clone(), re_encrypted.ciphertext);
}
Ok(keyring)
}
pub async fn issue_for_subject<W: WalletInterface>(
certifier_wallet: &W,
certifier_key: &crate::primitives::PrivateKey,
subject: PublicKey,
plain_fields: HashMap<String, String>,
cert_type: [u8; 32],
serial_number: Option<[u8; 32]>,
originator: &str,
) -> Result<Self> {
let serial_number = serial_number.unwrap_or_else(|| {
let mut serial = [0u8; 32];
use rand::RngCore;
rand::thread_rng().fill_bytes(&mut serial);
serial
});
let (encrypted_fields, master_keyring) = Self::create_certificate_fields(
certifier_wallet,
&subject,
plain_fields,
&serial_number,
originator,
)
.await?;
let mut certificate = Certificate::new(
cert_type,
serial_number,
subject,
certifier_key.public_key(),
);
certificate.fields = encrypted_fields;
certificate.sign(certifier_key)?;
Ok(Self {
certificate,
master_keyring,
})
}
pub async fn decrypt_field<W: WalletInterface>(
&self,
subject_wallet: &W,
certifier: &PublicKey,
field_name: &str,
originator: &str,
) -> Result<String> {
let encrypted_value = self.certificate.fields.get(field_name).ok_or_else(|| {
Error::AuthError(format!("Field '{}' not found in certificate", field_name))
})?;
let protocol = Protocol::new(
SecurityLevel::Counterparty,
CERTIFICATE_FIELD_ENCRYPTION_PROTOCOL,
);
let key_id = Certificate::get_field_encryption_key_id_master(field_name);
let decrypted = subject_wallet
.decrypt(
DecryptArgs {
ciphertext: encrypted_value.clone(),
protocol_id: protocol,
key_id,
counterparty: Some(Counterparty::Other(certifier.clone())),
},
originator,
)
.await?;
String::from_utf8(decrypted.plaintext).map_err(|e| Error::InvalidUtf8(e.to_string()))
}
pub async fn decrypt_fields<W: WalletInterface>(
&self,
subject_wallet: &W,
certifier: &PublicKey,
originator: &str,
) -> Result<HashMap<String, String>> {
let mut decrypted = HashMap::new();
for field_name in self.certificate.fields.keys() {
let value = self
.decrypt_field(subject_wallet, certifier, field_name, originator)
.await?;
decrypted.insert(field_name.clone(), value);
}
Ok(decrypted)
}
pub fn verify(&self) -> Result<bool> {
self.certificate.verify()
}
}
impl std::ops::Deref for MasterCertificate {
type Target = Certificate;
fn deref(&self) -> &Self::Target {
&self.certificate
}
}
impl std::ops::DerefMut for MasterCertificate {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.certificate
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_master_certificate_creation() {
let certifier = crate::primitives::PrivateKey::random();
let subject = crate::primitives::PrivateKey::random().public_key();
let mut cert = Certificate::new([1u8; 32], [2u8; 32], subject, certifier.public_key());
cert.sign(&certifier).unwrap();
let master_keyring = HashMap::new();
let master_cert = MasterCertificate::new(cert, master_keyring);
assert!(master_cert.verify().unwrap());
}
#[test]
fn test_deref_to_certificate() {
let certifier = crate::primitives::PrivateKey::random();
let subject = crate::primitives::PrivateKey::random().public_key();
let mut cert = Certificate::new(
[1u8; 32],
[2u8; 32],
subject.clone(),
certifier.public_key(),
);
cert.sign(&certifier).unwrap();
let master_cert = MasterCertificate::new(cert, HashMap::new());
assert_eq!(master_cert.subject, subject);
assert_eq!(master_cert.cert_type, [1u8; 32]);
}
}