framing.go 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. /*
  2. * Copyright (c) 2014, Yawning Angel <yawning at torproject dot org>
  3. * All rights reserved.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are met:
  7. *
  8. * * Redistributions of source code must retain the above copyright notice,
  9. * this list of conditions and the following disclaimer.
  10. *
  11. * * Redistributions in binary form must reproduce the above copyright notice,
  12. * this list of conditions and the following disclaimer in the documentation
  13. * and/or other materials provided with the distribution.
  14. *
  15. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  16. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  17. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  18. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
  19. * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  20. * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  21. * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  22. * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  23. * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  24. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  25. * POSSIBILITY OF SUCH DAMAGE.
  26. */
  27. //
  28. // Package framing implements the obfs4 link framing and cryptography.
  29. //
  30. // The Encoder/Decoder shared secret format is:
  31. // uint8_t[32] NaCl secretbox key
  32. // uint8_t[16] NaCl Nonce prefix
  33. // uint8_t[16] SipHash-2-4 key (used to obfsucate length)
  34. // uint8_t[8] SipHash-2-4 IV
  35. //
  36. // The frame format is:
  37. // uint16_t length (obfsucated, big endian)
  38. // NaCl secretbox (Poly1305/XSalsa20) containing:
  39. // uint8_t[16] tag (Part of the secretbox construct)
  40. // uint8_t[] payload
  41. //
  42. // The length field is length of the NaCl secretbox XORed with the truncated
  43. // SipHash-2-4 digest ran in OFB mode.
  44. //
  45. // Initialize K, IV[0] with values from the shared secret.
  46. // On each packet, IV[n] = H(K, IV[n - 1])
  47. // mask[n] = IV[n][0:2]
  48. // obfsLen = length ^ mask[n]
  49. //
  50. // The NaCl secretbox (Poly1305/XSalsa20) nonce format is:
  51. // uint8_t[24] prefix (Fixed)
  52. // uint64_t counter (Big endian)
  53. //
  54. // The counter is initialized to 1, and is incremented on each frame. Since
  55. // the protocol is designed to be used over a reliable medium, the nonce is not
  56. // transmitted over the wire as both sides of the conversation know the prefix
  57. // and the initial counter value. It is imperative that the counter does not
  58. // wrap, and sessions MUST terminate before 2^64 frames are sent.
  59. //
  60. package framing // import "gitlab.com/yawning/obfs4.git/transports/obfs4/framing"
  61. import (
  62. "bytes"
  63. "encoding/binary"
  64. "errors"
  65. "fmt"
  66. "io"
  67. "gitlab.com/yawning/obfs4.git/common/csrand"
  68. "gitlab.com/yawning/obfs4.git/common/drbg"
  69. "golang.org/x/crypto/nacl/secretbox"
  70. )
  71. const (
  72. // MaximumSegmentLength is the length of the largest possible segment
  73. // including overhead.
  74. MaximumSegmentLength = 1500 - (40 + 12)
  75. // FrameOverhead is the length of the framing overhead.
  76. FrameOverhead = lengthLength + secretbox.Overhead
  77. // MaximumFramePayloadLength is the length of the maximum allowed payload
  78. // per frame.
  79. MaximumFramePayloadLength = MaximumSegmentLength - FrameOverhead
  80. // KeyLength is the length of the Encoder/Decoder secret key.
  81. KeyLength = keyLength + noncePrefixLength + drbg.SeedLength
  82. maxFrameLength = MaximumSegmentLength - lengthLength
  83. minFrameLength = FrameOverhead - lengthLength
  84. keyLength = 32
  85. noncePrefixLength = 16
  86. nonceCounterLength = 8
  87. nonceLength = noncePrefixLength + nonceCounterLength
  88. lengthLength = 2
  89. )
  90. // Error returned when Decoder.Decode() requires more data to continue.
  91. var ErrAgain = errors.New("framing: More data needed to decode")
  92. // Error returned when Decoder.Decode() failes to authenticate a frame.
  93. var ErrTagMismatch = errors.New("framing: Poly1305 tag mismatch")
  94. // Error returned when the NaCl secretbox nonce's counter wraps (FATAL).
  95. var ErrNonceCounterWrapped = errors.New("framing: Nonce counter wrapped")
  96. // InvalidPayloadLengthError is the error returned when Encoder.Encode()
  97. // rejects the payload length.
  98. type InvalidPayloadLengthError int
  99. func (e InvalidPayloadLengthError) Error() string {
  100. return fmt.Sprintf("framing: Invalid payload length: %d", int(e))
  101. }
  102. type boxNonce struct {
  103. prefix [noncePrefixLength]byte
  104. counter uint64
  105. }
  106. func (nonce *boxNonce) init(prefix []byte) {
  107. if noncePrefixLength != len(prefix) {
  108. panic(fmt.Sprintf("BUG: Nonce prefix length invalid: %d", len(prefix)))
  109. }
  110. copy(nonce.prefix[:], prefix)
  111. nonce.counter = 1
  112. }
  113. func (nonce boxNonce) bytes(out *[nonceLength]byte) error {
  114. // The security guarantee of Poly1305 is broken if a nonce is ever reused
  115. // for a given key. Detect this by checking for counter wraparound since
  116. // we start each counter at 1. If it ever happens that more than 2^64 - 1
  117. // frames are transmitted over a given connection, support for rekeying
  118. // will be neccecary, but that's unlikely to happen.
  119. if nonce.counter == 0 {
  120. return ErrNonceCounterWrapped
  121. }
  122. copy(out[:], nonce.prefix[:])
  123. binary.BigEndian.PutUint64(out[noncePrefixLength:], nonce.counter)
  124. return nil
  125. }
  126. // Encoder is a frame encoder instance.
  127. type Encoder struct {
  128. key [keyLength]byte
  129. nonce boxNonce
  130. drbg *drbg.HashDrbg
  131. }
  132. // NewEncoder creates a new Encoder instance. It must be supplied a slice
  133. // containing exactly KeyLength bytes of keying material.
  134. func NewEncoder(key []byte) *Encoder {
  135. if len(key) != KeyLength {
  136. panic(fmt.Sprintf("BUG: Invalid encoder key length: %d", len(key)))
  137. }
  138. encoder := new(Encoder)
  139. copy(encoder.key[:], key[0:keyLength])
  140. encoder.nonce.init(key[keyLength : keyLength+noncePrefixLength])
  141. seed, err := drbg.SeedFromBytes(key[keyLength+noncePrefixLength:])
  142. if err != nil {
  143. panic(fmt.Sprintf("BUG: Failed to initialize DRBG: %s", err))
  144. }
  145. encoder.drbg, _ = drbg.NewHashDrbg(seed)
  146. return encoder
  147. }
  148. // Encode encodes a single frame worth of payload and returns the encoded
  149. // length. InvalidPayloadLengthError is recoverable, all other errors MUST be
  150. // treated as fatal and the session aborted.
  151. func (encoder *Encoder) Encode(frame, payload []byte) (n int, err error) {
  152. payloadLen := len(payload)
  153. if MaximumFramePayloadLength < payloadLen {
  154. return 0, InvalidPayloadLengthError(payloadLen)
  155. }
  156. if len(frame) < payloadLen+FrameOverhead {
  157. return 0, io.ErrShortBuffer
  158. }
  159. // Generate a new nonce.
  160. var nonce [nonceLength]byte
  161. if err = encoder.nonce.bytes(&nonce); err != nil {
  162. return 0, err
  163. }
  164. encoder.nonce.counter++
  165. // Encrypt and MAC payload.
  166. box := secretbox.Seal(frame[:lengthLength], payload, &nonce, &encoder.key)
  167. // Obfuscate the length.
  168. length := uint16(len(box) - lengthLength)
  169. lengthMask := encoder.drbg.NextBlock()
  170. length ^= binary.BigEndian.Uint16(lengthMask)
  171. binary.BigEndian.PutUint16(frame[:2], length)
  172. // Return the frame.
  173. return len(box), nil
  174. }
  175. // Decoder is a frame decoder instance.
  176. type Decoder struct {
  177. key [keyLength]byte
  178. nonce boxNonce
  179. drbg *drbg.HashDrbg
  180. nextNonce [nonceLength]byte
  181. nextLength uint16
  182. nextLengthInvalid bool
  183. }
  184. // NewDecoder creates a new Decoder instance. It must be supplied a slice
  185. // containing exactly KeyLength bytes of keying material.
  186. func NewDecoder(key []byte) *Decoder {
  187. if len(key) != KeyLength {
  188. panic(fmt.Sprintf("BUG: Invalid decoder key length: %d", len(key)))
  189. }
  190. decoder := new(Decoder)
  191. copy(decoder.key[:], key[0:keyLength])
  192. decoder.nonce.init(key[keyLength : keyLength+noncePrefixLength])
  193. seed, err := drbg.SeedFromBytes(key[keyLength+noncePrefixLength:])
  194. if err != nil {
  195. panic(fmt.Sprintf("BUG: Failed to initialize DRBG: %s", err))
  196. }
  197. decoder.drbg, _ = drbg.NewHashDrbg(seed)
  198. return decoder
  199. }
  200. // Decode decodes a stream of data and returns the length if any. ErrAgain is
  201. // a temporary failure, all other errors MUST be treated as fatal and the
  202. // session aborted.
  203. func (decoder *Decoder) Decode(data []byte, frames *bytes.Buffer) (int, error) {
  204. // A length of 0 indicates that we do not know how big the next frame is
  205. // going to be.
  206. if decoder.nextLength == 0 {
  207. // Attempt to pull out the next frame length.
  208. if lengthLength > frames.Len() {
  209. return 0, ErrAgain
  210. }
  211. // Remove the length field from the buffer.
  212. var obfsLen [lengthLength]byte
  213. _, err := io.ReadFull(frames, obfsLen[:])
  214. if err != nil {
  215. return 0, err
  216. }
  217. // Derive the nonce the peer used.
  218. if err = decoder.nonce.bytes(&decoder.nextNonce); err != nil {
  219. return 0, err
  220. }
  221. // Deobfuscate the length field.
  222. length := binary.BigEndian.Uint16(obfsLen[:])
  223. lengthMask := decoder.drbg.NextBlock()
  224. length ^= binary.BigEndian.Uint16(lengthMask)
  225. if maxFrameLength < length || minFrameLength > length {
  226. // Per "Plaintext Recovery Attacks Against SSH" by
  227. // Martin R. Albrecht, Kenneth G. Paterson and Gaven J. Watson,
  228. // there are a class of attacks againt protocols that use similar
  229. // sorts of framing schemes.
  230. //
  231. // While obfs4 should not allow plaintext recovery (CBC mode is
  232. // not used), attempt to mitigate out of bound frame length errors
  233. // by pretending that the length was a random valid range as per
  234. // the countermeasure suggested by Denis Bider in section 6 of the
  235. // paper.
  236. decoder.nextLengthInvalid = true
  237. length = uint16(csrand.IntRange(minFrameLength, maxFrameLength))
  238. }
  239. decoder.nextLength = length
  240. }
  241. if int(decoder.nextLength) > frames.Len() {
  242. return 0, ErrAgain
  243. }
  244. // Unseal the frame.
  245. var box [maxFrameLength]byte
  246. n, err := io.ReadFull(frames, box[:decoder.nextLength])
  247. if err != nil {
  248. return 0, err
  249. }
  250. out, ok := secretbox.Open(data[:0], box[:n], &decoder.nextNonce, &decoder.key)
  251. if !ok || decoder.nextLengthInvalid {
  252. // When a random length is used (on length error) the tag should always
  253. // mismatch, but be paranoid.
  254. return 0, ErrTagMismatch
  255. }
  256. // Clean up and prepare for the next frame.
  257. decoder.nextLength = 0
  258. decoder.nonce.counter++
  259. return len(out), nil
  260. }