forked from etotheipi/BitcoinArmory
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDetSign.cpp
More file actions
202 lines (177 loc) · 8.09 KB
/
DetSign.cpp
File metadata and controls
202 lines (177 loc) · 8.09 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
// #############################################################################
// # #
// # Copyright (C) 2011-2014, Armory Technologies, Inc. #
// # Distributed under the GNU Affero General Public License (AGPL v3) #
// # See LICENSE or http://www.gnu.org/licenses/agpl.html #
// # #
// #############################################################################
// Implementation of RFC 6979 for Crypto++. ECDSA_DetSign<ECP, SHA256>::Signer
// is an example of a signer that uses RFC 6979 to determine the k-value.
#include "pch.h"
#ifndef CRYPTOPP_IMPORTS
#include "DetSign.h"
#include "integer.h"
#include "hmac.h"
#include "pubkey.h"
#include "sha.h"
NAMESPACE_BEGIN(CryptoPP)
// Function that takes a big-endian block of data and creates a Crypto++ Integer
// from the data. The Integer will be no larger in size than the ECDSA curve's
// order. (See Sect. 2.3.2 of RFC 6979.)
// INPUT: Input data (const SecByteBlock&)
// Number of bits in the ECDSA curve's order (const unsigned int&)
// OUTPUT: N/A
// RETURN: An Integer created from the input data (Integer)
Integer bits2int(const SecByteBlock& inData, const unsigned int& numOrderBits)
{
Integer newInt(inData, inData.size());
// If the Integer's larger than the curve order, we must right shift the Int
// by enough bits to make it the same size as the curve order. Count all
// input bits, as we can't tell the input's boundaries beforehand.
if((newInt.ByteCount() * 8) > numOrderBits)
{
newInt >>= ((newInt.ByteCount() * 8) - numOrderBits);
}
return newInt;
}
// Function that takes a Crypto++ Integer and creates a big-endian data block
// with the same number of bytes as the ECDSA curve's order. (See Sect. 2.3.3 of
// RFC 6979.) Note that the code doesn't care if the input data has a larger
// value than the order. If such a state is important, the input must be
// checked/fixed before calling this function.
// INPUT: Input Integer (const Integer&)
// Number of bytes in the ECDSA curve's order (const unsigned int&)
// OUTPUT: N/A
// RETURN: A SecByteBlock, as long as the curve order, created from the input
// data (Integer)
SecByteBlock int2octets(const Integer& inInt, const unsigned int& numOrderBytes)
{
// Initial setup.
SecByteBlock retBlock;
SecByteBlock inIntData(inInt.ByteCount());
inInt.Encode(inIntData, inInt.ByteCount());
if(inIntData.size() < numOrderBytes)
{
// If incoming Integer is smaller than the curve, encode the input in
// MSB form and then save the encoded data.
SecByteBlock tmpData1(numOrderBytes);
memset(tmpData1, 0, numOrderBytes); // Make sure there are no stray bits
size_t offset = numOrderBytes - inIntData.size();
memcpy(tmpData1 + offset, inIntData, numOrderBytes - offset);
retBlock = tmpData1;
}
else if(inIntData.size() > numOrderBytes)
{
// If incoming Integer is larger than the curve, encode and save the
// LSBs of the input.
SecByteBlock tmpData2(numOrderBytes);
size_t offset = inIntData.size() - numOrderBytes;
memcpy(tmpData2, inIntData + offset, numOrderBytes);
retBlock = tmpData2;
}
else
{
// If incoming Integer the same size as the curve, just save the input.
retBlock = inIntData;
}
return retBlock;
}
// Function that takes a big-endian block of data and creates another big-endian
// block of data, set to the same bit length as the ECDSA curve's order. (See
// Sect. 2.3.4 of RFC 6979.)
// INPUT: Input data (const SecByteBlock&)
// Number of bits in the ECDSA curve's order (const unsigned int&)
// OUTPUT: N/A
// RETURN: An Integer from the input data modulo the curve order (Integer)
SecByteBlock bits2octets(const SecByteBlock& inData, const Integer& curveOrder,
const size_t& curveOrderNumBits)
{
// Reduce the input to the length of the ECDSA curve's order. Return it or,
// if larger than the order, the modulo value.
Integer newInt1 = bits2int(inData, (const unsigned int)curveOrderNumBits);
Integer newInt2 = newInt1 - curveOrder;
return int2octets(newInt2.IsNegative() ? newInt1 : newInt2,
curveOrder.ByteCount());
}
// Function that goes through the process of creating a k-value to be used when
// performing ECDSA signing of a message. (See Sects. 3.1 & 3.2 of RFC 6979.)
// INPUT: The private key (const Integer&)
// The message to hash (const byte*)
// The size of the message to hash (const size_t&)
// The ECDSA curve order (const Integer&)
// The number of bits in the ECDSA curve order (const size_t&)
// OUTPUT: N/A
// RETURN: The final k-value (Integer)
Integer getDetKVal(const Integer& prvKey, const byte* msgToHash,
const size_t& msgToHashSize, const Integer& curveOrder,
const size_t& curveOrderNumBits)
{
// Initial setup.
// NB: SHA256 is hard-coded. This ought to be changed if at all possible.
size_t numOrderBytes = (curveOrderNumBits + 7) / 8; // 32 for secp256k1
SecByteBlock hmacKey(32); // SHA-256
memset(hmacKey, 0, 32); // This is the initial key.
HMAC<SHA256> dummyHMAC(hmacKey, hmacKey.size());
const unsigned int hmacBits = dummyHMAC.DigestSize() * 8; // 256 for HMAC-SHA256
SecByteBlock inputHash(dummyHMAC.DigestSize());
SecByteBlock V(dummyHMAC.DigestSize());
SecByteBlock prvKeyBlock = int2octets(prvKey, (const unsigned int)numOrderBytes);
SecByteBlock singleByte(1);
memset(V, '\x01', dummyHMAC.DigestSize());
memset(singleByte, 0, 1);
// Hash the input.
SHA256 hashFunct;
hashFunct.CalculateDigest(inputHash, msgToHash, msgToHashSize);
SecByteBlock choppedHash = bits2octets(inputHash, curveOrder,
curveOrderNumBits);
// Create the second key. (The first key was already created w/ memset(0).)
HMAC<SHA256> detSignMAC1(hmacKey, hmacKey.size());
SecByteBlock hmacInput1 = V + singleByte + prvKeyBlock + choppedHash;
detSignMAC1.CalculateDigest(hmacKey, hmacInput1, hmacInput1.size());
// Hash the V value.
HMAC<SHA256> detSignMAC2(hmacKey, hmacKey.size());
detSignMAC2.CalculateDigest(V, V, V.size());
// Create the third (and probably final) key.
memset(singleByte, '\x01', 1);
HMAC<SHA256> detSignMAC3(hmacKey, hmacKey.size());
SecByteBlock hmacInput2 = V + singleByte + prvKeyBlock + choppedHash;
detSignMAC3.CalculateDigest(hmacKey, hmacInput2, hmacInput2.size());
// Hash the V value.
HMAC<SHA256> detSignMAC4(hmacKey, hmacKey.size());
detSignMAC4.CalculateDigest(V, V, V.size());
// Loop around and search for the final k-value.
bool finalKValFound = false;
Integer finalLoopVar;
while(!finalKValFound)
{
SecByteBlock loopVarData;
size_t loopVarBytes = 0;
while(loopVarBytes < numOrderBytes) {
HMAC<SHA256> detSignMAC5(hmacKey, hmacKey.size());
detSignMAC5.CalculateDigest(V, V, V.size());
loopVarData += V;
loopVarBytes += hmacKey.size();
}
// Check to see if the final value is valid. If not, we must compute a
// new k-value. (Failure is highly improbable.)
Integer tmpLoopVar = bits2int(loopVarData, (const unsigned int)curveOrderNumBits);
if(tmpLoopVar >= Integer::One() && tmpLoopVar < curveOrder)
{
finalLoopVar = tmpLoopVar;
finalKValFound = true;
}
else
{
// Create a new key and V-value.
memset(singleByte, 0, 1);
SecByteBlock newHMACInput = V + singleByte;
HMAC<SHA256> detSignMAC6(hmacKey, hmacKey.size());
detSignMAC6.CalculateDigest(hmacKey, newHMACInput, newHMACInput.size());
HMAC<SHA256> detSignMAC7(hmacKey, hmacKey.size());
detSignMAC7.CalculateDigest(V, V, V.size());
}
}
return finalLoopVar;
}
NAMESPACE_END
#endif