diff --git a/src/pkg/crypto/tls/alert.go b/src/pkg/crypto/tls/alert.go new file mode 100644 index 0000000000..4cf62e7a4e --- /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 0000000000..31bdb84da7 --- /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 0000000000..fb2ae658ed --- /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 0000000000..dc7d3cca71 --- /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, + }, +}