Skip to content
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

Closed
wants to merge 3 commits into from
Closed

Add support for Ed25519 DNSSEC signing from RFC 8080 #458

wants to merge 3 commits into from

Conversation

tmthrgd
Copy link
Collaborator

@tmthrgd tmthrgd commented Feb 16, 2017

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:

These packages are part of the Go Project but outside the main Go tree. They are developed under looser compatibility requirements than the Go core. Install them with "go get".

Note: the examples included in the RFC are invalid and were corrected in RFC Errata Report (4935).

@miekg
Copy link
Owner

miekg commented Feb 16, 2017

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 dep, but it has been something we managed to avoid. I think for that reason alone merging this would be impossible. Is there any chance this code will move into the std lib?

Code looks good btw.

@tmthrgd
Copy link
Collaborator Author

tmthrgd commented Feb 16, 2017

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.

@miekg
Copy link
Owner

miekg commented Feb 17, 2017

I have no problem leaving this PR open until enough stuff sits in the standard lib.

@andrewtj
Copy link
Collaborator

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 👍

@miekg
Copy link
Owner

miekg commented May 17, 2017 via email

@tmthrgd
Copy link
Collaborator Author

tmthrgd commented May 17, 2017

@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?

@miekg
Copy link
Owner

miekg commented May 17, 2017 via email

@miekg
Copy link
Owner

miekg commented May 19, 2017

The code check closing braces and errors on that, not superfluous opening braces

@miekg
Copy link
Owner

miekg commented May 19, 2017

named-checkzones barf: dns_rdata_fromtext: example.org:8: unbalanced parentheses.
#488

@miekg
Copy link
Owner

miekg commented Nov 15, 2017

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.

@andrewtj
Copy link
Collaborator

My only concern with vendoring is the possibility of missing upstream updates. A Travis job should be able to warn of that happening though.

@tmthrgd
Copy link
Collaborator Author

tmthrgd commented Nov 15, 2017

@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.
@tmthrgd
Copy link
Collaborator Author

tmthrgd commented Nov 15, 2017

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-io
Copy link

codecov-io commented Nov 15, 2017

Codecov Report

Merging #458 into master will increase coverage by 0.06%.
The diff coverage is 63.63%.

Impacted file tree graph

@@            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
Impacted Files Coverage Δ
dnssec_privkey.go 96.77% <100%> (+0.28%) ⬆️
dnssec_keygen.go 66.96% <57.14%> (-1.41%) ⬇️
dnssec_keyscan.go 69.35% <58.82%> (-1.06%) ⬇️
dnssec.go 61.42% <63.33%> (+0.48%) ⬆️
ztypes.go 41.92% <0%> (+0.32%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 9fc4eb2...538facf. Read the comment docs.

@miekg
Copy link
Owner

miekg commented Nov 15, 2017

My only concern with vendoring is the possibility of missing upstream updates. A Travis job should be able to warn of that happening though.
Some kind of signal we be good, yes.

I'll review this, get this merged; then setup godep to vendor this.

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)
Copy link
Owner

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?

Copy link
Collaborator Author

@tmthrgd tmthrgd Nov 15, 2017

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.

Copy link
Collaborator Author

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.

if len(p1) != 32 {
return nil, ErrPrivKey
}
_, p, err = ed25519.GenerateKey(bytes.NewReader(p1))
Copy link
Owner

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?

Copy link
Collaborator Author

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.

Copy link
Owner

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?

Copy link
Collaborator Author

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?

Copy link
Owner

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?

Copy link
Collaborator Author

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
Copy link
Owner

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?

Copy link
Collaborator Author

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.

@tmthrgd
Copy link
Collaborator Author

tmthrgd commented Nov 16, 2017

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.

@andrewtj
Copy link
Collaborator

It's in the BIND 9.12 RC and I think Knot and PowerDNS have some support too.

@miekg
Copy link
Owner

miekg commented Nov 27, 2017

planning to merge, add the dep and using dep today-ish

miekg added a commit that referenced this pull request Nov 27, 2017
This is PR #458 with the dependency added into it.
@miekg miekg mentioned this pull request Nov 27, 2017
miekg added a commit that referenced this pull request Nov 27, 2017
* 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.
@miekg
Copy link
Owner

miekg commented Nov 27, 2017

Merged as #591.

Closing this one. Thanks everybody!

@miekg miekg closed this Nov 27, 2017
@tmthrgd tmthrgd deleted the eddsa_dnssec branch November 27, 2017 11:07
tmthrgd added a commit to tmthrgd/miekgdns that referenced this pull request Jul 24, 2018
This was added in golang/crypto@5ba7f63082 and can replace the
workaround from miekg#458.
miekg pushed a commit that referenced this pull request Jul 25, 2018
This was added in golang/crypto@5ba7f63082 and can replace the
workaround from #458.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants