//------------------------------------------------------------------------------
/*
    Copyright (c) 2011-2013, OpenCoin, Inc.
*/
//==============================================================================

// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2011 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.
#ifndef RIPPLE_CKEY_H
#define RIPPLE_CKEY_H

// secp256k1:
// const unsigned int PRIVATE_KEY_SIZE = 279;
// const unsigned int PUBLIC_KEY_SIZE  = 65; // but we don't use full keys
// const unsigned int COMPUB_KEY_SIZE  = 33;
// const unsigned int SIGNATURE_SIZE   = 72;
//
// see www.keylength.com
// script supports up to 75 for single byte push

// VFALCO NOTE this is unused
/*
int static inline EC_KEY_regenerate_key(EC_KEY *eckey, BIGNUM *priv_key)
{
    int okay = 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_conv_form(eckey, POINT_CONVERSION_COMPRESSED);
    EC_KEY_set_private_key(eckey, priv_key);
    EC_KEY_set_public_key(eckey, pub_key);

    okay = 1;

err:

    if (pub_key)
        EC_POINT_free(pub_key);
    if (ctx != NULL)
        BN_CTX_free(ctx);

    return (okay);
}
*/

class key_error : public std::runtime_error
{
public:
    explicit key_error (const std::string& str) : std::runtime_error (str) {}
};

class CKey
{
protected:
    EC_KEY* pkey;
    bool fSet;


public:
    typedef boost::shared_ptr<CKey> pointer;

    CKey ()
    {
        pkey = EC_KEY_new_by_curve_name (NID_secp256k1);
        EC_KEY_set_conv_form (pkey, POINT_CONVERSION_COMPRESSED);

        if (pkey == NULL)
            throw key_error ("CKey::CKey() : EC_KEY_new_by_curve_name failed");

        fSet = false;
    }

    CKey (const CKey& b)
    {
        pkey = EC_KEY_dup (b.pkey);
        EC_KEY_set_conv_form (pkey, POINT_CONVERSION_COMPRESSED);

        if (pkey == NULL)
            throw key_error ("CKey::CKey(const CKey&) : EC_KEY_dup failed");

        fSet = b.fSet;
    }

    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 ()
    {
        EC_KEY_free (pkey);
    }


    static uint128 PassPhraseToKey (const std::string& passPhrase);
    static EC_KEY* GenerateRootDeterministicKey (const uint128& passPhrase);
    static EC_KEY* GenerateRootPubKey (BIGNUM* pubGenerator);
    static EC_KEY* GeneratePublicDeterministicKey (const RippleAddress& generator, int n);
    static EC_KEY* GeneratePrivateDeterministicKey (const RippleAddress& family, const BIGNUM* rootPriv, int n);
    static EC_KEY* GeneratePrivateDeterministicKey (const RippleAddress& family, uint256 const& rootPriv, int n);

    CKey (const uint128& passPhrase) : fSet (false)
    {
        pkey = GenerateRootDeterministicKey (passPhrase);
        fSet = true;
        assert (pkey);
    }

    CKey (const RippleAddress& generator, int n) : fSet (false)
    {
        // public deterministic key
        pkey = GeneratePublicDeterministicKey (generator, n);
        fSet = true;
        assert (pkey);
    }

    CKey (const RippleAddress& base, const BIGNUM* rootPrivKey, int n) : fSet (false)
    {
        // private deterministic key
        pkey = GeneratePrivateDeterministicKey (base, rootPrivKey, n);
        fSet = true;
        assert (pkey);
    }

    CKey (uint256 const& privateKey) : pkey (NULL), fSet (false)
    {
        // XXX Broken pkey is null.
        SetPrivateKeyU (privateKey);
    }

#if 0
    CKey (const RippleAddress& masterKey, int keyNum, bool isPublic) : pkey (NULL), fSet (false)
    {
        if (isPublic)
            SetPubSeq (masterKey, keyNum);
        else
            SetPrivSeq (masterKey, keyNum); // broken, need seed

        fSet = true;
    }
#endif

    bool IsNull () const
    {
        return !fSet;
    }

