Subject: Re: [Dcrup] ed25519 in DNS

From: Viktor Dukhovni <[email-masked]>
Date: Mon, 2 Apr 2018 17:33:11 -0400

If ed25519/ed448 were the *only* key types supported by DKIM in the past,
present and future, I'd gladly support a raw key format.  That, however,
is not the case.  However simple it might be to explicitly use ed25519
keys in various APIs, the fact is that typically one just wants to import
*whatever* key type is published in a *generic* way, that just yields an
opaque public key object that the application library can use for verification.

One should not have to write new code to make use of these keys, and indeed
none would be needed if the were in SPKI, which simply involves prepending
~12 fixed bytes to the front of the raw key, but substantially simplifies
the use of the key in a generic way.

Code that generates keys for various algorithms already supports producing
SPKI output, tools exist that can use the key in a generic way to check
signatures, ...

The desire to "simplify" by getting rid of ASN.1 is IMHO misplaced in
the context of an application that supports keys for a range of
algorithms, and where new algorithms might be added in the future.

The use of DER here requires no complex ASN.1 parsers, parsing DER
is simple, and generating DER for publishing in DNS is something
administrators already know how to do with RSA, they have command-line
tools for this already.

So, for me the obvious, simple and interoperable choice is SPKI.

-- 
	Viktor.

---

From: Scott Kitterman <[email-masked]>
Date: Mon, 02 Apr 2018 18:56:44 -0400

> So, for me the obvious, simple and interoperable choice is SPKI.

I struggled with this for some time and I think I finally understand it.  It 
assumes all key types will be supported by a single crypto library, which 
isn't a good assumption for all implementers and all algorithms.  One person's 
simplifying assumption is another person's added layer of complexity.

Having implemented this, the difference is (using the public key retrieval 
side of the equation as an example):

RSA:

Base64 decode -> ASN.1 decode (RSA) -> RSA public key object

ed25519:

Base64 decode -> ed25519 public key object

Even if we make this change and change it to:

ed25519:

Base64 decode -> ASN.1 decode (ed25519) -> ed25519 public key object

there is still a need to know how to handle the ed25519 key.  That's 
approximately 100% of the code difference and because of the k=ed25519 tag in 
the key record, you know before you start it's not an RSA key.  I understand 
that for the design approach you're considering, that's all exported to an 
external library, so not a concern, but that isn't always going to be the 
case.

Conveniently, in DKIM we will already know because of the k= tag in the DNS 
record, so we don't have to go down that path.

If you make the assumption that everything after Base64 decoding is someone 
else's problem (i.e. a single crypto library), then I see your point.  That 
assumption wasn't true when I implemented it in Python.  I don't think 
implementation specific assumptions, which is how I interpret your proposal, 
are the best basis for IETF protocol design.

If this made general sense, I think it would have been in RFC 8032 and not in 
an X.509 specific document like draft-ietf-curdle-pkix.

If you consider that an implementation may need to use different libraries for 
different crypto, does that change your view any?

Scott K

---

From: denis bider <[email-masked]>
Date: Mon, 2 Apr 2018 18:19:30 -0500

> One should not have to write new code to make use of these keys,

I don't know how you use the particular crypto library that you use.
However, in the libraries I've used, you definitely need to tell the
library what type of key it is. Which means you need to have special case
handling for each type of key (there are so many types here, RSA and
Ed25519) and call the appropriate library functions to import that key.

If you are instead using a library which can decide for you what kind of
key it is, and you literally want to write ZERO new code to support a new
key, then it seems to me you would be failing to check for a situation
where the DKIM algorithm says the key is of one type (e.g. RSA), but the
actual key is another (e.g. Ed25519).

If you do this check, WHICH YOU SHOULD, then you will have an "if"
statement. In pseudo code, this "if" statement might look something like
this:

    algorithm = determine_algorithm_from_dkim_parameters(dkim_input)

    if (algorithm == rsa_algorithm)
        key = crypto_library_load_key(dkim_key_value)
        if (key.algorithm != rsa_algorithm)
            error "unexpected key type";

    else if (algorithm == ed25519_algorithm)
        key = crypto_library_load_key(dkim_key_value)
        if (key.algorithm != ed25519_algorithm)
            error "unexpected key type";

Now, if your library desperately, unconditionally needs the key to be ASN.1
decorated, then the above pseudo code becomes HORRIBLY MORE COMPLEX as
follows:

    algorithm = determine_algorithm_from_dkim_parameters(dkim_input)

    if (algorithm == rsa_algorithm)
        key = crypto_library_load_key(dkim_key_value)
        if (key.algorithm != rsa_algorithm)
            error "unexpected key type";

    else if (algorithm == ed25519_algorithm)
        asn1_key = fixed_asn1_prefix + dkim_key_value    // Very tricky
part here! Complex string concatenation!
        key = crypto_library_load_key(asn1_key)
        if (key.algorithm != ed25519_algorithm)
            error "unexpected key type";

This whole thread is basically about people not wanting to do string
concatenation inside an "if" clause, and wanting other people to pad their
keys with the string you would otherwise want to prefix. And also for
people to write ASN.1 parsers because you are using one.

Because, you know, string concatenation is hard.

On Mon, Apr 2, 2018 at 4:33 PM, Viktor Dukhovni <[email-masked]>
wrote:

> _______________________________________________
> Dcrup mailing list
> [email-masked]
> https://www.ietf.org/mailman/listinfo/dcrup
>

