use super::ScratchpadAddress;
use crate::error::{Error, Result};
use crate::Bytes;
use crate::NetworkAddress;
use bls::{Ciphertext, PublicKey, SecretKey, Signature};
use serde::{Deserialize, Serialize};
use xor_name::XorName;
#[derive(Hash, Eq, PartialEq, PartialOrd, Ord, Clone, Serialize, Deserialize)]
pub struct Scratchpad {
address: ScratchpadAddress,
data_encoding: u64,
encrypted_data: Bytes,
counter: u64,
signature: Signature,
}
impl std::fmt::Debug for Scratchpad {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Scratchpad")
.field("address", &self.address)
.field("data_encoding", &self.data_encoding)
.field(
"encrypted_data",
&format!("({} bytes of encrypted data)", self.encrypted_data.len()),
)
.field("counter", &self.counter)
.field("signature", &hex::encode(self.signature.to_bytes()))
.finish()
}
}
impl Scratchpad {
pub const MAX_SIZE: usize = 4 * 1024 * 1024;
pub fn new(
owner: &SecretKey,
data_encoding: u64,
unencrypted_data: &Bytes,
counter: u64,
) -> Self {
let pk = owner.public_key();
let encrypted_data = Bytes::from(pk.encrypt(unencrypted_data).to_bytes());
let addr = ScratchpadAddress::new(pk);
let signature = owner.sign(Self::bytes_for_signature(
addr,
data_encoding,
&encrypted_data,
counter,
));
Self {
address: addr,
encrypted_data,
data_encoding,
counter,
signature,
}
}
pub fn new_with_signature(
owner: PublicKey,
data_encoding: u64,
encrypted_data: Bytes,
counter: u64,
signature: Signature,
) -> Self {
Self {
address: ScratchpadAddress::new(owner),
encrypted_data,
data_encoding,
counter,
signature,
}
}
pub fn bytes_for_signature(
address: ScratchpadAddress,
data_encoding: u64,
encrypted_data: &Bytes,
counter: u64,
) -> Vec<u8> {
let mut bytes_to_sign = data_encoding.to_be_bytes().to_vec();
bytes_to_sign.extend(address.to_hex().as_bytes());
bytes_to_sign.extend(counter.to_be_bytes().to_vec());
bytes_to_sign.extend(encrypted_data.to_vec());
bytes_to_sign
}
pub fn counter(&self) -> u64 {
self.counter
}
pub fn data_encoding(&self) -> u64 {
self.data_encoding
}
pub fn update(&mut self, unencrypted_data: &Bytes, sk: &SecretKey) {
self.counter += 1;
let pk = self.owner();
let address = ScratchpadAddress::new(*pk);
self.encrypted_data = Bytes::from(pk.encrypt(unencrypted_data).to_bytes());
let bytes_to_sign = Self::bytes_for_signature(
address,
self.data_encoding,
&self.encrypted_data,
self.counter,
);
self.signature = sk.sign(&bytes_to_sign);
debug_assert!(self.verify_signature(), "Must be valid after being signed. This is a bug, please report it by opening an issue on our github");
}
pub fn verify_signature(&self) -> bool {
let signing_bytes = Self::bytes_for_signature(
self.address,
self.data_encoding,
&self.encrypted_data,
self.counter,
);
self.owner().verify(&self.signature, &signing_bytes)
}
pub fn encrypted_data(&self) -> &Bytes {
&self.encrypted_data
}
pub fn decrypt_data(&self, sk: &SecretKey) -> Result<Bytes> {
let cipher = Ciphertext::from_bytes(&self.encrypted_data)
.map_err(|_| Error::ScratchpadCipherTextFailed)?;
let bytes = sk
.decrypt(&cipher)
.ok_or(Error::ScratchpadCipherTextInvalid)?;
Ok(Bytes::from(bytes))
}
pub fn encrypted_data_hash(&self) -> XorName {
XorName::from_content(&self.encrypted_data)
}
pub fn owner(&self) -> &PublicKey {
self.address.owner()
}
pub fn address(&self) -> &ScratchpadAddress {
&self.address
}
pub fn network_address(&self) -> NetworkAddress {
NetworkAddress::ScratchpadAddress(self.address)
}
pub fn xorname(&self) -> XorName {
self.address.xorname()
}
pub fn payload_size(&self) -> usize {
self.encrypted_data.len()
}
pub fn size(&self) -> usize {
size_of::<Scratchpad>() + self.payload_size()
}
pub fn is_too_big(&self) -> bool {
self.size() > Self::MAX_SIZE
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_scratchpad_sig_and_update() {
let sk = SecretKey::random();
let raw_data = Bytes::from_static(b"data to be encrypted");
let mut scratchpad = Scratchpad::new(&sk, 42, &raw_data, 0);
assert!(scratchpad.verify_signature());
assert_eq!(scratchpad.counter(), 0);
assert_ne!(scratchpad.encrypted_data(), &raw_data);
let raw_data2 = Bytes::from_static(b"data to be encrypted v2");
scratchpad.update(&raw_data2, &sk);
assert!(scratchpad.verify_signature());
assert_eq!(scratchpad.counter(), 1);
assert_ne!(scratchpad.encrypted_data(), &raw_data);
assert_ne!(scratchpad.encrypted_data(), &raw_data2);
}
#[test]
fn test_scratchpad_encryption() {
let sk = SecretKey::random();
let raw_data = Bytes::from_static(b"data to be encrypted");
let scratchpad = Scratchpad::new(&sk, 42, &raw_data, 0);
let decrypted_data = scratchpad.decrypt_data(&sk).unwrap();
assert_eq!(decrypted_data, raw_data);
}
}