Update License in File Headers
[novacoin.git] / src / key.h
index 0105916..f687f33 100644 (file)
--- a/src/key.h
+++ b/src/key.h
@@ -1,7 +1,7 @@
 // Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2011 The Bitcoin developers
+// Copyright (c) 2009-2012 The Bitcoin developers
 // Distributed under the MIT/X11 software license, see the accompanying
-// file license.txt or http://www.opensource.org/licenses/mit-license.php.
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
 #ifndef BITCOIN_KEY_H
 #define BITCOIN_KEY_H
 
@@ -12,9 +12,8 @@
 #include <openssl/ecdsa.h>
 #include <openssl/obj_mac.h>
 
-#include "serialize.h"
+#include "allocators.h"
 #include "uint256.h"
-#include "base58.h"
 
 // secp160k1
 // const unsigned int PRIVATE_KEY_SIZE = 192;
 // see www.keylength.com
 // script supports up to 75 for single byte push
 
-// Generate a private key from just the secret parameter
-int static inline EC_KEY_regenerate_key(EC_KEY *eckey, BIGNUM *priv_key)
-{
-    int ok = 0;
-    BN_CTX *ctx = NULL;
-    EC_POINT *pub_key = NULL;
-
-    if (!eckey) return 0;
-
-    const EC_GROUP *group = EC_KEY_get0_group(eckey);
-
-    if ((ctx = BN_CTX_new()) == NULL)
-        goto err;
-
-    pub_key = EC_POINT_new(group);
-
-    if (pub_key == NULL)
-        goto err;
-
-    if (!EC_POINT_mul(group, pub_key, priv_key, NULL, NULL, ctx))
-        goto err;
-
-    EC_KEY_set_private_key(eckey,priv_key);
-    EC_KEY_set_public_key(eckey,pub_key);
-
-    ok = 1;
-
-err:
-
-    if (pub_key)
-        EC_POINT_free(pub_key);
-    if (ctx != NULL)
-        BN_CTX_free(ctx);
-
-    return(ok);
-}
-
-// Perform ECDSA key recovery (see SEC1 4.1.6) for curves over (mod p)-fields
-// recid selects which key is recovered
-// if check is nonzero, additional checks are performed
-int static inline ECDSA_SIG_recover_key_GFp(EC_KEY *eckey, ECDSA_SIG *ecsig, const unsigned char *msg, int msglen, int recid, int check)
-{
-    if (!eckey) return 0;
-
-    int ret = 0;
-    BN_CTX *ctx = NULL;
-
-    BIGNUM *x = NULL;
-    BIGNUM *e = NULL;
-    BIGNUM *order = NULL;
-    BIGNUM *sor = NULL;
-    BIGNUM *eor = NULL;
-    BIGNUM *field = NULL;
-    EC_POINT *R = NULL;
-    EC_POINT *O = NULL;
-    EC_POINT *Q = NULL;
-    BIGNUM *rr = NULL;
-    BIGNUM *zero = NULL;
-    int n = 0;
-    int i = recid / 2;
-
-    const EC_GROUP *group = EC_KEY_get0_group(eckey);
-    if ((ctx = BN_CTX_new()) == NULL) { ret = -1; goto err; }
-    BN_CTX_start(ctx);
-    order = BN_CTX_get(ctx);
-    if (!EC_GROUP_get_order(group, order, ctx)) { ret = -2; goto err; }
-    x = BN_CTX_get(ctx);
-    if (!BN_copy(x, order)) { ret=-1; goto err; }
-    if (!BN_mul_word(x, i)) { ret=-1; goto err; }
-    if (!BN_add(x, x, ecsig->r)) { ret=-1; goto err; }
-    field = BN_CTX_get(ctx);
-    if (!EC_GROUP_get_curve_GFp(group, field, NULL, NULL, ctx)) { ret=-2; goto err; }
-    if (BN_cmp(x, field) >= 0) { ret=0; goto err; }
-    if ((R = EC_POINT_new(group)) == NULL) { ret = -2; goto err; }
-    if (!EC_POINT_set_compressed_coordinates_GFp(group, R, x, recid % 2, ctx)) { ret=0; goto err; }
-    if (check)
-    {
-        if ((O = EC_POINT_new(group)) == NULL) { ret = -2; goto err; }
-        if (!EC_POINT_mul(group, O, NULL, R, order, ctx)) { ret=-2; goto err; }
-        if (!EC_POINT_is_at_infinity(group, O)) { ret = 0; goto err; }
-    }
-    if ((Q = EC_POINT_new(group)) == NULL) { ret = -2; goto err; }
-    n = EC_GROUP_get_degree(group);
-    e = BN_CTX_get(ctx);
-    if (!BN_bin2bn(msg, msglen, e)) { ret=-1; goto err; }
-    if (8*msglen > n) BN_rshift(e, e, 8-(n & 7));
-    zero = BN_CTX_get(ctx);
-    if (!BN_zero(zero)) { ret=-1; goto err; }
-    if (!BN_mod_sub(e, zero, e, order, ctx)) { ret=-1; goto err; }
-    rr = BN_CTX_get(ctx);
-    if (!BN_mod_inverse(rr, ecsig->r, order, ctx)) { ret=-1; goto err; }
-    sor = BN_CTX_get(ctx);
-    if (!BN_mod_mul(sor, ecsig->s, rr, order, ctx)) { ret=-1; goto err; }
-    eor = BN_CTX_get(ctx);
-    if (!BN_mod_mul(eor, e, rr, order, ctx)) { ret=-1; goto err; }
-    if (!EC_POINT_mul(group, Q, eor, R, sor, ctx)) { ret=-2; goto err; }
-    if (!EC_KEY_set_public_key(eckey, Q)) { ret=-2; goto err; }
-
-    ret = 1;
-
-err:
-    if (ctx) {
-        BN_CTX_end(ctx);
-        BN_CTX_free(ctx);
-    }
-    if (R != NULL) EC_POINT_free(R);
-    if (O != NULL) EC_POINT_free(O);
-    if (Q != NULL) EC_POINT_free(Q);
-    return ret;
-}
+int extern EC_KEY_regenerate_key(EC_KEY *eckey, BIGNUM *priv_key);
+int extern ECDSA_SIG_recover_key_GFp(EC_KEY *eckey, ECDSA_SIG *ecsig, const unsigned char *msg, int msglen, int recid, int check);
 
 class key_error : public std::runtime_error
 {
@@ -163,21 +54,36 @@ typedef std::vector<unsigned char, secure_allocator<unsigned char> > CPrivKey;
 // CSecret is a serialization of just the secret parameter (32 bytes)
 typedef std::vector<unsigned char, secure_allocator<unsigned char> > CSecret;
 
+/** An encapsulated OpenSSL Elliptic Curve key (public and/or private) */
 class CKey
 {
 protected:
     EC_KEY* pkey;
     bool fSet;
+    bool fCompressedPubKey;
+
+    void SetCompressedPubKey()
+    {
+        EC_KEY_set_conv_form(pkey, POINT_CONVERSION_COMPRESSED);
+        fCompressedPubKey = true;
+    }
 
 public:
-    CKey()
+
+    void 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()
+    {
+        Reset();
+    }
+
     CKey(const CKey& b)
     {
         pkey = EC_KEY_dup(b.pkey);
@@ -204,10 +110,17 @@ public:
         return !fSet;
     }
 
-    void MakeNewKey()
+    bool IsCompressed() const
+    {
+        return fCompressedPubKey;
+    }
+
+    void MakeNewKey(bool fCompressed)
     {
         if (!EC_KEY_generate_key(pkey))
             throw key_error("CKey::MakeNewKey() : EC_KEY_generate_key failed");
+        if (fCompressed)
+            SetCompressedPubKey();
         fSet = true;
     }
 
@@ -220,7 +133,7 @@ public:
         return true;
     }
 
-    bool SetSecret(const CSecret& vchSecret)
+    bool SetSecret(const CSecret& vchSecret, bool fCompressed = false)
     {
         EC_KEY_free(pkey);
         pkey = EC_KEY_new_by_curve_name(NID_secp256k1);
@@ -229,16 +142,21 @@ public:
         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) 
+        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 GetSecret() const
+    CSecret GetSecret(bool &fCompressed) const
     {
         CSecret vchRet;
         vchRet.resize(32);
@@ -249,12 +167,13 @@ public:
         int n=BN_bn2bin(bn,&vchRet[32 - nBytes]);
         if (n != nBytes) 
             throw key_error("CKey::GetSecret(): BN_bn2bin failed");
+        fCompressed = fCompressedPubKey;
         return vchRet;
     }
 
     CPrivKey GetPrivKey() const
     {
-        unsigned int nSize = i2d_ECPrivateKey(pkey, NULL);
+        int nSize = i2d_ECPrivateKey(pkey, NULL);
         if (!nSize)
             throw key_error("CKey::GetPrivKey() : i2d_ECPrivateKey failed");
         CPrivKey vchPrivKey(nSize, 0);
@@ -270,12 +189,14 @@ public:
         if (!o2i_ECPublicKey(&pkey, &pbegin, vchPubKey.size()))
             return false;
         fSet = true;
+        if (vchPubKey.size() == 33)
+            SetCompressedPubKey();
         return true;
     }
 
     std::vector<unsigned char> GetPubKey() const
     {
-        unsigned int nSize = i2o_ECPublicKey(pkey, NULL);
+        int nSize = i2o_ECPublicKey(pkey, NULL);
         if (!nSize)
             throw key_error("CKey::GetPubKey() : i2o_ECPublicKey failed");
         std::vector<unsigned char> vchPubKey(nSize, 0);
@@ -287,13 +208,14 @@ public:
 
     bool Sign(uint256 hash, std::vector<unsigned char>& vchSig)
     {
-        vchSig.clear();
-        unsigned char pchSig[10000];
-        unsigned int nSize = 0;
-        if (!ECDSA_sign(0, (unsigned char*)&hash, sizeof(hash), pchSig, &nSize, pkey))
+        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);
-        memcpy(&vchSig[0], pchSig, nSize);
+        }
+        vchSig.resize(nSize); // Shrink to fit actual size
         return true;
     }
 
