Subject: [TLS] New Cached info draft

From: Stefan Santesson <[email-masked]>
Date: Thu, 25 Mar 2010 07:55:37 -0700

All,

I have now collected all suggestions and agreements reached in the cached
info discussion and made a substantial re-write of the current draft.
http://tools.ietf.org/html/draft-ietf-tls-cached-info-04
Diff:  
http://tools.ietf.org/rfcdiff?difftype=--hwdiff&url2=draft-ietf-tls-cached-i
nfo-04.txt

Major changes:
- The 64 bit FNV-1 non-cryptographic hash is used to provide a digest over
cached data. All use of hash algorithms has been removed, so also all
elements of hash algorithm agility. The FNV-1 digest algorithm used in this
specification is defined in Annex A.

- It is now possible to query a server with an empty cached info extension
to find out if, and to what extent the server supports caching before the
client starts to cache data for that server.

- The data substitution syntax has been explicitly defined for both defined
object types

I will present the proposed changes at the TLS meeting today.

/Stefan

---

From: "Kemp, David P." <[email-masked]>
Date: Thu, 25 Mar 2010 12:14:13 -0400

While I completely support the use of a non-cryptographic hash function
for this application, it is interesting to note how NIST guidance has
been misinterpreted by the CMVP labs.

Elaine Barker, speaking on algorithm evolution at a security conference
this week, stated that there are no restrictions on using SHA-1 for
purposes other than generating digital signatures for the foreseeable
future.
http://csrc.nist.gov/publications/drafts/800-131/draft-800-131_transitio
n-paper.pdf states that SHA-1 is "Approved for all non-digital signature
applications (including digital signature verification, HMACs, KDFs,
RNGs, ...)" for new validations and for use of existing validated
products beyond 2010.

Dave

From: [email-masked] [mailto:[email-masked]] On Behalf Of
Stefan Santesson
Sent: Thursday, March 25, 2010 10:56 AM
To: [email-masked]
Subject: [TLS] New Cached info draft

All,

I have now collected all suggestions and agreements reached in the
cached info discussion and made a substantial re-write of the current
draft.
http://tools.ietf.org/html/draft-ietf-tls-cached-info-04
Diff:  http://tools.ietf.org/rfcdiff?difftype=--hwdiff
<http://tools.ietf.org/rfcdiff?difftype=--hwdiff&url2=draft-ietf-tls-cac
hed-info-04.txt> &url2=draft-ietf-tls-cached-info-04.txt

Major changes:
- The 64 bit FNV-1 non-cryptographic hash is used to provide a digest
over cached data. All use of hash algorithms has been removed, so also
all elements of hash algorithm agility. The FNV-1 digest algorithm used
in this specification is defined in Annex A.

- It is now possible to query a server with an empty cached info
extension to find out if, and to what extent the server supports caching
before the client starts to cache data for that server.

- The data substitution syntax has been explicitly defined for both
defined object types

/Stefan

---

From: Adam Langley <[email-masked]>
Date: Thu, 25 Mar 2010 19:21:56 -0400

> - The data substitution syntax has been explicitly defined for both defined

I knocked up patches for NSS (client and server) and for OpenSSL
(server only) for this. (Available on request if anyone cares although
the quality is currently "quick-hack").

One issue that I ran into: the representation of the FNV64 hash on the
wire is unspecified. I assumed big-endian but it should be in the
spec.

I think there's a typo in the first line of section 5.1 (it mentioned
trusted_cas when I think it should be certificate_chain)

There are two section 5s.

Also, does a "digest calculated over the certificate_list element of a
server side Certificate message" contain the three length bytes? I
assumed not. To be clear, a certificate_list is prefixed with three
length bytes and contains a series of certificate, each of which has
its own three length bytes. It seems clear the the length bytes of the
certificates should be included, but I omitted the initial three.

Cheers

AGL

-- 
Adam Langley [email-masked] http://www.imperialviolet.org

---

From: Stefan Santesson <[email-masked]>
Date: Thu, 25 Mar 2010 17:36:42 -0700

Thanks for the review Adam,

Comments in line;

> wire is unspecified. I assumed big-endian but it should be in the

Agree, It is big-endian and text is added.

> trusted_cas when I think it should be certificate_chain)

Fixed.

> There are two section 5s.

Fixed.

> certificates should be included, but I omitted the initial three.

Good question. This makes me realize that I have made an error in version 04
since I have removed the inner 3 byte length for the substitution syntax.

The correct syntax should be:

DigestInfo ASN.1Cert<1..2^24-1>;

struct {
    opaque digest_value<0..8>;
} DigestInfo;

