Skip to content

Commit

Permalink
fixup! feat: Add an Elliptic Curve Encryption Scheme
Browse files Browse the repository at this point in the history
  • Loading branch information
poljar committed May 9, 2024
1 parent 679ac04 commit 044190c
Showing 1 changed file with 95 additions and 33 deletions.
128 changes: 95 additions & 33 deletions src/ecies/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,21 @@ use crate::Curve25519PublicKey;

mod messages;

const ENCRYPTION_KEY_S_INFO: &str = "MATRIX_QR_CODE_LOGIN_ENCKEY_S";
const ENCRYPTION_KEY_G_INFO: &str = "MATRIX_QR_CODE_LOGIN_ENCKEY_G";
const MATRIX_INFO_PREFIX: &str = "MATRIX_QR_CODE_LOGIN";

type ApplicationInfo = String;

fn get_check_code_info(info: &str) -> String {
format!("{info}_CHECKCODE")
}

fn get_encrytpion_key_g_info(info: &str) -> String {
format!("{info}_ENCKEY_G")
}

fn get_encrytpion_key_s_info(info: &str) -> String {
format!("{info}_ENCKEY_S")
}

/// The Error type for the ECIES submodule.
#[derive(Debug, Error)]
Expand Down Expand Up @@ -198,6 +211,7 @@ pub struct InboundCreationResult {
}

