net: add basic ChannelID support to TLSLite.

In order to test some ChannelID functions, this change adds basic ChannelID
support to testserver and TLSLite. With this change, testserver can receive a
ChannelID and echo its hash on /channel-id.

This code doesn't check the ChannelID signature.

BUG=none
TBR=darin

Review URL: https://chromiumcodereview.appspot.com/12313068

git-svn-id: http://src.chromium.org/svn/trunk/src/third_party/tlslite@185744 4ff67af0-8c30-449e-8e8b-ad334ec8d88c
diff --git a/README.chromium b/README.chromium
index 02d1d29..de941c0 100644
--- a/README.chromium
+++ b/README.chromium
@@ -32,3 +32,5 @@
   updated to add a parseChain method, that can parse multiple certificates from
   a PEM string.
 - patches/tls_intolerant.patch: allow TLSLite to simulate a TLS-intolerant server.
+- patches/channel_id.patch: add basic ChannelID support. (Signatures are not
+  checked.)
diff --git a/patches/channel_id.patch b/patches/channel_id.patch
new file mode 100644
index 0000000..472406e
--- /dev/null
+++ b/patches/channel_id.patch
@@ -0,0 +1,217 @@
+diff --git a/third_party/tlslite/tlslite/TLSConnection.py b/third_party/tlslite/tlslite/TLSConnection.py
+index f8811a9..e882e2c 100644
+--- a/third_party/tlslite/tlslite/TLSConnection.py
++++ b/third_party/tlslite/tlslite/TLSConnection.py
+@@ -611,6 +611,8 @@ class TLSConnection(TLSRecordLayer):
+                                    settings.cipherImplementations)
+ 
+             #Exchange ChangeCipherSpec and Finished messages
++            for result in self._getChangeCipherSpec():
++                yield result
+             for result in self._getFinished():
+                 yield result
+             for result in self._sendFinished():
+@@ -920,6 +922,8 @@ class TLSConnection(TLSRecordLayer):
+             #Exchange ChangeCipherSpec and Finished messages
+             for result in self._sendFinished():
+                 yield result
++            for result in self._getChangeCipherSpec():
++                yield result
+             for result in self._getFinished():
+                 yield result
+ 
+@@ -1089,6 +1093,7 @@ class TLSConnection(TLSRecordLayer):
+         clientCertChain = None
+         serverCertChain = None #We may set certChain to this later
+         postFinishedError = None
++        doingChannelID = False
+ 
+         #Tentatively set version to most-desirable version, so if an error
+         #occurs parsing the ClientHello, this is what we'll use for the
+@@ -1208,6 +1213,8 @@ class TLSConnection(TLSRecordLayer):
+                 serverHello.create(self.version, serverRandom,
+                                    session.sessionID, session.cipherSuite,
+                                    certificateType)
++                serverHello.channel_id = clientHello.channel_id
++                doingChannelID = clientHello.channel_id
+                 for result in self._sendMsg(serverHello):
+                     yield result
+ 
+@@ -1221,6 +1228,11 @@ class TLSConnection(TLSRecordLayer):
+                 #Exchange ChangeCipherSpec and Finished messages
+                 for result in self._sendFinished():
+                     yield result
++                for result in self._getChangeCipherSpec():
++                    yield result
++                if doingChannelID:
++                    for result in self._getEncryptedExtensions():
++                        yield result
+                 for result in self._getFinished():
+                     yield result
+ 
+@@ -1399,8 +1411,12 @@ class TLSConnection(TLSRecordLayer):
+             #Send ServerHello, Certificate[, CertificateRequest],
+             #ServerHelloDone
+             msgs = []
+-            msgs.append(ServerHello().create(self.version, serverRandom,
+-                        sessionID, cipherSuite, certificateType))
++            serverHello = ServerHello().create(
++                    self.version, serverRandom,
++                    sessionID, cipherSuite, certificateType)
++            serverHello.channel_id = clientHello.channel_id
++            doingChannelID = clientHello.channel_id
++            msgs.append(serverHello)
+             msgs.append(Certificate(certificateType).create(serverCertChain))
+             if reqCert and reqCAs:
+                 msgs.append(CertificateRequest().create([], reqCAs))
+@@ -1528,6 +1544,11 @@ class TLSConnection(TLSRecordLayer):
+                                settings.cipherImplementations)
+ 
+         #Exchange ChangeCipherSpec and Finished messages
++        for result in self._getChangeCipherSpec():
++            yield result
++        if doingChannelID:
++            for result in self._getEncryptedExtensions():
++                yield result
+         for result in self._getFinished():
+             yield result
+ 
+diff --git a/third_party/tlslite/tlslite/TLSRecordLayer.py b/third_party/tlslite/tlslite/TLSRecordLayer.py
+index 1bbd09d..933b95a 100644
+--- a/third_party/tlslite/tlslite/TLSRecordLayer.py
++++ b/third_party/tlslite/tlslite/TLSRecordLayer.py
+@@ -714,6 +714,8 @@ class TLSRecordLayer:
+                                             self.version).parse(p)
+                 elif subType == HandshakeType.finished:
+                     yield Finished(self.version).parse(p)
++                elif subType == HandshakeType.encrypted_extensions:
++                    yield EncryptedExtensions().parse(p)
+                 else:
+                     raise AssertionError()
+ 
+@@ -1067,7 +1069,7 @@ class TLSRecordLayer:
+         for result in self._sendMsg(finished):
+             yield result
+ 
+-    def _getFinished(self):
++    def _getChangeCipherSpec(self):
+         #Get and check ChangeCipherSpec
+         for result in self._getMsg(ContentType.change_cipher_spec):
+             if result in (0,1):
+@@ -1082,6 +1084,15 @@ class TLSRecordLayer:
+         #Switch to pending read state
+         self._changeReadState()
+ 
++    def _getEncryptedExtensions(self):
++        for result in self._getMsg(ContentType.handshake,
++                                   HandshakeType.encrypted_extensions):
++            if result in (0,1):
++                yield result
++        encrypted_extensions = result
++        self.channel_id = encrypted_extensions.channel_id_key
++
++    def _getFinished(self):
+         #Calculate verification data
+         verifyData = self._calcFinished(False)
+ 
+diff --git a/third_party/tlslite/tlslite/constants.py b/third_party/tlslite/tlslite/constants.py
+index 04302c0..e357dd0 100644
+--- a/third_party/tlslite/tlslite/constants.py
++++ b/third_party/tlslite/tlslite/constants.py
+@@ -22,6 +22,7 @@ class HandshakeType:
+     certificate_verify = 15
+     client_key_exchange = 16
+     finished = 20
++    encrypted_extensions = 203
+ 
+ class ContentType:
+     change_cipher_spec = 20
+@@ -30,6 +31,9 @@ class ContentType:
+     application_data = 23
+     all = (20,21,22,23)
+ 
++class ExtensionType:
++    channel_id = 30031
++
+ class AlertLevel:
+     warning = 1
+     fatal = 2
+diff --git a/third_party/tlslite/tlslite/messages.py b/third_party/tlslite/tlslite/messages.py
+index dc6ed32..fa4d817 100644
+--- a/third_party/tlslite/tlslite/messages.py
++++ b/third_party/tlslite/tlslite/messages.py
+@@ -130,6 +130,7 @@ class ClientHello(HandshakeMsg):
+         self.certificate_types = [CertificateType.x509]
+         self.compression_methods = []   # a list of 8-bit values
+         self.srp_username = None        # a string
++        self.channel_id = False
+ 
+     def create(self, version, random, session_id, cipher_suites,
+                certificate_types=None, srp_username=None):
+@@ -174,6 +175,8 @@ class ClientHello(HandshakeMsg):
+                         self.srp_username = bytesToString(p.getVarBytes(1))
+                     elif extType == 7:
+                         self.certificate_types = p.getVarList(1, 1)
++                    elif extType == ExtensionType.channel_id:
++                        self.channel_id = True
+                     else:
+                         p.getFixBytes(extLength)
+                     soFar += 4 + extLength
+@@ -220,6 +223,7 @@ class ServerHello(HandshakeMsg):
+         self.cipher_suite = 0
+         self.certificate_type = CertificateType.x509
+         self.compression_method = 0
++        self.channel_id = False
+ 
+     def create(self, version, random, session_id, cipher_suite,
+                certificate_type):
+@@ -266,6 +270,9 @@ class ServerHello(HandshakeMsg):
+                 CertificateType.x509:
+             extLength += 5
+ 
++        if self.channel_id:
++            extLength += 4
++
+         if extLength != 0:
+             w.add(extLength, 2)
+ 
+@@ -275,6 +282,10 @@ class ServerHello(HandshakeMsg):
+             w.add(1, 2)
+             w.add(self.certificate_type, 1)
+ 
++        if self.channel_id:
++            w.add(ExtensionType.channel_id, 2)
++            w.add(0, 2)
++
+         return HandshakeMsg.postWrite(self, w, trial)
+ 
+ class Certificate(HandshakeMsg):
+@@ -567,6 +578,28 @@ class Finished(HandshakeMsg):
+         w.addFixSeq(self.verify_data, 1)
+         return HandshakeMsg.postWrite(self, w, trial)
+ 
++class EncryptedExtensions(HandshakeMsg):
++    def __init__(self):
++        self.channel_id_key = None
++        self.channel_id_proof = None
++
++    def parse(self, p):
++        p.startLengthCheck(3)
++        soFar = 0
++        while soFar != p.lengthCheck:
++            extType = p.get(2)
++            extLength = p.get(2)
++            if extType == ExtensionType.channel_id:
++                if extLength != 32*4:
++                    raise SyntaxError()
++                self.channel_id_key = p.getFixBytes(64)
++                self.channel_id_proof = p.getFixBytes(64)
++            else:
++                p.getFixBytes(extLength)
++            soFar += 4 + extLength
++        p.stopLengthCheck()
++        return self
++
+ class ApplicationData(Msg):
+     def __init__(self):
+         self.contentType = ContentType.application_data
diff --git a/tlslite/TLSConnection.py b/tlslite/TLSConnection.py
index f8811a9..e882e2c 100644
--- a/tlslite/TLSConnection.py
+++ b/tlslite/TLSConnection.py
@@ -611,6 +611,8 @@
                                    settings.cipherImplementations)
 
             #Exchange ChangeCipherSpec and Finished messages
