Initial commit of DBCryptoModule
authorSebastian Grabowski <s.grabowski@samsung.com>
Tue, 3 Jun 2014 07:22:34 +0000 (09:22 +0200)
committerBartlomiej Grzelewski <b.grzelewski@samsung.com>
Fri, 12 Sep 2014 12:57:08 +0000 (14:57 +0200)
Change-Id: Id3c0714b86f4b49f0caa1c7cac18c00db81f3c23

src/CMakeLists.txt
src/manager/service/DBCryptoModule.cpp [new file with mode: 0644]
src/manager/service/DBCryptoModule.h [new file with mode: 0644]

index 8bad590..8cf83d6 100644 (file)
@@ -19,6 +19,7 @@ SET(KEY_MANAGER_SOURCES
     ${KEY_MANAGER_PATH}/service/ckm-logic.cpp
     ${KEY_MANAGER_PATH}/service/key-provider.cpp
     ${KEY_MANAGER_PATH}/service/ocsp.cpp
+    ${KEY_MANAGER_PATH}/service/DBCryptoModule.cpp
     )
 
 SET_SOURCE_FILES_PROPERTIES(
diff --git a/src/manager/service/DBCryptoModule.cpp b/src/manager/service/DBCryptoModule.cpp
new file mode 100644 (file)
index 0000000..7e44cf2
--- /dev/null
@@ -0,0 +1,408 @@
+
+#include <iostream>
+#include <fstream>
+#include <stdio.h>
+#include <string.h>
+
+#include <openssl/bio.h>
+#include <openssl/evp.h>
+#include <openssl/rand.h>
+#include <openssl/sha.h>
+
+#include <dpl/log/log.h>
+#include <Base64.h>
+
+#include <DBCryptoModule.h>
+
+namespace CKM {
+
+DBCryptoModule::DBCryptoModule(RawBuffer &domainKEK)
+{
+    m_domainKEK = domainKEK;
+}
+
+bool DBCryptoModule::haveKey(const std::string &smackLabel)
+{
+    return (m_keyMap.count(smackLabel) > 0);
+}
+
+int DBCryptoModule::pushKey(const std::string &smackLabel,
+                            const RawBuffer &applicationKey)
+{
+    int ret = -1;
+
+    if (m_domainKEK.size() == 0)
+        return ret;
+    if (smackLabel.length() == 0)
+        return ret;
+    if (applicationKey.size() == 0)
+        return ret;
+    if (haveKey(smackLabel))
+        return ret;
+    RawBuffer appkey = applicationKey;
+    RawBuffer emptyiv;
+    if ((ret = decryptAES(appkey, 0, m_domainKEK, emptyiv)))
+        return ret;
+    m_keyMap[smackLabel] = appkey;
+
+    return EXIT_SUCCESS;
+}
+
+int DBCryptoModule::insertDigest(RawBuffer &data, const int dataSize)
+{
+    int ret = -1;
+    RawBuffer digest;
+
+    ret = digestData(data, dataSize, digest);
+    if (ret != 0)
+        return -1;
+    if (SHA_DIGEST_LENGTH != digest.size())
+        return -1;
+    data.insert(data.begin(), digest.begin(), digest.end());
+    ret = digest.size();
+
+    return ret;
+}
+
+int DBCryptoModule::removeDigest(RawBuffer &data, RawBuffer &digest)
+{
+    if (data.size() < SHA_DIGEST_LENGTH)
+        return -1;
+
+    int len = data.size();
+    digest.assign(data.begin(), data.begin() + SHA_DIGEST_LENGTH);
+    digest.resize(SHA_DIGEST_LENGTH);
+    data.erase(data.begin(), data.begin() + SHA_DIGEST_LENGTH);
+    data.resize(len - SHA_DIGEST_LENGTH);
+
+    return EXIT_SUCCESS;
+}
+
+int DBCryptoModule::encryptRow(const RawBuffer &password, DBRow &row)
+{
+    int ret = -1;
+    RawBuffer emptyiv;
+    DBRow crow = row;
+    int dlen;
+    RawBuffer userkey;
+    RawBuffer appkey;
+
+    if (m_domainKEK.size() == 0)
+        return ret;
+    if (row.dataSize <= 0)
+        return ret;
+    if (!haveKey(row.smackLabel))
+        return ret;
+    appkey = m_keyMap[row.smackLabel];
+    crow.encryptionScheme = 0;
+
+    dlen = insertDigest(crow.data, crow.dataSize);
+    if (dlen <= 0)
+        return ret;
+    ret = cryptAES(crow.data, crow.dataSize + dlen, appkey, emptyiv);
+    if (ret != 0)
+        return ret;
+    crow.encryptionScheme |= DBRow::ENCR_APPKEY;
+    if (password.size() > 0) {
+        if ((ret = generateKeysFromPassword(password, userkey, crow.iv)))
+            return ret;
+        ret = cryptAES(crow.data, 0, userkey, crow.iv);
+        if (ret != 0)
+            return ret;
+        crow.encryptionScheme |= DBRow::ENCR_PASSWORD;
+    }
+    ret = encBase64(crow.data);
+    if (ret != 0)
+        return ret;
+    crow.encryptionScheme |= DBRow::ENCR_BASE64;
+    ret = encBase64(crow.iv);
+    if (ret != 0)
+        return ret;
+    /* TODO: Add setting of algorithmType */
+    row = crow;
+
+    return ret;
+}
+
+int DBCryptoModule::decryptRow(const RawBuffer &password, DBRow &row)
+{
+    int ret = -1;
+    DBRow crow = row;
+    RawBuffer appkey;
+    RawBuffer userkey;
+    RawBuffer dropiv;
+    RawBuffer emptyiv;
+    RawBuffer digest, dataDigest;
+
+    if (m_domainKEK.size() == 0)
+        return ret;
+    if (row.dataSize <= 0)
+        return ret;
+    if (row.encryptionScheme && DBRow::ENCR_PASSWORD)
+        if (password.size() == 0)
+            return ret;
+    if (!haveKey(row.smackLabel))
+        return ret;
+    appkey = m_keyMap[row.smackLabel];
+
+    ret = decBase64(crow.iv);
+    if (ret)
+        return ret;
+    if (crow.encryptionScheme && DBRow::ENCR_BASE64) {
+        ret = decBase64(crow.data);
+        if (ret)
+            return ret;
+    }
+    if (crow.encryptionScheme && DBRow::ENCR_PASSWORD) {
+        if ((ret = generateKeysFromPassword(password, userkey, dropiv)))
+            return ret;
+        ret = decryptAES(crow.data, 0, userkey, crow.iv);
+        if (ret)
+            return ret;
+    }
+    if (crow.encryptionScheme && DBRow::ENCR_APPKEY) {
+        ret = decryptAES(crow.data, 0, appkey, emptyiv);
+        if (ret)
+            return ret;
+    }
+    ret = removeDigest(crow.data, digest);
+    if (ret)
+        return ret;
+    if ((unsigned int)crow.dataSize != crow.data.size())
+        return -1;
+    ret = digestData(crow.data, 0, dataDigest);
+    if (ret)
+        return ret;
+    if (not equalDigests(digest, dataDigest))
+        return -1;
+    row = crow;
+
+    return EXIT_SUCCESS;
+}
+
+int DBCryptoModule::generateRandIV(RawBuffer &iv)
+{
+    int ret = -1;
+    RawBuffer civ(EVP_MAX_IV_LENGTH);
+
+    if (iv.size() > 0)
+        ret = RAND_bytes(&civ[0], civ.size());
+    if (1 == ret) {
+        iv = civ;
+        ret = EXIT_SUCCESS;
+    } else {
+        ret = -1;
+    }
+
+    return ret;
+}
+
+int DBCryptoModule::generateKeysFromPassword(const RawBuffer &password,
+                                             RawBuffer &key, RawBuffer &iv)
+{
+    int ret = -1;
+    const EVP_CIPHER *cipher = EVP_aes_256_cbc();
+    unsigned int keyLen = EVP_CIPHER_key_length(cipher);
+    unsigned int ivLen = EVP_CIPHER_iv_length(cipher);
+#if 0
+    const EVP_MD *md = EVP_sha1();
+#endif
+
+    if ((password.size() == 0) || (password[0] == 0))
+        return ret;
+    key.resize(keyLen);
+    iv.resize(ivLen);
+    if ((ret = generateRandIV(iv)))
+        return ret;
+    ret = PKCS5_PBKDF2_HMAC_SHA1(reinterpret_cast<const char *>(&password[0]),
+                                 -1, NULL, 0, 1024, keyLen, &key[0]);
+    if (ret != 1)
+        return -1;
+    else
+        ret = EXIT_SUCCESS;
+#if 0
+    ret = EVP_BytesToKey(cipher, md, NULL,
+                         const_cast<const unsigned char *>(&password[0]),
+                         strlen(reinterpret_cast<const char *>(&password[0])),
+                         1, &key[0], &iv[0]);
+    LogDebug("Generated key len: " << ret);
+    if (ret > 0)
+        ret = EXIT_SUCCESS;
+#endif
+
+    return ret;
+}
+
+int DBCryptoModule::cryptAES(RawBuffer &data, int len, const RawBuffer &key,
+                             const RawBuffer &iv)
+{
+    int ret = -1;
+    EVP_CIPHER_CTX ctx;
+    const EVP_CIPHER *cipher = EVP_aes_256_cbc();
+    unsigned int keyLen = EVP_CIPHER_key_length(cipher);
+    unsigned int ivLen = EVP_CIPHER_iv_length(cipher);
+    int maxBufLen;
+    int outl, outlf;
+
+    if (keyLen <= 0)
+        return ret;
+    if (key.size() != keyLen)
+        return ret;
+    if (data.size() == 0)
+        return ret;
+    /* iv may be empty */
+    if (iv.size() > 0)
+        if (iv.size() != ivLen)
+            return -1;
+    if (0 == len)
+        len = data.size();
+    maxBufLen = len + EVP_CIPHER_block_size(cipher);
+
+    LogDebug("key len: " << keyLen);
+    LogDebug("iv len: " << ivLen);
+    LogDebug("buf len: " << maxBufLen);
+    LogDebug("len: " << len);
+
+    RawBuffer buf(maxBufLen);
+
+    EVP_CIPHER_CTX_init(&ctx);
+    ret = EVP_EncryptInit_ex(&ctx, cipher, NULL, &key[0], &iv[0]);
+    if (ret != 1)
+        return -1;
+    EVP_CIPHER_CTX_set_padding(&ctx, 1);
+    ret = EVP_EncryptUpdate(&ctx, &buf[0], &outl, &data[0], len);
+    if (ret != 1)
+        return -1;
+    ret = EVP_EncryptFinal_ex(&ctx, &buf[outl], &outlf);
+    if (ret != 1)
+        return -1;
+    LogDebug("Total out len: " << outl + outlf);
+    EVP_CIPHER_CTX_cleanup(&ctx);
+    data.assign(buf.begin(), buf.end());
+    data.resize(outl + outlf);
+
+    return EXIT_SUCCESS;
+}
+
+int DBCryptoModule::decryptAES(RawBuffer &data, int len, const RawBuffer &key,
+                               const RawBuffer &iv)
+{
+    int ret = -1;
+    EVP_CIPHER_CTX ctx;
+    const EVP_CIPHER *cipher = EVP_aes_256_cbc();
+    unsigned int keyLen = EVP_CIPHER_key_length(cipher);
+    unsigned int ivLen = EVP_CIPHER_iv_length(cipher);
+    int maxBufLen;
+    int outl, outlf;
+
+    if (keyLen <= 0)
+        return ret;
+    if (key.size() != keyLen)
+        return ret;
+    if (iv.size() > 0)
+        if (iv.size() != ivLen)
+            return -1;
+    if (0 == len)
+        len = data.size();
+
+    maxBufLen = len + EVP_CIPHER_block_size(cipher) + 1;
+
+    LogDebug("buf len: " << maxBufLen);
+    LogDebug("data len: " << len);
+
+    RawBuffer buf(maxBufLen, 0);
+
+    EVP_CIPHER_CTX_init(&ctx);
+    ret = EVP_DecryptInit_ex(&ctx, cipher, NULL, &key[0], &iv[0]);
+    if (ret != 1)
+        return -1;
+    EVP_CIPHER_CTX_set_padding(&ctx, 1);
+    ret = EVP_DecryptUpdate(&ctx, &buf[0], &outl, &data[0], len);
+    if (ret != 1)
+        return -1;
+    ret = EVP_DecryptFinal_ex(&ctx, &buf[outl], &outlf);
+    if (ret != 1)
+        return -1;
+    LogDebug("Total out len: " << outl + outlf);
+    EVP_CIPHER_CTX_cleanup(&ctx);
+    if ((outl + outlf) == 0)
+        return -1;
+    data.assign(buf.begin(), buf.end());
+    data.resize(outl + outlf);
+
+    return EXIT_SUCCESS;
+}
+
+int DBCryptoModule::digestData(const RawBuffer &data, int len, RawBuffer &digest)
+{
+    int ret = -1;
+    EVP_MD_CTX ctx;
+    const EVP_MD *md = EVP_sha1();
+    unsigned int dlen;
+
+    if (data.size() == 0)
+        return -1;
+    if (0 == len)
+        len = data.size();
+
+    ret = EVP_DigestInit(&ctx, md);
+    if (ret != 1)
+        return -1;
+    ret = EVP_DigestUpdate(&ctx, &data[0], len);
+    if (ret != 1)
+        return -1;
+    digest.resize(EVP_MAX_MD_SIZE);
+    ret = EVP_DigestFinal(&ctx, &digest[0], &dlen);
+    if (ret != 1)
+        return -1;
+    if (dlen != EVP_MAX_MD_SIZE)
+        digest.resize(dlen);
+
+    return EXIT_SUCCESS;
+}
+
+int DBCryptoModule::encBase64(RawBuffer &data)
+{
+    Base64Encoder benc;
+    RawBuffer encdata;
+
+    benc.append(data);
+    benc.finalize();
+    encdata = benc.get();
+
+    if (encdata.size() == 0)
+        return -1;
+
+    data = std::move(encdata);
+
+    return EXIT_SUCCESS;
+}
+
+int DBCryptoModule::decBase64(RawBuffer &data)
+{
+    Base64Decoder bdec;
+
+    bdec.reset();
+    bdec.append(data);
+    if (not bdec.finalize())
+        return -1;
+
+    RawBuffer decdata = bdec.get();
+    if (decdata.size() == 0)
+        return -1;
+
+    data = std::move(decdata);
+
+    return EXIT_SUCCESS;
+}
+
+bool DBCryptoModule::equalDigests(RawBuffer &dig1, RawBuffer &dig2)
+{
+    if ((dig1.size() != SHA_DIGEST_LENGTH) ||
+        (dig2.size() != SHA_DIGEST_LENGTH))
+        return false;
+    return std::equal(dig1.begin(), dig1.end(), &dig2[0]);
+}
+
+} // namespace CKM
+
diff --git a/src/manager/service/DBCryptoModule.h b/src/manager/service/DBCryptoModule.h
new file mode 100644 (file)
index 0000000..b0f714d
--- /dev/null
@@ -0,0 +1,55 @@
+#pragma once
+
+#include <map>
+#include <ckm/ckm-type.h>
+
+namespace CKM {
+
+struct DBRow {
+       static const int ENCR_BASE64 =   1 << 0;
+       static const int ENCR_APPKEY =   1 << 1;
+       static const int ENCR_PASSWORD = 1 << 2;
+
+       std::string user;
+       std::string smackLabel;
+       int dataType;                       // cert/key/data
+       int algorithmType;                  // AES mode ?
+       int encryptionScheme;               // for example: (ENCR_BASE64 | ENCR_PASSWORD)
+       RawBuffer iv;                       // encoded in base64
+       int dataSize;                       // size of information without hash and padding
+       RawBuffer data;
+};
+
+
+class DBCryptoModule {
+public:
+       DBCryptoModule(RawBuffer &domainKEK);
+
+       int decryptRow(const RawBuffer &password, DBRow &row);
+       int encryptRow(const RawBuffer &password, DBRow &row);
+
+       bool haveKey(const std::string &smackLabel);
+       int pushKey(const std::string &smackLabel, const RawBuffer &applicationKey);
+
+private:
+       RawBuffer m_domainKEK;
+       std::map<std::string, RawBuffer> m_keyMap;
+
+    /* TODO: Move it to private/protected after tests (or remove if not needed) */
+    int cryptAES(RawBuffer &data, int len, const RawBuffer &key,
+                 const RawBuffer &iv);
+    int decryptAES(RawBuffer &data, int len, const RawBuffer &key,
+                   const RawBuffer &iv);
+    int decBase64(RawBuffer &data);
+    int digestData(const RawBuffer &data, int len, RawBuffer &digest);
+    int encBase64(RawBuffer &data);
+    bool equalDigests(RawBuffer &dig1, RawBuffer &dig2);
+    int insertDigest(RawBuffer &data, const int dataSize);
+    int generateKeysFromPassword(const RawBuffer &password,
+                                 RawBuffer &key, RawBuffer &iv);
+    int generateRandIV(RawBuffer &iv);
+    int removeDigest(RawBuffer &data, RawBuffer &digest);
+};
+
+} // namespace CKM
+