>

Libsecurity API

Project 3

For Project 3, we’re using OpenSSL 3’s libcrypto for cryptographic primitives. However, directly interfacing with libcrypto may prove to be quite challenging. As such, we’re providing our own libsecurity that has more course-grained cryptographic primitives specific to Project 3.

#Global Variables

EVP_PKEY* ec_peer_public_key

This variable is populated after a valid call to load_peer_public_key. Use this as the authority for verify if you’d like to check if some data was signed with the peer’s (the other client or server’s) private key.

EVP_PKEY* ec_ca_public_key

This variable is populated after a valid call to load_ca_public_key. Use this as the authority for verify if you’d like to check if some data was signed with the certificate authority’s private key.

uint8_t* certificate and size_t cert_size

This variable is populated after a valid call to load_certificate. This certificate is already encoded as TLV 0xA0 (see the Project 3 spec for more info). cert_size is the corresponding total length.

uint8_t* public_key and size_t pub_key_size

This variable is populated after a valid call to derive_public_key. This public key is encoded as ASN.1 DER; you do not need to know the specifics. This is the format used to send public keys over the wire. pub_key_size is the corresponding total length.

#Functions

void load_private_key(const char* filename);

From a file on the local file system, load in a private key.

Hint: In Project 3, the filename is just server_key.bin.


EVP_PKEY* get_private_key();

Retrieve the current private key to potentially restore it later (using set_private_key).

Returns a pointer to the current state of the private key.


void set_private_key(EVP_PKEY* key);

Set the private key to some previously known private key state (retrieved using get_private_key).


void load_peer_public_key(const uint8_t* peer_key, size_t size);

Take some public key sent over the wire and load it as the peer’s public key (in ec_peer_public_key).


void load_ca_public_key(const char* filename);

From a file on the local file system, load in the CA’s public key.

Hint: In Project 3, the filename is just ca_public_key.bin.


void load_certificate(const char* filename);

From a file on the local file system, load in the certificate that has been signed by the CA into the certificate global variable.

Hint: In Project 3, the filename is just server_cert.bin.


void generate_private_key();

Randomly generate a private key (can be retrieved using get_private_key). Make sure to call this (or load_private_key / set_private_key) before any operations that require a private key (such as derive_public_key, derive_secret, derive_keys, sign, encrypt_data, decrypt_cipher, and hmac).


void derive_public_key();

From the loaded private key, generate the public key in ASN.1 format and place it in the public_key global variable.


void derive_secret();

After loading the peer public key with load_peer_public_key and generating a private key, you may derive the shared (EC) Diffie-Hellman secret by calling this function.


void derive_keys(const uint8_t* salt, size_t size);

Using HKDF, derive two keys: (1) info ENC for symmetric encryption, and (2) info MAC for message authentication. Both keys need a salt.


size_t sign(uint8_t* signature, const uint8_t* data, size_t size);

Generate a signature over the given data using the private key.

Returns the resulting size of the signature stored in the signature buffer.


int verify(const uint8_t* signature, size_t sig_size, const uint8_t* data, size_t size, EVP_PKEY* authority);

Verify if a given signature has indeed been signed by some authority over some data.

Returns 1 if verification is successful, 0 if the signature is invalid, and any other value if there was an error in processing the parameters.


void generate_nonce(uint8_t* buf, size_t size);

Write random bytes to a buffer up to a given size.


size_t encrypt_data(uint8_t* iv, uint8_t* cipher, const uint8_t* data, size_t size);

After deriving the ENC key, this function takes some data and outputs the corresponding ciphertext + IV (AES-256-CBC).

Returns the size of the ciphertext. Note that the ciphertext can be up to 16 bytes longer than the plaintext.


size_t decrypt_cipher(uint8_t* data, const uint8_t* cipher, size_t size, const uint8_t* iv);

After deriving the ENC key, this function takes some ciphertext and IV and outputs the corresponding plaintext.

Returns the size of the plaintext.


void hmac(uint8_t* digest, const uint8_t* data, size_t size);

Calculates HMAC digest over some given data using the derived MAC key.