struct {
    ASN.1Cert certificate_list<0..2^24-1>;
} Certificate;

The spec intended to specify that the whole certificate_list vector was
hashed, including the length bytes, but it makes absolutely sense to exclude
hashing the length bytes of the certificate_list vector.

I can clarify this if the list approves.

/Stefan

---

From: Stefan Santesson <[email-masked]>
Date: Fri, 26 Mar 2010 07:36:07 -0700

I have submitted a 05 version of cached info draft that fixes the recorded
errors and problems.

http://tools.ietf.org/rfcdiff?url2=draft-ietf-tls-cached-info-05.txt

/Stefan

On 10-03-25 5:36 PM, "Stefan Santesson" <[email-masked]> wrote:

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

---

From: Marsh Ray <[email-masked]>
Date: Fri, 26 Mar 2010 13:38:29 -0500

> http://tools.ietf.org/rfcdiff?url2=draft-ietf-tls-cached-info-05.txt

Cool.

As a check, I implemented the FNV-1 64 algorithm directly from the
pseudocode in the draft, and without reference to any other docs.

Then I tried a few test vectors from
http://isthe.com/chongo/src/fnv/test_fnv.c

The results look good:

For input data:
0 bytes
Digest is: CB F2 9C E4 84 22 23 25

For input data:
0000  61                                                a
1 bytes
Digest is: AF 63 BD 4C 86 01 B7 BE

For input data:
0000  FF 00 00 01                                       ....
4 bytes
Digest is: D6 B2 B1 7B F4 B7 12 61

For input data:
0000  68 74 74 70 3A 2F 2F 65 6E 2E 77 69 6B 69 70 65   http://en.wikipe
0010  64 69 61 2E 6F 72 67 2F 77 69 6B 69 2F 46 6F 77   dia.org/wiki/Fow
0020  6C 65 72 5F 4E 6F 6C 6C 5F 56 6F 5F 68 61 73 68   ler_Noll_Vo_hash
48 bytes
Digest is: 26 CA F8 8B CB EF 2D 19

Minor suggestions:

It might help to mention that this is FNV-1 and not the FNV-1a variant.

The pseudocode relies on indentation for correct interpretation.
Whitespace seems to sometimes get whacked around a bit in these docs. A
pair of curlys might prevent a (trivial) ambiguity.

Including a few of these test vectors might in the document could be
useful, especially because there's not a solid normative ref for FNV-1.
Feel free to use my text. I printed the digest as individual bytes to
emphasize that they are a network sequence rather than a host integer.

- Marsh

---

From: Stefan Santesson <[email-masked]>
Date: Sun, 28 Mar 2010 13:06:02 +0100

Thanks Marsh,

Great suggestions, I will include the test vectors.
I'm not sure exactly how you suggest adding curly brackets.

/Stefan

---

From: Stefan Santesson <[email-masked]>
Date: Mon, 29 Mar 2010 11:36:54 +0100

Marsh and all,

I would like to switch to FNV-1a.
The reason being that FNV-1 has the downside that the last octet only flips
bits in the lowest 8 bits of the digest value, while FNV-1a influence all 64
bits (by changing the order of multiplication and XOR).

See also:
http://sites.google.com/site/murmurhash/avalanche

The pseudocode for FNV-1a is:

      digest = FNV_offset_basis
      for each octet_of_data to be digested {
             digest = digest XOR octet_of_data
             digest = digest * FNV_prime }
      return digest

Could you run some test vectors also for FNV-1a?

/Stefan

---

From: Marsh Ray <[email-masked]>
Date: Mon, 29 Mar 2010 10:47:29 -0500

> Could you run some test vectors also for FNV-1a?

Sure, here's what I get from the new pseudocode.

For input data:
0 bytes
8 bytes

For input data:
0000  61                                                a
1 bytes
Digest is: AF 63 DC 4C 86 01 EC 8C
8 bytes

For input data:
0000  FF 00 00 01                                       ....
4 bytes
Digest is: 69 61 19 64 91 CC 68 2D
8 bytes

For input data:
0000  68 74 74 70 3A 2F 2F 65 6E 2E 77 69 6B 69 70 65   http://en.wikipe
0010  64 69 61 2E 6F 72 67 2F 77 69 6B 69 2F 46 6F 77   dia.org/wiki/Fow
0020  6C 65 72 5F 4E 6F 6C 6C 5F 56 6F 5F 68 61 73 68   ler_Noll_Vo_hash
48 bytes
Digest is: D9 B9 57 FB 7F E7 94 C5
8 bytes

- Marsh

---

