| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422 |
- // SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
- // SPDX-License-Identifier: MIT
- package dtls
- import (
- "context"
- "crypto/rand"
- "crypto/x509"
- "github.com/pion/dtls/v2/internal/ciphersuite"
- "github.com/pion/dtls/v2/pkg/crypto/clientcertificate"
- "github.com/pion/dtls/v2/pkg/crypto/elliptic"
- "github.com/pion/dtls/v2/pkg/crypto/prf"
- "github.com/pion/dtls/v2/pkg/crypto/signaturehash"
- "github.com/pion/dtls/v2/pkg/protocol"
- "github.com/pion/dtls/v2/pkg/protocol/alert"
- "github.com/pion/dtls/v2/pkg/protocol/extension"
- "github.com/pion/dtls/v2/pkg/protocol/handshake"
- "github.com/pion/dtls/v2/pkg/protocol/recordlayer"
- inproxy_dtls "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/inproxy/dtls"
- "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/prng"
- )
- func flight4Parse(ctx context.Context, c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) (flightVal, *alert.Alert, error) { //nolint:gocognit
- seq, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence, state.cipherSuite,
- handshakeCachePullRule{handshake.TypeCertificate, cfg.initialEpoch, true, true},
- handshakeCachePullRule{handshake.TypeClientKeyExchange, cfg.initialEpoch, true, false},
- handshakeCachePullRule{handshake.TypeCertificateVerify, cfg.initialEpoch, true, true},
- )
- if !ok {
- // No valid message received. Keep reading
- return 0, nil, nil
- }
- // Validate type
- var clientKeyExchange *handshake.MessageClientKeyExchange
- if clientKeyExchange, ok = msgs[handshake.TypeClientKeyExchange].(*handshake.MessageClientKeyExchange); !ok {
- return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, nil
- }
- if h, hasCert := msgs[handshake.TypeCertificate].(*handshake.MessageCertificate); hasCert {
- state.PeerCertificates = h.Certificate
- // If the client offer its certificate, just disable session resumption.
- // Otherwise, we have to store the certificate identitfication and expire time.
- // And we have to check whether this certificate expired, revoked or changed.
- //
- // https://curl.se/docs/CVE-2016-5419.html
- state.SessionID = nil
- }
- if h, hasCertVerify := msgs[handshake.TypeCertificateVerify].(*handshake.MessageCertificateVerify); hasCertVerify {
- if state.PeerCertificates == nil {
- return 0, &alert.Alert{Level: alert.Fatal, Description: alert.NoCertificate}, errCertificateVerifyNoCertificate
- }
- plainText := cache.pullAndMerge(
- handshakeCachePullRule{handshake.TypeClientHello, cfg.initialEpoch, true, false},
- handshakeCachePullRule{handshake.TypeServerHello, cfg.initialEpoch, false, false},
- handshakeCachePullRule{handshake.TypeCertificate, cfg.initialEpoch, false, false},
- handshakeCachePullRule{handshake.TypeServerKeyExchange, cfg.initialEpoch, false, false},
- handshakeCachePullRule{handshake.TypeCertificateRequest, cfg.initialEpoch, false, false},
- handshakeCachePullRule{handshake.TypeServerHelloDone, cfg.initialEpoch, false, false},
- handshakeCachePullRule{handshake.TypeCertificate, cfg.initialEpoch, true, false},
- handshakeCachePullRule{handshake.TypeClientKeyExchange, cfg.initialEpoch, true, false},
- )
- // Verify that the pair of hash algorithm and signiture is listed.
- var validSignatureScheme bool
- for _, ss := range cfg.localSignatureSchemes {
- if ss.Hash == h.HashAlgorithm && ss.Signature == h.SignatureAlgorithm {
- validSignatureScheme = true
- break
- }
- }
- if !validSignatureScheme {
- return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, errNoAvailableSignatureSchemes
- }
- if err := verifyCertificateVerify(plainText, h.HashAlgorithm, h.Signature, state.PeerCertificates); err != nil {
- return 0, &alert.Alert{Level: alert.Fatal, Description: alert.BadCertificate}, err
- }
- var chains [][]*x509.Certificate
- var err error
- var verified bool
- if cfg.clientAuth >= VerifyClientCertIfGiven {
- if chains, err = verifyClientCert(state.PeerCertificates, cfg.clientCAs); err != nil {
- return 0, &alert.Alert{Level: alert.Fatal, Description: alert.BadCertificate}, err
- }
- verified = true
- }
- if cfg.verifyPeerCertificate != nil {
- if err := cfg.verifyPeerCertificate(state.PeerCertificates, chains); err != nil {
- return 0, &alert.Alert{Level: alert.Fatal, Description: alert.BadCertificate}, err
- }
- }
- state.peerCertificatesVerified = verified
- } else if state.PeerCertificates != nil {
- // A certificate was received, but we haven't seen a CertificateVerify
- // keep reading until we receive one
- return 0, nil, nil
- }
- if !state.cipherSuite.IsInitialized() {
- serverRandom := state.localRandom.MarshalFixed()
- clientRandom := state.remoteRandom.MarshalFixed()
- var err error
- var preMasterSecret []byte
- if state.cipherSuite.AuthenticationType() == CipherSuiteAuthenticationTypePreSharedKey {
- var psk []byte
- if psk, err = cfg.localPSKCallback(clientKeyExchange.IdentityHint); err != nil {
- return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
- }
- state.IdentityHint = clientKeyExchange.IdentityHint
- switch state.cipherSuite.KeyExchangeAlgorithm() {
- case CipherSuiteKeyExchangeAlgorithmPsk:
- preMasterSecret = prf.PSKPreMasterSecret(psk)
- case (CipherSuiteKeyExchangeAlgorithmPsk | CipherSuiteKeyExchangeAlgorithmEcdhe):
- if preMasterSecret, err = prf.EcdhePSKPreMasterSecret(psk, clientKeyExchange.PublicKey, state.localKeypair.PrivateKey, state.localKeypair.Curve); err != nil {
- return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
- }
- default:
- return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, errInvalidCipherSuite
- }
- } else {
- preMasterSecret, err = prf.PreMasterSecret(clientKeyExchange.PublicKey, state.localKeypair.PrivateKey, state.localKeypair.Curve)
- if err != nil {
- return 0, &alert.Alert{Level: alert.Fatal, Description: alert.IllegalParameter}, err
- }
- }
- if state.extendedMasterSecret {
- var sessionHash []byte
- sessionHash, err = cache.sessionHash(state.cipherSuite.HashFunc(), cfg.initialEpoch)
- if err != nil {
- return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
- }
- state.masterSecret, err = prf.ExtendedMasterSecret(preMasterSecret, sessionHash, state.cipherSuite.HashFunc())
- if err != nil {
- return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
- }
- } else {
- state.masterSecret, err = prf.MasterSecret(preMasterSecret, clientRandom[:], serverRandom[:], state.cipherSuite.HashFunc())
- if err != nil {
- return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
- }
- }
- if err := state.cipherSuite.Init(state.masterSecret, clientRandom[:], serverRandom[:], false); err != nil {
- return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
- }
- cfg.writeKeyLog(keyLogLabelTLS12, clientRandom[:], state.masterSecret)
- }
- if len(state.SessionID) > 0 {
- s := Session{
- ID: state.SessionID,
- Secret: state.masterSecret,
- }
- cfg.log.Tracef("[handshake] save new session: %x", s.ID)
- if err := cfg.sessionStore.Set(state.SessionID, s); err != nil {
- return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
- }
- }
- // Now, encrypted packets can be handled
- if err := c.handleQueuedPackets(ctx); err != nil {
- return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
- }
- seq, msgs, ok = cache.fullPullMap(seq, state.cipherSuite,
- handshakeCachePullRule{handshake.TypeFinished, cfg.initialEpoch + 1, true, false},
- )
- if !ok {
- // No valid message received. Keep reading
- return 0, nil, nil
- }
- state.handshakeRecvSequence = seq
- if _, ok = msgs[handshake.TypeFinished].(*handshake.MessageFinished); !ok {
- return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, nil
- }
- if state.cipherSuite.AuthenticationType() == CipherSuiteAuthenticationTypeAnonymous {
- if cfg.verifyConnection != nil {
- if err := cfg.verifyConnection(state.clone()); err != nil {
- return 0, &alert.Alert{Level: alert.Fatal, Description: alert.BadCertificate}, err
- }
- }
- return flight6, nil, nil
- }
- switch cfg.clientAuth {
- case RequireAnyClientCert:
- if state.PeerCertificates == nil {
- return 0, &alert.Alert{Level: alert.Fatal, Description: alert.NoCertificate}, errClientCertificateRequired
- }
- case VerifyClientCertIfGiven:
- if state.PeerCertificates != nil && !state.peerCertificatesVerified {
- return 0, &alert.Alert{Level: alert.Fatal, Description: alert.BadCertificate}, errClientCertificateNotVerified
- }
- case RequireAndVerifyClientCert:
- if state.PeerCertificates == nil {
- return 0, &alert.Alert{Level: alert.Fatal, Description: alert.NoCertificate}, errClientCertificateRequired
- }
- if !state.peerCertificatesVerified {
- return 0, &alert.Alert{Level: alert.Fatal, Description: alert.BadCertificate}, errClientCertificateNotVerified
- }
- case NoClientCert, RequestClientCert:
- // go to flight6
- }
- if cfg.verifyConnection != nil {
- if err := cfg.verifyConnection(state.clone()); err != nil {
- return 0, &alert.Alert{Level: alert.Fatal, Description: alert.BadCertificate}, err
- }
- }
- return flight6, nil, nil
- }
- func flight4Generate(ctx context.Context, c flightConn, state *State, _ *handshakeCache, cfg *handshakeConfig) ([]*packet, *alert.Alert, error) {
- extensions := []extension.Extension{&extension.RenegotiationInfo{
- RenegotiatedConnection: 0,
- }}
- if (cfg.extendedMasterSecret == RequestExtendedMasterSecret ||
- cfg.extendedMasterSecret == RequireExtendedMasterSecret) && state.extendedMasterSecret {
- extensions = append(extensions, &extension.UseExtendedMasterSecret{
- Supported: true,
- })
- }
- if state.getSRTPProtectionProfile() != 0 {
- extensions = append(extensions, &extension.UseSRTP{
- ProtectionProfiles: []SRTPProtectionProfile{state.getSRTPProtectionProfile()},
- })
- }
- if state.cipherSuite.AuthenticationType() == CipherSuiteAuthenticationTypeCertificate {
- extensions = append(extensions, &extension.SupportedPointFormats{
- PointFormats: []elliptic.CurvePointFormat{elliptic.CurvePointFormatUncompressed},
- })
- }
- selectedProto, err := extension.ALPNProtocolSelection(cfg.supportedProtocols, state.peerSupportedProtocols)
- if err != nil {
- return nil, &alert.Alert{Level: alert.Fatal, Description: alert.NoApplicationProtocol}, err
- }
- if selectedProto != "" {
- extensions = append(extensions, &extension.ALPN{
- ProtocolNameList: []string{selectedProto},
- })
- state.NegotiatedProtocol = selectedProto
- }
- var pkts []*packet
- cipherSuiteID := uint16(state.cipherSuite.ID())
- if cfg.sessionStore != nil {
- state.SessionID = make([]byte, sessionLength)
- if _, err := rand.Read(state.SessionID); err != nil {
- return nil, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
- }
- }
- // [Psiphon]
- // Randomize ServerHello
- seed, err := inproxy_dtls.GetDTLSSeed(ctx)
- if err != nil {
- return nil, nil, err
- }
- if seed != nil {
- PRNG := prng.NewPRNGWithSeed(seed)
- PRNG.Shuffle(len(extensions), func(i, j int) {
- extensions[i], extensions[j] = extensions[j], extensions[i]
- })
- }
- pkts = append(pkts, &packet{
- record: &recordlayer.RecordLayer{
- Header: recordlayer.Header{
- Version: protocol.Version1_2,
- },
- Content: &handshake.Handshake{
- Message: &handshake.MessageServerHello{
- Version: protocol.Version1_2,
- Random: state.localRandom,
- SessionID: state.SessionID,
- CipherSuiteID: &cipherSuiteID,
- CompressionMethod: defaultCompressionMethods()[0],
- Extensions: extensions,
- },
- },
- },
- })
- switch {
- case state.cipherSuite.AuthenticationType() == CipherSuiteAuthenticationTypeCertificate:
- certificate, err := cfg.getCertificate(&ClientHelloInfo{
- ServerName: state.serverName,
- CipherSuites: []ciphersuite.ID{state.cipherSuite.ID()},
- // [Psiphon]
- // Conjure DTLS support, from: https://github.com/mingyech/dtls/commit/a56eccc1
- RandomBytes: state.remoteRandom.RandomBytes,
- })
- if err != nil {
- return nil, &alert.Alert{Level: alert.Fatal, Description: alert.HandshakeFailure}, err
- }
- pkts = append(pkts, &packet{
- record: &recordlayer.RecordLayer{
- Header: recordlayer.Header{
- Version: protocol.Version1_2,
- },
- Content: &handshake.Handshake{
- Message: &handshake.MessageCertificate{
- Certificate: certificate.Certificate,
- },
- },
- },
- })
- serverRandom := state.localRandom.MarshalFixed()
- clientRandom := state.remoteRandom.MarshalFixed()
- // Find compatible signature scheme
- signatureHashAlgo, err := signaturehash.SelectSignatureScheme(cfg.localSignatureSchemes, certificate.PrivateKey)
- if err != nil {
- return nil, &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, err
- }
- signature, err := generateKeySignature(clientRandom[:], serverRandom[:], state.localKeypair.PublicKey, state.namedCurve, certificate.PrivateKey, signatureHashAlgo.Hash)
- if err != nil {
- return nil, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
- }
- state.localKeySignature = signature
- pkts = append(pkts, &packet{
- record: &recordlayer.RecordLayer{
- Header: recordlayer.Header{
- Version: protocol.Version1_2,
- },
- Content: &handshake.Handshake{
- Message: &handshake.MessageServerKeyExchange{
- EllipticCurveType: elliptic.CurveTypeNamedCurve,
- NamedCurve: state.namedCurve,
- PublicKey: state.localKeypair.PublicKey,
- HashAlgorithm: signatureHashAlgo.Hash,
- SignatureAlgorithm: signatureHashAlgo.Signature,
- Signature: state.localKeySignature,
- },
- },
- },
- })
- if cfg.clientAuth > NoClientCert {
- // An empty list of certificateAuthorities signals to
- // the client that it may send any certificate in response
- // to our request. When we know the CAs we trust, then
- // we can send them down, so that the client can choose
- // an appropriate certificate to give to us.
- var certificateAuthorities [][]byte
- if cfg.clientCAs != nil {
- // nolint:staticcheck // ignoring tlsCert.RootCAs.Subjects is deprecated ERR because cert does not come from SystemCertPool and it's ok if certificate authorities is empty.
- certificateAuthorities = cfg.clientCAs.Subjects()
- }
- pkts = append(pkts, &packet{
- record: &recordlayer.RecordLayer{
- Header: recordlayer.Header{
- Version: protocol.Version1_2,
- },
- Content: &handshake.Handshake{
- Message: &handshake.MessageCertificateRequest{
- CertificateTypes: []clientcertificate.Type{clientcertificate.RSASign, clientcertificate.ECDSASign},
- SignatureHashAlgorithms: cfg.localSignatureSchemes,
- CertificateAuthoritiesNames: certificateAuthorities,
- },
- },
- },
- })
- }
- case cfg.localPSKIdentityHint != nil || state.cipherSuite.KeyExchangeAlgorithm().Has(CipherSuiteKeyExchangeAlgorithmEcdhe):
- // To help the client in selecting which identity to use, the server
- // can provide a "PSK identity hint" in the ServerKeyExchange message.
- // If no hint is provided and cipher suite doesn't use elliptic curve,
- // the ServerKeyExchange message is omitted.
- //
- // https://tools.ietf.org/html/rfc4279#section-2
- srvExchange := &handshake.MessageServerKeyExchange{
- IdentityHint: cfg.localPSKIdentityHint,
- }
- if state.cipherSuite.KeyExchangeAlgorithm().Has(CipherSuiteKeyExchangeAlgorithmEcdhe) {
- srvExchange.EllipticCurveType = elliptic.CurveTypeNamedCurve
- srvExchange.NamedCurve = state.namedCurve
- srvExchange.PublicKey = state.localKeypair.PublicKey
- }
- pkts = append(pkts, &packet{
- record: &recordlayer.RecordLayer{
- Header: recordlayer.Header{
- Version: protocol.Version1_2,
- },
- Content: &handshake.Handshake{
- Message: srvExchange,
- },
- },
- })
- }
- pkts = append(pkts, &packet{
- record: &recordlayer.RecordLayer{
- Header: recordlayer.Header{
- Version: protocol.Version1_2,
- },
- Content: &handshake.Handshake{
- Message: &handshake.MessageServerHelloDone{},
- },
- },
- })
- return pkts, nil, nil
- }
|