-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for Ed25519 DNSSEC signing from RFC 8080 #458
Conversation
Thank you. This is a useful addition. I'm however torn about the golang.org/x dep that it adds; yes we could vendor, use the new Code looks good btw. |
Unfortunately I don't have an easy solution for the golang.org/x dep. Some things to note though:
It's quite unfortunate that ed25519 isn't yet part of the standard library. I'm happy for this not to merged and just left open for the indefinite future until either ed25519 becomes crypto/ed25519 or some other solution is arrived at. I've really got no idea when ed25519 might become part of the standard library. I guess other possible solutions are to simply import golang.org/x/crypto/ed25519 and consider the sub-repositories to be loosely part of the standard library or to vendor it. Neither is ideal of course. |
I have no problem leaving this PR open until enough stuff sits in the standard lib. |
The example RRSIG records have unbalanced parenthesis. I guess the parsing machinery doesn't explicitly check that it has found the end of an entry? Thanks for doing this. I'm using it for testing another (non-Go) implementation 👍 |
[ Quoting <notifications@github.com> in "Re: [miekg/dns] Add support for Ed2..." ]
The example RRSIG records have unbalanced parenthesis. I guess the parsing machinery doesn't explicitly check that it has found the end of an entry?
Geh, interesting bug. It does know when the RR ends, so by virtue of that we
don't check the parenthesis.
/Miek
…--
Miek Gieben
|
@andrewtj You're right, and it is an interesting bug. I didn't even notice it, principally because there was no parsing error. It's a mistake in RFC 8080 though, and I missed it when I filed the errata. I'm not familiar enough with the DNS record format to know where it should go. You're really more than welcome! I was very much hoping that getting an implementation out there would help quicken adoption of Ed25519! @miekg Could that be handled by simply keeping a counter that is incremented with opening parenthesis and decremented with closing parenthesis, and then checking it's equal to zero at the end of the record? |
[ Quoting <notifications@github.com> in "Re: [miekg/dns] Add support for Ed2..." ]
@andrewtj You're right, and it is an interesting bug. I didn't even notice it, principally because there was no parsing error. It's a mistake in [RFC 8080](https://tools.ietf.org/html/rfc8080) though, and I missed it when I filed the [errata](https://www.rfc-editor.org/errata_search.php?rfc=8080). I'm not familiar enough with the DNS record format to know where it should go. You're really more than welcome! I was very much hoping that getting an implementation out there would help quicken adoption of Ed25519!
oh, nice. Should file an errata ! :)
@miekg Could that be handled by simply keeping a counter that is incremented with opening parenthesis and decremented with closing parenthesis, and then checking it's equal to zero at the end of the record?
(haven't look at the code). Yes, but I believe I'm already doing that.
|
The code check closing braces and errors on that, not superfluous opening braces |
named-checkzones barf: dns_rdata_fromtext: example.org:8: unbalanced parentheses. |
The popularity of the algo's and the fact that we don't support them becomes problematic; we should revisit this and probably start vendoring things. |
My only concern with vendoring is the possibility of missing upstream updates. A Travis job should be able to warn of that happening though. |
@miekg I just resolved the merge conflicts associate with this pull request. I still think you can get away without vendoring in this case as the golang.org/x/crypto/ed25519 package has proved very stable. So far there have been no changes to the API in it's history. Even if it must be vendored, I think that's a very approachable problem. |
Note: The test case from RFC 8080 has been modified to correct the missing final brace, but is otherwise present as-is.
The changes have been rebased onto the master branch and the test failure noted in #458 (comment) has been fixed. I'm not sure if this matches the current coding style as I haven't otherwise changed the code since the 16th of February. |
Codecov Report
@@ Coverage Diff @@
## master #458 +/- ##
==========================================
+ Coverage 58.3% 58.37% +0.06%
==========================================
Files 37 37
Lines 9865 9924 +59
==========================================
+ Hits 5752 5793 +41
- Misses 3064 3072 +8
- Partials 1049 1059 +10
Continue to review full report at Codecov.
|
I'll review this, get this merged; then setup cc @fastest963 we might then also start vendoring the x/net/ stuff and pull out our internal "fork" of it |
dnssec.go
Outdated
h.Write(wire) | ||
switch rr.Algorithm { | ||
case ED25519: | ||
signature, err := sign(k, append(signdata, wire...), crypto.Hash(0), rr.Algorithm) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I haven't read the rfc/draft. Why does this need to be done like this? (some comments maybe?).
What's crypto.Hash(0)? And why can't we do the double h.Write
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@miekg That's not directly related to the draft but to how ed25519 operates. Most signature schemes operate on hashed data while ed25519 performs the hashing internally and thus requires the original message. While there is a variant of ed25519 that uses pre-hashing, RFC 8080 does not use it – very few things do. Basically ed25519 needs to be passed the raw message without any hashing so it's special cased here.
The crypto.Hash(0) is basically used as a placeholder to mean 'not hashed', it's checked in (ed25519.PrivateKey).Sign
.
It might be clearer if I switch this to calling ed25519.Sign
directly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@miekg Scratch calling ed25519.Sign
directly, that would prevent the use of opaque crypto.Signers
. I'll improve the documentation here instead.
dnssec_keyscan.go
Outdated
if len(p1) != 32 { | ||
return nil, ErrPrivKey | ||
} | ||
_, p, err = ed25519.GenerateKey(bytes.NewReader(p1)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The interface in the ed25519 pkgs uses GenerateKey to reconstruct a key? There is no e.g. setBytes equiv?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@miekg Unfortunately the representation of ed25519 private keys in Golang and RFC 8080 differ. RFC 8080 uses only the seed (or the random private half), while Golang uses seed || public-key
– in ed25519 the public key is derived from the seed. ed25519.GenerateKey
uses the first 32-bytes read from rand
(bytes.NewReader(p1)
) as the seed/private-half and then derives the public half from this. I'm using ed25519.GenerateKey
in the same fashion as ED25519_keypair_from_seed
from BoringSSL.
I'll completely agree that this isn't particularly obvious and I can add documentation to this. I'm not sure exactly what that documentation should be though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Something like your comment, maybe somewhat condensed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@miekg I've got something worked out now, but I'm not sure whether to draw the parallel to ED25519_keypair_from_seed.
// ...
//
// This is used in the same manner as |ED25519_keypair_from_seed|
// from BoringSSL.
Thoughts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK. Not sure about referencing a C impl. in the comments makes sense for a Go library though? Did you push your changes?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@miekg That was my thinking too. I've pushed them now, I was just waiting to hear what you thought first.
@@ -166,6 +171,39 @@ func readPrivateKeyECDSA(m map[string]string) (*ecdsa.PrivateKey, error) { | |||
return p, nil | |||
} | |||
|
|||
func readPrivateKeyED25519(m map[string]string) (ed25519.PrivateKey, error) { | |||
var p ed25519.PrivateKey | |||
// TODO: validate that the required flags are present |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we do this TODO here now as well? Or just garbage in garbage out?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@miekg I think there is a compelling argument to be strict from the get go, but at the same time this is consistent with all the other key types so I'd be inclined to leave it. It could be worth making them all stricter in a separate pull request though.
Does anyone know of any other implementations that could be used for interop testing? I would hate for this pull request to land and later find out that it entrenches some mistaken or invalid behaviour. |
It's in the BIND 9.12 RC and I think Knot and PowerDNS have some support too. |
planning to merge, add the dep and using |
* Add support for Ed25519 DNSSEC signing from RFC 8080 Note: The test case from RFC 8080 has been modified to correct the missing final brace, but is otherwise present as-is. * Explain why ed25519 is special cased in (*RRSIG).Sign * Explain use of ed25519.GenerateKey in readPrivateKeyED25519 * Add dep This is PR #458 with the dependency added into it.
Merged as #591. Closing this one. Thanks everybody! |
This was added in golang/crypto@5ba7f63082 and can replace the workaround from miekg#458.
This was added in golang/crypto@5ba7f63082 and can replace the workaround from #458.
This pull request adds support for the Ed25519 algorithm in DNSSEC from RFC 8080. RFC 8080 also specifies the use of Ed448 but there is no standard library implementation of Ed448 so it is not included, this should not be an issue as Ed25519 will always see more use than Ed448.
I'm not sure if this pull request meets the 'Depends only on the standard library.' feature listed in the README. Ed25519 support comes from a Golang sub-repositories, in particular golang.org/x/crypto/ed25519. The sub-repositories are described with:
Note: the examples included in the RFC are invalid and were corrected in RFC Errata Report (4935).