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/1016029v1.2.3
@@ -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; | |||||
} |
@@ -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; | |||||
} |
@@ -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); | |||||
} |
@@ -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, | |||||
}, | |||||
} |