|
- // 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
-
- // The handshake goroutine reads handshake messages from the record processor
- // and outputs messages to be written on another channel. It updates the record
- // processor with the state of the connection via the control channel. In the
- // case of handshake messages that need synchronous processing (because they
- // affect the handling of the next record) the record processor knows about
- // them and either waits for a control message (Finished) or includes a reply
- // channel in the message (ChangeCipherSpec).
-
- import (
- "crypto/hmac"
- "crypto/rc4"
- "crypto/rsa"
- "crypto/sha1"
- "crypto/subtle"
- "io"
- )
-
- type cipherSuite struct {
- id uint16 // The number of this suite on the wire.
- hashLength, cipherKeyLength int
- // TODO(agl): need a method to create the cipher and hash interfaces.
- }
-
- var cipherSuites = []cipherSuite{
- cipherSuite{TLS_RSA_WITH_RC4_128_SHA, 20, 16},
- }
-
- // A serverHandshake performs the server side of the TLS 1.1 handshake protocol.
- type serverHandshake struct {
- writeChan chan<- interface{}
- controlChan chan<- interface{}
- msgChan <-chan interface{}
- config *Config
- }
-
- func (h *serverHandshake) loop(writeChan chan<- interface{}, controlChan chan<- interface{}, msgChan <-chan interface{}, config *Config) {
- h.writeChan = writeChan
- h.controlChan = controlChan
- h.msgChan = msgChan
- h.config = config
-
- defer close(writeChan)
- defer close(controlChan)
-
- clientHello, ok := h.readHandshakeMsg().(*clientHelloMsg)
- if !ok {
- h.error(alertUnexpectedMessage)
- return
- }
- major, minor, ok := mutualVersion(clientHello.major, clientHello.minor)
- if !ok {
- h.error(alertProtocolVersion)
- return
- }
-
- finishedHash := newFinishedHash()
- finishedHash.Write(clientHello.marshal())
-
- hello := new(serverHelloMsg)
-
- // We only support a single ciphersuite so we look for it in the list
- // of client supported suites.
- //
- // TODO(agl): Add additional cipher suites.
- var suite *cipherSuite
-
- for _, id := range clientHello.cipherSuites {
- for _, supported := range cipherSuites {
- if supported.id == id {
- suite = &supported
- break
- }
- }
- }
-
- foundCompression := false
- // We only support null compression, so check that the client offered it.
- for _, compression := range clientHello.compressionMethods {
- if compression == compressionNone {
- foundCompression = true
- break
- }
- }
-
- if suite == nil || !foundCompression {
- h.error(alertHandshakeFailure)
- return
- }
-
- hello.major = major
- hello.minor = minor
- hello.cipherSuite = suite.id
- currentTime := uint32(config.Time())
- hello.random = make([]byte, 32)
- hello.random[0] = byte(currentTime >> 24)
- hello.random[1] = byte(currentTime >> 16)
- hello.random[2] = byte(currentTime >> 8)
- hello.random[3] = byte(currentTime)
- _, err := io.ReadFull(config.Rand, hello.random[4:])
- if err != nil {
- h.error(alertInternalError)
- return
- }
- hello.compressionMethod = compressionNone
- if clientHello.nextProtoNeg {
- hello.nextProtoNeg = true
- hello.nextProtos = config.NextProtos
- }
-
- finishedHash.Write(hello.marshal())
- writeChan <- writerSetVersion{major, minor}
- writeChan <- hello
-
- if len(config.Certificates) == 0 {
- h.error(alertInternalError)
- return
- }
-
- certMsg := new(certificateMsg)
- certMsg.certificates = config.Certificates[0].Certificate
- finishedHash.Write(certMsg.marshal())
- writeChan <- certMsg
-
- helloDone := new(serverHelloDoneMsg)
- finishedHash.Write(helloDone.marshal())
- writeChan <- helloDone
-
- ckx, ok := h.readHandshakeMsg().(*clientKeyExchangeMsg)
- if !ok {
- h.error(alertUnexpectedMessage)
- return
- }
- finishedHash.Write(ckx.marshal())
-
- preMasterSecret := make([]byte, 48)
- _, err = io.ReadFull(config.Rand, preMasterSecret[2:])
- if err != nil {
- h.error(alertInternalError)
- return
- }
-
- err = rsa.DecryptPKCS1v15SessionKey(config.Rand, config.Certificates[0].PrivateKey, ckx.ciphertext, preMasterSecret)
- if err != nil {
- h.error(alertHandshakeFailure)
- return
- }
- // We don't check the version number in the premaster secret. For one,
- // by checking it, we would leak information about the validity of the
- // encrypted pre-master secret. Secondly, it provides only a small
- // benefit against a downgrade attack and some implementations send the
- // wrong version anyway. See the discussion at the end of section
- // 7.4.7.1 of RFC 4346.
-
- masterSecret, clientMAC, serverMAC, clientKey, serverKey :=
- keysFromPreMasterSecret11(preMasterSecret, clientHello.random, hello.random, suite.hashLength, suite.cipherKeyLength)
-
- _, ok = h.readHandshakeMsg().(changeCipherSpec)
- if !ok {
- h.error(alertUnexpectedMessage)
- return
- }
-
- cipher, _ := rc4.NewCipher(clientKey)
- controlChan <- &newCipherSpec{cipher, hmac.New(sha1.New(), clientMAC)}
-
- clientProtocol := ""
- if hello.nextProtoNeg {
- nextProto, ok := h.readHandshakeMsg().(*nextProtoMsg)
- if !ok {
- h.error(alertUnexpectedMessage)
- return
- }
- finishedHash.Write(nextProto.marshal())
- clientProtocol = nextProto.proto
- }
-
- clientFinished, ok := h.readHandshakeMsg().(*finishedMsg)
- if !ok {
- h.error(alertUnexpectedMessage)
- return
- }
-
- verify := finishedHash.clientSum(masterSecret)
- if len(verify) != len(clientFinished.verifyData) ||
- subtle.ConstantTimeCompare(verify, clientFinished.verifyData) != 1 {
- h.error(alertHandshakeFailure)
- return
- }
-
- controlChan <- ConnectionState{true, "TLS_RSA_WITH_RC4_128_SHA", 0, clientProtocol}
-
- finishedHash.Write(clientFinished.marshal())
-
- cipher2, _ := rc4.NewCipher(serverKey)
- writeChan <- writerChangeCipherSpec{cipher2, hmac.New(sha1.New(), serverMAC)}
-
- finished := new(finishedMsg)
- finished.verifyData = finishedHash.serverSum(masterSecret)
- writeChan <- finished
-
- writeChan <- writerEnableApplicationData{}
-
- for {
- _, ok := h.readHandshakeMsg().(*clientHelloMsg)
- if !ok {
- h.error(alertUnexpectedMessage)
- return
- }
- // We reject all renegotication requests.
- writeChan <- alert{alertLevelWarning, alertNoRenegotiation}
- }
- }
-
- func (h *serverHandshake) readHandshakeMsg() interface{} {
- v := <-h.msgChan
- if closed(h.msgChan) {
- // If the channel closed then the processor received an error
- // from the peer and we don't want to echo it back to them.
- h.msgChan = nil
- return 0
- }
- if _, ok := v.(alert); ok {
- // We got an alert from the processor. We forward to the writer
- // and shutdown.
- h.writeChan <- v
- h.msgChan = nil
- return 0
- }
- return v
- }
-
- func (h *serverHandshake) error(e alertType) {
- if h.msgChan != nil {
- // If we didn't get an error from the processor, then we need
- // to tell it about the error.
- go func() {
- for _ = range h.msgChan {
- }
- }()
- h.controlChan <- ConnectionState{false, "", e, ""}
- close(h.controlChan)
- h.writeChan <- alert{alertLevelError, e}
- }
- }
|