Skip to content

Commit

Permalink
Allow decoding any crypto type
Browse files Browse the repository at this point in the history
Signed-off-by: Michiel <[email protected]>
  • Loading branch information
michielp1807 committed Sep 4, 2024
1 parent 2baa053 commit b7b7a9e
Show file tree
Hide file tree
Showing 8 changed files with 164 additions and 99 deletions.
3 changes: 3 additions & 0 deletions examples/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ homepage.workspace = true
publish.workspace = true
rust-version.workspace = true

[features]
nacl = ["tsp/nacl"]

[[bin]]
name = "create-did-web"
path = "src/create-did-web.rs"
Expand Down
51 changes: 51 additions & 0 deletions examples/cli-test-cross-type.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#!/bin/bash
HPKE="./build-hpke"
NACL="./build-nacl"

HPKE_TPS="$HPKE/bin/tsp"
NACL_TPS="$NACL/bin/tsp"

# install two different versions of the TSP command line example tool
cargo install --path . --bin tsp --root "$HPKE"
cargo install --path . --bin tsp --features nacl --root "$NACL"

randuser() {
head -c4 /dev/urandom | shasum | head -c8
}

echo "---- cleanup the database"
rm -f marlon.sqlite marc.sqlite

echo "---- create a new sender identity"
$HPKE_TPS --database marlon create --alias marlon `randuser`

echo "---- create a new receiver identity"
$NACL_TPS --database marc create --alias marc `randuser`

DID_MARC=$($NACL_TPS --database marc print marc)
DID_MARLON=$($HPKE_TPS --database marlon print marlon)

echo "---- verify the address of the receiver"
$HPKE_TPS --database marlon verify --alias marc "$DID_MARC"

echo "---- verify the address of the sender"
$NACL_TPS --database marc verify --alias marlon "$DID_MARLON"

echo "---- wait 2 seconds and then send a message to the receiver"
sleep 2 && echo "Oh hi Marc" | $HPKE_TPS --database marlon send -s marlon -r marc &

echo "---- receive the message"
$NACL_TPS --database marc receive --one marc

echo "---- wait 1 seconds and then send a message back"
sleep 1 && echo "Oh hello Marlon" | $NACL_TPS --database marc send -s marc -r marlon &

echo "---- receive the message"
$HPKE_TPS --database marlon receive --one marlon

echo "---- cleanup databases"
rm -f marc.sqlite marlon.sqlite

