fast_pair_encryption_fuzzer.cc 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. // Copyright 2021 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "ash/quick_pair/fast_pair_handshake/fast_pair_encryption.h"
  5. #include <fuzzer/FuzzedDataProvider.h>
  6. #include <stddef.h>
  7. #include <stdint.h>
  8. #include <array>
  9. #include "ash/quick_pair/common/logging.h"
  10. #include "ash/quick_pair/fast_pair_handshake/fast_pair_key_pair.h"
  11. #include "ash/services/quick_pair/fast_pair_decryption.h"
  12. #include "base/check.h"
  13. #include "base/no_destructor.h"
  14. #include "third_party/boringssl/src/include/openssl/base.h"
  15. #include "third_party/boringssl/src/include/openssl/bn.h"
  16. #include "third_party/boringssl/src/include/openssl/ec.h"
  17. #include "third_party/boringssl/src/include/openssl/nid.h"
  18. namespace {
  19. constexpr size_t kXSize = 32;
  20. constexpr size_t kYSize = 1;
  21. constexpr size_t kKeySize = /*type byte=*/1 + /*x coord=*/32 + /*y coord=*/32;
  22. struct Environment {
  23. Environment() {
  24. // Disable noisy logging for fuzzing.
  25. logging::SetMinLogLevel(logging::LOGGING_FATAL);
  26. }
  27. ash::quick_pair::ScopedDisableLoggingForTesting disable_logging_;
  28. };
  29. } // namespace
  30. extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
  31. // Enforce a minimum input size so that we can pass in valid parameters
  32. // to EncryptBytes and GenerateKeysWithEcdhKeyAgreement.
  33. if (size < 2 * ash::quick_pair::fast_pair_decryption::kBlockByteSize)
  34. return 0;
  35. static base::NoDestructor<Environment> env;
  36. FuzzedDataProvider fuzzed_data(data, size);
  37. std::vector<uint8_t> aes_key_bytes = fuzzed_data.ConsumeBytes<uint8_t>(
  38. ash::quick_pair::fast_pair_decryption::kBlockByteSize);
  39. std::array<uint8_t, ash::quick_pair::fast_pair_decryption::kBlockByteSize>
  40. aes_key_arr;
  41. std::copy_n(aes_key_bytes.begin(),
  42. ash::quick_pair::fast_pair_decryption::kBlockByteSize,
  43. aes_key_arr.begin());
  44. std::vector<uint8_t> data_bytes = fuzzed_data.ConsumeBytes<uint8_t>(
  45. ash::quick_pair::fast_pair_decryption::kBlockByteSize);
  46. std::array<uint8_t, ash::quick_pair::fast_pair_decryption::kBlockByteSize>
  47. data_arr;
  48. std::copy_n(data_bytes.begin(),
  49. ash::quick_pair::fast_pair_decryption::kBlockByteSize,
  50. data_arr.begin());
  51. ash::quick_pair::fast_pair_encryption::EncryptBytes(aes_key_arr, data_arr);
  52. // In order to fuzz a valid EC_POINT, the fuzz needs to have at least
  53. // kXSize + kYSize bytes remaining. For simplicity, exit early if there
  54. // are not exactly as many bytes as required.
  55. if (size != 2 * ash::quick_pair::fast_pair_decryption::kBlockByteSize +
  56. kXSize + kYSize) {
  57. std::string invalid_len_string = fuzzed_data.ConsumeRandomLengthString();
  58. ash::quick_pair::fast_pair_encryption::GenerateKeysWithEcdhKeyAgreement(
  59. invalid_len_string);
  60. return 0;
  61. }
  62. // Generates a random point on the curve defined by EC_GROUP.
  63. bssl::UniquePtr<EC_GROUP> ec_group(
  64. EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1));
  65. bssl::UniquePtr<EC_POINT> point(EC_POINT_new(ec_group.get()));
  66. // Set x value and y bit according to fuzz.
  67. auto x_value_arr = fuzzed_data.ConsumeBytes<uint8_t>(kXSize);
  68. DCHECK(x_value_arr.size() == kXSize);
  69. bssl::UniquePtr<BIGNUM> x(BN_new());
  70. DCHECK(BN_le2bn(&x_value_arr[0], kXSize, x.get()));
  71. auto y_value_arr = fuzzed_data.ConsumeBytes<uint8_t>(kYSize);
  72. DCHECK(y_value_arr.size() == kYSize);
  73. int y_bit = y_value_arr[0] & 1;
  74. // Set EC_POINT according to the compressed coordinates x and y_bit. This
  75. // effectively uses fuzz to generate a point on the EC without us having to
  76. // explicitly compute the solution y for x on the EC. Fails 50% of the time
  77. // when generated x value has no solution on the EC.
  78. if (!EC_POINT_set_compressed_coordinates_GFp(ec_group.get(), point.get(),
  79. x.get(), y_bit, /*ctx=*/nullptr))
  80. return 0;
  81. // Convert compressed EC_POINT into the uncompressed string expected by the
  82. // function.
  83. std::array<uint8_t, kKeySize> buffer;
  84. DCHECK(EC_POINT_point2oct(ec_group.get(), point.get(),
  85. POINT_CONVERSION_UNCOMPRESSED, buffer.data(),
  86. kKeySize, /*ctx=*/nullptr));
  87. // Function expects a string which is missing the first type byte.
  88. std::string anti_spoofing_key(buffer.data() + 1, buffer.data() + kKeySize);
  89. DCHECK(anti_spoofing_key.length() == kKeySize - 1);
  90. ash::quick_pair::fast_pair_encryption::GenerateKeysWithEcdhKeyAgreement(
  91. anti_spoofing_key);
  92. return 0;
  93. }