Support Ed25519 keys and signatures:

Recognize a new JSON parameter `key_type` in handlers for wallet_propose
and sign/submit.  In addition to letting the caller to specify either of
secp256k1 or ed25519, its presence prohibits the (now-deprecated) use of
heuristically polymorphic parameters for secret data -- the `passphrase`
parameter to wallet_propose will be not be considered as an encoded seed
value (for which `seed` and `seed_hex` should be used), and the `secret`
parameter to sign and submit will be obsoleted entirely by the same trio
above.

* Use constants instead of literals for JSON parameter names.
* Move KeyType to its own unit and add string conversions.
* RippleAddress
  * Pass the entire message, rather than a hash, to accountPrivateSign()
    and accountPublicVerify().
  * Recognize a 33-byte value beginning with 0xED as an Ed25519 key when
    signing and verifying (for accounts only).
  * Add keyFromSeed() to generate an Ed25519 secret key from a seed.
  * Add getSeedFromRPC() to extract the seed from JSON parameters for an
    RPC call.
  * Add generateKeysFromSeed() to produce a key pair of either type from
    a seed.
* Extend Ledger tests to cover both key types.
This commit is contained in:
Josh Juran
2015-02-26 16:20:16 -08:00
parent 1b46e003c3
commit d082a0696d
18 changed files with 527 additions and 94 deletions

View File