    void MakeNewKey ()
    {
        if (!EC_KEY_generate_key (pkey))
            throw key_error ("CKey::MakeNewKey() : EC_KEY_generate_key failed");

        EC_KEY_set_conv_form (pkey, POINT_CONVERSION_COMPRESSED);
        fSet = true;
    }

    // XXX Still used!
    BIGNUM* GetSecretBN () const
    {
        // DEPRECATED
        return BN_dup (EC_KEY_get0_private_key (pkey));
    }

    void GetPrivateKeyU (uint256& privKey)
    {
        const BIGNUM* bn = EC_KEY_get0_private_key (pkey);

        if (bn == NULL)
            throw key_error ("CKey::GetPrivateKeyU: EC_KEY_get0_private_key failed");

        privKey.zero ();
        BN_bn2bin (bn, privKey.begin () + (privKey.size () - BN_num_bytes (bn)));
    }

    bool SetPrivateKeyU (uint256 const& key, bool bThrow = false)
    {
        // XXX Broken if pkey is not set.
        BIGNUM* bn          = BN_bin2bn (key.begin (), key.size (), NULL);
        bool    bSuccess    = !!EC_KEY_set_private_key (pkey, bn);

        BN_clear_free (bn);

        if (bSuccess)
        {
            fSet = true;
        }
        else if (bThrow)
        {
            throw key_error ("CKey::SetPrivateKeyU: EC_KEY_set_private_key failed");
        }

        return bSuccess;
    }

    bool SetPubKey (const void* ptr, size_t len)
    {
        const unsigned char* pbegin = static_cast<const unsigned char*> (ptr);

        if (!o2i_ECPublicKey (&pkey, &pbegin, len))
            return false;

        EC_KEY_set_conv_form (pkey, POINT_CONVERSION_COMPRESSED);
        fSet = true;
        return true;
    }

    bool SetPubKey (Blob const& vchPubKey)
    {
        return SetPubKey (&vchPubKey[0], vchPubKey.size ());
    }

    bool SetPubKey (const std::string& pubKey)
    {
        return SetPubKey (pubKey.data (), pubKey.size ());
    }

    Blob GetPubKey () const
    {
        unsigned int nSize = i2o_ECPublicKey (pkey, NULL);
        assert (nSize <= 33);

        if (!nSize)
            throw key_error ("CKey::GetPubKey() : i2o_ECPublicKey failed");

        Blob vchPubKey (33, 0);
        unsigned char* pbegin = &vchPubKey[0];

        if (i2o_ECPublicKey (pkey, &pbegin) != nSize)
            throw key_error ("CKey::GetPubKey() : i2o_ECPublicKey returned unexpected size");

        assert (vchPubKey.size () <= 33);
        return vchPubKey;
    }

    bool Sign (uint256 const& hash, Blob& vchSig)
    {
        unsigned char pchSig[10000];
        unsigned int nSize = 0;

        vchSig.clear ();

        if (!ECDSA_sign (0, (unsigned char*)hash.begin (), hash.size (), pchSig, &nSize, pkey))
            return false;

        vchSig.resize (nSize);
        memcpy (&vchSig[0], pchSig, nSize);

        return true;
    }

    bool Verify (uint256 const& hash, const void* sig, size_t sigLen) const
    {
        // -1 = error, 0 = bad sig, 1 = good
        if (ECDSA_verify (0, hash.begin (), hash.size (), (const unsigned char*) sig, sigLen, pkey) != 1)
            return false;

        return true;
    }

    bool Verify (uint256 const& hash, Blob const& vchSig) const
    {
        return Verify (hash, &vchSig[0], vchSig.size ());
    }

    bool Verify (uint256 const& hash, const std::string& sig) const
    {
        return Verify (hash, sig.data (), sig.size ());
    }

    // ECIES functions. These throw on failure

    // returns a 32-byte secret unique to these two keys. At least one private key must be known.
    void getECIESSecret (CKey& otherKey, uint256& enc_key, uint256& hmac_key);

    // encrypt/decrypt functions with integrity checking.
    // Note that the other side must somehow know what keys to use
    Blob encryptECIES (CKey& otherKey, Blob const& plaintext);
    Blob decryptECIES (CKey& otherKey, Blob const& ciphertext);
};

#endif
// vim:ts=4