From: Stefan Santesson <[email-masked]>
Date: Tue, 30 Mar 2010 03:21:09 +0100

Thanks Marsh,

I implemented the pseudocode in Java and and obtained matching output.

Here is the simple code I wrote for both FNV-1 and FNV-1a;

    BigInteger fnvPrime = new BigInteger("1099511628211");
    BigInteger fnvOffsetBasis = new BigInteger("14695981039346656037");
    BigInteger m = new BigInteger("2").pow(64);

    public BigInteger fNV1(String inpString) {

        BigInteger digest = fnvOffsetBasis;

        for (int i = 0; i < inpString.length(); i++) {
            digest = digest.multiply(fnvPrime).mod(m);
            digest = digest.xor(BigInteger.valueOf((int)
                     inpString.charAt(i)));
        }
        return (digest);
    }

    public BigInteger fNV1a(String inpString) {

        BigInteger digest = fnvOffsetBasis;

        for (int i = 0; i < inpString.length(); i++) {
            digest = digest.xor(BigInteger.valueOf((int)
                     inpString.charAt(i)));
            digest = digest.multiply(fnvPrime).mod(m);
        }
        return (digest);
    }

I'm considering including this code sample in the draft.
I would gratefully receive a matching C sample.

/Stefan

---

From: Simon Josefsson <[email-masked]>
Date: Tue, 30 Mar 2010 10:41:39 +0200

> I implemented the pseudocode in Java and and obtained matching output.

I have implemented it in C independently from the pseudocode, and I also
got the same outputs.  My test code prints similar output like Marsh
code.

> I would gratefully receive a matching C sample.

I believe it is important to have sample code and test vectors in the
document.  I'm donating the code below.

/Simon

fnv1a64.h:

#ifndef FNV1A64_H
# define FNV1A64_H

#include <string.h> /* For size_t */
#include <stdint.h> /* For uint64_t */

extern uint64_t fnv1a64 (const uint8_t *buffer, size_t len);

#endif

fnv1a64.c:

/* fnv1a.c -- Implementation of the FNV-1A non-cryptographic hash function.
 * Written by Simon Josefsson <[email-masked]> on 2010-03-30.
 */

#include "fnv1a64.h"

#define FNV1A64_OFFSET_BASIS 14695981039346656037ULL
#define FNV1A64_PRIME 1099511628211ULL

uint64_t
fnv1a64 (const uint8_t *buffer, size_t len)
{
  uint64_t hash;
  size_t i;

  hash = FNV1A64_OFFSET_BASIS;
  for (i = 0; i < len; i++)
    {
      hash = hash ^ buffer[i];
      hash = hash * FNV1A64_PRIME;
    }

  return hash;
}

fnv1a64_test.c:

#include "fnv1a64.h"

#include <stdio.h> /* For printf */

static void
hash_buffer (const uint8_t *buffer, size_t len)
{
  uint64_t hash;
  size_t i;

  puts ("For input data:");
  for (i = 0; i < len; i++)
    {
      if ((i % 16) == 0)
	printf ("%04x  ", i);
      printf ("%02X ", buffer[i]);
      if ((i % 16) == 15)
	printf ("\n");
    }
  if (len > 0 && (i % 16) != 15)
    printf ("\n");
  printf ("%d bytes\n", len);

  hash = fnv1a64 (buffer, len);

  printf ("Digest is: ");
  for (i = 0; i < sizeof (hash); i++)
    printf ("%02X ", (int) (hash >> (8 * (sizeof (hash) - i -1))) & 0xFF);
  printf ("\n\n");
}

int
main (void)
{
  hash_buffer ((uint8_t *) "", 0);
  hash_buffer ((uint8_t *) "a", 1);
  hash_buffer ((uint8_t *) "b", 1);
  hash_buffer ((uint8_t *) "c", 1);
  hash_buffer ((uint8_t *) "d", 1);
  hash_buffer ((uint8_t *) "e", 1);
  hash_buffer ((uint8_t *) "f", 1);
  hash_buffer ((uint8_t *) "fo", 2);
  hash_buffer ((uint8_t *) "foo", 3);
  hash_buffer ((uint8_t *) "foob", 4);
  hash_buffer ((uint8_t *) "fooba", 5);
  hash_buffer ((uint8_t *) "foobar", 6);
  hash_buffer ((uint8_t *) "\xFF\x00\x00\x01", 4);
  hash_buffer ((uint8_t *) "http://en.wikipedia.org/wiki/"
	       "Fowler_Noll_Vo_hash_buffer", 48);

  return 0;
}

---