+            for result in self._getChangeCipherSpec():
+                yield result
             for result in self._getFinished():
                 yield result
             for result in self._sendFinished():
@@ -920,6 +922,8 @@
             #Exchange ChangeCipherSpec and Finished messages
             for result in self._sendFinished():
                 yield result
+            for result in self._getChangeCipherSpec():
+                yield result
             for result in self._getFinished():
                 yield result
 
@@ -1089,6 +1093,7 @@
         clientCertChain = None
         serverCertChain = None #We may set certChain to this later
         postFinishedError = None
+        doingChannelID = False
 
         #Tentatively set version to most-desirable version, so if an error
         #occurs parsing the ClientHello, this is what we'll use for the
@@ -1208,6 +1213,8 @@
                 serverHello.create(self.version, serverRandom,
                                    session.sessionID, session.cipherSuite,
                                    certificateType)
+                serverHello.channel_id = clientHello.channel_id
+                doingChannelID = clientHello.channel_id
                 for result in self._sendMsg(serverHello):
                     yield result
 
@@ -1221,6 +1228,11 @@
                 #Exchange ChangeCipherSpec and Finished messages
                 for result in self._sendFinished():
                     yield result
+                for result in self._getChangeCipherSpec():
+                    yield result
+                if doingChannelID:
+                    for result in self._getEncryptedExtensions():
+                        yield result
                 for result in self._getFinished():
                     yield result
 
