adds encrypted password store

Sat, 15 Sep 2018 11:56:36 +0200

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Sat, 15 Sep 2018 11:56:36 +0200
changeset 470
6bf798ad3aec
parent 469
6ab1f4ad2835
child 471
d8e883bd1fd8

adds encrypted password store

new repo config element: <stored-user>
new dav command: add-user

dav/Makefile file | annotate | diff | comparison | revisions
dav/config.c file | annotate | diff | comparison | revisions
dav/config.h file | annotate | diff | comparison | revisions
dav/main.c file | annotate | diff | comparison | revisions
dav/main.h file | annotate | diff | comparison | revisions
dav/pwd.c file | annotate | diff | comparison | revisions
dav/pwd.h file | annotate | diff | comparison | revisions
libidav/crypto.c file | annotate | diff | comparison | revisions
libidav/crypto.h file | annotate | diff | comparison | revisions
test/crypto.c file | annotate | diff | comparison | revisions
test/crypto.h file | annotate | diff | comparison | revisions
test/main.c file | annotate | diff | comparison | revisions
--- a/dav/Makefile	Thu Sep 06 12:51:37 2018 +0200
+++ b/dav/Makefile	Sat Sep 15 11:56:36 2018 +0200
@@ -37,8 +37,9 @@
 DAV_SRC += assistant.c
 DAV_SRC += tar.c
 DAV_SRC += system.c
+DAV_SRC += pwd.c
 
-SYNC_SRC = sync.c
+SYNC_SRC  = sync.c
 SYNC_SRC += config.c
 SYNC_SRC += scfg.c
 SYNC_SRC += sopt.c
@@ -48,6 +49,7 @@
 SYNC_SRC += libxattr.c
 SYNC_SRC += tags.c
 SYNC_SRC += system.c
+SYNC_SRC += pwd.c
 
 DAV_OBJ = $(DAV_SRC:%.c=../build/tool/%$(OBJ_EXT))
 SYNC_OBJ = $(SYNC_SRC:%.c=../build/tool/%$(OBJ_EXT))
--- a/dav/config.c	Thu Sep 06 12:51:37 2018 +0200
+++ b/dav/config.c	Sat Sep 15 11:56:36 2018 +0200
@@ -34,6 +34,7 @@
 #include <errno.h>
 #include <libxml/tree.h>
 
+#include "pwd.h"
 #include "config.h"
 #include "main.h"
 #include <libidav/utils.h>
@@ -62,6 +63,8 @@
 static UcxMap *repos;
 static UcxMap *keys;
 
+static PwdStore *pstore;
+
 int check_config_dir(void) {
     char *file = util_concat_path(ENV_HOME, ".dav");
     int ret = 0;
@@ -100,6 +103,10 @@
     repos = ucx_map_new(16);
     keys = ucx_map_new(16);
     
+    char *pwfile = util_concat_path(ENV_HOME, ".dav/pw.crypt");
+    pstore = pwdstore_open(pwfile);
+    free(pwfile);
+    
     char *file = util_concat_path(ENV_HOME, ".dav/config.xml");
     
     struct stat s;
@@ -216,6 +223,8 @@
         repo->user = strdup(value);
     } else if(xstreq(key, "password")) {
         repo->password = util_base64decode(value);
+    } else if(xstreq(key, "stored-user")) {
+        repo->stored_user = strdup(value);
     } else if(xstreq(key, "default-key")) {
         repo->default_key = strdup(value);
     } else if(xstreq(key, "full-encryption")) {
@@ -751,3 +760,14 @@
     }
     return list;
 }
+
+PwdStore* get_pwdstore(void) {
+    return pstore;
+}
+
+int pwdstore_save(PwdStore *pwdstore) {
+    char *pwfile = util_concat_path(ENV_HOME, ".dav/pw.crypt");
+    int ret = pwdstore_store(pwdstore, pwfile);
+    free(pwfile);
+    return ret; 
+}
--- a/dav/config.h	Thu Sep 06 12:51:37 2018 +0200
+++ b/dav/config.h	Sat Sep 15 11:56:36 2018 +0200
@@ -32,6 +32,7 @@
 #include <ucx/string.h>
 #include <stdbool.h>
 #include <libidav/webdav.h>