From: Stefan Santesson <[email-masked]>
Date: Tue, 30 Mar 2010 12:14:43 +0100

Thanks Simon,

I have included your sample in draft 07.
I think this document is done now and good to go.

Eric stated in Anaheim that he might get back on the issue of selecting FNV
as hash/digest/checksum function.

I have stated that I'm agnostic in principle whether we choose SHA1 or FNV
as long as we choose one without any extra agility complexity requiring
algorithm identifiers and possibly negotiating algorithm capabilities.

I would prefer that we stick to the current draft (FNV) as it is more than
good enough, free, fast, easy to implement, provides interop and effectively
avoids all requirements for agility complexity.

/Stefan

---

From: Adam Langley <[email-masked]>
Date: Tue, 30 Mar 2010 11:24:43 -0400

> I think this document is done now and good to go.

For people looking to test interop I've a couple of patches:

http://www.imperialviolet.org/binary/cached-info-nss.patch (client and server)
http://www.imperialviolet.org/binary/cached-info-openssl-0.9.8n.patch  (server)

For the OpenSSL patch, it's enabled by default (probably breaking the
no-tlsext build, but that's already broken since 0.9.8m).

For NSS, you can use the selfserv and tstclnt utilities from NSS. For
tstclnt, give -I /path/to/server-certificate.pem and for selfself,
give just -I.

One last point, I think I'd like to see the ability to cache the
CertificateStatus:

enum {
  certificate_status(3)
} CachedInformationType;

4.3.  Data Substitution Syntax for certificate_status

   When a digest for an object of type certificate_status is provided in the
   client hello, the server MAY substitute the cached data with a
   matching digest value received from the client by expanding the
   CertificateStatus handshake message as follows.

   Original handshake message syntax defined in RFC 3546 [RFC3546]:

     struct {
          CertificateStatusType status_type;
          select (status_type) {
              case ocsp: OCSPResponse;
          } response;
      } CertificateStatus;

      opaque OCSPResponse<1..2^24-1>;

   The substitution syntax is defined by defining an additional
status_type which is only valid in a CertificateStatus message:

      enum { ocsp_cached(2) } CertificateStatusType;

     struct {
          CertificateStatusType status_type;
          select (status_type) {
              case ocsp: OCSPResponse;
              case ocsp_cached: opaque digest_value<0..8>;
          } response;
      } CertificateStatus;

AGL

---

From: "Brian Smith" <[email-masked]>
Date: Tue, 30 Mar 2010 11:34:05 -0500

> avoids all requirements for agility complexity.

I agree. It is good to see the syntax was simplified as a result.

I have some suggestions:

* The draft says that CachedInformation.cached_info can be up to (2^16-1)*9
= 590KB in size. extension_data can't be larger than 64KB, so the max bound
for the CachedInformation.cached_info array must be 7281 or less. But,
really, sending more than a few hashes per type of cached info is likely to
run into DoS countermeasures. It would be better to have the specification
require and/or at least recommend that there not be more than one (or at
most a few) hashes per information type in the client hello.

* The draft says "An empty cached_information extension indicates that the
server supports information caching but provides no information about what
information types it supports." This is not really helpful. It would be
better to make a non-empty server cached_information extension a
SHOULD-level requirement, because it is very helpful for clients to know
what they should cache, and what they shouldn't.

* The draft says "A present non-empty digest_value indicates that the server
will honor caching of objects of the specified type that matches the present
digest value." I don't see why this is necessary. The server should always
be supporting the digests of the values that it most recently returned, for
the information items it claims to support, so the semantics for empty
digest_values in the server extension are good enough. And, this would let
you simplify the syntax to:

     struct {
           CachedInformationType type;
           opaque digest_value<8>;		<-- always 8 octets
      } CachedObject;

      struct {
           CachedObject cached_info<1..2^16-1>;
      } ClientCachedInformation;     

     struct {
           CachedInformationType cached_info<0..255>;
      } ServerCachedInformation;

Regards,
Brian

---

From: "Brian Smith" <[email-masked]>
Date: Tue, 30 Mar 2010 11:48:26 -0500

CertificateStatus:

Optimally, the client only really wants a certificate status update when a
previously-cached one has expired and/or the server's certificate has
changed. Server certificates very rarely change. Consequently, the client
should be able to get by with sending the certificate status request
extension only when it doesn't have a non-expired certificate status for
that server, right? AFAICT, this would work equally well for servers that do
and don't support the cached-info extension.

Please let me know if I am overlooking something.

Regards,
Brian

---

From: Stefan Santesson <[email-masked]>
Date: Tue, 30 Mar 2010 19:00:53 +0100

