| // Copyright 2025 The BoringSSL Authors | |
| // | |
| // 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 | |
| // | |
| // https://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 <gtest/gtest.h> | |
| #include <openssl/rand.h> | |
| #include "bedrock_unverified_platform.c.inc" | |
| namespace ref { | |
| #include "bedrock_unverified_bareminimum.c.inc" // currently unused but planned | |
| #include "bedrock_polyfill_platform.c.inc" | |
| } | |
| namespace { | |
| #define SECRET_EQ_W(a, b) EXPECT_EQ(br_declassify(a), br_declassify(b)) | |
| TEST(BedrockTest, ArithmeticAndBroadcast) { | |
| std::vector<br_word_t> test_values = { | |
| 0, | |
| 1, | |
| 1024, | |
| 12345, | |
| 32000, | |
| 0xffffffff / 2 - 1, | |
| 0xffffffff / 2, | |
| 0xffffffff / 2 + 1, | |
| 0xffffffff - 1, | |
| 0xffffffff, | |
| std::numeric_limits<br_word_t>::max() / 2 - 1, | |
| std::numeric_limits<br_word_t>::max() / 2, | |
| std::numeric_limits<br_word_t>::max() / 2 + 1, | |
| std::numeric_limits<br_word_t>::max() - 1, | |
| std::numeric_limits<br_word_t>::max(), | |
| }; | |
| for (int i = 0; i < 100; i++) { | |
| br_word_t word; | |
| RAND_bytes(reinterpret_cast<uint8_t *>(&word), sizeof(word)); | |
| test_values.push_back(word); | |
| } | |
| auto broadcast = [](bool b) { return b ? 0u-(br_word_t)1 : (br_word_t)0; }; | |
| for (br_word_t a : test_values) { | |
| SCOPED_TRACE(a); | |
| EXPECT_EQ(a, br_value_barrier(a)); | |
| EXPECT_EQ(a, ref::br_value_barrier(a)); | |
| EXPECT_EQ(a, br_declassify(a)); | |
| EXPECT_EQ(a, ref::br_declassify(a)); | |
| EXPECT_EQ(broadcast(a != 0), br_broadcast_nonzero(a)); | |
| EXPECT_EQ(broadcast(a != 0), ref::br_broadcast_nonzero(a)); | |
| EXPECT_EQ(broadcast((br_signed_t)a < 0), br_broadcast_negative(a)); | |
| EXPECT_EQ(broadcast((br_signed_t)a < 0), ref::br_broadcast_negative(a)); | |
| CONSTTIME_SECRET(&a, sizeof(a)); | |
| br_word_t t = broadcast(true); | |
| SECRET_EQ_W(br_broadcast_negative(a) | br_broadcast_negative(~a), t); | |
| SECRET_EQ_W(br_broadcast_nonzero(a) | br_broadcast_nonzero(~a), t); | |
| for (br_word_t b : test_values) { | |
| SCOPED_TRACE(b); | |
| CONSTTIME_SECRET(&b, sizeof(b)); | |
| SECRET_EQ_W(a, br_cmov(broadcast(true), a, b)); | |
| SECRET_EQ_W(b, br_cmov(broadcast(false), a, b)); | |
| br_word_t lo = 0, lr = 0; | |
| SECRET_EQ_W(br_full_mul(a, b, &lo), ref::br_full_mul(a, b, &lr)); | |
| SECRET_EQ_W(lo, lr); | |
| SECRET_EQ_W(lo, a * b); | |
| for (br_word_t c : {0, 1}) { | |
| SCOPED_TRACE(c); | |
| CONSTTIME_SECRET(&c, sizeof(c)); | |
| SECRET_EQ_W(br_declassify(a) ? b : c, br_cmov(a, b, c)); | |
| SECRET_EQ_W(br_declassify(a) ? c : b, br_cmov(a, c, b)); | |
| SECRET_EQ_W(br_full_add(a, b, c, &lo), ref::br_full_add(a, b, c, &lr)); | |
| SECRET_EQ_W(lo, lr); | |
| SECRET_EQ_W(lo, a + b + c); | |
| SECRET_EQ_W(br_full_sub(a, b, c, &lo), ref::br_full_sub(a, b, c, &lr)); | |
| SECRET_EQ_W(lo, lr); | |
| SECRET_EQ_W(lo, a - b - c); | |
| } | |
| } | |
| } | |
| } | |
| #undef SECRET_EQ_W | |
| } // namespace |