forked from dashpay/dash
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
src|test: Add
ExtendedPublicKey
, ExtendedPrivateKey
and `ChainCod…
…e` (dashpay#20) * src: Add ExtendedPublicKey, ExtendedPrivateKey and ChainCode * test: Add "Legacy HD" keys tests * Convert seeds to Bytes Co-authored-by: UdjinM6 <UdjinM6@users.noreply.github.com>
- Loading branch information
1 parent
a767dba
commit 2daa8f8
Showing
9 changed files
with
800 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
// Copyright 2018 Chia Network Inc | ||
|
||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
|
||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
|
||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
#include "bls.hpp" | ||
|
||
namespace bls { | ||
|
||
ChainCode ChainCode::FromBytes(const Bytes& bytes) { | ||
if (bytes.size() != ChainCode::SIZE) { | ||
throw std::invalid_argument("ChainCode::FromBytes: Invalid size"); | ||
} | ||
ChainCode c = ChainCode(); | ||
bn_new(c.chainCode); | ||
bn_read_bin(c.chainCode, bytes.begin(), ChainCode::SIZE); | ||
return c; | ||
} | ||
|
||
ChainCode::ChainCode(const ChainCode &cc) { | ||
uint8_t bytes[ChainCode::SIZE]; | ||
cc.Serialize(bytes); | ||
bn_new(chainCode); | ||
bn_read_bin(chainCode, bytes, ChainCode::SIZE); | ||
} | ||
|
||
// Comparator implementation. | ||
bool operator==(ChainCode const &a, ChainCode const &b) { | ||
return bn_cmp(a.chainCode, b.chainCode) == RLC_EQ; | ||
} | ||
|
||
bool operator!=(ChainCode const &a, ChainCode const &b) { | ||
return !(a == b); | ||
} | ||
|
||
std::ostream &operator<<(std::ostream &os, ChainCode const &s) { | ||
uint8_t buffer[ChainCode::SIZE]; | ||
s.Serialize(buffer); | ||
return os << Util::HexStr(buffer, ChainCode::SIZE); | ||
} | ||
|
||
void ChainCode::Serialize(uint8_t *buffer) const { | ||
bn_write_bin(buffer, ChainCode::SIZE, chainCode); | ||
} | ||
|
||
std::vector<uint8_t> ChainCode::Serialize() const { | ||
std::vector<uint8_t> data(SIZE); | ||
Serialize(data.data()); | ||
return data; | ||
} | ||
} // end namespace bls |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
// Copyright 2018 Chia Network Inc | ||
|
||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
|
||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
|
||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
#ifndef SRC_CHAINCODE_HPP_ | ||
#define SRC_CHAINCODE_HPP_ | ||
|
||
#include <iostream> | ||
#include <vector> | ||
|
||
#include "relic_conf.h" | ||
|
||
#if defined GMP && ARITH == GMP | ||
#include <gmp.h> | ||
#endif | ||
|
||
|
||
#include "relic.h" | ||
#include "relic_test.h" | ||
|
||
#include "util.hpp" | ||
|
||
namespace bls { | ||
class ChainCode { | ||
public: | ||
static const size_t SIZE = 32; | ||
|
||
static ChainCode FromBytes(const Bytes& bytes); | ||
|
||
ChainCode(const ChainCode &cc); | ||
|
||
// Comparator implementation. | ||
friend bool operator==(ChainCode const &a, ChainCode const &b); | ||
friend bool operator!=(ChainCode const &a, ChainCode const &b); | ||
friend std::ostream &operator<<(std::ostream &os, ChainCode const &s); | ||
|
||
void Serialize(uint8_t *buffer) const; | ||
std::vector<uint8_t> Serialize() const; | ||
|
||
// Prevent direct construction, use static constructor | ||
ChainCode() {} | ||
private: | ||
|
||
bn_t chainCode; | ||
}; | ||
} // end namespace bls | ||
|
||
#endif // SRC_CHAINCODE_HPP_ | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,200 @@ | ||
// Copyright 2018 Chia Network Inc | ||
|
||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
|
||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
|
||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
#include <cstring> | ||
#include "bls.hpp" | ||
|
||
namespace bls { | ||
|
||
ExtendedPrivateKey ExtendedPrivateKey::FromSeed(const Bytes& bytes) { | ||
// "BLS HD seed" in ascii | ||
const uint8_t prefix[] = {66, 76, 83, 32, 72, 68, 32, 115, 101, 101, 100}; | ||
|
||
uint8_t* hashInput = Util::SecAlloc<uint8_t>(bytes.size() + 1); | ||
std::memcpy(hashInput, bytes.begin(), bytes.size()); | ||
|
||
// 32 bytes for secret key, and 32 bytes for chaincode | ||
uint8_t* ILeft = Util::SecAlloc<uint8_t>( | ||
PrivateKey::PRIVATE_KEY_SIZE); | ||
uint8_t IRight[ChainCode::SIZE]; | ||
|
||
// Hash the seed into 64 bytes, half will be sk, half will be cc | ||
hashInput[bytes.size()] = 0; | ||
md_hmac(ILeft, hashInput, bytes.size() + 1, prefix, sizeof(prefix)); | ||
|
||
hashInput[bytes.size()] = 1; | ||
md_hmac(IRight, hashInput, bytes.size() + 1, prefix, sizeof(prefix)); | ||
|
||
// Make sure private key is less than the curve order | ||
bn_t* skBn = Util::SecAlloc<bn_t>(1); | ||
bn_t order; | ||
bn_new(order); | ||
g1_get_ord(order); | ||
|
||
bn_new(*skBn); | ||
bn_read_bin(*skBn, ILeft, PrivateKey::PRIVATE_KEY_SIZE); | ||
bn_mod_basic(*skBn, *skBn, order); | ||
bn_write_bin(ILeft, PrivateKey::PRIVATE_KEY_SIZE, *skBn); | ||
|
||
ExtendedPrivateKey esk(ExtendedPublicKey::VERSION, 0, 0, 0, | ||
ChainCode::FromBytes(Bytes(IRight, ChainCode::SIZE)), | ||
PrivateKey::FromBytes(Bytes(ILeft, PrivateKey::PRIVATE_KEY_SIZE))); | ||
|
||
Util::SecFree(skBn); | ||
Util::SecFree(ILeft); | ||
Util::SecFree(hashInput); | ||
return esk; | ||
} | ||
|
||
ExtendedPrivateKey ExtendedPrivateKey::FromBytes(const Bytes& bytes) { | ||
uint32_t version = Util::FourBytesToInt(bytes.begin()); | ||
uint32_t depth = bytes[4]; | ||
uint32_t parentFingerprint = Util::FourBytesToInt(bytes.begin() + 5); | ||
uint32_t childNumber = Util::FourBytesToInt(bytes.begin() + 9); | ||
const uint8_t* ccPointer = bytes.begin() + 13; | ||
const uint8_t* skPointer = ccPointer + ChainCode::SIZE; | ||
|
||
ExtendedPrivateKey esk(version, depth, parentFingerprint, childNumber, | ||
ChainCode::FromBytes(Bytes(ccPointer, ChainCode::SIZE)), | ||
PrivateKey::FromBytes(Bytes(skPointer, PrivateKey::PRIVATE_KEY_SIZE))); | ||
return esk; | ||
} | ||
|
||
ExtendedPrivateKey ExtendedPrivateKey::PrivateChild(uint32_t i, const bool fLegacy) const { | ||
if (depth >= 255) { | ||
throw std::logic_error("Cannot go further than 255 levels"); | ||
} | ||
// Hardened keys have i >= 2^31. Non-hardened have i < 2^31 | ||
uint32_t cmp = (1 << 31); | ||
bool hardened = i >= cmp; | ||
|
||
uint8_t* ILeft = Util::SecAlloc<uint8_t>(PrivateKey::PRIVATE_KEY_SIZE); | ||
uint8_t IRight[ChainCode::SIZE]; | ||
|
||
// Chain code is used as hmac key | ||
uint8_t hmacKey[ChainCode::SIZE]; | ||
chainCode.Serialize(hmacKey); | ||
|
||
size_t inputLen = hardened ? PrivateKey::PRIVATE_KEY_SIZE + 4 + 1 | ||
: G1Element::SIZE + 4 + 1; | ||
// Hmac input includes sk or pk, int i, and byte with 0 or 1 | ||
uint8_t* hmacInput = Util::SecAlloc<uint8_t>(inputLen); | ||
|
||
// Fill the input with the required data | ||
if (hardened) { | ||
sk.Serialize(hmacInput); | ||
Util::IntToFourBytes(hmacInput + PrivateKey::PRIVATE_KEY_SIZE, i); | ||
} else { | ||
memcpy(hmacInput, sk.GetG1Element().Serialize(fLegacy).data(), G1Element::SIZE); | ||
Util::IntToFourBytes(hmacInput + G1Element::SIZE, i); | ||
} | ||
hmacInput[inputLen - 1] = 0; | ||
|
||
md_hmac(ILeft, hmacInput, inputLen, | ||
hmacKey, ChainCode::SIZE); | ||
|
||
// Change 1 byte to generate a different sequence for chaincode | ||
hmacInput[inputLen - 1] = 1; | ||
|
||
md_hmac(IRight, hmacInput, inputLen, | ||
hmacKey, ChainCode::SIZE); | ||
|
||
PrivateKey newSk = PrivateKey::FromBytes(Bytes(ILeft, PrivateKey::PRIVATE_KEY_SIZE), true); | ||
newSk = PrivateKey::Aggregate({sk, newSk}); | ||
|
||
ExtendedPrivateKey esk(version, depth + 1, | ||
sk.GetG1Element().GetFingerprint(), i, | ||
ChainCode::FromBytes(Bytes(IRight, ChainCode::SIZE)), | ||
newSk); | ||
|
||
Util::SecFree(ILeft); | ||
Util::SecFree(hmacInput); | ||
|
||
return esk; | ||
} | ||
|
||
uint32_t ExtendedPrivateKey::GetVersion() const { | ||
return version; | ||
} | ||
|
||
uint8_t ExtendedPrivateKey::GetDepth() const { | ||
return depth; | ||
} | ||
|
||
uint32_t ExtendedPrivateKey::GetParentFingerprint() const { | ||
return parentFingerprint; | ||
} | ||
|
||
uint32_t ExtendedPrivateKey::GetChildNumber() const { | ||
return childNumber; | ||
} | ||
|
||
ExtendedPublicKey ExtendedPrivateKey::PublicChild(uint32_t i) const { | ||
return PrivateChild(i).GetExtendedPublicKey(); | ||
} | ||
|
||
PrivateKey ExtendedPrivateKey::GetPrivateKey() const { | ||
return sk; | ||
} | ||
|
||
G1Element ExtendedPrivateKey::GetPublicKey() const { | ||
return sk.GetG1Element(); | ||
} | ||
|
||
ChainCode ExtendedPrivateKey::GetChainCode() const { | ||
return chainCode; | ||
} | ||
|
||
ExtendedPublicKey ExtendedPrivateKey::GetExtendedPublicKey(const bool fLegacy) const { | ||
std::vector<uint8_t> buffer(ExtendedPublicKey::SIZE); | ||
Util::IntToFourBytes(buffer.data(), version); | ||
buffer[4] = depth; | ||
Util::IntToFourBytes(buffer.data() + 5, parentFingerprint); | ||
Util::IntToFourBytes(buffer.data() + 9, childNumber); | ||
|
||
chainCode.Serialize(buffer.data() + 13); | ||
auto vecG1 = sk.GetG1Element().Serialize(fLegacy); | ||
buffer.insert(buffer.begin() + 13 + ChainCode::SIZE, vecG1.begin(), vecG1.end()); | ||
|
||
return ExtendedPublicKey::FromBytes(Bytes(buffer), fLegacy); | ||
} | ||
|
||
// Comparator implementation. | ||
bool operator==(ExtendedPrivateKey const &a, ExtendedPrivateKey const &b) { | ||
return (a.GetPrivateKey() == b.GetPrivateKey() && | ||
a.GetChainCode() == b.GetChainCode()); | ||
} | ||
|
||
bool operator!=(ExtendedPrivateKey const&a, ExtendedPrivateKey const&b) { | ||
return !(a == b); | ||
} | ||
|
||
void ExtendedPrivateKey::Serialize(uint8_t *buffer) const { | ||
Util::IntToFourBytes(buffer, version); | ||
buffer[4] = depth; | ||
Util::IntToFourBytes(buffer + 5, parentFingerprint); | ||
Util::IntToFourBytes(buffer + 9, childNumber); | ||
chainCode.Serialize(buffer + 13); | ||
sk.Serialize(buffer + 13 + ChainCode::SIZE); | ||
} | ||
|
||
std::vector<uint8_t> ExtendedPrivateKey::Serialize() const { | ||
std::vector<uint8_t> data(SIZE); | ||
Serialize(data.data()); | ||
return data; | ||
} | ||
|
||
// Destructors in PrivateKey and ChainCode handle cleaning of memory | ||
ExtendedPrivateKey::~ExtendedPrivateKey() {} | ||
} // end namespace bls |
Oops, something went wrong.