Brian,

I do not agree with your change proposals.

The most important reason being that the server need to reply with the same
extension that the client sends, everything else is a violation of TLS.

RFC 5246:
   An extension type MUST NOT appear in the ServerHello unless the same
   extension type appeared in the corresponding ClientHello.

There are other reasons, but this is the most important one. Yes, we made a
stretch of that in the renego rfc, but I remember it was agreed to be a one
time thing.

I don't see any big reason to change the current syntax.

/Stefan

---

From: Martin Rex <[email-masked]>
Date: Tue, 30 Mar 2010 19:23:50 +0200 (MEST)

> extension that the client sends, everything else is a violation of TLS.

To me it sounded like Brian disliked the ServerHelloExtension
to be allowed empty on the initial discovery, rather than telling
the client for which handshake data the server supports caching.

I do not think that he suggested to not return the extension _and_
replace cached data.

> > most a few) hashes per information type in the client hello.

To me, allowing the client to cache distinct values for the same
server leads to cache management problems.  How should a client expire
outdated content from his cache?  If the client only caches one item
per "server:port" pair, then expiring of outdated cached information
is a non-issue.

> > digest_values in the server extension are good enough.

I would also appreciate semantics as suggested here.
Allow the server to return a ServerHelloExtension that explicitly list
the types of information for which the server supports caching, but
_without_ a digest_value, both on discovery and on actual use of 
the caching extension by the client, so that the server does not
have to pre-calculate this data of future handshake message
while it is composing ServerHello.

-Martin

---

From: Stefan Santesson <[email-masked]>
Date: Tue, 30 Mar 2010 20:08:46 +0100

Martin,

> I do not think that he suggested to not return the extension _and_

I interpreted the ServerCachedInformation structure as a separate extension
sent only by the server.

> per "server:port" pair, then expiring of outdated cached information

It's a non-issue in any case. A timer for example works well. Nothing
prevents the client to refuse caching more than one object per type and
server, but that restriction doesn't strike me as necessary.

> while it is composing ServerHello.

The server doesn't have to send digest values in current draft.

---

From: "Brian Smith" <[email-masked]>
Date: Tue, 30 Mar 2010 13:30:37 -0500

> I interpreted the ServerCachedInformation structure as a separate
extension

No, I meant for the client and the server to use the same extension ID, but
with different syntax for the extension_data. That is allowed. In fact the
current draft already has slightly different syntax for the client and
server extension data; the client digest_value is fixed at 8 bytes and the
server digest value can be either 0 or 8 bytes.

> >>> * The draft says that CachedInformation.cached_info can be up to 
> >>> 590KB in size. extension_data can't be larger than 64KB, so the max
bound
> server, but that restriction doesn't strike me as necessary.

It is good to keep the maximum size of extensions small so that the server
can allocate and reuse fixed-size buffers that are as small as possible. I
don't see the use for allowing multiple values per information type, but at
least I think a small cap on the total size of the extension_data (say, 1KB)
would be useful. There's no need for a server to waste resources to support
clients that send dozens, hundreds, or thousands of digests.

> The server doesn't have to send digest values in current draft.

AFAICT, there's nothing in the draft that says that the client should use
this information in any way. As long as the client is free to ignore the
server-sent digest_values when present, it doesn't hurt. But, I don't see
how it really helps either. It's better to keep the syntax as simple as
possible.

Again, it is best to require that the server explicitly list the information
types for which it supports caching. It costs the server basically nothing
to provide the few extra bytes, and it is very useful information for the
client to have.

Regards,
Brian

---

From: Martin Rex <[email-masked]>
Date: Tue, 30 Mar 2010 21:08:49 +0200 (MEST)

> Please let me know if I am overlooking something.

The cached info uses a fail-safe approach in that the real data will
be sent by the server when the information by the client does not
match (based on the server comparing the hash values supplied by the
client).

You can not do that with the TLS extension for OCSP.  The client only
knows what certificate the server from the reply to the ClientHello
handshake message.  IMHO, the robust approach for a TLS handshake
that has the highest likelihood to succeed, requires the use of
the TLS extension for OCSP plus the caching extension for
the actual extension contents (the OCSP responses).

I would not want to go through a handshake failure when the
OCSP responses that the client has cached are still valid, but
the server has switched to a different server certificate
since the last connect.

-Martin

---

From: Stefan Santesson <[email-masked]>
Date: Tue, 30 Mar 2010 21:21:22 +0100

Brian,

I see that I misunderstood you a bit.

I agree that it could be wise to limit the max size of this extension. It
does not have to be that large.

