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,
+	},
+}