dash/src/pubkey.h
Wladimir J. van der Laan 79399c8cd0
Merge #10657: Utils: Improvements to ECDSA key-handling code
63179d0 Scope the ECDSA constant sizes to CPubKey / CKey classes (Jack Grigg)
1ce9f0a Ensure that ECDSA constant sizes are correctly-sized (Jack Grigg)
48abe78 Remove redundant `= 0` initialisations (Jack Grigg)
17fa391 Specify ECDSA constant sizes as constants (Jack Grigg)
e4a1086 Update Debian copyright list (Jack Grigg)
e181dbe Add comments (Jack Grigg)
a3603ac Fix potential overflows in ECDSA DER parsers (Jack Grigg)

Pull request description:

  Mostly trivial, but includes fixes to potential overflows in the ECDSA DER parsers.

  Cherry-picked from Zcash PR https://github.com/zcash/zcash/pull/2335

Tree-SHA512: 8fcbd51b0bd6723e5d33fa5d592f7cb68ed182796a9b837ecc8217991ad69d6c970258617dc00eb378c8caa4cec5d6b304d9d2c066acd40cda98e4da68e0caa4
2017-12-20 18:00:32 +01:00

258 lines
7.3 KiB
C++

// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2016 The Bitcoin Core developers
// Copyright (c) 2017 The Zcash developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_PUBKEY_H
#define BITCOIN_PUBKEY_H
#include <hash.h>
#include <serialize.h>
#include <uint256.h>
#include <stdexcept>
#include <vector>
const unsigned int BIP32_EXTKEY_SIZE = 74;
/** A reference to a CKey: the Hash160 of its serialized public key */
class CKeyID : public uint160
{
public:
CKeyID() : uint160() {}
explicit CKeyID(const uint160& in) : uint160(in) {}
};
typedef uint256 ChainCode;
/** An encapsulated public key. */
class CPubKey
{
public:
/**
* secp256k1:
*/
static const unsigned int PUBLIC_KEY_SIZE = 65;
static const unsigned int COMPRESSED_PUBLIC_KEY_SIZE = 33;
static const unsigned int SIGNATURE_SIZE = 72;
static const unsigned int COMPACT_SIGNATURE_SIZE = 65;
/**
* see www.keylength.com
* script supports up to 75 for single byte push
*/
static_assert(
PUBLIC_KEY_SIZE >= COMPRESSED_PUBLIC_KEY_SIZE,
"COMPRESSED_PUBLIC_KEY_SIZE is larger than PUBLIC_KEY_SIZE");
private:
/**
* Just store the serialized data.
* Its length can very cheaply be computed from the first byte.
*/
unsigned char vch[PUBLIC_KEY_SIZE];
//! Compute the length of a pubkey with a given first byte.
unsigned int static GetLen(unsigned char chHeader)
{
if (chHeader == 2 || chHeader == 3)
return COMPRESSED_PUBLIC_KEY_SIZE;
if (chHeader == 4 || chHeader == 6 || chHeader == 7)
return PUBLIC_KEY_SIZE;
return 0;
}
//! Set this key data to be invalid
void Invalidate()
{
vch[0] = 0xFF;
}
public:
//! Construct an invalid public key.
CPubKey()
{
Invalidate();
}
//! Initialize a public key using begin/end iterators to byte data.
template <typename T>
void Set(const T pbegin, const T pend)
{
int len = pend == pbegin ? 0 : GetLen(pbegin[0]);
if (len && len == (pend - pbegin))
memcpy(vch, (unsigned char*)&pbegin[0], len);
else
Invalidate();
}
//! Construct a public key using begin/end iterators to byte data.
template <typename T>
CPubKey(const T pbegin, const T pend)
{
Set(pbegin, pend);
}
//! Construct a public key from a byte vector.
explicit CPubKey(const std::vector<unsigned char>& _vch)
{
Set(_vch.begin(), _vch.end());
}
//! Simple read-only vector-like interface to the pubkey data.
unsigned int size() const { return GetLen(vch[0]); }
const unsigned char* begin() const { return vch; }
const unsigned char* end() const { return vch + size(); }
const unsigned char& operator[](unsigned int pos) const { return vch[pos]; }
//! Comparator implementation.
friend bool operator==(const CPubKey& a, const CPubKey& b)
{
return a.vch[0] == b.vch[0] &&
memcmp(a.vch, b.vch, a.size()) == 0;
}
friend bool operator!=(const CPubKey& a, const CPubKey& b)
{
return !(a == b);
}
friend bool operator<(const CPubKey& a, const CPubKey& b)
{
return a.vch[0] < b.vch[0] ||
(a.vch[0] == b.vch[0] && memcmp(a.vch, b.vch, a.size()) < 0);
}
//! Implement serialization, as if this was a byte vector.
template <typename Stream>
void Serialize(Stream& s) const
{
unsigned int len = size();
::WriteCompactSize(s, len);
s.write((char*)vch, len);
}
template <typename Stream>
void Unserialize(Stream& s)
{
unsigned int len = ::ReadCompactSize(s);
if (len <= PUBLIC_KEY_SIZE) {
s.read((char*)vch, len);
} else {
// invalid pubkey, skip available data
char dummy;
while (len--)
s.read(&dummy, 1);
Invalidate();
}
}
//! Get the KeyID of this public key (hash of its serialization)
CKeyID GetID() const
{
return CKeyID(Hash160(vch, vch + size()));
}
//! Get the 256-bit hash of this public key.
uint256 GetHash() const
{
return Hash(vch, vch + size());
}
/*
* Check syntactic correctness.
*
* Note that this is consensus critical as CheckSig() calls it!
*/
bool IsValid() const
{
return size() > 0;
}
//! fully validate whether this is a valid public key (more expensive than IsValid())
bool IsFullyValid() const;
//! Check whether this is a compressed public key.
bool IsCompressed() const
{
return size() == COMPRESSED_PUBLIC_KEY_SIZE;
}
/**
* Verify a DER signature (~72 bytes).
* If this public key is not fully valid, the return value will be false.
*/
bool Verify(const uint256& hash, const std::vector<unsigned char>& vchSig) const;
/**
* Check whether a signature is normalized (lower-S).
*/
static bool CheckLowS(const std::vector<unsigned char>& vchSig);
//! Recover a public key from a compact signature.
bool RecoverCompact(const uint256& hash, const std::vector<unsigned char>& vchSig);
//! Turn this public key into an uncompressed public key.
bool Decompress();
//! Derive BIP32 child pubkey.
bool Derive(CPubKey& pubkeyChild, ChainCode &ccChild, unsigned int nChild, const ChainCode& cc) const;
};
struct CExtPubKey {
unsigned char nDepth;
unsigned char vchFingerprint[4];
unsigned int nChild;
ChainCode chaincode;
CPubKey pubkey;
friend bool operator==(const CExtPubKey &a, const CExtPubKey &b)
{
return a.nDepth == b.nDepth &&
memcmp(&a.vchFingerprint[0], &b.vchFingerprint[0], sizeof(vchFingerprint)) == 0 &&
a.nChild == b.nChild &&
a.chaincode == b.chaincode &&
a.pubkey == b.pubkey;
}
void Encode(unsigned char code[BIP32_EXTKEY_SIZE]) const;
void Decode(const unsigned char code[BIP32_EXTKEY_SIZE]);
bool Derive(CExtPubKey& out, unsigned int nChild) const;
void Serialize(CSizeComputer& s) const
{
// Optimized implementation for ::GetSerializeSize that avoids copying.
s.seek(BIP32_EXTKEY_SIZE + 1); // add one byte for the size (compact int)
}
template <typename Stream>
void Serialize(Stream& s) const
{
unsigned int len = BIP32_EXTKEY_SIZE;
::WriteCompactSize(s, len);
unsigned char code[BIP32_EXTKEY_SIZE];
Encode(code);
s.write((const char *)&code[0], len);
}
template <typename Stream>
void Unserialize(Stream& s)
{
unsigned int len = ::ReadCompactSize(s);
unsigned char code[BIP32_EXTKEY_SIZE];
if (len != BIP32_EXTKEY_SIZE)
throw std::runtime_error("Invalid extended key size\n");
s.read((char *)&code[0], len);
Decode(code);
}
};
/** Users of this module must hold an ECCVerifyHandle. The constructor and
* destructor of these are not allowed to run in parallel, though. */
class ECCVerifyHandle
{
static int refcount;
public:
ECCVerifyHandle();
~ECCVerifyHandle();
};
#endif // BITCOIN_PUBKEY_H