I'll get back to the rest later.

/Stefan

---

From: "Brian Smith" <[email-masked]>
Date: Tue, 30 Mar 2010 15:24:10 -0500

> match (based on the server comparing the hash values supplied by the

My concern is that a hash of the client's cached value compared against a
hash of the value the server would otherwise send would not result in
optimal caching, because the server will often have an OCSP response that is
different bitwise, but which is equivalent for verifying that the
certificate was valid at the time of the handshake. Ideally, most
applications in that situation you would rather not have the server return
the newer OCSP response, because the client's older OCSP response is good
enough at the time of the request, and by the time that old response
expires, the server will probably have an even newer response. 

Instead, it would be better to have an extension that said "Don't send me a
CertificateStatus unless there was a cache miss for the certificate and/or
the certificate status has changed from X," where X is the client's cached
value of the certificate status (usually "good"). Practically, that would be
the same as " Don't send me a CertificateStatus unless there was a cache
miss for the certificate" because a good server wouldn't carry on with the
handshake if it knows it has a bad certificate, and a malicious server
wouldn't bother telling you that its certificate has been revoked.

- Brian

---

From: Stefan Santesson <[email-masked]>
Date: Wed, 31 Mar 2010 01:32:54 +0100

I'm thinking along the same line here.

This and the fact that it mixes the use of two extensions makes me feel a
bit uncomfortable with this.

Is there a compelling use case?

/Stefan

---

From: Stefan Santesson <[email-masked]>
Date: Wed, 31 Mar 2010 10:42:44 +0100

> clients that send dozens, hundreds, or thousands of digests.

I think that is a good limitation. Suggesting:

      struct {

      struct {
           CachedObject cached_info<1..1024>;
      } CachedInformation;

> how it really helps either. It's better to keep the syntax as simple as

It could help the client to update it's cache and retire no-longer supported
cached items.

This could be clarified in the text. E.g. That if the server return digest
values, it MUST return all supported digest values in order to allow the
client update it's current set of valid cached objects.

> to provide the few extra bytes, and it is very useful information for the

I'm open to include such requirement. Do others agree?

/Stefan

---

From: Michael D'Errico <[email-masked]>
Date: Wed, 31 Mar 2010 08:52:37 -0700

>            CachedObject cached_info<1..1024>;

I haven't been able to totally keep up with the progress of this draft or
to follow the discussion, but I think it's a bad idea to artificially limit
the size of anything to 1024 bytes.  We can handle 8-bit, 16-bit and 24-bit
sizes, and there is no precedent for choosing anything other than the full
range.  A single certificate can be 16MB!

If somebody wants to use small fixed-size buffers, then they can check the
length of the vector to see if it will fit.  If it won't fit, then they
have to deal with it somehow.  Putting a cap of 1024 bytes in the spec.
does nothing to change this -- you still have to check the size since an
attacker isn't going to play by the rules.  This idea has buffer overflow
written all over it.

Mike

---

From: Marsh Ray <[email-masked]>
Date: Wed, 31 Mar 2010 11:04:21 -0500

On 3/31/2010 10:52 AM, Michael D'Errico wrote:
> the size of anything to 1024 bytes.

My understanding is that this structure is received before the parties
have authenticated each other. In this context, restrictive limits are
somewhat appropriate.

> We can handle 8-bit, 16-bit and 24-bit

I think in this case it's saying there can be a maximum of 1024 entries
in the list, each of which can take up to 9 bytes (with a one byte
length). So the receiver of this structure is only obligated to store
(or ignore) (2 + (1 + 8)*1024) bytes of data.

> have to deal with it somehow.

What ends up happening in the absence of a defined limit is that
products will either be subject to a DOS, or different limits will be
imposed by different products which harms interoperability.

> Putting a cap of 1024 bytes in the spec.

At some point, someone has to be expected to write correct code. :-)

- Marsh

---

From: "Brian Smith" <[email-masked]>
Date: Wed, 31 Mar 2010 12:18:18 -0500

Marsh Ray wrote:
> > I haven't been able to totally keep up with the progress of this draft
> > or to follow the discussion, but I think it's a bad idea to
> > artificially limit the size of anything to 1024 bytes.

> I think in this case it's saying there can be a maximum of 1024 entries in
the list,
> each of which can take up to 9 bytes (with a one byte length). So the
receiver of
> this structure is only obligated to store (or ignore) (2 + (1 + 8)*1024)
bytes of
> data.

No, Mike is right and I was wrong; <1024> means "1024 bytes," not "1024
entries.

> > If somebody wants to use small fixed-size buffers, then they can check
> > the length of the vector to see if it will fit.  If it won't fit, then
> > they have to deal with it somehow.
> 
> What ends up happening in the absence of a defined limit is that products
will
> either be subject to a DOS, or different limits will be imposed by
different
> products which harms interoperability.

Also, there are interoperability problems with fragmented ClientHellos so
the pragmatic total maximum size of a ClientHello is 2^14 bytes. 

Is 1024 bytes is an appropriate limit? Maybe not. First, I think we should
discuss whether/why it is sub-optimal to cache just the most recently
received value of each information item. Then I think it would become clear
whether or not it makes sense for the client to send dozens or hundreds or
more hashes in its ClientHello.

I can see that it might be useful for the client to cache one value of each
cached information item that were obtained during initial (unauthenticated,
unencrypted) handshakes plus another set of values per client certificate
that were obtained during renegotiation following client authentication.
However, in that case, the server probably wouldn't want the client to leak
the hash of its second certificate in subsequent initial (unauthenticated,
unencrypted) handshakes. It seems something about this should be added to
the security considerations of the draft.

If there are other cases where it seems useful to have the client send
multiple hashes for the same information item, then those cases should also
be discussed before publication so that the security properties of that
those kind of usages can be analyzed. For example, I think it is a big
privacy problem to send a digest of a value obtained from one server to
another server.

I think the author, Stefan Santesson, seems to know of some cases where
caching more than one value is useful. Stefan, it would be very helpful if
you could explain when it would be sub-optimal to send only one value per
information item in the ClientHello, and when/why it is not a
security/privacy problem to send a hash to a server that hadn't previously
sent you the value from which that hash was created.

Regards,
Brian

---

From: Adam Langley <[email-masked]>
Date: Wed, 31 Mar 2010 13:32:33 -0400

> Is there a compelling use case?

I agree that the mixing of extensions here is a little nasty. Maybe
this is best left to the multi ocsp draft.

AGL

---

From: Stefan Santesson <[email-masked]>
Date: Wed, 31 Mar 2010 20:21:04 +0100

I don't have a great use case where it is necessary to cache more than one
value per object type.

The closest I can think of is if there are two servers in a a cluster with
different server certificates.

I do however believe in the principle of not closing doors in standards
unless there is a well motivated reason to do so. Even if I can't think of
all good reasons to keep a door open, there might be more reasons discovered
in the future.

This standard offers future extensibility, so even if there are no reasons
to cache more than one value for current information object types, there
might be good reasons to do so for future defined object types.

The vector length defines the number of bytes and not the number of entries.
Each entry is 10 bytes (1 byte object type, 1 byte digest length, 8 byte
digest). 1024 bytes allows 102 digest values for various cached objects
types. I think this is more than enough.

I don't feel strongly about this limitation however.

Concerning security issues:
Fell free to suggest security considerations text. However I can't see any
integrity issues with advertising digest values of cached items so I don't
think we need to beat this horse to death.

/Stefan

---

From: Martin Rex <[email-masked]>
Date: Wed, 31 Mar 2010 21:08:49 +0200 (MEST)

> this is best left to the multi ocsp draft.

Although it looked like a pretty good candidate for caching,
I'm starting to believe the multi OCSP extension should take care of
this optimization.  The ClientHelloExtension container of that
extension should provide room for (at least) two pieces of
information:  A limit/constraint for "producedAt" and a hash of
either the server certificate or whole server certificate chain
which the client expects to receive from the server and for which
the client still has OCSP responses cached that it considers
acceptably fresh.

-Martin

---

From: Martin Rex <[email-masked]>
Date: Wed, 31 Mar 2010 21:47:27 +0200 (MEST)

Brian Smith wrote:
> 
> Marsh Ray wrote:
> > 
> > I think in this case it's saying there can be a maximum of 1024
> > entries in the list, each of which can take up to 9 bytes
> > (with a one byte length). So the receiver of this structure
> > is only obligated to store (or ignore) (2 + (1 + 8)*1024)
> > bytes of data.
> 
> No, Mike is right and I was wrong; <1024> means "1024 bytes",
> not "1024 entries".

While Marsh explanation sounded quite reasonable to me,
the implementors have a technical advantage of being more
accustomed to the TLS spec syntax for defining a vector size:

http://tools.ietf.org/html/rfc5246#section-4.3

> more hashes in its ClientHello.

Allowing a client to cache (and to propose) more than one value per type
makes cache management more complicated, but whether a single item is
sufficient appears to depends on several aspects.  One of the aspects
is the exact connection parameters that the clients uses as a lookup
key into his cache.

If the lookup key is only the server/target hostname, then one would need
more than one entry/value in order to support multiple servers on
a host.  If the lookup key is server:port, then one might still need
more than one entry/value if client certs with different characteristics
are used (while it is possible to use an RSA client cert with every
ciphersuite and every kind of server certificate, ECC client certs
can only be used when they share characteritics with the servers ECC cert
and that requires the use of ECC ciphersuites).

TLS-enabled Web-Servers that host several different "zones" of information
with different access policies and using TLS renegotiation to request
zone-specific client-cert authentication could potentially use
zone-specific list of "trusted CAs".  Such a scheme may interact
badly with TLS session caching, though (i.e. incur a full handshake
everytime an access to a different zone is performed from a client.

> the security considerations of the draft.

Interesting, I hadn't thought of this aspect.  I think that the
caching extension (update of client cache, proposal of cached values)
should apply to all full handshakes, which includes TLS renegotiations,
this will likely make hashes from data exchanged on a confidential
rengotiation handshake visible on new in-the-clear handshakes.

> privacy problem to send a digest of a value obtained from one server to

I mainly see a privacy problem for the client, similar to HTTP-Referers:
or the browser cache.  Maybe a "Cache considerations" sections would
be appropriate.

-Martin

---

From: Marsh Ray <[email-masked]>
Date: Wed, 31 Mar 2010 15:14:27 -0500

It says:
>    The length of
>    an encoded vector must be an even multiple of the length of a single
>    element (for example, a 17-byte vector of uint16 would be illegal).

This requirement does not seem to allow a vector to be made up of
variable-sized elements as in Stefan's suggestion.

>            CachedObject cached_info<1..1024>;

So perhaps some other notation is needed for this class of structure?

On 3/31/2010 10:52 AM, Michael D'Errico wrote:
>
> This idea has buffer overflow written all over it.

Now I'm inclined to agree!

There are something like four potentially contradictory byte lengths in
play.

- Marsh

---

From: Martin Rex <[email-masked]>
Date: Wed, 31 Mar 2010 22:40:15 +0200 (MEST)

> So perhaps some other notation is needed for this class of structure?

Stefan's description looks _very_ similar to the definition of
TLS extensions in (Extended)ClientHello of rfc-5246:

http://tools.ietf.org/html/rfc5246#appendix-A.4.1

   struct {
       ProtocolVersion client_version;
       Random random;
       SessionID session_id;
       CipherSuite cipher_suites<2..2^16-2>;
       CompressionMethod compression_methods<1..2^8-1>;
       select (extensions_present) {
           case false:
               struct {};
           case true:
               Extension extensions<0..2^16-1>;
       };
   } ClientHello;

   struct {
       ExtensionType extension_type;
       opaque extension_data<0..2^16-1>;
   } Extension;

so my interpretation would be that this "multiple-of" restriction applies
only to arrays of "scalar" types with a fixed size, but not to
composed types of variable size.

Anyway, reusing the syntax from the Extensions definition in the
TLS spec seems like the correct thing to do.

-Martin

---

From: Stefan Santesson <[email-masked]>
Date: Thu, 01 Apr 2010 01:26:56 +0100

> variable-sized elements as in Stefan's suggestion.

I don't believe this is discussing the same thing.

This is discussing an encoded vector, which is different from the min-max
values of the vector.

As long as it is possible to encode the actual value of the vector within
the min-max values, you can encode the vector with appropriate length.

Example:

      opaque A<1..2^24-1>;

      struct {
          A b<0..2^24-1>;
      } C;

Is legal  <3byte b len><<3byte A len><A>..<3byte A len><A>>

(compare with definition of the Certificate handshake message having this
syntax structure)

while:

      opaque A[16];

      struct {
          A b<23..24>;
      } C;

Would be impossible to encode b correctly since no multiple of A (16 bytes)
can be 23 or 24 bytes.

I don't see the buffer overflow issue. If the receiver receives a vector of
greater length than the max value, it should simply discard the data and
fail the handshake. I'm sure this exception case is defined somewhere.

The only issue I can see is whether the suggested max length is sufficient.
I.e. Is it realistic that a client will send more than 102 digest values of
cached objects.

/Stefan

---

From: Stefan Santesson <[email-masked]>
Date: Thu, 01 Apr 2010 01:38:50 +0100

> or the browser cache.  Maybe a "Cache considerations" sections would

I'm still not sure what the threat is.

The client only gives away digest values. In order to know what data these
hash values represent, the "attacker" reasonably needs access to the cached
data.

I can't think of anything useful or serious the attacker can do with this.

/Stefan