github.com/geph-official/geph2@v0.22.6-0.20210211030601-f527cb59b0df/libs/cshirt2/handshake.go (about) 1 package cshirt2 2 3 import ( 4 "crypto/rand" 5 "crypto/subtle" 6 "encoding/binary" 7 "errors" 8 "fmt" 9 "io" 10 "log" 11 "sync" 12 13 "net" 14 "time" 15 16 "github.com/geph-official/geph2/libs/erand" 17 "github.com/minio/blake2b-simd" 18 "github.com/patrickmn/go-cache" 19 "golang.org/x/crypto/chacha20poly1305" 20 "golang.org/x/crypto/curve25519" 21 ) 22 23 // ErrAttackDetected denotes an error that can only happen when active probing attempts are made. 24 var ErrAttackDetected = errors.New("active probing attack detected") 25 26 // ErrBadHandshakeMAC denotes a bad handshake mac. 27 var ErrBadHandshakeMAC = errors.New("bad MAC in handshake") 28 29 func mac256(m, k []byte) []byte { 30 mac := blake2b.NewMAC(32, k) 31 mac.Write(m) 32 return mac.Sum(nil) 33 } 34 35 func mac128(m, k []byte) []byte { 36 mac := blake2b.NewMAC(16, k) 37 mac.Write(m) 38 return mac.Sum(nil) 39 } 40 41 var ( 42 globCache = cache.New(time.Hour*3, time.Minute*30) 43 globCacheLock sync.Mutex 44 ) 45 46 const ( 47 pkSize = 192 48 ) 49 50 func readPK(compatibility bool, secret []byte, isDown bool, transport net.Conn) (pubKey, int64, error) { 51 epoch := time.Now().Unix() / 30 52 // Read their public key 53 theirPublic := make([]byte, pkSize) 54 _, err := io.ReadFull(transport, theirPublic) 55 if err != nil { 56 return nil, 0, err 57 } 58 // new PK format: chacha20poly1305-encrypted ed25519 public key, with following two bytes denoting padding 59 // try to decode as new PK format 60 var edpk []byte 61 var padlen uint16 62 for e := epoch - 30; e < epoch+30; e++ { 63 hsKey := mac256(secret, []byte(fmt.Sprintf("handshake-%v-%v", e, isDown))) 64 crypt, _ := chacha20poly1305.New(hsKey) 65 plain, er := crypt.Open(nil, make([]byte, 12), theirPublic, nil) 66 if er != nil { 67 err = er 68 continue 69 } 70 edpk = plain[:32] 71 padlen = binary.LittleEndian.Uint16(plain[32:][:2]) 72 epoch = e 73 } 74 if edpk != nil { 75 if !replayFilter(edpk) { 76 return nil, 0, ErrAttackDetected 77 } 78 // read past padding 79 _, err = io.ReadFull(transport, make([]byte, padlen)) 80 if err != nil { 81 err = fmt.Errorf("couldn't read past padding: %w", err) 82 return nil, 0, err 83 } 84 return edpk, epoch, nil 85 } 86 if !compatibility { 87 return nil, 0, errors.New("unrecognizable handshake") 88 } 89 // Read their public key MAC 90 theirPublicMAC := make([]byte, 32) 91 _, err = io.ReadFull(transport, theirPublicMAC) 92 if err != nil { 93 return nil, 0, err 94 } 95 macOK := false 96 //shift := 0 97 // shift one byte at a time until we find the mac 98 for i := 0; i < 1024+(erand.Int(1024)); i++ { 99 for e := epoch - 3; e < epoch+3; e++ { 100 macKey := mac256(secret, []byte(fmt.Sprintf("%v", e))) 101 if i > 0 && subtle.ConstantTimeCompare(theirPublicMAC, mac256(theirPublic, macKey)) == 1 { 102 log.Printf("*** ΔE = %v sec, shift = %v ***", (e-epoch)*30, i) 103 macOK = true 104 epoch = e 105 //shift = i 106 goto out 107 } 108 } 109 // read another byte 110 oneBytes := make([]byte, 1) 111 _, err = io.ReadFull(transport, oneBytes) 112 if err != nil { 113 return nil, 0, err 114 } 115 theirPublicMAC = append(theirPublicMAC, oneBytes...)[1:] 116 } 117 log.Println("** zero shift **", transport.RemoteAddr()) 118 return nil, 0, errors.New("zero shift") 119 out: 120 if !replayFilter(theirPublic) { 121 log.Printf("** replay attack detected ** %x %v", theirPublic[:10], transport.RemoteAddr()) 122 return nil, 0, ErrAttackDetected 123 } 124 log.Printf("-- GOOD %x %v", theirPublic[:10], transport.RemoteAddr()) 125 if !macOK { 126 log.Println("** bad pk mac **", transport.RemoteAddr()) 127 return nil, 0, ErrBadHandshakeMAC 128 } 129 return theirPublic, epoch, nil 130 } 131 132 func replayFilter(pk []byte) bool { 133 globCacheLock.Lock() 134 defer globCacheLock.Unlock() 135 if _, ok := globCache.Get(string(pk)); ok { 136 return false 137 } 138 // Reject if bad 139 globCache.SetDefault(string(pk), true) 140 return true 141 } 142 143 func writePKLegacy(epoch int64, shift int, secret []byte, myPublic pubKey, transport net.Conn) error { 144 if epoch == 0 { 145 epoch = time.Now().Unix() / 30 146 } 147 macKey := mac256(secret, []byte(fmt.Sprintf("%v", epoch))) 148 myPublicMAC := mac256(myPublic, macKey) 149 padding := make([]byte, shift) 150 rand.Read(padding) 151 _, err := transport.Write(append(append(myPublic, padding...), myPublicMAC...)) 152 if err != nil { 153 return err 154 } 155 return nil 156 } 157 158 func writePK(secret []byte, epoch int64, myPublic pubKey, isDown bool, transport net.Conn) error { 159 if epoch == 0 { 160 epoch = time.Now().Unix() / 30 161 } 162 hsPlain := make([]byte, 192-16) 163 copy(hsPlain, myPublic) 164 paddingAmount := erand.Int(65536) 165 binary.LittleEndian.PutUint16(hsPlain[32:][:2], uint16(paddingAmount)) 166 hsKey := mac256(secret, []byte(fmt.Sprintf("handshake-%v-%v", epoch, isDown))) 167 hsCrypter, _ := chacha20poly1305.New(hsKey) 168 hsCrypt := hsCrypter.Seal(nil, make([]byte, 12), hsPlain, nil) 169 padding := make([]byte, paddingAmount) 170 rand.Read(padding) 171 _, err := transport.Write(append(hsCrypt, padding...)) 172 return err 173 } 174 175 // Server negotiates obfuscation on a network connection, acting as the server. The secret must be provided. 176 func Server(secret []byte, compatibility bool, transport net.Conn) (net.Conn, error) { 177 theirPK, epoch, err := readPK(compatibility, secret, false, transport) 178 if err != nil { 179 return nil, err 180 } 181 if len(theirPK) == 32 { 182 mySK := make([]byte, 32) 183 rand.Read(mySK) 184 log.Printf("mySK = %x", mySK) 185 myPK, err := curve25519.X25519(mySK, curve25519.Basepoint) 186 if err != nil { 187 panic(err) 188 } 189 writePK(secret, epoch, myPK, true, transport) 190 log.Printf("myPK = %x, theirPK = %x", myPK, theirPK) 191 shSecret, err := curve25519.X25519(mySK, theirPK) 192 if err != nil { 193 return nil, err 194 } 195 return newTransport(transport, shSecret, true), nil 196 } 197 myPK, mySK := dhGenKey() 198 // if shift > 0 { 199 // shift = erand.Int(1024) 200 // } 201 err = writePKLegacy(epoch, erand.Int(1000)+1, secret, myPK, transport) 202 if err != nil { 203 return nil, err 204 } 205 // Compute shared secret 206 shSecret := udhSecret(mySK, theirPK) 207 return newLegacyTransport(transport, shSecret, true), nil 208 } 209 210 // Client negotiates low-level obfuscation as a client, using the new protocol. 211 func Client(secret []byte, transport net.Conn) (net.Conn, error) { 212 mySK := make([]byte, 32) 213 rand.Read(mySK) 214 myPK, err := curve25519.X25519(mySK, curve25519.Basepoint) 215 if err != nil { 216 panic(err) 217 } 218 err = writePK(secret, 0, myPK, false, transport) 219 if err != nil { 220 return nil, err 221 } 222 theirPK, _, err := readPK(false, secret, true, transport) 223 if err != nil { 224 return nil, err 225 } 226 if len(theirPK) != 32 { 227 err = errors.New("wrong length for theirPK") 228 return nil, err 229 } 230 shSecret, err := curve25519.X25519(mySK, theirPK) 231 if err != nil { 232 return nil, err 233 } 234 return newTransport(transport, shSecret, false), nil 235 } 236 237 // ClientLegacy negotiates low-level obfuscation as a client, using the legacy protocol.. The server 238 // secret must be given so that the client can prove knowledge. 239 func ClientLegacy(secret []byte, transport net.Conn) (net.Conn, error) { 240 myPK, mySK := dhGenKey() 241 err := writePKLegacy(0, erand.Int(1000)+1, secret, myPK, transport) 242 if err != nil { 243 return nil, err 244 } 245 theirPK, _, err := readPK(true, secret, true, transport) 246 if err != nil { 247 return nil, err 248 } 249 // Compute shared secret 250 shSecret := udhSecret(mySK, theirPK) 251 return newLegacyTransport(transport, shSecret, false), nil 252 }