#![warn(
missing_debug_implementations,
missing_docs,
rust_2018_idioms,
unreachable_pub
)]
#[allow(unreachable_pub, missing_docs)]
mod models;
pub mod stamp;
use std::convert::TryInto;
use aes::{
block_cipher::generic_array::{typenum::U16, GenericArray},
Aes128,
};
use bitcoin::transaction::Transaction;
use block_modes::{block_padding::Pkcs7, BlockMode, BlockModeError, Cbc};
use prost::{DecodeError as MessageDecodeError, Message as _};
use ring::{
digest::{digest, SHA256},
hmac::{sign, Key as HmacKey, HMAC_SHA256},
};
use secp256k1::{key::PublicKey, Error as SecpError, Secp256k1};
use thiserror::Error;
pub mod secp {
pub use secp256k1::{
key::{PublicKey, SecretKey as PrivateKey},
Error as SecpError, Secp256k1,
};
}
pub use crate::models::{
message::EncryptionScheme, Message, MessagePage, MessageSet, Payload, PayloadPage, Profile,
};
use stamp::*;
type Aes128Cbc = Cbc<Aes128, Pkcs7>;
#[derive(Debug, Clone, PartialEq)]
pub struct ParsedMessage {
pub source_public_key: PublicKey,
pub destination_public_key: PublicKey,
pub received_time: i64,
pub payload_digest: [u8; 32],
pub stamp: Stamp,
pub scheme: EncryptionScheme,
pub salt: Vec<u8>,
pub payload_hmac: [u8; 32],
pub payload_size: u64,
pub payload: Vec<u8>,
}
impl ParsedMessage {
pub fn into_message(self) -> Message {
Message {
source_public_key: self.source_public_key.serialize().to_vec(),
destination_public_key: self.destination_public_key.serialize().to_vec(),
received_time: self.received_time,
payload_digest: self.payload_digest.to_vec(),
stamp: Some(self.stamp),
scheme: self.scheme.into(),
salt: self.salt,
payload_hmac: self.payload_hmac.to_vec(),
payload_size: self.payload_size,
payload: self.payload,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Error)]
pub enum ParseError {
#[error(transparent)]
Digest(DigestError),
#[error("source public key: {0}")]
SourcePublicKey(SecpError),
#[error("destination public key: {0}")]
DestinationPublicKey(SecpError),
#[error("missing stamp")]
MissingStamp,
#[error("unsupported stamp type")]
UnsupportedStampType,
#[error("unexpected length payload hmac")]
UnexpectedLengthPayloadHmac,
}
#[derive(Debug, Clone, PartialEq, Eq, Error)]
pub enum DigestError {
#[error("digest and payload missing")]
DigestAndPayloadMissing,
#[error("fraudulent digest")]
FraudulentDigest,
#[error("unexpected length digest")]
UnexpectedLengthDigest,
}
impl Message {
#[inline]
pub fn digest(&self) -> Result<[u8; 32], DigestError> {
let payload_digest: [u8; 32] = match self.payload_digest.len() {
0 => {
if self.payload.is_empty() {
return Err(DigestError::DigestAndPayloadMissing);
}
let payload_digest: [u8; 32] =
digest(&SHA256, &self.payload).as_ref().try_into().unwrap();
payload_digest
}
32 => {
if !self.payload.is_empty() {
let payload_digest: [u8; 32] =
digest(&SHA256, &self.payload).as_ref().try_into().unwrap();
if payload_digest[..] != self.payload_digest[..] {
return Err(DigestError::FraudulentDigest);
}
payload_digest
} else {
let slice = &self.payload_digest[..];
slice.try_into().unwrap()
}
}
_ => return Err(DigestError::UnexpectedLengthDigest),
};
Ok(payload_digest)
}
#[inline]
pub fn parse(self) -> Result<ParsedMessage, ParseError> {
let source_public_key =
PublicKey::from_slice(&self.source_public_key).map_err(ParseError::SourcePublicKey)?;
let destination_public_key = PublicKey::from_slice(&self.destination_public_key)
.map_err(ParseError::DestinationPublicKey)?;
let payload_digest = self.digest().map_err(ParseError::Digest)?;
let stamp = self.stamp.ok_or(ParseError::MissingStamp)?;
let scheme =
EncryptionScheme::from_i32(self.scheme).ok_or(ParseError::UnsupportedStampType)?;
let payload_hmac: [u8; 32] = self.payload_hmac[..]
.try_into()
.map_err(|_| ParseError::UnexpectedLengthPayloadHmac)?;
Ok(ParsedMessage {
source_public_key,
destination_public_key,
received_time: self.received_time,
payload_digest,
stamp,
scheme,
salt: self.salt,
payload_hmac,
payload_size: self.payload_size,
payload: self.payload,
})
}
}
#[inline]
pub fn create_merged_key(
source_public_key: PublicKey,
private_key: &[u8],
) -> Result<PublicKey, SecpError> {
let mut merged_key = source_public_key;
merged_key.mul_assign(&Secp256k1::verification_only(), private_key)?;
Ok(merged_key)
}
#[inline]
pub fn create_shared_key(
source_public_key: PublicKey,
private_key: &[u8],
salt: &[u8],
) -> Result<[u8; 32], SecpError> {
let merged_key = create_merged_key(source_public_key, private_key)?;
let raw_merged_key = merged_key.serialize();
let key = HmacKey::new(HMAC_SHA256, &raw_merged_key);
let digest = sign(&key, salt);
let shared_key: [u8; 32] = digest.as_ref().try_into().unwrap();
Ok(shared_key)
}
#[derive(Debug, Clone, PartialEq, Error)]
#[error("invalid hmac")]
pub struct InvalidHmac;
#[inline]
pub fn authenticate(
shared_key: &[u8],
payload_digest: &[u8],
payload_hmac: &[u8],
) -> Result<(), InvalidHmac> {
let shared_key = HmacKey::new(HMAC_SHA256, shared_key);
let payload_hmac_expected = sign(&shared_key, payload_digest);
if payload_hmac_expected.as_ref() != payload_hmac {
return Err(InvalidHmac);
}
Ok(())
}
#[derive(Debug, Clone, PartialEq)]
pub struct Opened {
pub txs: Vec<Transaction>,
pub payload: Payload,
}
#[derive(Debug, Clone, Error)]
pub enum OpenError {
#[error("stamp errror: {0}")]
Stamp(StampError),
#[error("shared key: {0}")]
SharedKey(SecpError),
#[error("authentication failed")]
Authentication,
#[error("payload decoding failure: {0}")]
Payload(MessageDecodeError),
#[error("decryption failure: {0}")]
Decrypt(BlockModeError),
}
impl ParsedMessage {
#[inline]
pub fn create_merged_key(&self, private_key: &[u8]) -> Result<PublicKey, SecpError> {
create_merged_key(self.source_public_key, private_key)
}
#[inline]
pub fn create_shared_key(
&self,
private_key: &[u8],
salt: &[u8],
) -> Result<[u8; 32], SecpError> {
create_shared_key(self.source_public_key, private_key, salt)
}
#[inline]
pub fn authenticate(&self, shared_key: &[u8; 32]) -> Result<(), InvalidHmac> {
authenticate(shared_key, &self.payload_digest, &self.salt)?;
Ok(())
}
#[inline]
pub fn verify_stamp(&self) -> Result<Vec<Transaction>, StampError> {
self.stamp
.verify_stamp(&self.payload_digest, &self.destination_public_key)
}
#[inline]
pub fn open_in_place(&mut self, private_key: &[u8]) -> Result<Opened, OpenError> {
let txs = self.verify_stamp().map_err(OpenError::Stamp)?;
let shared_key = self
.create_shared_key(private_key, &self.salt)
.map_err(OpenError::SharedKey)?;
self.authenticate(&shared_key)
.map_err(|_| OpenError::Authentication)?;
let mut raw_payload = &mut self.payload;
let (key, iv) = shared_key.split_at(16);
let key = GenericArray::<u8, U16>::from_slice(&key);
let iv = GenericArray::<u8, U16>::from_slice(&iv);
let cipher = Aes128Cbc::new_var(&key, &iv).unwrap();
cipher
.decrypt(&mut raw_payload)
.map_err(OpenError::Decrypt)?;
let payload = Payload::decode(&mut raw_payload.as_slice()).map_err(OpenError::Payload)?;
Ok(Opened { txs, payload })
}
#[inline]
pub fn open(&self, private_key: &[u8]) -> Result<Opened, OpenError> {
let txs = self.verify_stamp().map_err(OpenError::Stamp)?;
let shared_key = self
.create_shared_key(private_key, &self.salt)
.map_err(OpenError::SharedKey)?;
self.authenticate(&shared_key)
.map_err(|_| OpenError::Authentication)?;
let raw_payload = &self.payload;
let (key, iv) = shared_key.as_ref().split_at(16);
let key = GenericArray::<u8, U16>::from_slice(&key);
let iv = GenericArray::<u8, U16>::from_slice(&iv);
let cipher = Aes128Cbc::new_var(&key, &iv).unwrap();
cipher
.decrypt_vec(raw_payload)
.map_err(OpenError::Decrypt)?;
let payload = Payload::decode(&mut raw_payload.as_slice()).map_err(OpenError::Payload)?;
Ok(Opened { txs, payload })
}
}
impl MessagePage {
pub fn into_payload_page(self) -> PayloadPage {
self.into()
}
}
impl Into<PayloadPage> for MessagePage {
fn into(self) -> PayloadPage {
let payloads: Vec<Vec<u8>> = self
.messages
.into_iter()
.map(|message| message.payload)
.collect();
PayloadPage {
start_time: self.start_time,
end_time: self.end_time,
start_digest: self.start_digest,
end_digest: self.end_digest,
payloads,
}
}
}
pub fn encrypt_payload(shared_key: &[u8], plaintext: &[u8]) -> Vec<u8> {
let (key, iv) = shared_key.as_ref().split_at(16);
let key = GenericArray::<u8, U16>::from_slice(&key);
let iv = GenericArray::<u8, U16>::from_slice(&iv);
let cipher = Aes128Cbc::new_var(&key, &iv).unwrap();
cipher.encrypt_vec(plaintext)
}
pub fn encrypt_payload_in_place(shared_key: &[u8], payload: &mut [u8]) {
let (key, iv) = shared_key.as_ref().split_at(16);
let key = GenericArray::<u8, U16>::from_slice(&key);
let iv = GenericArray::<u8, U16>::from_slice(&iv);
let cipher = Aes128Cbc::new_var(&key, &iv).unwrap();
cipher.encrypt(payload, 0).unwrap();
}