@@ -1399,8 +1411,12 @@
             #Send ServerHello, Certificate[, CertificateRequest],
             #ServerHelloDone
             msgs = []
-            msgs.append(ServerHello().create(self.version, serverRandom,
-                        sessionID, cipherSuite, certificateType))
+            serverHello = ServerHello().create(
+                    self.version, serverRandom,
+                    sessionID, cipherSuite, certificateType)
+            serverHello.channel_id = clientHello.channel_id
+            doingChannelID = clientHello.channel_id
+            msgs.append(serverHello)
             msgs.append(Certificate(certificateType).create(serverCertChain))
             if reqCert and reqCAs:
                 msgs.append(CertificateRequest().create([], reqCAs))
@@ -1528,6 +1544,11 @@
                                settings.cipherImplementations)
 
         #Exchange ChangeCipherSpec and Finished messages
+        for result in self._getChangeCipherSpec():
+            yield result
+        if doingChannelID:
+            for result in self._getEncryptedExtensions():
+                yield result
         for result in self._getFinished():
             yield result
 
diff --git a/tlslite/TLSRecordLayer.py b/tlslite/TLSRecordLayer.py
index 1bbd09d..933b95a 100644
--- a/tlslite/TLSRecordLayer.py
+++ b/tlslite/TLSRecordLayer.py
@@ -714,6 +714,8 @@
                                             self.version).parse(p)
                 elif subType == HandshakeType.finished:
                     yield Finished(self.version).parse(p)