echo "---- cleanup install"
rm -rf "$HPKE"
rm -rf "$NACL"
8 changes: 4 additions & 4 deletions tsp/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ essr = []
strict = []
fuzzing = ["dep:arbitrary"]
demo = []
nacl = ["dep:crypto_box", "dep:blake2", "dep:typenum", "essr"]
nacl = ["essr"]
pq = ["dep:hpke_pq", "essr"]
async = [
"resolve",
Expand Down Expand Up @@ -52,9 +52,9 @@ hpke = { workspace = true }
hpke_pq = { workspace = true, optional = true }
rand = { workspace = true }
sha2 = { workspace = true }
blake2 = { workspace = true, optional = true }
typenum = { workspace = true, optional = true }
crypto_box = { workspace = true, optional = true }
blake2 = { workspace = true }
typenum = { workspace = true }
crypto_box = { workspace = true }
# async
aries-askar = { workspace = true, optional = true }
async-stream = { workspace = true, optional = true }
Expand Down
1 change: 0 additions & 1 deletion tsp/src/crypto/digest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ pub fn sha256(content: &[u8]) -> [u8; 32] {
sha2::Sha256::digest(content).into()
}

#[cfg(feature = "nacl")]
pub fn blake2b256(content: &[u8]) -> [u8; 32] {
use blake2::Digest;
type Blake2b256 = blake2::Blake2b<typenum::U32>;
Expand Down
8 changes: 3 additions & 5 deletions tsp/src/crypto/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,11 @@ pub enum CryptoError {
Decode(#[from] crate::cesr::error::DecodeError),
#[cfg(feature = "pq")]
#[error("encryption or decryption failed: {0}")]
Cryptographic(#[from] hpke_pq::HpkeError),
#[cfg(all(not(feature = "nacl"), not(feature = "pq")))]
CryptographicHpkePq(#[from] hpke_pq::HpkeError),
#[error("encryption or decryption failed: {0}")]
Cryptographic(#[from] hpke::HpkeError),
#[cfg(feature = "nacl")]
CryptographicHpke(#[from] hpke::HpkeError),
#[error("encryption or decryption failed")]
Cryptographic(#[from] crypto_box::aead::Error),
CryptographicNacl(#[from] crypto_box::aead::Error),
#[error("could not verify signature: {0}")]
Verify(#[from] ed25519_dalek::ed25519::Error),
#[error("unexpected recipient")]
Expand Down
58 changes: 46 additions & 12 deletions tsp/src/crypto/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::definitions::{
Digest, NonConfidentialData, Payload, PrivateKeyData, PrivateSigningKeyData, PrivateVid,
PublicKeyData, PublicVerificationKeyData, TSPMessage, VerifiedVid,
};
#[cfg(feature = "nacl")]

pub use digest::blake2b256;
pub use digest::sha256;
use rand::rngs::OsRng;
Expand All @@ -11,21 +11,22 @@ mod digest;
pub mod error;
mod nonconfidential;

#[cfg(feature = "nacl")]
mod tsp_nacl;

#[cfg(not(feature = "nacl"))]
mod tsp_hpke;
#[cfg(not(feature = "pq"))]
mod tsp_nacl;

pub use error::CryptoError;

#[cfg(all(not(feature = "nacl"), not(feature = "pq")))]
#[cfg(not(feature = "pq"))]
use crate::cesr::CryptoType;

#[cfg(not(feature = "pq"))]
pub type Aead = hpke::aead::ChaCha20Poly1305;

#[cfg(all(not(feature = "nacl"), not(feature = "pq")))]
#[cfg(not(feature = "pq"))]
pub type Kdf = hpke::kdf::HkdfSha256;

#[cfg(all(not(feature = "nacl"), not(feature = "pq")))]
#[cfg(not(feature = "pq"))]
pub type Kem = hpke::kem::X25519HkdfSha256;

#[cfg(feature = "pq")]
Expand Down Expand Up @@ -78,11 +79,44 @@ pub fn open<'a>(
sender: &dyn VerifiedVid,
tsp_message: &'a mut [u8],
) -> Result<MessageContents<'a>, CryptoError> {
#[cfg(not(feature = "nacl"))]
return tsp_hpke::open::<Aead, Kdf, Kem>(receiver, sender, tsp_message);
let view = crate::cesr::decode_envelope(tsp_message)?;

// verify outer signature
let verification_challenge = view.as_challenge();
let signature = ed25519_dalek::Signature::from(verification_challenge.signature);
let verifying_key = ed25519_dalek::VerifyingKey::from_bytes(sender.verifying_key())?;
verifying_key.verify_strict(verification_challenge.signed_data, &signature)?;

// decode envelope
let crate::cesr::DecodedEnvelope {
raw_header,
envelope,
ciphertext: Some(ciphertext),
} = view
.into_opened::<&[u8]>()
.map_err(|_| crate::cesr::error::DecodeError::VidError)?
else {
return Err(CryptoError::MissingCiphertext);
};

// verify the message was intended for the specified receiver
if envelope.receiver != Some(receiver.identifier().as_bytes()) {
return Err(CryptoError::UnexpectedRecipient);
}

#[cfg(feature = "nacl")]
return tsp_nacl::open(receiver, sender, tsp_message);
#[cfg(feature = "pq")]
return tsp_hpke::open::<Aead, Kdf, Kem>(receiver, sender, raw_header, envelope, ciphertext);

#[cfg(not(feature = "pq"))]
match envelope.crypto_type {
CryptoType::HpkeAuth | CryptoType::HpkeEssr => {
tsp_hpke::open::<Aead, Kdf, Kem>(receiver, sender, raw_header, envelope, ciphertext)
}
CryptoType::NaclAuth | CryptoType::NaclEssr => {
tsp_nacl::open(receiver, sender, raw_header, envelope, ciphertext)
}
CryptoType::Plaintext => Err(CryptoError::MissingCiphertext), // TODO: better error code?
}
}

/// Construct and sign a non-confidential TSP message
Expand Down
73 changes: 33 additions & 40 deletions tsp/src/crypto/tsp_hpke.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,27 @@
use crate::{
cesr::{CryptoType, DecodedEnvelope, DecodedPayload, SignatureType},
definitions::{NonConfidentialData, Payload, PrivateVid, TSPMessage, VerifiedVid},
cesr::{CryptoType, DecodedPayload, Envelope},
definitions::{Payload, PrivateVid, VerifiedVid},
};

#[cfg(not(feature = "nacl"))]
use crate::{
cesr::SignatureType,
definitions::{NonConfidentialData, TSPMessage},
};

#[cfg(not(feature = "nacl"))]
use ed25519_dalek::Signer;
#[cfg(not(feature = "nacl"))]
use rand::{rngs::StdRng, SeedableRng};

#[cfg(not(feature = "pq"))]
use hpke::{
aead, kdf, kem, single_shot_open_in_place_detached, single_shot_seal_in_place_detached,
Deserializable, OpModeR, OpModeS, Serializable,
aead, kdf, kem, single_shot_open_in_place_detached, Deserializable, OpModeR, Serializable,
};

#[cfg(all(not(feature = "nacl"), not(feature = "pq")))]
use hpke::{single_shot_seal_in_place_detached, OpModeS};

#[cfg(feature = "pq")]
use hpke_pq::{
aead, kdf, kem, single_shot_open_in_place_detached, single_shot_seal_in_place_detached,
Expand All @@ -19,6 +30,7 @@ use hpke_pq::{

use super::{CryptoError, MessageContents};

#[cfg(not(feature = "nacl"))]
pub(crate) fn seal<A, Kdf, Kem>(
sender: &dyn PrivateVid,
receiver: &dyn VerifiedVid,
Expand Down Expand Up @@ -155,38 +167,15 @@ where
pub(crate) fn open<'a, A, Kdf, Kem>(
receiver: &dyn PrivateVid,
sender: &dyn VerifiedVid,
tsp_message: &'a mut [u8],
raw_header: &'a [u8],
envelope: Envelope<'a, &[u8]>,
ciphertext: &'a mut [u8],
) -> Result<MessageContents<'a>, CryptoError>
where
A: aead::Aead,
Kdf: kdf::Kdf,
Kem: kem::Kem,
{
let view = crate::cesr::decode_envelope(tsp_message)?;

// verify outer signature
let verification_challenge = view.as_challenge();
let signature = ed25519_dalek::Signature::from(verification_challenge.signature);
let verifying_key = ed25519_dalek::VerifyingKey::from_bytes(sender.verifying_key())?;
verifying_key.verify_strict(verification_challenge.signed_data, &signature)?;

// decode envelope
let DecodedEnvelope {
raw_header: info,
envelope,
ciphertext: Some(ciphertext),
} = view
.into_opened::<&[u8]>()
.map_err(|_| crate::cesr::error::DecodeError::VidError)?
else {
return Err(CryptoError::MissingCiphertext);
};

// verify the message was intended for the specified receiver
if envelope.receiver != Some(receiver.identifier().as_bytes()) {
return Err(CryptoError::UnexpectedRecipient);
}

// split encapsulated key and authenticated encryption tag length
let (ciphertext, footer) = ciphertext
.split_at_mut(ciphertext.len() - aead::AeadTag::<A>::size() - Kem::EncappedKey::size());
Expand All @@ -197,11 +186,13 @@ where
let encapped_key = Kem::EncappedKey::from_bytes(encapped_key)?;
let tag = aead::AeadTag::from_bytes(tag)?;

#[cfg(any(feature = "essr", feature = "pq"))]
#[cfg(feature = "pq")]
let mode = OpModeR::Base;

#[cfg(all(not(feature = "essr"), not(feature = "pq")))]
let mode = {
#[cfg(not(feature = "pq"))]
let mode = if envelope.crypto_type == CryptoType::HpkeEssr {
OpModeR::Base
} else {
let sender_encryption_key = Kem::PublicKey::from_bytes(sender.encryption_key().as_ref())?;
OpModeR::Auth(sender_encryption_key)
};
Expand All @@ -211,7 +202,7 @@ where
&mode,
&receiver_decryption_key,
&encapped_key,
info,
raw_header,
ciphertext,
&[],
&tag,
Expand All @@ -231,14 +222,15 @@ where
sender_identity,
} = crate::cesr::decode_payload(ciphertext)?;

#[cfg(feature = "essr")]
match sender_identity {
Some(id) => {
if id != sender.identifier().as_bytes() {
return Err(CryptoError::UnexpectedSender);
if envelope.crypto_type == CryptoType::HpkeEssr {
match sender_identity {
Some(id) => {
if id != sender.identifier().as_bytes() {
return Err(CryptoError::UnexpectedSender);
}
}
None => return Err(CryptoError::MissingSender),
}
None => return Err(CryptoError::MissingSender),
}

let secret_payload = match payload {
Expand Down Expand Up @@ -284,6 +276,7 @@ where
}

/// Generate N random bytes using the provided RNG
#[cfg(not(feature = "nacl"))]
fn fresh_nonce(csprng: &mut (impl rand::RngCore + rand::CryptoRng)) -> crate::cesr::Nonce {
crate::cesr::Nonce::generate(|dst| csprng.fill_bytes(dst))
}
Loading

0 comments on commit b7b7a9e

Please sign in to comment.