]> Cypherpunks repositories - gostls13.git/commitdiff
crypto/tls (part 1)
authorAdam Langley <agl@golang.org>
Tue, 3 Nov 2009 02:25:20 +0000 (18:25 -0800)
committerAdam Langley <agl@golang.org>
Tue, 3 Nov 2009 02:25:20 +0000 (18:25 -0800)
Rather than drop everything into a single, huge review, I've included
some simple bits of code here.

R=rsc
CC=go-dev
http://go/go-review/1016029

src/pkg/crypto/tls/alert.go [new file with mode: 0644]
src/pkg/crypto/tls/common.go [new file with mode: 0644]
src/pkg/crypto/tls/prf.go [new file with mode: 0644]
src/pkg/crypto/tls/prf_test.go [new file with mode: 0644]

diff --git a/src/pkg/crypto/tls/alert.go b/src/pkg/crypto/tls/alert.go
new file mode 100644 (file)
index 0000000..4cf62e7
--- /dev/null
@@ -0,0 +1,43 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package tls
+
+type alertLevel int
+type alertType int
+
+const (
+       alertLevelWarning       alertLevel      = 1;
+       alertLevelError         alertLevel      = 2;
+)
+
+const (
+       alertCloseNotify                alertType       = 0;
+       alertUnexpectedMessage          alertType       = 10;
+       alertBadRecordMAC               alertType       = 20;
+       alertDecryptionFailed           alertType       = 21;
+       alertRecordOverflow             alertType       = 22;
+       alertDecompressionFailure       alertType       = 30;
+       alertHandshakeFailure           alertType       = 40;
+       alertBadCertificate             alertType       = 42;
+       alertUnsupportedCertificate     alertType       = 43;
+       alertCertificateRevoked         alertType       = 44;
+       alertCertificateExpired         alertType       = 45;
+       alertCertificateUnknown         alertType       = 46;
+       alertIllegalParameter           alertType       = 47;
+       alertUnknownCA                  alertType       = 48;
+       alertAccessDenied               alertType       = 49;
+       alertDecodeError                alertType       = 50;
+       alertDecryptError               alertType       = 51;
+       alertProtocolVersion            alertType       = 70;
+       alertInsufficientSecurity       alertType       = 71;
+       alertInternalError              alertType       = 80;
+       alertUserCanceled               alertType       = 90;
+       alertNoRenegotiation            alertType       = 100;
+)
+
+type alert struct {
+       level   alertLevel;
+       error   alertType;
+}
diff --git a/src/pkg/crypto/tls/common.go b/src/pkg/crypto/tls/common.go
new file mode 100644 (file)
index 0000000..31bdb84
--- /dev/null
@@ -0,0 +1,123 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package tls
+
+import (
+       "crypto/rsa";
+       "io";
+       "os";
+)
+
+const (
+       // maxTLSCiphertext is the maximum length of a plaintext payload.
+       maxTLSPlaintext = 16384;
+       // maxTLSCiphertext is the maximum length payload after compression and encryption.
+       maxTLSCiphertext        = 16384+2048;
+       // maxHandshakeMsg is the largest single handshake message that we'll buffer.
+       maxHandshakeMsg = 65536;
+)
+
+
+// TLS record types.
+type recordType uint8
+
+const (
+       recordTypeChangeCipherSpec      recordType      = 20;
+       recordTypeAlert                 recordType      = 21;
+       recordTypeHandshake             recordType      = 22;
+       recordTypeApplicationData       recordType      = 23;
+)
+
+// TLS handshake message types.
+const (
+       typeClientHello         uint8   = 1;
+       typeServerHello         uint8   = 2;
+       typeCertificate         uint8   = 11;
+       typeServerHelloDone     uint8   = 14;
+       typeClientKeyExchange   uint8   = 16;
+       typeFinished            uint8   = 20;
+)
+
+// TLS cipher suites.
+var (
+       TLS_RSA_WITH_RC4_128_SHA uint16 = 5;
+)
+
+// TLS compression types.
+var (
+       compressionNone uint8 = 0;
+)
+
+type ConnectionState struct {
+       HandshakeComplete       bool;
+       CipherSuite             string;
+       Error                   alertType;
+}
+
+// A Config structure is used to configure a TLS client or server. After one
+// has been passed to a TLS function it must not be modified.
+type Config struct {
+       // Rand provides the source of entropy for nonces and RSA blinding.
+       Rand    io.Reader;
+       // Time returns the current time as the number of seconds since the epoch.
+       Time            func() int64;
+       Certificates    []Certificate;
+}
+
+type Certificate struct {
+       Certificate     [][]byte;
+       PrivateKey      *rsa.PrivateKey;
+}
+
+// A TLS record.
+type record struct {
+       contentType     recordType;
+       major, minor    uint8;
+       payload         []byte;
+}
+
+type handshakeMessage interface {
+       marshal() []byte;
+}
+
+type encryptor interface {
+       // XORKeyStream xors the contents of the slice with bytes from the key stream.
+       XORKeyStream(buf []byte);
+}
+
+// mutualVersion returns the protocol version to use given the advertised
+// version of the peer.
+func mutualVersion(theirMajor, theirMinor uint8) (major, minor uint8, ok bool) {
+       // We don't deal with peers < TLS 1.0 (aka version 3.1).
+       if theirMajor < 3 || theirMajor == 3 && theirMinor < 1 {
+               return 0, 0, false;
+       }
+       major = 3;
+       minor = 2;
+       if theirMinor < minor {
+               minor = theirMinor;
+       }
+       ok = true;
+       return;
+}
+
+// A nop implements the NULL encryption and MAC algorithms.
+type nop struct{}
+
+func (nop) XORKeyStream(buf []byte) {}
+
+func (nop) Write(buf []byte) (int, os.Error) {
+       return len(buf), nil;
+}
+
+func (nop) Sum() []byte {
+       return nil;
+}
+
+func (nop) Reset() {}
+
+func (nop) Size() int {
+       return 0;
+}
diff --git a/src/pkg/crypto/tls/prf.go b/src/pkg/crypto/tls/prf.go
new file mode 100644 (file)
index 0000000..fb2ae65
--- /dev/null
@@ -0,0 +1,148 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package tls
+
+import (
+       "bytes";
+       "crypto/hmac";
+       "crypto/md5";
+       "crypto/sha1";
+       "hash";
+       "os";
+       "strings";
+)
+
+// Split a premaster secret in two as specified in RFC 4346, section 5.
+func splitPreMasterSecret(secret []byte) (s1, s2 []byte) {
+       s1 = secret[0 : (len(secret)+1)/2];
+       s2 = secret[len(secret)/2 : len(secret)];
+       return;
+}
+
+// pHash implements the P_hash function, as defined in RFC 4346, section 5.
+func pHash(result, secret, seed []byte, hash hash.Hash) {
+       h := hmac.New(hash, secret);
+       h.Write(seed);
+       a := h.Sum();
+
+       j := 0;
+       for j < len(result) {
+               h.Reset();
+               h.Write(a);
+               h.Write(seed);
+               b := h.Sum();
+               todo := len(b);
+               if j+todo > len(result) {
+                       todo = len(result)-j;
+               }
+               bytes.Copy(result[j : j+todo], b);
+               j += todo;
+
+               h.Reset();
+               h.Write(a);
+               a = h.Sum();
+       }
+}
+
+// pRF11 implements the TLS 1.1 pseudo-random function, as defined in RFC 4346, section 5.
+func pRF11(result, secret, label, seed []byte) {
+       hashSHA1 := sha1.New();
+       hashMD5 := md5.New();
+
+       labelAndSeed := make([]byte, len(label)+len(seed));
+       bytes.Copy(labelAndSeed, label);
+       bytes.Copy(labelAndSeed[len(label):len(labelAndSeed)], seed);
+
+       s1, s2 := splitPreMasterSecret(secret);
+       pHash(result, s1, labelAndSeed, hashMD5);
+       result2 := make([]byte, len(result));
+       pHash(result2, s2, labelAndSeed, hashSHA1);
+
+       for i, b := range result2 {
+               result[i] ^= b;
+       }
+}
+
+const (
+       tlsRandomLength         = 32;   // Length of a random nonce in TLS 1.1.
+       masterSecretLength      = 48;   // Length of a master secret in TLS 1.1.
+       finishedVerifyLength    = 12;   // Length of verify_data in a Finished message.
+)
+
+var masterSecretLabel = strings.Bytes("master secret")
+var keyExpansionLabel = strings.Bytes("key expansion")
+var clientFinishedLabel = strings.Bytes("client finished")
+var serverFinishedLabel = strings.Bytes("server finished")
+
+// keysFromPreMasterSecret generates the connection keys from the pre master
+// secret, given the lengths of the MAC and cipher keys, as defined in RFC
+// 4346, section 6.3.
+func keysFromPreMasterSecret11(preMasterSecret, clientRandom, serverRandom []byte, macLen, keyLen int) (masterSecret, clientMAC, serverMAC, clientKey, serverKey []byte) {
+       var seed [tlsRandomLength * 2]byte;
+       bytes.Copy(seed[0:len(clientRandom)], clientRandom);
+       bytes.Copy(seed[len(clientRandom):len(seed)], serverRandom);
+       masterSecret = make([]byte, masterSecretLength);
+       pRF11(masterSecret, preMasterSecret, masterSecretLabel, seed[0:len(seed)]);
+
+       bytes.Copy(seed[0:len(clientRandom)], serverRandom);
+       bytes.Copy(seed[len(serverRandom):len(seed)], clientRandom);
+
+       n := 2*macLen + 2*keyLen;
+       keyMaterial := make([]byte, n);
+       pRF11(keyMaterial, masterSecret, keyExpansionLabel, seed[0:len(seed)]);
+       clientMAC = keyMaterial[0:macLen];
+       serverMAC = keyMaterial[macLen : macLen*2];
+       clientKey = keyMaterial[macLen*2 : macLen*2 + keyLen];
+       serverKey = keyMaterial[macLen*2 + keyLen : len(keyMaterial)];
+       return;
+}
+
+// A finishedHash calculates the hash of a set of handshake messages suitable
+// for including in a Finished message.
+type finishedHash struct {
+       clientMD5       hash.Hash;
+       clientSHA1      hash.Hash;
+       serverMD5       hash.Hash;
+       serverSHA1      hash.Hash;
+}
+
+func newFinishedHash() finishedHash {
+       return finishedHash{md5.New(), sha1.New(), md5.New(), sha1.New()};
+}
+
+func (h finishedHash) Write(msg []byte) (n int, err os.Error) {
+       h.clientMD5.Write(msg);
+       h.clientSHA1.Write(msg);
+       h.serverMD5.Write(msg);
+       h.serverSHA1.Write(msg);
+       return len(msg), nil;
+}
+
+// finishedSum calculates the contents of the verify_data member of a Finished
+// message given the MD5 and SHA1 hashes of a set of handshake messages.
+func finishedSum(md5, sha1, label, masterSecret []byte) []byte {
+       seed := make([]byte, len(md5)+len(sha1));
+       bytes.Copy(seed, md5);
+       bytes.Copy(seed[len(md5):len(seed)], sha1);
+       out := make([]byte, finishedVerifyLength);
+       pRF11(out, masterSecret, label, seed);
+       return out;
+}
+
+// clientSum returns the contents of the verify_data member of a client's
+// Finished message.
+func (h finishedHash) clientSum(masterSecret []byte) []byte {
+       md5 := h.clientMD5.Sum();
+       sha1 := h.clientSHA1.Sum();
+       return finishedSum(md5, sha1, clientFinishedLabel, masterSecret);
+}
+
+// serverSum returns the contents of the verify_data member of a server's
+// Finished message.
+func (h finishedHash) serverSum(masterSecret []byte) []byte {
+       md5 := h.serverMD5.Sum();
+       sha1 := h.serverSHA1.Sum();
+       return finishedSum(md5, sha1, serverFinishedLabel, masterSecret);
+}
diff --git a/src/pkg/crypto/tls/prf_test.go b/src/pkg/crypto/tls/prf_test.go
new file mode 100644 (file)
index 0000000..dc7d3cc
--- /dev/null
@@ -0,0 +1,104 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package tls
+
+import (
+       "encoding/hex";
+       "testing";
+)
+
+type testSplitPreMasterSecretTest struct {
+       in, out1, out2 string;
+}
+
+var testSplitPreMasterSecretTests = []testSplitPreMasterSecretTest{
+       testSplitPreMasterSecretTest{"", "", ""},
+       testSplitPreMasterSecretTest{"00", "00", "00"},
+       testSplitPreMasterSecretTest{"0011", "00", "11"},
+       testSplitPreMasterSecretTest{"001122", "0011", "1122"},
+       testSplitPreMasterSecretTest{"00112233", "0011", "2233"},
+}
+
+func TestSplitPreMasterSecret(t *testing.T) {
+       for i, test := range testSplitPreMasterSecretTests {
+               in, _ := hex.DecodeString(test.in);
+               out1, out2 := splitPreMasterSecret(in);
+               s1 := hex.EncodeToString(out1);
+               s2 := hex.EncodeToString(out2);
+               if s1 != test.out1 || s2 != test.out2 {
+                       t.Errorf("#%d: got: (%s, %s) want: (%s, %s)", i, s1, s2, test.out1, test.out2);
+               }
+       }
+}
+
+type testKeysFromTest struct {
+       preMasterSecret                 string;
+       clientRandom, serverRandom      string;
+       masterSecret                    string;
+       clientMAC, serverMAC            string;
+       clientKey, serverKey            string;
+       macLen, keyLen                  int;
+}
+
+func TestKeysFromPreMasterSecret(t *testing.T) {
+       for i, test := range testKeysFromTests {
+               in, _ := hex.DecodeString(test.preMasterSecret);
+               clientRandom, _ := hex.DecodeString(test.clientRandom);
+               serverRandom, _ := hex.DecodeString(test.serverRandom);
+               master, clientMAC, serverMAC, clientKey, serverKey := keysFromPreMasterSecret11(in, clientRandom, serverRandom, test.macLen, test.keyLen);
+               masterString := hex.EncodeToString(master);
+               clientMACString := hex.EncodeToString(clientMAC);
+               serverMACString := hex.EncodeToString(serverMAC);
+               clientKeyString := hex.EncodeToString(clientKey);
+               serverKeyString := hex.EncodeToString(serverKey);
+               if masterString != test.masterSecret ||
+                       clientMACString != test.clientMAC ||
+                       serverMACString != test.serverMAC ||
+                       clientKeyString != test.clientKey ||
+                       serverKeyString != test.serverKey {
+                       t.Errorf("#%d: got: (%s, %s, %s, %s, %s) want: (%s, %s, %s, %s %s)", i, masterString, clientMACString, serverMACString, clientKeyString, serverMACString, test.masterSecret, test.clientMAC, test.serverMAC, test.clientKey, test.serverKey);
+               }
+       }
+}
+
+// These test vectors were generated from GnuTLS using `gnutls-cli --insecure -d 9 `
+var testKeysFromTests = []testKeysFromTest{
+       testKeysFromTest{
+               "0302cac83ad4b1db3b9ab49ad05957de2a504a634a386fc600889321e1a971f57479466830ac3e6f468e87f5385fa0c5",
+               "4ae66303755184a3917fcb44880605fcc53baa01912b22ed94473fc69cebd558",
+               "4ae663020ec16e6bb5130be918cfcafd4d765979a3136a5d50c593446e4e44db",
+               "3d851bab6e5556e959a16bc36d66cfae32f672bfa9ecdef6096cbb1b23472df1da63dbbd9827606413221d149ed08ceb",
+               "805aaa19b3d2c0a0759a4b6c9959890e08480119",
+               "2d22f9fe519c075c16448305ceee209fc24ad109",
+               "d50b5771244f850cd8117a9ccafe2cf1",
+               "e076e33206b30507a85c32855acd0919",
+               20,
+               16,
+       },
+       testKeysFromTest{
+               "03023f7527316bc12cbcd69e4b9e8275d62c028f27e65c745cfcddc7ce01bd3570a111378b63848127f1c36e5f9e4890",
+               "4ae66364b5ea56b20ce4e25555aed2d7e67f42788dd03f3fee4adae0459ab106",
+               "4ae66363ab815cbf6a248b87d6b556184e945e9b97fbdf247858b0bdafacfa1c",
+               "7d64be7c80c59b740200b4b9c26d0baaa1c5ae56705acbcf2307fe62beb4728c19392c83f20483801cce022c77645460",
+               "97742ed60a0554ca13f04f97ee193177b971e3b0",
+               "37068751700400e03a8477a5c7eec0813ab9e0dc",
+               "207cddbc600d2a200abac6502053ee5c",
+               "df3f94f6e1eacc753b815fe16055cd43",
+               20,
+               16,
+       },
+       testKeysFromTest{
+               "832d515f1d61eebb2be56ba0ef79879efb9b527504abb386fb4310ed5d0e3b1f220d3bb6b455033a2773e6d8bdf951d278a187482b400d45deb88a5d5a6bb7d6a7a1decc04eb9ef0642876cd4a82d374d3b6ff35f0351dc5d411104de431375355addc39bfb1f6329fb163b0bc298d658338930d07d313cd980a7e3d9196cac1",
+               "4ae663b2ee389c0de147c509d8f18f5052afc4aaf9699efe8cb05ece883d3a5e",
+               "4ae664d503fd4cff50cfc1fb8fc606580f87b0fcdac9554ba0e01d785bdf278e",
+               "1aff2e7a2c4279d0126f57a65a77a8d9d0087cf2733366699bec27eb53d5740705a8574bb1acc2abbe90e44f0dd28d6c",
+               "3c7647c93c1379a31a609542aa44e7f117a70085",
+               "0d73102994be74a575a3ead8532590ca32a526d4",
+               "ac7581b0b6c10d85bbd905ffbf36c65e",
+               "ff07edde49682b45466bd2e39464b306",
+               20,
+               16,
+       },
+}