ripple / crypto-conditions

A Java implementation of Crypto-Conditions (draft-thomas-crypto-conditions)
Apache License 2.0
5 stars 12 forks source link

Discuss Fulfillment#verify() and how it should work with prefix conditions... #12

Closed sappenin closed 6 years ago

sappenin commented 6 years ago

I'm unclear on the interplay between the prefix of a PrefixSha256Fulfillment/Condition and the message of its subcondition/fulfillment. I would have expected prefix and message to be able to vary independently, but I'm wondering if they reduce to the same thing, in practice?

As an example, I expected the following code to work (where the message and prefix are non-empty and distinct):

  final String message = "message";
  final String prefix = "prefix";

  // Construct an Ed25519 Fulfillment (which signs "message")
  KeyPair ed25519KeyPair = constructEdDsaKeyPair();
  Signature edDsaSigner = new EdDSAEngine(sha512Digest);
  edDsaSigner.initSign(ed25519KeyPair.getPrivate());
  edDsaSigner.update(message.getBytes());
  byte[] edDsaSignature = edDsaSigner.sign();
  Ed25519Sha256Fulfillment signatureFulfillment = Ed25519Sha256Fulfillment((EdDSAPublicKey) ed25519KeyPair.getPublic(), edDsaSignature);

  // Construct a Prefix Fulfillment
  PrefixSha256Fulfillment prefixFulfillment = new PrefixSha256Fulfillment(
            prefix.getBytes(), 100, signatureFulfillment
   );
  PrefixSha256Condition prefixCondition = prefixFulfillment.getCondition();

  assertTrue(prefixFulfillment.verify(prefixCondition, message.getBytes());  

The assertion in the code above does not pass. In the Java implementation at least, it appears that the prefix for a PrefixSha256Fulfillment must be used as the message for an Ed25519Sha256Fulfillment so that verification can pass. For example, this passes:

  // These must match...
  final String message = "message";
  final String prefix = message;

  // Construct an Ed25519 Fulfillment.
  Ed25519Sha256Fulfillment signatureFulfillment = ... (same as above)

  // Construct a Prefix Fulfillment
  PrefixSha256Fulfillment prefixFulfillment = new PrefixSha256Fulfillment(
            prefix.getBytes(), 100, signatureFulfillment
   );
  PrefixSha256Condition prefixCondition = prefixFulfillment.getCondition();

  // Only works with an empty `message`, and the message equaling the prefix (see above)
  assertTrue(prefixFulfillment.verify(prefixCondition, "".getBytes());  

Either this is a flaw in the Java implementation, or something assumed/omitted in the RFC?

sappenin commented 6 years ago

From @adrianhopebailie, this should work:

  final String message = "message";
  final String prefix = "prefix";

  // Construct an Ed25519 Fulfillment (which signs "message")
  KeyPair ed25519KeyPair = constructEdDsaKeyPair();
  Signature edDsaSigner = new EdDSAEngine(sha512Digest);
  edDsaSigner.initSign(ed25519KeyPair.getPrivate());
  edDsaSigner.update((prefix + message).getBytes());
  byte[] edDsaSignature = edDsaSigner.sign();
  Ed25519Sha256Fulfillment signatureFulfillment = Ed25519Sha256Fulfillment((EdDSAPublicKey) ed25519KeyPair.getPublic(), edDsaSignature);

  // Construct a Prefix Fulfillment
  PrefixSha256Fulfillment prefixFulfillment = new PrefixSha256Fulfillment(
            prefix.getBytes(), 100, signatureFulfillment
   );
  PrefixSha256Condition prefixCondition = prefixFulfillment.getCondition();

  assertTrue(prefixFulfillment.verify(prefixCondition, message.getBytes()); 
sappenin commented 6 years ago

See PR here: https://github.com/ripple/crypto-conditions/pull/16

sappenin commented 6 years ago

One more important piece of information here. The [RFC] explicitely says:

Implementations [of PREFIX-SHA-256] MUST prepend the prefix to the provided message and will use the resulting value as the message to validate the sub-fulfillment.

Therefore, whenever one creates an Ed25519 or RSA condition that will be used inside of a prefix condition, the signature must have signed the concatenation of prefix and message when constructing the sub-fulfillment.