Skip to content

Commit

Permalink
finish
Browse files Browse the repository at this point in the history
  • Loading branch information
chrislattman committed May 8, 2024
1 parent 3f8a385 commit d94282e
Show file tree
Hide file tree
Showing 7 changed files with 285 additions and 28 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
.DS_Store
tls
target/
152 changes: 152 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[package]
name = "test-crypto"
version = "0.1.0"
edition = "2021"

[[bin]]
name = "tls"
path = "tls.rs"

[dependencies]
hex = "0.4.3"
ring = "0.17.8"
7 changes: 5 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ c:
gcc -Wall -Wextra -Werror -pedantic -std=c99 -o tls tls.c -lcrypto
./tls

rust:
cargo run --bin tls

clean:
rm tls
rm -rf tls target

.PHONY: java python nodejs go c clean
.PHONY: java python nodejs go c rust clean
6 changes: 3 additions & 3 deletions TLS.java
Original file line number Diff line number Diff line change
Expand Up @@ -95,13 +95,13 @@ public static void main(String[] args) throws Exception {
.getInstance("EC")
.generatePublic(decodedServerEcdhPublicKeySpec);
KeyPair clientEcdhKeyPair = keyPairGen.generateKeyPair();
byte[] encodedClientEcdhPublicKey = clientEcdhKeyPair.getPublic().getEncoded(); // 91 bytes
byte[] encodedClientEcdhPublicKey = clientEcdhKeyPair.getPublic().getEncoded();
KeyAgreement agreement = KeyAgreement.getInstance("ECDH");
agreement.init(clientEcdhKeyPair.getPrivate());
agreement.doPhase(decodedServerEcdhPublicKey, true);
byte[] clientMasterSecret = agreement.generateSecret(); // 32 bytes
byte[] clientMasterSecret = agreement.generateSecret();
sha256.reset();
byte[] aesKeyBytes = sha256.digest(clientMasterSecret); // 32 bytes
byte[] aesKeyBytes = sha256.digest(clientMasterSecret);
SecretKeySpec aesKey = new SecretKeySpec(aesKeyBytes, "AES");

/*
Expand Down
46 changes: 23 additions & 23 deletions tls.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ int main(void)
FILE *fp;
unsigned char *encoded_rsa_public_key, *encoded_rsa_private_key,
*encoded_server_ecdh_public_key, *ptr, *key_hash, *signature,
*encoded_client_ecdh_public_key, *client_shared_secret,
*server_shared_secret, *aes_key, iv[12], *ciphertext, tag[16], *decrypted;
*encoded_client_ecdh_public_key, *client_master_secret,
*server_master_secret, *aes_key, iv[12], *ciphertext, tag[16], *decrypted;
int encoded_rsa_public_key_len, encoded_rsa_private_key_len,
encoded_server_ecdh_public_key_len, verified,
encoded_client_ecdh_public_key_len, len, ciphertext_len, decrypted_len;
Expand All @@ -24,10 +24,10 @@ int main(void)
*client_ecdh_keypair = NULL, *decoded_client_ecdh_public_key = NULL;
EVP_PKEY_CTX *server_ecdh_param_context, *server_ecdh_key_context,
*client_ecdh_param_context, *client_ecdh_key_context,
*client_shared_secret_context, *server_shared_secret_context;
*client_master_secret_context, *server_master_secret_context;
EVP_MD_CTX *sha256_context, *sign_context, *verify_context;
unsigned int key_hash_len, aes_key_len;
size_t signature_len, client_shared_secret_len, server_shared_secret_len;
size_t signature_len, client_master_secret_len, server_master_secret_len;
const char *plaintext = "Hello world!", *aad = "authenticated but unencrypted data";
EVP_CIPHER_CTX *aes_gcm_encrypt_context, *aes_gcm_decrypt_context;

Expand Down Expand Up @@ -100,30 +100,30 @@ int main(void)
encoded_client_ecdh_public_key = malloc(encoded_client_ecdh_public_key_len);
ptr = encoded_client_ecdh_public_key;
i2d_PUBKEY(client_ecdh_keypair, &ptr);
client_shared_secret_context = EVP_PKEY_CTX_new(client_ecdh_keypair, NULL);
EVP_PKEY_derive_init(client_shared_secret_context);
EVP_PKEY_derive_set_peer(client_shared_secret_context, decoded_server_ecdh_public_key);
EVP_PKEY_derive(client_shared_secret_context, NULL, &client_shared_secret_len);
client_shared_secret = malloc(client_shared_secret_len);
EVP_PKEY_derive(client_shared_secret_context, client_shared_secret, &client_shared_secret_len);
client_master_secret_context = EVP_PKEY_CTX_new(client_ecdh_keypair, NULL);
EVP_PKEY_derive_init(client_master_secret_context);
EVP_PKEY_derive_set_peer(client_master_secret_context, decoded_server_ecdh_public_key);
EVP_PKEY_derive(client_master_secret_context, NULL, &client_master_secret_len);
client_master_secret = malloc(client_master_secret_len);
EVP_PKEY_derive(client_master_secret_context, client_master_secret, &client_master_secret_len);
EVP_MD_CTX_free(sha256_context);
sha256_context = EVP_MD_CTX_new();
EVP_DigestInit_ex(sha256_context, EVP_sha256(), NULL);
EVP_DigestUpdate(sha256_context, client_shared_secret, client_shared_secret_len);
EVP_DigestUpdate(sha256_context, client_master_secret, client_master_secret_len);
aes_key_len = EVP_MD_get_size(EVP_sha256());
aes_key = malloc(aes_key_len);
EVP_DigestFinal_ex(sha256_context, aes_key, &aes_key_len);

const_ptr = encoded_client_ecdh_public_key;
d2i_PUBKEY(&decoded_client_ecdh_public_key, &const_ptr, encoded_client_ecdh_public_key_len);
server_shared_secret_context = EVP_PKEY_CTX_new(server_ecdh_keypair, NULL);
EVP_PKEY_derive_init(server_shared_secret_context);
EVP_PKEY_derive_set_peer(server_shared_secret_context, decoded_client_ecdh_public_key);
EVP_PKEY_derive(server_shared_secret_context, NULL, &server_shared_secret_len);
server_shared_secret = malloc(server_shared_secret_len);
EVP_PKEY_derive(server_shared_secret_context, server_shared_secret, &server_shared_secret_len);
if (client_shared_secret_len != server_shared_secret_len ||
memcmp(client_shared_secret, server_shared_secret, client_shared_secret_len) != 0) {
server_master_secret_context = EVP_PKEY_CTX_new(server_ecdh_keypair, NULL);
EVP_PKEY_derive_init(server_master_secret_context);
EVP_PKEY_derive_set_peer(server_master_secret_context, decoded_client_ecdh_public_key);
EVP_PKEY_derive(server_master_secret_context, NULL, &server_master_secret_len);
server_master_secret = malloc(server_master_secret_len);
EVP_PKEY_derive(server_master_secret_context, server_master_secret, &server_master_secret_len);
if (client_master_secret_len != server_master_secret_len ||
memcmp(client_master_secret, server_master_secret, client_master_secret_len) != 0) {
printf("Master secrets don't match.\n");
exit(1);
}
Expand Down Expand Up @@ -159,13 +159,13 @@ int main(void)
EVP_CIPHER_CTX_free(aes_gcm_decrypt_context);
free(ciphertext);
EVP_CIPHER_CTX_free(aes_gcm_encrypt_context);
free(server_shared_secret);
EVP_PKEY_CTX_free(server_shared_secret_context);
free(server_master_secret);
EVP_PKEY_CTX_free(server_master_secret_context);
EVP_PKEY_free(decoded_client_ecdh_public_key);
free(aes_key);
EVP_MD_CTX_free(sha256_context);
free(client_shared_secret);
EVP_PKEY_CTX_free(client_shared_secret_context);
free(client_master_secret);
EVP_PKEY_CTX_free(client_master_secret_context);
free(encoded_client_ecdh_public_key);
EVP_PKEY_free(client_ecdh_keypair);
EVP_PKEY_CTX_free(client_ecdh_key_context);
Expand Down
89 changes: 89 additions & 0 deletions tls.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
use std::fs;

use ring::{aead, agreement, digest, rand::{self, SecureRandom}, rsa, signature};

fn main() {
let encoded_rsa_public_key = fs::read("public_key.der").unwrap();
let encoded_rsa_private_key = fs::read("private_key.der").unwrap();
let rsa_private_key = rsa::KeyPair::from_pkcs8(encoded_rsa_private_key.as_slice()).unwrap();

let rng = rand::SystemRandom::new();
let server_ecdh_private_key = agreement
::EphemeralPrivateKey::generate(&agreement::ECDH_P384, &rng).unwrap();
let server_ecdh_public_key = server_ecdh_private_key.compute_public_key().unwrap();
let server_ecdh_public_key_raw = server_ecdh_public_key.as_ref();
// hack to encode secp384r1 public key in DER SPKI format manually since
// ring doesn't support it yet :(
let mut encoded_server_ecdh_public_key = hex::decode(
"3076301006072a8648ce3d020106052b81040022036200").unwrap();
encoded_server_ecdh_public_key.extend_from_slice(server_ecdh_public_key_raw);

// SHA-256 hash of server's ECDH public key is computed by sign()
let mut signature = vec![0; rsa_private_key.public().modulus_len()];
rsa_private_key.sign(&signature::RSA_PKCS1_SHA256, &rng,
encoded_server_ecdh_public_key.as_slice(), &mut signature).unwrap();

let decoded_rsa_public_key = signature
::UnparsedPublicKey::new(&signature::RSA_PKCS1_2048_8192_SHA256,
&encoded_rsa_public_key.as_slice()[24..]); // removing the encoding manually
decoded_rsa_public_key.verify(encoded_server_ecdh_public_key.as_slice(),
signature.as_slice()).unwrap_or_else(|_| {
panic!("RSA signature wasn't verified.");
});

// client should import ring::constant_time and use
// constant_time::verify_slices_are_equal() to compare server's hash with
// its own hash of the server's encoded ECDH public key to avoid timing
// attacks
let decoded_server_ecdh_public_key = agreement
::UnparsedPublicKey::new(&agreement::ECDH_P384,
&encoded_server_ecdh_public_key.as_slice()[23..]); // removing the encoding manually
let client_ecdh_private_key = agreement
::EphemeralPrivateKey::generate(&agreement::ECDH_P384, &rng).unwrap();
let client_ecdh_public_key = client_ecdh_private_key.compute_public_key().unwrap();
let client_ecdh_public_key_raw = client_ecdh_public_key.as_ref();
// manual public key encoding again
let mut encoded_client_ecdh_public_key = hex::decode(
"3076301006072a8648ce3d020106052b81040022036200").unwrap();
encoded_client_ecdh_public_key.extend_from_slice(client_ecdh_public_key_raw);
let aes_key = agreement::agree_ephemeral(
client_ecdh_private_key, &decoded_server_ecdh_public_key,
|client_master_secret| {
// perform SHA-256 hash of the master secret in here
return digest::digest(&digest::SHA256, client_master_secret).as_ref().to_vec();
}
).unwrap();

let decoded_client_ecdh_public_key = agreement
::UnparsedPublicKey::new(&agreement::ECDH_P384,
&encoded_client_ecdh_public_key.as_slice()[23..]); // removing the encoding manually
let server_aes_key = agreement::agree_ephemeral(
server_ecdh_private_key, &decoded_client_ecdh_public_key,
|server_master_secret| {
// perform SHA-256 hash of the master secret in here
return digest::digest(&digest::SHA256, server_master_secret).as_ref().to_vec();
}
).unwrap();
if aes_key != server_aes_key {
panic!("Master secrets don't match.");
}

let plaintext = "Hello world!";
let mut ciphertext = plaintext.as_bytes().to_vec();
let aad_bytes = b"authenticated but unencrypted data";
let mut iv_bytes = [08; 12];
rng.fill(&mut iv_bytes).unwrap();
let iv = aead::Nonce::assume_unique_for_key(iv_bytes);
let aad = aead::Aad::from(aad_bytes);
let unbound_key = aead::UnboundKey::new(
&aead::AES_256_GCM, aes_key.as_slice()).unwrap();
let aes = aead::LessSafeKey::new(unbound_key);
aes.seal_in_place_append_tag(iv, aad, &mut ciphertext).unwrap();

let iv_copy = aead::Nonce::assume_unique_for_key(iv_bytes);
let decrypted = aes.open_in_place(iv_copy, aad, &mut ciphertext).unwrap();
let recovered = std::str::from_utf8(decrypted).unwrap();
if plaintext != recovered {
panic!("Plaintexts don't match.");
}
}

0 comments on commit d94282e

Please sign in to comment.