1 /*
2 * Copyright 2019 Google LLC.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * https://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 // Implements various modular exponentiation methods to be used for modular
17 // exponentiation of fixed bases.
18 //
19 // A note on sophisticated methods: Although there are more efficient methods
20 // besides what is implemented here, the storage overhead and also limitation
21 // of BigNum representation in C++ might not make them quite as efficient as
22 // they are claimed to be. One such example is Lim-Lee method, it is twice as
23 // fast as the simple modular exponentiation in Python, the C++ implementation
24 // is actually slower on all possible parameters due to the overhead of
25 // transposing the two dimensional bit representation of the exponent.
26
27 #include "private_join_and_compute/crypto/fixed_base_exp.h"
28
29 #include <cstdint>
30 #include <memory>
31 #include <string>
32 #include <vector>
33
34 #include "absl/flags/flag.h"
35 #include "private_join_and_compute/crypto/big_num.h"
36 #include "private_join_and_compute/crypto/context.h"
37 #include "private_join_and_compute/crypto/mont_mul.h"
38 #include "private_join_and_compute/util/status.inc"
39
40 ABSL_FLAG(bool, two_k_ary_exp, false,
41 "Whether to use 2^k-ary fixed based exponentiation.");
42
43 namespace private_join_and_compute {
44
45 namespace internal {
46
47 class FixedBaseExpImplBase {
48 public:
FixedBaseExpImplBase(const BigNum & fixed_base,const BigNum & modulus)49 FixedBaseExpImplBase(const BigNum& fixed_base, const BigNum& modulus)
50 : fixed_base_(fixed_base), modulus_(modulus) {}
51
52 // FixedBaseExpImplBase is neither copyable nor movable.
53 FixedBaseExpImplBase(const FixedBaseExpImplBase&) = delete;
54 FixedBaseExpImplBase& operator=(const FixedBaseExpImplBase&) = delete;
55
56 virtual ~FixedBaseExpImplBase() = default;
57
58 virtual BigNum ModExp(const BigNum& exp) const = 0;
59
60 // Most of the fixed base exponentiators uses precomputed tables for faster
61 // exponentiation so they need to know the fixed base and the modulus during
62 // the object construction.
GetFixedBase() const63 const BigNum& GetFixedBase() const { return fixed_base_; }
GetModulus() const64 const BigNum& GetModulus() const { return modulus_; }
65
66 private:
67 BigNum fixed_base_;
68 BigNum modulus_;
69 };
70
71 class SimpleBaseExpImpl : public FixedBaseExpImplBase {
72 public:
SimpleBaseExpImpl(const BigNum & fixed_base,const BigNum & modulus)73 SimpleBaseExpImpl(const BigNum& fixed_base, const BigNum& modulus)
74 : FixedBaseExpImplBase(fixed_base, modulus) {}
75
ModExp(const BigNum & exp) const76 BigNum ModExp(const BigNum& exp) const final {
77 return GetFixedBase().ModExp(exp, GetModulus());
78 }
79 };
80
81 // Uses the 2^k-ary technique proposed in
82 // Brauer, Alfred. "On addition chains." Bulletin of the American Mathematical
83 // Society 45.10 (1939): 736-739.
84 //
85 // This modular exponentiation is in average 20% faster than SimpleBaseExpImpl.
86 class TwoKAryFixedBaseExpImpl : public FixedBaseExpImplBase {
87 public:
TwoKAryFixedBaseExpImpl(Context * ctx,const BigNum & fixed_base,const BigNum & modulus)88 TwoKAryFixedBaseExpImpl(Context* ctx, const BigNum& fixed_base,
89 const BigNum& modulus)
90 : FixedBaseExpImplBase(fixed_base, modulus),
91 ctx_(ctx),
92 mont_ctx_(new MontContext(ctx, modulus)),
93 cache_() {
94 cache_.push_back(mont_ctx_->CreateMontBigNum(ctx_->CreateBigNum(1)));
95 MontBigNum g = mont_ctx_->CreateMontBigNum(GetFixedBase());
96 cache_.push_back(g);
97 int16_t max_exp = 256;
98 for (int i = 0; i < max_exp; ++i) {
99 cache_.push_back(cache_.back() * g);
100 }
101 }
102
103 // Returns the base^exp mod modulus
104 // Implements the 2^k-ary method, a generalization of the "square and
105 // multiply" exponentiation method. Since chars are iterated in the byte
106 // string of exp, the most straight k to use is 8. Other k values can also be
107 // used but this would complicate the exp bits iteration which adds a
108 // substantial overhead making the exponentiation slower than using
109 // SimpleBaseExpImpl. For instance, reading two bytes at a time and converting
110 // it to a short by shifting and adding is not faster than using a single
111 // byte.
ModExp(const BigNum & exp) const112 BigNum ModExp(const BigNum& exp) const final {
113 MontBigNum z = cache_[0]; // Copying 1 is faster than creating it.
114 std::string values = exp.ToBytes();
115 for (auto it = values.cbegin(); it != values.cend(); ++it) {
116 for (int j = 0; j < 8; ++j) {
117 z *= z;
118 }
119 z *= cache_[static_cast<uint8_t>(*it)];
120 }
121 return z.ToBigNum();
122 }
123
124 private:
125 Context* ctx_;
126 std::unique_ptr<MontContext> mont_ctx_;
127 std::vector<MontBigNum> cache_;
128 };
129
130 } // namespace internal
131
FixedBaseExp(internal::FixedBaseExpImplBase * impl)132 FixedBaseExp::FixedBaseExp(internal::FixedBaseExpImplBase* impl)
133 : impl_(std::unique_ptr<internal::FixedBaseExpImplBase>(impl)) {}
134
135 FixedBaseExp::~FixedBaseExp() = default;
136
ModExp(const BigNum & exp) const137 StatusOr<BigNum> FixedBaseExp::ModExp(const BigNum& exp) const {
138 if (!exp.IsNonNegative()) {
139 return InvalidArgumentError(
140 "FixedBaseExp::ModExp : Negative exponents not supported.");
141 }
142 return impl_->ModExp(exp);
143 }
144
GetFixedBaseExp(Context * ctx,const BigNum & fixed_base,const BigNum & modulus)145 std::unique_ptr<FixedBaseExp> FixedBaseExp::GetFixedBaseExp(
146 Context* ctx, const BigNum& fixed_base, const BigNum& modulus) {
147 if (absl::GetFlag(FLAGS_two_k_ary_exp)) {
148 return std::unique_ptr<FixedBaseExp>(new FixedBaseExp(
149 new internal::TwoKAryFixedBaseExpImpl(ctx, fixed_base, modulus)));
150 } else {
151 return std::unique_ptr<FixedBaseExp>(
152 new FixedBaseExp(new internal::SimpleBaseExpImpl(fixed_base, modulus)));
153 }
154 }
155
156 } // namespace private_join_and_compute
157