-rw-r--r-- | pwmanager/pwmanager/libgcryptif.cpp | 435 | ||||
-rw-r--r-- | pwmanager/pwmanager/libgcryptif.h | 158 |
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 |