diff --git a/cmd/ck-client/ck-client.go b/cmd/ck-client/ck-client.go index c484e7e..9cb509a 100644 --- a/cmd/ck-client/ck-client.go +++ b/cmd/ck-client/ck-client.go @@ -4,6 +4,7 @@ package main import ( "encoding/base64" + "encoding/binary" "flag" "fmt" "io" @@ -176,16 +177,18 @@ start: log.Println("Attemtping to start a new session") // sessionID is usergenerated. There shouldn't be a security concern because the scope of // sessionID is limited to its UID. - rand.Seed(time.Now().UnixNano()) - sessionID := rand.Uint32() + quad := make([]byte, 4) + rand.Read(quad) + sta.SessionID = binary.BigEndian.Uint32(quad) + + sta.UpdateIntervalKeys() if adminUID != nil { - sessionID = 0 + sta.SessionID = 0 sta.UID = adminUID sta.NumConn = 1 } - sta.SetSessionID(sessionID) var crypto mux.Crypto switch sta.EncryptionMethod { case 0x00: @@ -204,11 +207,8 @@ start: } } - sessionKey := make([]byte, 32) - rand.Read(sessionKey) - sta.SessionKey = sessionKey - - sesh := mux.MakeSession(sessionID, mux.UNLIMITED_VALVE, mux.MakeObfs(sta.SessionKey, crypto), mux.MakeDeobfs(sta.SessionKey, crypto), util.ReadTLS) + _, tthKey, _ := sta.GetIntervalKeys() + sesh := mux.MakeSession(sta.SessionID, mux.UNLIMITED_VALVE, mux.MakeObfs(tthKey, crypto), mux.MakeDeobfs(tthKey, crypto), util.ReadTLS) var wg sync.WaitGroup for i := 0; i < sta.NumConn; i++ { @@ -227,7 +227,7 @@ start: } wg.Wait() - log.Printf("Session %v established", sessionID) + log.Printf("Session %v established", sta.SessionID) for { if sesh.IsBroken() { diff --git a/cmd/ck-server/ck-server.go b/cmd/ck-server/ck-server.go index be575fc..1002cf3 100644 --- a/cmd/ck-server/ck-server.go +++ b/cmd/ck-server/ck-server.go @@ -73,7 +73,7 @@ func dispatchConnection(conn net.Conn, sta *server.State) { return } - isCloak, UID, sessionID, proxyMethod, encryptionMethod, sessionKey := server.TouchStone(ch, sta) + isCloak, UID, sessionID, proxyMethod, encryptionMethod, tthKey := server.TouchStone(ch, sta) if !isCloak { log.Printf("+1 non Cloak TLS traffic from %v\n", conn.RemoteAddr()) goWeb(data) @@ -109,8 +109,8 @@ func dispatchConnection(conn net.Conn, sta *server.State) { return } - obfs := mux.MakeObfs(sessionKey, crypto) - deobfs := mux.MakeDeobfs(sessionKey, crypto) + obfs := mux.MakeObfs(tthKey, crypto) + deobfs := mux.MakeDeobfs(tthKey, crypto) finishHandshake := func() error { reply := server.ComposeReply(ch) diff --git a/internal/client/auth.go b/internal/client/auth.go index 7850d51..a55ac04 100644 --- a/internal/client/auth.go +++ b/internal/client/auth.go @@ -1,67 +1,52 @@ package client import ( - "crypto" "crypto/rand" "crypto/sha256" "encoding/binary" - "io" - "github.com/cbeuw/Cloak/internal/ecdh" "github.com/cbeuw/Cloak/internal/util" ) -type keyPair struct { - crypto.PrivateKey - crypto.PublicKey -} - func MakeRandomField(sta *State) []byte { + // [4 bytes sessionId] [12 bytes random] [16 bytes hash] t := make([]byte, 8) binary.BigEndian.PutUint64(t, uint64(sta.Now().Unix()/(12*60*60))) - rdm := make([]byte, 16) - io.ReadFull(rand.Reader, rdm) + + front := make([]byte, 16) + binary.BigEndian.PutUint32(front[0:4], sta.SessionID) + rand.Read(front[4:]) preHash := make([]byte, 56) copy(preHash[0:32], sta.UID) copy(preHash[32:40], t) - copy(preHash[40:56], rdm) + copy(preHash[40:56], front) h := sha256.New() h.Write(preHash) + ret := make([]byte, 32) - copy(ret[0:16], rdm) + copy(ret[0:16], front) copy(ret[16:32], h.Sum(nil)[0:16]) return ret } func MakeSessionTicket(sta *State) []byte { - // sessionTicket: [marshalled ephemeral pub key 32 bytes][encrypted UID+sessionID 20 bytes, proxy method 16 bytes, encryption method 1 byte, sessionKey 32 bytes][16 bytes authentication tag][padding 75 bytes] + // sessionTicket: [marshalled ephemeral pub key 32 bytes][encrypted UID 16 bytes, proxy method 16 bytes, encryption method 1 byte][16 bytes authentication tag][padding 111 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() - ephKP := sta.keyPairs[tthInterval] - if ephKP == nil { - ephPv, ephPub, _ := ecdh.GenerateKey(rand.Reader) - ephKP = &keyPair{ - ephPv, - ephPub, - } - sta.keyPairs[tthInterval] = ephKP - } - sta.keyPairsM.Unlock() + ticket := make([]byte, 192) - copy(ticket[0:32], ecdh.Marshal(ephKP.PublicKey)) - key := ecdh.GenerateSharedSecret(ephKP.PrivateKey, sta.staticPub) - plain := make([]byte, 69) + //TODO: error when the interval has expired + ephPub, intervalKey, seed := sta.GetIntervalKeys() + copy(ticket[0:32], ecdh.Marshal(ephPub)) + + plain := make([]byte, 33) copy(plain, sta.UID) - binary.BigEndian.PutUint32(plain[16:20], sta.sessionID) - copy(plain[20:36], []byte(sta.ProxyMethod)) - plain[36] = sta.EncryptionMethod - copy(plain[37:69], sta.SessionKey) + copy(plain[16:32], []byte(sta.ProxyMethod)) + plain[32] = sta.EncryptionMethod - cipher, _ := util.AESGCMEncrypt(ticket[0:12], key, plain) - copy(ticket[32:117], cipher) + cipher, _ := util.AESGCMEncrypt(ticket[0:12], intervalKey, plain) + copy(ticket[32:81], 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 @@ -72,6 +57,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[117:192], util.PsudoRandBytes(75, tthInterval+int64(sta.sessionID))) + copy(ticket[81:192], util.PsudoRandBytes(111, seed)) return ticket } diff --git a/internal/client/auth_test.go b/internal/client/auth_test.go index 64b6a3d..d08b705 100644 --- a/internal/client/auth_test.go +++ b/internal/client/auth_test.go @@ -2,6 +2,7 @@ package client import ( "bytes" + "encoding/gob" //"crypto/aes" //"crypto/cipher" //"crypto/rand" @@ -15,89 +16,75 @@ import ( //"github.com/cbeuw/Cloak/internal/ecdh" ) -/* func TestMakeSessionTicket(t *testing.T) { - UID, _ := hex.DecodeString("26a8e88bcd7c64a69ca051740851d22a6818de2fddafc00882331f1c5a8b866c") - staticPv, staticPub, _ := ecdh.GenerateKey(rand.Reader) - mockSta := &State{ - Now: time.Now, - sessionID: 42, - UID: UID, - staticPub: staticPub, - keyPairs: make(map[int64]*keyPair), - TicketTimeHint: 3600, - } + stateGob, _ := hex.DecodeString("ffc5ff8103010105537461746501ff8200010c01094c6f63616c486f7374010c0001094c6f63616c506f7274010c00010a52656d6f7465486f7374010c00010a52656d6f7465506f7274010c00010953657373696f6e49440106000103554944010a00010b50726f78794d6574686f64010c000110456e6372797074696f6e4d6574686f64010600010e5469636b657454696d6548696e74010400010a5365727665724e616d65010c00010a42726f77736572536967010c0001074e756d436f6e6e010400000065ff8201093132372e302e302e3101043139383401093132372e302e302e31010334343301fc52fdfc0701104cd8cc15600d7eb68131fd8097673746010b736861646f77736f636b7302fe1c20010c7777772e62696e672e636f6d01066368726f6d65010800") + buf := bytes.NewBuffer(stateGob) + mockSta := &State{} + gob.NewDecoder(buf).Decode(mockSta) + mockSta.intervalData = &tthIntervalKeys{} + mockSta.intervalData.interval = 434487 + ephPub, _ := hex.DecodeString("7b7e0db16bb8c83355771a424234e36c02fd752b6a9310968d27787d7c117b10") + ephPv, _ := hex.DecodeString("68584fed8ede64e2b17619b9cc0effb2678feb2face92456a8414dafa629334b") + mockSta.intervalData.intervalKey, _ = hex.DecodeString("47e1c2413f1a6b397fbd61d6cf21397b20a2338cef48fae68643602881d93d4b") + mockSta.intervalData.seed = 7518847459617826018 + staticPub, _ := hex.DecodeString("218a14ce495efd3fe4ae213e51f766ec01d0b487869c159b8619536e60e95142") + + var a, b, c [32]byte + copy(a[:], ephPub) + copy(b[:], ephPv) + copy(c[:], staticPub) + mockSta.intervalData.ephPub = &a + mockSta.intervalData.ephPv = &b + mockSta.staticPub = &c + + target, _ := hex.DecodeString("7b7e0db16bb8c83355771a424234e36c02fd752b6a9310968d27787d7c117b103a2d246fd8b7d9e4243d6b83a7365858bd9cb583ba950287c4f4edc249cea935e235eda92c48569f455fca34ff6e4d37cf8f519b1d66e7cd51b31c1766ffb03134576e8d61ad5eae58a9ce4153ec33af73c7a8d04ab56d51155ac19fe731c792c17ee98a97fbfef8efc952964b7fd61dd2a35d7de4128abc730e20ba44e069e44ba8e29b66a8e4f114b9ab4cc2fba944a925086d7f09892a59147d990b58d393") ticket := MakeSessionTicket(mockSta) - // verification - ephPub, _ := ecdh.Unmarshal(ticket[0:32]) - key := ecdh.GenerateSharedSecret(staticPv, ephPub) - - // aes decrypt - UIDsID := make([]byte, len(ticket[32:68])) - copy(UIDsID, ticket[32:68]) // Because XORKeyStream is inplace, but we don't want the input to be changed - block, _ := aes.NewCipher(key) - stream := cipher.NewCTR(block, ticket[0:16]) - stream.XORKeyStream(UIDsID, UIDsID) - - decryUID := UIDsID[0:32] - decrySessionID := binary.BigEndian.Uint32(UIDsID[32:36]) - - // check padding - tthInterval := mockSta.Now().Unix() / int64(mockSta.TicketTimeHint) - r := prand.New(prand.NewSource(tthInterval + int64(mockSta.sessionID))) - pad := make([]byte, 124) - r.Read(pad) - - if !bytes.Equal(mockSta.UID, decryUID) { + if !bytes.Equal(target, ticket) { t.Error( - "For", "UID", - "expecting", fmt.Sprintf("%x", mockSta.UID), - "got", fmt.Sprintf("%x", decryUID), - ) - } - if mockSta.sessionID != decrySessionID { - t.Error( - "For", "sessionID", - "expecting", mockSta.sessionID, - "got", decrySessionID, - ) - } - if !bytes.Equal(pad, ticket[68:]) { - t.Error( - "For", "Padding", - "expecting", fmt.Sprintf("%x", pad), - "got", fmt.Sprintf("%x", ticket[68:]), + "For", "sessionTicket generation", + "expecting", fmt.Sprintf("%x", target), + "got", fmt.Sprintf("%x", ticket), ) } + } -*/ func TestMakeRandomField(t *testing.T) { - UID, _ := hex.DecodeString("26a8e88bcd7c64a69ca051740851d22a6818de2fddafc00882331f1c5a8b866c") + UID, _ := hex.DecodeString("4cd8cc15600d7eb68131fd8097673746") mockSta := &State{ - Now: time.Now, - UID: UID, + Now: time.Now, + UID: UID, + SessionID: 1, } random := MakeRandomField(mockSta) // verification tb := make([]byte, 8) binary.BigEndian.PutUint64(tb, uint64(time.Now().Unix()/(12*60*60))) - rdm := random[0:16] + front := random[0:16] preHash := make([]byte, 56) copy(preHash[0:32], UID) copy(preHash[32:40], tb) - copy(preHash[40:56], rdm) + copy(preHash[40:56], front) h := sha256.New() h.Write(preHash) exp := h.Sum(nil)[0:16] if !bytes.Equal(exp, random[16:32]) { t.Error( - "For", "Random", + "For", "Random generation", "expecting", fmt.Sprintf("%x", exp), "got", fmt.Sprintf("%x", random[16:32]), ) } + + random2 := MakeRandomField(mockSta) + if bytes.Equal(random, random2) { + t.Error( + "For", "Duplicate random generation", + "expecting", "two different randoms", + "got", fmt.Sprintf("the same: %x", random), + ) + } } diff --git a/internal/client/state.go b/internal/client/state.go index 850d4ac..1c494e8 100644 --- a/internal/client/state.go +++ b/internal/client/state.go @@ -2,7 +2,9 @@ package client import ( "crypto" + "crypto/rand" "encoding/base64" + "encoding/binary" "encoding/json" "errors" "io/ioutil" @@ -24,6 +26,14 @@ type rawConfig struct { NumConn int } +type tthIntervalKeys struct { + interval int64 + ephPv crypto.PrivateKey + ephPub crypto.PublicKey + intervalKey []byte + seed int64 +} + // State stores global variables type State struct { LocalHost string @@ -31,13 +41,13 @@ type State struct { RemoteHost string RemotePort string - Now func() time.Time - sessionID uint32 - SessionKey []byte - UID []byte - staticPub crypto.PublicKey - keyPairsM sync.RWMutex - keyPairs map[int64]*keyPair + Now func() time.Time + SessionID uint32 + UID []byte + staticPub crypto.PublicKey + + intervalDataM sync.Mutex + intervalData *tthIntervalKeys ProxyMethod string EncryptionMethod byte @@ -49,17 +59,36 @@ type State struct { func InitState(localHost, localPort, remoteHost, remotePort string, nowFunc func() time.Time) *State { ret := &State{ - LocalHost: localHost, - LocalPort: localPort, - RemoteHost: remoteHost, - RemotePort: remotePort, - Now: nowFunc, + LocalHost: localHost, + LocalPort: localPort, + RemoteHost: remoteHost, + RemotePort: remotePort, + Now: nowFunc, + intervalData: &tthIntervalKeys{}, } - ret.keyPairs = make(map[int64]*keyPair) return ret } -func (sta *State) SetSessionID(id uint32) { sta.sessionID = id } +func (sta *State) UpdateIntervalKeys() { + sta.intervalDataM.Lock() + currentInterval := sta.Now().Unix() / int64(sta.TicketTimeHint) + if currentInterval == sta.intervalData.interval { + sta.intervalDataM.Unlock() + return + } + sta.intervalData.interval = currentInterval + ephPv, ephPub, _ := ecdh.GenerateKey(rand.Reader) + intervalKey := ecdh.GenerateSharedSecret(ephPv, sta.staticPub) + seed := int64(binary.BigEndian.Uint64(ephPv.(*[32]byte)[0:8])) + sta.intervalData.ephPv, sta.intervalData.ephPub, sta.intervalData.intervalKey, sta.intervalData.seed = ephPv, ephPub, intervalKey, seed + sta.intervalDataM.Unlock() +} + +func (sta *State) GetIntervalKeys() (crypto.PublicKey, []byte, int64) { + sta.intervalDataM.Lock() + defer sta.intervalDataM.Unlock() + return sta.intervalData.ephPub, sta.intervalData.intervalKey, sta.intervalData.seed +} // semi-colon separated value. This is for Android plugin options func ssvToJson(ssv string) (ret []byte) { diff --git a/internal/server/auth.go b/internal/server/auth.go index 2bed132..10ce142 100644 --- a/internal/server/auth.go +++ b/internal/server/auth.go @@ -5,37 +5,37 @@ import ( "crypto" "crypto/sha256" "encoding/binary" - "log" - "github.com/cbeuw/Cloak/internal/ecdh" "github.com/cbeuw/Cloak/internal/util" + "log" ) -// input ticket, return UID -func decryptSessionTicket(staticPv crypto.PrivateKey, ticket []byte) ([]byte, uint32, string, byte, []byte) { +func decryptSessionTicket(staticPv crypto.PrivateKey, ticket []byte) (UID []byte, proxyMethod string, encryptionMethod byte, tthKey []byte) { + // sessionTicket: [marshalled ephemeral pub key 32 bytes][encrypted UID 16 bytes, proxy method 16 bytes, encryption method 1 byte][16 bytes authentication tag][padding 111 bytes] ephPub, _ := ecdh.Unmarshal(ticket[0:32]) - key := ecdh.GenerateSharedSecret(staticPv, ephPub) - plain, err := util.AESGCMDecrypt(ticket[0:12], key, ticket[32:117]) + tthKey = ecdh.GenerateSharedSecret(staticPv, ephPub) + plain, err := util.AESGCMDecrypt(ticket[0:12], tthKey, ticket[32:81]) if err != nil { - return nil, 0, "", 0x00, nil + return } - sessionID := binary.BigEndian.Uint32(plain[16:20]) - return plain[0:16], sessionID, string(bytes.Trim(plain[20:36], "\x00")), plain[36], plain[37:69] + return plain[0:16], string(bytes.Trim(plain[16:32], "\x00")), plain[32], tthKey } -func validateRandom(random []byte, UID []byte, time int64) bool { +func validateRandom(random []byte, UID []byte, time int64) (bool, uint32) { t := make([]byte, 8) binary.BigEndian.PutUint64(t, uint64(time/(12*60*60))) - rdm := random[0:16] + front := random[0:16] preHash := make([]byte, 56) copy(preHash[0:32], UID) copy(preHash[32:40], t) - copy(preHash[40:56], rdm) + copy(preHash[40:56], front) h := sha256.New() h.Write(preHash) - return bytes.Equal(h.Sum(nil)[0:16], random[16:32]) + + sessionID := binary.BigEndian.Uint32(front[0:4]) + return bytes.Equal(h.Sum(nil)[0:16], random[16:32]), sessionID } -func TouchStone(ch *ClientHello, sta *State) (isCK bool, UID []byte, sessionID uint32, proxyMethod string, encryptionMethod byte, sessionKey []byte) { +func TouchStone(ch *ClientHello, sta *State) (isCK bool, UID []byte, sessionID uint32, proxyMethod string, encryptionMethod byte, tthKey []byte) { var random [32]byte copy(random[:], ch.random) @@ -50,14 +50,17 @@ func TouchStone(ch *ClientHello, sta *State) (isCK bool, UID []byte, sessionID u } ticket := ch.extensions[[2]byte{0x00, 0x23}] - if len(ticket) < 68 { + + if len(ticket) < 81 { return } - UID, sessionID, proxyMethod, encryptionMethod, sessionKey = decryptSessionTicket(sta.staticPv, ticket) + + UID, proxyMethod, encryptionMethod, tthKey = decryptSessionTicket(sta.staticPv, ticket) + if len(UID) < 16 { return } - isCK = validateRandom(ch.random, UID, sta.Now().Unix()) + isCK, sessionID = validateRandom(ch.random, UID, sta.Now().Unix()) if !isCK { return } diff --git a/internal/server/auth_test.go b/internal/server/auth_test.go index 73b5188..3477ddd 100644 --- a/internal/server/auth_test.go +++ b/internal/server/auth_test.go @@ -1,22 +1,27 @@ package server import ( + "bytes" //"bytes" "encoding/hex" "fmt" + "github.com/cbeuw/Cloak/internal/ecdh" "testing" //"github.com/cbeuw/Cloak/internal/ecdh" ) -/* func TestDecryptSessionTicket(t *testing.T) { - UID, _ := hex.DecodeString("26a8e88bcd7c64a69ca051740851d22a6818de2fddafc00882331f1c5a8b866c") - sessionID := uint32(42) - pvb, _ := hex.DecodeString("083794692e77b28fa2152dfee53142185fd58ea8172d3545fdeeaea97b3c597c") + UID, _ := hex.DecodeString("4cd8cc15600d7eb68131fd8097673746") + pvb, _ := hex.DecodeString("10de5a3c4a4d04efafc3e06d1506363a72bd6d053baef123e6a9a79a0c04b547") staticPv, _ := ecdh.Unmarshal(pvb) - sessionTicket, _ := hex.DecodeString("f586223b50cada583d61dc9bf3d01cc3a45aab4b062ed6a31ead0badb87f7761aab4f9f737a1d8ff2a2aa4d50ceb808844588ee3c8fdf36c33a35ef5003e287337659c8164a7949e9e63623090763fc24d0386c8904e47bdd740e09dd9b395c72de669629c2a865ed581452d23306adf26de0c8a46ee05e3dac876f2bcd9a2de946d319498f579383d06b3e66b3aca05f533fdc5f017eeba45b42080aabd4f71151fa0dfc1b0e23be4ed3abdb47adc0d5740ca7b7689ad34426309fb6984a086") + proxyMethod := "shadowsocks" + encryptionMethod := byte(0) + tthKey, _ := hex.DecodeString("92389a9b2769e2b76514c4cb163217bed0c5500bceb4a5ade1ceae597616db23") + + sessionTicket, _ := hex.DecodeString("9ee339202508b6fbe9c19988575330c547efbc27b0d072ed93c0cc265b67d826825a49211b8f86b4364b436ed5db15925774c3bec4a1776f70a17db68ba541dc4c23871d2cc1a5074b081bbe0f8b86f1c7f7749964517dcfd8830532eddc8ac707544ec04b754a133b9595ebc2af988156dbe1e4f3b89c9dc289d441cb5a15d72cc59423981d43a498292d509e5fa5c8e8bf8ee85a2e4991ae126fcd6e4d2aa1119e918c80afa2dc38bec1ef621c9c3994af43b1983c241c68e04e8043c95d74") + + decryUID, decryProxyMethod, decryEncryptionMethod, decryTthKey := decryptSessionTicket(staticPv, sessionTicket) - decryUID, decrySessionID, _, _ := decryptSessionTicket(staticPv, sessionTicket) if !bytes.Equal(decryUID, UID) { t.Error( "For", "UID", @@ -24,43 +29,65 @@ func TestDecryptSessionTicket(t *testing.T) { "got", fmt.Sprintf("%x", decryUID), ) } - if decrySessionID != sessionID { + if proxyMethod != decryProxyMethod { t.Error( - "For", "sessionID", - "expecting", fmt.Sprintf("%x", sessionID), - "got", fmt.Sprintf("%x", decrySessionID), + "For", "proxyMethod", + "expecting", fmt.Sprintf("%x", proxyMethod), + "got", fmt.Sprintf("%x", decryProxyMethod), + ) + } + if encryptionMethod != decryEncryptionMethod { + t.Error( + "For", "encryptionMethod", + "expecting", fmt.Sprintf("%x", encryptionMethod), + "got", fmt.Sprintf("%x", decryEncryptionMethod), + ) + } + if !bytes.Equal(tthKey, decryTthKey) { + t.Error( + "For", "tthKey", + "expecting", fmt.Sprintf("%x", tthKey), + "got", fmt.Sprintf("%x", decryTthKey), ) } } -*/ func TestValidateRandom(t *testing.T) { - UID, _ := hex.DecodeString("26a8e88bcd7c64a69ca051740851d22a6818de2fddafc00882331f1c5a8b866c") - random, _ := hex.DecodeString("6274de9992a6f96a86fc35cf6644a5e7844951889a802e9531add440eabb939b") - right := validateRandom(random, UID, 1547912444) + sessionID := uint32(2422026642) + random, _ := hex.DecodeString("905d319272711946f6400db4f5028d6893f7b22659c78371c1f72386191a8ab4") + UID, _ := hex.DecodeString("4cd8cc15600d7eb68131fd8097673746") + + right, decrySessionID := validateRandom(random, UID, 1564150721) if !right { t.Error( - "For", fmt.Sprintf("good random: %x at time %v", random, 1547912444), + "For", fmt.Sprintf("good random: %x at time %v", random, 1564150721), "expecting", true, "got", false, ) } + if sessionID != decrySessionID { + t.Error( + "For", fmt.Sprintf("good random: %x at time %v", random, 1564150721), + "expecting", sessionID, + "got", decrySessionID, + ) + } - replay := validateRandom(random, UID, 1547955645) + replay, _ := validateRandom(random, UID, 1764150721) if replay { t.Error( - "For", fmt.Sprintf("expired random: %x at time %v", random, 1547955645), + "For", fmt.Sprintf("expired random: %x at time %v", random, 1764150721), "expecting", false, "got", true, ) } random[13] = 0x42 - bogus := validateRandom(random, UID, 1547912444) + bogus, _ := validateRandom(random, UID, 1564150721) if bogus { t.Error( - "For", fmt.Sprintf("bogus random: %x at time %v", random, 1547912444), + "For", fmt.Sprintf("bogus random: %x at time %v", random, 1564150721), "expecting", false, "got", true, )