@@ -318,6 +240,8 @@ public:
             {
                 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())
                     {
@@ -329,7 +253,7 @@ public:
             if (nRecId == -1)
                 throw key_error("CKey::SignCompact() : unable to construct recoverable key");
 
-            vchSig[0] = nRecId+27;
+            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;
@@ -346,7 +270,8 @@ public:
     {
         if (vchSig.size() != 65)
             return false;
-        if (vchSig[0]<27 || vchSig[0]>=31)
+        int nV = vchSig[0];
+        if (nV<27 || nV>=35)
             return false;
         ECDSA_SIG *sig = ECDSA_SIG_new();
         BN_bin2bn(&vchSig[1],32,sig->r);
@@ -354,7 +279,12 @@ public:
 
         EC_KEY_free(pkey);
         pkey = EC_KEY_new_by_curve_name(NID_secp256k1);
-        if (ECDSA_SIG_recover_key_GFp(pkey, sig, (unsigned char*)&hash, sizeof(hash), vchSig[0] - 27, 0) == 1)
+        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);
@@ -382,20 +312,15 @@ public:
         return true;
     }
 
-    // Get the address corresponding to this key
-    CBitcoinAddress GetAddress() const
-    {
-        return CBitcoinAddress(GetPubKey());
-    }
-
     bool IsValid()
     {
         if (!fSet)
             return false;
 
-        CSecret secret = GetSecret();
+        bool fCompr;
+        CSecret secret = GetSecret(fCompr);
         CKey key2;
-        key2.SetSecret(secret);
+        key2.SetSecret(secret, fCompr);
         return GetPubKey() == key2.GetPubKey();
     }
 };