Skip to content

Commit 2daa8f8

Browse files
xdustinfaceUdjinM6
andauthored
src|test: Add ExtendedPublicKey, ExtendedPrivateKey and ChainCode (dashpay#20)
* src: Add ExtendedPublicKey, ExtendedPrivateKey and ChainCode * test: Add "Legacy HD" keys tests * Convert seeds to Bytes Co-authored-by: UdjinM6 <[email protected]>
1 parent a767dba commit 2daa8f8

9 files changed

+800
-0
lines changed

src/CMakeLists.txt

+3
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,10 @@ add_library(bls-dash ${CMAKE_CURRENT_SOURCE_DIR}/privatekey.cpp)
3939
add_library(blstmp ${HEADERS}
4040
${CMAKE_CURRENT_SOURCE_DIR}/privatekey.cpp
4141
${CMAKE_CURRENT_SOURCE_DIR}/bls.cpp
42+
${CMAKE_CURRENT_SOURCE_DIR}/chaincode.cpp
4243
${CMAKE_CURRENT_SOURCE_DIR}/elements.cpp
44+
${CMAKE_CURRENT_SOURCE_DIR}/extendedprivatekey.cpp
45+
${CMAKE_CURRENT_SOURCE_DIR}/extendedpublickey.cpp
4346
${CMAKE_CURRENT_SOURCE_DIR}/legacy.cpp
4447
${CMAKE_CURRENT_SOURCE_DIR}/schemes.cpp
4548
${CMAKE_CURRENT_SOURCE_DIR}/threshold.cpp

src/bls.hpp

+3
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,10 @@
1818
#include "privatekey.hpp"
1919
#include "util.hpp"
2020
#include "schemes.hpp"
21+
#include "chaincode.hpp"
2122
#include "elements.hpp"
23+
#include "extendedprivatekey.hpp"
24+
#include "extendedpublickey.hpp"
2225
#include "hkdf.hpp"
2326
#include "hdkeys.hpp"
2427
#include "threshold.hpp"

src/chaincode.cpp

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// Copyright 2018 Chia Network Inc
2+
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+
// http://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+
#include "bls.hpp"
16+
17+
namespace bls {
18+
19+
ChainCode ChainCode::FromBytes(const Bytes& bytes) {
20+
if (bytes.size() != ChainCode::SIZE) {
21+
throw std::invalid_argument("ChainCode::FromBytes: Invalid size");
22+
}
23+
ChainCode c = ChainCode();
24+
bn_new(c.chainCode);
25+
bn_read_bin(c.chainCode, bytes.begin(), ChainCode::SIZE);
26+
return c;
27+
}
28+
29+
ChainCode::ChainCode(const ChainCode &cc) {
30+
uint8_t bytes[ChainCode::SIZE];
31+
cc.Serialize(bytes);
32+
bn_new(chainCode);
33+
bn_read_bin(chainCode, bytes, ChainCode::SIZE);
34+
}
35+
36+
// Comparator implementation.
37+
bool operator==(ChainCode const &a, ChainCode const &b) {
38+
return bn_cmp(a.chainCode, b.chainCode) == RLC_EQ;
39+
}
40+
41+
bool operator!=(ChainCode const &a, ChainCode const &b) {
42+
return !(a == b);
43+
}
44+
45+
std::ostream &operator<<(std::ostream &os, ChainCode const &s) {
46+
uint8_t buffer[ChainCode::SIZE];
47+
s.Serialize(buffer);
48+
return os << Util::HexStr(buffer, ChainCode::SIZE);
49+
}
50+
51+
void ChainCode::Serialize(uint8_t *buffer) const {
52+
bn_write_bin(buffer, ChainCode::SIZE, chainCode);
53+
}
54+
55+
std::vector<uint8_t> ChainCode::Serialize() const {
56+
std::vector<uint8_t> data(SIZE);
57+
Serialize(data.data());
58+
return data;
59+
}
60+
} // end namespace bls

src/chaincode.hpp

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// Copyright 2018 Chia Network Inc
2+
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+
// http://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+
#ifndef SRC_CHAINCODE_HPP_
16+
#define SRC_CHAINCODE_HPP_
17+
18+
#include <iostream>
19+
#include <vector>
20+
21+
#include "relic_conf.h"
22+
23+
#if defined GMP && ARITH == GMP
24+
#include <gmp.h>
25+
#endif
26+
27+
28+
#include "relic.h"
29+
#include "relic_test.h"
30+
31+
#include "util.hpp"
32+
33+
namespace bls {
34+
class ChainCode {
35+
public:
36+
static const size_t SIZE = 32;
37+
38+
static ChainCode FromBytes(const Bytes& bytes);
39+
40+
ChainCode(const ChainCode &cc);
41+
42+
// Comparator implementation.
43+
friend bool operator==(ChainCode const &a, ChainCode const &b);
44+
friend bool operator!=(ChainCode const &a, ChainCode const &b);
45+
friend std::ostream &operator<<(std::ostream &os, ChainCode const &s);
46+
47+
void Serialize(uint8_t *buffer) const;
48+
std::vector<uint8_t> Serialize() const;
49+
50+
// Prevent direct construction, use static constructor
51+
ChainCode() {}
52+
private:
53+
54+
bn_t chainCode;
55+
};
56+
} // end namespace bls
57+
58+
#endif // SRC_CHAINCODE_HPP_
59+

src/extendedprivatekey.cpp

+200
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
// Copyright 2018 Chia Network Inc
2+
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+
// http://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+
#include <cstring>
16+
#include "bls.hpp"
17+
18+
namespace bls {
19+
20+
ExtendedPrivateKey ExtendedPrivateKey::FromSeed(const Bytes& bytes) {
21+
// "BLS HD seed" in ascii
22+
const uint8_t prefix[] = {66, 76, 83, 32, 72, 68, 32, 115, 101, 101, 100};
23+
24+
uint8_t* hashInput = Util::SecAlloc<uint8_t>(bytes.size() + 1);
25+
std::memcpy(hashInput, bytes.begin(), bytes.size());
26+
27+
// 32 bytes for secret key, and 32 bytes for chaincode
28+
uint8_t* ILeft = Util::SecAlloc<uint8_t>(
29+
PrivateKey::PRIVATE_KEY_SIZE);
30+
uint8_t IRight[ChainCode::SIZE];
31+
32+
// Hash the seed into 64 bytes, half will be sk, half will be cc
33+
hashInput[bytes.size()] = 0;
34+
md_hmac(ILeft, hashInput, bytes.size() + 1, prefix, sizeof(prefix));
35+
36+
hashInput[bytes.size()] = 1;
37+
md_hmac(IRight, hashInput, bytes.size() + 1, prefix, sizeof(prefix));
38+
39+
// Make sure private key is less than the curve order
40+
bn_t* skBn = Util::SecAlloc<bn_t>(1);
41+
bn_t order;
42+
bn_new(order);
43+
g1_get_ord(order);
44+
45+
bn_new(*skBn);
46+
bn_read_bin(*skBn, ILeft, PrivateKey::PRIVATE_KEY_SIZE);
47+
bn_mod_basic(*skBn, *skBn, order);
48+
bn_write_bin(ILeft, PrivateKey::PRIVATE_KEY_SIZE, *skBn);
49+
50+
ExtendedPrivateKey esk(ExtendedPublicKey::VERSION, 0, 0, 0,
51+
ChainCode::FromBytes(Bytes(IRight, ChainCode::SIZE)),
52+
PrivateKey::FromBytes(Bytes(ILeft, PrivateKey::PRIVATE_KEY_SIZE)));
53+
54+
Util::SecFree(skBn);
55+
Util::SecFree(ILeft);
56+
Util::SecFree(hashInput);
57+
return esk;
58+
}
59+
60+
ExtendedPrivateKey ExtendedPrivateKey::FromBytes(const Bytes& bytes) {
61+
uint32_t version = Util::FourBytesToInt(bytes.begin());
62+
uint32_t depth = bytes[4];
63+
uint32_t parentFingerprint = Util::FourBytesToInt(bytes.begin() + 5);
64+
uint32_t childNumber = Util::FourBytesToInt(bytes.begin() + 9);
65+
const uint8_t* ccPointer = bytes.begin() + 13;
66+
const uint8_t* skPointer = ccPointer + ChainCode::SIZE;
67+
68+
ExtendedPrivateKey esk(version, depth, parentFingerprint, childNumber,
69+
ChainCode::FromBytes(Bytes(ccPointer, ChainCode::SIZE)),
70+
PrivateKey::FromBytes(Bytes(skPointer, PrivateKey::PRIVATE_KEY_SIZE)));
71+
return esk;
72+
}
73+
74+
ExtendedPrivateKey ExtendedPrivateKey::PrivateChild(uint32_t i, const bool fLegacy) const {
75+
if (depth >= 255) {
76+
throw std::logic_error("Cannot go further than 255 levels");
77+
}
78+
// Hardened keys have i >= 2^31. Non-hardened have i < 2^31
79+
uint32_t cmp = (1 << 31);
80+
bool hardened = i >= cmp;
81+
82+
uint8_t* ILeft = Util::SecAlloc<uint8_t>(PrivateKey::PRIVATE_KEY_SIZE);
83+
uint8_t IRight[ChainCode::SIZE];
84+
85+
// Chain code is used as hmac key
86+
uint8_t hmacKey[ChainCode::SIZE];
87+
chainCode.Serialize(hmacKey);
88+
89+
size_t inputLen = hardened ? PrivateKey::PRIVATE_KEY_SIZE + 4 + 1
90+
: G1Element::SIZE + 4 + 1;
91+
// Hmac input includes sk or pk, int i, and byte with 0 or 1
92+
uint8_t* hmacInput = Util::SecAlloc<uint8_t>(inputLen);
93+
94+
// Fill the input with the required data
95+
if (hardened) {
96+
sk.Serialize(hmacInput);
97+
Util::IntToFourBytes(hmacInput + PrivateKey::PRIVATE_KEY_SIZE, i);
98+
} else {
99+
memcpy(hmacInput, sk.GetG1Element().Serialize(fLegacy).data(), G1Element::SIZE);
100+
Util::IntToFourBytes(hmacInput + G1Element::SIZE, i);
101+
}
102+
hmacInput[inputLen - 1] = 0;
103+
104+
md_hmac(ILeft, hmacInput, inputLen,
105+
hmacKey, ChainCode::SIZE);
106+
107+
// Change 1 byte to generate a different sequence for chaincode
108+
hmacInput[inputLen - 1] = 1;
109+
110+
md_hmac(IRight, hmacInput, inputLen,
111+
hmacKey, ChainCode::SIZE);
112+
113+
PrivateKey newSk = PrivateKey::FromBytes(Bytes(ILeft, PrivateKey::PRIVATE_KEY_SIZE), true);
114+
newSk = PrivateKey::Aggregate({sk, newSk});
115+
116+
ExtendedPrivateKey esk(version, depth + 1,
117+
sk.GetG1Element().GetFingerprint(), i,
118+
ChainCode::FromBytes(Bytes(IRight, ChainCode::SIZE)),
119+
newSk);
120+
121+
Util::SecFree(ILeft);
122+
Util::SecFree(hmacInput);
123+
124+
return esk;
125+
}
126+
127+
uint32_t ExtendedPrivateKey::GetVersion() const {
128+
return version;
129+
}
130+
131+
uint8_t ExtendedPrivateKey::GetDepth() const {
132+
return depth;
133+
}
134+
135+
uint32_t ExtendedPrivateKey::GetParentFingerprint() const {
136+
return parentFingerprint;
137+
}
138+
139+
uint32_t ExtendedPrivateKey::GetChildNumber() const {
140+
return childNumber;
141+
}
142+
143+
ExtendedPublicKey ExtendedPrivateKey::PublicChild(uint32_t i) const {
144+
return PrivateChild(i).GetExtendedPublicKey();
145+
}
146+
147+
PrivateKey ExtendedPrivateKey::GetPrivateKey() const {
148+
return sk;
149+
}
150+
151+
G1Element ExtendedPrivateKey::GetPublicKey() const {
152+
return sk.GetG1Element();
153+
}
154+
155+
ChainCode ExtendedPrivateKey::GetChainCode() const {
156+
return chainCode;
157+
}
158+
159+
ExtendedPublicKey ExtendedPrivateKey::GetExtendedPublicKey(const bool fLegacy) const {
160+
std::vector<uint8_t> buffer(ExtendedPublicKey::SIZE);
161+
Util::IntToFourBytes(buffer.data(), version);
162+
buffer[4] = depth;
163+
Util::IntToFourBytes(buffer.data() + 5, parentFingerprint);
164+
Util::IntToFourBytes(buffer.data() + 9, childNumber);
165+
166+
chainCode.Serialize(buffer.data() + 13);
167+
auto vecG1 = sk.GetG1Element().Serialize(fLegacy);
168+
buffer.insert(buffer.begin() + 13 + ChainCode::SIZE, vecG1.begin(), vecG1.end());
169+
170+
return ExtendedPublicKey::FromBytes(Bytes(buffer), fLegacy);
171+
}
172+
173+
// Comparator implementation.
174+
bool operator==(ExtendedPrivateKey const &a, ExtendedPrivateKey const &b) {
175+
return (a.GetPrivateKey() == b.GetPrivateKey() &&
176+
a.GetChainCode() == b.GetChainCode());
177+
}
178+
179+
bool operator!=(ExtendedPrivateKey const&a, ExtendedPrivateKey const&b) {
180+
return !(a == b);
181+
}
182+
183+
void ExtendedPrivateKey::Serialize(uint8_t *buffer) const {
184+
Util::IntToFourBytes(buffer, version);
185+
buffer[4] = depth;
186+
Util::IntToFourBytes(buffer + 5, parentFingerprint);
187+
Util::IntToFourBytes(buffer + 9, childNumber);
188+
chainCode.Serialize(buffer + 13);
189+
sk.Serialize(buffer + 13 + ChainCode::SIZE);
190+
}
191+
192+
std::vector<uint8_t> ExtendedPrivateKey::Serialize() const {
193+
std::vector<uint8_t> data(SIZE);
194+
Serialize(data.data());
195+
return data;
196+
}
197+
198+
// Destructors in PrivateKey and ChainCode handle cleaning of memory
199+
ExtendedPrivateKey::~ExtendedPrivateKey() {}
200+
} // end namespace bls

0 commit comments

Comments
 (0)