utils.go 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. package tapdance
  2. import (
  3. "bytes"
  4. "crypto/aes"
  5. "crypto/cipher"
  6. "crypto/rand"
  7. "crypto/sha256"
  8. "encoding/binary"
  9. "errors"
  10. mrand "math/rand"
  11. "net"
  12. "strconv"
  13. "strings"
  14. "time"
  15. "github.com/agl/ed25519/extra25519"
  16. "golang.org/x/crypto/curve25519"
  17. )
  18. // The key argument should be the AES key, either 16 or 32 bytes
  19. // to select AES-128 or AES-256.
  20. func aesGcmEncrypt(plaintext []byte, key []byte, iv []byte) ([]byte, error) {
  21. block, err := aes.NewCipher(key)
  22. if err != nil {
  23. return nil, err
  24. }
  25. aesGcmCipher, err := cipher.NewGCM(block)
  26. if err != nil {
  27. return nil, err
  28. }
  29. return aesGcmCipher.Seal(nil, iv, plaintext, nil), nil
  30. }
  31. // Tries to get crypto random int in range [min, max]
  32. // In case of crypto failure -- return insecure pseudorandom
  33. func getRandInt(min int, max int) int {
  34. // I can't believe Golang is making me do that
  35. // Flashback to awful C/C++ libraries
  36. diff := max - min
  37. if diff < 0 {
  38. Logger().Warningf("getRandInt(): max is less than min")
  39. min = max
  40. diff *= -1
  41. } else if diff == 0 {
  42. return min
  43. }
  44. var v int64
  45. err := binary.Read(rand.Reader, binary.LittleEndian, &v)
  46. if v < 0 {
  47. v *= -1
  48. }
  49. if err != nil {
  50. Logger().Warningf("Unable to securely get getRandInt(): " + err.Error())
  51. v = mrand.Int63()
  52. }
  53. return min + int(v%int64(diff+1))
  54. }
  55. // returns random duration between min and max in milliseconds
  56. func getRandomDuration(min int, max int) time.Duration {
  57. return time.Millisecond * time.Duration(getRandInt(min, max))
  58. }
  59. // Get padding of length [minLen, maxLen).
  60. // Distributed in pseudogaussian style.
  61. // Padded using symbol '#'. Known plaintext attacks, anyone?
  62. func getRandPadding(minLen int, maxLen int, smoothness int) string {
  63. paddingLen := 0
  64. for j := 0; j < smoothness; j++ {
  65. paddingLen += getRandInt(minLen, maxLen)
  66. }
  67. paddingLen = paddingLen / smoothness
  68. return strings.Repeat("#", paddingLen)
  69. }
  70. func getRandString(length int) string {
  71. const alphabet = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
  72. randString := make([]byte, length)
  73. for i := range randString {
  74. randString[i] = alphabet[getRandInt(0, len(alphabet)-1)]
  75. }
  76. return string(randString)
  77. }
  78. // obfuscateTagAndProtobuf() generates key-pair and combines it /w stationPubkey to generate
  79. // sharedSecret. Client will use Eligator to find and send uniformly random representative for its
  80. // public key (and avoid sending it directly over the wire, as points on ellyptic curve are
  81. // distinguishable)
  82. // Then the sharedSecret will be used to encrypt stegoPayload and protobuf slices:
  83. // - stegoPayload is encrypted with AES-GCM KEY=sharedSecret[0:16], IV=sharedSecret[16:28]
  84. // - protobuf is encrypted with AES-GCM KEY=sharedSecret[0:16], IV={new random IV}, that will be
  85. // prepended to encryptedProtobuf and eventually sent out together
  86. // Returns
  87. // - tag(concatenated representative and encrypted stegoPayload),
  88. // - encryptedProtobuf(concatenated 12 byte IV + encrypted protobuf)
  89. // - error
  90. func obfuscateTagAndProtobuf(stegoPayload []byte, protobuf []byte, stationPubkey []byte) ([]byte, []byte, error) {
  91. if len(stationPubkey) != 32 {
  92. return nil, nil, errors.New("Unexpected station pubkey length. Expected: 32." +
  93. " Received: " + strconv.Itoa(len(stationPubkey)) + ".")
  94. }
  95. var sharedSecret, clientPrivate, clientPublic, representative [32]byte
  96. for ok := false; ok != true; {
  97. var sliceKeyPrivate []byte = clientPrivate[:]
  98. _, err := rand.Read(sliceKeyPrivate)
  99. if err != nil {
  100. return nil, nil, err
  101. }
  102. ok = extra25519.ScalarBaseMult(&clientPublic, &representative, &clientPrivate)
  103. }
  104. var stationPubkeyByte32 [32]byte
  105. copy(stationPubkeyByte32[:], stationPubkey)
  106. curve25519.ScalarMult(&sharedSecret, &clientPrivate, &stationPubkeyByte32)
  107. // extra25519.ScalarBaseMult does not randomize most significant bit(sign of y_coord?)
  108. // Other implementations of elligator may have up to 2 non-random bits.
  109. // Here we randomize the bit, expecting it to be flipped back to 0 on station
  110. randByte := make([]byte, 1)
  111. _, err := rand.Read(randByte)
  112. if err != nil {
  113. return nil, nil, err
  114. }
  115. representative[31] |= (0x80 & randByte[0])
  116. tagBuf := new(bytes.Buffer) // What we have to encrypt with the shared secret using AES
  117. tagBuf.Write(representative[:])
  118. stationPubkeyHash := sha256.Sum256(sharedSecret[:])
  119. aesKey := stationPubkeyHash[:16]
  120. aesIvTag := stationPubkeyHash[16:28] // 12 bytes for stegoPayload nonce
  121. encryptedStegoPayload, err := aesGcmEncrypt(stegoPayload, aesKey, aesIvTag)
  122. if err != nil {
  123. return nil, nil, err
  124. }
  125. tagBuf.Write(encryptedStegoPayload)
  126. tag := tagBuf.Bytes()
  127. if len(protobuf) == 0 {
  128. return tag, nil, err
  129. }
  130. // probably could have used all zeros as IV here, but better to err on safe side
  131. aesIvProtobuf := make([]byte, 12)
  132. _, err = rand.Read(aesIvProtobuf)
  133. if err != nil {
  134. return nil, nil, err
  135. }
  136. encryptedProtobuf, err := aesGcmEncrypt(protobuf, aesKey, aesIvProtobuf)
  137. return tag, append(aesIvProtobuf, encryptedProtobuf...), err
  138. }
  139. func getMsgWithHeader(msgType msgType, msgBytes []byte) []byte {
  140. if len(msgBytes) == 0 {
  141. return nil
  142. }
  143. bufSend := new(bytes.Buffer)
  144. var err error
  145. switch msgType {
  146. case msgProtobuf:
  147. if len(msgBytes) <= int(maxInt16) {
  148. bufSend.Grow(2 + len(msgBytes)) // to avoid double allocation
  149. err = binary.Write(bufSend, binary.BigEndian, int16(len(msgBytes)))
  150. } else {
  151. bufSend.Grow(2 + 4 + len(msgBytes)) // to avoid double allocation
  152. bufSend.Write([]byte{0, 0})
  153. err = binary.Write(bufSend, binary.BigEndian, int32(len(msgBytes)))
  154. }
  155. case msgRawData:
  156. err = binary.Write(bufSend, binary.BigEndian, int16(-len(msgBytes)))
  157. default:
  158. panic("getMsgWithHeader() called with msgType: " + strconv.Itoa(int(msgType)))
  159. }
  160. if err != nil {
  161. // shouldn't ever happen
  162. Logger().Errorln("getMsgWithHeader() failed with error: ", err)
  163. Logger().Errorln("msgType ", msgType)
  164. Logger().Errorln("msgBytes ", msgBytes)
  165. }
  166. bufSend.Write(msgBytes)
  167. return bufSend.Bytes()
  168. }
  169. func uint16toInt16(i uint16) int16 {
  170. pos := int16(i & 32767)
  171. neg := int16(0)
  172. if i&32768 != 0 {
  173. neg = int16(-32768)
  174. }
  175. return pos + neg
  176. }
  177. func reverseEncrypt(ciphertext []byte, keyStream []byte) (plaintext string) {
  178. // our plaintext can be antyhing where x & 0xc0 == 0x40
  179. // i.e. 64-127 in ascii (@, A-Z, [\]^_`, a-z, {|}~ DEL)
  180. // This means that we are allowed to choose the last 6 bits
  181. // of each byte in the ciphertext arbitrarily; the upper 2
  182. // bits will have to be 01, so that our plaintext ends up
  183. // in the desired range.
  184. var ka, kb, kc, kd byte // key stream bytes
  185. var ca, cb, cc, cd byte // ciphertext bytes
  186. var pa, pb, pc, pd byte // plaintext bytes
  187. var sa, sb, sc byte // secret bytes
  188. var tagIdx, keystreamIdx int
  189. for tagIdx < len(ciphertext) {
  190. ka = keyStream[keystreamIdx]
  191. kb = keyStream[keystreamIdx+1]
  192. kc = keyStream[keystreamIdx+2]
  193. kd = keyStream[keystreamIdx+3]
  194. keystreamIdx += 4
  195. // read 3 bytes
  196. sa = ciphertext[tagIdx]
  197. sb = ciphertext[tagIdx+1]
  198. sc = ciphertext[tagIdx+2]
  199. tagIdx += 3
  200. // figure out what plaintext needs to be in base64 encode
  201. ca = (ka & 0xc0) | ((sa & 0xfc) >> 2) // 6 bits sa
  202. cb = (kb & 0xc0) | (((sa & 0x03) << 4) | ((sb & 0xf0) >> 4)) // 2 bits sa, 4 bits sb
  203. cc = (kc & 0xc0) | (((sb & 0x0f) << 2) | ((sc & 0xc0) >> 6)) // 4 bits sb, 2 bits sc
  204. cd = (kd & 0xc0) | (sc & 0x3f) // 6 bits sc
  205. // Xor with key_stream, and add on 0x40 so it's in range of allowed
  206. pa = (ca ^ ka) + 0x40
  207. pb = (cb ^ kb) + 0x40
  208. pc = (cc ^ kc) + 0x40
  209. pd = (cd ^ kd) + 0x40
  210. plaintext += string(pa)
  211. plaintext += string(pb)
  212. plaintext += string(pc)
  213. plaintext += string(pd)
  214. }
  215. return
  216. }
  217. func minInt(a, b int) int {
  218. if a > b {
  219. return b
  220. }
  221. return a
  222. }
  223. func maxInt(a, b int) int {
  224. if a > b {
  225. return a
  226. }
  227. return b
  228. }
  229. // Converts provided duration to raw milliseconds.
  230. // Returns a pointer to u32, because protobuf wants pointers.
  231. // Max valid input duration (that fits into uint32): 49.71 days.
  232. func durationToU32ptrMs(d time.Duration) *uint32 {
  233. i := uint32(d.Nanoseconds() / int64(time.Millisecond))
  234. return &i
  235. }
  236. func readAndClose(c net.Conn, readDeadline time.Duration) {
  237. tinyBuf := []byte{0}
  238. c.SetReadDeadline(time.Now().Add(readDeadline))
  239. c.Read(tinyBuf)
  240. c.Close()
  241. }
  242. func errIsTimeout(err error) bool {
  243. if err != nil {
  244. if strings.Contains(err.Error(), ": i/o timeout") || // client timed out
  245. err.Error() == "EOF" { // decoy timed out
  246. return true
  247. }
  248. }
  249. return false
  250. }