Skip to content

Commit

Permalink
Merge branch 'master' into staging-client
Browse files Browse the repository at this point in the history
  • Loading branch information
rod-hynes committed Feb 21, 2019
2 parents 042b58e + 2199640 commit b9c5051
Show file tree
Hide file tree
Showing 23 changed files with 576 additions and 76 deletions.
2 changes: 2 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ script:
- cd psiphon
- go test -race -v ./common
- go test -race -v ./common/accesscontrol
- go test -race -v ./common/crypto/ssh
- go test -race -v ./common/fragmentor
- go test -race -v ./common/obfuscator
- go test -race -v ./common/osl
Expand All @@ -33,6 +34,7 @@ script:
- go test -race -v
- go test -v -covermode=count -coverprofile=common.coverprofile ./common
- go test -v -covermode=count -coverprofile=accesscontrol.coverprofile ./common/accesscontrol
- go test -v -covermode=count -coverprofile=ssh.coverprofile ./common/crypto/ssh
- go test -v -covermode=count -coverprofile=fragmentor.coverprofile ./common/fragmentor
- go test -v -covermode=count -coverprofile=obfuscator.coverprofile ./common/obfuscator
- go test -v -covermode=count -coverprofile=osl.coverprofile ./common/osl
Expand Down
1 change: 1 addition & 0 deletions ConsoleClient/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,7 @@ func main() {
common.WriteRuntimeProfiles(
psiphon.NoticeCommonLogger(),
config.DataStoreDirectory,
"",
profileSampleDurationSeconds,
profileSampleDurationSeconds)
case <-systemStopSignal:
Expand Down
2 changes: 1 addition & 1 deletion MobileLibrary/Android/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ RUN curl -L http://dl.google.com/android/android-sdk_r24.4.1-linux.tgz -o /tmp/a

# Install Pinned Gomobile
# - Ordered last to allow use of previously cached layers when changing revisions
ENV GOMOBILE_PINNED_REV=320ec40f6328971c405979b804e20d5f3c86770c
ENV GOMOBILE_PINNED_REV=74ee969d3ffc95befa9a96a6590c36d71b6afd52
RUN mkdir -p $GOPATH/pkg/gomobile/dl \
&& cd $GOPATH/pkg/gomobile/dl \
&& curl -O https://dl.google.com/go/mobile/gomobile-ndk-r10e-linux-x86_64.tar.gz \
Expand Down
1 change: 1 addition & 0 deletions MobileLibrary/psi/psi.go
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@ func WriteRuntimeProfiles(outputDirectory string, cpuSampleDurationSeconds, bloc
common.WriteRuntimeProfiles(
psiphon.NoticeCommonLogger(),
outputDirectory,
"",
cpuSampleDurationSeconds,
blockSampleDurationSeconds)
}
Expand Down
1 change: 1 addition & 0 deletions Server/privatePlugins.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,5 @@ package main

import (
_ "github.com/Psiphon-Inc/psiphon-tunnel-core-private-plugins/common_plugins"
_ "github.com/Psiphon-Inc/psiphon-tunnel-core-private-plugins/server_plugins"
)
14 changes: 13 additions & 1 deletion psiphon/common/crypto/ssh/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,10 @@ var supportedKexAlgos = []string{
// P384 and P521 are not constant-time yet, but since we don't
// reuse ephemeral keys, using them for ECDH should be OK.
kexAlgoECDH256, kexAlgoECDH384, kexAlgoECDH521,
kexAlgoDH14SHA1, kexAlgoDH1SHA1,

// [Psiphon]
// Remove kexAlgoDH1SHA1 and add kexAlgoDH14SHA256
kexAlgoDH14SHA256, kexAlgoDH14SHA1,
}

// supportedHostKeyAlgos specifies the supported host-key algorithms (i.e. methods
Expand Down Expand Up @@ -216,8 +219,17 @@ type Config struct {
MACs []string

// [Psiphon]

// NoEncryptThenMACHash is used to disable Encrypt-then-MAC hash
// algorithms.
NoEncryptThenMACHash bool

// KEXPRNGSeed is used for KEX randomization and replay.
KEXPRNGSeed *prng.Seed

// PeerKEXPRNGSeed is used to predict KEX randomization and make
// adjustments to ensure negotiation succeeds.
PeerKEXPRNGSeed *prng.Seed
}

// SetDefaults sets sensible values for unset fields in config. This is
Expand Down
139 changes: 108 additions & 31 deletions psiphon/common/crypto/ssh/handshake.go
Original file line number Diff line number Diff line change
Expand Up @@ -469,24 +469,59 @@ func (t *handshakeTransport) sendKexInit() error {

// [Psiphon]
//
// Randomize KEX. The offered algorithms are shuffled and
// truncated (longer lists are selected with higher
// probability).
// When KEXPRNGSeed is specified, randomize the KEX. The offered
// algorithms are shuffled and truncated. Longer lists are selected with
// higher probability.
//
// As the client and server have the same set of algorithms,
// almost any combination is expected to be workable.
// When PeerKEXPRNGSeed is specified, the peer is expected to randomize
// its KEX using the specified seed; deterministically adjust own
// randomized KEX to ensure negotiation succeeds.
//
// The compression algorithm is not actually supported, but
// the server will not negotiate it.
// When NoEncryptThenMACHash is specified, do not use Encrypt-then-MAC has
// algorithms.

equal := func(list1, list2 []string) bool {
if len(list1) != len(list2) {
return false
}
for i, entry := range list1 {
if list2[i] != entry {
return false
}
}
return true
}

// Psiphon transforms assume that default algorithms are configured.
if (t.config.NoEncryptThenMACHash || t.config.KEXPRNGSeed != nil) &&
(!equal(t.config.KeyExchanges, supportedKexAlgos) ||
!equal(t.config.Ciphers, preferredCiphers) ||
!equal(t.config.MACs, supportedMACs)) {

return errors.New("ssh: custom algorithm preferences not supported")
}

// This is the list of supported non-Encrypt-then-MAC algorithms from
// https://github.com/Psiphon-Labs/psiphon-tunnel-core/blob/3ef11effe6acd9
// 2c3aefd140ee09c42a1f15630b/psiphon/common/crypto/ssh/common.go#L60
//
// The "t.remoteAddr != nil" condition should be true only
// for clients.
// With Encrypt-then-MAC hash algorithms, packet length is transmitted in
// plaintext, which aids in traffic analysis.
//
if t.remoteAddr != nil {
// When using obfuscated SSH, where only the initial, unencrypted
// packets are obfuscated, NoEncryptThenMACHash should be set.
noEncryptThenMACs := []string{"hmac-sha2-256", "hmac-sha1", "hmac-sha1-96"}

if t.config.NoEncryptThenMACHash {
msg.MACsClientServer = noEncryptThenMACs
msg.MACsServerClient = noEncryptThenMACs
}

if t.config.KEXPRNGSeed != nil {

PRNG := prng.NewPRNGWithSeed(t.config.KEXPRNGSeed)

permute := func(list []string) []string {
permute := func(PRNG *prng.PRNG, list []string) []string {
newList := make([]string, len(list))
perm := PRNG.Perm(len(list))
for i, j := range perm {
Expand All @@ -495,7 +530,7 @@ func (t *handshakeTransport) sendKexInit() error {
return newList
}

truncate := func(list []string) []string {
truncate := func(PRNG *prng.PRNG, list []string) []string {
cut := len(list)
for ; cut > 1; cut-- {
if !PRNG.FlipCoin() {
Expand All @@ -505,39 +540,81 @@ func (t *handshakeTransport) sendKexInit() error {
return list[:cut]
}

msg.KexAlgos = truncate(permute(t.config.KeyExchanges))
ciphers := truncate(permute(t.config.Ciphers))
retain := func(PRNG *prng.PRNG, list []string, item string) []string {
for _, entry := range list {
if entry == item {
return list
}
}
replace := PRNG.Intn(len(list))
list[replace] = item
return list
}

msg.KexAlgos = truncate(PRNG, permute(PRNG, msg.KexAlgos))
ciphers := truncate(PRNG, permute(PRNG, msg.CiphersClientServer))
msg.CiphersClientServer = ciphers
msg.CiphersServerClient = ciphers
MACs := truncate(permute(t.config.MACs))
MACs := truncate(PRNG, permute(PRNG, msg.MACsClientServer))
msg.MACsClientServer = MACs
msg.MACsServerClient = MACs

if len(t.hostKeys) > 0 {
msg.ServerHostKeyAlgos = permute(msg.ServerHostKeyAlgos)
msg.ServerHostKeyAlgos = permute(PRNG, msg.ServerHostKeyAlgos)
} else {
serverHostKeyAlgos := truncate(permute(msg.ServerHostKeyAlgos))

// Must offer KeyAlgoRSA to Psiphon server.
hasKeyAlgoRSA := false
for _, algo := range serverHostKeyAlgos {
if algo == KeyAlgoRSA {
hasKeyAlgoRSA = true
break
}
msg.ServerHostKeyAlgos = retain(
PRNG,
truncate(PRNG, permute(PRNG, msg.ServerHostKeyAlgos)),
KeyAlgoRSA)
}

if t.config.PeerKEXPRNGSeed != nil {

// Generate the peer KEX and make adjustments if negotiation would
// fail. This assumes that PeerKEXPRNGSeed remains static (in
// Psiphon, the peer is the server and PeerKEXPRNGSeed is derived
// from the server entry); and that the PRNG is invoked in the
// exact same order on the peer (i.e., the code block immediately
// above is what the peer runs); and that the peer sets
// NoEncryptThenMACHash in the same cases.

PeerPRNG := prng.NewPRNGWithSeed(t.config.PeerKEXPRNGSeed)

peerKexAlgos := truncate(PeerPRNG, permute(PeerPRNG, supportedKexAlgos))
if _, err := findCommon("", msg.KexAlgos, peerKexAlgos); err != nil {
msg.KexAlgos = retain(PRNG, msg.KexAlgos, peerKexAlgos[0])
}
if !hasKeyAlgoRSA {
replace := PRNG.Intn(len(serverHostKeyAlgos))
serverHostKeyAlgos[replace] = KeyAlgoRSA

peerCiphers := truncate(PeerPRNG, permute(PeerPRNG, preferredCiphers))
if _, err := findCommon("", ciphers, peerCiphers); err != nil {
ciphers = retain(PRNG, ciphers, peerCiphers[0])
msg.CiphersClientServer = ciphers
msg.CiphersServerClient = ciphers
}

msg.ServerHostKeyAlgos = serverHostKeyAlgos
peerMACs := supportedMACs
if t.config.NoEncryptThenMACHash {
peerMACs = noEncryptThenMACs
}

peerMACs = truncate(PeerPRNG, permute(PeerPRNG, peerMACs))
if _, err := findCommon("", MACs, peerMACs); err != nil {
MACs = retain(PRNG, MACs, peerMACs[0])
msg.MACsClientServer = MACs
msg.MACsServerClient = MACs
}
}

// Offer "[email protected]", which is offered by OpenSSH.
// Since server only supports "none", must always offer "none"
// Offer "[email protected]", which is offered by OpenSSH. Compression
// is not actually implemented, but since "[email protected]"
// compression is delayed until after authentication
// (https://www.openssh.com/txt/draft-miller-secsh-compression-
// delayed-00.txt), an unauthenticated probe of the SSH server will
// not detect this. "none" is always included to ensure negotiation
// succeeds.
if PRNG.FlipCoin() {
compressions := []string{"none", "[email protected]"}
compressions := permute(PRNG, []string{"none", "[email protected]"})
msg.CompressionClientServer = compressions
msg.CompressionServerClient = compressions
}
Expand Down
35 changes: 31 additions & 4 deletions psiphon/common/crypto/ssh/kex.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
const (
kexAlgoDH1SHA1 = "diffie-hellman-group1-sha1"
kexAlgoDH14SHA1 = "diffie-hellman-group14-sha1"
kexAlgoDH14SHA256 = "diffie-hellman-group14-sha256"
kexAlgoECDH256 = "ecdh-sha2-nistp256"
kexAlgoECDH384 = "ecdh-sha2-nistp384"
kexAlgoECDH521 = "ecdh-sha2-nistp521"
Expand Down Expand Up @@ -78,6 +79,9 @@ type kexAlgorithm interface {
// dhGroup is a multiplicative group suitable for implementing Diffie-Hellman key agreement.
type dhGroup struct {
g, p, pMinus1 *big.Int

// [Psiphon]
hashFunc crypto.Hash
}

func (group *dhGroup) diffieHellman(theirPublic, myPrivate *big.Int) (*big.Int, error) {
Expand All @@ -88,7 +92,9 @@ func (group *dhGroup) diffieHellman(theirPublic, myPrivate *big.Int) (*big.Int,
}

func (group *dhGroup) Client(c packetConn, randSource io.Reader, magics *handshakeMagics) (*kexResult, error) {
hashFunc := crypto.SHA1

// [Psiphon]
hashFunc := group.hashFunc

var x *big.Int
for {
Expand Down Expand Up @@ -138,12 +144,15 @@ func (group *dhGroup) Client(c packetConn, randSource io.Reader, magics *handsha
K: K,
HostKey: kexDHReply.HostKey,
Signature: kexDHReply.Signature,
Hash: crypto.SHA1,
Hash: hashFunc,
}, nil
}

func (group *dhGroup) Server(c packetConn, randSource io.Reader, magics *handshakeMagics, priv Signer) (result *kexResult, err error) {
hashFunc := crypto.SHA1

// [Psiphon]
hashFunc := group.hashFunc

packet, err := c.readPacket()
if err != nil {
return
Expand Down Expand Up @@ -203,7 +212,7 @@ func (group *dhGroup) Server(c packetConn, randSource io.Reader, magics *handsha
K: K,
HostKey: hostKeyBytes,
Signature: sig,
Hash: crypto.SHA1,
Hash: hashFunc,
}, nil
}

Expand Down Expand Up @@ -386,6 +395,8 @@ func init() {
g: new(big.Int).SetInt64(2),
p: p,
pMinus1: new(big.Int).Sub(p, bigOne),

hashFunc: crypto.SHA1,
}

// This is the group called diffie-hellman-group14-sha1 in RFC
Expand All @@ -396,6 +407,22 @@ func init() {
g: new(big.Int).SetInt64(2),
p: p,
pMinus1: new(big.Int).Sub(p, bigOne),

hashFunc: crypto.SHA1,
}

// [Psiphon]
// RFC 8268:
// > The method of key exchange used for the name "diffie-hellman-
// > group14-sha256" is the same as that for "diffie-hellman-group14-sha1"
// > except that the SHA256 hash algorithm is used.

kexAlgoMap[kexAlgoDH14SHA256] = &dhGroup{
g: new(big.Int).SetInt64(2),
p: p,
pMinus1: new(big.Int).Sub(p, bigOne),

hashFunc: crypto.SHA256,
}

kexAlgoMap[kexAlgoECDH521] = &ecdh{elliptic.P521()}
Expand Down
Loading

0 comments on commit b9c5051

Please sign in to comment.