Subject: Re: [openpgp] patch for EdDSA key packet formats

From: NIIBE Yutaka <[email-masked]>
Date: Tue, 14 Feb 2017 20:30:19 +0900

Hello,

Taylor R Campbell <[email-masked]> wrote:
> The current text describing the EdDSA secret key packet format is
> wrong (it is not used as a scalar at all), and the text describing the
> public key packet format is confusing (can't find the notation Q
> anywhere, and it matches neither the EdDSA papers nor the CFRG EdDSA
> draft).

I agree these points.

But, I'm not sure the fixes in the patch are good enough.

In the patch:
> From: Taylor R Campbell <[email-masked]>
> Date: Wed, 11 Jan 2017 14:21:13 +0000
> Subject: [PATCH] Fix EdDSA secret key packet format with reference to CFRG
>  notation.
>
> What is stored is *not* a scalar; it is a b-bit secret input to a
> 2b-bit hash function that expands it into
>
> (a) the b-bit secret scalar a, giving the public key A = a B, where B
> is the standard base point; and
> (b) the b-bit nonce PRF key.
>
> While here, clarify EdDSA public key packet format with reference to
> CFRG notation too.

I agree these are correct and reference to CFRG notation is good.

Well, Simon's is now RFC8032: 
    https://datatracker.ietf.org/doc/rfc8032/

Here are my comments for the fixes.

> diff --git a/middle.mkd b/middle.mkd
> index 5182c7d..905bde1 100644
> --- a/middle.mkd
> +++ b/middle.mkd
> @@ -1936,8 +1936,9 @@ A version 4 packet contains:
>             - the octets representing a curve OID, defined in
>               section NN{FIXME};
>  
> -      - a MPI of an EC point representing a public key Q as described
> -        under EdDSA Point Format below.
> +      - a MPI, encoded as described under EdDSA Point Format, of an EC
> +        point A, in the notation of [](#I-D.irtf-cfrg-eddsa),
> +        Section 3.2 "Keys".

I think that the expression "an EC point A" would not be good.  In RFC
8032, Section 3.1 "Encoding" explains about ENC(), the little-endian
encoding, and Section 3.2 "Keys" says:

	The EdDSA public key is ENC(A).

... where A is an EC point.

We put the prefix 0x40 to ENC(A), it is not entirely same of
the notation of Section 3.2 "Keys".

Sorry, as I am not good in English, I don't have my own proposal.

>      Algorithm-Specific Fields for ECDH keys:
>  
> @@ -2034,8 +2035,8 @@ The packet contains:
>  
>      Algorithm-Specific Fields for EdDSA keys:
>  
> -      - MPI of an integer representing the secret key, which is a
> -        scalar of the public EC point.
> +      - an opaque octet string k, in the notation of
> +        [](#I-D.irtf-cfrg-eddsa), Section 3.2 "Keys".

Right.  It's not a scalar of the public EC point.  It is the one
which generates the scalar.

In my opinion, "MPI of an integer representing the secret key" is not
wrong.
--

---

From: Taylor R Campbell <[email-masked]>
Date: Tue, 14 Feb 2017 17:26:52 +0000

> Date: Tue, 14 Feb 2017 20:30:19 +0900
> From: NIIBE Yutaka <[email-masked]>

Hi, Niibe-san!  Thanks for the review.

> the notation of Section 3.2 "Keys".

Right, but in RFC 8032, the letter `A' does mean a point on the curve.
What my text says is that we encode the point A using the encoding
described below in the Section 13.3 `EdDSA Point Format'.  This is not
the same as storing the RFC 8032 public key, which is the point A
encoded as ENC(A).

It may be that the Section 13.3 `EdDSA Point Format' encoding is
actually 0x40 || ENC(A), but I'm not sure offhand.

> In my opinion, "MPI of an integer representing the secret key" is not

You are probably right!  I'm afraid I neglected to write notes when I
prepared the patch, so I forgot where in the code I should have cited
for that.  Do you have a quick reference to the source code in
libgcrypt or gnupg that handles encoding this?

---

From: NIIBE Yutaka <[email-masked]>
Date: Wed, 15 Feb 2017 12:20:28 +0900

> Right, but in RFC 8032, the letter `A' does mean a point on the curve.

Yes.

> actually 0x40 || ENC(A), but I'm not sure offhand.

I believe this (0x40 || ENC(A)) is the `EdDSA Point Format' encoding.

> libgcrypt or gnupg that handles encoding this?

In GnuPG, secret keys are primarily handled by gpg-agent with libgcrypt.
And libgcrypt stores them in the format of SEXP [0].  So, GnuPG only
handles OpenPGP secret keys when importing/exporting in OpenPGP format.

For importing, we have the function parse_key in
gnupg/g10/parse-packet.c:2112 (in the release 2.1.18).  When secret key
is not protected, it is handled by the mpi_read function (at line 2526).
Here, in the code, the expression is "pk->pkey[i]", where pk and pkey
stand for "public key", but this is because of historical reason, it
means secret key materials.

And then, it is handled by the function transfer_secret_keys in
gnupg/g10/import.c:1788.  It composes SEXP around line 1917, by "%m"
format, which means MPI, to send gpg-agent.

Exporting is similar.  In the function do_key in
gnupg/g10/build-packet.c:354, non-protected secret key is
written by gpg_mpi_write at line 478.

So, for the GnuPG implementation, secret key is handled as MPI.  But I
think that this is basically due to its historical reason, when keys in
OpenPGP format were specified as MPI.

For specification, "an opaque octet string k" may be better.  I support
this, personally.

On the other hand, I don't know why the ECC format of

	04 || x || y

was described as MPI in RFC6637.  For me, it is an octet string composed
by two MPIs and the prefix.

[0] SEXP --- (S-expressions)
http://people.csail.mit.edu/rivest/sexp.html
--