From fe56321f8d5b737fbb695fb05b4e9dd85b534890 Mon Sep 17 00:00:00 2001 From: Denver Coneybeare Date: Tue, 21 Feb 2023 22:30:02 -0500 Subject: [PATCH 01/19] Md5 class defined --- Firestore/core/CMakeLists.txt | 24 ++++++- Firestore/core/src/util/config_detected.h.in | 2 + Firestore/core/src/util/md5.h | 76 ++++++++++++++++++++ Firestore/core/src/util/md5_openssl.cc | 33 +++++++++ Firestore/core/test/unit/util/md5_test.cc | 30 ++++++++ 5 files changed, 164 insertions(+), 1 deletion(-) create mode 100644 Firestore/core/src/util/md5.h create mode 100644 Firestore/core/src/util/md5_openssl.cc create mode 100644 Firestore/core/test/unit/util/md5_test.cc diff --git a/Firestore/core/CMakeLists.txt b/Firestore/core/CMakeLists.txt index 4663119d440..a966bc20799 100644 --- a/Firestore/core/CMakeLists.txt +++ b/Firestore/core/CMakeLists.txt @@ -98,6 +98,7 @@ if(TARGET OpenSSL::Crypto) CMAKE_REQUIRED_INCLUDES OpenSSL::Crypto INTERFACE_INCLUDE_DIRECTORIES ) check_include_files(openssl/rand.h HAVE_OPENSSL_RAND_H) + check_include_files(openssl/md5.h HAVE_OPENSSL_MD5_H) endif() firebase_ios_glob( @@ -120,6 +121,27 @@ else() ) endif() +if(HAVE_OPENSSL_MD5_H) + firebase_ios_glob( + util_sources APPEND + src/util/md5_openssl.cc + ) + # Suppress deprecation warnings from openssl since its md5 + # implementation is deprecated, but only because md5 is not + # cryptographically secure, something we don't care about in our + # md5 usage. + set_source_files_properties( + src/util/md5_openssl.cc + PROPERTIES + COMPILE_FLAGS + -Wno-deprecated-declarations + ) +else() + message( + FATAL_ERROR + "Don't know how to calculate md5 digests on this platform." + ) +endif() configure_file( src/util/config_detected.h.in @@ -148,7 +170,7 @@ target_link_libraries( grpc++ ) -if(HAVE_OPENSSL_RAND_H) +if(HAVE_OPENSSL_RAND_H OR HAVE_OPENSSL_MD5_H) target_link_libraries( firestore_util PRIVATE OpenSSL::Crypto diff --git a/Firestore/core/src/util/config_detected.h.in b/Firestore/core/src/util/config_detected.h.in index 69a9a50052f..9031897286e 100644 --- a/Firestore/core/src/util/config_detected.h.in +++ b/Firestore/core/src/util/config_detected.h.in @@ -27,4 +27,6 @@ #cmakedefine HAVE_OPENSSL_RAND_H 1 +#cmakedefine HAVE_OPENSSL_MD5_H 1 + #endif // FIRESTORE_CORE_SRC_UTIL_CONFIG_DETECTED_H_ diff --git a/Firestore/core/src/util/md5.h b/Firestore/core/src/util/md5.h new file mode 100644 index 00000000000..d211c0ca9a0 --- /dev/null +++ b/Firestore/core/src/util/md5.h @@ -0,0 +1,76 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FIRESTORE_CORE_SRC_UTIL_MD5_H_ +#define FIRESTORE_CORE_SRC_UTIL_MD5_H_ + +#include +#include + +#include "Firestore/core/src/util/config.h" + +#if HAVE_OPENSSL_MD5_H +#include "openssl/md5.h" +#else +#error "No MD5 implementation is available" +#endif + +namespace firebase { +namespace firestore { +namespace util { + +class Md5 final { + public: + Md5(); + ~Md5() = default; + + // Copyable and movable + Md5(const Md5&); + Md5& operator=(const Md5&); + Md5(Md5&&) noexcept; + Md5& operator=(Md5&&) noexcept; + + /** + * Resets the internal state to its newly-constructed state. + * + * Invoke this method if it is desired to calculate a new digest after this + * object has already been used to calculate another digest. + */ + void Reset(); + + /** + * Consumes the given data and updates the digest calculated so far. + * @param data the data to consume. + * @param len the length of the given data to consume. + */ + void Update(const void* data, int len); + + /** + * Returns the calculated digest based on previous calls to Update(). + */ + std::array Digest(); + + private: +#if HAVE_OPENSSL_MD5_H + MD5_CTX ctx_; +#endif +}; + +} // namespace util +} // namespace firestore +} // namespace firebase + +#endif // FIRESTORE_CORE_SRC_UTIL_MD5_H_ diff --git a/Firestore/core/src/util/md5_openssl.cc b/Firestore/core/src/util/md5_openssl.cc new file mode 100644 index 00000000000..335e58e63cb --- /dev/null +++ b/Firestore/core/src/util/md5_openssl.cc @@ -0,0 +1,33 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Firestore/core/src/util/md5.h" + +#include "Firestore/core/src/util/hard_assert.h" + +namespace firebase { +namespace firestore { +namespace util { + +Md5::Md5() { + const int md5_init_result = MD5_Init(&ctx_); + HARD_ASSERT(md5_init_result == 1, "MD5_Init() returns %s but expected 1", + md5_init_result); +} + +} // namespace util +} // namespace firestore +} // namespace firebase diff --git a/Firestore/core/test/unit/util/md5_test.cc b/Firestore/core/test/unit/util/md5_test.cc new file mode 100644 index 00000000000..b7dd11ed733 --- /dev/null +++ b/Firestore/core/test/unit/util/md5_test.cc @@ -0,0 +1,30 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Firestore/core/src/util/md5.h" + +#include "gtest/gtest.h" + +using firebase::firestore::util::Md5; + +namespace { + +TEST(Md5Test, DefaultConstrutorWorks) { + Md5 instance1; + Md5 instance2; +} + +} // namespace From 8757e3628057b5c89af6dcd097573c3c33a2ae7c Mon Sep 17 00:00:00 2001 From: Denver Coneybeare Date: Tue, 21 Feb 2023 23:50:03 -0500 Subject: [PATCH 02/19] add more tests for Digest() and Update() --- Firestore/core/src/util/md5.h | 3 + Firestore/core/src/util/md5_openssl.cc | 14 ++ Firestore/core/test/unit/util/md5_test.cc | 157 ++++++++++++++++++++++ 3 files changed, 174 insertions(+) diff --git a/Firestore/core/src/util/md5.h b/Firestore/core/src/util/md5.h index d211c0ca9a0..4c30bbd50de 100644 --- a/Firestore/core/src/util/md5.h +++ b/Firestore/core/src/util/md5.h @@ -60,6 +60,9 @@ class Md5 final { /** * Returns the calculated digest based on previous calls to Update(). + * + * NOTE: After calling Digest(), calling Update() or Digest() again is + * undefined behavior. To continue using this object, call Reset(). */ std::array Digest(); diff --git a/Firestore/core/src/util/md5_openssl.cc b/Firestore/core/src/util/md5_openssl.cc index 335e58e63cb..079d5d4b913 100644 --- a/Firestore/core/src/util/md5_openssl.cc +++ b/Firestore/core/src/util/md5_openssl.cc @@ -28,6 +28,20 @@ Md5::Md5() { md5_init_result); } +void Md5::Update(const void* data, int len) { + int md5_update_result = MD5_Update(&ctx_, data, len); + HARD_ASSERT(md5_update_result == 1, "MD5_Update() returns %s but expected 1", + md5_update_result); +} + +std::array Md5::Digest() { + std::array digest; + int md5_final_result = MD5_Final(digest.data(), &ctx_); + HARD_ASSERT(md5_final_result == 1, "MD5_Final() returns %s but expected 1", + md5_final_result); + return digest; +} + } // namespace util } // namespace firestore } // namespace firebase diff --git a/Firestore/core/test/unit/util/md5_test.cc b/Firestore/core/test/unit/util/md5_test.cc index b7dd11ed733..22600bc5942 100644 --- a/Firestore/core/test/unit/util/md5_test.cc +++ b/Firestore/core/test/unit/util/md5_test.cc @@ -14,6 +14,10 @@ * limitations under the License. */ +#include +#include + +#include "Firestore/core/src/util/hard_assert.h" #include "Firestore/core/src/util/md5.h" #include "gtest/gtest.h" @@ -22,9 +26,162 @@ using firebase::firestore::util::Md5; namespace { +// Gets the unsigned char corresponding to the given hex digit. +// The digit must be one of '0', '1', ... , '9', 'a', 'b', ... , 'f'. +// The lower 4 bits of the returned value will be set and the rest will be 0. +unsigned char UnsignedCharFromHexDigit(char digit); + +// Calculates the 16-byte unsigned char array represented by the given hex +// string. The given string must be exactly 32 characters and each character +// must be one that is accepted by the UnsignedCharFromHexDigit() function. +// e.g. "fc3ff98e8c6a0d3087d515c0473f8677". +// The `md5sum` command from GNU coreutils can be used to generate a string to +// specify to this function. +// e.g. +// $ printf 'hello world!' | md5sum - +// fc3ff98e8c6a0d3087d515c0473f8677 - +std::array UnsignedCharArrayFromHexDigest( + const std::string&); + +// Returns the md5 digest for the given string. +// This function does not "calculate" the digest, but rather has a hardcoded set +// of pre-calculated digests that it returns. +// It is an error if this function does not have a pre-calculated digest for the +// given string. +std::array GetPreComputedDigest(const std::string&); + +// Generates and returns a string with all possible characters with the given +// length. The given length must be at least 256. +std::string GetStringWithAllPossibleCharacters(int length); + TEST(Md5Test, DefaultConstrutorWorks) { Md5 instance1; Md5 instance2; } +TEST(Md5Test, DigestReturnsCorrectValueOnDefaultConstructedInstance) { + Md5 md5; + EXPECT_EQ(md5.Digest(), GetPreComputedDigest("")); +} + +TEST(Md5Test, UpdateInvokedOnceYieldCorrectDigest) { + Md5 md5; + + md5.Update("hello world!", 12); + + EXPECT_EQ(md5.Digest(), GetPreComputedDigest("hello world!")); +} + +TEST(Md5Test, UpdateInvokedTwiceYieldCorrectDigest) { + Md5 md5; + + md5.Update("hello ", 6); + md5.Update("world!", 6); + + EXPECT_EQ(md5.Digest(), GetPreComputedDigest("hello world!")); +} + +TEST(Md5Test, UpdateInvokedThriceYieldCorrectDigest) { + Md5 md5; + + md5.Update("hell", 4); + md5.Update("o wo", 4); + md5.Update("rld!", 4); + + EXPECT_EQ(md5.Digest(), GetPreComputedDigest("hello world!")); +} + +TEST(Md5Test, DigestsOfVariousStrings) { + std::vector strings{ + "a", "abc", "the quick brown fox jumps over the lazy dog", + GetStringWithAllPossibleCharacters(512), + GetStringWithAllPossibleCharacters(8192)}; + + for (const std::string& s : strings) { + Md5 md5; + md5.Update(s.data(), s.length()); + EXPECT_EQ(md5.Digest(), GetPreComputedDigest(s)); + } +} + +unsigned char UnsignedCharFromHexDigit(char digit) { + switch (digit) { + case '0': + return 0x0; + case '1': + return 0x1; + case '2': + return 0x2; + case '3': + return 0x3; + case '4': + return 0x4; + case '5': + return 0x5; + case '6': + return 0x6; + case '7': + return 0x7; + case '8': + return 0x8; + case '9': + return 0x9; + case 'a': + return 0xA; + case 'b': + return 0xB; + case 'c': + return 0xC; + case 'd': + return 0xD; + case 'e': + return 0xE; + case 'f': + return 0xF; + }; + HARD_FAIL("unrecognized hex digit: %s", std::to_string(digit)); +} + +std::array UnsignedCharArrayFromHexDigest( + const std::string& s) { + HARD_ASSERT(s.length() == 32); + std::array result; + for (int i = 0; i < 16; ++i) { + unsigned char c1 = UnsignedCharFromHexDigit(s[i * 2]); + unsigned char c2 = UnsignedCharFromHexDigit(s[(i * 2) + 1]); + result[i] = (c1 << 4) | c2; + } + return result; +} + +std::array GetPreComputedDigest(const std::string& s) { + if (s == "") { + return UnsignedCharArrayFromHexDigest("d41d8cd98f00b204e9800998ecf8427e"); + } else if (s == "hello world!") { + return UnsignedCharArrayFromHexDigest("fc3ff98e8c6a0d3087d515c0473f8677"); + } else if (s == "a") { + return UnsignedCharArrayFromHexDigest("0cc175b9c0f1b6a831c399e269772661"); + } else if (s == "abc") { + return UnsignedCharArrayFromHexDigest("900150983cd24fb0d6963f7d28e17f72"); + } else if (s == "the quick brown fox jumps over the lazy dog") { + return UnsignedCharArrayFromHexDigest("77add1d5f41223d5582fca736a5cb335"); + } else if (s == GetStringWithAllPossibleCharacters(512)) { + return UnsignedCharArrayFromHexDigest("f5c8e3c31c044bae0e65569560b54332"); + } else if (s == GetStringWithAllPossibleCharacters(8192)) { + return UnsignedCharArrayFromHexDigest("6556112372898c69e1de0bf689d8db26"); + } else { + HARD_FAIL("no precomputed digest for string: %s", s); + return {}; + } +} + +std::string GetStringWithAllPossibleCharacters(int length) { + HARD_ASSERT(length >= 256); + std::string result; + for (int i = 0; i < length; ++i) { + result += static_cast(i); + } + return result; +} + } // namespace From e4574a4d025b34cf1f76cd7c27bcbb1af57d1599 Mon Sep 17 00:00:00 2001 From: Denver Coneybeare Date: Wed, 22 Feb 2023 00:04:47 -0500 Subject: [PATCH 03/19] replace Md5 class with CalculateMd5Digest() function --- Firestore/core/src/util/md5.h | 52 ++------------------ Firestore/core/src/util/md5_openssl.cc | 38 +++++++++------ Firestore/core/test/unit/util/md5_test.cc | 59 ++++++++--------------- 3 files changed, 48 insertions(+), 101 deletions(-) diff --git a/Firestore/core/src/util/md5.h b/Firestore/core/src/util/md5.h index 4c30bbd50de..a00de05facd 100644 --- a/Firestore/core/src/util/md5.h +++ b/Firestore/core/src/util/md5.h @@ -18,59 +18,17 @@ #define FIRESTORE_CORE_SRC_UTIL_MD5_H_ #include -#include -#include "Firestore/core/src/util/config.h" - -#if HAVE_OPENSSL_MD5_H -#include "openssl/md5.h" -#else -#error "No MD5 implementation is available" -#endif +#include "absl/strings/string_view.h" namespace firebase { namespace firestore { namespace util { -class Md5 final { - public: - Md5(); - ~Md5() = default; - - // Copyable and movable - Md5(const Md5&); - Md5& operator=(const Md5&); - Md5(Md5&&) noexcept; - Md5& operator=(Md5&&) noexcept; - - /** - * Resets the internal state to its newly-constructed state. - * - * Invoke this method if it is desired to calculate a new digest after this - * object has already been used to calculate another digest. - */ - void Reset(); - - /** - * Consumes the given data and updates the digest calculated so far. - * @param data the data to consume. - * @param len the length of the given data to consume. - */ - void Update(const void* data, int len); - - /** - * Returns the calculated digest based on previous calls to Update(). - * - * NOTE: After calling Digest(), calling Update() or Digest() again is - * undefined behavior. To continue using this object, call Reset(). - */ - std::array Digest(); - - private: -#if HAVE_OPENSSL_MD5_H - MD5_CTX ctx_; -#endif -}; +/** + * Calculates and returns the md5 digest of the given string. + */ +std::array CalculateMd5Digest(absl::string_view); } // namespace util } // namespace firestore diff --git a/Firestore/core/src/util/md5_openssl.cc b/Firestore/core/src/util/md5_openssl.cc index 079d5d4b913..4c72747d17f 100644 --- a/Firestore/core/src/util/md5_openssl.cc +++ b/Firestore/core/src/util/md5_openssl.cc @@ -18,28 +18,34 @@ #include "Firestore/core/src/util/hard_assert.h" +#include "openssl/md5.h" + namespace firebase { namespace firestore { namespace util { -Md5::Md5() { - const int md5_init_result = MD5_Init(&ctx_); - HARD_ASSERT(md5_init_result == 1, "MD5_Init() returns %s but expected 1", - md5_init_result); -} +std::array CalculateMd5Digest(absl::string_view s) { + MD5_CTX ctx; -void Md5::Update(const void* data, int len) { - int md5_update_result = MD5_Update(&ctx_, data, len); - HARD_ASSERT(md5_update_result == 1, "MD5_Update() returns %s but expected 1", - md5_update_result); -} + { + const int md5_init_result = MD5_Init(&ctx); + HARD_ASSERT(md5_init_result == 1, "MD5_Init() returned %s, but expected 1", + md5_init_result); + } + + { + int md5_update_result = MD5_Update(&ctx, s.data(), s.length()); + HARD_ASSERT(md5_update_result == 1, + "MD5_Update() returned %s, but expected 1", md5_update_result); + } -std::array Md5::Digest() { - std::array digest; - int md5_final_result = MD5_Final(digest.data(), &ctx_); - HARD_ASSERT(md5_final_result == 1, "MD5_Final() returns %s but expected 1", - md5_final_result); - return digest; + { + std::array digest; + int md5_final_result = MD5_Final(digest.data(), &ctx); + HARD_ASSERT(md5_final_result == 1, "MD5_Final() returned %s but expected 1", + md5_final_result); + return digest; + } } } // namespace util diff --git a/Firestore/core/test/unit/util/md5_test.cc b/Firestore/core/test/unit/util/md5_test.cc index 22600bc5942..eaf9bc7a3c4 100644 --- a/Firestore/core/test/unit/util/md5_test.cc +++ b/Firestore/core/test/unit/util/md5_test.cc @@ -22,7 +22,7 @@ #include "gtest/gtest.h" -using firebase::firestore::util::Md5; +using firebase::firestore::util::CalculateMd5Digest; namespace { @@ -54,54 +54,37 @@ std::array GetPreComputedDigest(const std::string&); // length. The given length must be at least 256. std::string GetStringWithAllPossibleCharacters(int length); -TEST(Md5Test, DefaultConstrutorWorks) { - Md5 instance1; - Md5 instance2; +TEST(CalculateMd5DigestTest, ShouldReturnMd5DigestOfEmptyString) { + EXPECT_EQ(CalculateMd5Digest(""), GetPreComputedDigest("")); } -TEST(Md5Test, DigestReturnsCorrectValueOnDefaultConstructedInstance) { - Md5 md5; - EXPECT_EQ(md5.Digest(), GetPreComputedDigest("")); +TEST(CalculateMd5DigestTest, ShouldReturnMd5DigestOfA) { + EXPECT_EQ(CalculateMd5Digest("a"), GetPreComputedDigest("a")); } -TEST(Md5Test, UpdateInvokedOnceYieldCorrectDigest) { - Md5 md5; - - md5.Update("hello world!", 12); - - EXPECT_EQ(md5.Digest(), GetPreComputedDigest("hello world!")); +TEST(CalculateMd5DigestTest, ShouldReturnMd5DigestOfABC) { + EXPECT_EQ(CalculateMd5Digest("abc"), GetPreComputedDigest("abc")); } -TEST(Md5Test, UpdateInvokedTwiceYieldCorrectDigest) { - Md5 md5; - - md5.Update("hello ", 6); - md5.Update("world!", 6); - - EXPECT_EQ(md5.Digest(), GetPreComputedDigest("hello world!")); +TEST(CalculateMd5DigestTest, ShouldReturnMd5DigestOfHelloWorld) { + EXPECT_EQ(CalculateMd5Digest("hello world!"), + GetPreComputedDigest("hello world!")); } -TEST(Md5Test, UpdateInvokedThriceYieldCorrectDigest) { - Md5 md5; - - md5.Update("hell", 4); - md5.Update("o wo", 4); - md5.Update("rld!", 4); - - EXPECT_EQ(md5.Digest(), GetPreComputedDigest("hello world!")); +TEST(CalculateMd5DigestTest, ShouldReturnMd5DigestOfTheQuickBrownFox) { + EXPECT_EQ( + CalculateMd5Digest("the quick brown fox jumps over the lazy dog"), + GetPreComputedDigest("the quick brown fox jumps over the lazy dog")); } -TEST(Md5Test, DigestsOfVariousStrings) { - std::vector strings{ - "a", "abc", "the quick brown fox jumps over the lazy dog", - GetStringWithAllPossibleCharacters(512), - GetStringWithAllPossibleCharacters(8192)}; +TEST(CalculateMd5DigestTest, ShouldReturnMd5DigestOfShortStringWithAllChars) { + const std::string s = GetStringWithAllPossibleCharacters(512); + EXPECT_EQ(CalculateMd5Digest(s), GetPreComputedDigest(s)); +} - for (const std::string& s : strings) { - Md5 md5; - md5.Update(s.data(), s.length()); - EXPECT_EQ(md5.Digest(), GetPreComputedDigest(s)); - } +TEST(CalculateMd5DigestTest, ShouldReturnMd5DigestOfLongStringWithAllChars) { + const std::string s = GetStringWithAllPossibleCharacters(8192); + EXPECT_EQ(CalculateMd5Digest(s), GetPreComputedDigest(s)); } unsigned char UnsignedCharFromHexDigit(char digit) { From 09934bc225ca454f40104729694b36c7997f9ca9 Mon Sep 17 00:00:00 2001 From: Denver Coneybeare Date: Wed, 22 Feb 2023 00:09:14 -0500 Subject: [PATCH 04/19] rename GetPreComputedDigest to GetPreComputedMd5Digest --- Firestore/core/test/unit/util/md5_test.cc | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Firestore/core/test/unit/util/md5_test.cc b/Firestore/core/test/unit/util/md5_test.cc index eaf9bc7a3c4..beddc1acc63 100644 --- a/Firestore/core/test/unit/util/md5_test.cc +++ b/Firestore/core/test/unit/util/md5_test.cc @@ -48,43 +48,43 @@ std::array UnsignedCharArrayFromHexDigest( // of pre-calculated digests that it returns. // It is an error if this function does not have a pre-calculated digest for the // given string. -std::array GetPreComputedDigest(const std::string&); +std::array GetPreComputedMd5Digest(const std::string&); // Generates and returns a string with all possible characters with the given // length. The given length must be at least 256. std::string GetStringWithAllPossibleCharacters(int length); TEST(CalculateMd5DigestTest, ShouldReturnMd5DigestOfEmptyString) { - EXPECT_EQ(CalculateMd5Digest(""), GetPreComputedDigest("")); + EXPECT_EQ(CalculateMd5Digest(""), GetPreComputedMd5Digest("")); } TEST(CalculateMd5DigestTest, ShouldReturnMd5DigestOfA) { - EXPECT_EQ(CalculateMd5Digest("a"), GetPreComputedDigest("a")); + EXPECT_EQ(CalculateMd5Digest("a"), GetPreComputedMd5Digest("a")); } TEST(CalculateMd5DigestTest, ShouldReturnMd5DigestOfABC) { - EXPECT_EQ(CalculateMd5Digest("abc"), GetPreComputedDigest("abc")); + EXPECT_EQ(CalculateMd5Digest("abc"), GetPreComputedMd5Digest("abc")); } TEST(CalculateMd5DigestTest, ShouldReturnMd5DigestOfHelloWorld) { EXPECT_EQ(CalculateMd5Digest("hello world!"), - GetPreComputedDigest("hello world!")); + GetPreComputedMd5Digest("hello world!")); } TEST(CalculateMd5DigestTest, ShouldReturnMd5DigestOfTheQuickBrownFox) { EXPECT_EQ( CalculateMd5Digest("the quick brown fox jumps over the lazy dog"), - GetPreComputedDigest("the quick brown fox jumps over the lazy dog")); + GetPreComputedMd5Digest("the quick brown fox jumps over the lazy dog")); } TEST(CalculateMd5DigestTest, ShouldReturnMd5DigestOfShortStringWithAllChars) { const std::string s = GetStringWithAllPossibleCharacters(512); - EXPECT_EQ(CalculateMd5Digest(s), GetPreComputedDigest(s)); + EXPECT_EQ(CalculateMd5Digest(s), GetPreComputedMd5Digest(s)); } TEST(CalculateMd5DigestTest, ShouldReturnMd5DigestOfLongStringWithAllChars) { const std::string s = GetStringWithAllPossibleCharacters(8192); - EXPECT_EQ(CalculateMd5Digest(s), GetPreComputedDigest(s)); + EXPECT_EQ(CalculateMd5Digest(s), GetPreComputedMd5Digest(s)); } unsigned char UnsignedCharFromHexDigit(char digit) { @@ -137,7 +137,7 @@ std::array UnsignedCharArrayFromHexDigest( return result; } -std::array GetPreComputedDigest(const std::string& s) { +std::array GetPreComputedMd5Digest(const std::string& s) { if (s == "") { return UnsignedCharArrayFromHexDigest("d41d8cd98f00b204e9800998ecf8427e"); } else if (s == "hello world!") { From 53f2e127390240dd1bbe89f304afd684adee455f Mon Sep 17 00:00:00 2001 From: Denver Coneybeare Date: Wed, 22 Feb 2023 00:11:10 -0500 Subject: [PATCH 05/19] config_detected.h.in: remove HAVE_OPENSSL_MD5_H since it's not used --- Firestore/core/src/util/config_detected.h.in | 2 -- 1 file changed, 2 deletions(-) diff --git a/Firestore/core/src/util/config_detected.h.in b/Firestore/core/src/util/config_detected.h.in index 9031897286e..69a9a50052f 100644 --- a/Firestore/core/src/util/config_detected.h.in +++ b/Firestore/core/src/util/config_detected.h.in @@ -27,6 +27,4 @@ #cmakedefine HAVE_OPENSSL_RAND_H 1 -#cmakedefine HAVE_OPENSSL_MD5_H 1 - #endif // FIRESTORE_CORE_SRC_UTIL_CONFIG_DETECTED_H_ From 382a5bc9e1d3a81491cc27bb64640ce6f1919960 Mon Sep 17 00:00:00 2001 From: Denver Coneybeare Date: Wed, 22 Feb 2023 00:26:21 -0500 Subject: [PATCH 06/19] Firestore.xcodeproj/project.pbxproj: add md5_test.cc --- .../Example/Firestore.xcodeproj/project.pbxproj | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Firestore/Example/Firestore.xcodeproj/project.pbxproj b/Firestore/Example/Firestore.xcodeproj/project.pbxproj index 9003d232284..05cad83099b 100644 --- a/Firestore/Example/Firestore.xcodeproj/project.pbxproj +++ b/Firestore/Example/Firestore.xcodeproj/project.pbxproj @@ -411,6 +411,7 @@ 4DF18D15AC926FB7A4888313 /* lru_garbage_collector_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 277EAACC4DD7C21332E8496A /* lru_garbage_collector_test.cc */; }; 4E0777435A9A26B8B2C08A1E /* remote_document_cache_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 7EB299CF85034F09CFD6F3FD /* remote_document_cache_test.cc */; }; 4E2E0314F9FDD7BCED60254A /* counting_query_engine.cc in Sources */ = {isa = PBXBuildFile; fileRef = 99434327614FEFF7F7DC88EC /* counting_query_engine.cc */; }; + 4EC3518D09A8C37A28763052 /* md5_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = DD044ED27730B7A9000DF4FE /* md5_test.cc */; }; 4EC642DFC4AE98DBFFB37B17 /* fields_array_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA4CBA48204C9E25B56993BC /* fields_array_test.cc */; }; 4EE1ABA574FBFDC95165624C /* delayed_constructor_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = D0A6E9136804A41CEC9D55D4 /* delayed_constructor_test.cc */; }; 4F5714D37B6D119CB07ED8AE /* orderby_spec_test.json in Resources */ = {isa = PBXBuildFile; fileRef = 54DA12A21F315EE100DD57A1 /* orderby_spec_test.json */; }; @@ -426,6 +427,7 @@ 5150E9F256E6E82D6F3CB3F1 /* bundle_cache_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = F7FC06E0A47D393DE1759AE1 /* bundle_cache_test.cc */; }; 518BF03D57FBAD7C632D18F8 /* FIRQueryUnitTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = FF73B39D04D1760190E6B84A /* FIRQueryUnitTests.mm */; }; 52967C3DD7896BFA48840488 /* byte_string_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 5342CDDB137B4E93E2E85CCA /* byte_string_test.cc */; }; + 52BC453A24D8335C5A57C775 /* md5_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = DD044ED27730B7A9000DF4FE /* md5_test.cc */; }; 53AB47E44D897C81A94031F6 /* write.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 544129D921C2DDC800EFB9CC /* write.pb.cc */; }; 53BBB5CDED453F923ADD08D2 /* stream_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 5B5414D28802BC76FDADABD6 /* stream_test.cc */; }; 53F449F69DF8A3ABC711FD59 /* secure_random_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54740A531FC913E500713A1A /* secure_random_test.cc */; }; @@ -791,6 +793,7 @@ 80D7FEBB1056E489F24C6C8F /* firebase_app_check_credentials_provider_test.mm in Sources */ = {isa = PBXBuildFile; fileRef = F119BDDF2F06B3C0883B8297 /* firebase_app_check_credentials_provider_test.mm */; }; 80D8B7D6FFFEA12AF10E4E2B /* leveldb_overlay_migration_manager_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = D8A6D52723B1BABE1B7B8D8F /* leveldb_overlay_migration_manager_test.cc */; }; 814724DE70EFC3DDF439CD78 /* executor_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6FB4688208F9B9100554BA2 /* executor_test.cc */; }; + 816375E636FD0EAECFA1E764 /* md5_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = DD044ED27730B7A9000DF4FE /* md5_test.cc */; }; 816E8E62DC163649BA96951C /* EncodableFieldValueTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1235769122B7E915007DDFA9 /* EncodableFieldValueTests.swift */; }; 81A6B241E63540900F205817 /* view_snapshot_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = CC572A9168BBEF7B83E4BBC5 /* view_snapshot_test.cc */; }; 81AF02881A8D23D02FC202F6 /* bundle_loader_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = A853C81A6A5A51C9D0389EDA /* bundle_loader_test.cc */; }; @@ -798,6 +801,7 @@ 81D1B1D2B66BD8310AC5707F /* string_win_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 79507DF8378D3C42F5B36268 /* string_win_test.cc */; }; 82228CD6CE4A7A9254F8E82D /* leveldb_snappy_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = D9D94300B9C02F7069523C00 /* leveldb_snappy_test.cc */; }; 822E5D5EC4955393DF26BC5C /* string_apple_benchmark.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4C73C0CC6F62A90D8573F383 /* string_apple_benchmark.mm */; }; + 82473F290CC7D9579D64A175 /* md5_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = DD044ED27730B7A9000DF4FE /* md5_test.cc */; }; 82E3634FCF4A882948B81839 /* FIRQueryUnitTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = FF73B39D04D1760190E6B84A /* FIRQueryUnitTests.mm */; }; 8342277EB0553492B6668877 /* leveldb_opener_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 75860CD13AF47EB1EA39EC2F /* leveldb_opener_test.cc */; }; 8388418F43042605FB9BFB92 /* testutil.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54A0352820A3B3BD003E0143 /* testutil.cc */; }; @@ -819,6 +823,7 @@ 85D7C370C7812166A467FEE9 /* string_apple_benchmark.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4C73C0CC6F62A90D8573F383 /* string_apple_benchmark.mm */; }; 86004E06C088743875C13115 /* load_bundle_task_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 8F1A7B4158D9DD76EE4836BF /* load_bundle_task_test.cc */; }; 8612F3C7E4A7D17221442699 /* grpc_unary_call_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6D964942163E63900EB9CFB /* grpc_unary_call_test.cc */; }; + 861EA75409AB15BADCD5793F /* md5_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = DD044ED27730B7A9000DF4FE /* md5_test.cc */; }; 862B1AC9EDAB309BBF4FB18C /* sorted_map_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 549CCA4E20A36DBB00BCEB75 /* sorted_map_test.cc */; }; 86494278BE08F10A8AAF9603 /* iterator_adaptors_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 54A0353420A3D8CB003E0143 /* iterator_adaptors_test.cc */; }; 867B370BF2DF84B6AB94B874 /* filesystem_testing.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA02DA2FCD0001CFC6EB08DA /* filesystem_testing.cc */; }; @@ -830,6 +835,7 @@ 87B5AC3EBF0E83166B142FA4 /* string_apple_benchmark.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4C73C0CC6F62A90D8573F383 /* string_apple_benchmark.mm */; }; 881E55152AB34465412F8542 /* FSTAPIHelpers.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5492E04E202154AA00B64F25 /* FSTAPIHelpers.mm */; }; 88929ED628DA8DD9592974ED /* task_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 899FC22684B0F7BEEAE13527 /* task_test.cc */; }; + 88C1B719AC0AABE9EEEA71BA /* md5_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = DD044ED27730B7A9000DF4FE /* md5_test.cc */; }; 88FD82A1FC5FEC5D56B481D8 /* maybe_document.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 618BBE7E20B89AAC00B5BCE7 /* maybe_document.pb.cc */; }; 897F3C1936612ACB018CA1DD /* http.pb.cc in Sources */ = {isa = PBXBuildFile; fileRef = 618BBE9720B89AAC00B5BCE7 /* http.pb.cc */; }; 89C71AEAA5316836BB1D5A01 /* view_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = C7429071B33BDF80A7FA2F8A /* view_test.cc */; }; @@ -1783,6 +1789,7 @@ DAFF0D0221E64AC40062958F /* macOS.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = macOS.entitlements; sourceTree = ""; }; DB1F1E1B1ED15E8D042144B1 /* leveldb_query_engine_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = leveldb_query_engine_test.cc; sourceTree = ""; }; DB5A1E760451189DA36028B3 /* memory_index_manager_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = memory_index_manager_test.cc; sourceTree = ""; }; + DD044ED27730B7A9000DF4FE /* md5_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; path = md5_test.cc; sourceTree = ""; }; DE03B2E91F2149D600A30B9C /* Firestore_IntegrationTests_iOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Firestore_IntegrationTests_iOS.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; DE0761F61F2FE68D003233AF /* BasicCompileTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BasicCompileTests.swift; sourceTree = ""; }; DE51B1881F0D48AC0013853F /* FSTHelpers.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FSTHelpers.h; sourceTree = ""; }; @@ -2063,6 +2070,7 @@ 54A0353420A3D8CB003E0143 /* iterator_adaptors_test.cc */, 54C2294E1FECABAE007D065B /* log_test.cc */, 28B45B2104E2DAFBBF86DBB7 /* logic_utils_test.cc */, + DD044ED27730B7A9000DF4FE /* md5_test.cc */, 0473AFFF5567E667A125347B /* ordered_code_benchmark.cc */, AB380D03201BC6E400D97691 /* ordered_code_test.cc */, 403DBF6EFB541DFD01582AA3 /* path_test.cc */, @@ -3720,6 +3728,7 @@ F924DF3D9DCD2720C315A372 /* logic_utils_test.cc in Sources */, 3F6C9F8A993CF4B0CD51E7F0 /* lru_garbage_collector_test.cc in Sources */, 12158DFCEE09D24B7988A340 /* maybe_document.pb.cc in Sources */, + 52BC453A24D8335C5A57C775 /* md5_test.cc in Sources */, FA43BA0195DA90CE29B29D36 /* memory_bundle_cache_test.cc in Sources */, 8F2055702DB5EE8DA4BACD7C /* memory_document_overlay_cache_test.cc in Sources */, CFF1EBC60A00BA5109893C6E /* memory_index_manager_test.cc in Sources */, @@ -3927,6 +3936,7 @@ 7EF56BA2A480026D62CCA35A /* logic_utils_test.cc in Sources */, 1F56F51EB6DF0951B1F4F85B /* lru_garbage_collector_test.cc in Sources */, 88FD82A1FC5FEC5D56B481D8 /* maybe_document.pb.cc in Sources */, + 88C1B719AC0AABE9EEEA71BA /* md5_test.cc in Sources */, 9611A0FAA2E10A6B1C1AC2EA /* memory_bundle_cache_test.cc in Sources */, 75C6CECF607CA94F56260BAB /* memory_document_overlay_cache_test.cc in Sources */, 3987A3E8534BAA496D966735 /* memory_index_manager_test.cc in Sources */, @@ -4150,6 +4160,7 @@ 0595B5EBEB8F09952B72C883 /* logic_utils_test.cc in Sources */, 913F6E57AF18F84C5ECFD414 /* lru_garbage_collector_test.cc in Sources */, 6F511ABFD023AEB81F92DB12 /* maybe_document.pb.cc in Sources */, + 861EA75409AB15BADCD5793F /* md5_test.cc in Sources */, FF6333B8BD9732C068157221 /* memory_bundle_cache_test.cc in Sources */, 5F6FD840AC2D729B50991CCB /* memory_document_overlay_cache_test.cc in Sources */, E6B825EE85BF20B88AF3E3CD /* memory_index_manager_test.cc in Sources */, @@ -4373,6 +4384,7 @@ 0D6AE96565603226DB2E6838 /* logic_utils_test.cc in Sources */, 95CE3F5265B9BB7297EE5A6B /* lru_garbage_collector_test.cc in Sources */, C19214F5B43AA745A7FC2FC1 /* maybe_document.pb.cc in Sources */, + 82473F290CC7D9579D64A175 /* md5_test.cc in Sources */, 94854FAEAEA75A1AC77A0515 /* memory_bundle_cache_test.cc in Sources */, 053C11420E49AE1A77E21C20 /* memory_document_overlay_cache_test.cc in Sources */, 4D8367018652104A8803E8DB /* memory_index_manager_test.cc in Sources */, @@ -4590,6 +4602,7 @@ D156B9F19B5B29E77664FDFC /* logic_utils_test.cc in Sources */, 1290FA77A922B76503AE407C /* lru_garbage_collector_test.cc in Sources */, 618BBEA720B89AAC00B5BCE7 /* maybe_document.pb.cc in Sources */, + 4EC3518D09A8C37A28763052 /* md5_test.cc in Sources */, A0E1C7F5C7093A498F65C5CF /* memory_bundle_cache_test.cc in Sources */, E56EEC9DAC455E2BE77D110A /* memory_document_overlay_cache_test.cc in Sources */, 3B47CC43DBA24434E215B8ED /* memory_index_manager_test.cc in Sources */, @@ -4832,6 +4845,7 @@ 6FCC64A1937E286E76C294D0 /* logic_utils_test.cc in Sources */, 4DF18D15AC926FB7A4888313 /* lru_garbage_collector_test.cc in Sources */, 12E04A12ABD5533B616D552A /* maybe_document.pb.cc in Sources */, + 816375E636FD0EAECFA1E764 /* md5_test.cc in Sources */, 479A392EAB42453D49435D28 /* memory_bundle_cache_test.cc in Sources */, 5CEB0E83DA68652927D2CF07 /* memory_document_overlay_cache_test.cc in Sources */, 90FE088B8FD9EC06EEED1F39 /* memory_index_manager_test.cc in Sources */, From 5c292f445fd0003f16f098c995b37afd30256d7f Mon Sep 17 00:00:00 2001 From: Denver Coneybeare Date: Wed, 22 Feb 2023 00:35:16 -0500 Subject: [PATCH 07/19] md5_test.cc: removed unnecessary semicolon that lint was complaining about. --- Firestore/core/test/unit/util/md5_test.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Firestore/core/test/unit/util/md5_test.cc b/Firestore/core/test/unit/util/md5_test.cc index beddc1acc63..31cfe074cc7 100644 --- a/Firestore/core/test/unit/util/md5_test.cc +++ b/Firestore/core/test/unit/util/md5_test.cc @@ -121,7 +121,7 @@ unsigned char UnsignedCharFromHexDigit(char digit) { return 0xE; case 'f': return 0xF; - }; + } HARD_FAIL("unrecognized hex digit: %s", std::to_string(digit)); } From 073731c4e0c1fdfa8ff42ca63567a2c729a49fd0 Mon Sep 17 00:00:00 2001 From: Denver Coneybeare Date: Wed, 22 Feb 2023 11:14:31 -0500 Subject: [PATCH 08/19] Avoid setting clang/gcc -Wno-deprecated-declarations flag on other compilers --- Firestore/core/CMakeLists.txt | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/Firestore/core/CMakeLists.txt b/Firestore/core/CMakeLists.txt index a966bc20799..5dfde238969 100644 --- a/Firestore/core/CMakeLists.txt +++ b/Firestore/core/CMakeLists.txt @@ -126,16 +126,19 @@ if(HAVE_OPENSSL_MD5_H) util_sources APPEND src/util/md5_openssl.cc ) - # Suppress deprecation warnings from openssl since its md5 - # implementation is deprecated, but only because md5 is not - # cryptographically secure, something we don't care about in our - # md5 usage. - set_source_files_properties( - src/util/md5_openssl.cc - PROPERTIES - COMPILE_FLAGS - -Wno-deprecated-declarations - ) + # Suppress deprecation warnings when compiling md5_openssl.cc because using + # openssl's md5 functions results in deprecation warnings, which causes the + # build to fail because warnings are treated as errors. In the future, we can + # migrate to the newer-style digest algorithms in openssl/evp.h and remove + # this deprecation suppression. + if(CXX_CLANG OR CXX_GNU) + set_source_files_properties( + src/util/md5_openssl.cc + PROPERTIES + COMPILE_FLAGS + -Wno-deprecated-declarations + ) + endif() else() message( FATAL_ERROR From ca3216b04661a38b56337bd963230e545347e968 Mon Sep 17 00:00:00 2001 From: Denver Coneybeare Date: Wed, 22 Feb 2023 11:14:57 -0500 Subject: [PATCH 09/19] md5_openssl.cc: avoid compiling when openssl/md5.h is not available --- Firestore/core/src/util/config_detected.h.in | 2 ++ Firestore/core/src/util/md5_openssl.cc | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/Firestore/core/src/util/config_detected.h.in b/Firestore/core/src/util/config_detected.h.in index 69a9a50052f..9031897286e 100644 --- a/Firestore/core/src/util/config_detected.h.in +++ b/Firestore/core/src/util/config_detected.h.in @@ -27,4 +27,6 @@ #cmakedefine HAVE_OPENSSL_RAND_H 1 +#cmakedefine HAVE_OPENSSL_MD5_H 1 + #endif // FIRESTORE_CORE_SRC_UTIL_CONFIG_DETECTED_H_ diff --git a/Firestore/core/src/util/md5_openssl.cc b/Firestore/core/src/util/md5_openssl.cc index 4c72747d17f..f5f99f0f56c 100644 --- a/Firestore/core/src/util/md5_openssl.cc +++ b/Firestore/core/src/util/md5_openssl.cc @@ -16,6 +16,10 @@ #include "Firestore/core/src/util/md5.h" +#include "Firestore/core/src/util/config.h" + +#if HAVE_OPENSSL_MD5_H + #include "Firestore/core/src/util/hard_assert.h" #include "openssl/md5.h" @@ -51,3 +55,5 @@ std::array CalculateMd5Digest(absl::string_view s) { } // namespace util } // namespace firestore } // namespace firebase + +#endif // HAVE_OPENSSL_MD5_H From 2216c806268eddae9558119b97bfaaa15c789215 Mon Sep 17 00:00:00 2001 From: Denver Coneybeare Date: Wed, 22 Feb 2023 11:23:20 -0500 Subject: [PATCH 10/19] CMakeLists.txt: add in wiring for commoncrypto --- Firestore/core/CMakeLists.txt | 13 +++++++++++-- Firestore/core/src/util/config_detected.h.in | 4 +++- Firestore/core/src/util/md5_openssl.cc | 4 ++-- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/Firestore/core/CMakeLists.txt b/Firestore/core/CMakeLists.txt index 5dfde238969..d823f43d19f 100644 --- a/Firestore/core/CMakeLists.txt +++ b/Firestore/core/CMakeLists.txt @@ -101,6 +101,8 @@ if(TARGET OpenSSL::Crypto) check_include_files(openssl/md5.h HAVE_OPENSSL_MD5_H) endif() +check_symbol_exists(CC_MD5_Init CommonCrypto/CommonDigest.h HAVE_COMMONCRYPTO_MD5) + firebase_ios_glob( util_sources EXCLUDE src/util/secure_random_*.cc ) @@ -121,7 +123,14 @@ else() ) endif() -if(HAVE_OPENSSL_MD5_H) +if(HAVE_COMMONCRYPTO_MD5) + set(FIRESTORE_MD5_IMPL_COMMONCRYPTO 1) + firebase_ios_glob( + util_sources APPEND + src/util/md5_commoncrypto.cc + ) +elseif(HAVE_OPENSSL_MD5_H) + set(FIRESTORE_MD5_IMPL_OPENSSL 1) firebase_ios_glob( util_sources APPEND src/util/md5_openssl.cc @@ -173,7 +182,7 @@ target_link_libraries( grpc++ ) -if(HAVE_OPENSSL_RAND_H OR HAVE_OPENSSL_MD5_H) +if(HAVE_OPENSSL_RAND_H OR FIRESTORE_MD5_IMPL_OPENSSL) target_link_libraries( firestore_util PRIVATE OpenSSL::Crypto diff --git a/Firestore/core/src/util/config_detected.h.in b/Firestore/core/src/util/config_detected.h.in index 9031897286e..8e42351e822 100644 --- a/Firestore/core/src/util/config_detected.h.in +++ b/Firestore/core/src/util/config_detected.h.in @@ -27,6 +27,8 @@ #cmakedefine HAVE_OPENSSL_RAND_H 1 -#cmakedefine HAVE_OPENSSL_MD5_H 1 +// Exactly one of the following FIRESTORE_MD5_IMPL_ will be defined. +#cmakedefine FIRESTORE_MD5_IMPL_COMMONCRYPTO 1 +#cmakedefine FIRESTORE_MD5_IMPL_OPENSSL 1 #endif // FIRESTORE_CORE_SRC_UTIL_CONFIG_DETECTED_H_ diff --git a/Firestore/core/src/util/md5_openssl.cc b/Firestore/core/src/util/md5_openssl.cc index f5f99f0f56c..3bc55a3f311 100644 --- a/Firestore/core/src/util/md5_openssl.cc +++ b/Firestore/core/src/util/md5_openssl.cc @@ -18,7 +18,7 @@ #include "Firestore/core/src/util/config.h" -#if HAVE_OPENSSL_MD5_H +#if FIRESTORE_MD5_IMPL_OPENSSL #include "Firestore/core/src/util/hard_assert.h" @@ -56,4 +56,4 @@ std::array CalculateMd5Digest(absl::string_view s) { } // namespace firestore } // namespace firebase -#endif // HAVE_OPENSSL_MD5_H +#endif // FIRESTORE_MD5_IMPL_OPENSSL From eb1da37a0c1b84fe8f6f40dd4f3da1551455975d Mon Sep 17 00:00:00 2001 From: Denver Coneybeare Date: Wed, 22 Feb 2023 11:42:28 -0500 Subject: [PATCH 11/19] md5_commoncrypto.cc: implement it --- Firestore/core/CMakeLists.txt | 14 ++++++ Firestore/core/src/util/md5_commoncrypto.cc | 56 +++++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 Firestore/core/src/util/md5_commoncrypto.cc diff --git a/Firestore/core/CMakeLists.txt b/Firestore/core/CMakeLists.txt index d823f43d19f..e767da2df8e 100644 --- a/Firestore/core/CMakeLists.txt +++ b/Firestore/core/CMakeLists.txt @@ -129,6 +129,20 @@ if(HAVE_COMMONCRYPTO_MD5) util_sources APPEND src/util/md5_commoncrypto.cc ) + # Suppress deprecation warnings when compiling md5_commoncrypto.cc because + # using CommonCrypto's md5 functions results in deprecation warnings, which + # causes the build to fail because warnings are treated as errors. The MD5 + # functions are marked as "deprecated" because they are "cryptographically + # broken"; however, Firestore's use of md5 is _not_ in a cryptographic context + # and, therefore, a non-cryptographically-secure algorithm is acceptable. + if(CXX_CLANG OR CXX_GNU) + set_source_files_properties( + src/util/md5_commoncrypto.cc + PROPERTIES + COMPILE_FLAGS + -Wno-deprecated-declarations + ) + endif() elseif(HAVE_OPENSSL_MD5_H) set(FIRESTORE_MD5_IMPL_OPENSSL 1) firebase_ios_glob( diff --git a/Firestore/core/src/util/md5_commoncrypto.cc b/Firestore/core/src/util/md5_commoncrypto.cc new file mode 100644 index 00000000000..42d6b854f68 --- /dev/null +++ b/Firestore/core/src/util/md5_commoncrypto.cc @@ -0,0 +1,56 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Firestore/core/src/util/md5.h" + +#include "Firestore/core/src/util/config.h" + +#if FIRESTORE_MD5_IMPL_COMMONCRYPTO + +#include "Firestore/core/src/util/hard_assert.h" + +#include + +namespace firebase { +namespace firestore { +namespace util { + +std::array CalculateMd5Digest(absl::string_view s) { + CC_MD5_CTX ctx; + + { + const int md5_init_result = CC_MD5_Init(&ctx); + HARD_ASSERT(md5_init_result == 1, "CC_MD5_Init() returned %s, but expected 1", md5_init_result); + } + + { + int md5_update_result = CC_MD5_Update(&ctx, s.data(), s.length()); + HARD_ASSERT(md5_update_result == 1, "CC_MD5_Update() returned %s, but expected 1", md5_update_result); + } + + { + std::array digest; + int md5_final_result = CC_MD5_Final(digest.data(), &ctx); + HARD_ASSERT(md5_final_result == 1, "CC_MD5_Final() returned %s but expected 1", md5_final_result); + return digest; + } +} + +} // namespace util +} // namespace firestore +} // namespace firebase + +#endif // FIRESTORE_MD5_IMPL_COMMONCRYPTO From 2fa212ebd97a3cdfff0b9040850d0de805fca695 Mon Sep 17 00:00:00 2001 From: Denver Coneybeare Date: Wed, 22 Feb 2023 11:46:07 -0500 Subject: [PATCH 12/19] md5_commoncrypto.cc: format code --- Firestore/core/src/util/md5_commoncrypto.cc | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Firestore/core/src/util/md5_commoncrypto.cc b/Firestore/core/src/util/md5_commoncrypto.cc index 42d6b854f68..5adb5c09593 100644 --- a/Firestore/core/src/util/md5_commoncrypto.cc +++ b/Firestore/core/src/util/md5_commoncrypto.cc @@ -33,18 +33,22 @@ std::array CalculateMd5Digest(absl::string_view s) { { const int md5_init_result = CC_MD5_Init(&ctx); - HARD_ASSERT(md5_init_result == 1, "CC_MD5_Init() returned %s, but expected 1", md5_init_result); + HARD_ASSERT(md5_init_result == 1, + "CC_MD5_Init() returned %s, but expected 1", md5_init_result); } { int md5_update_result = CC_MD5_Update(&ctx, s.data(), s.length()); - HARD_ASSERT(md5_update_result == 1, "CC_MD5_Update() returned %s, but expected 1", md5_update_result); + HARD_ASSERT(md5_update_result == 1, + "CC_MD5_Update() returned %s, but expected 1", + md5_update_result); } { std::array digest; int md5_final_result = CC_MD5_Final(digest.data(), &ctx); - HARD_ASSERT(md5_final_result == 1, "CC_MD5_Final() returned %s but expected 1", md5_final_result); + HARD_ASSERT(md5_final_result == 1, + "CC_MD5_Final() returned %s but expected 1", md5_final_result); return digest; } } From c3daab35d93bbc418469c036f2fa4db876c995a3 Mon Sep 17 00:00:00 2001 From: Denver Coneybeare Date: Wed, 22 Feb 2023 13:21:53 -0500 Subject: [PATCH 13/19] md5_commoncrypto.cc: reorder includes --- Firestore/core/src/util/md5_commoncrypto.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Firestore/core/src/util/md5_commoncrypto.cc b/Firestore/core/src/util/md5_commoncrypto.cc index 5adb5c09593..b9bd0f5f11f 100644 --- a/Firestore/core/src/util/md5_commoncrypto.cc +++ b/Firestore/core/src/util/md5_commoncrypto.cc @@ -20,10 +20,10 @@ #if FIRESTORE_MD5_IMPL_COMMONCRYPTO -#include "Firestore/core/src/util/hard_assert.h" - #include +#include "Firestore/core/src/util/hard_assert.h" + namespace firebase { namespace firestore { namespace util { From 1f15d687a9eaaee3097f46226f4d2586ba71f98b Mon Sep 17 00:00:00 2001 From: Denver Coneybeare Date: Wed, 22 Feb 2023 13:10:12 -0500 Subject: [PATCH 14/19] config.h: set FIRESTORE_MD5_IMPL_COMMONCRYPTO/FIRESTORE_MD5_IMPL_OPENSSL, as appropriate --- Firestore/core/src/util/config.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Firestore/core/src/util/config.h b/Firestore/core/src/util/config.h index a03227b70e7..4b215fd7e73 100644 --- a/Firestore/core/src/util/config.h +++ b/Firestore/core/src/util/config.h @@ -46,9 +46,11 @@ // Handle CocoaPods the same way since the settings are the same. #define HAVE_ARC4RANDOM 1 #define HAVE_LIBDISPATCH 1 +#define FIRESTORE_MD5_IMPL_COMMONCRYPTO 1 #elif __linux__ && SWIFT_PACKAGE #define HAVE_OPENSSL_RAND_H 1 +#define FIRESTORE_MD5_IMPL_OPENSSL 1 #else #error "Unknown build configuration" From c8b50836fb5339a36aa50be1ed0e32b1de9b6f49 Mon Sep 17 00:00:00 2001 From: Denver Coneybeare Date: Wed, 22 Feb 2023 13:30:45 -0500 Subject: [PATCH 15/19] md5_commoncrypto.cc: add NOLINTNEXTLINE(build/include) for CommonCrypto/CommonDigest.h --- Firestore/core/src/util/md5_commoncrypto.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/Firestore/core/src/util/md5_commoncrypto.cc b/Firestore/core/src/util/md5_commoncrypto.cc index b9bd0f5f11f..9c03f86385f 100644 --- a/Firestore/core/src/util/md5_commoncrypto.cc +++ b/Firestore/core/src/util/md5_commoncrypto.cc @@ -20,6 +20,7 @@ #if FIRESTORE_MD5_IMPL_COMMONCRYPTO +// NOLINTNEXTLINE(build/include) #include #include "Firestore/core/src/util/hard_assert.h" From ff50941bf77cb02979b29908c8ee3baa7dda4358 Mon Sep 17 00:00:00 2001 From: Denver Coneybeare Date: Wed, 22 Feb 2023 13:45:00 -0500 Subject: [PATCH 16/19] move deprecation warning suppression into sources, rather than cmakelists --- Firestore/core/CMakeLists.txt | 27 --------------------- Firestore/core/src/util/md5_commoncrypto.cc | 11 +++++++-- Firestore/core/src/util/md5_openssl.cc | 8 ++++-- 3 files changed, 15 insertions(+), 31 deletions(-) diff --git a/Firestore/core/CMakeLists.txt b/Firestore/core/CMakeLists.txt index e767da2df8e..d72e9927665 100644 --- a/Firestore/core/CMakeLists.txt +++ b/Firestore/core/CMakeLists.txt @@ -129,39 +129,12 @@ if(HAVE_COMMONCRYPTO_MD5) util_sources APPEND src/util/md5_commoncrypto.cc ) - # Suppress deprecation warnings when compiling md5_commoncrypto.cc because - # using CommonCrypto's md5 functions results in deprecation warnings, which - # causes the build to fail because warnings are treated as errors. The MD5 - # functions are marked as "deprecated" because they are "cryptographically - # broken"; however, Firestore's use of md5 is _not_ in a cryptographic context - # and, therefore, a non-cryptographically-secure algorithm is acceptable. - if(CXX_CLANG OR CXX_GNU) - set_source_files_properties( - src/util/md5_commoncrypto.cc - PROPERTIES - COMPILE_FLAGS - -Wno-deprecated-declarations - ) - endif() elseif(HAVE_OPENSSL_MD5_H) set(FIRESTORE_MD5_IMPL_OPENSSL 1) firebase_ios_glob( util_sources APPEND src/util/md5_openssl.cc ) - # Suppress deprecation warnings when compiling md5_openssl.cc because using - # openssl's md5 functions results in deprecation warnings, which causes the - # build to fail because warnings are treated as errors. In the future, we can - # migrate to the newer-style digest algorithms in openssl/evp.h and remove - # this deprecation suppression. - if(CXX_CLANG OR CXX_GNU) - set_source_files_properties( - src/util/md5_openssl.cc - PROPERTIES - COMPILE_FLAGS - -Wno-deprecated-declarations - ) - endif() else() message( FATAL_ERROR diff --git a/Firestore/core/src/util/md5_commoncrypto.cc b/Firestore/core/src/util/md5_commoncrypto.cc index 9c03f86385f..0e24c5a77e8 100644 --- a/Firestore/core/src/util/md5_commoncrypto.cc +++ b/Firestore/core/src/util/md5_commoncrypto.cc @@ -17,18 +17,24 @@ #include "Firestore/core/src/util/md5.h" #include "Firestore/core/src/util/config.h" +#include "Firestore/core/src/util/hard_assert.h" +#include "Firestore/core/src/util/warnings.h" #if FIRESTORE_MD5_IMPL_COMMONCRYPTO // NOLINTNEXTLINE(build/include) #include -#include "Firestore/core/src/util/hard_assert.h" - namespace firebase { namespace firestore { namespace util { +// Suppress deprecation warnings because due to CommonCrypto's md5 functions. +// The MD5 functions are marked as "deprecated" because they are +// "cryptographically broken"; however, Firestore's use of md5 is _not_ in a +// cryptographic context and, therefore, a non-cryptographically-secure +// algorithm is acceptable. +SUPPRESS_DEPRECATED_DECLARATIONS_BEGIN() std::array CalculateMd5Digest(absl::string_view s) { CC_MD5_CTX ctx; @@ -53,6 +59,7 @@ std::array CalculateMd5Digest(absl::string_view s) { return digest; } } +SUPPRESS_END() } // namespace util } // namespace firestore diff --git a/Firestore/core/src/util/md5_openssl.cc b/Firestore/core/src/util/md5_openssl.cc index 3bc55a3f311..e8eaac1801b 100644 --- a/Firestore/core/src/util/md5_openssl.cc +++ b/Firestore/core/src/util/md5_openssl.cc @@ -17,17 +17,21 @@ #include "Firestore/core/src/util/md5.h" #include "Firestore/core/src/util/config.h" +#include "Firestore/core/src/util/hard_assert.h" +#include "Firestore/core/src/util/warnings.h" #if FIRESTORE_MD5_IMPL_OPENSSL -#include "Firestore/core/src/util/hard_assert.h" - #include "openssl/md5.h" namespace firebase { namespace firestore { namespace util { +// Suppress deprecation warnings due to using openssl's md5 functions. +// In the future, we can migrate to the newer-style digest algorithms in +// openssl/evp.h and remove this deprecation suppression. +SUPPRESS_DEPRECATED_DECLARATIONS_BEGIN() std::array CalculateMd5Digest(absl::string_view s) { MD5_CTX ctx; From 283d190f8935bcbc5aa9c8cc819b74105885c8e2 Mon Sep 17 00:00:00 2001 From: Denver Coneybeare Date: Mon, 27 Feb 2023 12:12:58 -0500 Subject: [PATCH 17/19] Replace dependency on openssl/commoncrypto for md5 by just copying the md5 implementation from boringssl --- Firestore/core/CMakeLists.txt | 23 +- Firestore/core/src/util/config.h | 2 - Firestore/core/src/util/config_detected.h.in | 4 - Firestore/core/src/util/md5.cc | 399 +++++++++++++++++++ Firestore/core/src/util/md5_commoncrypto.cc | 68 ---- Firestore/core/src/util/md5_openssl.cc | 63 --- 6 files changed, 400 insertions(+), 159 deletions(-) create mode 100644 Firestore/core/src/util/md5.cc delete mode 100644 Firestore/core/src/util/md5_commoncrypto.cc delete mode 100644 Firestore/core/src/util/md5_openssl.cc diff --git a/Firestore/core/CMakeLists.txt b/Firestore/core/CMakeLists.txt index d72e9927665..4663119d440 100644 --- a/Firestore/core/CMakeLists.txt +++ b/Firestore/core/CMakeLists.txt @@ -98,11 +98,8 @@ if(TARGET OpenSSL::Crypto) CMAKE_REQUIRED_INCLUDES OpenSSL::Crypto INTERFACE_INCLUDE_DIRECTORIES ) check_include_files(openssl/rand.h HAVE_OPENSSL_RAND_H) - check_include_files(openssl/md5.h HAVE_OPENSSL_MD5_H) endif() -check_symbol_exists(CC_MD5_Init CommonCrypto/CommonDigest.h HAVE_COMMONCRYPTO_MD5) - firebase_ios_glob( util_sources EXCLUDE src/util/secure_random_*.cc ) @@ -123,24 +120,6 @@ else() ) endif() -if(HAVE_COMMONCRYPTO_MD5) - set(FIRESTORE_MD5_IMPL_COMMONCRYPTO 1) - firebase_ios_glob( - util_sources APPEND - src/util/md5_commoncrypto.cc - ) -elseif(HAVE_OPENSSL_MD5_H) - set(FIRESTORE_MD5_IMPL_OPENSSL 1) - firebase_ios_glob( - util_sources APPEND - src/util/md5_openssl.cc - ) -else() - message( - FATAL_ERROR - "Don't know how to calculate md5 digests on this platform." - ) -endif() configure_file( src/util/config_detected.h.in @@ -169,7 +148,7 @@ target_link_libraries( grpc++ ) -if(HAVE_OPENSSL_RAND_H OR FIRESTORE_MD5_IMPL_OPENSSL) +if(HAVE_OPENSSL_RAND_H) target_link_libraries( firestore_util PRIVATE OpenSSL::Crypto diff --git a/Firestore/core/src/util/config.h b/Firestore/core/src/util/config.h index 4b215fd7e73..a03227b70e7 100644 --- a/Firestore/core/src/util/config.h +++ b/Firestore/core/src/util/config.h @@ -46,11 +46,9 @@ // Handle CocoaPods the same way since the settings are the same. #define HAVE_ARC4RANDOM 1 #define HAVE_LIBDISPATCH 1 -#define FIRESTORE_MD5_IMPL_COMMONCRYPTO 1 #elif __linux__ && SWIFT_PACKAGE #define HAVE_OPENSSL_RAND_H 1 -#define FIRESTORE_MD5_IMPL_OPENSSL 1 #else #error "Unknown build configuration" diff --git a/Firestore/core/src/util/config_detected.h.in b/Firestore/core/src/util/config_detected.h.in index 8e42351e822..69a9a50052f 100644 --- a/Firestore/core/src/util/config_detected.h.in +++ b/Firestore/core/src/util/config_detected.h.in @@ -27,8 +27,4 @@ #cmakedefine HAVE_OPENSSL_RAND_H 1 -// Exactly one of the following FIRESTORE_MD5_IMPL_ will be defined. -#cmakedefine FIRESTORE_MD5_IMPL_COMMONCRYPTO 1 -#cmakedefine FIRESTORE_MD5_IMPL_OPENSSL 1 - #endif // FIRESTORE_CORE_SRC_UTIL_CONFIG_DETECTED_H_ diff --git a/Firestore/core/src/util/md5.cc b/Firestore/core/src/util/md5.cc new file mode 100644 index 00000000000..a1ef27308b9 --- /dev/null +++ b/Firestore/core/src/util/md5.cc @@ -0,0 +1,399 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Firestore/core/src/util/md5.h" + +#include +#include + +namespace firebase { +namespace firestore { +namespace util { + +// This anonymous namespace contains the md5 implementation copied from +// BoringSSL. +namespace { + +// MD5_CBLOCK is the block size of MD5. +#define MD5_CBLOCK 64 + +// MD5_DIGEST_LENGTH is the length of an MD5 digest. +#define MD5_DIGEST_LENGTH 16 + +struct md5_state_st { + uint32_t h[4]; + uint32_t Nl, Nh; + uint8_t data[MD5_CBLOCK]; + unsigned num; +}; +typedef struct md5_state_st MD5_CTX; + +inline void OPENSSL_memset(void* dst, int c, size_t n) { + if (n != 0) { + std::memset(dst, c, n); + } +} + +inline void OPENSSL_memcpy(void* dst, const void* src, size_t n) { + if (n != 0) { + std::memcpy(dst, src, n); + } +} + +inline uint32_t CRYPTO_rotl_u32(uint32_t value, int shift) { +#if defined(_MSC_VER) + return _rotl(value, shift); +#else + return (value << shift) | (value >> ((-shift) & 31)); +#endif +} + +inline uint32_t CRYPTO_bswap4(uint32_t x) { + x = (x >> 16) | (x << 16); + x = ((x & 0xff00ff00) >> 8) | ((x & 0x00ff00ff) << 8); + return x; +} + +inline uint32_t CRYPTO_load_u32_le(const void* in) { + uint32_t v; + OPENSSL_memcpy(&v, in, sizeof(v)); + return v; +} + +inline void CRYPTO_store_u32_le(void* out, uint32_t v) { + OPENSSL_memcpy(out, &v, sizeof(v)); +} + +inline void CRYPTO_store_u32_be(void* out, uint32_t v) { + v = CRYPTO_bswap4(v); + OPENSSL_memcpy(out, &v, sizeof(v)); +} + +#define F(b, c, d) ((((c) ^ (d)) & (b)) ^ (d)) +#define G(b, c, d) ((((b) ^ (c)) & (d)) ^ (c)) +#define H(b, c, d) ((b) ^ (c) ^ (d)) +#define I(b, c, d) (((~(d)) | (b)) ^ (c)) + +#define R0(a, b, c, d, k, s, t) \ + do { \ + (a) += ((k) + (t) + F((b), (c), (d))); \ + (a) = CRYPTO_rotl_u32(a, s); \ + (a) += (b); \ + } while (0) + +#define R1(a, b, c, d, k, s, t) \ + do { \ + (a) += ((k) + (t) + G((b), (c), (d))); \ + (a) = CRYPTO_rotl_u32(a, s); \ + (a) += (b); \ + } while (0) + +#define R2(a, b, c, d, k, s, t) \ + do { \ + (a) += ((k) + (t) + H((b), (c), (d))); \ + (a) = CRYPTO_rotl_u32(a, s); \ + (a) += (b); \ + } while (0) + +#define R3(a, b, c, d, k, s, t) \ + do { \ + (a) += ((k) + (t) + I((b), (c), (d))); \ + (a) = CRYPTO_rotl_u32(a, s); \ + (a) += (b); \ + } while (0) + +#ifdef X +#undef X +#endif +static void md5_block_data_order(uint32_t* state, + const uint8_t* data, + size_t num) { + uint32_t A, B, C, D; + uint32_t XX0, XX1, XX2, XX3, XX4, XX5, XX6, XX7, XX8, XX9, XX10, XX11, XX12, + XX13, XX14, XX15; +#define X(i) XX##i + + A = state[0]; + B = state[1]; + C = state[2]; + D = state[3]; + + for (; num--;) { + X(0) = CRYPTO_load_u32_le(data); + data += 4; + X(1) = CRYPTO_load_u32_le(data); + data += 4; + // Round 0 + R0(A, B, C, D, X(0), 7, 0xd76aa478L); + X(2) = CRYPTO_load_u32_le(data); + data += 4; + R0(D, A, B, C, X(1), 12, 0xe8c7b756L); + X(3) = CRYPTO_load_u32_le(data); + data += 4; + R0(C, D, A, B, X(2), 17, 0x242070dbL); + X(4) = CRYPTO_load_u32_le(data); + data += 4; + R0(B, C, D, A, X(3), 22, 0xc1bdceeeL); + X(5) = CRYPTO_load_u32_le(data); + data += 4; + R0(A, B, C, D, X(4), 7, 0xf57c0fafL); + X(6) = CRYPTO_load_u32_le(data); + data += 4; + R0(D, A, B, C, X(5), 12, 0x4787c62aL); + X(7) = CRYPTO_load_u32_le(data); + data += 4; + R0(C, D, A, B, X(6), 17, 0xa8304613L); + X(8) = CRYPTO_load_u32_le(data); + data += 4; + R0(B, C, D, A, X(7), 22, 0xfd469501L); + X(9) = CRYPTO_load_u32_le(data); + data += 4; + R0(A, B, C, D, X(8), 7, 0x698098d8L); + X(10) = CRYPTO_load_u32_le(data); + data += 4; + R0(D, A, B, C, X(9), 12, 0x8b44f7afL); + X(11) = CRYPTO_load_u32_le(data); + data += 4; + R0(C, D, A, B, X(10), 17, 0xffff5bb1L); + X(12) = CRYPTO_load_u32_le(data); + data += 4; + R0(B, C, D, A, X(11), 22, 0x895cd7beL); + X(13) = CRYPTO_load_u32_le(data); + data += 4; + R0(A, B, C, D, X(12), 7, 0x6b901122L); + X(14) = CRYPTO_load_u32_le(data); + data += 4; + R0(D, A, B, C, X(13), 12, 0xfd987193L); + X(15) = CRYPTO_load_u32_le(data); + data += 4; + R0(C, D, A, B, X(14), 17, 0xa679438eL); + R0(B, C, D, A, X(15), 22, 0x49b40821L); + // Round 1 + R1(A, B, C, D, X(1), 5, 0xf61e2562L); + R1(D, A, B, C, X(6), 9, 0xc040b340L); + R1(C, D, A, B, X(11), 14, 0x265e5a51L); + R1(B, C, D, A, X(0), 20, 0xe9b6c7aaL); + R1(A, B, C, D, X(5), 5, 0xd62f105dL); + R1(D, A, B, C, X(10), 9, 0x02441453L); + R1(C, D, A, B, X(15), 14, 0xd8a1e681L); + R1(B, C, D, A, X(4), 20, 0xe7d3fbc8L); + R1(A, B, C, D, X(9), 5, 0x21e1cde6L); + R1(D, A, B, C, X(14), 9, 0xc33707d6L); + R1(C, D, A, B, X(3), 14, 0xf4d50d87L); + R1(B, C, D, A, X(8), 20, 0x455a14edL); + R1(A, B, C, D, X(13), 5, 0xa9e3e905L); + R1(D, A, B, C, X(2), 9, 0xfcefa3f8L); + R1(C, D, A, B, X(7), 14, 0x676f02d9L); + R1(B, C, D, A, X(12), 20, 0x8d2a4c8aL); + // Round 2 + R2(A, B, C, D, X(5), 4, 0xfffa3942L); + R2(D, A, B, C, X(8), 11, 0x8771f681L); + R2(C, D, A, B, X(11), 16, 0x6d9d6122L); + R2(B, C, D, A, X(14), 23, 0xfde5380cL); + R2(A, B, C, D, X(1), 4, 0xa4beea44L); + R2(D, A, B, C, X(4), 11, 0x4bdecfa9L); + R2(C, D, A, B, X(7), 16, 0xf6bb4b60L); + R2(B, C, D, A, X(10), 23, 0xbebfbc70L); + R2(A, B, C, D, X(13), 4, 0x289b7ec6L); + R2(D, A, B, C, X(0), 11, 0xeaa127faL); + R2(C, D, A, B, X(3), 16, 0xd4ef3085L); + R2(B, C, D, A, X(6), 23, 0x04881d05L); + R2(A, B, C, D, X(9), 4, 0xd9d4d039L); + R2(D, A, B, C, X(12), 11, 0xe6db99e5L); + R2(C, D, A, B, X(15), 16, 0x1fa27cf8L); + R2(B, C, D, A, X(2), 23, 0xc4ac5665L); + // Round 3 + R3(A, B, C, D, X(0), 6, 0xf4292244L); + R3(D, A, B, C, X(7), 10, 0x432aff97L); + R3(C, D, A, B, X(14), 15, 0xab9423a7L); + R3(B, C, D, A, X(5), 21, 0xfc93a039L); + R3(A, B, C, D, X(12), 6, 0x655b59c3L); + R3(D, A, B, C, X(3), 10, 0x8f0ccc92L); + R3(C, D, A, B, X(10), 15, 0xffeff47dL); + R3(B, C, D, A, X(1), 21, 0x85845dd1L); + R3(A, B, C, D, X(8), 6, 0x6fa87e4fL); + R3(D, A, B, C, X(15), 10, 0xfe2ce6e0L); + R3(C, D, A, B, X(6), 15, 0xa3014314L); + R3(B, C, D, A, X(13), 21, 0x4e0811a1L); + R3(A, B, C, D, X(4), 6, 0xf7537e82L); + R3(D, A, B, C, X(11), 10, 0xbd3af235L); + R3(C, D, A, B, X(2), 15, 0x2ad7d2bbL); + R3(B, C, D, A, X(9), 21, 0xeb86d391L); + + A = state[0] += A; + B = state[1] += B; + C = state[2] += C; + D = state[3] += D; + } +} + +#undef X + +#undef F +#undef G +#undef H +#undef I +#undef R0 +#undef R1 +#undef R2 +#undef R3 + +typedef void (*crypto_md32_block_func)(uint32_t* state, + const uint8_t* data, + size_t num_blocks); + +// crypto_md32_update adds |len| bytes from |in| to the digest. |data| must be a +// buffer of length |block_size| with the first |*num| bytes containing a +// partial block. This function combines the partial block with |in| and +// incorporates any complete blocks into the digest state |h|. It then updates +// |data| and |*num| with the new partial block and updates |*Nh| and |*Nl| with +// the data consumed. +inline void crypto_md32_update(crypto_md32_block_func block_func, + uint32_t* h, + uint8_t* data, + size_t block_size, + unsigned* num, + uint32_t* Nh, + uint32_t* Nl, + const uint8_t* in, + size_t len) { + if (len == 0) { + return; + } + + uint32_t l = *Nl + (((uint32_t)len) << 3); + if (l < *Nl) { + // Handle carries. + (*Nh)++; + } + *Nh += (uint32_t)(len >> 29); + *Nl = l; + + size_t n = *num; + if (n != 0) { + if (len >= block_size || len + n >= block_size) { + OPENSSL_memcpy(data + n, in, block_size - n); + block_func(h, data, 1); + n = block_size - n; + in += n; + len -= n; + *num = 0; + // Keep |data| zeroed when unused. + OPENSSL_memset(data, 0, block_size); + } else { + OPENSSL_memcpy(data + n, in, len); + *num += (unsigned)len; + return; + } + } + + n = len / block_size; + if (n > 0) { + block_func(h, in, n); + n *= block_size; + in += n; + len -= n; + } + + if (len != 0) { + *num = (unsigned)len; + OPENSSL_memcpy(data, in, len); + } +} + +// crypto_md32_final incorporates the partial block and trailing length into the +// digest state |h|. The trailing length is encoded in little-endian if +// |is_big_endian| is zero and big-endian otherwise. |data| must be a buffer of +// length |block_size| with the first |*num| bytes containing a partial block. +// |Nh| and |Nl| contain the total number of bits processed. On return, this +// function clears the partial block in |data| and +// |*num|. +// +// This function does not serialize |h| into a final digest. This is the +// responsibility of the caller. +inline void crypto_md32_final(crypto_md32_block_func block_func, + uint32_t* h, + uint8_t* data, + size_t block_size, + unsigned* num, + uint32_t Nh, + uint32_t Nl, + int is_big_endian) { + // |data| always has room for at least one byte. A full block would have + // been consumed. + size_t n = *num; + assert(n < block_size); + data[n] = 0x80; + n++; + + // Fill the block with zeros if there isn't room for a 64-bit length. + if (n > block_size - 8) { + OPENSSL_memset(data + n, 0, block_size - n); + n = 0; + block_func(h, data, 1); + } + OPENSSL_memset(data + n, 0, block_size - 8 - n); + + // Append a 64-bit length to the block and process it. + if (is_big_endian) { + CRYPTO_store_u32_be(data + block_size - 8, Nh); + CRYPTO_store_u32_be(data + block_size - 4, Nl); + } else { + CRYPTO_store_u32_le(data + block_size - 8, Nl); + CRYPTO_store_u32_le(data + block_size - 4, Nh); + } + block_func(h, data, 1); + *num = 0; + OPENSSL_memset(data, 0, block_size); +} + +void MD5_Init(MD5_CTX* md5) { + OPENSSL_memset(md5, 0, sizeof(MD5_CTX)); + md5->h[0] = 0x67452301UL; + md5->h[1] = 0xefcdab89UL; + md5->h[2] = 0x98badcfeUL; + md5->h[3] = 0x10325476UL; +} + +void MD5_Update(MD5_CTX* c, const void* data, size_t len) { + crypto_md32_update(&md5_block_data_order, c->h, c->data, MD5_CBLOCK, &c->num, + &c->Nh, &c->Nl, static_cast(data), len); +} + +void MD5_Final(uint8_t out[MD5_DIGEST_LENGTH], MD5_CTX* c) { + crypto_md32_final(&md5_block_data_order, c->h, c->data, MD5_CBLOCK, &c->num, + c->Nh, c->Nl, /*is_big_endian=*/0); + + CRYPTO_store_u32_le(out, c->h[0]); + CRYPTO_store_u32_le(out + 4, c->h[1]); + CRYPTO_store_u32_le(out + 8, c->h[2]); + CRYPTO_store_u32_le(out + 12, c->h[3]); +} + +} // namespace + +std::array CalculateMd5Digest(absl::string_view s) { + MD5_CTX ctx; + MD5_Init(&ctx); + MD5_Update(&ctx, s.data(), s.length()); + std::array digest; + MD5_Final(digest.data(), &ctx); + return digest; +} + +} // namespace util +} // namespace firestore +} // namespace firebase diff --git a/Firestore/core/src/util/md5_commoncrypto.cc b/Firestore/core/src/util/md5_commoncrypto.cc deleted file mode 100644 index 0e24c5a77e8..00000000000 --- a/Firestore/core/src/util/md5_commoncrypto.cc +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2023 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Firestore/core/src/util/md5.h" - -#include "Firestore/core/src/util/config.h" -#include "Firestore/core/src/util/hard_assert.h" -#include "Firestore/core/src/util/warnings.h" - -#if FIRESTORE_MD5_IMPL_COMMONCRYPTO - -// NOLINTNEXTLINE(build/include) -#include - -namespace firebase { -namespace firestore { -namespace util { - -// Suppress deprecation warnings because due to CommonCrypto's md5 functions. -// The MD5 functions are marked as "deprecated" because they are -// "cryptographically broken"; however, Firestore's use of md5 is _not_ in a -// cryptographic context and, therefore, a non-cryptographically-secure -// algorithm is acceptable. -SUPPRESS_DEPRECATED_DECLARATIONS_BEGIN() -std::array CalculateMd5Digest(absl::string_view s) { - CC_MD5_CTX ctx; - - { - const int md5_init_result = CC_MD5_Init(&ctx); - HARD_ASSERT(md5_init_result == 1, - "CC_MD5_Init() returned %s, but expected 1", md5_init_result); - } - - { - int md5_update_result = CC_MD5_Update(&ctx, s.data(), s.length()); - HARD_ASSERT(md5_update_result == 1, - "CC_MD5_Update() returned %s, but expected 1", - md5_update_result); - } - - { - std::array digest; - int md5_final_result = CC_MD5_Final(digest.data(), &ctx); - HARD_ASSERT(md5_final_result == 1, - "CC_MD5_Final() returned %s but expected 1", md5_final_result); - return digest; - } -} -SUPPRESS_END() - -} // namespace util -} // namespace firestore -} // namespace firebase - -#endif // FIRESTORE_MD5_IMPL_COMMONCRYPTO diff --git a/Firestore/core/src/util/md5_openssl.cc b/Firestore/core/src/util/md5_openssl.cc deleted file mode 100644 index e8eaac1801b..00000000000 --- a/Firestore/core/src/util/md5_openssl.cc +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2023 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Firestore/core/src/util/md5.h" - -#include "Firestore/core/src/util/config.h" -#include "Firestore/core/src/util/hard_assert.h" -#include "Firestore/core/src/util/warnings.h" - -#if FIRESTORE_MD5_IMPL_OPENSSL - -#include "openssl/md5.h" - -namespace firebase { -namespace firestore { -namespace util { - -// Suppress deprecation warnings due to using openssl's md5 functions. -// In the future, we can migrate to the newer-style digest algorithms in -// openssl/evp.h and remove this deprecation suppression. -SUPPRESS_DEPRECATED_DECLARATIONS_BEGIN() -std::array CalculateMd5Digest(absl::string_view s) { - MD5_CTX ctx; - - { - const int md5_init_result = MD5_Init(&ctx); - HARD_ASSERT(md5_init_result == 1, "MD5_Init() returned %s, but expected 1", - md5_init_result); - } - - { - int md5_update_result = MD5_Update(&ctx, s.data(), s.length()); - HARD_ASSERT(md5_update_result == 1, - "MD5_Update() returned %s, but expected 1", md5_update_result); - } - - { - std::array digest; - int md5_final_result = MD5_Final(digest.data(), &ctx); - HARD_ASSERT(md5_final_result == 1, "MD5_Final() returned %s but expected 1", - md5_final_result); - return digest; - } -} - -} // namespace util -} // namespace firestore -} // namespace firebase - -#endif // FIRESTORE_MD5_IMPL_OPENSSL From 2cd675bb6bd688c5ae51e279eff25c97dd882a82 Mon Sep 17 00:00:00 2001 From: Denver Coneybeare Date: Mon, 27 Feb 2023 15:38:36 -0500 Subject: [PATCH 18/19] Swap out implementation with that from chromium. --- Firestore/core/src/util/md5.cc | 583 ++++++++++++++------------------- Firestore/core/src/util/md5.h | 3 +- 2 files changed, 247 insertions(+), 339 deletions(-) diff --git a/Firestore/core/src/util/md5.cc b/Firestore/core/src/util/md5.cc index a1ef27308b9..4e3a2c400bd 100644 --- a/Firestore/core/src/util/md5.cc +++ b/Firestore/core/src/util/md5.cc @@ -16,381 +16,288 @@ #include "Firestore/core/src/util/md5.h" -#include -#include +#include namespace firebase { namespace firestore { namespace util { -// This anonymous namespace contains the md5 implementation copied from -// BoringSSL. +// The contents of this anonymous namespace were copied, and slightly adapted, +// from +// https://source.chromium.org/chromium/chromium/src/+/main:base/hash/md5_nacl.cc;drc=e4622aaeccea84652488d1822c28c78b7115684f namespace { -// MD5_CBLOCK is the block size of MD5. -#define MD5_CBLOCK 64 +// Copyright 2011 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. -// MD5_DIGEST_LENGTH is the length of an MD5 digest. -#define MD5_DIGEST_LENGTH 16 +// The original file was copied from sqlite, and was in the public domain. -struct md5_state_st { - uint32_t h[4]; - uint32_t Nl, Nh; - uint8_t data[MD5_CBLOCK]; - unsigned num; +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + */ + +// The output of an MD5 operation. +struct MD5Digest { + uint8_t a[16]; }; -typedef struct md5_state_st MD5_CTX; -inline void OPENSSL_memset(void* dst, int c, size_t n) { - if (n != 0) { - std::memset(dst, c, n); - } -} +// Used for storing intermediate data during an MD5 computation. Callers +// should not access the data. +typedef char MD5Context[88]; -inline void OPENSSL_memcpy(void* dst, const void* src, size_t n) { - if (n != 0) { - std::memcpy(dst, src, n); - } -} +struct Context { + uint32_t buf[4]; + uint32_t bits[2]; + uint8_t in[64]; +}; -inline uint32_t CRYPTO_rotl_u32(uint32_t value, int shift) { -#if defined(_MSC_VER) - return _rotl(value, shift); -#else - return (value << shift) | (value >> ((-shift) & 31)); -#endif +/* + * Note: this code is harmless on little-endian machines. + */ +void byteReverse(uint8_t* buf, unsigned longs) { + do { + uint32_t temp = + static_cast(static_cast(buf[3]) << 8 | buf[2]) + << 16 | + (static_cast(buf[1]) << 8 | buf[0]); + *reinterpret_cast(buf) = temp; + buf += 4; + } while (--longs); } -inline uint32_t CRYPTO_bswap4(uint32_t x) { - x = (x >> 16) | (x << 16); - x = ((x & 0xff00ff00) >> 8) | ((x & 0x00ff00ff) << 8); - return x; -} +/* The four core functions - F1 is optimized somewhat */ -inline uint32_t CRYPTO_load_u32_le(const void* in) { - uint32_t v; - OPENSSL_memcpy(&v, in, sizeof(v)); - return v; -} +/* #define F1(x, y, z) (x & y | ~x & z) */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) -inline void CRYPTO_store_u32_le(void* out, uint32_t v) { - OPENSSL_memcpy(out, &v, sizeof(v)); -} +/* This is the central step in the MD5 algorithm. */ +#define MD5STEP(f, w, x, y, z, data, s) \ + (w += f(x, y, z) + data, w = w << s | w >> (32 - s), w += x) -inline void CRYPTO_store_u32_be(void* out, uint32_t v) { - v = CRYPTO_bswap4(v); - OPENSSL_memcpy(out, &v, sizeof(v)); +/* + * The core of the MD5 algorithm, this alters an existing MD5 hash to + * reflect the addition of 16 longwords of new data. MD5Update blocks + * the data and converts bytes into longwords for this routine. + */ +void MD5Transform(uint32_t buf[4], const uint32_t in[16]) { + uint32_t a, b, c, d; + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; } -#define F(b, c, d) ((((c) ^ (d)) & (b)) ^ (d)) -#define G(b, c, d) ((((b) ^ (c)) & (d)) ^ (c)) -#define H(b, c, d) ((b) ^ (c) ^ (d)) -#define I(b, c, d) (((~(d)) | (b)) ^ (c)) - -#define R0(a, b, c, d, k, s, t) \ - do { \ - (a) += ((k) + (t) + F((b), (c), (d))); \ - (a) = CRYPTO_rotl_u32(a, s); \ - (a) += (b); \ - } while (0) - -#define R1(a, b, c, d, k, s, t) \ - do { \ - (a) += ((k) + (t) + G((b), (c), (d))); \ - (a) = CRYPTO_rotl_u32(a, s); \ - (a) += (b); \ - } while (0) - -#define R2(a, b, c, d, k, s, t) \ - do { \ - (a) += ((k) + (t) + H((b), (c), (d))); \ - (a) = CRYPTO_rotl_u32(a, s); \ - (a) += (b); \ - } while (0) - -#define R3(a, b, c, d, k, s, t) \ - do { \ - (a) += ((k) + (t) + I((b), (c), (d))); \ - (a) = CRYPTO_rotl_u32(a, s); \ - (a) += (b); \ - } while (0) - -#ifdef X -#undef X -#endif -static void md5_block_data_order(uint32_t* state, - const uint8_t* data, - size_t num) { - uint32_t A, B, C, D; - uint32_t XX0, XX1, XX2, XX3, XX4, XX5, XX6, XX7, XX8, XX9, XX10, XX11, XX12, - XX13, XX14, XX15; -#define X(i) XX##i - - A = state[0]; - B = state[1]; - C = state[2]; - D = state[3]; - - for (; num--;) { - X(0) = CRYPTO_load_u32_le(data); - data += 4; - X(1) = CRYPTO_load_u32_le(data); - data += 4; - // Round 0 - R0(A, B, C, D, X(0), 7, 0xd76aa478L); - X(2) = CRYPTO_load_u32_le(data); - data += 4; - R0(D, A, B, C, X(1), 12, 0xe8c7b756L); - X(3) = CRYPTO_load_u32_le(data); - data += 4; - R0(C, D, A, B, X(2), 17, 0x242070dbL); - X(4) = CRYPTO_load_u32_le(data); - data += 4; - R0(B, C, D, A, X(3), 22, 0xc1bdceeeL); - X(5) = CRYPTO_load_u32_le(data); - data += 4; - R0(A, B, C, D, X(4), 7, 0xf57c0fafL); - X(6) = CRYPTO_load_u32_le(data); - data += 4; - R0(D, A, B, C, X(5), 12, 0x4787c62aL); - X(7) = CRYPTO_load_u32_le(data); - data += 4; - R0(C, D, A, B, X(6), 17, 0xa8304613L); - X(8) = CRYPTO_load_u32_le(data); - data += 4; - R0(B, C, D, A, X(7), 22, 0xfd469501L); - X(9) = CRYPTO_load_u32_le(data); - data += 4; - R0(A, B, C, D, X(8), 7, 0x698098d8L); - X(10) = CRYPTO_load_u32_le(data); - data += 4; - R0(D, A, B, C, X(9), 12, 0x8b44f7afL); - X(11) = CRYPTO_load_u32_le(data); - data += 4; - R0(C, D, A, B, X(10), 17, 0xffff5bb1L); - X(12) = CRYPTO_load_u32_le(data); - data += 4; - R0(B, C, D, A, X(11), 22, 0x895cd7beL); - X(13) = CRYPTO_load_u32_le(data); - data += 4; - R0(A, B, C, D, X(12), 7, 0x6b901122L); - X(14) = CRYPTO_load_u32_le(data); - data += 4; - R0(D, A, B, C, X(13), 12, 0xfd987193L); - X(15) = CRYPTO_load_u32_le(data); - data += 4; - R0(C, D, A, B, X(14), 17, 0xa679438eL); - R0(B, C, D, A, X(15), 22, 0x49b40821L); - // Round 1 - R1(A, B, C, D, X(1), 5, 0xf61e2562L); - R1(D, A, B, C, X(6), 9, 0xc040b340L); - R1(C, D, A, B, X(11), 14, 0x265e5a51L); - R1(B, C, D, A, X(0), 20, 0xe9b6c7aaL); - R1(A, B, C, D, X(5), 5, 0xd62f105dL); - R1(D, A, B, C, X(10), 9, 0x02441453L); - R1(C, D, A, B, X(15), 14, 0xd8a1e681L); - R1(B, C, D, A, X(4), 20, 0xe7d3fbc8L); - R1(A, B, C, D, X(9), 5, 0x21e1cde6L); - R1(D, A, B, C, X(14), 9, 0xc33707d6L); - R1(C, D, A, B, X(3), 14, 0xf4d50d87L); - R1(B, C, D, A, X(8), 20, 0x455a14edL); - R1(A, B, C, D, X(13), 5, 0xa9e3e905L); - R1(D, A, B, C, X(2), 9, 0xfcefa3f8L); - R1(C, D, A, B, X(7), 14, 0x676f02d9L); - R1(B, C, D, A, X(12), 20, 0x8d2a4c8aL); - // Round 2 - R2(A, B, C, D, X(5), 4, 0xfffa3942L); - R2(D, A, B, C, X(8), 11, 0x8771f681L); - R2(C, D, A, B, X(11), 16, 0x6d9d6122L); - R2(B, C, D, A, X(14), 23, 0xfde5380cL); - R2(A, B, C, D, X(1), 4, 0xa4beea44L); - R2(D, A, B, C, X(4), 11, 0x4bdecfa9L); - R2(C, D, A, B, X(7), 16, 0xf6bb4b60L); - R2(B, C, D, A, X(10), 23, 0xbebfbc70L); - R2(A, B, C, D, X(13), 4, 0x289b7ec6L); - R2(D, A, B, C, X(0), 11, 0xeaa127faL); - R2(C, D, A, B, X(3), 16, 0xd4ef3085L); - R2(B, C, D, A, X(6), 23, 0x04881d05L); - R2(A, B, C, D, X(9), 4, 0xd9d4d039L); - R2(D, A, B, C, X(12), 11, 0xe6db99e5L); - R2(C, D, A, B, X(15), 16, 0x1fa27cf8L); - R2(B, C, D, A, X(2), 23, 0xc4ac5665L); - // Round 3 - R3(A, B, C, D, X(0), 6, 0xf4292244L); - R3(D, A, B, C, X(7), 10, 0x432aff97L); - R3(C, D, A, B, X(14), 15, 0xab9423a7L); - R3(B, C, D, A, X(5), 21, 0xfc93a039L); - R3(A, B, C, D, X(12), 6, 0x655b59c3L); - R3(D, A, B, C, X(3), 10, 0x8f0ccc92L); - R3(C, D, A, B, X(10), 15, 0xffeff47dL); - R3(B, C, D, A, X(1), 21, 0x85845dd1L); - R3(A, B, C, D, X(8), 6, 0x6fa87e4fL); - R3(D, A, B, C, X(15), 10, 0xfe2ce6e0L); - R3(C, D, A, B, X(6), 15, 0xa3014314L); - R3(B, C, D, A, X(13), 21, 0x4e0811a1L); - R3(A, B, C, D, X(4), 6, 0xf7537e82L); - R3(D, A, B, C, X(11), 10, 0xbd3af235L); - R3(C, D, A, B, X(2), 15, 0x2ad7d2bbL); - R3(B, C, D, A, X(9), 21, 0xeb86d391L); - - A = state[0] += A; - B = state[1] += B; - C = state[2] += C; - D = state[3] += D; - } +/* + * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious + * initialization constants. + */ +void MD5Init(MD5Context* context) { + struct Context* ctx = reinterpret_cast(context); + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; + ctx->bits[0] = 0; + ctx->bits[1] = 0; } -#undef X - -#undef F -#undef G -#undef H -#undef I -#undef R0 -#undef R1 -#undef R2 -#undef R3 - -typedef void (*crypto_md32_block_func)(uint32_t* state, - const uint8_t* data, - size_t num_blocks); - -// crypto_md32_update adds |len| bytes from |in| to the digest. |data| must be a -// buffer of length |block_size| with the first |*num| bytes containing a -// partial block. This function combines the partial block with |in| and -// incorporates any complete blocks into the digest state |h|. It then updates -// |data| and |*num| with the new partial block and updates |*Nh| and |*Nl| with -// the data consumed. -inline void crypto_md32_update(crypto_md32_block_func block_func, - uint32_t* h, - uint8_t* data, - size_t block_size, - unsigned* num, - uint32_t* Nh, - uint32_t* Nl, - const uint8_t* in, - size_t len) { - if (len == 0) { - return; - } +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +void MD5Update(MD5Context* context, absl::string_view data) { + struct Context* ctx = reinterpret_cast(context); + const uint8_t* buf = reinterpret_cast(data.data()); + size_t len = data.size(); - uint32_t l = *Nl + (((uint32_t)len) << 3); - if (l < *Nl) { - // Handle carries. - (*Nh)++; - } - *Nh += (uint32_t)(len >> 29); - *Nl = l; - - size_t n = *num; - if (n != 0) { - if (len >= block_size || len + n >= block_size) { - OPENSSL_memcpy(data + n, in, block_size - n); - block_func(h, data, 1); - n = block_size - n; - in += n; - len -= n; - *num = 0; - // Keep |data| zeroed when unused. - OPENSSL_memset(data, 0, block_size); - } else { - OPENSSL_memcpy(data + n, in, len); - *num += (unsigned)len; - return; - } - } + /* Update bitcount */ - n = len / block_size; - if (n > 0) { - block_func(h, in, n); - n *= block_size; - in += n; - len -= n; - } + uint32_t t = ctx->bits[0]; + if ((ctx->bits[0] = t + (static_cast(len) << 3)) < t) + ctx->bits[1]++; /* Carry from low to high */ + ctx->bits[1] += static_cast(len >> 29); - if (len != 0) { - *num = (unsigned)len; - OPENSSL_memcpy(data, in, len); - } -} + t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ + + /* Handle any leading odd-sized chunks */ -// crypto_md32_final incorporates the partial block and trailing length into the -// digest state |h|. The trailing length is encoded in little-endian if -// |is_big_endian| is zero and big-endian otherwise. |data| must be a buffer of -// length |block_size| with the first |*num| bytes containing a partial block. -// |Nh| and |Nl| contain the total number of bits processed. On return, this -// function clears the partial block in |data| and -// |*num|. -// -// This function does not serialize |h| into a final digest. This is the -// responsibility of the caller. -inline void crypto_md32_final(crypto_md32_block_func block_func, - uint32_t* h, - uint8_t* data, - size_t block_size, - unsigned* num, - uint32_t Nh, - uint32_t Nl, - int is_big_endian) { - // |data| always has room for at least one byte. A full block would have - // been consumed. - size_t n = *num; - assert(n < block_size); - data[n] = 0x80; - n++; - - // Fill the block with zeros if there isn't room for a 64-bit length. - if (n > block_size - 8) { - OPENSSL_memset(data + n, 0, block_size - n); - n = 0; - block_func(h, data, 1); + if (t) { + uint8_t* p = static_cast(ctx->in + t); + + t = 64 - t; + if (len < t) { + memcpy(p, buf, len); + return; + } + memcpy(p, buf, t); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, reinterpret_cast(ctx->in)); + buf += t; + len -= t; } - OPENSSL_memset(data + n, 0, block_size - 8 - n); - // Append a 64-bit length to the block and process it. - if (is_big_endian) { - CRYPTO_store_u32_be(data + block_size - 8, Nh); - CRYPTO_store_u32_be(data + block_size - 4, Nl); - } else { - CRYPTO_store_u32_le(data + block_size - 8, Nl); - CRYPTO_store_u32_le(data + block_size - 4, Nh); + /* Process data in 64-byte chunks */ + + while (len >= 64) { + memcpy(ctx->in, buf, 64); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, reinterpret_cast(ctx->in)); + buf += 64; + len -= 64; } - block_func(h, data, 1); - *num = 0; - OPENSSL_memset(data, 0, block_size); -} -void MD5_Init(MD5_CTX* md5) { - OPENSSL_memset(md5, 0, sizeof(MD5_CTX)); - md5->h[0] = 0x67452301UL; - md5->h[1] = 0xefcdab89UL; - md5->h[2] = 0x98badcfeUL; - md5->h[3] = 0x10325476UL; -} + /* Handle any remaining bytes of data. */ -void MD5_Update(MD5_CTX* c, const void* data, size_t len) { - crypto_md32_update(&md5_block_data_order, c->h, c->data, MD5_CBLOCK, &c->num, - &c->Nh, &c->Nl, static_cast(data), len); + memcpy(ctx->in, buf, len); } -void MD5_Final(uint8_t out[MD5_DIGEST_LENGTH], MD5_CTX* c) { - crypto_md32_final(&md5_block_data_order, c->h, c->data, MD5_CBLOCK, &c->num, - c->Nh, c->Nl, /*is_big_endian=*/0); - - CRYPTO_store_u32_le(out, c->h[0]); - CRYPTO_store_u32_le(out + 4, c->h[1]); - CRYPTO_store_u32_le(out + 8, c->h[2]); - CRYPTO_store_u32_le(out + 12, c->h[3]); +/* + * Final wrapup - pad to 64-byte boundary with the bit pattern + * 1 0* (64-bit count of bits processed, MSB-first) + */ +void MD5Final(MD5Digest* digest, MD5Context* context) { + struct Context* ctx = reinterpret_cast(context); + unsigned count; + uint8_t* p; + + /* Compute number of bytes mod 64 */ + count = (ctx->bits[0] >> 3) & 0x3F; + + /* Set the first char of padding to 0x80. This is safe since there is + always at least one byte free */ + p = ctx->in + count; + *p++ = 0x80; + + /* Bytes of padding needed to make 64 bytes */ + count = 64 - 1 - count; + + /* Pad out to 56 mod 64 */ + if (count < 8) { + /* Two lots of padding: Pad the first block to 64 bytes */ + memset(p, 0, count); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, reinterpret_cast(ctx->in)); + + /* Now fill the next block with 56 bytes */ + memset(ctx->in, 0, 56); + } else { + /* Pad block to 56 bytes */ + memset(p, 0, count - 8); + } + byteReverse(ctx->in, 14); + + /* Append length in bits and transform */ + memcpy(&ctx->in[14 * sizeof(ctx->bits[0])], &ctx->bits[0], + sizeof(ctx->bits[0])); + memcpy(&ctx->in[15 * sizeof(ctx->bits[1])], &ctx->bits[1], + sizeof(ctx->bits[1])); + + MD5Transform(ctx->buf, reinterpret_cast(ctx->in)); + byteReverse(reinterpret_cast(ctx->buf), 4); + memcpy(digest->a, ctx->buf, 16); + memset(ctx, 0, sizeof(*ctx)); /* In case it's sensitive */ } } // namespace -std::array CalculateMd5Digest(absl::string_view s) { - MD5_CTX ctx; - MD5_Init(&ctx); - MD5_Update(&ctx, s.data(), s.length()); - std::array digest; - MD5_Final(digest.data(), &ctx); +std::array CalculateMd5Digest(absl::string_view s) { + MD5Context ctx; + MD5Init(&ctx); + MD5Update(&ctx, s); + std::array digest; + static_assert(sizeof(uint8_t[16]) == sizeof(MD5Digest), ""); + MD5Final(reinterpret_cast(digest.data()), &ctx); return digest; } diff --git a/Firestore/core/src/util/md5.h b/Firestore/core/src/util/md5.h index a00de05facd..d2738245750 100644 --- a/Firestore/core/src/util/md5.h +++ b/Firestore/core/src/util/md5.h @@ -18,6 +18,7 @@ #define FIRESTORE_CORE_SRC_UTIL_MD5_H_ #include +#include #include "absl/strings/string_view.h" @@ -28,7 +29,7 @@ namespace util { /** * Calculates and returns the md5 digest of the given string. */ -std::array CalculateMd5Digest(absl::string_view); +std::array CalculateMd5Digest(absl::string_view); } // namespace util } // namespace firestore From 70763a9e151bd1c754ceaaf16e1b2de3f97f1b33 Mon Sep 17 00:00:00 2001 From: Denver Coneybeare Date: Mon, 27 Feb 2023 16:09:14 -0500 Subject: [PATCH 19/19] factor out test helper methods into the new file md5_testing.h/cc --- .../core/test/unit/testutil/md5_testing.cc | 78 +++++++++ .../core/test/unit/testutil/md5_testing.h | 50 ++++++ Firestore/core/test/unit/util/md5_test.cc | 153 +++++------------- 3 files changed, 167 insertions(+), 114 deletions(-) create mode 100644 Firestore/core/test/unit/testutil/md5_testing.cc create mode 100644 Firestore/core/test/unit/testutil/md5_testing.h diff --git a/Firestore/core/test/unit/testutil/md5_testing.cc b/Firestore/core/test/unit/testutil/md5_testing.cc new file mode 100644 index 00000000000..d6b9b7e3713 --- /dev/null +++ b/Firestore/core/test/unit/testutil/md5_testing.cc @@ -0,0 +1,78 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Firestore/core/test/unit/testutil/md5_testing.h" + +#include "Firestore/core/src/util/hard_assert.h" + +namespace firebase { +namespace firestore { +namespace testutil { +namespace md5 { + +std::uint8_t UnsignedCharFromHexDigit(char digit) { + switch (digit) { + case '0': + return 0x0; + case '1': + return 0x1; + case '2': + return 0x2; + case '3': + return 0x3; + case '4': + return 0x4; + case '5': + return 0x5; + case '6': + return 0x6; + case '7': + return 0x7; + case '8': + return 0x8; + case '9': + return 0x9; + case 'a': + return 0xA; + case 'b': + return 0xB; + case 'c': + return 0xC; + case 'd': + return 0xD; + case 'e': + return 0xE; + case 'f': + return 0xF; + } + HARD_FAIL("unrecognized hex digit: %s", std::to_string(digit)); +} + +std::array Uint8ArrayFromHexDigest(const std::string& s) { + HARD_ASSERT(s.length() == 32); + std::array result; + for (int i = 0; i < 16; ++i) { + std::uint8_t c1 = UnsignedCharFromHexDigit(s[i * 2]); + std::uint8_t c2 = UnsignedCharFromHexDigit(s[(i * 2) + 1]); + result[i] = (c1 << 4) | c2; + } + return result; +} + +} // namespace md5 +} // namespace testutil +} // namespace firestore +} // namespace firebase diff --git a/Firestore/core/test/unit/testutil/md5_testing.h b/Firestore/core/test/unit/testutil/md5_testing.h new file mode 100644 index 00000000000..21944f7a023 --- /dev/null +++ b/Firestore/core/test/unit/testutil/md5_testing.h @@ -0,0 +1,50 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FIRESTORE_CORE_TEST_UNIT_TESTUTIL_MD5_TESTING_H_ +#define FIRESTORE_CORE_TEST_UNIT_TESTUTIL_MD5_TESTING_H_ + +#include +#include +#include + +namespace firebase { +namespace firestore { +namespace testutil { +namespace md5 { + +// Gets the unsigned char corresponding to the given hex digit. +// The digit must be one of '0', '1', ... , '9', 'a', 'b', ... , 'f'. +// The lower 4 bits of the returned value will be set and the rest will be 0. +std::uint8_t UnsignedCharFromHexDigit(char digit); + +// Calculates the 16-byte uint8_t array represented by the given hex +// string. The given string must be exactly 32 characters and each character +// must be one that is accepted by the UnsignedCharFromHexDigit() function. +// e.g. "fc3ff98e8c6a0d3087d515c0473f8677". +// The `md5sum` command from GNU coreutils can be used to generate a string to +// specify to this function. +// e.g. +// $ printf 'hello world!' | md5sum - +// fc3ff98e8c6a0d3087d515c0473f8677 - +std::array Uint8ArrayFromHexDigest(const std::string&); + +} // namespace md5 +} // namespace testutil +} // namespace firestore +} // namespace firebase + +#endif // FIRESTORE_CORE_TEST_UNIT_TESTUTIL_MD5_TESTING_H_ diff --git a/Firestore/core/test/unit/util/md5_test.cc b/Firestore/core/test/unit/util/md5_test.cc index 31cfe074cc7..7c2545a44e2 100644 --- a/Firestore/core/test/unit/util/md5_test.cc +++ b/Firestore/core/test/unit/util/md5_test.cc @@ -15,156 +15,81 @@ */ #include -#include -#include "Firestore/core/src/util/hard_assert.h" #include "Firestore/core/src/util/md5.h" +#include "Firestore/core/test/unit/testutil/md5_testing.h" #include "gtest/gtest.h" +using firebase::firestore::testutil::md5::Uint8ArrayFromHexDigest; using firebase::firestore::util::CalculateMd5Digest; namespace { -// Gets the unsigned char corresponding to the given hex digit. -// The digit must be one of '0', '1', ... , '9', 'a', 'b', ... , 'f'. -// The lower 4 bits of the returned value will be set and the rest will be 0. -unsigned char UnsignedCharFromHexDigit(char digit); - -// Calculates the 16-byte unsigned char array represented by the given hex -// string. The given string must be exactly 32 characters and each character -// must be one that is accepted by the UnsignedCharFromHexDigit() function. -// e.g. "fc3ff98e8c6a0d3087d515c0473f8677". -// The `md5sum` command from GNU coreutils can be used to generate a string to -// specify to this function. -// e.g. -// $ printf 'hello world!' | md5sum - -// fc3ff98e8c6a0d3087d515c0473f8677 - -std::array UnsignedCharArrayFromHexDigest( - const std::string&); - -// Returns the md5 digest for the given string. -// This function does not "calculate" the digest, but rather has a hardcoded set -// of pre-calculated digests that it returns. -// It is an error if this function does not have a pre-calculated digest for the -// given string. -std::array GetPreComputedMd5Digest(const std::string&); - -// Generates and returns a string with all possible characters with the given -// length. The given length must be at least 256. -std::string GetStringWithAllPossibleCharacters(int length); - TEST(CalculateMd5DigestTest, ShouldReturnMd5DigestOfEmptyString) { - EXPECT_EQ(CalculateMd5Digest(""), GetPreComputedMd5Digest("")); + EXPECT_EQ(CalculateMd5Digest(""), + Uint8ArrayFromHexDigest("d41d8cd98f00b204e9800998ecf8427e")); } TEST(CalculateMd5DigestTest, ShouldReturnMd5DigestOfA) { - EXPECT_EQ(CalculateMd5Digest("a"), GetPreComputedMd5Digest("a")); + EXPECT_EQ(CalculateMd5Digest("a"), + Uint8ArrayFromHexDigest("0cc175b9c0f1b6a831c399e269772661")); } TEST(CalculateMd5DigestTest, ShouldReturnMd5DigestOfABC) { - EXPECT_EQ(CalculateMd5Digest("abc"), GetPreComputedMd5Digest("abc")); + EXPECT_EQ(CalculateMd5Digest("abc"), + Uint8ArrayFromHexDigest("900150983cd24fb0d6963f7d28e17f72")); } TEST(CalculateMd5DigestTest, ShouldReturnMd5DigestOfHelloWorld) { EXPECT_EQ(CalculateMd5Digest("hello world!"), - GetPreComputedMd5Digest("hello world!")); + Uint8ArrayFromHexDigest("fc3ff98e8c6a0d3087d515c0473f8677")); } -TEST(CalculateMd5DigestTest, ShouldReturnMd5DigestOfTheQuickBrownFox) { - EXPECT_EQ( - CalculateMd5Digest("the quick brown fox jumps over the lazy dog"), - GetPreComputedMd5Digest("the quick brown fox jumps over the lazy dog")); +TEST(CalculateMd5DigestTest, ShouldReturnMd5DigestOfMessageDigest) { + EXPECT_EQ(CalculateMd5Digest("message digest"), + Uint8ArrayFromHexDigest("f96b697d7cb7938d525a2f31aaf161d0")); } -TEST(CalculateMd5DigestTest, ShouldReturnMd5DigestOfShortStringWithAllChars) { - const std::string s = GetStringWithAllPossibleCharacters(512); - EXPECT_EQ(CalculateMd5Digest(s), GetPreComputedMd5Digest(s)); +TEST(CalculateMd5DigestTest, ShouldReturnMd5DigestOfLowercaseAlphabet) { + EXPECT_EQ(CalculateMd5Digest("abcdefghijklmnopqrstuvwxyz"), + Uint8ArrayFromHexDigest("c3fcd3d76192e4007dfb496cca67e13b")); } -TEST(CalculateMd5DigestTest, ShouldReturnMd5DigestOfLongStringWithAllChars) { - const std::string s = GetStringWithAllPossibleCharacters(8192); - EXPECT_EQ(CalculateMd5Digest(s), GetPreComputedMd5Digest(s)); +TEST(CalculateMd5DigestTest, ShouldReturnMd5DigestOfAlphabetLowerUpperNums) { + EXPECT_EQ( + CalculateMd5Digest( + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"), + Uint8ArrayFromHexDigest("d174ab98d277d9f5a5611c2c9f419d9f")); } -unsigned char UnsignedCharFromHexDigit(char digit) { - switch (digit) { - case '0': - return 0x0; - case '1': - return 0x1; - case '2': - return 0x2; - case '3': - return 0x3; - case '4': - return 0x4; - case '5': - return 0x5; - case '6': - return 0x6; - case '7': - return 0x7; - case '8': - return 0x8; - case '9': - return 0x9; - case 'a': - return 0xA; - case 'b': - return 0xB; - case 'c': - return 0xC; - case 'd': - return 0xD; - case 'e': - return 0xE; - case 'f': - return 0xF; - } - HARD_FAIL("unrecognized hex digit: %s", std::to_string(digit)); +TEST(CalculateMd5DigestTest, ShouldReturnMd5DigestOfDigits) { + EXPECT_EQ(CalculateMd5Digest("12345678901234567890123456789012345678901234567" + "890123456789012345678901234567890"), + Uint8ArrayFromHexDigest("57edf4a22be3c955ac49da2e2107b67a")); } -std::array UnsignedCharArrayFromHexDigest( - const std::string& s) { - HARD_ASSERT(s.length() == 32); - std::array result; - for (int i = 0; i < 16; ++i) { - unsigned char c1 = UnsignedCharFromHexDigit(s[i * 2]); - unsigned char c2 = UnsignedCharFromHexDigit(s[(i * 2) + 1]); - result[i] = (c1 << 4) | c2; - } - return result; +TEST(CalculateMd5DigestTest, ShouldReturnMd5DigestOfTheQuickBrownFox) { + EXPECT_EQ(CalculateMd5Digest("the quick brown fox jumps over the lazy dog"), + Uint8ArrayFromHexDigest("77add1d5f41223d5582fca736a5cb335")); } -std::array GetPreComputedMd5Digest(const std::string& s) { - if (s == "") { - return UnsignedCharArrayFromHexDigest("d41d8cd98f00b204e9800998ecf8427e"); - } else if (s == "hello world!") { - return UnsignedCharArrayFromHexDigest("fc3ff98e8c6a0d3087d515c0473f8677"); - } else if (s == "a") { - return UnsignedCharArrayFromHexDigest("0cc175b9c0f1b6a831c399e269772661"); - } else if (s == "abc") { - return UnsignedCharArrayFromHexDigest("900150983cd24fb0d6963f7d28e17f72"); - } else if (s == "the quick brown fox jumps over the lazy dog") { - return UnsignedCharArrayFromHexDigest("77add1d5f41223d5582fca736a5cb335"); - } else if (s == GetStringWithAllPossibleCharacters(512)) { - return UnsignedCharArrayFromHexDigest("f5c8e3c31c044bae0e65569560b54332"); - } else if (s == GetStringWithAllPossibleCharacters(8192)) { - return UnsignedCharArrayFromHexDigest("6556112372898c69e1de0bf689d8db26"); - } else { - HARD_FAIL("no precomputed digest for string: %s", s); - return {}; +TEST(CalculateMd5DigestTest, ShouldReturnMd5DigestOfShortStringWithAllChars) { + std::string s; + for (int i = 0; i < 512; ++i) { + s += static_cast(i); } + EXPECT_EQ(CalculateMd5Digest(s), + Uint8ArrayFromHexDigest("f5c8e3c31c044bae0e65569560b54332")); } -std::string GetStringWithAllPossibleCharacters(int length) { - HARD_ASSERT(length >= 256); - std::string result; - for (int i = 0; i < length; ++i) { - result += static_cast(i); +TEST(CalculateMd5DigestTest, ShouldReturnMd5DigestOfLongStringWithAllChars) { + std::string s; + for (int i = 0; i < 8192; ++i) { + s += static_cast(i); } - return result; + EXPECT_EQ(CalculateMd5Digest(s), + Uint8ArrayFromHexDigest("6556112372898c69e1de0bf689d8db26")); } } // namespace