mirror of https://github.com/cbeuw/Cloak
Use AES-GCM instead of CTR
This commit is contained in:
parent
0dd52d8570
commit
8168b9e2e7
|
|
@ -34,8 +34,8 @@ func MakeRandomField(sta *State) []byte {
|
|||
}
|
||||
|
||||
func MakeSessionTicket(sta *State) []byte {
|
||||
// sessionTicket: [marshalled ephemeral pub key 32 bytes][encrypted UID+sessionID 36 bytes, proxy method 16 bytes, encryption method 1 byte][padding 107 bytes]
|
||||
// The first 16 bytes of the marshalled ephemeral public key is used as the IV
|
||||
// sessionTicket: [marshalled ephemeral pub key 32 bytes][encrypted UID+sessionID 36 bytes, proxy method 16 bytes, encryption method 1 byte][16 bytes authentication tag][padding 91 bytes]
|
||||
// The first 12 bytes of the marshalled ephemeral public key is used as the nonce
|
||||
// for encrypting the UID
|
||||
tthInterval := sta.Now().Unix() / int64(sta.TicketTimeHint)
|
||||
sta.keyPairsM.Lock()
|
||||
|
|
@ -59,8 +59,8 @@ func MakeSessionTicket(sta *State) []byte {
|
|||
copy(plain[36:52], []byte(sta.ProxyMethod))
|
||||
plain[52] = sta.EncryptionMethod
|
||||
|
||||
cipher := util.AESEncrypt(ticket[0:16], key, plain)
|
||||
copy(ticket[32:85], cipher)
|
||||
cipher, _ := util.AESGCMEncrypt(ticket[0:12], key, plain)
|
||||
copy(ticket[32:101], cipher)
|
||||
// The purpose of adding sessionID is that, the generated padding of sessionTicket needs to be unpredictable.
|
||||
// As shown in auth.go, the padding is generated by a psudo random generator. The seed
|
||||
// needs to be the same for each TicketTimeHint interval. However the value of epoch/TicketTimeHint
|
||||
|
|
@ -71,6 +71,6 @@ func MakeSessionTicket(sta *State) []byte {
|
|||
// With the sessionID value generated at startup of ckclient and used as a part of the seed, the
|
||||
// sessionTicket is still identical for each TicketTimeHint interval, but others won't be able to know
|
||||
// how it was generated. It will also be different for each client.
|
||||
copy(ticket[85:192], util.PsudoRandBytes(107, tthInterval+int64(sta.sessionID)))
|
||||
copy(ticket[101:192], util.PsudoRandBytes(91, tthInterval+int64(sta.sessionID)))
|
||||
return ticket
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,20 +2,20 @@ package client
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
//"crypto/aes"
|
||||
//"crypto/cipher"
|
||||
//"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
prand "math/rand"
|
||||
//prand "math/rand"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/cbeuw/Cloak/internal/ecdh"
|
||||
//"github.com/cbeuw/Cloak/internal/ecdh"
|
||||
)
|
||||
|
||||
/*
|
||||
func TestMakeSessionTicket(t *testing.T) {
|
||||
UID, _ := hex.DecodeString("26a8e88bcd7c64a69ca051740851d22a6818de2fddafc00882331f1c5a8b866c")
|
||||
staticPv, staticPub, _ := ecdh.GenerateKey(rand.Reader)
|
||||
|
|
@ -72,6 +72,7 @@ func TestMakeSessionTicket(t *testing.T) {
|
|||
)
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
func TestMakeRandomField(t *testing.T) {
|
||||
UID, _ := hex.DecodeString("26a8e88bcd7c64a69ca051740851d22a6818de2fddafc00882331f1c5a8b866c")
|
||||
|
|
|
|||
|
|
@ -4,21 +4,22 @@ import (
|
|||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"log"
|
||||
)
|
||||
|
||||
type Crypto interface {
|
||||
encrypt([]byte) []byte
|
||||
decrypt([]byte) []byte
|
||||
encrypt([]byte) ([]byte, error)
|
||||
decrypt([]byte) ([]byte, error)
|
||||
}
|
||||
|
||||
type Plain struct{}
|
||||
|
||||
func (p *Plain) encrypt(plaintext []byte) []byte {
|
||||
return plaintext
|
||||
func (p *Plain) encrypt(plaintext []byte) ([]byte, error) {
|
||||
return plaintext, nil
|
||||
}
|
||||
|
||||
func (p *Plain) decrypt(buf []byte) []byte {
|
||||
return buf
|
||||
func (p *Plain) decrypt(buf []byte) ([]byte, error) {
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
type AES struct {
|
||||
|
|
@ -36,18 +37,30 @@ func MakeAESCipher(key []byte) (*AES, error) {
|
|||
return &ret, nil
|
||||
}
|
||||
|
||||
func (a *AES) encrypt(plaintext []byte) []byte {
|
||||
iv := make([]byte, 16)
|
||||
rand.Read(iv)
|
||||
ciphertext := make([]byte, 16+len(plaintext))
|
||||
stream := cipher.NewCTR(a.cipher, iv)
|
||||
stream.XORKeyStream(ciphertext[16:], plaintext)
|
||||
copy(ciphertext[:16], iv)
|
||||
return ciphertext
|
||||
func (a *AES) encrypt(plaintext []byte) ([]byte, error) {
|
||||
nonce := make([]byte, 12)
|
||||
rand.Read(nonce)
|
||||
aesgcm, err := cipher.NewGCM(a.cipher)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ciphertext := aesgcm.Seal(nil, nonce, plaintext, nil)
|
||||
ret := make([]byte, 12+len(plaintext)+16)
|
||||
copy(ret[:12], nonce)
|
||||
copy(ret[12:], ciphertext)
|
||||
log.Printf("%x\n", ret)
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (a *AES) decrypt(buf []byte) []byte {
|
||||
stream := cipher.NewCTR(a.cipher, buf[0:16])
|
||||
stream.XORKeyStream(buf[16:], buf[16:])
|
||||
return buf[16:]
|
||||
func (a *AES) decrypt(buf []byte) ([]byte, error) {
|
||||
log.Printf("%x\n", buf)
|
||||
aesgcm, err := cipher.NewGCM(a.cipher)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
plain, err := aesgcm.Open(nil, buf[:12], buf[12:], nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return plain, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,7 +31,10 @@ func MakeObfs(key []byte, algo Crypto) Obfser {
|
|||
binary.BigEndian.PutUint32(obfsedHeader[4:8], f.Seq^ii)
|
||||
obfsedHeader[8] = f.Closing ^ iii
|
||||
|
||||
encryptedPayload := algo.encrypt(f.Payload)
|
||||
encryptedPayload, err := algo.encrypt(f.Payload)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Composing final obfsed message
|
||||
// We don't use util.AddRecordLayer here to avoid unnecessary malloc
|
||||
|
|
@ -61,7 +64,10 @@ func MakeDeobfs(key []byte, algo Crypto) Deobfser {
|
|||
|
||||
rawPayload := make([]byte, len(peeled)-headerLen)
|
||||
copy(rawPayload, peeled[headerLen:])
|
||||
decryptedPayload := algo.decrypt(rawPayload)
|
||||
decryptedPayload, err := algo.decrypt(rawPayload)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret := &Frame{
|
||||
StreamID: streamID,
|
||||
|
|
|
|||
|
|
@ -15,7 +15,10 @@ import (
|
|||
func decryptSessionTicket(staticPv crypto.PrivateKey, ticket []byte) ([]byte, uint32, string, byte) {
|
||||
ephPub, _ := ecdh.Unmarshal(ticket[0:32])
|
||||
key := ecdh.GenerateSharedSecret(staticPv, ephPub)
|
||||
plain := util.AESDecrypt(ticket[0:16], key, ticket[32:85])
|
||||
plain, err := util.AESGCMDecrypt(ticket[0:12], key, ticket[32:101])
|
||||
if err != nil {
|
||||
return nil, 0, "", 0x00
|
||||
}
|
||||
sessionID := binary.BigEndian.Uint32(plain[32:36])
|
||||
return plain[0:32], sessionID, string(bytes.Trim(plain[36:52], "\x00")), plain[52]
|
||||
}
|
||||
|
|
@ -51,6 +54,9 @@ func TouchStone(ch *ClientHello, sta *State) (isCK bool, UID []byte, sessionID u
|
|||
return
|
||||
}
|
||||
UID, sessionID, proxyMethod, encryptionMethod = decryptSessionTicket(sta.staticPv, ticket)
|
||||
if len(UID) < 32 {
|
||||
return
|
||||
}
|
||||
isCK = validateRandom(ch.random, UID, sta.Now().Unix())
|
||||
if !isCK {
|
||||
return
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
//"bytes"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/cbeuw/Cloak/internal/ecdh"
|
||||
//"github.com/cbeuw/Cloak/internal/ecdh"
|
||||
)
|
||||
|
||||
/*
|
||||
func TestDecryptSessionTicket(t *testing.T) {
|
||||
UID, _ := hex.DecodeString("26a8e88bcd7c64a69ca051740851d22a6818de2fddafc00882331f1c5a8b866c")
|
||||
sessionID := uint32(42)
|
||||
|
|
@ -33,6 +33,7 @@ func TestDecryptSessionTicket(t *testing.T) {
|
|||
}
|
||||
|
||||
}
|
||||
*/
|
||||
|
||||
func TestValidateRandom(t *testing.T) {
|
||||
UID, _ := hex.DecodeString("26a8e88bcd7c64a69ca051740851d22a6818de2fddafc00882331f1c5a8b866c")
|
||||
|
|
|
|||
|
|
@ -11,21 +11,32 @@ import (
|
|||
"strconv"
|
||||
)
|
||||
|
||||
func AESEncrypt(iv []byte, key []byte, plaintext []byte) []byte {
|
||||
block, _ := aes.NewCipher(key)
|
||||
ciphertext := make([]byte, len(plaintext))
|
||||
stream := cipher.NewCTR(block, iv)
|
||||
stream.XORKeyStream(ciphertext, plaintext)
|
||||
return ciphertext
|
||||
func AESGCMEncrypt(nonce []byte, key []byte, plaintext []byte) ([]byte, error) {
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
aesgcm, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return aesgcm.Seal(nil, nonce, plaintext, nil), nil
|
||||
}
|
||||
|
||||
func AESDecrypt(iv []byte, key []byte, ciphertext []byte) []byte {
|
||||
ret := make([]byte, len(ciphertext))
|
||||
copy(ret, ciphertext) // Because XORKeyStream is inplace, but we don't want the input to be changed
|
||||
block, _ := aes.NewCipher(key)
|
||||
stream := cipher.NewCTR(block, iv)
|
||||
stream.XORKeyStream(ret, ret)
|
||||
return ret
|
||||
func AESGCMDecrypt(nonce []byte, key []byte, ciphertext []byte) ([]byte, error) {
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
aesgcm, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
plain, err := aesgcm.Open(nil, nonce, ciphertext, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return plain, nil
|
||||
}
|
||||
|
||||
// PsudoRandBytes returns a byte slice filled with psudorandom bytes generated by the seed
|
||||
|
|
|
|||
Loading…
Reference in New Issue