upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/src/test_bip32.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/test_bip32.cpp')
-rw-r--r--src/test_bip32.cpp119
1 files changed, 119 insertions, 0 deletions
diff --git a/src/test_bip32.cpp b/src/test_bip32.cpp
index 610a651..6c11510 100644
--- a/src/test_bip32.cpp
+++ b/src/test_bip32.cpp
@@ -5,6 +5,8 @@
5 5
6#include "bip32.h" 6#include "bip32.h"
7 7
8#include <openssl/sha.h>
9
8#include <cstdio> 10#include <cstdio>
9#include <cstring> 11#include <cstring>
10#include <string> 12#include <string>
@@ -57,6 +59,76 @@ static std::string ToHex(const std::vector<unsigned char>& v)
57 return out; 59 return out;
58} 60}
59 61
62static bool DecodeBase58Check(const std::string& str, std::vector<unsigned char>& out)
63{
64 static const char* pszBase58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
65 out.clear();
66
67 std::vector<unsigned char> b256(str.size() * 733 / 1000 + 1);
68 for (char c : str)
69 {
70 const char* p = strchr(pszBase58, c);
71 if (!p)
72 return false;
73 int carry = (int)(p - pszBase58);
74 for (std::vector<unsigned char>::reverse_iterator it = b256.rbegin();
75 it != b256.rend(); ++it)
76 {
77 carry += 58 * (*it);
78 *it = (unsigned char)(carry & 0xff);
79 carry >>= 8;
80 }
81 if (carry != 0)
82 return false;
83 }
84
85 size_t zeros = 0;
86 while (zeros < str.size() && str[zeros] == '1')
87 zeros++;
88 std::vector<unsigned char>::iterator it = b256.begin();
89 while (it != b256.end() && *it == 0)
90 ++it;
91
92 std::vector<unsigned char> full;
93 full.assign(zeros, 0);
94 while (it != b256.end())
95 full.push_back(*it++);
96 if (full.size() < 4)
97 return false;
98
99 std::vector<unsigned char> payload(full.begin(), full.end() - 4);
100 unsigned char h1[SHA256_DIGEST_LENGTH];
101 unsigned char h2[SHA256_DIGEST_LENGTH];
102 SHA256(&payload[0], payload.size(), h1);
103 SHA256(h1, sizeof(h1), h2);
104 if (memcmp(h2, &full[full.size() - 4], 4) != 0)
105 return false;
106
107 out = payload;
108 return true;
109}
110
111static bool DecodeXPrvNode(const std::string& xprv, BIP32PrivateNode& nodeOut)
112{
113 nodeOut.privateKey.clear();
114 nodeOut.chainCode.clear();
115
116 std::vector<unsigned char> payload;
117 if (!DecodeBase58Check(xprv, payload))
118 return false;
119 if (payload.size() != 78)
120 return false;
121 if (payload[0] != 0x04 || payload[1] != 0x88 ||
122 payload[2] != 0xad || payload[3] != 0xe4)
123 return false;
124 if (payload[45] != 0x00)
125 return false;
126
127 nodeOut.chainCode.assign(payload.begin() + 13, payload.begin() + 45);
128 nodeOut.privateKey.assign(payload.begin() + 46, payload.end());
129 return true;
130}
131
60int main() 132int main()
61{ 133{
62 std::string err; 134 std::string err;
@@ -124,6 +196,53 @@ int main()
124 "pre-hardened index rejected"); 196 "pre-hardened index rejected");
125 } 197 }
126 198
199 printf("bip32_paths\n");
200 {
201 std::vector<unsigned char> seed = FromHex("000102030405060708090a0b0c0d0e0f");
202 BIP32PrivateNode master;
203 CHECK(BIP32MasterFromSeed(seed, master, err), "path test master derives");
204
205 struct PathVector
206 {
207 const char* name;
208 std::vector<unsigned int> path;
209 const char* xprv;
210 };
211
212 std::vector<PathVector> vectors;
213 vectors.push_back({"m",
214 {},
215 "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi"});
216 vectors.push_back({"m/0'",
217 {0 | BIP32_HARDENED},
218 "xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7"});
219 vectors.push_back({"m/0'/1",
220 {0 | BIP32_HARDENED, 1},
221 "xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs"});
222 vectors.push_back({"m/0'/1/2'",
223 {0 | BIP32_HARDENED, 1, 2 | BIP32_HARDENED},
224 "xprv9z4pot5VBttmtdRTWfWQmoH1taj2axGVzFqSb8C9xaxKymcFzXBDptWmT7FwuEzG3ryjH4ktypQSAewRiNMjANTtpgP4mLTj34bhnZX7UiM"});
225 vectors.push_back({"m/0'/1/2'/2",
226 {0 | BIP32_HARDENED, 1, 2 | BIP32_HARDENED, 2},
227 "xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334"});
228 vectors.push_back({"m/0'/1/2'/2/1000000000",
229 {0 | BIP32_HARDENED, 1, 2 | BIP32_HARDENED, 2, 1000000000},
230 "xprvA41z7zogVVwxVSgdKUHDy1SKmdb533PjDz7J6N6mV6uS3ze1ai8FHa8kmHScGpWmj4WggLyQjgPie1rFSruoUihUZREPSL39UNdE3BBDu76"});
231
232 for (const PathVector& v : vectors)
233 {
234 BIP32PrivateNode actual;
235 BIP32PrivateNode expected;
236 std::string label = std::string("derives ") + v.name;
237 CHECK(BIP32DerivePath(master, v.path, actual, err), label.c_str());
238 label = std::string("decodes vector ") + v.name;
239 CHECK(DecodeXPrvNode(v.xprv, expected), label.c_str());
240 label = std::string("matches vector ") + v.name;
241 CHECK(actual.privateKey == expected.privateKey &&
242 actual.chainCode == expected.chainCode, label.c_str());
243 }
244 }
245
127 printf("\n%s (%d failures)\n", 246 printf("\n%s (%d failures)\n",
128 g_fail == 0 ? "ALL TESTS PASSED" : "TESTS FAILED", g_fail); 247 g_fail == 0 ? "ALL TESTS PASSED" : "TESTS FAILED", g_fail);
129 return g_fail == 0 ? 0 : 1; 248 return g_fail == 0 ? 0 : 1;