/// The result of an outbound ECIES channel establishment.
#[derive(Debug)]
pub struct OutboundCreationResult {
/// The established ECIES channel.
pub ecies: EstablishedEcies,
Expand All @@ -208,16 +222,31 @@ pub struct OutboundCreationResult {
/// An unestablished ECIES session.
pub struct Ecies {
secret_key: EphemeralSecret,
application_info_prefix: ApplicationInfo,
}

impl Ecies {
/// Create a new, random, unestablished ECIES session.
///
/// This method will use the `MATRIX_QR_CODE_LOGIN` info. If you are using
/// this for a different purpose consider using the [`Ecies::with_info()`]
/// method.
#[allow(clippy::new_without_default)]
pub fn new() -> Self {
Self::with_info(MATRIX_INFO_PREFIX)
}

/// Create a new, random, unestablished ECIES session with the given
/// application info.
///
/// The application info will be used to derive the various secrets and
/// provide domain separation.
pub fn with_info(info: &str) -> Self {
let rng = thread_rng();
let secret_key = EphemeralSecret::random_from_rng(rng);
let application_info_prefix = info.to_owned();

Self { secret_key }
Self { secret_key, application_info_prefix }
}

/// Create an [`EstablishedEcies`] session using the other side's Curve25519
Expand All @@ -235,8 +264,13 @@ impl Ecies {
let shared_secret = self.secret_key.diffie_hellman(&their_public_key.inner);

if shared_secret.was_contributory() {
let mut ecies =
EstablishedEcies::new(&shared_secret, our_public_key, their_public_key, true);
let mut ecies = EstablishedEcies::new(
&shared_secret,
our_public_key,
their_public_key,
self.application_info_prefix,
true,
);

let message = ecies.encrypt(initial_plaintext);
let message =
Expand All @@ -259,8 +293,13 @@ impl Ecies {
let shared_secret = self.secret_key.diffie_hellman(&message.public_key.inner);

if shared_secret.was_contributory() {
let mut ecies =
EstablishedEcies::new(&shared_secret, our_public_key, message.public_key, false);
let mut ecies = EstablishedEcies::new(
&shared_secret,
our_public_key,
message.public_key,
self.application_info_prefix,
false,
);

let nonce = ecies.decryption_nonce.get();
let message = ecies.decrypt_helper(&nonce, &message.ciphertext)?;
Expand Down Expand Up @@ -338,27 +377,20 @@ impl EstablishedEcies {
shared_secret: &SharedSecret,
our_public_key: Curve25519PublicKey,
their_public_key: Curve25519PublicKey,
info: &ApplicationInfo,
initiator: bool,
) -> CheckCode {
const CHECKCODE_INFO: &str = "MATRIX_QR_CODE_LOGIN_CHECKCODE";

let mut bytes = [0u8; 2];
let kdf: Hkdf<Sha512> = Hkdf::new(None, shared_secret.as_bytes());

let info = get_check_code_info(info);

let info = if initiator {
// we are Device G. Gp = our_public_key, Sp = their_public_key
format!(
"{CHECKCODE_INFO}|{}|{}",
our_public_key.to_base64(),
their_public_key.to_base64(),
)
format!("{info}|{}|{}", our_public_key.to_base64(), their_public_key.to_base64(),)
} else {
// we are Device S. Gp = their_public_key, Sp = our_public_key
format!(
"{CHECKCODE_INFO}|{}|{}",
their_public_key.to_base64(),
our_public_key.to_base64(),
)
format!("{info}|{}|{}", their_public_key.to_base64(), our_public_key.to_base64(),)
};

kdf.expand(info.as_bytes(), bytes.as_mut_slice())
Expand Down Expand Up @@ -395,50 +427,68 @@ impl EstablishedEcies {
shared_secret: &SharedSecret,
our_public_key: Curve25519PublicKey,
their_public_key: Curve25519PublicKey,
info: &ApplicationInfo,
initiator: bool,
) -> Box<[u8; 32]> {
let info: &str = if initiator {
let info = if initiator {
// we are Device G
ENCRYPTION_KEY_G_INFO
get_encrytpion_key_g_info(info)
} else {
// we are Device S
ENCRYPTION_KEY_S_INFO
get_encrytpion_key_s_info(info)
};

Self::create_key(info, shared_secret, our_public_key, their_public_key, initiator)
Self::create_key(&info, shared_secret, our_public_key, their_public_key, initiator)
}

fn create_decryption_key(
shared_secret: &SharedSecret,
our_public_key: Curve25519PublicKey,
their_public_key: Curve25519PublicKey,
info: &ApplicationInfo,
initiator: bool,
) -> Box<[u8; 32]> {
let info: &str = if initiator {
let info = if initiator {
// we are Device G, they are Device S
ENCRYPTION_KEY_S_INFO
get_encrytpion_key_s_info(info)
} else {
// we are Device S, they are Device G
ENCRYPTION_KEY_G_INFO
get_encrytpion_key_g_info(info)
};

Self::create_key(info, shared_secret, our_public_key, their_public_key, initiator)
Self::create_key(&info, shared_secret, our_public_key, their_public_key, initiator)
}

fn new(
shared_secret: &SharedSecret,
our_public_key: Curve25519PublicKey,
their_public_key: Curve25519PublicKey,
infos: ApplicationInfo,
initiator: bool,
) -> Self {
let (encryption_nonce, decryption_nonce) = (EciesNonce::new(), EciesNonce::new());

let encryption_key =
Self::create_encryption_key(shared_secret, our_public_key, their_public_key, initiator);
let decryption_key =
Self::create_decryption_key(shared_secret, our_public_key, their_public_key, initiator);
let check_code =
Self::create_check_code(shared_secret, our_public_key, their_public_key, initiator);
let encryption_key = Self::create_encryption_key(
shared_secret,
our_public_key,
their_public_key,
&infos,
initiator,
);
let decryption_key = Self::create_decryption_key(
shared_secret,
our_public_key,
their_public_key,
&infos,
initiator,
);
let check_code = Self::create_check_code(
shared_secret,
our_public_key,
their_public_key,
&infos,
initiator,
);

Self {
encryption_key,
Expand Down Expand Up @@ -612,6 +662,18 @@ mod test {
assert_eq!(digit, 55, "u8::MAX should generate 55");
}

#[test]
fn info_expansion() {
let info = get_check_code_info(MATRIX_INFO_PREFIX);
assert_eq!(info, "MATRIX_QR_CODE_LOGIN_CHECKCODE");

let info = get_encrytpion_key_g_info(MATRIX_INFO_PREFIX);
assert_eq!(info, "MATRIX_QR_CODE_LOGIN_ENCKEY_G");

let info = get_encrytpion_key_s_info(MATRIX_INFO_PREFIX);
assert_eq!(info, "MATRIX_QR_CODE_LOGIN_ENCKEY_S");
}

proptest! {
#[test]
fn check_code_proptest(bytes in prop::array::uniform2(0u8..) ) {
Expand Down

0 comments on commit 044190c

Please sign in to comment.