1 // https://wiki.vuze.com/w/Message_Stream_Encryption
21 "github.com/anacrolix/missinggo/perf"
27 CryptoMethodPlaintext CryptoMethod = 1 // After header obfuscation, drop into plaintext
28 CryptoMethodRC4 CryptoMethod = 2 // After header obfuscation, use RC4 for the rest of the stream
29 AllSupportedCrypto = CryptoMethodPlaintext | CryptoMethodRC4
32 type CryptoMethod uint32
35 // Prime P according to the spec, and G, the generator.
37 // The rand.Int max arg for use in newPadLen()
39 // For use in initer's hashes
43 // Verification constant "VC" which is all zeroes in the bittorrent
48 // Tracks counts of received crypto_provides
49 cryptoProvidesCount = expvar.NewMap("mseCryptoProvides")
53 p.SetString("0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A63A36210000000000090563", 0)
55 newPadLenMax.SetInt64(maxPadLen + 1)
58 func hash(parts ...[]byte) []byte {
60 for _, p := range parts {
72 func newEncrypt(initer bool, s, skey []byte) (c *rc4.Cipher) {
73 c, err := rc4.NewCipher(hash([]byte(func() string {
83 var burnSrc, burnDst [1024]byte
84 c.XORKeyStream(burnDst[:], burnSrc[:])
88 type cipherReader struct {
95 func (cr *cipherReader) Read(b []byte) (n int, err error) {
98 if len(cr.be) >= len(b) {
104 be = make([]byte, len(b))
106 n, err = cr.r.Read(be[:len(b)])
107 cr.c.XORKeyStream(b[:n], be[:n])
109 if len(be) > len(cr.be) {
116 func newCipherReader(c *rc4.Cipher, r io.Reader) io.Reader {
117 return &cipherReader{c: c, r: r}
120 type cipherWriter struct {
126 func (cr *cipherWriter) Write(b []byte) (n int, err error) {
127 be := func() []byte {
128 if len(cr.b) < len(b) {
129 return make([]byte, len(b))
136 cr.c.XORKeyStream(be, b)
137 n, err = cr.w.Write(be[:len(b)])
139 // The cipher will have advanced beyond the callers stream position.
140 // We can't use the cipher anymore.
143 if len(be) > len(cr.b) {
149 func newX() big.Int {
151 X.SetBytes(func() []byte {
153 _, err := rand.Read(b[:])
162 func paddedLeft(b []byte, _len int) []byte {
166 ret := make([]byte, _len)
167 if n := copy(ret[_len-len(b):], b); n != len(b) {
173 // Calculate, and send Y, our public key.
174 func (h *handshake) postY(x *big.Int) error {
177 return h.postWrite(paddedLeft(y.Bytes(), 96))
180 func (h *handshake) establishS() error {
184 _, err := io.ReadFull(h.conn, b[:])
186 return fmt.Errorf("error reading Y: %w", err)
192 copy(h.s[96-len(sBytes):96], sBytes)
196 func newPadLen() int64 {
197 i, err := rand.Int(rand.Reader, &newPadLenMax)
202 if ret < 0 || ret > maxPadLen {
208 // Manages state for both initiating and receiving handshakes.
209 type handshake struct {
212 initer bool // Whether we're initiating or receiving.
213 skeys SecretKeyIter // Skeys we'll accept if receiving.
214 skey []byte // Skey we're initiating with.
215 ia []byte // Initial payload. Only used by the initiator.
216 // Return the bit for the crypto method the receiver wants to use.
217 chooseMethod CryptoSelector
218 // Sent to the receiver.
219 cryptoProvides CryptoMethod
232 func (h *handshake) finishWriting() {
235 h.writeCond.Broadcast()
245 func (h *handshake) writer() {
249 h.writerCond.Broadcast()
255 if len(h.writes) != 0 {
265 h.writes = h.writes[1:]
267 _, err := h.conn.Write(b)
277 func (h *handshake) postWrite(b []byte) error {
279 defer h.writeMu.Unlock()
280 if h.writeErr != nil {
283 h.writes = append(h.writes, b)
288 func xor(a, b []byte) (ret []byte) {
293 ret = make([]byte, max)
294 xorInPlace(ret, a, b)
298 func xorInPlace(dst, a, b []byte) {
304 func marshal(w io.Writer, data ...interface{}) (err error) {
305 for _, data := range data {
306 err = binary.Write(w, binary.BigEndian, data)
314 func unmarshal(r io.Reader, data ...interface{}) (err error) {
315 for _, data := range data {
316 err = binary.Read(r, binary.BigEndian, data)
324 // Looking for b at the end of a.
325 func suffixMatchLen(a, b []byte) int {
329 // i is how much of b to try to match
330 for i := len(b); i > 0; i-- {
331 // j is how many chars we've compared
334 if b[i-1-j] != a[len(a)-1-j] {
344 // Reads from r until b has been seen. Keeps the minimum amount of data in
346 func readUntil(r io.Reader, b []byte) error {
347 b1 := make([]byte, len(b))
350 _, err := io.ReadFull(r, b1[i:])
354 i = suffixMatchLen(b1, b)
358 if copy(b1, b1[len(b1)-i:]) != i {
365 type readWriter struct {
370 func (h *handshake) newEncrypt(initer bool) *rc4.Cipher {
371 return newEncrypt(initer, h.s[:], h.skey)
374 func (h *handshake) initerSteps() (ret io.ReadWriter, selected CryptoMethod, err error) {
375 h.postWrite(hash(req1, h.s[:]))
376 h.postWrite(xor(hash(req2, h.skey), hash(req3, h.s[:])))
377 buf := &bytes.Buffer{}
378 padLen := uint16(newPadLen())
379 if len(h.ia) > math.MaxUint16 {
380 err = errors.New("initial payload too large")
383 err = marshal(buf, vc[:], h.cryptoProvides, padLen, zeroPad[:padLen], uint16(len(h.ia)), h.ia)
387 e := h.newEncrypt(true)
388 be := make([]byte, buf.Len())
389 e.XORKeyStream(be, buf.Bytes())
391 bC := h.newEncrypt(false)
393 bC.XORKeyStream(eVC[:], vc[:])
394 // Read until the all zero VC. At this point we've only read the 96 byte
395 // public key, Y. There is potentially 512 byte padding, between us and
396 // the 8 byte verification constant.
397 err = readUntil(io.LimitReader(h.conn, 520), eVC[:])
400 err = errors.New("failed to synchronize on VC")
402 err = fmt.Errorf("error reading until VC: %s", err)
406 r := newCipherReader(bC, h.conn)
407 var method CryptoMethod
408 err = unmarshal(r, &method, &padLen)
412 _, err = io.CopyN(ioutil.Discard, r, int64(padLen))
416 selected = method & h.cryptoProvides
418 case CryptoMethodRC4:
419 ret = readWriter{r, &cipherWriter{e, h.conn, nil}}
420 case CryptoMethodPlaintext:
423 err = fmt.Errorf("receiver chose unsupported method: %x", method)
428 var ErrNoSecretKeyMatch = errors.New("no skey matched")
430 func (h *handshake) receiverSteps() (ret io.ReadWriter, chosen CryptoMethod, err error) {
431 // There is up to 512 bytes of padding, then the 20 byte hash.
432 err = readUntil(io.LimitReader(h.conn, 532), hash(req1, h.s[:]))
435 err = errors.New("failed to synchronize on S hash")
440 _, err = io.ReadFull(h.conn, b[:])
444 expectedHash := hash(req3, h.s[:])
445 eachHash := sha1.New()
446 var sum, xored [sha1.Size]byte
447 err = ErrNoSecretKeyMatch
448 h.skeys(func(skey []byte) bool {
452 eachHash.Sum(sum[:0])
453 xorInPlace(xored[:], sum[:], expectedHash)
454 if bytes.Equal(xored[:], b[:]) {
464 r := newCipherReader(newEncrypt(true, h.s[:], h.skey), h.conn)
467 provides CryptoMethod
471 err = unmarshal(r, vc[:], &provides, &padLen)
475 cryptoProvidesCount.Add(strconv.FormatUint(uint64(provides), 16), 1)
476 chosen = h.chooseMethod(provides)
477 _, err = io.CopyN(ioutil.Discard, r, int64(padLen))
484 h.ia = make([]byte, lenIA)
487 buf := &bytes.Buffer{}
488 w := cipherWriter{h.newEncrypt(false), buf, nil}
489 padLen = uint16(newPadLen())
490 err = marshal(&w, &vc, uint32(chosen), padLen, zeroPad[:padLen])
494 err = h.postWrite(buf.Bytes())
499 case CryptoMethodRC4:
501 io.MultiReader(bytes.NewReader(h.ia), r),
502 &cipherWriter{w.c, h.conn, nil},
504 case CryptoMethodPlaintext:
506 io.MultiReader(bytes.NewReader(h.ia), h.conn),
510 err = errors.New("chosen crypto method is not supported")
515 func (h *handshake) Do() (ret io.ReadWriter, method CryptoMethod, err error) {
516 h.writeCond.L = &h.writeMu
517 h.writerCond.L = &h.writerMu
527 err = fmt.Errorf("error while establishing secret: %w", err)
530 pad := make([]byte, newPadLen())
531 io.ReadFull(rand.Reader, pad)
532 err = h.postWrite(pad)
537 ret, method, err = h.initerSteps()
539 ret, method, err = h.receiverSteps()
544 func InitiateHandshake(
545 rw io.ReadWriter, skey, initialPayload []byte, cryptoProvides CryptoMethod,
547 ret io.ReadWriter, method CryptoMethod, err error,
554 cryptoProvides: cryptoProvides,
556 defer perf.ScopeTimerErr(&err)()
560 type HandshakeResult struct {
567 func ReceiveHandshake(rw io.ReadWriter, skeys SecretKeyIter, selectCrypto CryptoSelector) (io.ReadWriter, CryptoMethod, error) {
568 res := ReceiveHandshakeEx(rw, skeys, selectCrypto)
569 return res.ReadWriter, res.CryptoMethod, res.error
572 func ReceiveHandshakeEx(rw io.ReadWriter, skeys SecretKeyIter, selectCrypto CryptoSelector) (ret HandshakeResult) {
577 chooseMethod: selectCrypto,
579 ret.ReadWriter, ret.CryptoMethod, ret.error = h.Do()
580 ret.SecretKey = h.skey
584 // A function that given a function, calls it with secret keys until it
585 // returns false or exhausted.
586 type SecretKeyIter func(callback func(skey []byte) (more bool))
588 func DefaultCryptoSelector(provided CryptoMethod) CryptoMethod {
589 // We prefer plaintext for performance reasons.
590 if provided&CryptoMethodPlaintext != 0 {
591 return CryptoMethodPlaintext
593 return CryptoMethodRC4
596 type CryptoSelector func(CryptoMethod) CryptoMethod