+                elif subType == HandshakeType.encrypted_extensions:
+                    yield EncryptedExtensions().parse(p)
                 else:
                     raise AssertionError()
 
@@ -1067,7 +1069,7 @@
         for result in self._sendMsg(finished):
             yield result
 
-    def _getFinished(self):
+    def _getChangeCipherSpec(self):
         #Get and check ChangeCipherSpec
         for result in self._getMsg(ContentType.change_cipher_spec):
             if result in (0,1):
@@ -1082,6 +1084,15 @@
         #Switch to pending read state
         self._changeReadState()
 
+    def _getEncryptedExtensions(self):
+        for result in self._getMsg(ContentType.handshake,
+                                   HandshakeType.encrypted_extensions):
+            if result in (0,1):
+                yield result
+        encrypted_extensions = result
+        self.channel_id = encrypted_extensions.channel_id_key
+
+    def _getFinished(self):
         #Calculate verification data
         verifyData = self._calcFinished(False)
 
diff --git a/tlslite/constants.py b/tlslite/constants.py
index 04302c0..e357dd0 100644
--- a/tlslite/constants.py
+++ b/tlslite/constants.py
@@ -22,6 +22,7 @@
     certificate_verify = 15
     client_key_exchange = 16
     finished = 20
+    encrypted_extensions = 203
 
 class ContentType:
     change_cipher_spec = 20
@@ -30,6 +31,9 @@
     application_data = 23
     all = (20,21,22,23)
 
+class ExtensionType:
+    channel_id = 30031
+
 class AlertLevel:
     warning = 1
     fatal = 2
diff --git a/tlslite/messages.py b/tlslite/messages.py
index dc6ed32..fa4d817 100644
--- a/tlslite/messages.py
+++ b/tlslite/messages.py
@@ -130,6 +130,7 @@
         self.certificate_types = [CertificateType.x509]
         self.compression_methods = []   # a list of 8-bit values
         self.srp_username = None        # a string
+        self.channel_id = False
 
     def create(self, version, random, session_id, cipher_suites,
                certificate_types=None, srp_username=None):
@@ -174,6 +175,8 @@
                         self.srp_username = bytesToString(p.getVarBytes(1))
                     elif extType == 7:
                         self.certificate_types = p.getVarList(1, 1)
+                    elif extType == ExtensionType.channel_id:
+                        self.channel_id = True
                     else:
                         p.getFixBytes(extLength)
                     soFar += 4 + extLength
@@ -220,6 +223,7 @@
         self.cipher_suite = 0
         self.certificate_type = CertificateType.x509
         self.compression_method = 0
+        self.channel_id = False
 
     def create(self, version, random, session_id, cipher_suite,
                certificate_type):
@@ -266,6 +270,9 @@
                 CertificateType.x509:
             extLength += 5
 
+        if self.channel_id:
+            extLength += 4
+
         if extLength != 0:
             w.add(extLength, 2)
 
@@ -275,6 +282,10 @@
             w.add(1, 2)
             w.add(self.certificate_type, 1)
 
+        if self.channel_id:
+            w.add(ExtensionType.channel_id, 2)
+            w.add(0, 2)
+
         return HandshakeMsg.postWrite(self, w, trial)
 
 class Certificate(HandshakeMsg):
@@ -567,6 +578,28 @@
         w.addFixSeq(self.verify_data, 1)
         return HandshakeMsg.postWrite(self, w, trial)
 
+class EncryptedExtensions(HandshakeMsg):
+    def __init__(self):
+        self.channel_id_key = None
+        self.channel_id_proof = None
+
+    def parse(self, p):
+        p.startLengthCheck(3)
+        soFar = 0
+        while soFar != p.lengthCheck:
+            extType = p.get(2)
+            extLength = p.get(2)
+            if extType == ExtensionType.channel_id:
+                if extLength != 32*4:
+                    raise SyntaxError()
+                self.channel_id_key = p.getFixBytes(64)
+                self.channel_id_proof = p.getFixBytes(64)
+            else:
+                p.getFixBytes(extLength)
+            soFar += 4 + extLength
+        p.stopLengthCheck()
+        return self
+
 class ApplicationData(Msg):
     def __init__(self):
         self.contentType = ContentType.application_data