void CCM_Decryption::finish(secure_vector<uint8_t>& buffer, size_t offset) { BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane"); buffer.insert(buffer.begin() + offset, msg_buf().begin(), msg_buf().end()); const size_t sz = buffer.size() - offset; uint8_t* buf = buffer.data() + offset; BOTAN_ASSERT(sz >= tag_size(), "We have the tag"); const secure_vector<uint8_t>& ad = ad_buf(); BOTAN_ASSERT(ad.size() % CCM_BS == 0, "AD is block size multiple"); const BlockCipher& E = cipher(); secure_vector<uint8_t> T(CCM_BS); E.encrypt(format_b0(sz - tag_size()), T); for(size_t i = 0; i != ad.size(); i += CCM_BS) { xor_buf(T.data(), &ad[i], CCM_BS); E.encrypt(T); } secure_vector<uint8_t> C = format_c0(); secure_vector<uint8_t> S0(CCM_BS); E.encrypt(C, S0); inc(C); secure_vector<uint8_t> X(CCM_BS); const uint8_t* buf_end = &buf[sz - tag_size()]; while(buf != buf_end) { const size_t to_proc = std::min<size_t>(CCM_BS, buf_end - buf); E.encrypt(C, X); xor_buf(buf, X.data(), to_proc); inc(C); xor_buf(T.data(), buf, to_proc); E.encrypt(T); buf += to_proc; } T ^= S0; if(!constant_time_compare(T.data(), buf_end, tag_size())) throw Integrity_Failure("CCM tag check failed"); buffer.resize(buffer.size() - tag_size()); }
bool check_passhash9(const std::string& pass, const std::string& hash) { const size_t BINARY_LENGTH = ALGID_BYTES + WORKFACTOR_BYTES + PASSHASH9_PBKDF_OUTPUT_LEN + SALT_BYTES; const size_t BASE64_LENGTH = MAGIC_PREFIX.size() + (BINARY_LENGTH * 8) / 6; if(hash.size() != BASE64_LENGTH) return false; for(size_t i = 0; i != MAGIC_PREFIX.size(); ++i) if(hash[i] != MAGIC_PREFIX[i]) return false; secure_vector<uint8_t> bin = base64_decode(hash.c_str() + MAGIC_PREFIX.size()); if(bin.size() != BINARY_LENGTH) return false; uint8_t alg_id = bin[0]; const size_t work_factor = load_be<uint16_t>(&bin[ALGID_BYTES], 0); // Bug in the format, bad states shouldn't be representable, but are... if(work_factor == 0) return false; if(work_factor > 512) throw Invalid_Argument("Requested passhash9 work factor " + std::to_string(work_factor) + " is too large"); const size_t kdf_iterations = WORK_FACTOR_SCALE * work_factor; std::unique_ptr<MessageAuthenticationCode> pbkdf_prf = get_pbkdf_prf(alg_id); if(!pbkdf_prf) return false; // unknown algorithm, reject PKCS5_PBKDF2 kdf(pbkdf_prf.release()); // takes ownership of pointer secure_vector<uint8_t> cmp = kdf.derive_key( PASSHASH9_PBKDF_OUTPUT_LEN, pass, &bin[ALGID_BYTES + WORKFACTOR_BYTES], SALT_BYTES, kdf_iterations).bits_of(); return constant_time_compare(cmp.data(), &bin[ALGID_BYTES + WORKFACTOR_BYTES + SALT_BYTES], PASSHASH9_PBKDF_OUTPUT_LEN); }
secure_vector<uint8_t> RTSS_Share::reconstruct(const std::vector<RTSS_Share>& shares) { if(shares.size() <= 1) throw Decoding_Error("Insufficient shares to do TSS reconstruction"); for(size_t i = 0; i != shares.size(); ++i) { if(shares[i].size() < RTSS_HEADER_SIZE + 1) throw Decoding_Error("Missing or malformed RTSS header"); if(shares[i].share_id() == 0) throw Decoding_Error("Invalid (id = 0) RTSS share detected"); if(i > 0) { if(shares[i].size() != shares[0].size()) throw Decoding_Error("Different sized RTSS shares detected"); if(!same_mem(&shares[0].m_contents[0], &shares[i].m_contents[0], RTSS_HEADER_SIZE)) throw Decoding_Error("Different RTSS headers detected"); } } const uint8_t N = shares[0].m_contents[17]; if(shares.size() < N) throw Decoding_Error("Insufficient shares to do TSS reconstruction"); const uint16_t share_len = make_uint16(shares[0].m_contents[18], shares[0].m_contents[19]); const uint8_t hash_id = shares[0].m_contents[16]; std::unique_ptr<HashFunction> hash(get_rtss_hash_by_id(hash_id)); const size_t hash_len = (hash ? hash->output_length() : 0); if(shares[0].size() != RTSS_HEADER_SIZE + share_len) { /* * This second (laxer) check accomodates a bug in TSS that was * fixed in 2.9.0 - previous versions used the length of the * *secret* here, instead of the length of the *share*, which is * precisely 1 + hash_len longer. */ if(shares[0].size() <= RTSS_HEADER_SIZE + 1 + hash_len) throw Decoding_Error("Bad RTSS length field in header"); } std::vector<uint8_t> V(shares.size()); secure_vector<uint8_t> recovered; for(size_t i = RTSS_HEADER_SIZE + 1; i != shares[0].size(); ++i) { for(size_t j = 0; j != V.size(); ++j) V[j] = shares[j].m_contents[i]; uint8_t r = 0; for(size_t k = 0; k != shares.size(); ++k) { // L_i function: uint8_t r2 = 1; for(size_t l = 0; l != shares.size(); ++l) { if(k == l) continue; uint8_t share_k = shares[k].share_id(); uint8_t share_l = shares[l].share_id(); if(share_k == share_l) throw Decoding_Error("Duplicate shares found in RTSS recovery"); uint8_t div = RTSS_EXP[(255 + RTSS_LOG[share_l] - RTSS_LOG[share_k ^ share_l]) % 255]; r2 = gfp_mul(r2, div); } r ^= gfp_mul(V[k], r2); } recovered.push_back(r); } if(hash) { if(recovered.size() < hash->output_length()) throw Decoding_Error("RTSS recovered value too short to be valid"); const size_t secret_len = recovered.size() - hash->output_length(); hash->update(recovered.data(), secret_len); secure_vector<uint8_t> hash_check = hash->final(); if(!constant_time_compare(hash_check.data(), &recovered[secret_len], hash->output_length())) { throw Decoding_Error("RTSS hash check failed"); } // remove the trailing hash value recovered.resize(secret_len); } return recovered; }