Skip to content

Commit 0b4d290

Browse files
fjahrsipa
andcommitted
crypto: Add Num3072 implementation
Num3072 is a specialized bignum implementation used in MuHash3072. Co-authored-by: Pieter Wuille <[email protected]>
1 parent 589f958 commit 0b4d290

File tree

2 files changed

+339
-0
lines changed

2 files changed

+339
-0
lines changed

src/crypto/muhash.cpp

Lines changed: 277 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,277 @@
1+
// Copyright (c) 2017-2020 The Bitcoin Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
#include <crypto/muhash.h>
6+
7+
#include <crypto/chacha20.h>
8+
#include <crypto/common.h>
9+
#include <hash.h>
10+
11+
#include <cassert>
12+
#include <cstdio>
13+
#include <limits>
14+
15+
namespace {
16+
17+
using limb_t = Num3072::limb_t;
18+
using double_limb_t = Num3072::double_limb_t;
19+
constexpr int LIMB_SIZE = Num3072::LIMB_SIZE;
20+
constexpr int LIMBS = Num3072::LIMBS;
21+
/** 2^3072 - 1103717, the largest 3072-bit safe prime number, is used as the modulus. */
22+
constexpr limb_t MAX_PRIME_DIFF = 1103717;
23+
24+
/** Extract the lowest limb of [c0,c1,c2] into n, and left shift the number by 1 limb. */
25+
inline void extract3(limb_t& c0, limb_t& c1, limb_t& c2, limb_t& n)
26+
{
27+
n = c0;
28+
c0 = c1;
29+
c1 = c2;
30+
c2 = 0;
31+
}
32+
33+
/** [c0,c1] = a * b */
34+
inline void mul(limb_t& c0, limb_t& c1, const limb_t& a, const limb_t& b)
35+
{
36+
double_limb_t t = (double_limb_t)a * b;
37+
c1 = t >> LIMB_SIZE;
38+
c0 = t;
39+
}
40+
41+
/* [c0,c1,c2] += n * [d0,d1,d2]. c2 is 0 initially */
42+
inline void mulnadd3(limb_t& c0, limb_t& c1, limb_t& c2, limb_t& d0, limb_t& d1, limb_t& d2, const limb_t& n)
43+
{
44+
double_limb_t t = (double_limb_t)d0 * n + c0;
45+
c0 = t;
46+
t >>= LIMB_SIZE;
47+
t += (double_limb_t)d1 * n + c1;
48+
c1 = t;
49+
t >>= LIMB_SIZE;
50+
c2 = t + d2 * n;
51+
}
52+
53+
/* [c0,c1] *= n */
54+
inline void muln2(limb_t& c0, limb_t& c1, const limb_t& n)
55+
{
56+
double_limb_t t = (double_limb_t)c0 * n;
57+
c0 = t;
58+
t >>= LIMB_SIZE;
59+
t += (double_limb_t)c1 * n;
60+
c1 = t;
61+
}
62+
63+
/** [c0,c1,c2] += a * b */
64+
inline void muladd3(limb_t& c0, limb_t& c1, limb_t& c2, const limb_t& a, const limb_t& b)
65+
{
66+
double_limb_t t = (double_limb_t)a * b;
67+
limb_t th = t >> LIMB_SIZE;
68+
limb_t tl = t;
69+
70+
c0 += tl;
71+
th += (c0 < tl) ? 1 : 0;
72+
c1 += th;
73+
c2 += (c1 < th) ? 1 : 0;
74+
}
75+
76+
/** [c0,c1,c2] += 2 * a * b */
77+
inline void muldbladd3(limb_t& c0, limb_t& c1, limb_t& c2, const limb_t& a, const limb_t& b)
78+
{
79+
double_limb_t t = (double_limb_t)a * b;
80+
limb_t th = t >> LIMB_SIZE;
81+
limb_t tl = t;
82+
83+
c0 += tl;
84+
limb_t tt = th + ((c0 < tl) ? 1 : 0);
85+
c1 += tt;
86+
c2 += (c1 < tt) ? 1 : 0;
87+
c0 += tl;
88+
th += (c0 < tl) ? 1 : 0;
89+
c1 += th;
90+
c2 += (c1 < th) ? 1 : 0;
91+
}
92+
93+
/**
94+
* Add limb a to [c0,c1]: [c0,c1] += a. Then extract the lowest
95+
* limb of [c0,c1] into n, and left shift the number by 1 limb.
96+
* */
97+
inline void addnextract2(limb_t& c0, limb_t& c1, const limb_t& a, limb_t& n)
98+
{
99+
limb_t c2 = 0;
100+
101+
// add
102+
c0 += a;
103+
if (c0 < a) {
104+
c1 += 1;
105+
106+
// Handle case when c1 has overflown
107+
if (c1 == 0)
108+
c2 = 1;
109+
}
110+
111+
// extract
112+
n = c0;
113+
c0 = c1;
114+
c1 = c2;
115+
}
116+
117+
/** in_out = in_out^(2^sq) * mul */
118+
inline void square_n_mul(Num3072& in_out, const int sq, const Num3072& mul)
119+
{
120+
for (int j = 0; j < sq; ++j) in_out.Square();
121+
in_out.Multiply(mul);
122+
}
123+
124+
} // namespace
125+
126+
/** Indicates wether d is larger than the modulus. */
127+
bool Num3072::IsOverflow() const
128+
{
129+
if (this->limbs[0] <= std::numeric_limits<limb_t>::max() - MAX_PRIME_DIFF) return false;
130+
for (int i = 1; i < LIMBS; ++i) {
131+
if (this->limbs[i] != std::numeric_limits<limb_t>::max()) return false;
132+
}
133+
return true;
134+
}
135+
136+
void Num3072::FullReduce()
137+
{
138+
limb_t c0 = MAX_PRIME_DIFF;
139+
limb_t c1 = 0;
140+
for (int i = 0; i < LIMBS; ++i) {
141+
addnextract2(c0, c1, this->limbs[i], this->limbs[i]);
142+
}
143+
}
144+
145+
Num3072 Num3072::GetInverse() const
146+
{
147+
// For fast exponentiation a sliding window exponentiation with repunit
148+
// precomputation is utilized. See "Fast Point Decompression for Standard
149+
// Elliptic Curves" (Brumley, Järvinen, 2008).
150+
151+
Num3072 p[12]; // p[i] = a^(2^(2^i)-1)
152+
Num3072 out;
153+
154+
p[0] = *this;
155+
156+
for (int i = 0; i < 11; ++i) {
157+
p[i + 1] = p[i];
158+
for (int j = 0; j < (1 << i); ++j) p[i + 1].Square();
159+
p[i + 1].Multiply(p[i]);
160+
}
161+
162+
out = p[11];
163+
164+
square_n_mul(out, 512, p[9]);
165+
square_n_mul(out, 256, p[8]);
166+
square_n_mul(out, 128, p[7]);
167+
square_n_mul(out, 64, p[6]);
168+
square_n_mul(out, 32, p[5]);
169+
square_n_mul(out, 8, p[3]);
170+
square_n_mul(out, 2, p[1]);
171+
square_n_mul(out, 1, p[0]);
172+
square_n_mul(out, 5, p[2]);
173+
square_n_mul(out, 3, p[0]);
174+
square_n_mul(out, 2, p[0]);
175+
square_n_mul(out, 4, p[0]);
176+
square_n_mul(out, 4, p[1]);
177+
square_n_mul(out, 3, p[0]);
178+
179+
return out;
180+
}
181+
182+
void Num3072::Multiply(const Num3072& a)
183+
{
184+
limb_t c0 = 0, c1 = 0, c2 = 0;
185+
Num3072 tmp;
186+
187+
/* Compute limbs 0..N-2 of this*a into tmp, including one reduction. */
188+
for (int j = 0; j < LIMBS - 1; ++j) {
189+
limb_t d0 = 0, d1 = 0, d2 = 0;
190+
mul(d0, d1, this->limbs[1 + j], a.limbs[LIMBS + j - (1 + j)]);
191+
for (int i = 2 + j; i < LIMBS; ++i) muladd3(d0, d1, d2, this->limbs[i], a.limbs[LIMBS + j - i]);
192+
mulnadd3(c0, c1, c2, d0, d1, d2, MAX_PRIME_DIFF);
193+
for (int i = 0; i < j + 1; ++i) muladd3(c0, c1, c2, this->limbs[i], a.limbs[j - i]);
194+
extract3(c0, c1, c2, tmp.limbs[j]);
195+
}
196+
197+
/* Compute limb N-1 of a*b into tmp. */
198+
assert(c2 == 0);
199+
for (int i = 0; i < LIMBS; ++i) muladd3(c0, c1, c2, this->limbs[i], a.limbs[LIMBS - 1 - i]);
200+
extract3(c0, c1, c2, tmp.limbs[LIMBS - 1]);
201+
202+
/* Perform a second reduction. */
203+
muln2(c0, c1, MAX_PRIME_DIFF);
204+
for (int j = 0; j < LIMBS; ++j) {
205+
addnextract2(c0, c1, tmp.limbs[j], this->limbs[j]);
206+
}
207+
208+
assert(c1 == 0);
209+
assert(c0 == 0 || c0 == 1);
210+
211+
/* Perform up to two more reductions if the internal state has already
212+
* overflown the MAX of Num3072 or if it is larger than the modulus or
213+
* if both are the case.
214+
* */
215+
if (this->IsOverflow()) this->FullReduce();
216+
if (c0) this->FullReduce();
217+
}
218+
219+
void Num3072::Square()
220+
{
221+
limb_t c0 = 0, c1 = 0, c2 = 0;
222+
Num3072 tmp;
223+
224+
/* Compute limbs 0..N-2 of this*this into tmp, including one reduction. */
225+
for (int j = 0; j < LIMBS - 1; ++j) {
226+
limb_t d0 = 0, d1 = 0, d2 = 0;
227+
for (int i = 0; i < (LIMBS - 1 - j) / 2; ++i) muldbladd3(d0, d1, d2, this->limbs[i + j + 1], this->limbs[LIMBS - 1 - i]);
228+
if ((j + 1) & 1) muladd3(d0, d1, d2, this->limbs[(LIMBS - 1 - j) / 2 + j + 1], this->limbs[LIMBS - 1 - (LIMBS - 1 - j) / 2]);
229+
mulnadd3(c0, c1, c2, d0, d1, d2, MAX_PRIME_DIFF);
230+
for (int i = 0; i < (j + 1) / 2; ++i) muldbladd3(c0, c1, c2, this->limbs[i], this->limbs[j - i]);
231+
if ((j + 1) & 1) muladd3(c0, c1, c2, this->limbs[(j + 1) / 2], this->limbs[j - (j + 1) / 2]);
232+
extract3(c0, c1, c2, tmp.limbs[j]);
233+
}
234+
235+
assert(c2 == 0);
236+
for (int i = 0; i < LIMBS / 2; ++i) muldbladd3(c0, c1, c2, this->limbs[i], this->limbs[LIMBS - 1 - i]);
237+
extract3(c0, c1, c2, tmp.limbs[LIMBS - 1]);
238+
239+
/* Perform a second reduction. */
240+
muln2(c0, c1, MAX_PRIME_DIFF);
241+
for (int j = 0; j < LIMBS; ++j) {
242+
addnextract2(c0, c1, tmp.limbs[j], this->limbs[j]);
243+
}
244+
245+
assert(c1 == 0);
246+
assert(c0 == 0 || c0 == 1);
247+
248+
/* Perform up to two more reductions if the internal state has already
249+
* overflown the MAX of Num3072 or if it is larger than the modulus or
250+
* if both are the case.
251+
* */
252+
if (this->IsOverflow()) this->FullReduce();
253+
if (c0) this->FullReduce();
254+
}
255+
256+
void Num3072::SetToOne()
257+
{
258+
this->limbs[0] = 1;
259+
for (int i = 1; i < LIMBS; ++i) this->limbs[i] = 0;
260+
}
261+
262+
void Num3072::Divide(const Num3072& a)
263+
{
264+
if (this->IsOverflow()) this->FullReduce();
265+
266+
Num3072 inv{};
267+
if (a.IsOverflow()) {
268+
Num3072 b = a;
269+
b.FullReduce();
270+
inv = b.GetInverse();
271+
} else {
272+
inv = a.GetInverse();
273+
}
274+
275+
this->Multiply(inv);
276+
if (this->IsOverflow()) this->FullReduce();
277+
}