---

From: Jeremy Harris <[email-masked]>
Date: Tue, 3 Apr 2018 00:33:57 +0100

On 02/04/18 23:56, Scott Kitterman wrote:
> assumption wasn't true when I implemented it in Python.

I think it isn't true, most likely, for any DKIM implementation.

Because of the difference is hashing requirements of RSA and ED25519
in DKIM - specifically, with RSA and the convenience of incremental
hashing followed by pure RSA signing/verification, the coding sequence
could be very different from that imposed by the Ed25519 separate
hash plus HashSign/HashVerify.

Now it turns out that the implementation I ended up with needs pretty
localised change to handle the proposal to relitigate the design aspect
of the pubkey representation.  I would still have to rip into bits of
the documentation where I've tried to clarify the differences.
I'm fairly much on the fence as to which way is better; mostly I
don't care.  But I do have a release coming up and unless the issue
is resolved pronto I get to either release as-is (bare key, base64'd)
or rip the whole lot out.

I do confess to being quite surprised that the two libraries I
deal with (OpenSSL, GnuTLS) do not see fit to provide utility-level
access to the bare key value.  Having to skip a magic number of bytes
into a binary object feels _fragile_.  But that is mostly tangential to
the issue.
-- 
Jeremy

---

From: James Cloos <[email-masked]>
Date: Mon, 02 Apr 2018 19:52:23 -0400

>>>>> "VD" == Viktor Dukhovni <[email-masked]> writes:

VD> If ed25519/ed448 were the *only* key types supported by DKIM in the past,
VD> present and future, I'd gladly support a raw key format.  That, however,
VD> is not the case.

That is irrelevant.  Ed25519 should always and only be exchanged as 256 bit
strings.  Whether in binary or some text encoding.  But always just the key.

That is the whole point.

-JimC
-- 
James Cloos <[email-masked]>         OpenPGP: 0x997A9F17ED7DAEA6

---

From: denis bider <[email-masked]>
Date: Mon, 2 Apr 2018 19:00:48 -0500

> I do confess to being quite surprised that the two libraries
> I deal with (OpenSSL, GnuTLS) do not see fit to provide
> utility-level access to the bare key value.  Having to skip
> a magic number of bytes into a binary object feels _fragile_.
> But that is mostly tangential to the issue.

It seems to me that is, actually, pretty much *the* issue.

Current interfaces of these libraries make it awkward to deal with bare
keys, so folks would like to design the internet around the current
interfaces of these libraries.

But the interfaces of these libraries can change tomorrow if users argue
with maintainers that easier access to the bare keys is needed.

Meanwhile, if we design the internet around the current interfaces, we will
have to live with that for 15 years.

And even with current interfaces, workarounds exist. On encoding, drop the
first N bytes. On decoding, prepend fixed prefix. The encoding rules are
DER, so although it looks hacky, it's not fragile. The prefix will not
change.

---

From: "John Levine" <[email-masked]>
Date: 2 Apr 2018 21:43:31 -0400

>opaque public key object that the application library can use for verification.

I'm with Scott.  Whatever you're describing is not the library I'm
using.  

Since the signatures and keys both identify the algorithm, the code
already knows what algorithm it's using, and there's no reason to
assume the RSA and ed25519 routines are in the same library or have
the same interface.

R's,
John

---

From: Viktor Dukhovni <[email-masked]>
Date: Mon, 2 Apr 2018 22:26:35 -0400

> On Apr 2, 2018, at 7:52 PM, James Cloos <[email-masked]> wrote:
> 

Naturally, for ed25519/ed448 the two approaches are isomorphic under adding
or removing a fixed prefix, that (at least for these algorithms) is a function
of the algorithm name.  The same isomorphism is not possible with RSA, because
RSA keys come in different sizes and an additionally encoded exponent.

So for some key types (e.g. RSA) a flexible encoding is required.  A DKIM
verifier should not IMHO use algorithm-spefic crypto libraries, it should
use a library that supports multiple key types, and provide a uniform
signature verification interface.  That way, when a new key type is
introduced, you just pass the key data to the library and if the library
supports the new key type, the DKIM verifier requires no code changes.

Sure, if the idea is to explicitly play "ed25519" then sure, one can publish
ed25519-specific key formats, but that seems like poor long-term design to
me.  The application code is more maintainable if all the verification and
key import functions are farmed out to a crypto library that adds new key
types as time goes by, without the DKIM layer needing to co-evolve and call
new APIs.

Since the algorithms in question will also be used in certificates and with
CMS, they will already have SPKI forms.  Inventing yet another form is not
doing anyone a favour: https://xkcd.com/927/

-- 
	Viktor.

---

From: Viktor Dukhovni <[email-masked]>
Date: Mon, 2 Apr 2018 22:32:15 -0400

> On Apr 2, 2018, at 9:43 PM, John Levine <[email-masked]> wrote:
> 

Except that of course any crypto library that has not been abandoned
will support an algorithm independent API for handling SPKI keys and
verification, as well as PKCS8 keys and signing.  Having to bring in
algorithm-specific APIs looks wasteful from my POV.

I guess if I ever have to write a DKIM verifier, I'll have to remember
to add a non-empty prefix for ed25519 and not do it for RSA, and then
call the generic routines, and of course we're bikeshedding to some
extent, all these approaches work, but it sure seems odd to go out
of one's way to break the abstraction.

-- 
-- 
	Viktor.

---

From: denis bider <[email-masked]>
Date: Mon, 2 Apr 2018 22:33:12 -0500

Crypto++ is not abandoned and does not support such an
algorithm-independent API.

Windows cryptography is not abandoned and does not support such an
algorithm-independent API.

You are literally generalizing from one or two libraries that you are used
to which I have not ever used (from code) in 20 years.

---

From: denis bider <[email-masked]>
Date: Mon, 2 Apr 2018 22:41:34 -0500

> the two approaches are isomorphic under
> adding or removing a fixed prefix

It's not isomorphic, though, because the idea is that you DER-encode, but
BER-decode.

There is only one DER-encoded ASN.1 prefix for the fixed-size 32-byte
Ed25519 key. However, there are an infinite number of BER-encoded prefixes.

The encoder that needs to deal with the ASN.1 library can just drop the
first N bytes if it knows those bytes are DER-encoded. This is a known
implementation detail.

But a decoder that is receiving keys from across the internet cannot know
if they are DER-encoded or BER-encoded. Therefore at least a partial ASN.1
parser needs to be implemented for the types used in the prefix encoding.

---

From: denis bider <[email-masked]>
Date: Mon, 2 Apr 2018 22:59:43 -0500

I propose a good solution is to modify the draft as follows:

1. Keep the 32-byte Ed25519 key encoding.

2. Add a section like the following:

"*Use with ASN.1 libraries*

At the time of this writing, there exist popular cryptographic libraries
which do not provide an easy way to import or export a 32-byte Ed25519 key.
However, these libraries provide ways to import and export keys with ASN.1
wrapping.

To import a 32-byte Ed25519 key into such a library, it is sufficient to
prefix the key as follows:

    ASN.1 wrapped key = DER prefix | Ed25519 key
    DER prefix =  "\x30\x..."

To convert an exported ASN.1 wrapped key into a 32-byte Ed25519 key, it is
sufficient to remove everything before the last 32 bytes of the key."

---

From: Viktor Dukhovni <[email-masked]>
Date: Tue, 3 Apr 2018 00:35:30 -0400

> On Apr 2, 2018, at 11:41 PM, denis bider <[email-masked]> wrote:
> 
> It's not isomorphic, though, because the idea is that you DER-encode, but BER-decode.
> 
> There is only one DER-encoded ASN.1 prefix for the fixed-size 32-byte Ed25519 key. However, there are an infinite number of BER-encoded prefixes.
> 
> The encoder that needs to deal with the ASN.1 library can just drop the first N bytes if it knows those bytes are DER-encoded. This is a known implementation detail.
> 
> But a decoder that is receiving keys from across the internet cannot know if they are DER-encoded or BER-encoded. Therefore at least a partial ASN.1 parser needs to be implemented for the types used in the prefix encoding.

IIRC SPKI is DER encoded by definition, but if I misremembered, then this specification
can require DER encoding.  I've never seen BER encoded SPKI, and indeed, for example,
DANE requires DER-encoded SPKI values as input for TLSA records.

-- 
	Viktor.

---

From: "John Levine" <[email-masked]>
Date: 3 Apr 2018 00:35:57 -0400

In article <[email-masked]> you write:
>signature verification interface.

Well, that's one theory.

> That way, when a new key type is

Given that it took us a decade to introduce a new key type, and we
have no plans ever to do it again since the required double signing
transition is such a pain, this looks to me like a severely premature
optimization, even ignoring the fact that it'd need code changes
anyway to handle the new tag values in signatures and key records.
This isn't TLS/SSL with pages of algorithms and interactive
negotiation about which one to use.

R's,
John

---

From: Viktor Dukhovni <[email-masked]>
Date: Tue, 3 Apr 2018 01:09:09 -0400

> On Apr 3, 2018, at 12:35 AM, John Levine <[email-masked]> wrote:
> 

Ever again is a long time.  Though not universally anticipated, scalable
universal quantum computers might require yet another new set of signature
algorithms.  The cost of the extra twelve bytes up front is rather modest,
and this retains compatibility with a widely used general-purpose public-key
format.  So it seems prudent, but of course not critical.  If the WG strongly
feels that Ed25519 needs to be "special", so be it, but it seems needlessly
different to me...

-- 
	Viktor.

---

From: Alessandro Vesely <[email-masked]>
Date: Tue, 3 Apr 2018 11:56:00 +0200

> supports the new key type, the DKIM verifier requires no code changes.

If that were true, the k= tag would be totally useless.  The generic library
used by the DKIM verifier can readily recognize what kind of key and signature
are being used.  Indeed, k= is OPTIONAL.  Its raison d'être is to provide for
future extensions without making assumptions on the syntax and semantics of p=.

Because of that design, I don't believe a DKIM implementation will ever exist
which can handle a new signature type with no code changes, whatever the
enhancements of its underlying library functions.

> doing anyone a favour: https://xkcd.com/927/

DKIM already did so, by getting rid of a number of pleonastic PKI features on
key management.  IMHO, those simplifications are part of the reasons why we
currently find and verify more DKIM than S/MIME or PGP signatures.

I don't understand what you mean by "SPKI".  I only found a past century WG:

https://datatracker.ietf.org/wg/spki/about/

And then, the only mention of ASN.1 in the two experimental RFCs associated to
that WG is the following snippet:

   No library code should be required for the packing or parsing of SPKI
   certificates.  In particular, ASN.1 is not to be used.
                              https://tools.ietf.org/html/rfc2692#page-5

Ale
--

---

From: Viktor Dukhovni <[email-masked]>
Date: Tue, 3 Apr 2018 11:56:14 -0400

> On Apr 3, 2018, at 5:56 AM, Alessandro Vesely <[email-masked]> wrote:
> 

Ascribing the wide use of DKIM signing to a simplified key format in DNS is rather a reach.
Indeed the present RSA key in DKIM *is* in SPKI format, and there's little reason to *change*
the format when introducing ed25519 keys.

DKIM signing has caught on because it does not affect the usability of email, requires no user interaction, and is believed to improve deliverability and reduce phishing.  The public key format has nothing to do with that.  The key difference is complete transparency to the user, and acceptance of an insecure key delivery channel (the attacker being thwarted sends forged emails, but does not cache poison the DNS of the target domains).

SPKI == Subject-Public-Key-Info and is the public key element of an X.509 certificate.
This includes an algorithm OID allowing for a generic decoder that understands all
the requisite algorithm OIDs.  For a signature scheme one needs a hash algorithm id
in addition to the key (SPKI).

None of this is terribly important of course, I am just perplexed by the desire to
to be different here.  MTAs already use libraries for TLS and perhaps S/MIME, that
support the usual key formats used with X.509 certs  and DANE TLSA records, ....
Why should DKIM be different?  Does that really make things better?

-- 
	Viktor.

---

From: denis bider <[email-masked]>
Date: Tue, 3 Apr 2018 16:35:41 -0500

> MTAs already use libraries for TLS and perhaps S/MIME,
> that support the usual key formats used with X.509 certs
> and DANE TLSA records, ....

To offer some insight - in my implementation, which is native Win32 (not
.NET), I use Windows Schannel for TLS, which provides very limited control
over the properties of the TLS connection and approximately zero access to
the underlying primitives. I can configure system-wide TLS algorithm
preferences via registry entries and make some broad choices using the
APIs. That's about it.

For DKIM, I have the following options that are easily available to me:

- For RSA, I can use Windows cryptography or Crypto++. If I use Windows
cryptography, I have to implement my own ASN.1 decoding because Win32 does
not expose this. If I use Crypto++, it can load the ASN.1 RSA key.

- For Ed25519, this is not yet offered either in Windows or Crypto++, so I
pick up DJB's public domain implementation. I have to implement my own
ASN.1 decoding if the key is wrapped.

The bottom line is that what is true in your development environment is not
true in others. The cost of implementing ASN.1 decoration in other
development environments is greater than the cost to you of dropping it.

On Tue, Apr 3, 2018 at 10:56 AM, Viktor Dukhovni <[email-masked]>
wrote:

> DKIM signing has caught on because it does not affect the usability of
> email, requires no user interaction, and is believed to improve
> deliverability and reduce phishing.  The public key format has nothing to
> do with that.  The key difference is complete transparency to the user, and
> acceptance of an insecure key delivery channel (the attacker being thwarted
> sends forged emails, but does not cache poison the DNS of the target
> domains).
>

---

From: Viktor Dukhovni <[email-masked]>
Date: Tue, 3 Apr 2018 17:55:16 -0400

> On Apr 3, 2018, at 5:35 PM, denis bider <[email-masked]> wrote:
> 
> - For RSA, I can use Windows cryptography or Crypto++. If I use Windows cryptography, I have to implement my own ASN.1 decoding because Win32 does not expose this. If I use Crypto++, it can load the ASN.1 RSA key.
> 
> - For Ed25519, this is not yet offered either in Windows or Crypto++, so I pick up DJB's public domain implementation. I have to implement my own ASN.1 decoding if the key is wrapped.

You do not need to implement ASN.1 decoding.  To decode an ed25519 DER-encoded key, just
skip the first 12 bytes, these contain

	SEQ  - 0x30  (SPKI: (alg oid [params]), key)
	len  - 0x2a  (42)
	SEQ  - 0x30  (alg oid) -- no parameters
        len  - 0x05
	OBJ  - 0x06
	len  - 0x03
	OID  - 0x2b, 0x65, 0x70  (1.3.101.112 - id-Curve25519ph)
        BSTR - 0x03  (bit string)
        len  - 0x21  (33 bytes with leading 00 padding bit count)
	PAD  - 0x00

After these 12 *fixed* bytes follows the proposed raw Ed25519 key.
You don't need an ASN.1 decoder to skip these 12 bytes, though you
could check that the skipped bytes are indeed:

	30 2a 30 05 06 03 2b 65 70 03 21 00

as required.
	
> The bottom line is that what is true in your development environment is not true in others. The cost of implementing ASN.1 decoration in other development environments is greater than the cost to you of dropping it.

No ASN.1 parser is required.  Prefixing the ASN.1 bits makes the key format
consistent with prior practice, and compatible with algorithm neutral key
processing.  I'm just suggesting a measly 12-byte *fixed* prefix to avoid
special casing Ed25519 in a way that's unique to DKIM and deviates from
practice with other contexts in which Ed25519 keys might be used (e.g.
TLS, DANE, ...).  The ASN.1 obstacle is a mirage.

-- 
	Viktor.

---

From: denis bider <[email-masked]>
Date: Tue, 3 Apr 2018 18:25:27 -0500

> To decode an ed25519 DER-encoded key, just skip the first 12 bytes

Actually, it seems the more reliable solution would be to skip everything
except the last 32 bytes. That would cover situations where BER encoding
was used.

Someone who has to do this would then naturally ask themselves why on Earth
these useless leading bytes are being sent in the first place, if they
communicate no useful information.

Inserting them always for the convenience of some users seems like the more
questionable design choice, given that the bytes are always the same and
the affected users can insert them using DKIM algorithm information. This
information MUST be verified anyway to ensure the key is of same type as
specified in the "k=" parameter.

> Prefixing the ASN.1 bits makes the key format consistent with prior
practice,

Omitting the ASN.1 bits is more compatible with prior practice, if you
interpret correctly that prior practice was "encode the key in the natural
algorithm-specific format". ASN.1 is the natural algorithm-specific format
for RSA, 32 bytes is the natural algorithm-specific format for Ed25519.

DJB didn't wrap the Ed25519 key into ASN.1, and this was a conscious design
choice done for a reason. You want to retroactively undo that design choice
by shoehorning the design preferences of a specific library.

> and compatible with algorithm neutral key processing

You cannot have algorithm-neutral key processing because you MUST verify
that the key is consistent with the "k=" DKIM parameter.

You are trying to have the whole world insert a 12 byte prefix in front of
the key because you don't want to perform a string concatenation that is
only necessary for your library and only because of its ideology about how
keys ought to be represented, which is NOT universal. But the ideology
ATTEMPTS to be universal, as all ideologies do.

On Tue, Apr 3, 2018 at 4:55 PM, Viktor Dukhovni <[email-masked]>
wrote:

> > - For Ed25519, this is not yet offered either in Windows or Crypto++, so
> I pick up DJB's public domain implementation. I have to implement my own
> > The bottom line is that what is true in your development environment is
> not true in others. The cost of implementing ASN.1 decoration in other

---

From: Viktor Dukhovni <[email-masked]>
Date: Tue, 3 Apr 2018 20:02:33 -0400

> On Apr 3, 2018, at 7:25 PM, denis bider <[email-masked]> wrote:
> 
> You cannot have algorithm-neutral key processing because you MUST verify that the key is consistent with the "k=" DKIM parameter..

I don't find any such "MUST" language in the DKIM RFC.  All that it requires is that the "k" value in the signature header be compatible with the public key, else the signature verification fails.  There is no explicit requirement that the public key payload match "k=" in the DNS TXT record.  With SPKI, one could read the key type from either the "k" value or the "p" value, whichever is more convenient.

That way, when a library is upgraded to support new key types, one can take the "k" value as
as "news" (so that's what that key type is called!) and process the public key generically,
and likewise signatures from DKIM-Signature headers that have "k" tags that match the TXT
record.  At no point does the verifier need to understand the new key type, it is just an
opaque string that allows one to short-circuit signature checks early when there's a mismatch.

There's value in abstraction, you don't need to recompile the verifier when new algorithms
show up, just pass suitable self-describing data to the library and off you go.  And there's
value in re-using formats widely used in other context in the same application space.

> You are trying to have the whole world insert a 12 byte prefix in front of the key because
> you don't want to perform a string concatenation

No, because having uniform key formats (across algorithms) make things more predictable for
users, who can use a broader range of tools that all use the same data representation.

> that is only necessary for your library and only because of its ideology about how keys
> ought to be represented, which is NOT universal.

I frankly object to the ad-hominem imputing of motives.  This is unprofessional.
SPKI is used and supported by many libraries.  These include:

   * Heimhdal's libhx509
   * GnuTLS
   * OpenSSL's libcrypto

Yes, of course I'll have no trouble prepending the bytes after writing some code
to a table of algorithm-specific prefixes, and populating it with ed25519.  But
that code needlessly breaks extensibility.  And yes this is not such a big deal,
I'll get over it if it has to be done, but I still think it would be a mistake,
and there's no ideology involved, just judgement based on long experience.
Novelty in formats rarely benefits the user.

-- 
	Viktor.

---

From: Scott Kitterman <[email-masked]>
Date: Wed, 04 Apr 2018 00:11:57 +0000

On April 4, 2018 12:02:33 AM UTC, Viktor Dukhovni <[email-masked]> wrote:
>> You cannot have algorithm-neutral key processing because you MUST
>verify that the key is consistent with the "k=" DKIM parameter..
>
>I don't find any such "MUST" language in the DKIM RFC.  All that it
>requires is that the "k" value in the signature header be compatible
>with the public key, else the signature verification fails.  There is
>no explicit requirement that the public key payload match "k=" in the
>DNS TXT record.  With SPKI, one could read the key type from either the
>"k" value or the "p" value, whichever is more convenient.
...

To focus on this one point:

If I read you correctly, your claim is that a record with RSA as the "k" value and an ed25519 key isn't an error if we ASN.1 the ed25519 key so we can extract the key type from the ASN.1 structure?

Is that correct?

Scott K

---

From: Viktor Dukhovni <[email-masked]>
Date: Tue, 3 Apr 2018 20:48:18 -0400

> On Apr 3, 2018, at 8:11 PM, Scott Kitterman <[email-masked]> wrote:
> 

It is not explicitly proscribed by the specification.  Of course an implementation
may reasonably object.  What the freedom to not object gives you is the ability
to support novel "k=" values without modifying the code.  Recall also that
"k=rsa" is implicit, so some users may be leaving it out, and might forget
"k=ed25519" (though unlikely perhaps).  The key data is less likely to lie.

There is no harm if the application does not object.  If the DKIM-signature calls
ed25519 "fred25519" and the TXT record does likewise, and the signature checks out,
there's no harm (just loss of interoperability with more strict implementations).
One can take the "k=" value as informational and the actual SPKI data as authoritative
(if SPKI form is retained going forward).

This is not set in stone yet.  And the issues are not so critical in the end,
but perhaps worth spending a few extra cycles to get from adequate to good.

-- 
	Viktor.

---

From: denis bider <[email-masked]>
Date: Tue, 3 Apr 2018 19:52:11 -0500

I would think the suggestion to send the key with ASN.1 padding would make
much more sense if it was accompanied with a suggestion to remove the "k="
parameter, which the ASN.1 wrapping makes unnecessary.

What seems strange to me is asking for two ways for the key type to be
expressed: one human readable ("k=") and then another more obscured (ASN.1)
that can contain an altogether different and conflicting type of key.

Your further suggestions appear to be to ignore "k=" and treat it as
advisory. I find this deeply problematic. If implementations are going to
do this then either "k=" needs to be removed or ASN.1 wrapping needs to be
prohibited. Double signaling of algorithm type that is not actually checked
for consistency is potentially exploitable if one of the algorithms becomes
vulnerable, and the other doesn't.

On Tue, Apr 3, 2018 at 7:02 PM, Viktor Dukhovni <[email-masked]>
wrote:

> that the key is consistent with the "k=" DKIM parameter..
>
> requires is that the "k" value in the signature header be compatible with
> the public key, else the signature verification fails.  There is no
> explicit requirement that the public key payload match "k=" in the DNS TXT
> record.  With SPKI, one could read the key type from either the "k" value
> or the "p" value, whichever is more convenient.
>

---

From: denis bider <[email-masked]>
Date: Tue, 3 Apr 2018 19:56:05 -0500

To be clear, I favor human readable signaling of algorithm type ("k="
parameter) to ASN.1 signaling (obscured), but either is preferable to
double-signaling.

I find the idea that you'll just let your crypto library handle whatever
non-standard algorithms also concerning. If you're doing this, it means you
already have a wide open attack surface so that if an error is identified
in how your crypto library handles a completely unrelated algorithm, one
not used in DKIM, then DKIM becomes an exploitation vector with which your
implementation can be attacked, even though the obscure algorithm is not
supported or standardized in DKIM.

So, yeah... I understand these principles you espouse come from experience,
and I find them contrary to good security practice and deeply concerning.

---

From: "Salz, Rich" <[email-masked]>
Date: Wed, 4 Apr 2018 01:00:43 +0000

Speaking as an indvidiual, I agree that we should have only one way of saying things.  Since “k=” is already there, key-wrapping seems error-prone with little gain.  “When code and comment disagree, both are wrong.”

---

From: Viktor Dukhovni <[email-masked]>
Date: Tue, 3 Apr 2018 21:26:57 -0400

> On Apr 3, 2018, at 8:56 PM, denis bider <[email-masked]> wrote:
> 
> I find the idea that you'll just let your crypto library handle whatever non-standard algorithms also concerning. If you're doing this, it means you already have a wide open attack surface so that if an error is identified in how your crypto library handles a completely unrelated algorithm, one not used in DKIM, then DKIM becomes an exploitation vector with which your implementation can be attacked, even though the obscure algorithm is not supported or standardized in DKIM.

OK, it probably makes sense for some layer in the software stack to check that the
algorithm is valid for DKIM.  Thus, while for TLS applications, it is OK to delegate
such details to the TLS library, since it knows what's valid for TLS, for DKIM there
is no middle-layer above the crypto library, the DKIM library needs to know which
algorithms are kosher.  With that some knowledge of each supported algorithm is
unavoidable.  And we lose zero code algorithm support, because the crypto library
is not a DKIM library.

If so, do all DKIM implementations check when the DER bits of an RSA key are loaded that
what they got was an RSA key?

Fine, drop the prefix.  The user can prepend the right one for this new key type,
and ensure that the library will then load that type of key.  For RSA, it would
have to check after the fact.

-- 
-- 
	Viktor.

---

From: "John Levine" <[email-masked]>
Date: 3 Apr 2018 21:56:06 -0400

In article <[email-masked]> you write:
>algorithms.  The cost of the extra twelve bytes up front is rather modest, ...

For the umpteenth time, there is more to adding a new algorithm to
DKIM than handing the key to OpenSSL, so whatever simplification you
imagine this provides does not actually exist.  This is not a helpful
line of argument.  Please stop.

R's,
John

---

From: denis bider <[email-masked]>
Date: Tue, 3 Apr 2018 21:27:27 -0500

> If so, do all DKIM implementations check when the DER bits of an RSA
> key are loaded that what they got was an RSA key?

The ideal behavior would be that non-RSA code paths are prevented from
being invoked. Allowing non-RSA code paths to be invoked, even while
loading the key, is a security liability.

However, now that this conversation has explained to me the interface
exposed by OpenSSL; and the ways in which implementers might be using it; I
am concerned that indeed many implementations may exist which do not make
this check, and would easily swallow some completely unrelated key type
that's not defined in DKIM. And perhaps not only load that key, but perhaps
even validate a signature with it. Now this means that if any of those key
encodings are complex, and if any bug is found in a popular crypto library
related to decoding an unusual key type, this would mean a surprising
number of DKIM implementations might be vulnerable. This sounds like an
interesting avenue for security researchers to investigate.

>  Fine, drop the prefix.

Thank you. :-)

>  For RSA, it would have to check after the fact.

I would strongly suggest BEFORE the fact. Lock away access to alternate
code paths that would attempt to load unsupported key types. This is a bit
like wanting to support ZIP, but accidentally also supporting RAR, 7z,
lzma, tar.gz and so on... Especially if some of those key types are rarely
used, that increases the likelihood that the implementations have bugs.

---

From: Viktor Dukhovni <[email-masked]>
Date: Wed, 4 Apr 2018 04:05:51 -0400

> On Apr 3, 2018, at 10:27 PM, denis bider <[email-masked]> wrote:
> 
> However, now that this conversation has explained to me the interface exposed by OpenSSL; and the ways in which implementers might be using it; I am concerned that indeed many implementations may exist which do not make this check, and would easily swallow some completely unrelated key type that's not defined in DKIM.

The generic SPKI decoding function in OpenSSL is d2i_PUBKEY().  There's also
d2i_RSA_PUBKEY(), which decodes an SPKI object and ensures that the result is
an RSA key.  And d2i_RSAPubkey(), which decodes just the RSA key (a sequence
of the key and exponent) without the algorithm oid and (NULL) parameters.
So one can certainly use algorithm-specific functions if one so chooses.

Likely DKIM implementations based on OpenSSL use d2i_RSA_PUBKEY or equivalent,
that's essentially the case with OpenDKIM which implements the equivalent of
d2i_RSA_PUBKEY() on top of d2i_PUBKEY_bio().

The same public key algorithms that could exercised via DKIM can be found in
peer certificates, so they all need to be handled correctly.  Unconstrained,
the key type might be any of:

	DH
	DSA
	RSA
	RSA_PSS
	ECDSA
	Ed25519 - (signature)
	Ed448   - (signature)
	X25519  - (key exchange)
	X448    - (key exchange)
	(or GOST if that engine is loaded)

These are all required to be able to handle untrusted data (a peer's public key).

-- 
	Viktor.

---

From: Alessandro Vesely <[email-masked]>
Date: Wed, 4 Apr 2018 10:38:56 +0200

On Wed 04/Apr/2018 02:48:18 +0200 Viktor Dukhovni wrote: 
>> If I read you correctly, your claim is that a record with RSA as the "k"
>> value and an ed25519 key isn't an error if we ASN.1 the ed25519 key so we
>> can extract the key type from the ASN.1 structure?>>
>> Is that correct?
> 
> It is not explicitly proscribed by the specification.  Of course an
> implementation may reasonably object.  What the freedom to not object gives
> you is the ability to support novel "k=" values without modifying the code.
> Recall also that "k=rsa" is implicit, so some users may be leaving it out,
> and might forget "k=ed25519" (though unlikely perhaps).  The key data is
> less likely to lie.>
> There is no harm if the application does not object.  If the DKIM-signature
> calls ed25519 "fred25519" and the TXT record does likewise, and the
> signature checks out, there's no harm (just loss of interoperability with
> more strict implementations). One can take the "k=" value as informational
> and the actual SPKI data as authoritative (if SPKI form is retained going
> forward).>
> This is not set in stone yet.  And the issues are not so critical in the
> end, but perhaps worth spending a few extra cycles to get from adequate to
> good.

Assuming that coding a key-type-agnostic DKIM verifier is feasible, the benefit
of adding those 12 byte SPKI prefix is lost if a future key type will introduce
a different semantic rather than follow the preceding types.  Now, to silently
follow unwritten conventions is not a good practice for an SDO, so I think we
can all agree on one point:

   Mandating SPKI format for ed 25519 makes no sense unless the ID also
   updates the descriptions of tags k= and p= so as to explicitly state
   SPKI usage and role.

For example, the ID could say that the content of p= establishes the actual key
type, and that k= is obsolete.

Ale
--

---

From: "Murray S. Kucherawy" <[email-masked]>
Date: Thu, 5 Apr 2018 13:12:47 -0700

> line of argument.  Please stop.

I disagree.  We can guess all we like about how soon it will be before
another change like this will be needed, but we are notoriously bad at
making such predictions and would rather not dismiss the notion out of hand.

-MSK

---

From: "Murray S. Kucherawy" <[email-masked]>
Date: Thu, 5 Apr 2018 13:20:02 -0700

> supported or standardized in DKIM.

I agree that implementations should be confirming what they read in, e.g.,
the key type, if the key import function is generic in nature.

My implementation allows selection between OpenSSL and GNUTLS.  For
OpenSSL, yes, I'm going to have to make different calls for different kinds
of keys here, which I think is an unfortunate choice but it does seem that
the consensus is to do just that.  I haven't tried this yet for GNUTLS so I
don't know if it's the same story over there.

I suppose you could make the argument that this is pressure we should be
applying on those library providers to provide generic access to keys via a
single function and have the import logic split live in there.

> what they got was an RSA key?

Presumably RSA_verify() will fail somehow if what you give it as the public
key isn't actually an RSA key, assuming that's the interface you use and
you got that far in the first place.  An earlier call to
EVP_PKEY_get1_RSA() would probably also fail.

-MSK

---

From: "Murray S. Kucherawy" <[email-masked]>
Date: Thu, 5 Apr 2018 13:24:54 -0700

> except the last 32 bytes. That would cover situations where BER encoding

That means now I have to understand DER encoding, rather than invoking a
generic function that does all that for me.  That's what I was hoping to
avoid.

I would be very happy to hand a base64 blob to one function and it hands me
back a key object that I can then inspect (to get type, size, whatever) or
use as needed.  The proposal here prevents that sort of convenience from
existing, unless the library intends to provide that switching somehow.
But unless the logic in that library specifically says "any 32-byte blob is
assumed to be an ED25519 key", I don't see how that's possible.  It needs a
hint at least.

-MSK

---

From: Viktor Dukhovni <[email-masked]>
Date: Thu, 5 Apr 2018 16:28:29 -0400

> On Apr 5, 2018, at 4:24 PM, Murray S. Kucherawy <[email-masked]> wrote:
> 
> I would be very happy to hand a base64 blob to one function and it hands me back a key object that I can then inspect (to get type, size, whatever) or use as needed.  The proposal here prevents that sort of convenience from existing, unless the library intends to provide that switching somehow.  But unless the logic in that library specifically says "any 32-byte blob is assumed to be an ED25519 key", I don't see how that's possible.  It needs a hint at least.

I've admitted my error, and now support the short raw key.  You can decode via the generic d2i_PUBKEY and
be sure you'll get the right key type, by prefixing the correct 12 byte DER header to the raw key.  When
you prepend DER that carries the Ed25519 OID, the parsed key is sure to be an Ed25519 key.

-- 
	Viktor.

---

From: "Murray S. Kucherawy" <[email-masked]>
Date: Thu, 5 Apr 2018 13:29:22 -0700

On Tue, Apr 3, 2018 at 5:11 PM, Scott Kitterman <[email-masked]>
wrote:

> can extract the key type from the ASN.1 structure?
>
> Is that correct?
>

It's more like:

- the signature says "a=ed25519-sha256"
- they public key retrieved from the DNS is <b64blob>
- <b64blob> is decoded and handed to a key import function provided by a
crypto library
- key import function gives me a key object
- we can then confirm what type of key that was and match it against what's
in the "a" value

In my ideal world, the key import function in the third step can import
either kind of key and then tell me what type it is.  In the world you
prefer, the switching logic lives in the DKIM implementation, and so the
last step isn't necessary because the DKIM implementation already knows the
answer.

In my ideal world, the bottom four steps work as-is even if new key types
are added later.

-MSK

---

From: Scott Kitterman <[email-masked]>
Date: Thu, 05 Apr 2018 21:54:28 +0000

>In my ideal world, the bottom four steps work as-is even if new key

I think you miss verifying the k= tag in the DNS record.  You don't see it much today since no k= is implicitly RSA.  That won't be the case in the future since a DNS record with no k= tag and an ed25519 key is broken and wrong.

I understand the desire, but it looks to me like you want to add a new protocol requirement that all future algorithms have an ASN.1 key type embedded in them that doesn't currently exist.  Given the existence of k=, I think the existing protocol works fine and doesn't need changing.

Keep in mind your reply earlier today about the poor job we do at predicting future crypto.  I agree with you.  That's one of the reasons I think baking a particular key representation into the protocol is a risky move.

Scott K

---

From: "John R Levine" <[email-masked]>
Date: 5 Apr 2018 18:00:34 -0400

> making such predictions and would rather not dismiss the notion out of hand.

I don't think we're disagreeing.  Victor has apperently been saying that 
if you wrap the key in ASN.1, crypto libraries will automatically select 
the right code to handle it so you could add new algorithms by relinking 
with the new crypto library without changing the DKIM code.

Even if that were true, which I doubt, you still need to change the DKIM 
library that handles the k= tag in the key record and the a= tag in the 
signatures and lets the caller or a configuration file set the algorithm. 
By the time you've done all that, the extra code to switch off to the 
appropriate crypto routine which uses whatever key format it uses is easy.

This would be the same if we added another algo tomorrow or another ten 
years from now.

Regards,
John Levine, [email-masked], Taughannock Networks, Trumansburg NY
Please consider the environment before reading this e-mail. https://jl.ly

---

From: Phillip Hallam-Baker <[email-masked]>
Date: Fri, 6 Apr 2018 07:34:34 -0400

> This would be the same if we added another algo tomorrow or another ten

​This would have been true if:

1) DKIM had originally specified use of a SubjectPublicKeyInfo blob for the
key.​
​2) Applications used the OID in the field and not the tag for algorithm
selection.

We did not.

The issue is not whether to use ASN.1 encoding it is WHAT WE USE ASN.1 to
encode. PKIX and CMS do not encode public key parameters in ASN.1. That is
simply a fact. They never have and never will.

SubjectPublicKeyInfo contains two slots. A slot for an OID and a slot for a
bag of bits.

The DKIM specification uses its own tag and the same data that goes in that
bag of bits.

For RSA, that bag of bits just happened to be encoded in ASN.1. For ECC, it
is not ASN.1.

In the hope of getting to closure on this, I suggest people stop talking
about ASN.1 encoding and instead talk about the ASN.1 structure they
propose to use.

​There is no reason that Ed25519 keys should need to use the RSAPublic
structure. So this is an unproductive argument.

This is not a matter of opinion, it is a matter of understanding the
specification, sorry. The pattern of this argument is that the issue is
continually re-opened by people coming in to the argument who imagine that
this is an issue of ASN.1 hate and it is not. It is a matter of the
proposal being mistaken.

We do not use the ​SubjectPublicKeyInfo blob in DKIM. To propose to use it
is a major change to the spec.