+#include "pwd.h"
 
 #ifdef	__cplusplus
 extern "C" {
@@ -56,6 +57,7 @@
     char *url;
     char *user;
     char *password;
+    char *stored_user;
     char *default_key;
     char *cert;
     bool verification;
@@ -91,6 +93,9 @@
 int list_repositories(void);
 UcxList* get_repositories(void);
 
+PwdStore* get_pwdstore(void);
+int pwdstore_save(PwdStore *pwdstore);
+
 #ifdef	__cplusplus
 }
 #endif
--- a/dav/main.c	Thu Sep 06 12:51:37 2018 +0200
+++ b/dav/main.c	Sat Sep 15 11:56:36 2018 +0200
@@ -46,6 +46,7 @@
 #include "error.h"
 #include "assistant.h"
 #include "system.h"
+#include "pwd.h"
 #include "main.h"
 
 static DavContext *ctx;
@@ -152,6 +153,8 @@
         } else if(!strcasecmp(cmd, "repository-url")
                 || !strcasecmp(cmd, "repo-url")) {
             ret = cmd_repository_url(args);
+        } else if(!strcasecmp(cmd, "add-user")) {
+            ret = cmd_add_user(args);
         } else if(!strcasecmp(cmd, "version") || !strcasecmp(cmd, "-version")
                 || !strcasecmp(cmd, "--version")) {
             fprintf(stderr, "dav %s\n", DAV_VERSION);
@@ -402,7 +405,35 @@
 }
 
 static DavSession* connect_to_repo(Repository *repo, CmdArgs *a) {
-    DavSession *sn = dav_session_new_auth(ctx, repo->url, repo->user, repo->password);
+    char *user = repo->user;
+    char *password = repo->password;
+    if(repo->stored_user) {
+        PwdStore *pstore = get_pwdstore();
+        if(pstore) {
+            char *ps_password = util_password_input("Unlock password store: ");
+            if(ps_password) {
+                if(!pwdstore_setpassword(pstore, ps_password)) {
+                    if(!pwdstore_decrypt(pstore))  {
+                        PwdEntry *stored_user = pwdstore_get(pstore, repo->stored_user);
+                        if(stored_user) {
+                            user = stored_user->user;
+                            password = stored_user->password;
+                        } else {
+                            fprintf(stderr, "Error: stored user '%s' not found\n", repo->stored_user);
+                        }
+                    } else {
+                        fprintf(stderr, "Error: cannot decrypt password store\n");
+                    }
+                } else {
+                    fprintf(stderr, "Error: cannot create key from password\n");
+                }
+            }
+        } else {
+            fprintf(stderr, "Error: no password store available\n");
+        }
+    }
+    
+    DavSession *sn = dav_session_new_auth(ctx, repo->url, user, password);
     sn->flags = get_repository_flags(repo);
     sn->key = dav_context_get_key(ctx, repo->default_key);
     curl_easy_setopt(sn->handle, CURLOPT_HTTPAUTH, repo->authmethods);
@@ -2031,6 +2062,32 @@
     }
 }
 
+int cmd_add_user(CmdArgs *args) {
+    char *user = assistant_getcfg("User");
+    char *password = util_password_input("Password: ");
+    if(user && password) {
+        PwdStore *pstore = get_pwdstore();
+        if(!pstore) {
+            pstore = pwdstore_new();
+        }
+        pwdstore_put(pstore, user, password);
+        char *master_pw = util_password_input("Master password: ");
+        if(!master_pw) {
+            return 1;
+        }
+        if(pwdstore_setpassword(pstore, master_pw)) {
+            fprintf(stderr, "Error: Cannot generate key from password.\nAbort.\n");
+            return 1;
+        }
+        int ret = pwdstore_save(pstore);
+        if(ret) {
+            fprintf(stderr, "Error: saving password store failed.\n");
+        }
+        return ret;
+    }
+    return 1;
+}
+
 
 int shell_completion(CmdArgs *args, int index) {
     if(args->argc < 2 || args->argc < 3) {
--- a/dav/main.h	Thu Sep 06 12:51:37 2018 +0200
+++ b/dav/main.h	Sat Sep 15 11:56:36 2018 +0200
@@ -60,7 +60,6 @@
 typedef int(*getfunc)(Repository *, GetResource *, CmdArgs *, void *);
     
 void print_usage(char *cmd);
-char* password_input(char *prompt);
 int request_auth(Repository *repo, DavSession *sn, CmdArgs *args);
 
 int update_progress(DavResource *res, int64_t total, int64_t now, Progress *p);
@@ -104,6 +103,8 @@
 
 int cmd_repository_url(CmdArgs *args);
 
+int cmd_add_user(CmdArgs *args);
+
 int shell_completion(CmdArgs *args, int index);
 
 int url_completion(char *url);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dav/pwd.c	Sat Sep 15 11:56:36 2018 +0200
@@ -0,0 +1,233 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2018 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <netinet/in.h>
+
+#include "pwd.h"
+
+#include <ucx/buffer.h>
+#include <ucx/utils.h>
+
+
+
+PwdStore* pwdstore_open(const char *file) {
+    FILE *in = fopen(file, "r");
+    if(!in) {
+        return NULL;
+    }
+    
+    UcxBuffer *buf = ucx_buffer_new(NULL, 2048, UCX_BUFFER_AUTOEXTEND);
+    ucx_stream_copy(in, buf, (read_func)fread, (write_func)ucx_buffer_write);
+    fclose(in);
+    
+    if(buf->size < PWDS_HEADER_SIZE || buf->space[0] != PWDS_MAGIC_CHAR) {
+        ucx_buffer_free(buf);
+        return NULL;
+    }
+    
+    PwdStore *p = malloc(sizeof(PwdStore));
+    p->pwds = ucx_map_new(16);
+    p->content = buf;
+    p->key = NULL;
+    p->isdecrypted = 0;
+    
+    return p;
+}
+
+PwdStore* pwdstore_new(void) {
+    PwdStore *p = calloc(1, sizeof(PwdStore));
+    p->pwds = ucx_map_new(16);
+    p->content = ucx_buffer_new(NULL, PWDS_HEADER_SIZE, UCX_BUFFER_AUTOEXTEND);
+    PWDS_MAGIC(p) = PWDS_MAGIC_CHAR;
+    PWDS_VERSION(p) = 1;
+    PWDS_ENC(p) = DAV_KEY_AES256;
+    PWDS_PWFUNC(p) = DAV_PWFUNC_PBKDF2_SHA256;
+    dav_rand_bytes(p->content->space+4, 16);
+    return p;
+}
+
+static int read_pwdentry(PwdStore *p, UcxBuffer *in) {
+    int type = ucx_buffer_getc(in);
+    if(type == EOF || type != 0) {
+        // only type 0 supported yet
+        return 0;
+    }
+    
+    uint32_t ulen = 0;
+    uint32_t plen = 0;
+    
+    if(ucx_buffer_read(&ulen, 1, sizeof(uint32_t), in) != sizeof(uint32_t)) {
+        return 0;
+    }
+    ulen = ntohl(ulen);
+    if(ulen == 0 || ulen > PWDSTORE_MAX_LEN) {
+        return 0;
+    }
+    
+    char *user = malloc(ulen+1);
+    user[ulen] = 0;
+    if(ucx_buffer_read(user, 1, ulen, in) != ulen) {
+        free(user);
+        return 0;
+    }
+    
+    if(ucx_buffer_read(&plen, 1, sizeof(uint32_t), in) != sizeof(uint32_t)) {
+        return 0;
+    }
+    plen = ntohl(plen);
+    if(plen == 0 || plen > PWDSTORE_MAX_LEN) {
+        return 0;
+    }
+    char *password = malloc(plen+1);
+    password[plen] = 0;
+    if(ucx_buffer_read(password, 1, plen, in) != plen) {
+        free(user);
+        free(password);
+        return 0;
+    }
+    
+    pwdstore_put(p, user, password);
+    free(user);
+    free(password);
+    return 1;
+    
+}
+
+int pwdstore_decrypt(PwdStore *p) {
+    if(!p->key) {
+        return 1;
+    }
+    
+    // decrypt contet
+    size_t encsz = p->content->size - PWDS_HEADER_SIZE;
+    UcxBuffer *enc = ucx_buffer_new(p->content->space + PWDS_HEADER_SIZE, encsz, 0);
+    enc->size = encsz;
+    enc->size = p->content->size - PWDS_HEADER_SIZE;
+    UcxBuffer *content = aes_decrypt_buffer(enc, p->key);
+    ucx_buffer_free(enc);
+    if(!content) {
+        return 1;
+    }
+    
+    while(read_pwdentry(p, content)) {}
+    
+    ucx_buffer_free(content);
+    
+    return 0;
+}
+
+int pwdstore_setpassword(PwdStore *p, const char *password) {
+    DavKey *key = dav_pw2key(
+            password,
+            p->content->space + 4,
+            16,
+            PWDS_PWFUNC(p),
+            PWDS_ENC(p));
+    if(!key) {
+        return 1;
+    }
+    
+    p->key = key;
+    return 0;
+}
+
+void pwdstore_encsettings(PwdStore *p, uint8_t enc, uint8_t pwfunc) {
+    PWDS_ENC(p) = enc;
+    PWDS_PWFUNC(p) = pwfunc;
+}
+
+static void free_entry(PwdEntry *e) {
+    free(e->user);
+    free(e->password);
+    free(e);
+}
+
+void pwdstore_free(PwdStore* p) {
+    ucx_map_free_content(p->pwds, (ucx_destructor)free_entry);
+    ucx_map_free(p->pwds);
+    
+    if(p->content) {
+        ucx_buffer_free(p->content);
+    }
+    
+    free(p);
+}
+
+PwdEntry* pwdstore_get(PwdStore *p, const char *username) {
+    return ucx_map_cstr_get(p->pwds, username);
+}
+
+void pwdstore_put(PwdStore *p, const char *username, const char *password) {
+    PwdEntry *entry = malloc(sizeof(PwdEntry));
+    entry->user = strdup(username);
+    entry->password = strdup(password);
+    ucx_map_cstr_put(p->pwds, entry->user, entry);
+}
+
+int pwdstore_store(PwdStore *p, const char *file) {
+    if(!p->key) {
+        return 1;
+    }
+    
+    UcxBuffer *content = ucx_buffer_new(NULL, 2048, UCX_BUFFER_AUTOEXTEND);
+    
+    UcxMapIterator i = ucx_map_iterator(p->pwds);
+    PwdEntry *value;
+    UCX_MAP_FOREACH(key, value, i) {
+        ucx_buffer_putc(content, 0); // type
+        uint32_t ulen = strlen(value->user);
+        uint32_t plen = strlen(value->password);
+        uint32_t netulen = htonl(ulen);
+        uint32_t netplen = htonl(plen);
+        ucx_buffer_write(&netulen, 1, sizeof(uint32_t), content);
+        ucx_buffer_write(value->user, 1, ulen, content);
+        ucx_buffer_write(&netplen, 1, sizeof(uint32_t), content);
+        ucx_buffer_write(value->password, 1, plen, content);
+    }
+    
+    content->pos = 0;
+    UcxBuffer *enc = aes_encrypt_buffer(content, p->key);
+
+    p->content->pos = PWDS_HEADER_SIZE;
+    p->content->size = PWDS_HEADER_SIZE;
+    ucx_buffer_write(enc->space, 1, enc->size, p->content);
+    
+    ucx_buffer_free(enc);
+    
+    FILE *out = fopen(file, "w");
+    if(!out) {
+        return 1;
+    }
+    fwrite(p->content->space, 1, p->content->size, out);
+    fclose(out);
+    
+    return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dav/pwd.h	Sat Sep 15 11:56:36 2018 +0200
@@ -0,0 +1,138 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2018 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef PWD_H
+#define PWD_H
+
+#include <stdlib.h>
+#include <inttypes.h>
+
+#include <ucx/map.h>
+#include <ucx/buffer.h>
+#include <libidav/crypto.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define PWDSTORE_MAX_LEN 1024
+    
+/*
+ * File Format:
+ * 
+ * file = header, enc_content
+ * header = magic, version, enc, pwfunc, salt
+ * magic = 1 byte
+ * version = 1 byte
+ * enc = 1 byte
+ * pwfunc = 1 byte
+ * salt = 16 bytes
+ * content = { entry }
+ * entry = length username length password
+ * length = uint16
+ * username = string
+ * password = string
+ * 
+ * The content is AES encrypted with a key derived from a password
+ * and the salt. The first 16 bytes are the aes iv.
+ * 
+ * All integers are big endian
+ */
+    
+#define PWDS_HEADER_SIZE 20
+    
+typedef struct PwdStore PwdStore;
+typedef struct PwdEntry PwdEntry;
+
+struct PwdStore {
+    /*
+     * map of all usernames and passwords
+     * key is the username
+     * value is PwdEntry*
+     */
+    UcxMap *pwds;
+    
+    /*
+     * a buffer containing the complete file content
+     */
+    UcxBuffer *content;
+    
+    /*
+     * key used for encryption/decryption
+     */
+    DavKey *key;
+    
+    /*
+     * indicates if the PwdStore is decrypted with pwdstore_decrypt
+     */
+    uint8_t isdecrypted;
+};
+
+#define PWDS_MAGIC(p) (p)->content->space[0]
+#define PWDS_VERSION(p) (p)->content->space[1]
+#define PWDS_ENC(p) (p)->content->space[2]
+#define PWDS_PWFUNC(p) (p)->content->space[3]
+
+#define PWDS_MAGIC_CHAR 'P'
+
+struct PwdEntry {
+    char *user;
+    char *password;
+};
+
+/*
+ * opens the password store
+ * the content is still encrypted and must be decrypted using pwdstore_decrypt
+ */
+PwdStore* pwdstore_open(const char *file);
+
+PwdStore* pwdstore_new(void);
+
+/*
+ * decrypts the password store with a password
+ */
+int pwdstore_decrypt(PwdStore *p);
+
+int pwdstore_setpassword(PwdStore *p, const char *password);
+
+void pwdstore_encsettings(PwdStore *p, uint8_t enc, uint8_t pwfunc);
+
+void pwdstore_free(PwdStore* p);
+
+PwdEntry* pwdstore_get(PwdStore *p, const char *username);
+
+void pwdstore_put(PwdStore *p, const char *username, const char *password);
+
+int pwdstore_store(PwdStore *p, const char *file);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* PWD_H */
+
--- a/libidav/crypto.c	Thu Sep 06 12:51:37 2018 +0200
+++ b/libidav/crypto.c	Sat Sep 15 11:56:36 2018 +0200
@@ -56,6 +56,10 @@
 
 #endif
 
+int dav_rand_bytes(unsigned char *buf, size_t len) {
+    return !RAND_bytes(buf, len);
+}
+
 AESDecrypter* aes_decrypter_new(DavKey *key, void *stream, dav_write_func write_func) {
     AESDecrypter *dec = calloc(1, sizeof(AESDecrypter));
     SHA256_Init(&dec->sha256);
@@ -112,8 +116,8 @@
     
     int outlen = len + 16;
     unsigned char *out = malloc(outlen);
-    EVP_DecryptUpdate(dec->ctx, out, &len, buf, len);
-    ssize_t wlen = dec->write(out, 1, len, dec->stream);
+    EVP_DecryptUpdate(dec->ctx, out, &outlen, buf, len);
+    ssize_t wlen = dec->write(out, 1, outlen, dec->stream);
     SHA256_Update(&dec->sha256, out, wlen);
     free(out);
     return (s*n) / s;
@@ -344,6 +348,63 @@
     return util_hexstr(hash, DAV_SHA256_DIGEST_LENGTH);
 }
 
+DavKey* dav_pw2key(const char *password, const char *salt, int saltlen, int pwfunc, int enc) {
+    if(!password) {
+        return NULL;
+    }
+    size_t len = strlen(password);
+    if(len == 0) {
+        return NULL;
+    }
+    
+    // setup key data and length
+    unsigned char keydata[32];
+    int keylen = 32;
+    switch(enc) {
+        case DAV_KEY_AES128: keylen = 16; break;
+        case DAV_KEY_AES256: keylen = 32; break;
+        default: return NULL;
+    }
+    
+    // generate key
+    switch(pwfunc) {
+        case DAV_PWFUNC_PBKDF2_SHA256: {
+            PKCS5_PBKDF2_HMAC(
+                    password,
+                    len,
+                    salt,
+                    saltlen,
+                    DAV_CRYPTO_ITERATION_COUNT,
+                    EVP_sha256(),
+                    keylen,
+                    keydata);
+            break;
+        }
+        case DAV_PWFUNC_PBKDF2_SHA512: {
+            PKCS5_PBKDF2_HMAC(
+                    password,
+                    len,
+                    salt,
+                    saltlen,
+                    DAV_CRYPTO_ITERATION_COUNT,
+                    EVP_sha512(),
+                    keylen,
+                    keydata);
+            break;
+        }
+        default: return NULL;
+    }
+    
+    // create DavKey with generated data
+    DavKey *key = malloc(sizeof(DavKey));
+    key->data = malloc(keylen);
+    key->length = keylen;
+    key->name = NULL;
+    key->type = enc;
+    memcpy(key->data, keydata, keylen);
+    return key;
+}
+
 #endif
 
 
@@ -697,3 +758,46 @@
 }
 
 #endif
+
+UcxBuffer* aes_encrypt_buffer(UcxBuffer *in, DavKey *key) {
+    UcxBuffer *encbuf = ucx_buffer_new(
+            NULL,
+            in->size+16,
+            UCX_BUFFER_AUTOEXTEND);
+    
+    AESEncrypter *enc = aes_encrypter_new(
+            key,
+            in,
+            (dav_read_func)ucx_buffer_read);
+    if(!enc) {
+        ucx_buffer_free(encbuf);
+        return NULL;
+    }
+    
+    char buf[1024];
+    size_t r;
+    while((r = aes_read(buf, 1, 1024, enc)) > 0) {
+        ucx_buffer_write(buf, 1, r, encbuf);
+    }
+    aes_encrypter_close(enc);
+    
+    encbuf->pos = 0;
+    return encbuf;
+}
+
+UcxBuffer* aes_decrypt_buffer(UcxBuffer *in, DavKey *key) {
+    UcxBuffer *decbuf = ucx_buffer_new(
+            NULL,
+            in->size,
+            UCX_BUFFER_AUTOEXTEND);
+    AESDecrypter *dec = aes_decrypter_new(
+            key,
+            decbuf,
+            (dav_write_func)ucx_buffer_write);
+    
+    aes_write(in->space, 1, in->size, dec);
+    aes_decrypter_shutdown(dec);
+    aes_decrypter_close(dec);
+    decbuf->pos = 0;
+    return decbuf;
+}
--- a/libidav/crypto.h	Thu Sep 06 12:51:37 2018 +0200
+++ b/libidav/crypto.h	Sat Sep 15 11:56:36 2018 +0200
@@ -67,6 +67,11 @@
 extern "C" {
 #endif
 
+#define DAV_PWFUNC_PBKDF2_SHA256 0
+#define DAV_PWFUNC_PBKDF2_SHA512 1
+    
+#define DAV_CRYPTO_ITERATION_COUNT 4000
+    
 typedef struct {
     DAV_AES_CTX    ctx;
     DAV_SHA_CTX    sha256;
@@ -109,6 +114,11 @@
 
 char* dav_create_hash(const char *data, size_t len);
 
+DavKey* dav_pw2key(const char *password, const char *salt, int saltlen, int pwfunc, int enc);
+
+UcxBuffer* aes_encrypt_buffer(UcxBuffer *buf, DavKey *key);
+UcxBuffer* aes_decrypt_buffer(UcxBuffer *buf, DavKey *key);
+
 #ifdef	__cplusplus
 }
 #endif
--- a/test/crypto.c	Thu Sep 06 12:51:37 2018 +0200
+++ b/test/crypto.c	Sat Sep 15 11:56:36 2018 +0200
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright 2017 Olaf Wintermann. All rights reserved.
+ * Copyright 2018 Olaf Wintermann. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are met:
@@ -247,6 +247,34 @@
     UCX_TEST_END;
 }
 
+UCX_TEST(test_crypto_buffer) {
+    UCX_TEST_BEGIN;
+    
+    for(int i=0;i<32;i++) {
+        DavKey *key = i < 16 ? &keys256[i] : &keys128[i%16];
+        
+        for(int j=0;j<20;j++) {
+            UcxBuffer *content = ucx_buffer_new(NULL, 256, UCX_BUFFER_AUTOEXTEND);
+            ucx_buffer_puts(content, strings[j]);
+            content->pos = 0;
+            
+            UcxBuffer *enc = aes_encrypt_buffer(content, key);
+            UCX_TEST_ASSERT(enc->size >= content->size + 16, "aes_encrypt_buffer failed");
+            
+            UcxBuffer *dec = aes_decrypt_buffer(enc, key);
+            UCX_TEST_ASSERT(dec->size == content->size, "aes_decrypt_buffer failed");
+            
+            UCX_TEST_ASSERT(!memcmp(content->space, dec->space, dec->size), "decrypted buffer has wrong content");
+            
+            ucx_buffer_free(content);
+            ucx_buffer_free(enc);
+            ucx_buffer_free(dec);
+        }
+    }
+    
+    UCX_TEST_END;
+}
+
 UCX_TEST(test_crypto_stream) {
     UcxBuffer *data = ucx_buffer_new(NULL, 1024, UCX_BUFFER_AUTOEXTEND);
     UcxBuffer *cbuf = ucx_buffer_new(NULL, 1024, UCX_BUFFER_AUTOEXTEND);
@@ -283,6 +311,13 @@
 
             UCX_TEST_ASSERT(slen == pbuf->pos, "wrong length after enc-dec");
             UCX_TEST_ASSERT(!memcmp(strings[j], pbuf->space, slen), "wrong content after enc-dec");
+            
+            data->pos = 0;
+            UcxBuffer *enc2 = aes_encrypt_buffer(data, key);
+            UcxBuffer *dec2 = aes_decrypt_buffer(enc2, key);
+            
+            UCX_TEST_ASSERT(dec2->size == data->size, "dec2 has wrong size");
+            UCX_TEST_ASSERT(!memcmp(strings[j], dec2->space, dec2->size), "dec2 has wrong content");
         }
     }
     
--- a/test/crypto.h	Thu Sep 06 12:51:37 2018 +0200
+++ b/test/crypto.h	Sat Sep 15 11:56:36 2018 +0200
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright 2017 Olaf Wintermann. All rights reserved.
+ * Copyright 2018 Olaf Wintermann. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are met:
@@ -37,7 +37,7 @@
 
 UCX_TEST(test_util_decrypt_str_k);
 UCX_TEST(test_util_encrypt_str_k);
-
+UCX_TEST(test_crypto_buffer);
 UCX_TEST(test_crypto_stream);
 
 
--- a/test/main.c	Thu Sep 06 12:51:37 2018 +0200
+++ b/test/main.c	Sat Sep 15 11:56:36 2018 +0200
@@ -45,6 +45,7 @@
     ucx_test_register(suite, test_util_base64encode);
     ucx_test_register(suite, test_util_decrypt_str_k);
     ucx_test_register(suite, test_util_encrypt_str_k);
+    ucx_test_register(suite, test_crypto_buffer);
     ucx_test_register(suite, test_crypto_stream);
     
     ucx_test_run(suite, stdout);

mercurial