summaryrefslogtreecommitdiffabout
Side-by-side diff
Diffstat (more/less context) (show whitespace changes)
-rw-r--r--pwmanager/pwmanager/libgcryptif.cpp435
-rw-r--r--pwmanager/pwmanager/libgcryptif.h158
2 files changed, 593 insertions, 0 deletions
diff --git a/pwmanager/pwmanager/libgcryptif.cpp b/pwmanager/pwmanager/libgcryptif.cpp
new file mode 100644
index 0000000..8e55144
--- a/dev/null
+++ b/pwmanager/pwmanager/libgcryptif.cpp
@@ -0,0 +1,435 @@
+/***************************************************************************
+ * *
+ * copyright (C) 2004 by Michael Buesch *
+ * email: mbuesch@freenet.de *
+ * *
+ * hashPassphrase() is derived from GnuPG and is *
+ * Copyright (C) 1998, 1999, 2000, 2001, 2003 *
+ * Free Software Foundation, Inc. *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License version 2 *
+ * as published by the Free Software Foundation. *
+ * *
+ ***************************************************************************/
+
+#include "libgcryptif.h"
+
+#ifdef CONFIG_PWMANAGER_GCRY
+
+#include "pwmdoc.h"
+#include "randomizer.h"
+
+#include <gcrypt.h>
+
+PwMerror LibGCryptIf::encrypt(unsigned char **outBuf,
+ size_t *outBufLen,
+ unsigned char *inBuf,
+ size_t inBufLen,
+ const unsigned char *key,
+ size_t keylen,
+ char _algo)
+{
+ PwMerror ret = e_success;
+ gcry_error_t err;
+ gcry_cipher_hd_t handle;
+ size_t blklen;
+ size_t unpaddedLen = inBufLen;
+ size_t cipherKeylen;
+ unsigned char *hashedKey;
+ unsigned char salt[STRING2KEY_SALTLEN];
+ int algo = mapCipherId(_algo);
+
+ if (!inBufLen || !keylen)
+ return e_invalidArg;
+
+ // test if algo is ready for encryption
+ err = gcry_cipher_algo_info(algo,
+ GCRYCTL_TEST_ALGO,
+ 0, 0);
+ if (err != GPG_ERR_NO_ERROR) {
+ printDebug(string("LibGCryptIf::doEncrypt(): GCRYCTL_TEST_ALGO failed: ")
+ + gcry_strerror(err));
+ ret = e_cryptNotImpl;
+ goto out;
+ }
+ // get the algo block length
+ err = gcry_cipher_algo_info(algo,
+ GCRYCTL_GET_BLKLEN,
+ 0,
+ &blklen);
+ if (err != GPG_ERR_NO_ERROR) {
+ printDebug(string("LibGCryptIf::doEncrypt(): GCRYCTL_GET_BLKLEN failed: ")
+ + gcry_strerror(err));
+ ret = e_cryptNotImpl;
+ goto out;
+ }
+ /* double check if we have enough space.
+ * We have only 1024 extra bytes for padding and salt.
+ */
+ BUG_ON(blklen > 1024 - STRING2KEY_SALTLEN);
+ // get the algo key length
+ err = gcry_cipher_algo_info(algo,
+ GCRYCTL_GET_KEYLEN,
+ 0,
+ &cipherKeylen);
+ if (err != GPG_ERR_NO_ERROR) {
+ printDebug(string("LibGCryptIf::doEncrypt(): GCRYCTL_GET_KEYLEN failed: ")
+ + gcry_strerror(err));
+ ret = e_cryptNotImpl;
+ goto out;
+ }
+ // now open the algo and get a handle
+ err = gcry_cipher_open(&handle,
+ algo,
+ GCRY_CIPHER_MODE_CBC,
+ 0);
+ if (err != GPG_ERR_NO_ERROR) {
+ printDebug(string("LibGCryptIf::doEncrypt(): gcry_cipher_open() failed: ")
+ + gcry_strerror(err));
+ ret = e_cryptNotImpl;
+ goto out;
+ }
+ // hash the "key" to a fixed size hash matching "cipherKeylen"
+ hashedKey = new unsigned char[cipherKeylen];
+ hashPassphrase(key, keylen, salt, hashedKey, cipherKeylen, true);
+ // so now set the hashed key
+ err = gcry_cipher_setkey(handle, hashedKey, cipherKeylen);
+ if (err != GPG_ERR_NO_ERROR) {
+ printDebug(string("LibGCryptIf::doEncrypt(): gcry_cipher_setkey() failed: ")
+ + gcry_strerror(err));
+ ret = e_cryptNotImpl;
+ delete [] hashedKey;
+ goto out_close;
+ }
+ delete [] hashedKey;
+ /* allocate a buffer for the encrypted data.
+ * The size of the buffer is the inBuf length, but blklen
+ * aligned and plus the length of the salt, that is appended.
+ */
+ *outBufLen = getBufLen(unpaddedLen, blklen) + STRING2KEY_SALTLEN;
+ *outBuf = new unsigned char[*outBufLen];
+ padData(inBuf, unpaddedLen, blklen);
+ // encrypt the padded data
+ err = gcry_cipher_encrypt(handle,
+ *outBuf,
+ *outBufLen - STRING2KEY_SALTLEN,
+ inBuf,
+ *outBufLen - STRING2KEY_SALTLEN);
+ if (err != GPG_ERR_NO_ERROR) {
+ printDebug(string("LibGCryptIf::doEncrypt(): gcry_cipher_encrypt() failed: ")
+ + gcry_strerror(err));
+ ret = e_cryptNotImpl;
+ goto out_delete;
+ }
+ // append the salt to the encrypted data
+ memcpy(*outBuf + *outBufLen - STRING2KEY_SALTLEN, salt, STRING2KEY_SALTLEN);
+ goto out_close;
+out_delete:
+ delete [] *outBuf;
+out_close:
+ gcry_cipher_close(handle);
+out:
+ return ret;
+}
+
+PwMerror LibGCryptIf::decrypt(unsigned char **outBuf,
+ size_t *outBufLen,
+ const unsigned char *inBuf,
+ size_t inBufLen,
+ const unsigned char *key,
+ size_t keylen,
+ char _algo)
+{
+ PwMerror ret = e_success;
+ gcry_error_t err;
+ gcry_cipher_hd_t handle;
+ size_t cipherKeylen;
+ unsigned char *hashedKey;
+ unsigned char salt[STRING2KEY_SALTLEN];
+ int algo = mapCipherId(_algo);
+
+ if (!inBufLen || !keylen)
+ return e_invalidArg;
+
+ // test if algo is ready for encryption
+ err = gcry_cipher_algo_info(algo,
+ GCRYCTL_TEST_ALGO,
+ 0, 0);
+ if (err != GPG_ERR_NO_ERROR) {
+ printDebug(string("LibGCryptIf::doDecrypt(): GCRYCTL_TEST_ALGO failed: ")
+ + gcry_strerror(err));
+ ret = e_cryptNotImpl;
+ goto out;
+ }
+ // get algo key length
+ err = gcry_cipher_algo_info(algo,
+ GCRYCTL_GET_KEYLEN,
+ 0,
+ &cipherKeylen);
+ if (err != GPG_ERR_NO_ERROR) {
+ printDebug(string("LibGCryptIf::doDecrypt(): GCRYCTL_GET_KEYLEN failed: ")
+ + gcry_strerror(err));
+ ret = e_cryptNotImpl;
+ goto out;
+ }
+ // extract the salt of the encrypted data buffer
+ memcpy(salt, inBuf + inBufLen - STRING2KEY_SALTLEN, STRING2KEY_SALTLEN);
+ // open the algo and get a handle
+ err = gcry_cipher_open(&handle,
+ algo,
+ GCRY_CIPHER_MODE_CBC,
+ 0);
+ if (err != GPG_ERR_NO_ERROR) {
+ printDebug(string("LibGCryptIf::doDecrypt(): gcry_cipher_open() failed: ")
+ + gcry_strerror(err));
+ ret = e_cryptNotImpl;
+ goto out;
+ }
+ // hash the "key" to a fixed size hash matching "cipherKeylen"
+ hashedKey = new unsigned char[cipherKeylen];
+ hashPassphrase(key, keylen, salt, hashedKey, cipherKeylen, false);
+ // so now set the hashed key
+ err = gcry_cipher_setkey(handle, hashedKey, cipherKeylen);
+ if (err != GPG_ERR_NO_ERROR) {
+ printDebug(string("LibGCryptIf::doDecrypt(): gcry_cipher_setkey() failed: ")
+ + gcry_strerror(err));
+ ret = e_cryptNotImpl;
+ delete [] hashedKey;
+ goto out_close;
+ }
+ delete [] hashedKey;
+ *outBufLen = inBufLen - STRING2KEY_SALTLEN;
+ *outBuf = new unsigned char[*outBufLen];
+ // decrypt the data
+ err = gcry_cipher_decrypt(handle,
+ *outBuf,
+ *outBufLen,
+ inBuf,
+ *outBufLen);
+ if (err != GPG_ERR_NO_ERROR) {
+ printDebug(string("LibGCryptIf::doEncrypt(): gcry_cipher_encrypt() failed: ")
+ + gcry_strerror(err));
+ ret = e_cryptNotImpl;
+ goto out_delete;
+ }
+ // remove all random padding
+ unpadData(*outBuf, outBufLen);
+ goto out_close;
+out_delete:
+ delete [] *outBuf;
+out_close:
+ gcry_cipher_close(handle);
+out:
+ return ret;
+}
+
+PwMerror LibGCryptIf::hash(unsigned char **outBuf,
+ size_t *outBufLen,
+ const unsigned char *inBuf,
+ size_t inBufLen,
+ char _algo)
+{
+ PwMerror ret = e_success;
+ unsigned int hashLen;
+ int algo = mapHashId(_algo);
+
+ hashLen = gcry_md_get_algo_dlen(algo);
+ *outBufLen = hashLen;
+ *outBuf = new unsigned char[*outBufLen];
+ gcry_md_hash_buffer(algo,
+ *outBuf,
+ inBuf,
+ inBufLen);
+ return ret;
+}
+
+unsigned int LibGCryptIf::hashLength(char _algo)
+{
+ unsigned int ret;
+ int algo = mapHashId(_algo);
+ ret = gcry_md_get_algo_dlen(algo);
+ return ret;
+}
+
+int LibGCryptIf::mapCipherId(char algo)
+{
+ switch (algo) {
+ case PWM_CRYPT_AES128:
+ return GCRY_CIPHER_AES;
+ case PWM_CRYPT_AES192:
+ return GCRY_CIPHER_AES192;
+ case PWM_CRYPT_AES256:
+ return GCRY_CIPHER_AES256;
+ case PWM_CRYPT_3DES:
+ return GCRY_CIPHER_3DES;
+ case PWM_CRYPT_TWOFISH:
+ return GCRY_CIPHER_TWOFISH;
+ case PWM_CRYPT_TWOFISH128:
+ return GCRY_CIPHER_TWOFISH128;
+ default:
+ BUG();
+ }
+ return GCRY_CIPHER_NONE;
+}
+
+int LibGCryptIf::mapHashId(char algo)
+{
+ switch (algo) {
+ case PWM_HASH_SHA1:
+ return GCRY_MD_SHA1;
+ case PWM_HASH_SHA256:
+ return GCRY_MD_SHA256;
+ case PWM_HASH_SHA384:
+ return GCRY_MD_SHA384;
+ case PWM_HASH_SHA512:
+ return GCRY_MD_SHA512;
+ case PWM_HASH_MD5:
+ return GCRY_MD_MD5;
+ case PWM_HASH_RMD160:
+ return GCRY_MD_RMD160;
+ case PWM_HASH_TIGER:
+ return GCRY_MD_TIGER;
+ default:
+ BUG();
+ }
+ return GCRY_MD_NONE;
+}
+
+bool LibGCryptIf::hashPassphrase(const unsigned char *pw,
+ size_t pwlen,
+ unsigned char *salt,
+ unsigned char *key,
+ size_t keylen,
+ bool create)
+{
+ DEK dek;
+ STRING2KEY s2k;
+ bool ret;
+
+ dek.keylen = keylen;
+ s2k.mode = 1;
+ s2k.hash_algo = mapHashId(conf()->confGlobHashAlgo());
+ s2k.count = 0;
+ if (!create)
+ memcpy(s2k.salt, salt, STRING2KEY_SALTLEN);
+ ret = doHashPassphrase(&dek,
+ pw,
+ pwlen,
+ &s2k,
+ create);
+ if (!ret)
+ goto out;
+ memcpy(key, dek.key, dek.keylen);
+ if (create)
+ memcpy(salt, s2k.salt, STRING2KEY_SALTLEN);
+out:
+ return ret;
+}
+
+
+bool LibGCryptIf::doHashPassphrase(DEK *dek,
+ const unsigned char *pw,
+ size_t pwlen,
+ STRING2KEY *s2k,
+ bool create)
+{
+ // This function is derived from GnuPG-1.2.5-rc2
+ gcry_md_hd_t md;
+ gcry_error_t err;
+ bool ret = true;
+ size_t pass, i;
+ size_t used = 0;
+
+ PWM_ASSERT(s2k->hash_algo);
+ BUG_ON(!(dek->keylen > 0 && dek->keylen <= array_size(dek->key)));
+
+ err = gcry_md_open(&md, s2k->hash_algo, 0);
+ if (err != GPG_ERR_NO_ERROR) {
+ ret = false;
+ goto out;
+ }
+ for (pass = 0; used < dek->keylen; pass++) {
+ if (pass) {
+ gcry_md_reset(md);
+ for (i = 0; i < pass; i++) // preset the hash context
+ gcry_md_putc(md, 0);
+ }
+ if (s2k->mode == 1 || s2k->mode == 3) {
+ size_t len2 = pwlen + 8;
+ size_t count = len2;
+
+ if (create && !pass) {
+ Randomizer *rnd = Randomizer::obj();
+ const unsigned int salt_len = 8;
+ string rndBuf(rnd->genRndBuf(salt_len));
+ memcpy(s2k->salt, rndBuf.c_str(), salt_len);
+ if (s2k->mode == 3)
+ s2k->count = 96; // 65536 iterations
+ }
+ if (s2k->mode == 3) {
+ count = (16ul + (s2k->count & 15)) << ((s2k->count >> 4) + 6);
+ if (count < len2)
+ count = len2;
+ }
+ // a little bit complicated because we need a ulong for count
+ while (count > len2) { // maybe iterated+salted
+ gcry_md_write(md, s2k->salt, 8);
+ gcry_md_write(md, pw, pwlen);
+ count -= len2;
+ }
+ if (count < 8) {
+ gcry_md_write(md, s2k->salt, count);
+ } else {
+ gcry_md_write(md, s2k->salt, 8);
+ count -= 8;
+ gcry_md_write(md, pw, count);
+ }
+ } else
+ gcry_md_write(md, pw, pwlen);
+ gcry_md_final(md);
+ i = gcry_md_get_algo_dlen(s2k->hash_algo);
+ if (i > dek->keylen - used)
+ i = dek->keylen - used;
+ memcpy(dek->key+used, gcry_md_read(md, s2k->hash_algo), i);
+ used += i;
+ }
+ gcry_md_close(md);
+out:
+ return ret;
+}
+
+void LibGCryptIf::padData(unsigned char *buf,
+ size_t bufLen,
+ size_t boundary)
+{
+ size_t numPadBytes = boundary - ((bufLen + 1) % boundary);
+ buf[bufLen] = static_cast<char>(0x01);
+ size_t i = 0;
+ Randomizer *rnd = Randomizer::obj();
+ char c;
+ unsigned char *b;
+ while (i < numPadBytes) {
+ c = rnd->genRndChar();
+ if (c == static_cast<char>(0x01))
+ continue;
+ b = buf + bufLen + 1 + i;
+ *b = c;
+ ++i;
+ }
+}
+
+void LibGCryptIf::unpadData(const unsigned char *buf,
+ size_t *bufLen)
+{
+ size_t pos;
+ BUG_ON(*bufLen % 8);
+ pos = *bufLen - 1;
+ while (buf[pos] != static_cast<char>(0x01)) {
+ BUG_ON(!pos);
+ --pos;
+ }
+ *bufLen = pos;
+}
+
+#endif // CONFIG_PWMANAGER_GCRY
diff --git a/pwmanager/pwmanager/libgcryptif.h b/pwmanager/pwmanager/libgcryptif.h
new file mode 100644
index 0000000..e86d638
--- a/dev/null
+++ b/pwmanager/pwmanager/libgcryptif.h
@@ -0,0 +1,158 @@
+/***************************************************************************
+ * *
+ * copyright (C) 2004 by Michael Buesch *
+ * email: mbuesch@freenet.de *
+ * *
+ * hashPassphrase() is derived from GnuPG and is *
+ * Copyright (C) 1998, 1999, 2000, 2001, 2003 *
+ * Free Software Foundation, Inc. *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License version 2 *
+ * as published by the Free Software Foundation. *
+ * *
+ ***************************************************************************/
+
+#ifndef __LIBGCRYPTIF_H
+#define __LIBGCRYPTIF_H
+
+#include "pwmexception.h"
+
+//#undef CONFIG_PWMANAGER_GCRY // for debugging only.
+#ifdef CONFIG_PWMANAGER_GCRY
+
+#include <stddef.h>
+#include <sys/types.h>
+#include <stdint.h>
+
+#define STRING2KEY_SALTLEN 8
+
+/** interface class for the libgcrypt cipher and hash algorithms
+ * NOTE: Always allocate 1024 extra bytes for the inBuf (for padding)
+ */
+class LibGCryptIf
+{
+protected:
+ struct STRING2KEY
+ {
+ int mode;
+ int hash_algo;
+ uint8_t salt[STRING2KEY_SALTLEN];
+ uint32_t count;
+ };
+ struct DEK
+ {
+ size_t keylen;
+ uint8_t key[32]; // this is the largest used keylen (256 bit)
+ };
+
+public:
+ LibGCryptIf() { }
+ /** is libgcrypt available? */
+ static bool available()
+ { return true; }
+ /** encrypt data. _algo is the PWM_CRYPT_* ID
+ * of the algorithm.
+ */
+ PwMerror encrypt(unsigned char **outBuf,
+ size_t *outBufLen,
+ unsigned char *inBuf,
+ size_t inBufLen,
+ const unsigned char *key,
+ size_t keylen,
+ char _algo);
+ /** decrypt data. _algo is the PWM_CRYPT_* ID
+ * of the algorithm.
+ */
+ PwMerror decrypt(unsigned char **outBuf,
+ size_t *outBufLen,
+ const unsigned char *inBuf,
+ size_t inBufLen,
+ const unsigned char *key,
+ size_t keylen,
+ char _algo);
+ /** hash data. _algo is the PWM_HASH_* ID of the hash */
+ PwMerror hash(unsigned char **outBuf,
+ size_t *outBufLen,
+ const unsigned char *inBuf,
+ size_t inBufLen,
+ char _algo);
+ /** returns the length of the hash. _algo is the PWM_HASH_*
+ * id of the hash. returns 0 on error.
+ */
+ unsigned int hashLength(char _algo);
+
+protected:
+ /** returns the total buffer length */
+ size_t getBufLen(size_t inBufLen, size_t boundary)
+ {
+ return ((boundary - (inBufLen % boundary)) + inBufLen);
+ }
+ /** pad the data up to the given boundary.
+ * "buf" has to be big enough!
+ */
+ void padData(unsigned char *buf,
+ size_t bufLen,
+ size_t boundary);
+ /** unpad the data */
+ void unpadData(const unsigned char *buf,
+ size_t *bufLen);
+ /** maps the PWM_CRYPT_* ID of an algorithm
+ * to the libgcrypt GCRY_CIPHER_* ID
+ */
+ int mapCipherId(char algo);
+ /** maps the PWM_HASH_* ID of an algorithm
+ * to the libgcrypt GCRY_MD_* ID
+ */
+ int mapHashId(char algo);
+ /** hash a passphrase to a cipher key */
+ bool hashPassphrase(const unsigned char *pw,
+ size_t pwlen,
+ unsigned char *salt,
+ unsigned char *key,
+ size_t keylen,
+ bool create);
+ /** hash a passphrase to a cipher key */
+ bool doHashPassphrase(DEK *dek,
+ const unsigned char *pw,
+ size_t pwlen,
+ STRING2KEY *s2k,
+ bool create);
+};
+
+
+#else // CONFIG_PWMANAGER_GCRY
+/** libgcrypt is not installed. This is a NOP wrapper. */
+class LibGCryptIf
+{
+public:
+ LibGCryptIf() { }
+ static bool available()
+ { return false; }
+ PwMerror encrypt(unsigned char **,
+ size_t *,
+ unsigned char *,
+ size_t,
+ const unsigned char *,
+ size_t,
+ char)
+ { return e_cryptNotImpl; }
+ PwMerror decrypt(unsigned char **,
+ size_t *,
+ const unsigned char *,
+ size_t,
+ const unsigned char *,
+ size_t,
+ char)
+ { return e_cryptNotImpl; }
+ PwMerror hash(unsigned char **,
+ size_t *,
+ const unsigned char *,
+ size_t,
+ char)
+ { return e_hashNotImpl; }
+ unsigned int hashLength(char)
+ { return 0; }
+};
+#endif // CONFIG_PWMANAGER_GCRY
+#endif // __LIBGCRYPTIF_H