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