src/crypto/muhash.h

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// Copyright (c) 2017-2020 The Bitcoin Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
#ifndef BITCOIN_CRYPTO_MUHASH_H
6+
#define BITCOIN_CRYPTO_MUHASH_H
7+
8+
#if defined(HAVE_CONFIG_H)
9+
#include <config/bitcoin-config.h>
10+
#endif
11+
12+
#include <serialize.h>
13+
#include <uint256.h>
14+
15+
#include <stdint.h>
16+
17+
class Num3072
18+
{
19+
private:
20+
void FullReduce();
21+
bool IsOverflow() const;
22+
Num3072 GetInverse() const;
23+
24+
public:
25+
26+
#ifdef HAVE___INT128
27+
typedef unsigned __int128 double_limb_t;
28+
typedef uint64_t limb_t;
29+
static constexpr int LIMBS = 48;
30+
static constexpr int LIMB_SIZE = 64;
31+
#else
32+
typedef uint64_t double_limb_t;
33+
typedef uint32_t limb_t;
34+
static constexpr int LIMBS = 96;
35+
static constexpr int LIMB_SIZE = 32;
36+
#endif
37+
limb_t limbs[LIMBS];
38+
39+
// Sanity check for Num3072 constants
40+
static_assert(LIMB_SIZE * LIMBS == 3072, "Num3072 isn't 3072 bits");
41+
static_assert(sizeof(double_limb_t) == sizeof(limb_t) * 2, "bad size for double_limb_t");
42+
static_assert(sizeof(limb_t) * 8 == LIMB_SIZE, "LIMB_SIZE is incorrect");
43+
44+
// Hard coded values in MuHash3072 constructor and Finalize
45+
static_assert(sizeof(limb_t) == 4 || sizeof(limb_t) == 8, "bad size for limb_t");
46+
47+
void Multiply(const Num3072& a);
48+
void Divide(const Num3072& a);
49+
void SetToOne();
50+
void Square();
51+
52+
Num3072() { this->SetToOne(); };
53+
54+
SERIALIZE_METHODS(Num3072, obj)
55+
{
56+
for (auto& limb : obj.limbs) {
57+
READWRITE(limb);
58+
}
59+
}
60+
};
61+
62+
#endif // BITCOIN_CRYPTO_MUHASH_H

0 commit comments

Comments
 (0)