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
This commit is contained in:
commit
745ac15cb1
43
alert.go
Normal file
43
alert.go
Normal file
@ -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;
|
||||||
|
}
|
123
common.go
Normal file
123
common.go
Normal file
@ -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;
|
||||||
|
}
|
148
prf.go
Normal file
148
prf.go
Normal file
@ -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);
|
||||||
|
}
|
104
prf_test.go
Normal file
104
prf_test.go
Normal file
@ -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,
|
||||||
|
},
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user