| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157 |
- /*
- * Copyright (c) 2019, Psiphon Inc.
- * All rights reserved.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
- package ssh
- import (
- "bytes"
- "context"
- "crypto/rand"
- "crypto/rsa"
- "net"
- "testing"
- "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/prng"
- "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/errors"
- "golang.org/x/sync/errgroup"
- )
- func TestRandomizedSSHKEXes(t *testing.T) {
- rsaKey, err := rsa.GenerateKey(rand.Reader, 4096)
- if err != nil {
- t.Fatalf("rsa.GenerateKey failed: %s", err)
- }
- signer, err := NewSignerFromKey(rsaKey)
- if err != nil {
- t.Fatalf("NewSignerFromKey failed: %s", err)
- }
- publicKey := signer.PublicKey()
- username := "username"
- password := "password"
- for _, doPeerKEXPRNGSeed := range []bool{true, false} {
- failed := false
- for i := 0; i < 1000; i++ {
- clientSeed, err := prng.NewSeed()
- if err != nil {
- t.Fatalf("prng.NewSeed failed: %s", err)
- }
- serverSeed, err := prng.NewSeed()
- if err != nil {
- t.Fatalf("prng.NewSeed failed: %s", err)
- }
- clientConn, serverConn, err := netPipe()
- if err != nil {
- t.Fatalf("netPipe failed: %s", err)
- }
- testGroup, _ := errgroup.WithContext(context.Background())
- // Client
- testGroup.Go(func() error {
- certChecker := &CertChecker{
- HostKeyFallback: func(addr string, remote net.Addr, key PublicKey) error {
- if !bytes.Equal(publicKey.Marshal(), key.Marshal()) {
- return errors.TraceNew("unexpected host public key")
- }
- return nil
- },
- }
- clientConfig := &ClientConfig{
- User: username,
- Auth: []AuthMethod{Password(password)},
- HostKeyCallback: certChecker.CheckHostKey,
- }
- clientConfig.KEXPRNGSeed = clientSeed
- if doPeerKEXPRNGSeed {
- clientConfig.PeerKEXPRNGSeed = serverSeed
- }
- clientSSHConn, _, _, err := NewClientConn(clientConn, "", clientConfig)
- if err != nil {
- return errors.Trace(err)
- }
- clientSSHConn.Close()
- clientConn.Close()
- return nil
- })
- // Server
- testGroup.Go(func() error {
- insecurePasswordCallback := func(c ConnMetadata, pass []byte) (*Permissions, error) {
- if c.User() == username && string(pass) == password {
- return nil, nil
- }
- return nil, errors.TraceNew("authentication failed")
- }
- serverConfig := &ServerConfig{
- PasswordCallback: insecurePasswordCallback,
- }
- serverConfig.AddHostKey(signer)
- serverConfig.KEXPRNGSeed = serverSeed
- serverSSHConn, _, _, err := NewServerConn(serverConn, serverConfig)
- if err != nil {
- return errors.Trace(err)
- }
- serverSSHConn.Close()
- serverConn.Close()
- return nil
- })
- err = testGroup.Wait()
- if err != nil {
- // Expect no failure to negotiates when setting PeerKEXPRNGSeed.
- if doPeerKEXPRNGSeed {
- t.Fatalf("goroutine failed: %s", err)
- } else {
- failed = true
- break
- }
- }
- }
- // Expect at least one failure to negotiate when not setting PeerKEXPRNGSeed.
- if !doPeerKEXPRNGSeed && !failed {
- t.Fatalf("unexpected success")
- }
- }
- }
|