@ -2,8 +2,15 @@
@@ -2,8 +2,15 @@
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
# include <openssl/ec.h>
# include <map>
# include <boost/tuple/tuple.hpp>
# include <openssl/ecdsa.h>
# include <openssl/obj_mac.h>
# include "key.h"
# include "sync.h"
# include "util.h"
// Generate a private key from just the secret parameter
int EC_KEY_regenerate_key ( EC_KEY * eckey , BIGNUM * priv_key )
@ -115,3 +122,326 @@ err:
@@ -115,3 +122,326 @@ err:
if ( Q ! = NULL ) EC_POINT_free ( Q ) ;
return ret ;
}
void CKey : : SetCompressedPubKey ( )
{
EC_KEY_set_conv_form ( pkey , POINT_CONVERSION_COMPRESSED ) ;
fCompressedPubKey = true ;
}
void CKey : : Reset ( )
{
fCompressedPubKey = false ;
pkey = EC_KEY_new_by_curve_name ( NID_secp256k1 ) ;
if ( pkey = = NULL )
throw key_error ( " CKey::CKey() : EC_KEY_new_by_curve_name failed " ) ;
fSet = false ;
}
CKey : : CKey ( )
{
Reset ( ) ;
}
CKey : : CKey ( const CKey & b )
{
pkey = EC_KEY_dup ( b . pkey ) ;
if ( pkey = = NULL )
throw key_error ( " CKey::CKey(const CKey&) : EC_KEY_dup failed " ) ;
fSet = b . fSet ;
}
CKey & CKey : : operator = ( const CKey & b )
{
if ( ! EC_KEY_copy ( pkey , b . pkey ) )
throw key_error ( " CKey::operator=(const CKey&) : EC_KEY_copy failed " ) ;
fSet = b . fSet ;
return ( * this ) ;
}
CKey : : ~ CKey ( )
{
EC_KEY_free ( pkey ) ;
}
bool CKey : : IsNull ( ) const
{
return ! fSet ;
}
bool CKey : : IsCompressed ( ) const
{
return fCompressedPubKey ;
}
void CKey : : MakeNewKey ( bool fCompressed )
{
if ( ! EC_KEY_generate_key ( pkey ) )
throw key_error ( " CKey::MakeNewKey() : EC_KEY_generate_key failed " ) ;
if ( fCompressed )
SetCompressedPubKey ( ) ;
fSet = true ;
}
bool CKey : : SetPrivKey ( const CPrivKey & vchPrivKey )
{
const unsigned char * pbegin = & vchPrivKey [ 0 ] ;
if ( ! d2i_ECPrivateKey ( & pkey , & pbegin , vchPrivKey . size ( ) ) )
return false ;
fSet = true ;
return true ;
}
bool CKey : : SetSecret ( const CSecret & vchSecret , bool fCompressed )
{
EC_KEY_free ( pkey ) ;
pkey = EC_KEY_new_by_curve_name ( NID_secp256k1 ) ;
if ( pkey = = NULL )
throw key_error ( " CKey::SetSecret() : EC_KEY_new_by_curve_name failed " ) ;
if ( vchSecret . size ( ) ! = 32 )
throw key_error ( " CKey::SetSecret() : secret must be 32 bytes " ) ;
BIGNUM * bn = BN_bin2bn ( & vchSecret [ 0 ] , 32 , BN_new ( ) ) ;
if ( bn = = NULL )
throw key_error ( " CKey::SetSecret() : BN_bin2bn failed " ) ;
if ( ! EC_KEY_regenerate_key ( pkey , bn ) )
{
BN_clear_free ( bn ) ;
throw key_error ( " CKey::SetSecret() : EC_KEY_regenerate_key failed " ) ;
}
BN_clear_free ( bn ) ;
fSet = true ;
if ( fCompressed | | fCompressedPubKey )
SetCompressedPubKey ( ) ;
return true ;
}
CSecret CKey : : GetSecret ( bool & fCompressed ) const
{
CSecret vchRet ;
vchRet . resize ( 32 ) ;
const BIGNUM * bn = EC_KEY_get0_private_key ( pkey ) ;
int nBytes = BN_num_bytes ( bn ) ;
if ( bn = = NULL )
throw key_error ( " CKey::GetSecret() : EC_KEY_get0_private_key failed " ) ;
int n = BN_bn2bin ( bn , & vchRet [ 32 - nBytes ] ) ;
if ( n ! = nBytes )
throw key_error ( " CKey::GetSecret() : BN_bn2bin failed " ) ;
fCompressed = fCompressedPubKey ;
return vchRet ;
}
CPrivKey CKey : : GetPrivKey ( ) const
{
int nSize = i2d_ECPrivateKey ( pkey , NULL ) ;
if ( ! nSize )
throw key_error ( " CKey::GetPrivKey() : i2d_ECPrivateKey failed " ) ;
CPrivKey vchPrivKey ( nSize , 0 ) ;
unsigned char * pbegin = & vchPrivKey [ 0 ] ;
if ( i2d_ECPrivateKey ( pkey , & pbegin ) ! = nSize )
throw key_error ( " CKey::GetPrivKey() : i2d_ECPrivateKey returned unexpected size " ) ;
return vchPrivKey ;
}
bool CKey : : SetPubKey ( const std : : vector < unsigned char > & vchPubKey )
{
const unsigned char * pbegin = & vchPubKey [ 0 ] ;
if ( ! o2i_ECPublicKey ( & pkey , & pbegin , vchPubKey . size ( ) ) )
return false ;
fSet = true ;
if ( vchPubKey . size ( ) = = 33 )
SetCompressedPubKey ( ) ;
return true ;
}
std : : vector < unsigned char > CKey : : GetPubKey ( ) const
{
int nSize = i2o_ECPublicKey ( pkey , NULL ) ;
if ( ! nSize )
throw key_error ( " CKey::GetPubKey() : i2o_ECPublicKey failed " ) ;
std : : vector < unsigned char > vchPubKey ( nSize , 0 ) ;
unsigned char * pbegin = & vchPubKey [ 0 ] ;
if ( i2o_ECPublicKey ( pkey , & pbegin ) ! = nSize )
throw key_error ( " CKey::GetPubKey() : i2o_ECPublicKey returned unexpected size " ) ;
return vchPubKey ;
}
bool CKey : : Sign ( uint256 hash , std : : vector < unsigned char > & vchSig )
{
unsigned int nSize = ECDSA_size ( pkey ) ;
vchSig . resize ( nSize ) ; // Make sure it is big enough
if ( ! ECDSA_sign ( 0 , ( unsigned char * ) & hash , sizeof ( hash ) , & vchSig [ 0 ] , & nSize , pkey ) )
{
vchSig . clear ( ) ;
return false ;
}
vchSig . resize ( nSize ) ; // Shrink to fit actual size
return true ;
}
// create a compact signature (65 bytes), which allows reconstructing the used public key
// The format is one header byte, followed by two times 32 bytes for the serialized r and s values.
// The header byte: 0x1B = first key with even y, 0x1C = first key with odd y,
// 0x1D = second key with even y, 0x1E = second key with odd y
bool CKey : : SignCompact ( uint256 hash , std : : vector < unsigned char > & vchSig )
{
bool fOk = false ;
ECDSA_SIG * sig = ECDSA_do_sign ( ( unsigned char * ) & hash , sizeof ( hash ) , pkey ) ;
if ( sig = = NULL )
return false ;
vchSig . clear ( ) ;
vchSig . resize ( 65 , 0 ) ;
int nBitsR = BN_num_bits ( sig - > r ) ;
int nBitsS = BN_num_bits ( sig - > s ) ;
if ( nBitsR < = 256 & & nBitsS < = 256 )
{
int nRecId = - 1 ;
for ( int i = 0 ; i < 4 ; i + + )
{
CKey keyRec ;
keyRec . fSet = true ;
if ( fCompressedPubKey )
keyRec . SetCompressedPubKey ( ) ;
if ( ECDSA_SIG_recover_key_GFp ( keyRec . pkey , sig , ( unsigned char * ) & hash , sizeof ( hash ) , i , 1 ) = = 1 )
if ( keyRec . GetPubKey ( ) = = this - > GetPubKey ( ) )
{
nRecId = i ;
break ;
}
}
if ( nRecId = = - 1 )
throw key_error ( " CKey::SignCompact() : unable to construct recoverable key " ) ;
vchSig [ 0 ] = nRecId + 27 + ( fCompressedPubKey ? 4 : 0 ) ;
BN_bn2bin ( sig - > r , & vchSig [ 33 - ( nBitsR + 7 ) / 8 ] ) ;
BN_bn2bin ( sig - > s , & vchSig [ 65 - ( nBitsS + 7 ) / 8 ] ) ;
fOk = true ;
}
ECDSA_SIG_free ( sig ) ;
return fOk ;
}
// reconstruct public key from a compact signature
// This is only slightly more CPU intensive than just verifying it.
// If this function succeeds, the recovered public key is guaranteed to be valid
// (the signature is a valid signature of the given data for that key)
bool CKey : : SetCompactSignature ( uint256 hash , const std : : vector < unsigned char > & vchSig )
{
if ( vchSig . size ( ) ! = 65 )
return false ;
int nV = vchSig [ 0 ] ;
if ( nV < 27 | | nV > = 35 )
return false ;
ECDSA_SIG * sig = ECDSA_SIG_new ( ) ;
BN_bin2bn ( & vchSig [ 1 ] , 32 , sig - > r ) ;
BN_bin2bn ( & vchSig [ 33 ] , 32 , sig - > s ) ;
EC_KEY_free ( pkey ) ;
pkey = EC_KEY_new_by_curve_name ( NID_secp256k1 ) ;
if ( nV > = 31 )
{
SetCompressedPubKey ( ) ;
nV - = 4 ;
}
if ( ECDSA_SIG_recover_key_GFp ( pkey , sig , ( unsigned char * ) & hash , sizeof ( hash ) , nV - 27 , 0 ) = = 1 )
{
fSet = true ;
ECDSA_SIG_free ( sig ) ;
return true ;
}
return false ;
}
// Valid signature cache, to avoid doing expensive ECDSA signature checking
// twice for every transaction (once when accepted into memory pool, and
// again when accepted into the block chain)
// sigdata_type is (signature hash, signature, public key):
typedef boost : : tuple < uint256 , std : : vector < unsigned char > , std : : vector < unsigned char > > sigdata_type ;
static std : : set < sigdata_type > setValidSigCache ;
static CCriticalSection cs_sigcache ;
static bool
GetValidSigCache ( uint256 hash , const std : : vector < unsigned char > & vchSig , const std : : vector < unsigned char > & pubKey )
{
LOCK ( cs_sigcache ) ;
sigdata_type k ( hash , vchSig , pubKey ) ;
std : : set < sigdata_type > : : iterator mi = setValidSigCache . find ( k ) ;
if ( mi ! = setValidSigCache . end ( ) )
return true ;
return false ;
}
static void
SetValidSigCache ( uint256 hash , const std : : vector < unsigned char > & vchSig , const std : : vector < unsigned char > & pubKey )
{
// DoS prevention: limit cache size to less than 10MB
// (~200 bytes per cache entry times 50,000 entries)
// Since there are a maximum of 20,000 signature operations per block
// 50,000 is a reasonable default.
int64 nMaxCacheSize = GetArg ( " -maxsigcachesize " , 50000 ) ;
if ( nMaxCacheSize < = 0 ) return ;
LOCK ( cs_sigcache ) ;
while ( setValidSigCache . size ( ) > nMaxCacheSize )
{
// Evict a random entry. Random because that helps
// foil would-be DoS attackers who might try to pre-generate
// and re-use a set of valid signatures just-slightly-greater
// than our cache size.
uint256 randomHash = GetRandHash ( ) ;
std : : vector < unsigned char > unused ;
std : : set < sigdata_type > : : iterator it =
setValidSigCache . lower_bound ( sigdata_type ( randomHash , unused , unused ) ) ;
if ( it = = setValidSigCache . end ( ) )
it = setValidSigCache . begin ( ) ;
setValidSigCache . erase ( * it ) ;
}
sigdata_type k ( hash , vchSig , pubKey ) ;
setValidSigCache . insert ( k ) ;
}
bool CKey : : Verify ( uint256 hash , const std : : vector < unsigned char > & vchSig )
{
if ( GetValidSigCache ( hash , vchSig , GetPubKey ( ) ) )
return true ;
// -1 = error, 0 = bad sig, 1 = good
if ( ECDSA_verify ( 0 , ( unsigned char * ) & hash , sizeof ( hash ) , & vchSig [ 0 ] , vchSig . size ( ) , pkey ) ! = 1 )
return false ;
// good sig
SetValidSigCache ( hash , vchSig , GetPubKey ( ) ) ;
return true ;
}
bool CKey : : VerifyCompact ( uint256 hash , const std : : vector < unsigned char > & vchSig )
{
if ( GetValidSigCache ( hash , vchSig , GetPubKey ( ) ) )
return true ;
CKey key ;
if ( ! key . SetCompactSignature ( hash , vchSig ) )
return false ;
if ( GetPubKey ( ) ! = key . GetPubKey ( ) )
return false ;
SetValidSigCache ( hash , vchSig , GetPubKey ( ) ) ;
return true ;
}
bool CKey : : IsValid ( )
{
if ( ! fSet )
return false ;
bool fCompr ;
CSecret secret = GetSecret ( fCompr ) ;
CKey key2 ;
key2 . SetSecret ( secret , fCompr ) ;
return GetPubKey ( ) = = key2 . GetPubKey ( ) ;
}