Refactor touchStone

This commit is contained in:
Andy Wang 2019-08-31 21:40:50 +01:00
parent a3c3c5dc07
commit 3a28bb65d5
3 changed files with 71 additions and 30 deletions

View File

@ -2,11 +2,13 @@ package server
import ( import (
"bytes" "bytes"
"crypto"
"crypto/rand" "crypto/rand"
"encoding/binary" "encoding/binary"
"encoding/hex" "encoding/hex"
"errors" "errors"
"fmt" "fmt"
"github.com/cbeuw/Cloak/internal/ecdh"
"github.com/cbeuw/Cloak/internal/util" "github.com/cbeuw/Cloak/internal/util"
"net" "net"
@ -222,6 +224,30 @@ var ErrNotCloak = errors.New("TLS but non-Cloak ClientHello")
var ErrReplay = errors.New("duplicate random") var ErrReplay = errors.New("duplicate random")
var ErrBadProxyMethod = errors.New("invalid proxy method") var ErrBadProxyMethod = errors.New("invalid proxy method")
func unmarshalClientHello(ch *ClientHello, staticPv crypto.PrivateKey) (ai authenticationInfo, err error) {
ephPub, ok := ecdh.Unmarshal(ch.random)
if !ok {
err = ErrInvalidPubKey
return
}
ai.nonce = ch.random[:12]
ai.sharedSecret = ecdh.GenerateSharedSecret(staticPv, ephPub)
var keyShare []byte
keyShare, err = parseKeyShare(ch.extensions[[2]byte{0x00, 0x33}])
if err != nil {
return
}
ai.ciphertextWithTag = append(ch.sessionId, keyShare...)
if len(ai.ciphertextWithTag) != 64 {
err = fmt.Errorf("%v: %v", ErrCiphertextLength, len(ai.ciphertextWithTag))
return
}
return
}
// PrepareConnection checks if the first packet of data is ClientHello, and checks if it was from a Cloak client // PrepareConnection checks if the first packet of data is ClientHello, and checks if it was from a Cloak client
// if it is from a Cloak client, it returns the ClientInfo with the decrypted fields. It doesn't check if the user // if it is from a Cloak client, it returns the ClientInfo with the decrypted fields. It doesn't check if the user
// is authorised. It also returns a finisher callback function to be called when the caller wishes to proceed with // is authorised. It also returns a finisher callback function to be called when the caller wishes to proceed with
@ -239,8 +265,12 @@ func PrepareConnection(firstPacket []byte, sta *State, conn net.Conn) (info Clie
return return
} }
var sharedSecret []byte var ai authenticationInfo
info, sharedSecret, err = touchStone(ch, sta.staticPv, sta.Now) ai, err = unmarshalClientHello(ch, sta.staticPv)
if err != nil {
return
}
info, err = touchStone(ai, sta.Now)
if err != nil { if err != nil {
log.Debug(err) log.Debug(err)
err = ErrNotCloak err = ErrNotCloak
@ -252,7 +282,7 @@ func PrepareConnection(firstPacket []byte, sta *State, conn net.Conn) (info Clie
} }
finisher = func(sessionKey []byte) error { finisher = func(sessionKey []byte) error {
reply, err := composeReply(ch, sharedSecret, sessionKey) reply, err := composeReply(ch, ai.sharedSecret, sessionKey)
if err != nil { if err != nil {
return err return err
} }

View File

@ -2,11 +2,9 @@ package server
import ( import (
"bytes" "bytes"
"crypto"
"encoding/binary" "encoding/binary"
"errors" "errors"
"fmt" "fmt"
"github.com/cbeuw/Cloak/internal/ecdh"
"github.com/cbeuw/Cloak/internal/util" "github.com/cbeuw/Cloak/internal/util"
"time" "time"
) )
@ -19,6 +17,12 @@ type ClientInfo struct {
Unordered bool Unordered bool
} }
type authenticationInfo struct {
sharedSecret []byte
nonce []byte
ciphertextWithTag []byte
}
const ( const (
UNORDERED_FLAG = 0x01 // 0000 0001 UNORDERED_FLAG = 0x01 // 0000 0001
) )
@ -29,28 +33,10 @@ var ErrTimestampOutOfWindow = errors.New("timestamp is outside of the accepting
// touchStone checks if a ClientHello came from a Cloak client by checking and decrypting the fields Cloak hides data in // touchStone checks if a ClientHello came from a Cloak client by checking and decrypting the fields Cloak hides data in
// It returns the ClientInfo, but it doesn't check if the UID is authorised // It returns the ClientInfo, but it doesn't check if the UID is authorised
func touchStone(ch *ClientHello, staticPv crypto.PrivateKey, now func() time.Time) (info ClientInfo, sharedSecret []byte, err error) { func touchStone(ai authenticationInfo, now func() time.Time) (info ClientInfo, err error) {
ephPub, ok := ecdh.Unmarshal(ch.random)
if !ok {
err = ErrInvalidPubKey
return
}
sharedSecret = ecdh.GenerateSharedSecret(staticPv, ephPub)
var keyShare []byte
keyShare, err = parseKeyShare(ch.extensions[[2]byte{0x00, 0x33}])
if err != nil {
return
}
ciphertext := append(ch.sessionId, keyShare...)
if len(ciphertext) != 64 {
err = fmt.Errorf("%v: %v", ErrCiphertextLength, len(ciphertext))
return
}
var plaintext []byte var plaintext []byte
plaintext, err = util.AESGCMDecrypt(ch.random[0:12], sharedSecret, ciphertext) plaintext, err = util.AESGCMDecrypt(ai.nonce, ai.sharedSecret, ai.ciphertextWithTag)
if err != nil { if err != nil {
return return
} }

View File

@ -16,9 +16,14 @@ func TestTouchStone(t *testing.T) {
t.Run("correct time", func(t *testing.T) { t.Run("correct time", func(t *testing.T) {
chBytes, _ := hex.DecodeString("1603010200010001fc0303ac530b5778469dbbc3f9a83c6ac35b63aa6a70c2014026ade30f2faf0266f0242068424f320bcad49b4315a761f9f6dec32b0a403c2d8c0ab337608a694c6e411c0024130113031302c02bc02fcca9cca8c02cc030c00ac009c013c01400330039002f0035000a0100018f00000011000f00000c7777772e62696e672e636f6d00170000ff01000100000a000e000c001d00170018001901000101000b00020100002300000010000e000c02683208687474702f312e310005000501000000000033006b0069001d00204655c2c83aaed1db2e89ed17d671fcdc76dc96e36bde8840022f1bda2f31019600170041543af1f8d28b37d984073f40e8361613da502f16e4039f00656f427de0f66480b2e77e3e552e126bb0cc097168f6e5454c7f9501126a2377fb40151f6cfc007e0e002b0009080304030303020301000d0018001604030503060308040805080604010501060102030201002d00020101001c00024001001500920000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000") chBytes, _ := hex.DecodeString("1603010200010001fc0303ac530b5778469dbbc3f9a83c6ac35b63aa6a70c2014026ade30f2faf0266f0242068424f320bcad49b4315a761f9f6dec32b0a403c2d8c0ab337608a694c6e411c0024130113031302c02bc02fcca9cca8c02cc030c00ac009c013c01400330039002f0035000a0100018f00000011000f00000c7777772e62696e672e636f6d00170000ff01000100000a000e000c001d00170018001901000101000b00020100002300000010000e000c02683208687474702f312e310005000501000000000033006b0069001d00204655c2c83aaed1db2e89ed17d671fcdc76dc96e36bde8840022f1bda2f31019600170041543af1f8d28b37d984073f40e8361613da502f16e4039f00656f427de0f66480b2e77e3e552e126bb0cc097168f6e5454c7f9501126a2377fb40151f6cfc007e0e002b0009080304030303020301000d0018001604030503060308040805080604010501060102030201002d00020101001c00024001001500920000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")
ch, _ := parseClientHello(chBytes) ch, _ := parseClientHello(chBytes)
ai, err := unmarshalClientHello(ch, staticPv)
if err != nil {
t.Errorf("expecting no error, got %v", err)
return
}
nineSixSix := func() time.Time { return time.Unix(1565998966, 0) } nineSixSix := func() time.Time { return time.Unix(1565998966, 0) }
cinfo, _, err := touchStone(ch, staticPv, nineSixSix) cinfo, err := touchStone(ai, nineSixSix)
if err != nil { if err != nil {
t.Errorf("expecting no error, got %v", err) t.Errorf("expecting no error, got %v", err)
return return
@ -30,9 +35,14 @@ func TestTouchStone(t *testing.T) {
t.Run("over interval", func(t *testing.T) { t.Run("over interval", func(t *testing.T) {
chBytes, _ := hex.DecodeString("1603010200010001fc0303ac530b5778469dbbc3f9a83c6ac35b63aa6a70c2014026ade30f2faf0266f0242068424f320bcad49b4315a761f9f6dec32b0a403c2d8c0ab337608a694c6e411c0024130113031302c02bc02fcca9cca8c02cc030c00ac009c013c01400330039002f0035000a0100018f00000011000f00000c7777772e62696e672e636f6d00170000ff01000100000a000e000c001d00170018001901000101000b00020100002300000010000e000c02683208687474702f312e310005000501000000000033006b0069001d00204655c2c83aaed1db2e89ed17d671fcdc76dc96e36bde8840022f1bda2f31019600170041543af1f8d28b37d984073f40e8361613da502f16e4039f00656f427de0f66480b2e77e3e552e126bb0cc097168f6e5454c7f9501126a2377fb40151f6cfc007e0e002b0009080304030303020301000d0018001604030503060308040805080604010501060102030201002d00020101001c00024001001500920000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000") chBytes, _ := hex.DecodeString("1603010200010001fc0303ac530b5778469dbbc3f9a83c6ac35b63aa6a70c2014026ade30f2faf0266f0242068424f320bcad49b4315a761f9f6dec32b0a403c2d8c0ab337608a694c6e411c0024130113031302c02bc02fcca9cca8c02cc030c00ac009c013c01400330039002f0035000a0100018f00000011000f00000c7777772e62696e672e636f6d00170000ff01000100000a000e000c001d00170018001901000101000b00020100002300000010000e000c02683208687474702f312e310005000501000000000033006b0069001d00204655c2c83aaed1db2e89ed17d671fcdc76dc96e36bde8840022f1bda2f31019600170041543af1f8d28b37d984073f40e8361613da502f16e4039f00656f427de0f66480b2e77e3e552e126bb0cc097168f6e5454c7f9501126a2377fb40151f6cfc007e0e002b0009080304030303020301000d0018001604030503060308040805080604010501060102030201002d00020101001c00024001001500920000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")
ch, _ := parseClientHello(chBytes) ch, _ := parseClientHello(chBytes)
ai, err := unmarshalClientHello(ch, staticPv)
if err != nil {
t.Errorf("expecting no error, got %v", err)
return
}
nineSixSixOver := func() time.Time { return time.Unix(1565998966, 0).Add(TIMESTAMP_TOLERANCE + 10) } nineSixSixOver := func() time.Time { return time.Unix(1565998966, 0).Add(TIMESTAMP_TOLERANCE + 10) }
_, _, err := touchStone(ch, staticPv, nineSixSixOver) _, err = touchStone(ai, nineSixSixOver)
if err == nil { if err == nil {
t.Errorf("expecting %v, got %v", ErrTimestampOutOfWindow, err) t.Errorf("expecting %v, got %v", ErrTimestampOutOfWindow, err)
return return
@ -41,9 +51,14 @@ func TestTouchStone(t *testing.T) {
t.Run("under interval", func(t *testing.T) { t.Run("under interval", func(t *testing.T) {
chBytes, _ := hex.DecodeString("1603010200010001fc0303ac530b5778469dbbc3f9a83c6ac35b63aa6a70c2014026ade30f2faf0266f0242068424f320bcad49b4315a761f9f6dec32b0a403c2d8c0ab337608a694c6e411c0024130113031302c02bc02fcca9cca8c02cc030c00ac009c013c01400330039002f0035000a0100018f00000011000f00000c7777772e62696e672e636f6d00170000ff01000100000a000e000c001d00170018001901000101000b00020100002300000010000e000c02683208687474702f312e310005000501000000000033006b0069001d00204655c2c83aaed1db2e89ed17d671fcdc76dc96e36bde8840022f1bda2f31019600170041543af1f8d28b37d984073f40e8361613da502f16e4039f00656f427de0f66480b2e77e3e552e126bb0cc097168f6e5454c7f9501126a2377fb40151f6cfc007e0e002b0009080304030303020301000d0018001604030503060308040805080604010501060102030201002d00020101001c00024001001500920000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000") chBytes, _ := hex.DecodeString("1603010200010001fc0303ac530b5778469dbbc3f9a83c6ac35b63aa6a70c2014026ade30f2faf0266f0242068424f320bcad49b4315a761f9f6dec32b0a403c2d8c0ab337608a694c6e411c0024130113031302c02bc02fcca9cca8c02cc030c00ac009c013c01400330039002f0035000a0100018f00000011000f00000c7777772e62696e672e636f6d00170000ff01000100000a000e000c001d00170018001901000101000b00020100002300000010000e000c02683208687474702f312e310005000501000000000033006b0069001d00204655c2c83aaed1db2e89ed17d671fcdc76dc96e36bde8840022f1bda2f31019600170041543af1f8d28b37d984073f40e8361613da502f16e4039f00656f427de0f66480b2e77e3e552e126bb0cc097168f6e5454c7f9501126a2377fb40151f6cfc007e0e002b0009080304030303020301000d0018001604030503060308040805080604010501060102030201002d00020101001c00024001001500920000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")
ch, _ := parseClientHello(chBytes) ch, _ := parseClientHello(chBytes)
ai, err := unmarshalClientHello(ch, staticPv)
if err != nil {
t.Errorf("expecting no error, got %v", err)
return
}
nineSixSixUnder := func() time.Time { return time.Unix(1565998966, 0).Add(TIMESTAMP_TOLERANCE - 10) } nineSixSixUnder := func() time.Time { return time.Unix(1565998966, 0).Add(TIMESTAMP_TOLERANCE - 10) }
_, _, err := touchStone(ch, staticPv, nineSixSixUnder) _, err = touchStone(ai, nineSixSixUnder)
if err == nil { if err == nil {
t.Errorf("expecting %v, got %v", ErrTimestampOutOfWindow, err) t.Errorf("expecting %v, got %v", ErrTimestampOutOfWindow, err)
return return
@ -52,9 +67,14 @@ func TestTouchStone(t *testing.T) {
t.Run("not cloak psk", func(t *testing.T) { t.Run("not cloak psk", func(t *testing.T) {
chBytes, _ := hex.DecodeString("1603010246010002420303794ae79c6db7a31e67e2ce91b8afcb82995ae79ad1d0dc885f933e4193bf95cd208abd7a70f3b82cc31c02f1c2b94ba74d5222a66695a5cf92a366421d7f5eb9530022fafa130113021303c02bc02fc02cc030cca9cca8c013c014009c009d002f0035000a010001d75a5a00000000001e001c0000196c68332e676f6f676c6575736572636f6e74656e742e636f6d00170000ff01000100000a000a0008baba001d00170018000b00020100002300000010000e000c02683208687474702f312e31000500050100000000000d00140012040308040401050308050501080606010201001200000033002b0029baba000100001d002074bfe93336c364b43cf0879d997b2e11dc97068b86fc90174e0f2bcea1d4ed1c002d00020101002b000b0ababa0304030303020301001b00030200029a9a0001000029010500e000da00d1f6c0918f865390ae3ca33c77f61a1974cb4533456071b214ec018d17dc22845f2f72cf1dba48f9cdc0758803002dda9b964fad5522e82442af7cbbe242241e39233386f2383bce3ced8e16b1ae3f0ef52a706f58e1e6a1bca0cd3b3a2a4c4cb738770b01b56bf3e73c472bf4fb238cab510aa78f8427a3ca99f741aa433f548be460705f43a3abe878cec6ee3158c129406910b93e798e8a7aaffc2e7ff7b8fd872778d3687a0beaa1452fe7ec418070d537344b64d09f6edd053346ff9c9678eef6b8886882aba81d4be11d9df653de35659f93a22ac39399e3ba400021204e22b73261693967a9216fe4a3b004571c53f316309e76671a18d78931b5b072") chBytes, _ := hex.DecodeString("1603010246010002420303794ae79c6db7a31e67e2ce91b8afcb82995ae79ad1d0dc885f933e4193bf95cd208abd7a70f3b82cc31c02f1c2b94ba74d5222a66695a5cf92a366421d7f5eb9530022fafa130113021303c02bc02fc02cc030cca9cca8c013c014009c009d002f0035000a010001d75a5a00000000001e001c0000196c68332e676f6f676c6575736572636f6e74656e742e636f6d00170000ff01000100000a000a0008baba001d00170018000b00020100002300000010000e000c02683208687474702f312e31000500050100000000000d00140012040308040401050308050501080606010201001200000033002b0029baba000100001d002074bfe93336c364b43cf0879d997b2e11dc97068b86fc90174e0f2bcea1d4ed1c002d00020101002b000b0ababa0304030303020301001b00030200029a9a0001000029010500e000da00d1f6c0918f865390ae3ca33c77f61a1974cb4533456071b214ec018d17dc22845f2f72cf1dba48f9cdc0758803002dda9b964fad5522e82442af7cbbe242241e39233386f2383bce3ced8e16b1ae3f0ef52a706f58e1e6a1bca0cd3b3a2a4c4cb738770b01b56bf3e73c472bf4fb238cab510aa78f8427a3ca99f741aa433f548be460705f43a3abe878cec6ee3158c129406910b93e798e8a7aaffc2e7ff7b8fd872778d3687a0beaa1452fe7ec418070d537344b64d09f6edd053346ff9c9678eef6b8886882aba81d4be11d9df653de35659f93a22ac39399e3ba400021204e22b73261693967a9216fe4a3b004571c53f316309e76671a18d78931b5b072")
ch, _ := parseClientHello(chBytes) ch, _ := parseClientHello(chBytes)
ai, err := unmarshalClientHello(ch, staticPv)
if err != nil {
t.Errorf("expecting no error, got %v", err)
return
}
fiveOSix := func() time.Time { return time.Unix(1565999506, 0) } fiveOSix := func() time.Time { return time.Unix(1565999506, 0) }
cinfo, _, err := touchStone(ch, staticPv, fiveOSix) cinfo, err := touchStone(ai, fiveOSix)
if err == nil { if err == nil {
t.Errorf("not a cloak, got nil error and cinfo %v", cinfo) t.Errorf("not a cloak, got nil error and cinfo %v", cinfo)
return return
@ -63,9 +83,14 @@ func TestTouchStone(t *testing.T) {
t.Run("not cloak no psk", func(t *testing.T) { t.Run("not cloak no psk", func(t *testing.T) {
chBytes, _ := hex.DecodeString("1603010200010001fc0303eae4c204a867390a758fcff3afa5803cac3e07011cf0c9f3befc1267445aabee20fc398df698113617f8161cbcb89534efa892088a6c5e49246534e05f790ea36f00220a0a130113021303c02bc02fc02cc030cca9cca8c013c014009c009d002f0035000a010001910a0a000000000014001200000f63646e2e62697a69626c652e636f6d00170000ff01000100000a000a0008caca001d00170018000b00020100002300000010000e000c02683208687474702f312e31000500050100000000000d00140012040308040401050308050501080606010201001200000033002b0029caca000100001d00204c8f1563fb70c261bc0c32c1b568b8d02fab25f4094711e7868b1712751dc754002d00020101002b000b0a2a2a0304030303020301001b00030200026a6a000100001500c9000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000") chBytes, _ := hex.DecodeString("1603010200010001fc0303eae4c204a867390a758fcff3afa5803cac3e07011cf0c9f3befc1267445aabee20fc398df698113617f8161cbcb89534efa892088a6c5e49246534e05f790ea36f00220a0a130113021303c02bc02fc02cc030cca9cca8c013c014009c009d002f0035000a010001910a0a000000000014001200000f63646e2e62697a69626c652e636f6d00170000ff01000100000a000a0008caca001d00170018000b00020100002300000010000e000c02683208687474702f312e31000500050100000000000d00140012040308040401050308050501080606010201001200000033002b0029caca000100001d00204c8f1563fb70c261bc0c32c1b568b8d02fab25f4094711e7868b1712751dc754002d00020101002b000b0a2a2a0304030303020301001b00030200026a6a000100001500c9000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")
ch, _ := parseClientHello(chBytes) ch, _ := parseClientHello(chBytes)
ai, err := unmarshalClientHello(ch, staticPv)
if err != nil {
t.Errorf("expecting no error, got %v", err)
return
}
sixOneFive := func() time.Time { return time.Unix(1565999615, 0) } sixOneFive := func() time.Time { return time.Unix(1565999615, 0) }
cinfo, _, err := touchStone(ch, staticPv, sixOneFive) cinfo, err := touchStone(ai, sixOneFive)
if err == nil { if err == nil {
t.Errorf("not a cloak, got nil error and cinfo %v", cinfo) t.Errorf("not a cloak, got nil error and cinfo %v", cinfo)
return return