void genkey(const Memblock &string, uint8_t *key, uint32_t length) const { pgpry_SHA_CTX ctx; uint32_t numHashes = (length + SHA_DIGEST_LENGTH - 1) / SHA_DIGEST_LENGTH; for (uint32_t i = 0; i < numHashes; i++) { pgpry_SHA1_Init(&ctx); for (uint32_t j = 0; j < i; j++) { pgpry_SHA1_Update(&ctx, "\0", 1); } pgpry_SHA1_Update(&ctx, string.data, string.length); pgpry_SHA1_Final(key + (i * SHA_DIGEST_LENGTH), &ctx); } }
void genkey(const Memblock &string, uint8_t *key, uint32_t length) const { pgpry_SHA_CTX ctx; uint32_t numHashes = (length + SHA_DIGEST_LENGTH - 1) / SHA_DIGEST_LENGTH; // TODO: This is not very efficient with multiple hashes for (uint32_t i = 0; i < numHashes; i++) { pgpry_SHA1_Init(&ctx); for (uint32_t j = 0; j < i; j++) { pgpry_SHA1_Update(&ctx, "\0", 1); } // Find multiplicator int32_t tl = string.length + 8; int32_t mul = 1; while (mul < tl && ((64 * mul) % tl)) { ++mul; } // Try to feed the hash function with 64-byte blocks const int32_t bs = mul * 64; assert(bs <= KEYBUFFER_LENGTH); uint8_t *bptr = m_keybuf + tl; int32_t n = bs / tl; memcpy(m_keybuf + 8, string.data, string.length); while (n-- > 1) { memcpy(bptr, m_keybuf, tl); bptr += tl; } n = m_count / bs; while (n-- > 0) { pgpry_SHA1_Update(&ctx, m_keybuf, bs); } pgpry_SHA1_Update(&ctx, m_keybuf, m_count % bs); pgpry_SHA1_Final(key + (i * SHA_DIGEST_LENGTH), &ctx); } }
// Checks if the given password is valid bool Tester::check(const Memblock &mblock) { const String2Key &s2k = m_key.string2Key(); int32_t tmp = 0; // Generate key from password s2k.generateKey(mblock, m_keydata, m_keySize); // Decrypt first data block in order to check the first two bits of // the MPI. If they are correct, there's a good chance that the // password is correct, too. #if 1 memcpy(m_ivec, s2k.ivec(), m_blockSize); switch (m_cipher) { case CryptUtils::CIPHER_CAST5: { CAST_KEY ck; CAST_set_key(&ck, m_keySize, m_keydata); CAST_cfb64_encrypt(m_in, m_out, CAST_BLOCK, &ck, m_ivec, &tmp, CAST_DECRYPT); } break; case CryptUtils::CIPHER_BLOWFISH: { BF_KEY ck; BF_set_key(&ck, m_keySize, m_keydata); BF_cfb64_encrypt(m_in, m_out, BF_BLOCK, &ck, m_ivec, &tmp, BF_DECRYPT); } break; case CryptUtils::CIPHER_AES128: case CryptUtils::CIPHER_AES192: case CryptUtils::CIPHER_AES256: { AES_KEY ck; AES_set_encrypt_key(m_keydata, m_keySize * 8, &ck); AES_cfb128_encrypt(m_in, m_out, AES_BLOCK_SIZE, &ck, m_ivec, &tmp, AES_DECRYPT); } break; default: break; } uint32_t num_bits = ((m_out[0] << 8) | m_out[1]); if (num_bits < MIN_BN_BITS || num_bits > m_bits) { return false; } #endif // Decrypt all data memcpy(m_ivec, s2k.ivec(), m_blockSize); tmp = 0; switch (m_cipher) { case CryptUtils::CIPHER_CAST5: { CAST_KEY ck; CAST_set_key(&ck, m_keySize, m_keydata); CAST_cfb64_encrypt(m_in, m_out, m_datalen, &ck, m_ivec, &tmp, CAST_DECRYPT); } break; case CryptUtils::CIPHER_BLOWFISH: { BF_KEY ck; BF_set_key(&ck, m_keySize, m_keydata); BF_cfb64_encrypt(m_in, m_out, m_datalen, &ck, m_ivec, &tmp, BF_DECRYPT); } break; case CryptUtils::CIPHER_AES128: case CryptUtils::CIPHER_AES192: case CryptUtils::CIPHER_AES256: { AES_KEY ck; AES_set_encrypt_key(m_keydata, m_keySize * 8, &ck); AES_cfb128_encrypt(m_in, m_out, m_datalen, &ck, m_ivec, &tmp, AES_DECRYPT); } break; default: break; } // Verify bool checksumOk = false; switch (s2k.usage()) { case 254: { uint8_t checksum[SHA_DIGEST_LENGTH]; pgpry_SHA_CTX ctx; pgpry_SHA1_Init(&ctx); pgpry_SHA1_Update(&ctx, m_out, m_datalen - SHA_DIGEST_LENGTH); pgpry_SHA1_Final(checksum, &ctx); if (memcmp(checksum, m_out + m_datalen - SHA_DIGEST_LENGTH, SHA_DIGEST_LENGTH) == 0) { checksumOk = true; } } break; case 0: case 255: { uint16_t sum = 0; for (uint32_t i = 0; i < m_datalen - 2; i++) { sum += m_out[i]; } if (sum == ((m_out[m_datalen - 2] << 8) | m_out[m_datalen - 1])) { checksumOk = true; } } break; default: break; } // If the checksum is ok, try to parse the first MPI of the private key if (checksumOk) { BIGNUM *b = NULL; uint32_t blen = (num_bits + 7) / 8; if (blen < m_datalen && ((b = BN_bin2bn(m_out + 2, blen, NULL)) != NULL)) { BN_free(b); return true; } } return false; }
// Checks if the given password is valid bool Tester::check(const Memblock &mblock) { const String2Key &s2k = m_key.string2Key(); int32_t tmp = 0; // Generate key from password s2k.generateKey(mblock, m_keydata, m_keySize); // Decrypt first data block in order to check the first two bits of // the MPI. If they are correct, there's a good chance that the // password is correct, too. #if 1 memcpy(m_ivec, s2k.ivec(), m_blockSize); if (m_key.version() < 4) { // Prior to V4, MPI lengths are unencrypted. memcpy(m_out, m_in, 2); m_key.decrypt(&m_in[2], &m_out[2], m_blockSize, m_keydata, m_keySize, m_ivec, &tmp); } else { m_key.decrypt(m_in, m_out, m_blockSize, m_keydata, m_keySize, m_ivec, &tmp); } uint32_t num_bits = ((m_out[0] << 8) | m_out[1]); if (num_bits < MIN_BN_BITS || num_bits > m_bits || (num_bits % 8 != BN_num_bits_word(m_out[2]) % 8)) { return false; } #endif // Decrypt all data memcpy(m_ivec, s2k.ivec(), m_blockSize); if (m_key.version() < 4) { // V3 keys are RSA only according to [5.5.3]. if (CryptUtils::PKA_RSA_ENCSIGN != m_key.algorithm()) { throw Utils::strprintf("Unexpected V3 algorithm: %d", m_key.algorithm()); } // Prior to V4, the four RSA MPIs were encrypted separately. uint32_t ofs = 0; for (uint32_t i = 0; i != 4; ++i) { memcpy(&m_out[ofs], &m_in[ofs], 2); uint32_t len = (((m_out[ofs] << 8) | m_out[1 + ofs]) + 7) / 8; ofs += 2; if (m_datalen < ofs + len) { throw "Insufficient data length"; } tmp = 0; m_key.decrypt(&m_in[ofs], &m_out[ofs], len, m_keydata, m_keySize, m_ivec, &tmp); ofs += len; } // Copy checksum. memcpy(&m_out[ofs], &m_in[ofs], 2); ofs += 2; if (ofs != m_datalen) { throw "Data length mismatch"; } } else { tmp = 0; m_key.decrypt(m_in, m_out, m_datalen, m_keydata, m_keySize, m_ivec, &tmp); } // Verify bool checksumOk = false; switch (s2k.usage()) { case 254: { uint8_t checksum[SHA_DIGEST_LENGTH]; pgpry_SHA_CTX ctx; pgpry_SHA1_Init(&ctx); pgpry_SHA1_Update(&ctx, m_out, m_datalen - SHA_DIGEST_LENGTH); pgpry_SHA1_Final(checksum, &ctx); if (memcmp(checksum, m_out + m_datalen - SHA_DIGEST_LENGTH, SHA_DIGEST_LENGTH) == 0) { checksumOk = true; } } break; default: { uint16_t sum = 0; for (uint32_t i = 0; i < m_datalen - 2; i++) { sum += m_out[i]; } if (sum == ((m_out[m_datalen - 2] << 8) | m_out[m_datalen - 1])) { checksumOk = true; } } break; } // If the checksum is ok, verify the secret key equations. return checksumOk && m_key.verify(m_out, m_datalen - 2); }