| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481 |
- package mint
- import (
- "bytes"
- "crypto"
- "crypto/x509"
- "encoding/binary"
- "fmt"
- "github.com/bifurcation/mint/syntax"
- )
- type HandshakeMessageBody interface {
- Type() HandshakeType
- Marshal() ([]byte, error)
- Unmarshal(data []byte) (int, error)
- }
- // struct {
- // ProtocolVersion legacy_version = 0x0303; /* TLS v1.2 */
- // Random random;
- // opaque legacy_session_id<0..32>;
- // CipherSuite cipher_suites<2..2^16-2>;
- // opaque legacy_compression_methods<1..2^8-1>;
- // Extension extensions<0..2^16-1>;
- // } ClientHello;
- type ClientHelloBody struct {
- LegacyVersion uint16
- Random [32]byte
- LegacySessionID []byte
- CipherSuites []CipherSuite
- Extensions ExtensionList
- }
- type clientHelloBodyInnerTLS struct {
- LegacyVersion uint16
- Random [32]byte
- LegacySessionID []byte `tls:"head=1,max=32"`
- CipherSuites []CipherSuite `tls:"head=2,min=2"`
- LegacyCompressionMethods []byte `tls:"head=1,min=1"`
- Extensions []Extension `tls:"head=2"`
- }
- type clientHelloBodyInnerDTLS struct {
- LegacyVersion uint16
- Random [32]byte
- LegacySessionID []byte `tls:"head=1,max=32"`
- EmptyCookie uint8
- CipherSuites []CipherSuite `tls:"head=2,min=2"`
- LegacyCompressionMethods []byte `tls:"head=1,min=1"`
- Extensions []Extension `tls:"head=2"`
- }
- func (ch ClientHelloBody) Type() HandshakeType {
- return HandshakeTypeClientHello
- }
- func (ch ClientHelloBody) Marshal() ([]byte, error) {
- if ch.LegacyVersion == tls12Version {
- return syntax.Marshal(clientHelloBodyInnerTLS{
- LegacyVersion: ch.LegacyVersion,
- Random: ch.Random,
- LegacySessionID: []byte{},
- CipherSuites: ch.CipherSuites,
- LegacyCompressionMethods: []byte{0},
- Extensions: ch.Extensions,
- })
- } else {
- return syntax.Marshal(clientHelloBodyInnerDTLS{
- LegacyVersion: ch.LegacyVersion,
- Random: ch.Random,
- LegacySessionID: []byte{},
- CipherSuites: ch.CipherSuites,
- LegacyCompressionMethods: []byte{0},
- Extensions: ch.Extensions,
- })
- }
- }
- func (ch *ClientHelloBody) Unmarshal(data []byte) (int, error) {
- var read int
- var err error
- // Note that this might be 0, in which case we do TLS. That
- // makes the tests easier.
- if ch.LegacyVersion != dtls12WireVersion {
- var inner clientHelloBodyInnerTLS
- read, err = syntax.Unmarshal(data, &inner)
- if err != nil {
- return 0, err
- }
- if len(inner.LegacyCompressionMethods) != 1 || inner.LegacyCompressionMethods[0] != 0 {
- return 0, fmt.Errorf("tls.clienthello: Invalid compression method")
- }
- ch.LegacyVersion = inner.LegacyVersion
- ch.Random = inner.Random
- ch.LegacySessionID = inner.LegacySessionID
- ch.CipherSuites = inner.CipherSuites
- ch.Extensions = inner.Extensions
- } else {
- var inner clientHelloBodyInnerDTLS
- read, err = syntax.Unmarshal(data, &inner)
- if err != nil {
- return 0, err
- }
- if inner.EmptyCookie != 0 {
- return 0, fmt.Errorf("tls.clienthello: Invalid cookie")
- }
- if len(inner.LegacyCompressionMethods) != 1 || inner.LegacyCompressionMethods[0] != 0 {
- return 0, fmt.Errorf("tls.clienthello: Invalid compression method")
- }
- ch.LegacyVersion = inner.LegacyVersion
- ch.Random = inner.Random
- ch.LegacySessionID = inner.LegacySessionID
- ch.CipherSuites = inner.CipherSuites
- ch.Extensions = inner.Extensions
- }
- return read, nil
- }
- // TODO: File a spec bug to clarify this
- func (ch ClientHelloBody) Truncated() ([]byte, error) {
- if len(ch.Extensions) == 0 {
- return nil, fmt.Errorf("tls.clienthello.truncate: No extensions")
- }
- pskExt := ch.Extensions[len(ch.Extensions)-1]
- if pskExt.ExtensionType != ExtensionTypePreSharedKey {
- return nil, fmt.Errorf("tls.clienthello.truncate: Last extension is not PSK")
- }
- body, err := ch.Marshal()
- if err != nil {
- return nil, err
- }
- chm := &HandshakeMessage{
- msgType: ch.Type(),
- body: body,
- length: uint32(len(body)),
- }
- chData := chm.Marshal()
- psk := PreSharedKeyExtension{
- HandshakeType: HandshakeTypeClientHello,
- }
- _, err = psk.Unmarshal(pskExt.ExtensionData)
- if err != nil {
- return nil, err
- }
- // Marshal just the binders so that we know how much to truncate
- binders := struct {
- Binders []PSKBinderEntry `tls:"head=2,min=33"`
- }{Binders: psk.Binders}
- binderData, _ := syntax.Marshal(binders)
- binderLen := len(binderData)
- chLen := len(chData)
- return chData[:chLen-binderLen], nil
- }
- // struct {
- // ProtocolVersion legacy_version = 0x0303; /* TLS v1.2 */
- // Random random;
- // opaque legacy_session_id_echo<0..32>;
- // CipherSuite cipher_suite;
- // uint8 legacy_compression_method = 0;
- // Extension extensions<6..2^16-1>;
- // } ServerHello;
- type ServerHelloBody struct {
- Version uint16
- Random [32]byte
- LegacySessionID []byte `tls:"head=1,max=32"`
- CipherSuite CipherSuite
- LegacyCompressionMethod uint8
- Extensions ExtensionList `tls:"head=2"`
- }
- func (sh ServerHelloBody) Type() HandshakeType {
- return HandshakeTypeServerHello
- }
- func (sh ServerHelloBody) Marshal() ([]byte, error) {
- return syntax.Marshal(sh)
- }
- func (sh *ServerHelloBody) Unmarshal(data []byte) (int, error) {
- return syntax.Unmarshal(data, sh)
- }
- // struct {
- // opaque verify_data[verify_data_length];
- // } Finished;
- //
- // verifyDataLen is not a field in the TLS struct, but we add it here so
- // that calling code can tell us how much data to expect when we marshal /
- // unmarshal. (We could add this to the marshal/unmarshal methods, but let's
- // try to keep the signature consistent for now.)
- //
- // For similar reasons, we don't use the `syntax` module here, because this
- // struct doesn't map well to standard TLS presentation language concepts.
- //
- // TODO: File a spec bug
- type FinishedBody struct {
- VerifyDataLen int
- VerifyData []byte
- }
- func (fin FinishedBody) Type() HandshakeType {
- return HandshakeTypeFinished
- }
- func (fin FinishedBody) Marshal() ([]byte, error) {
- if len(fin.VerifyData) != fin.VerifyDataLen {
- return nil, fmt.Errorf("tls.finished: data length mismatch")
- }
- body := make([]byte, len(fin.VerifyData))
- copy(body, fin.VerifyData)
- return body, nil
- }
- func (fin *FinishedBody) Unmarshal(data []byte) (int, error) {
- if len(data) < fin.VerifyDataLen {
- return 0, fmt.Errorf("tls.finished: Malformed finished; too short")
- }
- fin.VerifyData = make([]byte, fin.VerifyDataLen)
- copy(fin.VerifyData, data[:fin.VerifyDataLen])
- return fin.VerifyDataLen, nil
- }
- // struct {
- // Extension extensions<0..2^16-1>;
- // } EncryptedExtensions;
- //
- // Marshal() and Unmarshal() are handled by ExtensionList
- type EncryptedExtensionsBody struct {
- Extensions ExtensionList `tls:"head=2"`
- }
- func (ee EncryptedExtensionsBody) Type() HandshakeType {
- return HandshakeTypeEncryptedExtensions
- }
- func (ee EncryptedExtensionsBody) Marshal() ([]byte, error) {
- return syntax.Marshal(ee)
- }
- func (ee *EncryptedExtensionsBody) Unmarshal(data []byte) (int, error) {
- return syntax.Unmarshal(data, ee)
- }
- // opaque ASN1Cert<1..2^24-1>;
- //
- // struct {
- // ASN1Cert cert_data;
- // Extension extensions<0..2^16-1>
- // } CertificateEntry;
- //
- // struct {
- // opaque certificate_request_context<0..2^8-1>;
- // CertificateEntry certificate_list<0..2^24-1>;
- // } Certificate;
- type CertificateEntry struct {
- CertData *x509.Certificate
- Extensions ExtensionList
- }
- type CertificateBody struct {
- CertificateRequestContext []byte
- CertificateList []CertificateEntry
- }
- type certificateEntryInner struct {
- CertData []byte `tls:"head=3,min=1"`
- Extensions ExtensionList `tls:"head=2"`
- }
- type certificateBodyInner struct {
- CertificateRequestContext []byte `tls:"head=1"`
- CertificateList []certificateEntryInner `tls:"head=3"`
- }
- func (c CertificateBody) Type() HandshakeType {
- return HandshakeTypeCertificate
- }
- func (c CertificateBody) Marshal() ([]byte, error) {
- inner := certificateBodyInner{
- CertificateRequestContext: c.CertificateRequestContext,
- CertificateList: make([]certificateEntryInner, len(c.CertificateList)),
- }
- for i, entry := range c.CertificateList {
- inner.CertificateList[i] = certificateEntryInner{
- CertData: entry.CertData.Raw,
- Extensions: entry.Extensions,
- }
- }
- return syntax.Marshal(inner)
- }
- func (c *CertificateBody) Unmarshal(data []byte) (int, error) {
- inner := certificateBodyInner{}
- read, err := syntax.Unmarshal(data, &inner)
- if err != nil {
- return read, err
- }
- c.CertificateRequestContext = inner.CertificateRequestContext
- c.CertificateList = make([]CertificateEntry, len(inner.CertificateList))
- for i, entry := range inner.CertificateList {
- c.CertificateList[i].CertData, err = x509.ParseCertificate(entry.CertData)
- if err != nil {
- return 0, fmt.Errorf("tls:certificate: Certificate failed to parse: %v", err)
- }
- c.CertificateList[i].Extensions = entry.Extensions
- }
- return read, nil
- }
- // struct {
- // SignatureScheme algorithm;
- // opaque signature<0..2^16-1>;
- // } CertificateVerify;
- type CertificateVerifyBody struct {
- Algorithm SignatureScheme
- Signature []byte `tls:"head=2"`
- }
- func (cv CertificateVerifyBody) Type() HandshakeType {
- return HandshakeTypeCertificateVerify
- }
- func (cv CertificateVerifyBody) Marshal() ([]byte, error) {
- return syntax.Marshal(cv)
- }
- func (cv *CertificateVerifyBody) Unmarshal(data []byte) (int, error) {
- return syntax.Unmarshal(data, cv)
- }
- func (cv *CertificateVerifyBody) EncodeSignatureInput(data []byte) []byte {
- // TODO: Change context for client auth
- // TODO: Put this in a const
- const context = "TLS 1.3, server CertificateVerify"
- sigInput := bytes.Repeat([]byte{0x20}, 64)
- sigInput = append(sigInput, []byte(context)...)
- sigInput = append(sigInput, []byte{0}...)
- sigInput = append(sigInput, data...)
- return sigInput
- }
- func (cv *CertificateVerifyBody) Sign(privateKey crypto.Signer, handshakeHash []byte) (err error) {
- sigInput := cv.EncodeSignatureInput(handshakeHash)
- cv.Signature, err = sign(cv.Algorithm, privateKey, sigInput)
- logf(logTypeHandshake, "Signed: alg=[%04x] sigInput=[%x], sig=[%x]", cv.Algorithm, sigInput, cv.Signature)
- return
- }
- func (cv *CertificateVerifyBody) Verify(publicKey crypto.PublicKey, handshakeHash []byte) error {
- sigInput := cv.EncodeSignatureInput(handshakeHash)
- logf(logTypeHandshake, "About to verify: alg=[%04x] sigInput=[%x], sig=[%x]", cv.Algorithm, sigInput, cv.Signature)
- return verify(cv.Algorithm, publicKey, sigInput, cv.Signature)
- }
- // struct {
- // opaque certificate_request_context<0..2^8-1>;
- // Extension extensions<2..2^16-1>;
- // } CertificateRequest;
- type CertificateRequestBody struct {
- CertificateRequestContext []byte `tls:"head=1"`
- Extensions ExtensionList `tls:"head=2"`
- }
- func (cr CertificateRequestBody) Type() HandshakeType {
- return HandshakeTypeCertificateRequest
- }
- func (cr CertificateRequestBody) Marshal() ([]byte, error) {
- return syntax.Marshal(cr)
- }
- func (cr *CertificateRequestBody) Unmarshal(data []byte) (int, error) {
- return syntax.Unmarshal(data, cr)
- }
- // struct {
- // uint32 ticket_lifetime;
- // uint32 ticket_age_add;
- // opaque ticket_nonce<1..255>;
- // opaque ticket<1..2^16-1>;
- // Extension extensions<0..2^16-2>;
- // } NewSessionTicket;
- type NewSessionTicketBody struct {
- TicketLifetime uint32
- TicketAgeAdd uint32
- TicketNonce []byte `tls:"head=1,min=1"`
- Ticket []byte `tls:"head=2,min=1"`
- Extensions ExtensionList `tls:"head=2"`
- }
- const ticketNonceLen = 16
- func NewSessionTicket(ticketLen int, ticketLifetime uint32) (*NewSessionTicketBody, error) {
- buf := make([]byte, 4+ticketNonceLen+ticketLen)
- _, err := prng.Read(buf)
- if err != nil {
- return nil, err
- }
- tkt := &NewSessionTicketBody{
- TicketLifetime: ticketLifetime,
- TicketAgeAdd: binary.BigEndian.Uint32(buf[:4]),
- TicketNonce: buf[4 : 4+ticketNonceLen],
- Ticket: buf[4+ticketNonceLen:],
- }
- return tkt, err
- }
- func (tkt NewSessionTicketBody) Type() HandshakeType {
- return HandshakeTypeNewSessionTicket
- }
- func (tkt NewSessionTicketBody) Marshal() ([]byte, error) {
- return syntax.Marshal(tkt)
- }
- func (tkt *NewSessionTicketBody) Unmarshal(data []byte) (int, error) {
- return syntax.Unmarshal(data, tkt)
- }
- // enum {
- // update_not_requested(0), update_requested(1), (255)
- // } KeyUpdateRequest;
- //
- // struct {
- // KeyUpdateRequest request_update;
- // } KeyUpdate;
- type KeyUpdateBody struct {
- KeyUpdateRequest KeyUpdateRequest
- }
- func (ku KeyUpdateBody) Type() HandshakeType {
- return HandshakeTypeKeyUpdate
- }
- func (ku KeyUpdateBody) Marshal() ([]byte, error) {
- return syntax.Marshal(ku)
- }
- func (ku *KeyUpdateBody) Unmarshal(data []byte) (int, error) {
- return syntax.Unmarshal(data, ku)
- }
- // struct {} EndOfEarlyData;
- type EndOfEarlyDataBody struct{}
- func (eoed EndOfEarlyDataBody) Type() HandshakeType {
- return HandshakeTypeEndOfEarlyData
- }
- func (eoed EndOfEarlyDataBody) Marshal() ([]byte, error) {
- return []byte{}, nil
- }
- func (eoed *EndOfEarlyDataBody) Unmarshal(data []byte) (int, error) {
- return 0, nil
- }
|