crypto/tls (part 1)
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
diff --git a/src/pkg/crypto/tls/alert.go b/src/pkg/crypto/tls/alert.go
new file mode 100644
index 0000000..4cf62e7
--- /dev/null
+++ b/src/pkg/crypto/tls/alert.go
@@ -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
index 0000000..31bdb84
--- /dev/null
+++ b/src/pkg/crypto/tls/common.go
@@ -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
index 0000000..fb2ae65
--- /dev/null
+++ b/src/pkg/crypto/tls/prf.go
@@ -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
index 0000000..dc7d3cc
--- /dev/null
+++ b/src/pkg/crypto/tls/prf_test.go
@@ -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,
+ },
+}