Re: Please review EdDSA API
On 7/26/2018 5:05 PM, Michael StJohns wrote: The test vectors will not pass, because they are calling the byte array from which the private key and the signing value are derived as the private key. However, each and every signature generated by the above approach (e.g. using a *real* private key and a signing value downstream derived from that private key) *will* verify, and each and every signature by that private key over the same data using the above approach will produce identical signatures. I've stated in the JEP[1] that the goal of this effort is an implementation of EdDSA as described in the RFC. What you are proposing is a slightly different key generation and signing procedure. The fact that the signatures will still verify is not sufficient to convince me that the procedures that you are proposing offer the same security as the ones in the RFC. I understand that you don't like the fact that I am representing the private key value as a byte array instead of an integer. If you can come up with an alternative representation that still allows the same functions that are specified in the RFC, then I will consider it. [1] https://bugs.openjdk.java.net/browse/JDK-8199231
Re: Please review EdDSA API
On 7/26/2018 4:24 PM, Adam Petcher wrote: On 7/26/2018 3:58 PM, Michael StJohns wrote: On 7/25/2018 2:05 PM, Adam Petcher wrote: Did you mean PrivateKey ::= OctetToInteger(random)? Setting/clearing bits here destroys information. If we don't prune here, then we can reverse this operation later to get the byte array back to give to the hash. No - I meant what I wrote: 1) generate a private key from a random and store it as a big integer. E.g. generate a byte array of the appropriate length (32), twiddle the bits as described in step 2 of section 5.1.5 of RFC8032 and - interpreting that buffer as a little-endian encoding, save 's' (the secret scalar - aka - the actual private key) as a BigInteger. That's the limit of what goes into the PrivateKey spec/interface. 2) When you do a signing, do SigningValue = HASH(IntegerToLittleEndianOctets(s)). 3) When done with signing, throw away the hash value - it doesn't need to be stored. Does this produce the same result as the signing function described in sections 3.2 and 3.3 of the RFC? If I do as you suggest, will the test vectors in Section 7 pass? It's not obvious to me that the signing procedure that you are proposing is the same function. Note that the signing value (e.g. prefix) is used as part of the formation of 'r' in signing, but is not recoverable from the signature. s is calculated from whatever value of r you get and the two taken together (r,s) form the actual signature. Note that 'prefix' could be a random value if you wanted non-deterministic signatures, but the inclusion of a fixed prefix value means that the same signature will be generated by the same private key over the same data. The test vectors will not pass, because they are calling the byte array from which the private key and the signing value are derived as the private key. However, each and every signature generated by the above approach (e.g. using a *real* private key and a signing value downstream derived from that private key) *will* verify, and each and every signature by that private key over the same data using the above approach will produce identical signatures. Mike
Re: Please review EdDSA API
On 7/26/2018 3:58 PM, Michael StJohns wrote: On 7/25/2018 2:05 PM, Adam Petcher wrote: Did you mean PrivateKey ::= OctetToInteger(random)? Setting/clearing bits here destroys information. If we don't prune here, then we can reverse this operation later to get the byte array back to give to the hash. No - I meant what I wrote: 1) generate a private key from a random and store it as a big integer. E.g. generate a byte array of the appropriate length (32), twiddle the bits as described in step 2 of section 5.1.5 of RFC8032 and - interpreting that buffer as a little-endian encoding, save 's' (the secret scalar - aka - the actual private key) as a BigInteger. That's the limit of what goes into the PrivateKey spec/interface. 2) When you do a signing, do SigningValue = HASH(IntegerToLittleEndianOctets(s)). 3) When done with signing, throw away the hash value - it doesn't need to be stored. Does this produce the same result as the signing function described in sections 3.2 and 3.3 of the RFC? If I do as you suggest, will the test vectors in Section 7 pass? It's not obvious to me that the signing procedure that you are proposing is the same function.
Re: Please review EdDSA API
On 7/25/2018 2:05 PM, Adam Petcher wrote: On 7/25/2018 11:24 AM, Michael StJohns wrote: *sigh* Private keys are big integers. There's an associated parameter used in signing that the implementation described in the RFC (*not a standard please note*) generates from a common random byte array - that byte array is NOT a (or the) private key. E.g. Private key ::= OctetToInteger(Adjust(Left (HASH(random), length))) and SigningValue ::= Right(HASH(random),length). Instead, you can get the exact same result (deterministic signatures) - and store a bog standard EC private key - by PrivateKey ::= OctetToInteger(Adjust(random)); Did you mean PrivateKey ::= OctetToInteger(random)? Setting/clearing bits here destroys information. If we don't prune here, then we can reverse this operation later to get the byte array back to give to the hash. No - I meant what I wrote: 1) generate a private key from a random and store it as a big integer. E.g. generate a byte array of the appropriate length (32), twiddle the bits as described in step 2 of section 5.1.5 of RFC8032 and - interpreting that buffer as a little-endian encoding, save 's' (the secret scalar - aka - the actual private key) as a BigInteger. That's the limit of what goes into the PrivateKey spec/interface. 2) When you do a signing, do SigningValue = HASH(IntegerToLittleEndianOctets(s)). 3) When done with signing, throw away the hash value - it doesn't need to be stored. The *only* important characteristics of the signing value are a) it's confidential, and b) it's the same for each signature. It could even be a random value generated and stored at the same time as the private key - but why? SigningValue ::= HASH (IntegerToOctet(PrivateKey)); // signing value may be regenerated at any time and need not be stored in the ECPrivateKey class. With the modification above, I agree that this would give the value that can be split in half to produce the scalar value (after pruning and interpreting as an integer) and the prefix that is used in signing. I think there may be some issues with this approach, but we need to start by agreeing on what you are proposing. Can you confirm that my understanding of your proposal is correct, or else clarify it for me? What I'm trying to get to is make the java interfaces for EC private keys consistent regardless of which of the curve flavor you want to use. In each and every case, looking behind the specified external representation, the scalar 's' can be represented as a BigInteger. Internal representation IN A SPECIFIC IMPLEMENTATION might use little endian internally, but that's irrelevant here for the purposes of java interfaces. (At least in my opinion). Later, Mike
Re: Please review EdDSA API
On 7/25/2018 11:24 AM, Michael StJohns wrote: *sigh* Private keys are big integers. There's an associated parameter used in signing that the implementation described in the RFC (*not a standard please note*) generates from a common random byte array - that byte array is NOT a (or the) private key. E.g. Private key ::= OctetToInteger(Adjust(Left (HASH(random), length))) and SigningValue ::= Right(HASH(random),length). Instead, you can get the exact same result (deterministic signatures) - and store a bog standard EC private key - by PrivateKey ::= OctetToInteger(Adjust(random)); Did you mean PrivateKey ::= OctetToInteger(random)? Setting/clearing bits here destroys information. If we don't prune here, then we can reverse this operation later to get the byte array back to give to the hash. SigningValue ::= HASH (IntegerToOctet(PrivateKey)); // signing value may be regenerated at any time and need not be stored in the ECPrivateKey class. With the modification above, I agree that this would give the value that can be split in half to produce the scalar value (after pruning and interpreting as an integer) and the prefix that is used in signing. I think there may be some issues with this approach, but we need to start by agreeing on what you are proposing. Can you confirm that my understanding of your proposal is correct, or else clarify it for me?
Re: Please review EdDSA API
On 7/25/2018 10:29 AM, Adam Petcher wrote: 2) Similar to X25519/X448, private keys are byte arrays, and public keys coordinates. Though we can't get by with a single BigInteger coordinate for EdDSA, so I am using the new EdPoint class to hold the coordinates. *sigh* Private keys are big integers. There's an associated parameter used in signing that the implementation described in the RFC (*not a standard please note*) generates from a common random byte array - that byte array is NOT a (or the) private key. E.g. Private key ::= OctetToInteger(Adjust(Left (HASH(random), length))) and SigningValue ::= Right(HASH(random),length). Instead, you can get the exact same result (deterministic signatures) - and store a bog standard EC private key - by PrivateKey ::= OctetToInteger(Adjust(random)); SigningValue ::= HASH (IntegerToOctet(PrivateKey)); // signing value may be regenerated at any time and need not be stored in the ECPrivateKey class. Adjust twiddles the bits in the byte array to make the byte array a valid little-endian private key before encoding for this set of curves. OctetToInteger turns that byte array into a BigInteger. At this point in time, you've got the correct values to do your point math using common libraries if you don't happen to have implemented exactly what's in the RFC. I know the above is a losing argument, but seriously, do we really need two new groups of EC classes simply because someone wrote an RFC that didn't consider existing representational structures? Or will it be three or more? Later, Mike
Re: Please review EdDSA API
+core-libs-dev for additional API expertise. On 7/25/2018 10:29 AM, Adam Petcher wrote: The draft CSR[1] for the EdDSA API[2] is ready for review. Please take a look and send me any feedback you may have. Here are a few high-level notes to explain the API: 1) Where possible, this API is similar to the API for X25519/X448. To get the complete background/motivation for the API design, you can review the discussion[3] on this topic. 2) Similar to X25519/X448, private keys are byte arrays, and public keys coordinates. Though we can't get by with a single BigInteger coordinate for EdDSA, so I am using the new EdPoint class to hold the coordinates. 3) EdDSA has multiple signature modes defined in the RFC[4], including some that "prehash" the input before signing. The draft API uses the EdDSAParameterSpec class to specify parameters of these modes. The standard does not allow an arbitrary choice of prehash function, so the API for EdDSA does not support algorithm names like "SHA256withEdDSA". [1] https://wiki.openjdk.java.net/display/csr/Main [2] https://bugs.openjdk.java.net/browse/JDK-8190219 [3] http://mail.openjdk.java.net/pipermail/security-dev/2017-September/016325.html [4] https://tools.ietf.org/html/rfc8032