Squashed 'third_party/rawrtc/re/' content from commit f3163ce8b

Change-Id: I6a235e6ac0f03269d951026f9d195da05c40fdab
git-subtree-dir: third_party/rawrtc/re
git-subtree-split: f3163ce8b526a13b35ef71ce4dd6f43585064d8a
diff --git a/src/aes/openssl/aes.c b/src/aes/openssl/aes.c
new file mode 100644
index 0000000..4d710f2
--- /dev/null
+++ b/src/aes/openssl/aes.c
@@ -0,0 +1,267 @@
+/**
+ * @file openssl/aes.c  AES (Advanced Encryption Standard) using OpenSSL
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#include <string.h>
+#include <openssl/aes.h>
+#include <openssl/evp.h>
+#include <openssl/err.h>
+#include <re_types.h>
+#include <re_fmt.h>
+#include <re_mem.h>
+#include <re_aes.h>
+
+
+struct aes {
+	EVP_CIPHER_CTX *ctx;
+	enum aes_mode mode;
+	bool encr;
+};
+
+
+static const EVP_CIPHER *aes_cipher(enum aes_mode mode, size_t key_bits)
+{
+	if (mode == AES_MODE_CTR) {
+
+		switch (key_bits) {
+
+		case 128: return EVP_aes_128_ctr();
+		case 192: return EVP_aes_192_ctr();
+		case 256: return EVP_aes_256_ctr();
+		default:
+			return NULL;
+		}
+	}
+	else if (mode == AES_MODE_GCM) {
+
+		switch (key_bits) {
+
+		case 128: return EVP_aes_128_gcm();
+		case 256: return EVP_aes_256_gcm();
+		default:
+			return NULL;
+		}
+	}
+	else {
+		return NULL;
+	}
+}
+
+
+static inline bool set_crypt_dir(struct aes *aes, bool encr)
+{
+	if (aes->encr != encr) {
+
+		/* update the encrypt/decrypt direction */
+		if (!EVP_CipherInit_ex(aes->ctx, NULL, NULL,
+				       NULL, NULL, encr)) {
+			ERR_clear_error();
+			return false;
+		}
+
+		aes->encr = encr;
+	}
+
+	return true;
+}
+
+
+static void destructor(void *arg)
+{
+	struct aes *st = arg;
+
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+	if (st->ctx)
+		EVP_CIPHER_CTX_free(st->ctx);
+#else
+	if (st->ctx)
+		EVP_CIPHER_CTX_cleanup(st->ctx);
+	mem_deref(st->ctx);
+#endif
+}
+
+
+int aes_alloc(struct aes **aesp, enum aes_mode mode,
+	      const uint8_t *key, size_t key_bits,
+	      const uint8_t *iv)
+{
+	const EVP_CIPHER *cipher;
+	struct aes *st;
+	int err = 0, r;
+
+	if (!aesp || !key)
+		return EINVAL;
+
+	cipher = aes_cipher(mode, key_bits);
+	if (!cipher)
+		return ENOTSUP;
+
+	st = mem_zalloc(sizeof(*st), destructor);
+	if (!st)
+		return ENOMEM;
+
+	st->mode = mode;
+	st->encr = true;
+
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+	st->ctx = EVP_CIPHER_CTX_new();
+	if (!st->ctx) {
+		ERR_clear_error();
+		err = ENOMEM;
+		goto out;
+	}
+
+#else
+	st->ctx = mem_zalloc(sizeof(*st->ctx), NULL);
+	if (!st->ctx) {
+		err = ENOMEM;
+		goto out;
+	}
+
+	EVP_CIPHER_CTX_init(st->ctx);
+#endif
+
+	r = EVP_EncryptInit_ex(st->ctx, cipher, NULL, key, iv);
+	if (!r) {
+		ERR_clear_error();
+		err = EPROTO;
+	}
+
+ out:
+	if (err)
+		mem_deref(st);
+	else
+		*aesp = st;
+
+	return err;
+}
+
+
+void aes_set_iv(struct aes *aes, const uint8_t *iv)
+{
+	int r;
+
+	if (!aes || !iv)
+		return;
+
+	r = EVP_CipherInit_ex(aes->ctx, NULL, NULL, NULL, iv, -1);
+	if (!r)
+		ERR_clear_error();
+}
+
+
+int aes_encr(struct aes *aes, uint8_t *out, const uint8_t *in, size_t len)
+{
+	int c_len = (int)len;
+
+	if (!aes || !in)
+		return EINVAL;
+
+	if (!set_crypt_dir(aes, true))
+		return EPROTO;
+
+	if (!EVP_EncryptUpdate(aes->ctx, out, &c_len, in, (int)len)) {
+		ERR_clear_error();
+		return EPROTO;
+	}
+
+	return 0;
+}
+
+
+int aes_decr(struct aes *aes, uint8_t *out, const uint8_t *in, size_t len)
+{
+	int c_len = (int)len;
+
+	if (!aes || !in)
+		return EINVAL;
+
+	if (!set_crypt_dir(aes, false))
+		return EPROTO;
+
+	if (!EVP_DecryptUpdate(aes->ctx, out, &c_len, in, (int)len)) {
+		ERR_clear_error();
+		return EPROTO;
+	}
+
+	return 0;
+}
+
+
+/**
+ * Get the authentication tag for an AEAD cipher (e.g. GCM)
+ *
+ * @param aes    AES Context
+ * @param tag    Authentication tag
+ * @param taglen Length of Authentication tag
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int aes_get_authtag(struct aes *aes, uint8_t *tag, size_t taglen)
+{
+	int tmplen;
+
+	if (!aes || !tag || !taglen)
+		return EINVAL;
+
+	switch (aes->mode) {
+
+	case AES_MODE_GCM:
+		if (!EVP_EncryptFinal_ex(aes->ctx, NULL, &tmplen)) {
+			ERR_clear_error();
+			return EPROTO;
+		}
+
+		if (!EVP_CIPHER_CTX_ctrl(aes->ctx, EVP_CTRL_GCM_GET_TAG,
+					 (int)taglen, tag)) {
+			ERR_clear_error();
+			return EPROTO;
+		}
+
+		return 0;
+
+	default:
+		return ENOTSUP;
+	}
+}
+
+
+/**
+ * Authenticate a decryption tag for an AEAD cipher (e.g. GCM)
+ *
+ * @param aes    AES Context
+ * @param tag    Authentication tag
+ * @param taglen Length of Authentication tag
+ *
+ * @return 0 if success, otherwise errorcode
+ *
+ * @retval EAUTH if authentication failed
+ */
+int aes_authenticate(struct aes *aes, const uint8_t *tag, size_t taglen)
+{
+	int tmplen;
+
+	if (!aes || !tag || !taglen)
+		return EINVAL;
+
+	switch (aes->mode) {
+
+	case AES_MODE_GCM:
+		if (!EVP_CIPHER_CTX_ctrl(aes->ctx, EVP_CTRL_GCM_SET_TAG,
+					 (int)taglen, (void *)tag)) {
+			ERR_clear_error();
+			return EPROTO;
+		}
+
+		if (EVP_DecryptFinal_ex(aes->ctx, NULL, &tmplen) <= 0) {
+			ERR_clear_error();
+			return EAUTH;
+		}
+
+		return 0;
+
+	default:
+		return ENOTSUP;
+	}
+}