@@ -25,16 +25,40 @@
#include <ripple/crypto/GenerateDeterministicKey.h>
#include <ripple/crypto/RandomNumbers.h>
#include <ripple/crypto/RFC1751.h>
#include <ripple/protocol/JsonFields.h>
#include <ripple/protocol/RippleAddress.h>
#include <ripple/protocol/Serializer.h>
#include <ripple/protocol/RipplePublicKey.h>
#include <beast/unit_test/suite.h>
#include <ed25519-donna/ed25519.h>
#include <openssl/ripemd.h>
#include <openssl/pem.h>
#include <algorithm>
#include <mutex>
namespace ripple {
static
bool isCanonicalEd25519Signature (std::uint8_t const* signature)
{
using std::uint8_t;
// Big-endian `l`, the Ed25519 subgroup order
char const* const order = "\x10\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00"
"\x14\xDE\xF9\xDE\xA2\xF7\x9C\xD6"
"\x58\x12\x63\x1A\x5C\xF5\xD3\xED";
uint8_t const* const l = reinterpret_cast<uint8_t const*> (order);
// Take the second half of signature and byte-reverse it to big-endian.
uint8_t const* S_le = signature + 32;
uint8_t S[32];
std::reverse_copy (S_le, S_le + 32, S);
return std::lexicographical_compare (S, S + 32, l, l + 32);
}
// <-- seed
static uint128 PassPhraseToKey (std::string const& passPhrase)
{
@@ -486,8 +510,28 @@ void RippleAddress::setAccountPublic (RippleAddress const& generator, int seq)
}
bool RippleAddress::accountPublicVerify (
uint256 const& uHash, Blob const& vucSig, ECDSA fullyCanonical) const
Blob const& message, Blob const& vucSig, ECDSA fullyCanonical) const
{
if (vchData.size() == 33 && vchData[0] == 0xED)
{
if (vucSig.size() != 64)
{
return false;
}
uint8_t const* publicKey = &vchData[1];
uint8_t const* signature = &vucSig[0];
if (ed25519_sign_open (message.data(), message.size(), publicKey, signature) != 0)
{
return false;
}
return isCanonicalEd25519Signature (signature);
}
uint256 const uHash = getSHA512Half (message);
return verifySignature (getAccountPublic(), uHash, vucSig, fullyCanonical);
}
@@ -521,7 +565,7 @@ uint256 RippleAddress::getAccountPrivate () const
throw std::runtime_error ("unset source - getAccountPrivate");
case VER_ACCOUNT_PRIVATE:
return uint256 (vchData);
return uint256::fromVoid (vchData.data() + (vchData.size() - 32));
default:
throw std::runtime_error ("bad source: " + std::to_string(nVersion));
@@ -556,15 +600,32 @@ void RippleAddress::setAccountPrivate (
setAccountPrivate (secretKey);
}
bool RippleAddress::accountPrivateSign (uint256 const& uHash, Blob& vucSig) const
Blob RippleAddress::accountPrivateSign (Blob const& message) const
{
vucSig = ECDSASign (uHash, getAccountPrivate());
const bool ok = !vucSig.empty();
if (vchData.size() == 33 && vchData[0] == 0xED)
{
uint8_t const* secretKey = &vchData[1];
ed25519_public_key publicKey;
Blob signature (sizeof (ed25519_signature));
ed25519_publickey (secretKey, publicKey);
ed25519_sign (message.data(), message.size(), secretKey, publicKey, &signature[0]);
assert (isCanonicalEd25519Signature (signature.data()));
return signature;
}
uint256 const uHash = getSHA512Half (message);
Blob result = ECDSASign (uHash, getAccountPrivate());
bool const ok = !result.empty();
CondLog (!ok, lsWARNING, RippleAddress)
<< "accountPrivateSign: Signing failed.";
return ok;
return result;
}
Blob RippleAddress::accountPrivateEncrypt (
@@ -813,4 +874,104 @@ RippleAddress RippleAddress::createSeedGeneric (std::string const& strText)
return naNew;
}
uint256 keyFromSeed (uint128 const& seed)
{
Serializer s;
s.add128 (seed);
uint256 result = s.getSHA512Half();
s.secureErase ();
return result;
}
RippleAddress getSeedFromRPC (Json::Value const& params)
{
// This function is only called when `key_type` is present.
assert (params.isMember (jss::key_type));
bool const has_passphrase = params.isMember (jss::passphrase);
bool const has_seed = params.isMember (jss::seed);
bool const has_seed_hex = params.isMember (jss::seed_hex);
int const n_secrets = has_passphrase + has_seed + has_seed_hex;
if (n_secrets > 1)
{
// `passphrase`, `seed`, and `seed_hex` are mutually exclusive.
return RippleAddress();
}
RippleAddress result;
if (has_seed)
{
std::string const seed = params[jss::seed].asString();
result.setSeed (seed);
}
else if (has_seed_hex)
{
uint128 seed;
std::string const seed_hex = params[jss::seed_hex].asString();
if (seed_hex.size() != 32 || !seed.SetHex (seed_hex, true))
{
return RippleAddress();
}
result.setSeed (seed);
}
else if (has_passphrase)
{
std::string const passphrase = params[jss::passphrase].asString();
// Given `key_type`, `passphrase` is always the passphrase.
uint128 const seed = PassPhraseToKey (passphrase);
result.setSeed (seed);
}
return result;
}
KeyPair generateKeysFromSeed (KeyType type, RippleAddress const& seed)
{
KeyPair result;
if (! seed.isSet())
{
return result;
}
if (type == KeyType::secp256k1)
{
RippleAddress generator = RippleAddress::createGeneratorPublic (seed);
result.secretKey.setAccountPrivate (generator, seed, 0);
result.publicKey.setAccountPublic (generator, 0);
}
else if (type == KeyType::ed25519)
{
uint256 secretkey = keyFromSeed (seed.getSeed());
Blob ed25519_key (33);
ed25519_key[0] = 0xED;
assert (secretkey.size() + 1 == ed25519_key.size());
memcpy (&ed25519_key[1], secretkey.data(), secretkey.size());
result.secretKey.setAccountPrivate (ed25519_key);
ed25519_publickey (secretkey.data(), &ed25519_key[1]);
result.publicKey.setAccountPublic (ed25519_key);
secretkey.zero(); // security erase
}
else
{
assert (false); // not reached
}
return result;
}
} // ripple