Squashed 'third_party/rawrtc/rawrtc/' content from commit aa3ae4b24

Change-Id: I38a655a4259b62f591334e90a1315bd4e7e4d8ec
git-subtree-dir: third_party/rawrtc/rawrtc
git-subtree-split: aa3ae4b247275cc6e69c30613b3a4ba7fdc82d1b
diff --git a/src/certificate/certificate.c b/src/certificate/certificate.c
new file mode 100644
index 0000000..9f68b8e
--- /dev/null
+++ b/src/certificate/certificate.c
@@ -0,0 +1,899 @@
+#include "certificate.h"
+#include "../utils/utils.h"
+#include <rawrtc/certificate.h>
+#include <rawrtc/config.h>
+#include <rawrtcc/code.h>
+#include <rawrtcc/utils.h>
+#include <re.h>
+#include <openssl/asn1.h>
+#include <openssl/asn1t.h>
+#include <openssl/bio.h>
+#include <openssl/bn.h>
+#include <openssl/crypto.h>
+#include <openssl/ec.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/objects.h>
+#include <openssl/pem.h>
+#include <openssl/rsa.h>
+#include <openssl/x509.h>
+#include <limits.h>  // INT_MAX, LONG_MAX
+#include <string.h>  // strlen
+
+#define DEBUG_MODULE "certificate"
+//#define RAWRTC_DEBUG_MODULE_LEVEL 7 // Note: Uncomment this to debug this module only
+#include <rawrtcc/debug.h>
+
+/*
+ * Default certificate options.
+ */
+struct rawrtc_certificate_options rawrtc_default_certificate_options = {
+    .key_type = RAWRTC_CERTIFICATE_KEY_TYPE_EC,
+    .common_name = "anonymous@rawrtc.org",
+    .valid_until = 3600 * 24 * 30,  // 30 days
+    .sign_algorithm = RAWRTC_CERTIFICATE_SIGN_ALGORITHM_SHA256,
+    .named_curve = "prime256v1",
+    .modulus_length = 3072,
+};
+
+/*
+ * Print and flush the OpenSSL error queue.
+ */
+static int print_openssl_error(char const* message, size_t length, void* arg) {
+    (void) message;
+    (void) length;
+    (void) arg;
+    DEBUG_WARNING("%b", message, length);
+
+    // 1 to continue outputting the error queue
+    return 1;
+}
+
+/*
+ * Generates an n-bit RSA key pair.
+ * Caller must call `EVP_PKEY_free(*keyp)` when done.
+ */
+static enum rawrtc_code generate_key_rsa(
+    EVP_PKEY** const keyp,  // de-referenced
+    uint_fast32_t const modulus_length) {
+    enum rawrtc_code error = RAWRTC_CODE_UNKNOWN_ERROR;
+    EVP_PKEY* key = NULL;
+    RSA* rsa = NULL;
+    BIGNUM* bn = NULL;
+
+    // Check arguments
+    if (!keyp) {
+        return RAWRTC_CODE_INVALID_ARGUMENT;
+    }
+#if (UINT_FAST32_MAX > INT_MAX)
+    if (modulus_length > INT_MAX) {
+        return RAWRTC_CODE_INVALID_ARGUMENT;
+    }
+#endif
+
+    // Create an empty EVP_PKEY structure
+    key = EVP_PKEY_new();
+    if (!key) {
+        DEBUG_WARNING("Could not create EVP_PKEY structure\n");
+        goto out;
+    }
+
+    // Initialise RSA structure
+    rsa = RSA_new();
+    if (!rsa) {
+        DEBUG_WARNING("Could not initialise RSA structure\n");
+        goto out;
+    }
+
+    // Allocate BIGNUM
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(OPENSSL_IS_BORINGSSL)
+    bn = BN_secure_new();
+#else
+    bn = BN_new();
+#endif
+    if (!bn) {
+        DEBUG_WARNING("Could not allocate BIGNUM\n");
+        goto out;
+    }
+
+    // Generate RSA key pair and store it in the RSA structure
+    BN_set_word(bn, RSA_F4);
+    if (!RSA_generate_key_ex(rsa, (int) modulus_length, bn, NULL)) {
+        DEBUG_WARNING("Could not generate RSA key pair\n");
+        goto out;
+    }
+
+    // Store the generated RSA key pair in the EVP_PKEY structure
+    if (!EVP_PKEY_set1_RSA(key, rsa)) {
+        DEBUG_WARNING("Could not assign RSA key pair to EVP_PKEY structure\n");
+        goto out;
+    }
+
+    // Done
+    error = RAWRTC_CODE_SUCCESS;
+
+out:
+    if (rsa) {
+        RSA_free(rsa);
+    }
+    if (bn) {
+        BN_free(bn);
+    }
+    if (error) {
+        if (key) {
+            EVP_PKEY_free(key);
+        }
+        ERR_print_errors_cb(print_openssl_error, NULL);
+    } else {
+        *keyp = key;
+    }
+    return error;
+}
+
+/*
+ * Generates an ECC key pair.
+ * Caller must call `EVP_PKEY_free(*keyp)` when done.
+ */
+static enum rawrtc_code generate_key_ecc(
+    EVP_PKEY** const keyp,  // de-referenced
+    char* const named_curve) {
+    enum rawrtc_code error = RAWRTC_CODE_UNKNOWN_ERROR;
+    EVP_PKEY* key = NULL;
+    int curve_group_nid;
+    EC_KEY* ecc = NULL;
+
+    // Check arguments
+    if (!keyp || !named_curve) {
+        return RAWRTC_CODE_INVALID_ARGUMENT;
+    }
+
+    // Create an empty EVP_PKEY structure
+    key = EVP_PKEY_new();
+    if (!key) {
+        DEBUG_WARNING("Could not create EVP_PKEY structure\n");
+        goto out;
+    }
+
+    // Get NID of named curve
+    curve_group_nid = OBJ_txt2nid(named_curve);
+    if (curve_group_nid == NID_undef) {
+        DEBUG_WARNING("Could not determine group NID of named curve: %s\n", named_curve);
+        goto out;
+    }
+
+    // Initialise EC structure for named curve
+    ecc = EC_KEY_new_by_curve_name(curve_group_nid);
+    if (!ecc) {
+        DEBUG_WARNING("Could not initialise EC structure for named curve\n");
+        goto out;
+    }
+
+    // This is needed to correctly sign the certificate
+    EC_KEY_set_asn1_flag(ecc, OPENSSL_EC_NAMED_CURVE);
+
+    // Generate the ECC key pair and store it in the EC structure
+    if (!EC_KEY_generate_key(ecc)) {
+        DEBUG_WARNING("Could not generate ECC key pair\n");
+        goto out;
+    }
+
+    // Store the generated ECC key pair in the EVP_PKEY structure
+    if (!EVP_PKEY_assign_EC_KEY(key, ecc)) {
+        DEBUG_WARNING("Could not assign ECC key pair to EVP_PKEY structure\n");
+        goto out;
+    }
+
+    // Done
+    error = RAWRTC_CODE_SUCCESS;
+
+out:
+    if (error) {
+        if (ecc) {
+            EC_KEY_free(ecc);
+        }
+        if (key) {
+            EVP_PKEY_free(key);
+        }
+        ERR_print_errors_cb(print_openssl_error, NULL);
+    } else {
+        *keyp = key;
+    }
+    return error;
+}
+
+/*
+ * Generates a self-signed certificate.
+ * Caller must call `X509_free(*certificatep)` when done.
+ */
+static enum rawrtc_code generate_self_signed_certificate(
+    X509** const certificatep,  // de-referenced
+    EVP_PKEY* const key,
+    char* const common_name,
+    uint_fast32_t const valid_until,
+    enum rawrtc_certificate_sign_algorithm const sign_algorithm) {
+    enum rawrtc_code error = RAWRTC_CODE_UNKNOWN_ERROR;
+    X509* certificate = NULL;
+    X509_NAME* name = NULL;
+    EVP_MD const* sign_function;
+
+    // Check arguments
+    if (!certificatep || !key || !common_name) {
+        return RAWRTC_CODE_INVALID_ARGUMENT;
+    }
+#if (UINT_FAST32_MAX > LONG_MAX)
+    if (valid_until > LONG_MAX) {
+        return RAWRTC_CODE_INVALID_ARGUMENT;
+    }
+#endif
+
+    // Get sign function
+    sign_function = rawrtc_get_sign_function(sign_algorithm);
+    if (!sign_function) {
+        return RAWRTC_CODE_INVALID_ARGUMENT;
+    }
+
+    // Allocate and initialise x509 structure
+    certificate = X509_new();
+    if (!certificate) {
+        DEBUG_WARNING("Could not initialise x509 structure\n");
+        goto out;
+    }
+
+    // Set x509 version
+    // Note: '2' maps to version 3
+    if (!X509_set_version(certificate, 2)) {
+        DEBUG_WARNING("Could not set x509 version\n");
+        goto out;
+    }
+
+    // Set the serial number randomly (doesn't need to be unique as we are self-signing)
+    if (!ASN1_INTEGER_set(X509_get_serialNumber(certificate), rand_u32())) {
+        DEBUG_WARNING("Could not set x509 serial number\n");
+        goto out;
+    }
+
+    // Create an empty X509_NAME structure
+    name = X509_NAME_new();
+    if (!name) {
+        DEBUG_WARNING("Could not create x509_NAME structure\n");
+        goto out;
+    }
+
+    // Set common name field on X509_NAME structure
+    if (!X509_NAME_add_entry_by_txt(
+            name, "CN", MBSTRING_ASC, (uint8_t*) common_name, (int) strlen(common_name), -1, 0)) {
+        DEBUG_WARNING("Could not apply common name (%s) on certificate\n", common_name);
+        goto out;
+    }
+
+    // Set issuer and subject name
+    if (!X509_set_issuer_name(certificate, name) || !X509_set_subject_name(certificate, name)) {
+        DEBUG_WARNING("Could not set issuer name on certificate\n");
+        goto out;
+    }
+
+    // Certificate is valid from now (-1 day) until whatever has been provided in parameters
+    if (!X509_gmtime_adj(X509_get_notBefore(certificate), -3600 * 24) ||
+        !X509_gmtime_adj(X509_get_notAfter(certificate), (long) valid_until)) {
+        DEBUG_WARNING("Could not apply lifetime range to certificate\n");
+        goto out;
+    }
+
+    // Set public key of certificate
+    if (!X509_set_pubkey(certificate, key)) {
+        DEBUG_WARNING("Could not set public key to certificate\n");
+        goto out;
+    }
+
+    // Sign the certificate
+    if (!X509_sign(certificate, key, sign_function)) {
+        DEBUG_WARNING("Could not sign the certificate\n");
+        goto out;
+    }
+
+    // Done
+    error = RAWRTC_CODE_SUCCESS;
+
+out:
+    if (name) {
+        X509_NAME_free(name);
+    }
+    if (error) {
+        if (certificate) {
+            X509_free(certificate);
+        }
+        ERR_print_errors_cb(print_openssl_error, NULL);
+    } else {
+        *certificatep = certificate;
+    }
+    return error;
+}
+
+/*
+ * Destructor for existing certificate options.
+ */
+static void rawrtc_certificate_options_destroy(void* arg) {
+    struct rawrtc_certificate_options* const options = arg;
+
+    // Un-reference
+    mem_deref(options->named_curve);
+    mem_deref(options->common_name);
+}
+
+/*
+ * Create certificate options.
+ *
+ * All arguments but `key_type` are optional. Sane and safe default
+ * values will be applied, don't worry!
+ *
+ * `*optionsp` must be unreferenced.
+ *
+ * If `common_name` is `NULL` the default common name will be applied.
+ * If `valid_until` is `0` the default certificate lifetime will be
+ * applied.
+ * If the key type is `ECC` and `named_curve` is `NULL`, the default
+ * named curve will be used.
+ * If the key type is `RSA` and `modulus_length` is `0`, the default
+ * amount of bits will be used. The same applies to the
+ * `sign_algorithm` if it has been set to `NONE`.
+ */
+enum rawrtc_code rawrtc_certificate_options_create(
+    struct rawrtc_certificate_options** const optionsp,  // de-referenced
+    enum rawrtc_certificate_key_type const key_type,
+    char* common_name,  // nullable, copied
+    uint_fast32_t valid_until,
+    enum rawrtc_certificate_sign_algorithm sign_algorithm,
+    char* named_curve,  // nullable, copied, ignored for RSA
+    uint_fast32_t modulus_length  // ignored for ECC
+) {
+    struct rawrtc_certificate_options* options;
+    enum rawrtc_code error = RAWRTC_CODE_SUCCESS;
+
+    // Check arguments
+    if (!optionsp) {
+        return RAWRTC_CODE_INVALID_ARGUMENT;
+    }
+#if (UINT_FAST32_MAX > LONG_MAX)
+    if (valid_until > LONG_MAX) {
+        return RAWRTC_CODE_INVALID_ARGUMENT;
+    }
+#endif
+#if (UINT_FAST32_MAX > INT_MAX)
+    if (modulus_length > INT_MAX) {
+        return RAWRTC_CODE_INVALID_ARGUMENT;
+    }
+#endif
+
+    // Set defaults
+    if (!common_name) {
+        common_name = rawrtc_default_certificate_options.common_name;
+    }
+    if (!valid_until) {
+        valid_until = rawrtc_default_certificate_options.valid_until;
+    }
+
+    // Check sign algorithm/set default
+    // Note: We say 'no' to SHA1 intentionally
+    // Note: SHA-384 and SHA-512 are currently not supported (needs to be added to libre)
+    switch (sign_algorithm) {
+        case RAWRTC_CERTIFICATE_SIGN_ALGORITHM_NONE:
+            sign_algorithm = rawrtc_default_certificate_options.sign_algorithm;
+            break;
+        case RAWRTC_CERTIFICATE_SIGN_ALGORITHM_SHA256:
+            break;
+        default:
+            return RAWRTC_CODE_INVALID_ARGUMENT;
+    }
+
+    // Set defaults depending on key type
+    switch (key_type) {
+        case RAWRTC_CERTIFICATE_KEY_TYPE_RSA:
+            // Unset ECC vars
+            named_curve = NULL;
+
+            // Prevent user from being stupid
+            if (modulus_length < RAWRTC_MODULUS_LENGTH_MIN) {
+                modulus_length = rawrtc_default_certificate_options.modulus_length;
+            }
+
+            break;
+
+        case RAWRTC_CERTIFICATE_KEY_TYPE_EC:
+            // Unset RSA vars
+            modulus_length = 0;
+
+            // Set default named curve (if required)
+            if (!named_curve) {
+                named_curve = rawrtc_default_certificate_options.named_curve;
+            }
+
+            break;
+
+        default:
+            return RAWRTC_CODE_INVALID_STATE;
+    }
+
+    // Allocate
+    options = mem_zalloc(sizeof(*options), rawrtc_certificate_options_destroy);
+    if (!options) {
+        return RAWRTC_CODE_NO_MEMORY;
+    }
+
+    // Set fields/copy
+    options->key_type = key_type;
+    if (common_name) {
+        error = rawrtc_strdup(&options->common_name, common_name);
+        if (error) {
+            goto out;
+        }
+    }
+    options->valid_until = valid_until;
+    options->sign_algorithm = sign_algorithm;
+    if (named_curve) {
+        error = rawrtc_strdup(&options->named_curve, named_curve);
+        if (error) {
+            goto out;
+        }
+    }
+    options->modulus_length = modulus_length;
+
+out:
+    if (error) {
+        mem_deref(options);
+    } else {
+        // Set pointer
+        *optionsp = options;
+    }
+    return error;
+}
+
+/*
+ * Destructor for existing certificate.
+ */
+static void rawrtc_certificate_destroy(void* arg) {
+    struct rawrtc_certificate* const certificate = arg;
+
+    // Free
+    if (certificate->certificate) {
+        X509_free(certificate->certificate);
+    }
+    if (certificate->key) {
+        EVP_PKEY_free(certificate->key);
+    }
+}
+
+/*
+ * Create and generate a self-signed certificate.
+ *
+ * Sane and safe default options will be applied if `options` is
+ * `NULL`.
+ *
+ * `*certificatep` must be unreferenced.
+ */
+enum rawrtc_code rawrtc_certificate_generate(
+    struct rawrtc_certificate** const certificatep,
+    struct rawrtc_certificate_options* options  // nullable
+) {
+    struct rawrtc_certificate* certificate;
+    enum rawrtc_code error;
+
+    // Check arguments
+    if (!certificatep) {
+        return RAWRTC_CODE_INVALID_ARGUMENT;
+    }
+
+    // Default options
+    if (!options) {
+        options = &rawrtc_default_certificate_options;
+    }
+
+    // Allocate
+    certificate = mem_zalloc(sizeof(*certificate), rawrtc_certificate_destroy);
+    if (!certificate) {
+        return RAWRTC_CODE_NO_MEMORY;
+    }
+
+    // Generate key pair
+    switch (options->key_type) {
+        case RAWRTC_CERTIFICATE_KEY_TYPE_RSA:
+            error = generate_key_rsa(&certificate->key, options->modulus_length);
+            break;
+        case RAWRTC_CERTIFICATE_KEY_TYPE_EC:
+            error = generate_key_ecc(&certificate->key, options->named_curve);
+            break;
+        default:
+            return RAWRTC_CODE_INVALID_STATE;
+    }
+    if (error) {
+        goto out;
+    }
+
+    // Generate certificate
+    error = generate_self_signed_certificate(
+        &certificate->certificate, certificate->key, options->common_name, options->valid_until,
+        options->sign_algorithm);
+    if (error) {
+        goto out;
+    }
+
+    // Set key type
+    certificate->key_type = options->key_type;
+
+out:
+    if (error) {
+        mem_deref(certificate);
+    } else {
+        // Set pointer
+        *certificatep = certificate;
+    }
+    return error;
+}
+
+/*
+ * Copy a certificate.
+ * References the x509 certificate and private key.
+ */
+enum rawrtc_code rawrtc_certificate_copy(
+    struct rawrtc_certificate** const certificatep,  // de-referenced
+    struct rawrtc_certificate* const source_certificate) {
+    enum rawrtc_code error = RAWRTC_CODE_UNKNOWN_ERROR;
+    struct rawrtc_certificate* certificate;
+
+    // Check arguments
+    if (!certificatep || !source_certificate) {
+        return RAWRTC_CODE_INVALID_ARGUMENT;
+    }
+
+    // Allocate
+    certificate = mem_zalloc(sizeof(*certificate), rawrtc_certificate_destroy);
+    if (!certificate) {
+        return RAWRTC_CODE_NO_MEMORY;
+    }
+
+    // Increment reference count of certificate and private key, copy the pointers
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+    if (!X509_up_ref(source_certificate->certificate)) {
+        goto out;
+    }
+#else
+    if (!CRYPTO_add(&source_certificate->certificate->references, 1, CRYPTO_LOCK_X509)) {
+        goto out;
+    }
+#endif
+    certificate->certificate = source_certificate->certificate;
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+    if (!EVP_PKEY_up_ref(source_certificate->key)) {
+        goto out;
+    }
+#else
+    if (!CRYPTO_add(&source_certificate->key->references, 1, CRYPTO_LOCK_EVP_PKEY)) {
+        goto out;
+    }
+#endif
+    certificate->key = source_certificate->key;
+    certificate->key_type = source_certificate->key_type;
+
+    // Done
+    error = RAWRTC_CODE_SUCCESS;
+
+out:
+    if (error) {
+        mem_deref(certificate);
+        ERR_print_errors_cb(print_openssl_error, NULL);
+    } else {
+        // Set pointer
+        *certificatep = certificate;
+    }
+    return error;
+}
+
+static enum rawrtc_code what_to_encode(
+    enum rawrtc_certificate_encode const to_encode,
+    bool* encode_certificatep,  // de-referenced
+    bool* encode_keyp  // de-referenced
+) {
+    *encode_certificatep = false;
+    *encode_keyp = false;
+
+    // What to encode?
+    switch (to_encode) {
+        case RAWRTC_CERTIFICATE_ENCODE_CERTIFICATE:
+            *encode_certificatep = true;
+            break;
+        case RAWRTC_CERTIFICATE_ENCODE_PRIVATE_KEY:
+            *encode_keyp = true;
+            break;
+        case RAWRTC_CERTIFICATE_ENCODE_BOTH:
+            *encode_certificatep = true;
+            *encode_keyp = true;
+            break;
+        default:
+            return RAWRTC_CODE_INVALID_ARGUMENT;
+    }
+
+    // Done
+    return RAWRTC_CODE_SUCCESS;
+}
+
+/*
+ * Get PEM of the certificate and/or the private key if requested.
+ * *pemp will NOT be null-terminated!
+ */
+enum rawrtc_code rawrtc_certificate_get_pem(
+    char** const pemp,  // de-referenced
+    size_t* const pem_lengthp,  // de-referenced
+    struct rawrtc_certificate* const certificate,
+    enum rawrtc_certificate_encode const to_encode) {
+    bool encode_certificate;
+    bool encode_key;
+    enum rawrtc_code error;
+    BIO* bio = NULL;
+    char* pem = NULL;
+    uint64_t length;
+
+    // Check arguments
+    if (!pemp || !certificate) {
+        return RAWRTC_CODE_INVALID_ARGUMENT;
+    }
+
+    // What to encode?
+    error = what_to_encode(to_encode, &encode_certificate, &encode_key);
+    if (error) {
+        return error;
+    }
+    error = RAWRTC_CODE_UNKNOWN_ERROR;
+
+    // Create bio structure
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(OPENSSL_IS_BORINGSSL)
+    bio = BIO_new(BIO_s_secmem());
+#else
+    bio = BIO_new(BIO_s_mem());
+#endif
+
+    // Write certificate
+    if (encode_certificate && !PEM_write_bio_X509(bio, certificate->certificate)) {
+        goto out;
+    }
+
+    // Write private key (if requested)
+    if (encode_key && !PEM_write_bio_PrivateKey(bio, certificate->key, NULL, NULL, 0, 0, NULL)) {
+        goto out;
+    }
+
+    // Allocate buffer
+    length = BIO_number_written(bio);
+#if (UINT64_MAX > INT_MAX)
+    if (length > INT_MAX) {
+        goto out;
+    }
+#endif
+    pem = mem_alloc(length, NULL);
+    if (!pem) {
+        error = RAWRTC_CODE_NO_MEMORY;
+        goto out;
+    }
+
+    // Copy to buffer
+    if (BIO_read(bio, pem, (int) length) < (int) length) {
+        goto out;
+    }
+
+    // Done
+    error = RAWRTC_CODE_SUCCESS;
+
+out:
+    if (bio) {
+        BIO_free(bio);
+    }
+    if (error) {
+        mem_deref(pem);
+        ERR_print_errors_cb(print_openssl_error, NULL);
+    } else {
+        // Set pointers
+        *pemp = pem;
+        *pem_lengthp = length;
+    }
+    return error;
+}
+
+/*
+ * Get DER of the certificate and/or the private key if requested.
+ * *derp will NOT be null-terminated!
+ */
+enum rawrtc_code rawrtc_certificate_get_der(
+    uint8_t** const derp,  // de-referenced
+    size_t* const der_lengthp,  // de-referenced
+    struct rawrtc_certificate* const certificate,
+    enum rawrtc_certificate_encode const to_encode) {
+    bool encode_certificate;
+    bool encode_key;
+    enum rawrtc_code error;
+    int length_certificate = 0;
+    int length_key = 0;
+    size_t length;
+    uint8_t* der = NULL;
+    uint8_t* der_i2d;
+
+    // Check arguments
+    if (!derp || !certificate) {
+        return RAWRTC_CODE_INVALID_ARGUMENT;
+    }
+
+    // What to encode?
+    error = what_to_encode(to_encode, &encode_certificate, &encode_key);
+    if (error) {
+        return error;
+    }
+    error = RAWRTC_CODE_UNKNOWN_ERROR;
+
+    // Allocate buffer
+    if (encode_certificate) {
+        length_certificate = i2d_X509(certificate->certificate, NULL);
+        if (length_certificate < 1) {
+            return RAWRTC_CODE_UNKNOWN_ERROR;
+        }
+    }
+    if (encode_key) {
+        length_key = i2d_PrivateKey(certificate->key, NULL);
+        if (length_key < 1) {
+            return RAWRTC_CODE_UNKNOWN_ERROR;
+        }
+    }
+    length = (size_t)(length_certificate + length_key);
+    der = mem_alloc(length, NULL);
+    if (!der) {
+        error = RAWRTC_CODE_NO_MEMORY;
+        goto out;
+    }
+    der_i2d = der;
+
+    // Write certificate
+    if (encode_certificate && i2d_X509(certificate->certificate, &der_i2d) < length_certificate) {
+        goto out;
+    }
+
+    // Write private key (if requested)
+    if (encode_key && i2d_PrivateKey(certificate->key, &der_i2d) < length_key) {
+        goto out;
+    }
+
+    // Done
+    error = RAWRTC_CODE_SUCCESS;
+
+out:
+    if (error) {
+        mem_deref(der);
+        ERR_print_errors_cb(print_openssl_error, NULL);
+    } else {
+        // Set pointers
+        *derp = der;
+        *der_lengthp = length;
+    }
+    return error;
+}
+
+/*
+ * Get certificate's fingerprint.
+ * Caller must ensure that `buffer` has space for
+ * `RAWRTC_FINGERPRINT_MAX_SIZE_HEX` bytes
+ */
+enum rawrtc_code rawrtc_certificate_get_fingerprint(
+    char** const fingerprint,  // de-referenced
+    struct rawrtc_certificate* const certificate,
+    enum rawrtc_certificate_sign_algorithm const algorithm) {
+    EVP_MD const* sign_function;
+    uint8_t bytes_buffer[RAWRTC_FINGERPRINT_MAX_SIZE_HEX];
+    uint_least32_t length;
+
+    // Check arguments
+    if (!fingerprint || !certificate) {
+        return RAWRTC_CODE_INVALID_ARGUMENT;
+    }
+
+    // Get sign function for algorithm
+    sign_function = rawrtc_get_sign_function(algorithm);
+    if (!sign_function) {
+        return RAWRTC_CODE_INVALID_ARGUMENT;
+    }
+
+    // Generate certificate fingerprint
+    if (!X509_digest(certificate->certificate, sign_function, bytes_buffer, &length)) {
+        return RAWRTC_CODE_NO_VALUE;
+    }
+    if (length < 1) {
+        return RAWRTC_CODE_UNKNOWN_ERROR;
+    }
+
+    // Convert bytes to hex
+    return rawrtc_bin_to_colon_hex(fingerprint, bytes_buffer, (size_t) length);
+}
+
+/*
+ * Copy and append a certificate to a list.
+ */
+static enum rawrtc_code copy_and_append_certificate(
+    struct list* const certificate_list,  // de-referenced, not checked
+    struct rawrtc_certificate* const certificate  // copied
+) {
+    enum rawrtc_code error;
+    struct rawrtc_certificate* copied_certificate;
+
+    // Check arguments
+    if (!certificate) {
+        return RAWRTC_CODE_INVALID_ARGUMENT;
+    }
+
+    // Copy certificate
+    // Note: Copying is needed as the 'le' element cannot be associated to multiple lists
+    error = rawrtc_certificate_copy(&copied_certificate, certificate);
+    if (error) {
+        return error;
+    }
+
+    // Append to list
+    list_append(certificate_list, &copied_certificate->le, copied_certificate);
+    return RAWRTC_CODE_SUCCESS;
+}
+
+/*
+ * Copy an array of certificates to a list.
+ * Warning: The list will be flushed on error.
+ */
+enum rawrtc_code rawrtc_certificate_array_to_list(
+    struct list* const certificate_list,  // de-referenced, copied into
+    struct rawrtc_certificate* const certificates[],  // copied (each item)
+    size_t const n_certificates) {
+    size_t i;
+    enum rawrtc_code error = RAWRTC_CODE_SUCCESS;
+
+    // Check arguments
+    if (!certificate_list || !certificates) {
+        return RAWRTC_CODE_INVALID_ARGUMENT;
+    }
+
+    // Append and reference certificates
+    for (i = 0; i < n_certificates; ++i) {
+        error = copy_and_append_certificate(certificate_list, certificates[i]);
+        if (error) {
+            goto out;
+        }
+    }
+
+out:
+    if (error) {
+        list_flush(certificate_list);
+    }
+    return error;
+}
+
+/*
+ * Copy a certificate list.
+ * Warning: The destination list will be flushed on error.
+ */
+enum rawrtc_code rawrtc_certificate_list_copy(
+    struct list* const destination_list,  // de-referenced, copied into
+    struct list* const source_list  // de-referenced, copied (each item)
+) {
+    struct le* le;
+    enum rawrtc_code error = RAWRTC_CODE_SUCCESS;
+
+    // Check arguments
+    if (!destination_list || !source_list) {
+        return RAWRTC_CODE_INVALID_ARGUMENT;
+    }
+
+    // Append and reference certificates
+    for (le = list_head(source_list); le != NULL; le = le->next) {
+        struct rawrtc_certificate* const certificate = le->data;
+        error = copy_and_append_certificate(destination_list, certificate);
+        if (error) {
+            goto out;
+        }
+    }
+
+out:
+    if (error) {
+        list_flush(destination_list);
+    }
+    return error;
+}
diff --git a/src/certificate/certificate.h b/src/certificate/certificate.h
new file mode 100644
index 0000000..4409928
--- /dev/null
+++ b/src/certificate/certificate.h
@@ -0,0 +1,90 @@
+#pragma once
+#include <rawrtc/certificate.h>
+#include <re.h>
+#include <openssl/evp.h>  // EVP_*
+#include <openssl/x509.h>  // X509
+
+/*
+ * Maximum digest size of certificate fingerprint.
+ */
+enum {
+    RAWRTC_MODULUS_LENGTH_MIN = 1024,
+    RAWRTC_FINGERPRINT_MAX_SIZE = EVP_MAX_MD_SIZE,
+    RAWRTC_FINGERPRINT_MAX_SIZE_HEX = (EVP_MAX_MD_SIZE * 2),
+};
+
+/*
+ * Certificate options.
+ */
+struct rawrtc_certificate_options {
+    enum rawrtc_certificate_key_type key_type;
+    char* common_name;  // copied
+    uint_fast32_t valid_until;
+    enum rawrtc_certificate_sign_algorithm sign_algorithm;
+    char* named_curve;  // nullable, copied, ignored for RSA
+    uint_fast32_t modulus_length;  // ignored for ECC
+};
+
+/*
+ * Certificate.
+ */
+struct rawrtc_certificate {
+    struct le le;
+    X509* certificate;
+    EVP_PKEY* key;
+    enum rawrtc_certificate_key_type key_type;
+};
+
+extern struct rawrtc_certificate_options rawrtc_default_certificate_options;
+
+enum rawrtc_code rawrtc_certificate_copy(
+    struct rawrtc_certificate** const certificatep,  // de-referenced
+    struct rawrtc_certificate* const source_certificate);
+
+enum rawrtc_code rawrtc_certificate_get_pem(
+    char** const pemp,  // de-referenced
+    size_t* const pem_lengthp,  // de-referenced
+    struct rawrtc_certificate* const certificate,
+    enum rawrtc_certificate_encode const to_encode);
+
+enum rawrtc_code rawrtc_certificate_get_der(
+    uint8_t** const derp,  // de-referenced
+    size_t* const der_lengthp,  // de-referenced
+    struct rawrtc_certificate* const certificate,
+    enum rawrtc_certificate_encode const to_encode);
+
+enum rawrtc_code rawrtc_certificate_get_fingerprint(
+    char** const fingerprint,  // de-referenced
+    struct rawrtc_certificate* const certificate,
+    enum rawrtc_certificate_sign_algorithm const algorithm);
+
+enum rawrtc_code rawrtc_certificate_array_to_list(
+    struct list* const certificate_list,  // de-referenced, copied into
+    struct rawrtc_certificate* const certificates[],  // copied (each item)
+    size_t const n_certificates);
+
+enum rawrtc_code rawrtc_certificate_list_copy(
+    struct list* const destination_list,  // de-referenced, copied into
+    struct list* const source_list  // de-referenced, copied (each item)
+);
+
+enum tls_keytype rawrtc_certificate_key_type_to_tls_keytype(
+    const enum rawrtc_certificate_key_type type);
+
+enum rawrtc_code rawrtc_tls_keytype_to_certificate_key_type(
+    enum rawrtc_certificate_key_type* const typep,  // de-referenced
+    enum tls_keytype const re_type);
+
+enum rawrtc_code rawrtc_certificate_sign_algorithm_to_tls_fingerprint(
+    enum tls_fingerprint* const fingerprintp,  // de-referenced
+    enum rawrtc_certificate_sign_algorithm const algorithm);
+
+enum rawrtc_code rawrtc_tls_fingerprint_to_certificate_sign_algorithm(
+    enum rawrtc_certificate_sign_algorithm* const algorithmp,  // de-referenced
+    enum tls_fingerprint re_algorithm);
+
+EVP_MD const* rawrtc_get_sign_function(enum rawrtc_certificate_sign_algorithm type);
+
+enum rawrtc_code rawrtc_get_sign_algorithm_length(
+    size_t* const sizep,  // de-referenced
+    enum rawrtc_certificate_sign_algorithm const type);
diff --git a/src/certificate/meson.build b/src/certificate/meson.build
new file mode 100644
index 0000000..9d0e932
--- /dev/null
+++ b/src/certificate/meson.build
@@ -0,0 +1,4 @@
+sources += files([
+    'certificate.c',
+    'utils.c',
+])
diff --git a/src/certificate/utils.c b/src/certificate/utils.c
new file mode 100644
index 0000000..e9ab58c
--- /dev/null
+++ b/src/certificate/utils.c
@@ -0,0 +1,177 @@
+#include "certificate.h"
+#include <rawrtc/certificate.h>
+#include <rawrtcc/code.h>
+#include <re.h>
+
+/*
+ * Translate a certificate key type to the corresponding re type.
+ */
+enum tls_keytype rawrtc_certificate_key_type_to_tls_keytype(
+    enum rawrtc_certificate_key_type const type) {
+    // No conversion needed
+    return (enum tls_keytype) type;
+}
+
+/*
+ * Translate a re key type to the corresponding rawrtc type.
+ */
+enum rawrtc_code rawrtc_tls_keytype_to_certificate_key_type(
+    enum rawrtc_certificate_key_type* const typep,  // de-referenced
+    enum tls_keytype const re_type) {
+    // Check arguments
+    if (!typep) {
+        return RAWRTC_CODE_INVALID_ARGUMENT;
+    }
+
+    // Convert ice_cand_type
+    switch (re_type) {
+        case TLS_KEYTYPE_RSA:
+            *typep = RAWRTC_CERTIFICATE_KEY_TYPE_RSA;
+            return RAWRTC_CODE_SUCCESS;
+        case TLS_KEYTYPE_EC:
+            *typep = RAWRTC_CERTIFICATE_KEY_TYPE_EC;
+            return RAWRTC_CODE_SUCCESS;
+        default:
+            return RAWRTC_CODE_INVALID_ARGUMENT;
+    }
+}
+
+/*
+ * Translate a certificate sign algorithm to the corresponding re fingerprint algorithm.
+ */
+enum rawrtc_code rawrtc_certificate_sign_algorithm_to_tls_fingerprint(
+    enum tls_fingerprint* const fingerprintp,  // de-referenced
+    enum rawrtc_certificate_sign_algorithm const algorithm) {
+    switch (algorithm) {
+        case RAWRTC_CERTIFICATE_SIGN_ALGORITHM_NONE:
+            return RAWRTC_CODE_INVALID_ARGUMENT;
+        case RAWRTC_CERTIFICATE_SIGN_ALGORITHM_SHA384:
+        case RAWRTC_CERTIFICATE_SIGN_ALGORITHM_SHA512:
+            // Note: SHA-384 and SHA-512 are currently not supported (needs to be added to re)
+            return RAWRTC_CODE_UNSUPPORTED_ALGORITHM;
+        default:
+            break;
+    }
+
+    // No conversion needed
+    *fingerprintp = (enum tls_fingerprint) algorithm;
+    return RAWRTC_CODE_SUCCESS;
+}
+
+/*
+ * Translate a re fingerprint algorithm to the corresponding rawrtc algorithm.
+ */
+enum rawrtc_code rawrtc_tls_fingerprint_to_certificate_sign_algorithm(
+    enum rawrtc_certificate_sign_algorithm* const algorithmp,  // de-referenced
+    enum tls_fingerprint re_algorithm) {
+    // Check arguments
+    if (!algorithmp) {
+        return RAWRTC_CODE_INVALID_ARGUMENT;
+    }
+
+    // Convert ice_cand_type
+    // Note: SHA-384 and SHA-512 are currently not supported (needs to be added to libre)
+    switch (re_algorithm) {
+        case TLS_FINGERPRINT_SHA256:
+            *algorithmp = RAWRTC_CERTIFICATE_SIGN_ALGORITHM_SHA256;
+            return RAWRTC_CODE_SUCCESS;
+        default:
+            return RAWRTC_CODE_INVALID_ARGUMENT;
+    }
+}
+
+static enum rawrtc_certificate_sign_algorithm const map_enum_certificate_sign_algorithm[] = {
+    RAWRTC_CERTIFICATE_SIGN_ALGORITHM_SHA256,
+    RAWRTC_CERTIFICATE_SIGN_ALGORITHM_SHA384,
+    RAWRTC_CERTIFICATE_SIGN_ALGORITHM_SHA512,
+};
+
+static char const* const map_str_certificate_sign_algorithm[] = {
+    "sha-256",
+    "sha-384",
+    "sha-512",
+};
+
+static size_t const map_certificate_sign_algorithm_length =
+    ARRAY_SIZE(map_enum_certificate_sign_algorithm);
+
+/*
+ * Translate a certificate sign algorithm to str.
+ */
+char const* rawrtc_certificate_sign_algorithm_to_str(
+    enum rawrtc_certificate_sign_algorithm const algorithm) {
+    size_t i;
+
+    for (i = 0; i < map_certificate_sign_algorithm_length; ++i) {
+        if (map_enum_certificate_sign_algorithm[i] == algorithm) {
+            return map_str_certificate_sign_algorithm[i];
+        }
+    }
+
+    return "???";
+}
+
+/*
+ * Translate a str to a certificate sign algorithm (case-insensitive).
+ */
+enum rawrtc_code rawrtc_str_to_certificate_sign_algorithm(
+    enum rawrtc_certificate_sign_algorithm* const algorithmp,  // de-referenced
+    char const* const str) {
+    size_t i;
+
+    // Check arguments
+    if (!algorithmp || !str) {
+        return RAWRTC_CODE_INVALID_ARGUMENT;
+    }
+
+    for (i = 0; i < map_certificate_sign_algorithm_length; ++i) {
+        if (str_casecmp(map_str_certificate_sign_algorithm[i], str) == 0) {
+            *algorithmp = map_enum_certificate_sign_algorithm[i];
+            return RAWRTC_CODE_SUCCESS;
+        }
+    }
+
+    return RAWRTC_CODE_NO_VALUE;
+}
+
+/*
+ * Get the EVP_MD* structure for a certificate sign algorithm type.
+ */
+EVP_MD const* rawrtc_get_sign_function(enum rawrtc_certificate_sign_algorithm const type) {
+    switch (type) {
+        case RAWRTC_CERTIFICATE_SIGN_ALGORITHM_SHA256:
+            return EVP_sha256();
+        case RAWRTC_CERTIFICATE_SIGN_ALGORITHM_SHA384:
+            return EVP_sha384();
+        case RAWRTC_CERTIFICATE_SIGN_ALGORITHM_SHA512:
+            return EVP_sha512();
+        default:
+            return NULL;
+    }
+}
+
+/*
+ * Get the length of the fingerprint to a certificate sign algorithm type.
+ */
+enum rawrtc_code rawrtc_get_sign_algorithm_length(
+    size_t* const sizep,  // de-referenced
+    enum rawrtc_certificate_sign_algorithm const type) {
+    EVP_MD const* sign_function;
+    int size;
+
+    // Get sign algorithm function
+    sign_function = rawrtc_get_sign_function(type);
+    if (!sign_function) {
+        return RAWRTC_CODE_INVALID_ARGUMENT;
+    }
+
+    // Get length
+    size = EVP_MD_size(sign_function);
+    if (size < 1) {
+        return RAWRTC_CODE_UNSUPPORTED_ALGORITHM;
+    }
+
+    // Set size
+    *sizep = (size_t) size;
+    return RAWRTC_CODE_SUCCESS;
+}