avformat/crypto: added encryption
authorChristian Suloway <csuloway@row44.com>
Mon, 1 Dec 2014 18:55:28 +0000 (12:55 -0600)
committerMichael Niedermayer <michaelni@gmx.at>
Tue, 2 Dec 2014 00:46:10 +0000 (01:46 +0100)
Signed-off-by: Christian Suloway <csuloway@globaleagleent.com>
Signed-off-by: Michael Niedermayer <michaelni@gmx.at>
libavformat/crypto.c

index a9b6e47..f3a85c7 100644 (file)
@@ -41,14 +41,32 @@ typedef struct {
     int keylen;
     uint8_t *iv;
     int ivlen;
-    struct AVAES *aes;
+    uint8_t *decrypt_key;
+    int decrypt_keylen;
+    uint8_t *decrypt_iv;
+    int decrypt_ivlen;
+    uint8_t *encrypt_key;
+    int encrypt_keylen;
+    uint8_t *encrypt_iv;
+    int encrypt_ivlen;
+    struct AVAES *aes_decrypt;
+    struct AVAES *aes_encrypt;
+
+    uint8_t pad[BLOCKSIZE];
+    int pad_len;
+
 } CryptoContext;
 
 #define OFFSET(x) offsetof(CryptoContext, x)
 #define D AV_OPT_FLAG_DECODING_PARAM
+#define E AV_OPT_FLAG_ENCODING_PARAM
 static const AVOption options[] = {
-    {"key", "AES decryption key", OFFSET(key), AV_OPT_TYPE_BINARY, .flags = D },
-    {"iv",  "AES decryption initialization vector", OFFSET(iv), AV_OPT_TYPE_BINARY, .flags = D },
+    {"key", "AES encryption/decryption key",                   OFFSET(key),         AV_OPT_TYPE_BINARY, .flags = D|E },
+    {"iv",  "AES encryption/decryption initialization vector", OFFSET(iv),          AV_OPT_TYPE_BINARY, .flags = D|E },
+    {"decryption_key", "AES decryption key",                   OFFSET(decrypt_key), AV_OPT_TYPE_BINARY, .flags = D },
+    {"decryption_iv",  "AES decryption initialization vector", OFFSET(decrypt_iv),  AV_OPT_TYPE_BINARY, .flags = D },
+    {"encryption_key", "AES encryption key",                   OFFSET(encrypt_key), AV_OPT_TYPE_BINARY, .flags = E },
+    {"encryption_iv",  "AES encryption initialization vector", OFFSET(encrypt_iv),  AV_OPT_TYPE_BINARY, .flags = E },
     { NULL }
 };
 
@@ -59,6 +77,34 @@ static const AVClass crypto_class = {
     .version        = LIBAVUTIL_VERSION_INT,
 };
 
