Sign manifest with ephemeral and master keys (RIPD-1083)

This commit is contained in:
wilsonianb
2016-02-03 14:28:12 -08:00
committed by Edward Hennis
parent 7d46d153c6
commit b55edfa8f0
15 changed files with 262 additions and 182 deletions

View File

@@ -42,7 +42,8 @@ make_Manifest (std::string s)
auto const opt_spk = get<PublicKey>(st, sfSigningPubKey);
auto const opt_seq = get (st, sfSequence);
auto const opt_sig = get (st, sfSignature);
if (!opt_pk || !opt_spk || !opt_seq || !opt_sig)
auto const opt_msig = get (st, sfMasterSignature);
if (!opt_pk || !opt_spk || !opt_seq || !opt_sig || !opt_msig)
{
return boost::none;
}
@@ -99,7 +100,11 @@ bool Manifest::verify () const
STObject st (sfGeneric);
SerialIter sit (serialized.data (), serialized.size ());
st.set (sit);
return ripple::verify (st, HashPrefix::manifest, masterKey, true);
if (! ripple::verify (st, HashPrefix::manifest, signingKey, true))
return false;
return ripple::verify (
st, HashPrefix::manifest, masterKey, true, sfMasterSignature);
}
uint256 Manifest::hash () const
@@ -127,6 +132,14 @@ Blob Manifest::getSignature () const
return st.getFieldVL (sfSignature);
}
Blob Manifest::getMasterSignature () const
{
STObject st (sfGeneric);
SerialIter sit (serialized.data (), serialized.size ());
st.set (sit);
return st.getFieldVL (sfMasterSignature);
}
bool
ManifestCache::loadValidatorKeys(
Section const& keys,
@@ -415,7 +428,9 @@ void ManifestCache::load (
{
if (!mo->verify())
{
Throw<std::runtime_error> ("Unverifiable manifest in db");
JLOG(journal.warn())
<< "Unverifiable manifest in db";
continue;
}
if (trusted(mo->masterKey) || unl.trusted(mo->masterKey))
@@ -444,13 +459,15 @@ void ManifestCache::save (DatabaseCon& dbCon) const
*db << "DELETE FROM ValidatorManifests";
static const char* const sql =
"INSERT INTO ValidatorManifests (RawData) VALUES (:rawData);";
// soci does not support bulk insertion of blob data
soci::blob rawData(*db);
for (auto const& v : map_)
{
if (!v.second.m)
continue;
// soci does not support bulk insertion of blob data
// Do not reuse blob because manifest ecdsa signatures vary in length
// but blob write length is expected to be >= the last write
soci::blob rawData(*db);
convert (v.second.m->serialized, rawData);
*db << sql,
soci::use (rawData);

View File

@@ -99,6 +99,9 @@ struct Manifest
uint256 hash () const;
bool revoked () const;
Blob getSignature () const;
/// Returns manifest master key signature
Blob getMasterSignature () const;
};
boost::optional<Manifest> make_Manifest(std::string s);

View File

@@ -441,6 +441,7 @@ JSS ( validated_ledger ); // out: NetworkOPs
JSS ( validated_ledgers ); // out: NetworkOPs
JSS ( validation_key ); // out: ValidationCreate, ValidationSeed
JSS ( validation_manifest ); // out: NetworkOPs
JSS ( validation_private_key ); // out: ValidationCreate
JSS ( validation_public_key ); // out: ValidationCreate, ValidationSeed
JSS ( validation_quorum ); // out: NetworkOPs
JSS ( validation_seed ); // out: ValidationCreate, ValidationSeed

View File

@@ -447,6 +447,7 @@ extern SF_Blob const sfCreateCode;
extern SF_Blob const sfMemoType;
extern SF_Blob const sfMemoData;
extern SF_Blob const sfMemoFormat;
extern SF_Blob const sfMasterSignature;
// variable length (uncommon)
extern SF_Blob const sfProof;

View File

@@ -28,23 +28,34 @@
namespace ripple {
/** Sign a STObject using any secret key.
The signature is placed in sfSignature. If
a signature already exists, it is overwritten.
/** Sign an STObject
@param st Object to sign
@param prefix Prefix to insert before serialized object when hashing
@param type Signing key type used to derive public key
@param sk Signing secret key
@param sigField Field in which to store the signature on the object.
If not specified the value defaults to `sfSignature`.
@note If a signature already exists, it is overwritten.
*/
void
sign (STObject& st,
HashPrefix const& prefix,
KeyType type, SecretKey const& sk);
sign (STObject& st, HashPrefix const& prefix,
KeyType type, SecretKey const& sk,
SF_Blob const& sigField = sfSignature);
/** Verify the signature on a STObject.
The signature must be contained in sfSignature.
/** Returns `true` if STObject contains valid signature
@param st Signed object
@param prefix Prefix inserted before serialized object when hashing
@param pk Public key for verifying signature
@param sigField Object's field containing the signature.
If not specified the value defaults to `sfSignature`.
*/
bool
verify (STObject const& st,
HashPrefix const& prefix,
PublicKey const& pk,
bool mustBeFullyCanonical);
verify (STObject const& st, HashPrefix const& prefix,
PublicKey const& pk, bool mustBeFullyCanonical,
SF_Blob const& sigField = sfSignature);
/** Return a Serializer suitable for computing a multisigning TxnSignature. */
Serializer

View File

@@ -199,9 +199,11 @@ SF_Blob const sfMemoType = make::one<SF_Blob::type>(&sfMemoType, STI
SF_Blob const sfMemoData = make::one<SF_Blob::type>(&sfMemoData, STI_VL, 13, "MemoData");
SF_Blob const sfMemoFormat = make::one<SF_Blob::type>(&sfMemoFormat, STI_VL, 14, "MemoFormat");
// variable length (uncommon)
// 16 has not been used yet...
SF_Blob const sfProof = make::one<SF_Blob::type>(&sfProof, STI_VL, 17, "Proof");
SF_Blob const sfProof = make::one<SF_Blob::type>(&sfProof, STI_VL, 17, "Proof");
SF_Blob const sfMasterSignature = make::one<SF_Blob::type>(&sfMasterSignature, STI_VL, 18, "MasterSignature", SField::sMD_Default, SField::notSigning);
// account
SF_Account const sfAccount = make::one<SF_Account::type>(&sfAccount, STI_ACCOUNT, 1, "Account");

View File

@@ -24,22 +24,22 @@ namespace ripple {
void
sign (STObject& st, HashPrefix const& prefix,
KeyType type, SecretKey const& sk)
KeyType type, SecretKey const& sk,
SF_Blob const& sigField)
{
Serializer ss;
ss.add32(prefix);
st.addWithoutSigningFields(ss);
set(st, sfSignature,
set(st, sigField,
sign(type, sk, ss.slice()));
}
bool
verify (STObject const& st,
HashPrefix const& prefix,
PublicKey const& pk,
bool mustBeFullyCanonical)
verify (STObject const& st, HashPrefix const& prefix,
PublicKey const& pk, bool mustBeFullyCanonical,
SF_Blob const& sigField)
{
auto const sig = get(st, sfSignature);
auto const sig = get(st, sigField);
if (! sig)
return false;
Serializer ss;

View File

@@ -52,11 +52,14 @@ Json::Value doValidationCreate (RPC::Context& context)
if (!seed)
return rpcError (rpcBAD_SEED);
auto const private_key = generateSecretKey (KeyType::secp256k1, *seed);
obj[jss::validation_public_key] = toBase58 (
TokenType::TOKEN_NODE_PUBLIC,
derivePublicKey (
KeyType::secp256k1,
generateSecretKey (KeyType::secp256k1, *seed)));
derivePublicKey (KeyType::secp256k1, private_key));
obj[jss::validation_private_key] = toBase58 (
TokenType::TOKEN_NODE_PRIVATE, private_key);
obj[jss::validation_seed] = toBase58 (*seed);
obj[jss::validation_key] = seedAs1751 (*seed);

View File

@@ -102,18 +102,23 @@ public:
Manifest
make_Manifest
(KeyType type, SecretKey const& sk, PublicKey const& spk, int seq,
bool broken = false)
(SecretKey const& sk, KeyType type, SecretKey const& ssk, KeyType stype,
int seq, bool broken = false)
{
auto const pk = derivePublicKey(type, sk);
auto const spk = derivePublicKey(stype, ssk);
STObject st(sfGeneric);
st[sfSequence] = seq;
st[sfPublicKey] = pk;
st[sfSigningPubKey] = spk;
sign(st, HashPrefix::manifest, type, sk);
BEAST_EXPECT(verify(st, HashPrefix::manifest, pk, true));
sign(st, HashPrefix::manifest, stype, ssk);
BEAST_EXPECT(verify(st, HashPrefix::manifest, spk, true));
sign(st, HashPrefix::manifest, type, sk, sfMasterSignature);
BEAST_EXPECT(verify(
st, HashPrefix::manifest, pk, true, sfMasterSignature));
if (broken)
{
@@ -227,7 +232,8 @@ public:
auto const sk = randomSecretKey();
auto const kp = randomKeyPair(KeyType::secp256k1);
auto const m = make_Manifest (KeyType::ed25519, sk, kp.first, 0);
auto const m = make_Manifest (
sk, KeyType::ed25519, kp.second, KeyType::secp256k1, 0);
cache.configManifest (clone (m), *unl, journal);
BEAST_EXPECT(cache.trusted (m.masterKey));
@@ -330,7 +336,8 @@ public:
auto const sk = randomSecretKey();
auto const pk = derivePublicKey(KeyType::ed25519, sk);
auto const kp = randomKeyPair(KeyType::secp256k1);
auto const m = make_Manifest (KeyType::ed25519, sk, kp.first, 0);
auto const m = make_Manifest (
sk, KeyType::ed25519, kp.second, KeyType::secp256k1, 0);
STObject st(sfGeneric);
st[sfSequence] = 0;
@@ -339,9 +346,11 @@ public:
Serializer ss;
ss.add32(HashPrefix::manifest);
st.addWithoutSigningFields(ss);
auto const sig = sign(KeyType::ed25519, sk, ss.slice());
auto const sig = sign(KeyType::secp256k1, kp.second, ss.slice());
BEAST_EXPECT(strHex(sig) == strHex(m.getSignature()));
auto const masterSig = sign(KeyType::ed25519, sk, ss.slice());
BEAST_EXPECT(strHex(masterSig) == strHex(m.getMasterSignature()));
}
void
@@ -360,16 +369,21 @@ public:
auto const sk_a = randomSecretKey();
auto const pk_a = derivePublicKey(KeyType::ed25519, sk_a);
auto const kp_a = randomKeyPair(KeyType::secp256k1);
auto const s_a0 = make_Manifest (KeyType::ed25519, sk_a, kp_a.first, 0);
auto const s_a1 = make_Manifest (KeyType::ed25519, sk_a, kp_a.first, 1);
auto const s_a0 = make_Manifest (
sk_a, KeyType::ed25519, kp_a.second, KeyType::secp256k1, 0);
auto const s_a1 = make_Manifest (
sk_a, KeyType::ed25519, kp_a.second, KeyType::secp256k1, 1);
auto const sk_b = randomSecretKey();
auto const pk_b = derivePublicKey(KeyType::ed25519, sk_b);
auto const kp_b = randomKeyPair(KeyType::secp256k1);
auto const s_b0 = make_Manifest (KeyType::ed25519, sk_b, kp_b.first, 0);
auto const s_b1 = make_Manifest (KeyType::ed25519, sk_b, kp_b.first, 1);
auto const s_b2 =
make_Manifest (KeyType::ed25519, sk_b, kp_b.first, 2, true); // broken
auto const s_b0 = make_Manifest (
sk_b, KeyType::ed25519, kp_b.second, KeyType::secp256k1, 0);
auto const s_b1 = make_Manifest (
sk_b, KeyType::ed25519, kp_b.second, KeyType::secp256k1, 1);
auto const s_b2 = make_Manifest (
sk_b, KeyType::ed25519, kp_b.second, KeyType::secp256k1, 2,
true); // broken
auto const fake = s_b1.serialized + '\0';
BEAST_EXPECT(cache.applyManifest (clone (s_a0), *unl, journal) == untrusted);
@@ -395,7 +409,8 @@ public:
auto const sk_c = randomSecretKey();
auto const pk_c = derivePublicKey(KeyType::ed25519, sk_c);
auto const kp_c = randomKeyPair(KeyType::secp256k1);
auto const s_c0 = make_Manifest (KeyType::ed25519, sk_c, kp_c.first, 0);
auto const s_c0 = make_Manifest (
sk_c, KeyType::ed25519, kp_c.second, KeyType::secp256k1, 0);
BEAST_EXPECT(unl->insertPermanentKey(pk_c, "trusted key"));
BEAST_EXPECT(unl->trusted(pk_c));
BEAST_EXPECT(!cache.trusted(pk_c));

View File

@@ -31,15 +31,15 @@ namespace test {
namespace validator {
static auto const seed = "ss7t3J9dYentEFgKdPA3q6eyxtrLB";
static auto const master_key =
"nHU4LxxrSQsRTKy5uZbX95eYowoamUEPCcWraxoiCNbtDaUr1V34";
"nHUYwQk8AyQ8pW9p4SvrWC2hosvaoii9X54uGLDYGBtEFwWFHsJK";
static auto const signing_key =
"n9LHPLA36SBky1YjbaVEApQQ3s9XcpazCgfAG7jsqBb1ugDAosbm";
// Format manifest string to test trim()
static auto const manifest =
" JAAAAAFxIe2FwblmJwz4pVYXHLJSzSBgIK7mpQuHNQ88CxW\n"
" \tjIN7q4nMhAuUTyasIhvj2KPfNRbmmIBnqNUzidgkKb244eP \n"
"\t794ZpMdkC+8l5n3R/CHP6SAwhYDOaqub0Cs2NjjewBnp1mf\n"
"\t 23rhAzdcjRuWzm0IT12eduZ0DwcF5Ng8rAelaYP1iT93ScE\t \t";
" JAAAAAFxIe2cDLvm5IqpeGFlMTD98HCqv7+GE54anRD/zbvGNYtOsXMhAuUTyasIhvj2KPfN\n"
" \tRbmmIBnqNUzidgkKb244eP794ZpMdkYwRAIgNVq8SYP7js0C/GAGMKVYXiCGUTIL7OKPSBLS \n"
"\t7LTyrL4CIE+s4Tsn/FrrYj0nMEV1Mvf7PMRYCxtEERD3PG/etTJ3cBJAbwWWofHqg9IACoYV\n"
"\t +n9ulZHSVRajo55EkZYw0XUXDw8zcI4gD58suOSLZTG/dXtZp17huIyHgxHbR2YeYjQpCw==\t \t";
static auto sequence = 1;
}