Subject: Re: [Cfrg] I-D Action: draft-nir-cfrg-chacha20-poly1305-01.txt

From: Ted Krovetz <[email-masked]>
Date: Tue, 11 Mar 2014 19:57:55 -0700

Hello Yoav,

Nice work on draft-nir-cfrg-chacha20-poly1305-01.txt. It's a useful document. I wonder, though, if endian conventions are clear enough. What exactly is a little-endian integer? (I thought there was only one kind of integer.) I think you mean that keys, etc, residing in memory are read little-endian, 32-bits at a time. But, what if these quantities do not come from memory? What if the nonce, or key, or counter happens to be a computed value and resides in registers? When exactly do these need byte reversal? As an example, when you talk about the counter being 1, is that 00:00:00:01 in memory, which then becomes 01:00:00:00 in a register, or vice-versa? I was able to figure it out by looking at your examples, but it might be better if it were made clearer and didn't *need* looking at examples.

One approach you might consider is defining the interfaces as taking plain old integers, but then writing a note that explains how big-endian and little-endian systems would interact with such an interface to get standard behavior.

Damn Intel for making the world primarily little-endian! It's such a pain.

Cheers,
Ted

---

From: Yoav Nir <[email-masked]>
Date: Thu, 13 Mar 2014 16:45:55 +0200

On Mar 12, 2014, at 4:57 AM, Ted Krovetz <[email-masked]> wrote:

> Hello Yoav,
> 
> Nice work on draft-nir-cfrg-chacha20-poly1305-01.txt. It's a useful document. I wonder, though, if endian conventions are clear enough. What exactly is a little-endian integer? (I thought there was only one kind of integer.)

I don’t know what a 32-bit integer is, either. I thought the list of integers extended to infinity. I guess I should change the text to say that it’s a little-endian representation of integers limited to 32 bits.

> I think you mean that keys, etc, residing in memory are read little-endian, 32-bits at a time. But, what if these quantities do not come from memory? What if the nonce, or key, or counter happens to be a computed value and resides in registers?

That is exactly what happens in both ESP and TLS (and I assume SSH). Some KDF churns out an octet string, and that has to be used as keys. So if my draft says that the key to ChaCha is 8 little-endian 32-bit integers, what I mean is that the 32 octets (or 256 bits) are taken 4 (or 32) at a time, and taken to be a 32-bit little-endian representation of an integer. This produces that 8 integers that are the input to ChaCha.

> When exactly do these need byte reversal? As an example, when you talk about the counter being 1, is that 00:00:00:01 in memory, which then becomes 01:00:00:00 in a register, or vice-versa? I was able to figure it out by looking at your examples, but it might be better if it were made clearer and didn't *need* looking at examples.

I’m guessing you didn’t attend the JSON meeting on Friday, where someone (I don’t remember who) said something to the effect that real developers look first at the examples rather than read ABNF. Some cheered (I know I did)

Anyway, the block counter is internally generated, so it’s simply a number regardless of the internal architecture of the machine. The nonce in ChaCha, however, and the blocks in Poly1305 are read from an octet string as if they were a 32-bit little-endian representation of integers.

> One approach you might consider is defining the interfaces as taking plain old integers, but then writing a note that explains how big-endian and little-endian systems would interact with such an i…

It could be a good idea. I’ll see if it fits for the next revision.

> Damn Intel for making the world primarily little-endian! It's such a pain.

I don’t know. This is pretty nifty:

int main(int argc, char** argv)
{
    long long int num;
    long long int* llp;
    long int* lp;
    int* p;
    short* sp;
    char* cp;

    num = 7;
    llp = &num;
    lp = (long*) llp;
    p = (int*) llp;
    sp = (short*) llp;
    cp = (char*) llp;

    printf(“%lld %ld %d %hd %hhd\n”, 
        *llp, *lp, *p, *sp, *cp); 
}

Yoav

---

From: Ted Krovetz <[email-masked]>
Date: Thu, 13 Mar 2014 08:16:49 -0700

> I guess I should change the text to say that it’s a little-endian representation of integers limited to 32 bits.

I would recommend that you never use endianess to describe integers. An integer is an integer, and endianess has only to do with cpu-memory interfacing. An integer can be read little-endian from memory, but the resulting integer is not a little-endian integer, it's just an integer. It may be clearest to talk about inputs and output being strings and each successive 32-bit chunk getting byte-reversed.

So, for example, the input to chacha is a 256-bit key string, 96-bit nonce string and 32-bit unsigned integer. The first thing that happens in chacha is the key and nonce are split into 32-bit unsigned integers by byte reversing each 32-bit chunk and then interpreting each 32-bit chunk as an unsigned integer. Now we're in the realm of integers, and the word endianess is never mentioned.

-Ted

---

From: Mike Hamburg <[email-masked]>
Date: Thu, 13 Mar 2014 10:05:55 -0700

On Mar 13, 2014, at 8:16 AM, Ted Krovetz <[email-masked]> wrote:

> I would recommend that you never use endianess to describe integers. An integer is an integer, and endianess has only to do with cpu-memory interfacing. An integer can be read little-endian from memory, but the resulting integer is not a little-endian integer, it's just an integer.

I probably would have said "little-endian integer" too, but this suggestion is clearer.

> So, for example, the input to chacha is a 256-bit key string, 96-bit nonce string and 32-bit unsigned integer. The first thing that happens in chacha is the key and nonce are split into 32-bit unsi…

I don't like this, though.  Your target is software on computers that use octets, right?  In that case, the key contains 32 octets, and the nonce contains 12 octets.  The first thing that happens is that the key and nonce are split into 4-octet fields, which are then converted to unsigned 32-bit integers by interpreting them in little-endian order, that is, with least significant octet first.

On a computer that uses 32-bit unsigned integer words as the addressable unit instead of octets (eg, a DSP), the input is an 8-word key and a 3-word nonce, and there's no endian issue.  That's why it's tempting to say "little-endian integers" even though it's less clear: it specifies the input on multiple platforms simultaneously.

I say this because "interpreting each 32-bit chunk as an unsigned integer" is ambiguous.  The natural meaning is "according to the platform's endian" which is not what you want.

-- Mike

---

From: Alyssa Rowan <[email-masked]>
Date: Thu, 13 Mar 2014 18:27:58 +0000

On 13/03/2014 15:16, Ted Krovetz wrote:
>> So, for example, the input to chacha is a 256-bit key string,
>> 96-bit nonce string and 32-bit unsigned integer. The first thing
>> that happens in chacha is the key and nonce are split into 32-bit
>> unsigned integers by byte reversing each 32-bit chunk and then
>> interpreting each 32-bit chunk as an unsigned integer. Now we're
>> in the realm of integers, and the word endianess is never
>> mentioned.

Putting my programmer hat on for a moment, I find this wording rather
opaque, because of (as Mike mentions)…

On 13/03/2014 17:05, Mike Hamburg wrote:
> I say this because "interpreting each 32-bit chunk as an unsigned
> integer" is ambiguous.  The natural meaning is "according to the
> platform's endian" which is not what you want.

…this.

I'm not sure avoiding the term 'little-endian' when that's what people
will recognise as explicitly describing the byte order (well, _octet_
order, if you must) of the representation of an integer in memory
makes it any clearer.
- -- 
/akr