+static int set_aes_arg(CryptoContext *c, uint8_t **buf, int *buf_len,
+                       uint8_t *default_buf, int default_buf_len,
+                       const char *desc)
+{
+    if (!*buf_len) {
+        if (!default_buf_len) {
+            av_log(c, AV_LOG_ERROR, "%s not set\n", desc);
+            return AVERROR(EINVAL);
+        } else if (default_buf_len != BLOCKSIZE) {
+            av_log(c, AV_LOG_ERROR,
+                   "invalid %s size (%d bytes, block size is %d)\n",
+                   desc, default_buf_len, BLOCKSIZE);
+            return AVERROR(EINVAL);
+        }
+        *buf = av_malloc(default_buf_len);
+        if (!*buf)
+            return AVERROR(ENOMEM);
+        memcpy(*buf, default_buf, default_buf_len);
+        *buf_len = default_buf_len;
+    } else if (*buf_len != BLOCKSIZE) {
+        av_log(c, AV_LOG_ERROR,
+               "invalid %s size (%d bytes, block size is %d)\n",
+               desc, *buf_len, BLOCKSIZE);
+        return AVERROR(EINVAL);
+    }
+    return 0;
+}
+
 static int crypto_open2(URLContext *h, const char *uri, int flags, AVDictionary **options)
 {
     const char *nested_url;
@@ -72,28 +118,54 @@ static int crypto_open2(URLContext *h, const char *uri, int flags, AVDictionary
         goto err;
     }
 
-    if (c->keylen < BLOCKSIZE || c->ivlen < BLOCKSIZE) {
-        av_log(h, AV_LOG_ERROR, "Key or IV not set\n");
-        ret = AVERROR(EINVAL);
-        goto err;
+    if (flags & AVIO_FLAG_READ) {
+        if ((ret = set_aes_arg(c, &c->decrypt_key, &c->decrypt_keylen,
+                               c->key, c->keylen, "decryption key")) < 0)
+            goto err;
+        if ((ret = set_aes_arg(c, &c->decrypt_iv, &c->decrypt_ivlen,
+                               c->key, c->keylen, "decryption IV")) < 0)
+            goto err;
     }
+
     if (flags & AVIO_FLAG_WRITE) {
-        av_log(h, AV_LOG_ERROR, "Only decryption is supported currently\n");
-        ret = AVERROR(ENOSYS);
-        goto err;
+        if ((ret = set_aes_arg(c, &c->encrypt_key, &c->encrypt_keylen,
+                               c->key, c->keylen, "encryption key")) < 0)
+        if (ret < 0)
+            goto err;
+        if ((ret = set_aes_arg(c, &c->encrypt_iv, &c->encrypt_ivlen,
+                               c->key, c->keylen, "encryption IV")) < 0)
+            goto err;
     }
-    if ((ret = ffurl_open(&c->hd, nested_url, AVIO_FLAG_READ,
+
+    if ((ret = ffurl_open(&c->hd, nested_url, flags,
                           &h->interrupt_callback, options)) < 0) {
-        av_log(h, AV_LOG_ERROR, "Unable to open input\n");
+        av_log(h, AV_LOG_ERROR, "Unable to open resource: %s\n", nested_url);
         goto err;
     }
-    c->aes = av_aes_alloc();
-    if (!c->aes) {
-        ret = AVERROR(ENOMEM);
-        goto err;
+
+    if (flags & AVIO_FLAG_READ) {
+        c->aes_decrypt = av_aes_alloc();
+        if (!c->aes_decrypt) {
+            ret = AVERROR(ENOMEM);
+            goto err;
+        }
+        ret = av_aes_init(c->aes_decrypt, c->decrypt_key, BLOCKSIZE*8, 1);
+        if (ret < 0)
+            goto err;
     }
 
-    av_aes_init(c->aes, c->key, 128, 1);
+    if (flags & AVIO_FLAG_WRITE) {
+        c->aes_encrypt = av_aes_alloc();
+        if (!c->aes_encrypt) {
+            ret = AVERROR(ENOMEM);
+            goto err;
+        }
+        ret = av_aes_init(c->aes_encrypt, c->encrypt_key, BLOCKSIZE*8, 0);
+        if (ret < 0)
+            goto err;
+    }
+
+    c->pad_len = 0;
 
     h->is_streamed = 1;
 
@@ -131,8 +203,8 @@ retry:
         return AVERROR_EOF;
     if (!c->eof)
         blocks--;
-    av_aes_crypt(c->aes, c->outbuffer, c->inbuffer + c->indata_used, blocks,
-                 c->iv, 1);
+    av_aes_crypt(c->aes_decrypt, c->outbuffer, c->inbuffer + c->indata_used,
+                 blocks, c->decrypt_iv, 1);
     c->outdata      = BLOCKSIZE * blocks;
     c->outptr       = c->outbuffer;
     c->indata_used += BLOCKSIZE * blocks;
@@ -150,12 +222,65 @@ retry:
     goto retry;
 }
 
+static int crypto_write(URLContext *h, const unsigned char *buf, int size)
+{
+    CryptoContext *c = h->priv_data;
+    int total_size, blocks, pad_len, out_size;
+    uint8_t *out_buf;
+    int ret = 0;
+
+    total_size = size + c->pad_len;
+    pad_len = total_size % BLOCKSIZE;
+    out_size = total_size - pad_len;
+    blocks = out_size / BLOCKSIZE;
+
+    if (out_size) {
+        out_buf = av_malloc(out_size);
+        if (!out_buf)
+            return AVERROR(ENOMEM);
+
+        if (c->pad_len) {
+            memcpy(&c->pad[c->pad_len], buf, BLOCKSIZE - c->pad_len);
+            av_aes_crypt(c->aes_encrypt, out_buf, c->pad, 1, c->encrypt_iv, 0);
+            blocks--;
+        }
+
+        av_aes_crypt(c->aes_encrypt, &out_buf[c->pad_len ? BLOCKSIZE : 0],
+                             &buf[c->pad_len ? BLOCKSIZE - c->pad_len: 0],
+                             blocks, c->encrypt_iv, 0);
+
+        ret = ffurl_write(c->hd, out_buf, out_size);
+        av_free(out_buf);
+        if (ret < 0)
+            return ret;
+
+        memcpy(c->pad, &buf[size - pad_len], pad_len);
+    } else
+        memcpy(&c->pad[c->pad_len], buf, size);
+
+    c->pad_len = pad_len;
+
+    return size;
+}
+
 static int crypto_close(URLContext *h)
 {
     CryptoContext *c = h->priv_data;
+    uint8_t out_buf[BLOCKSIZE];
+    int ret, pad;
+
+    if (c->aes_encrypt) {
+        pad = BLOCKSIZE - c->pad_len;
+        memset(&c->pad[c->pad_len], pad, pad);
+        av_aes_crypt(c->aes_encrypt, out_buf, c->pad, 1, c->encrypt_iv, 0);
+        if ((ret =  ffurl_write(c->hd, out_buf, BLOCKSIZE)) < 0)
+            return ret;
+    }
+
     if (c->hd)
         ffurl_close(c->hd);
-    av_freep(&c->aes);
+    av_freep(&c->aes_decrypt);
+    av_freep(&c->aes_encrypt);
     return 0;
 }
 
@@ -163,6 +288,7 @@ URLProtocol ff_crypto_protocol = {
     .name            = "crypto",
     .url_open2       = crypto_open2,
     .url_read        = crypto_read,
+    .url_write       = crypto_write,
     .url_close       = crypto_close,
     .priv_data_size  = sizeof(CryptoContext),
     .priv_data_class = &crypto_class,