This crate is very complicated to use, documentation is so basic that any complex functions are virtually unusable.
No support for serialization, and I can't figure out the most basic of things like verifying the signature of an encrypted message. This here does not work, and I don't understand why the EncryptedMessage would not be able to be used as the message ibn the verify function (it does not implement Hashable, and neither does &[u8;384]. Honest question, how was I supposed to use this?)
pub fn verify_payload_signature(
public_key: &PublicSigningKey,
signature: &Ed25519Signature,
payload: EncryptedMessage,
) -> bool {
public_key.verify(&payload.bytes(), signature)
}
Another thing that has been really annoying is declaring EncryptedOnceValue as a enum variant of EncryptedValue. That makes it so that I can't do anything with the EncryptedValue enum unless I "if let" the contents out from it:
if let EncryptedValue::EncryptedOnceValue {
ephemeral_public_key,
encrypted_message,
auth_hash,
public_signing_key,
signature,
} = encrypted_value
{
}
This makes it look terrible and everything becomes a big hassle. Serializing this for storage or to send it somewhere else is so inefficient. I had to create a helper struct just to do this:
use recrypt::api::*;
use serde::{Deserialize, Serialize};
use crate::error::ServerError;
#[derive(Serialize, Deserialize)]
pub struct EncryptedValueBytes {
pub ephemeral_public_key: Vec<u8>,
pub encrypted_message: Vec<u8>,
pub auth_hash: Vec<u8>,
pub public_signing_key: Vec<u8>,
pub signature: Vec<u8>,
}
impl From<EncryptedValue> for EncryptedValueBytes {
fn from(value: EncryptedValue) -> Self {
let EncryptedValue::EncryptedOnceValue {
ephemeral_public_key,
encrypted_message,
auth_hash,
public_signing_key,
signature,
} = value
else {
panic!("Expected EncryptedOnceValue");
};
Self {
ephemeral_public_key: ephemeral_public_key.to_bytes().to_vec(),
encrypted_message: encrypted_message.bytes().to_vec(),
auth_hash: auth_hash.bytes().to_vec(),
public_signing_key: public_signing_key.bytes().to_vec(),
signature: signature.bytes().to_vec(),
}
}
}
impl EncryptedValueBytes {
pub fn to_encrypted_value(&self) -> Result<EncryptedValue, RecryptErr> {
Ok(EncryptedValue::EncryptedOnceValue {
ephemeral_public_key: PublicKey::new_from_slice((
&self.ephemeral_public_key[0..32],
&self.ephemeral_public_key[32..64],
))?,
encrypted_message: EncryptedMessage::new_from_slice(
&self.encrypted_message,
)?,
auth_hash: AuthHash::new_from_slice(&self.auth_hash)?,
public_signing_key: PublicSigningKey::new_from_slice(
&self.public_signing_key,
)?,
signature: Ed25519Signature::new_from_slice(&self.signature)?,
})
}
pub fn to_sql(&self) -> Result<Vec<u8>, ServerError> {
Ok(serde_json::to_vec(self)?)
}
}
There should also be more integration with the ed25519_dalek crate, which is already one of your dependencies. There are no utility functions to convert between them, and the only way I found of constructing a SingingKeypair from my already existent keypair was this big mess here:
use std::io::Read;
use ed25519_dalek::{SigningKey, VerifyingKey};
use recrypt::api::{PrivateKey, PublicKey, SigningKeypair};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
pub struct KeysRaw {
pub private_key_encoding: [u8; 32],
pub public_key_encoding: Vec<u8>, // serde does not support [u8;64] natively, and I don't want a
// new crate just for this
pub signing_key: [u8; 32],
pub verifying_key: [u8; 32],
}
enum EcryptedV {
EncryptedOV {
auth_hash: String,
signature: String,
},
}
pub struct Keys {
pub private_key_encoding: PrivateKey,
pub public_key_encoding: PublicKey,
pub signing_key: SigningKey,
pub verifying_key: VerifyingKey,
pub signing_key_pair: SigningKeypair,
}
impl From<KeysRaw> for Keys {
fn from(value: KeysRaw) -> Self {
let pub_1 = &value.public_key_encoding[0..32];
let pub_2 = &value.public_key_encoding[32..64];
let mut signing_keypair_bytes = value.signing_key.to_vec();
signing_keypair_bytes.append(&mut value.verifying_key.to_vec());
Keys {
private_key_encoding: PrivateKey::new_from_slice(&value.private_key_encoding).unwrap(),
public_key_encoding: PublicKey::new_from_slice((pub_1, pub_2)).unwrap(),
signing_key: SigningKey::from_bytes(&value.signing_key),
verifying_key: VerifyingKey::from_bytes(&value.verifying_key).unwrap(),
signing_key_pair: SigningKeypair::from_bytes(
&signing_keypair_bytes.try_into().unwrap(),
)
.unwrap(),
}
}
}
I don't want to just complain, this is a good project, and it's really nice that you had it audited so that we can trust the soundness of the cryptography, but in it's current state, I think the crate is barely usable for anyone without internal access to the development.
What I think would really benefit the project:
- More documentation
- Serialization support would be greatly appreciated
- Separating the EncryptedOnceValue and the other one into their own separate structs that implement the type EncryptedValue
- Separating the functions of singing the encryption from the encryption itself. (Maybe I'm talking bullshit, I don't know enough about cryptography to argue about that, but I already had my signing keys from the ed25519_dalek crate, and I was already going to sign them independently, but instead I had to reconstruct them to fit the internal ed25519 keypair), and the simplest way to convert between them was the code shown above)
This crate is very complicated to use, documentation is so basic that any complex functions are virtually unusable.
No support for serialization, and I can't figure out the most basic of things like verifying the signature of an encrypted message. This here does not work, and I don't understand why the EncryptedMessage would not be able to be used as the message ibn the verify function (it does not implement Hashable, and neither does &[u8;384]. Honest question, how was I supposed to use this?)
Another thing that has been really annoying is declaring EncryptedOnceValue as a enum variant of EncryptedValue. That makes it so that I can't do anything with the EncryptedValue enum unless I "if let" the contents out from it:
This makes it look terrible and everything becomes a big hassle. Serializing this for storage or to send it somewhere else is so inefficient. I had to create a helper struct just to do this:
There should also be more integration with the ed25519_dalek crate, which is already one of your dependencies. There are no utility functions to convert between them, and the only way I found of constructing a SingingKeypair from my already existent keypair was this big mess here:
I don't want to just complain, this is a good project, and it's really nice that you had it audited so that we can trust the soundness of the cryptography, but in it's current state, I think the crate is barely usable for anyone without internal access to the development.
What I think would really benefit the project: