Пример #1
0
void EAX_Decryption::finish(secure_vector<byte>& buffer, size_t offset)
{
    BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane");
    const size_t sz = buffer.size() - offset;
    byte* buf = buffer.data() + offset;

    BOTAN_ASSERT(sz >= tag_size(), "Have the tag as part of final input");

    const size_t remaining = sz - tag_size();

    if(remaining)
    {
        m_cmac->update(buf, remaining);
        m_ctr->cipher(buf, buf, remaining);
    }

    const byte* included_tag = &buf[remaining];

    secure_vector<byte> mac = m_cmac->final();
    mac ^= m_nonce_mac;
    mac ^= m_ad_mac;

    if(!same_mem(mac.data(), included_tag, tag_size()))
        throw Integrity_Failure("EAX tag check failed");

    buffer.resize(offset + remaining);
}
Пример #2
0
bool EMSA1::verify(const secure_vector<uint8_t>& input,
                   const secure_vector<uint8_t>& raw,
                   size_t key_bits)
   {
   try {
      if(raw.size() != m_hash->output_length())
         throw Encoding_Error("EMSA1::encoding_of: Invalid size for input");

      // Call emsa1_encoding to handle any required bit shifting
      const secure_vector<uint8_t> our_coding = emsa1_encoding(raw, key_bits);

      if(our_coding.size() < input.size())
         return false;

      const size_t offset = our_coding.size() - input.size(); // must be >= 0 per check above

      // If our encoding is longer, all the bytes in it must be zero
      for(size_t i = 0; i != offset; ++i)
         if(our_coding[i] != 0)
            return false;

      return same_mem(input.data(), &our_coding[offset], input.size());
      }
   catch(Invalid_Argument)
      {
      return false;
      }
   }
Пример #3
0
/*
* PSSR Decode/Verify Operation
*/
bool PSSR::verify(const secure_vector<byte>& const_coded,
                   const secure_vector<byte>& raw, size_t key_bits)
   {
   const size_t HASH_SIZE = hash->output_length();
   const size_t KEY_BYTES = (key_bits + 7) / 8;

   if(key_bits < 8*HASH_SIZE + 9)
      return false;

   if(raw.size() != HASH_SIZE)
      return false;

   if(const_coded.size() > KEY_BYTES || const_coded.size() <= 1)
      return false;

   if(const_coded[const_coded.size()-1] != 0xBC)
      return false;

   secure_vector<byte> coded = const_coded;
   if(coded.size() < KEY_BYTES)
      {
      secure_vector<byte> temp(KEY_BYTES);
      buffer_insert(temp, KEY_BYTES - coded.size(), coded);
      coded = temp;
      }

   const size_t TOP_BITS = 8 * ((key_bits + 7) / 8) - key_bits;
   if(TOP_BITS > 8 - high_bit(coded[0]))
      return false;

   byte* DB = &coded[0];
   const size_t DB_size = coded.size() - HASH_SIZE - 1;

   const byte* H = &coded[DB_size];
   const size_t H_size = HASH_SIZE;

   mgf1_mask(*hash, &H[0], H_size, &DB[0], DB_size);
   DB[0] &= 0xFF >> TOP_BITS;

   size_t salt_offset = 0;
   for(size_t j = 0; j != DB_size; ++j)
      {
      if(DB[j] == 0x01)
         { salt_offset = j + 1; break; }
      if(DB[j])
         return false;
      }
   if(salt_offset == 0)
      return false;

   for(size_t j = 0; j != 8; ++j)
      hash->update(0);
   hash->update(raw);
   hash->update(&DB[salt_offset], DB_size - salt_offset);
   secure_vector<byte> H2 = hash->final();

   return same_mem(&H[0], &H2[0], HASH_SIZE);
   }
Пример #4
0
void CCM_Decryption::finish(secure_vector<byte>& 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;
   byte* buf = buffer.data() + offset;

   BOTAN_ASSERT(sz >= tag_size(), "We have the tag");

   const secure_vector<byte>& ad = ad_buf();
   BOTAN_ASSERT(ad.size() % BS == 0, "AD is block size multiple");

   const BlockCipher& E = cipher();

   secure_vector<byte> T(BS);
   E.encrypt(format_b0(sz - tag_size()), T);

   for(size_t i = 0; i != ad.size(); i += BS)
      {
      xor_buf(T.data(), &ad[i], BS);
      E.encrypt(T);
      }

   secure_vector<byte> C = format_c0();

   secure_vector<byte> S0(BS);
   E.encrypt(C, S0);
   inc(C);

   secure_vector<byte> X(BS);

   const byte* buf_end = &buf[sz - tag_size()];

   while(buf != buf_end)
      {
      const size_t to_proc = std::min<size_t>(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(!same_mem(T.data(), buf_end, tag_size()))
      throw Integrity_Failure("CCM tag check failed");

   buffer.resize(buffer.size() - tag_size());
   }
Пример #5
0
/*
* Finish decrypting in EAX mode
*/
void EAX_Decryption::end_msg()
   {
   if((queue_end - queue_start) != TAG_SIZE)
      throw Decoding_Error(name() + ": Message authentication failure");

   const byte* included_mac = &queue[queue_start];

   SecureVector<byte> computed_mac = cmac->final();

   xor_buf(&computed_mac[0], &nonce_mac[0], TAG_SIZE);
   xor_buf(&computed_mac[0], &header_mac[0], TAG_SIZE);

   if(!same_mem(included_mac, &computed_mac[0], TAG_SIZE))
      throw Decoding_Error(name() + ": Message authentication failure");

   queue_start = queue_end = 0;
   }
Пример #6
0
bool check_bcrypt(const std::string& pass, const std::string& hash)
   {
   if(hash.size() != 60 ||
      hash[0] != '$' || hash[1] != '2' || hash[2] != 'a' ||
      hash[3] != '$' || hash[6] != '$')
      {
      return false;
      }

   const u16bit workfactor = to_u32bit(hash.substr(4, 2));

   const std::vector<byte> salt = bcrypt_base64_decode(hash.substr(7, 22));
   if(salt.size() != 16)
      return false;

   const std::string compare = make_bcrypt(pass, salt, workfactor);

   return same_mem(hash.data(), compare.data(), compare.size());
   }
Пример #7
0
/*
* EMSA-Raw Verify Operation
*/
bool EMSA_Raw::verify(const secure_vector<byte>& coded,
                      const secure_vector<byte>& raw,
                      size_t)
   {
   if(coded.size() == raw.size())
      return (coded == raw);

   if(coded.size() > raw.size())
      return false;

   // handle zero padding differences
   const size_t leading_zeros_expected = raw.size() - coded.size();

   bool same_modulo_leading_zeros = true;

   for(size_t i = 0; i != leading_zeros_expected; ++i)
      if(raw[i])
         same_modulo_leading_zeros = false;

   if(!same_mem(&coded[0], &raw[leading_zeros_expected], coded.size()))
      same_modulo_leading_zeros = false;

   return same_modulo_leading_zeros;
   }
Пример #8
0
/*
* EME1 Unpad Operation
*/
SecureVector<byte> EME1::unpad(const byte in[], size_t in_length,
                               size_t key_length) const
   {
   /*
   Must be careful about error messages here; if an attacker can
   distinguish them, it is easy to use the differences as an oracle to
   find the secret key, as described in "A Chosen Ciphertext Attack on
   RSA Optimal Asymmetric Encryption Padding (OAEP) as Standardized in
   PKCS #1 v2.0", James Manger, Crypto 2001

   Also have to be careful about timing attacks! Pointed out by Falko
   Strenzke.
   */

   key_length /= 8;

   // Invalid input: truncate to zero length input, causing later
   // checks to fail
   if(in_length > key_length)
      in_length = 0;

   SecureVector<byte> input(key_length);
   input.copy(key_length - in_length, in, in_length);

   mgf->mask(&input[Phash.size()], input.size() - Phash.size(),
             &input[0], Phash.size());
   mgf->mask(&input[0], Phash.size(),
             &input[Phash.size()], input.size() - Phash.size());

   bool waiting_for_delim = true;
   bool bad_input = false;
   size_t delim_idx = 2 * Phash.size();

   /*
   * GCC 4.5 on x86-64 compiles this in a way that is still vunerable
   * to timing analysis. Other compilers, or GCC on other platforms,
   * may or may not.
   */
   for(size_t i = delim_idx; i < input.size(); ++i)
      {
      const bool zero_p = !input[i];
      const bool one_p = input[i] == 0x01;

      const bool add_1 = waiting_for_delim && zero_p;

      bad_input |= waiting_for_delim && !(zero_p || one_p);

      delim_idx += add_1;

      waiting_for_delim &= zero_p;
      }

   // If we never saw any non-zero byte, then it's not valid input
   bad_input |= waiting_for_delim;

   bad_input |= !same_mem(&input[Phash.size()], &Phash[0], Phash.size());

   if(bad_input)
      throw Decoding_Error("Invalid EME1 encoding");

   return SecureVector<byte>(input + delim_idx + 1,
                             input.size() - delim_idx - 1);
   }
Пример #9
0
secure_vector<byte>
RTSS_Share::reconstruct(const std::vector<RTSS_Share>& shares)
   {
   const size_t RTSS_HEADER_SIZE = 20;

   for(size_t i = 0; i != shares.size(); ++i)
      {
      if(shares[i].size() != shares[0].size())
         throw Decoding_Error("Different sized RTSS shares detected");
      if(shares[i].share_id() == 0)
         throw Decoding_Error("Invalid (id = 0) RTSS share detected");
      if(shares[i].size() < RTSS_HEADER_SIZE)
         throw Decoding_Error("Missing or malformed RTSS header");

      if(!same_mem(&shares[0].m_contents[0],
                   &shares[i].m_contents[0], RTSS_HEADER_SIZE))
         throw Decoding_Error("Different RTSS headers detected");
      }

   if(shares.size() < shares[0].m_contents[17])
      throw Decoding_Error("Insufficient shares to do TSS reconstruction");

   u16bit secret_len = make_u16bit(shares[0].m_contents[18],
                                   shares[0].m_contents[19]);

   byte hash_id = shares[0].m_contents[16];

   std::unique_ptr<HashFunction> hash(get_rtss_hash_by_id(hash_id));

   if(shares[0].size() != secret_len + hash->output_length() + RTSS_HEADER_SIZE + 1)
      throw Decoding_Error("Bad RTSS length field in header");

   std::vector<byte> V(shares.size());
   secure_vector<byte> secret;

   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];

      byte r = 0;
      for(size_t k = 0; k != shares.size(); ++k)
         {
         // L_i function:
         byte r2 = 1;
         for(size_t l = 0; l != shares.size(); ++l)
            {
            if(k == l)
               continue;

            byte share_k = shares[k].share_id();
            byte share_l = shares[l].share_id();

            if(share_k == share_l)
               throw Decoding_Error("Duplicate shares found in RTSS recovery");

            byte 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);
         }
      secret.push_back(r);
      }

   if(secret.size() != secret_len + hash->output_length())
      throw Decoding_Error("Bad length in RTSS output");

   hash->update(secret.data(), secret_len);
   secure_vector<byte> hash_check = hash->final();

   if(!same_mem(hash_check.data(),
                &secret[secret_len], hash->output_length()))
      throw Decoding_Error("RTSS hash check failed");

   return secure_vector<byte>(secret.cbegin(), secret.cbegin() + secret_len);
   }
Пример #10
0
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;
   }