Open johannww opened 1 month ago
When this API was designed, it seemed to make a lot of sense for the hash and signing implementations to be decoupled:
In hindsight it seems less obvious that it's the right choice, particularly as we now support Ed25519 where (in most implementations) you must pass the full message to the signing implementation. Perhaps it would have been simpler to just provide the signable message bytes and require the signing implementation (or off-line signer) to do any required hashing. In some cases it might be slightly more work for the end user, but it would avoid a lot of confusion.
Another alternative to help people avoid mistakes would be to remove the default hash implementation and require the hash to be explicitly specified, just like the signer is already.
Both of these options are breaking changes in the API. I am open to moving up a major version and implementing breaking changes, but it needs careful thought to make sure the benefits are worth the disruption.
On your proposed change, I like the idea in principle. I'm worried about the execution though. We need to maintain consistent behaviour across Go, Node and Java implementations. The Go standard library is the (only?) obvious choice for crypto so there are fewer opportunities for confusion. There are more choices when it comes to Java crypto implementation, and much more choice and variation in behaviour for Node.
I'm worried that we might not be able to reliably provide the correct defaults for all client languages. I'm also worried that having a different default hash depending on the key type is another potential source of confusion.
I did add a fair bit of explanation of the relationship between hash and signing implementation to the API documentation. I understand it's probably easy to miss though. I wonder if just having some samples that use Ed25519 (in the fabric-samples repo) for people to get started with might be enough to avoid errors. What do you think?
I think you should wait for more complaints before implementing breaking changes. Fabric v3.0.0 was just released, and people might face (or not) this type of problem. However, I have more considerations.
About choosing hash functions, according to my tests with fabric-gateway and fabric (both 2.5.9 and v3.0.0), the BCCSP hash function is determined by the "core.peer.bccsp.sw" section:
SW:
# TODO: The default Hash and Security level needs refactoring to be
# fully configurable. Changing these defaults requires coordination
# SHA2 is hardcoded in several places, not only BCCSP
Hash: SHA2
Security: 256
Even though there is a TODO, currently, the hash function is determined at an organization level when it joins the channel, right? Therefore, the gateway has no option to choose. It must follow the MSP.
Currently, due to fabric's go implementation, the other gateways (node and java) must use SHA512 to sign with ed25519 keys since the standard library implementation does it: (edit: afterall, it follows the specification)
func sign(signature, privateKey, message []byte) {
if l := len(privateKey); l != PrivateKeySize {
panic("ed25519: bad private key length: " + strconv.Itoa(l))
}
...
mh := sha512.New()
mh.Write(prefix)
mh.Write(message)
So, I think we are in a state where the need for changes is recognized, and the gateway is ahead of fabric, in the sense of modular hash functions. Pragmatically, there is no reason to let the user choose the hash function for ed25519 keys, but perhaps this issue is not a priority right now.
Following up with my pull request that implemented the support for ed25519 keys in fabric, I had trouble with signing transactions with such keys.
I believe the problem comes from ./pkg/client/transactions.go and ./pkg/client/signingidentity.go:
and
In Go, ed25519 sign functions receive the message as parameter, not the digest.
PS: I realized that it is possible to pass the function hash.NONE to the gateway, and it solved my problem. However, I still think that the gateway should handle this internally, but I wait on your further analysis.
I would suggest the following changes to ./client/signingidentity.go: