// Chaff the data. noncePrfx is 64-bit nonce. Output data will be much // larger: 256 bytes for each input byte. func Chaff(authKey *[32]byte, noncePrfx, in []byte) []byte { out := make([]byte, len(in)*EnlargeFactor) keys := make([]byte, 8*64) nonce := make([]byte, 24) copy(nonce[:8], noncePrfx) var i int var v byte tag := new([16]byte) macKey := new([32]byte) for n, b := range in { binary.BigEndian.PutUint64(nonce[16:], uint64(n)) salsa20.XORKeyStream(keys, keys, nonce, authKey) for i = 0; i < 8; i++ { v = (b >> uint8(i)) & 1 copy(macKey[:], keys[64*i:64*i+32]) if v == 0 { poly1305.Sum(tag, []byte("1"), macKey) } else { poly1305.Sum(tag, []byte("0"), macKey) } copy(out[16*(n*16+i*2):], tag[:]) copy(macKey[:], keys[64*i+32:64*i+64]) if v == 1 { poly1305.Sum(tag, []byte("1"), macKey) } else { poly1305.Sum(tag, []byte("0"), macKey) } copy(out[16*(n*16+i*2+1):], tag[:]) } zero(keys) } zero(macKey[:]) return out }
// Start handshake's procedure from the client. It is the entry point // for starting the handshake procedure. // First handshake packet // will be sent immediately. func HandshakeStart(addr string, conn io.Writer, conf *PeerConf) *Handshake { state := NewHandshake(addr, conn, conf) var dhPubRepr *[32]byte state.dhPriv, dhPubRepr = dhKeypairGen() state.rNonce = new([RSize]byte) if _, err := Rand.Read(state.rNonce[:]); err != nil { log.Fatalln("Error reading random for nonce:", err) } var enc []byte if conf.Noise { enc = make([]byte, conf.MTU-xtea.BlockSize-RSize) } else { enc = make([]byte, 32) } copy(enc, dhPubRepr[:]) if conf.Encless { var err error enc, err = EnclessEncode(state.dsaPubH, state.rNonce[:], enc) if err != err { panic(err) } } else { salsa20.XORKeyStream(enc, enc, state.rNonce[:], state.dsaPubH) } data := append(state.rNonce[:], enc...) data = append(data, idTag(state.Conf.Id, state.Conf.TimeSync, state.rNonce[:])...) state.conn.Write(data) return state }
func (session *CryptoSession) Encrypt(plaintext string) []byte { in := make([]byte, 64) copy(in, []byte(plaintext)) out := make([]byte, 64) salsa20.XORKeyStream(out, in, Session.nonce, Session.sessionKey) return out }
func (sr *Salsa20Rand) fill() { var nonce [8]byte binary.BigEndian.PutUint64(nonce[:], sr.nonce) sr.nonce += 1 salsa20.XORKeyStream(sr.buffer, sr.zeroes, nonce[:], &sr.key) sr.bufferOffset = 0 }
// Salsa20 cipher func BenchmarkSalsa20_1K(b *testing.B) { var key [32]byte var nonce [8]byte size := 1024 b.SetBytes(int64(size)) for i := 0; i < b.N; i++ { salsa20.XORKeyStream(buf[:size], buf[:size], nonce[:], &key) } }
// Winnow the data. func Winnow(authKey *[32]byte, noncePrfx, in []byte) ([]byte, error) { if len(in)%EnlargeFactor != 0 { return nil, errors.New("Invalid data size") } out := make([]byte, len(in)/EnlargeFactor) keys := make([]byte, 8*64) nonce := make([]byte, 24) copy(nonce[:8], noncePrfx) var i int var v byte tag := new([16]byte) macKey := new([32]byte) defer zero(macKey[:]) var is01 bool var is00 bool var is11 bool var is10 bool for n := 0; n < len(out); n++ { binary.BigEndian.PutUint64(nonce[16:], uint64(n)) salsa20.XORKeyStream(keys, keys, nonce, authKey) v = 0 for i = 0; i < 8; i++ { copy(macKey[:], keys[64*i:64*i+32]) poly1305.Sum(tag, []byte("1"), macKey) is01 = subtle.ConstantTimeCompare( tag[:], in[16*(n*16+i*2):16*(n*16+i*2+1)], ) == 1 poly1305.Sum(tag, []byte("0"), macKey) is00 = subtle.ConstantTimeCompare( tag[:], in[16*(n*16+i*2):16*(n*16+i*2+1)], ) == 1 copy(macKey[:], keys[64*i+32:64*i+64]) poly1305.Sum(tag, []byte("1"), macKey) is11 = subtle.ConstantTimeCompare( tag[:], in[16*(n*16+i*2+1):16*(n*16+i*2+2)], ) == 1 poly1305.Sum(tag, []byte("0"), macKey) is10 = subtle.ConstantTimeCompare( tag[:], in[16*(n*16+i*2+1):16*(n*16+i*2+2)], ) == 1 if !((is01 && is10) || (is00 && is11)) { zero(keys) return nil, errors.New("Invalid authenticator received") } if is11 { v = v | 1<<uint8(i) } } out[n] = v zero(keys) } return out, nil }
// Process incoming UDP packet. // ConnListen'es synchronization channel used to tell him that he is // free to receive new packets. Authenticated and decrypted packets // will be written to the interface immediately (except heartbeat ones). func (p *Peer) PktProcess(data []byte, tap io.Writer, ready chan struct{}) bool { p.size = len(data) copy(p.buf, Emptiness) copy(p.tag[:], data[p.size-poly1305.TagSize:]) copy(p.buf[S20BS:], data[NonceSize:p.size-poly1305.TagSize]) salsa20.XORKeyStream( p.buf[:S20BS+p.size-poly1305.TagSize], p.buf[:S20BS+p.size-poly1305.TagSize], data[:NonceSize], p.Key, ) copy(p.keyAuth[:], p.buf[:SSize]) if !poly1305.Verify(p.tag, data[:p.size-poly1305.TagSize], p.keyAuth) { ready <- struct{}{} p.FramesUnauth++ return false } // Check if received nonce is known to us in either of two buckets. // If yes, then this is ignored duplicate. // Check from the oldest bucket, as in most cases this will result // in constant time check. // If Bucket0 is filled, then it becomes Bucket1. p.NonceCipher.Decrypt(p.buf, data[:NonceSize]) ready <- struct{}{} p.nonceRecv, _ = binary.Uvarint(p.buf[:NonceSize]) if _, p.nonceFound = p.nonceBucket1[p.NonceRecv]; p.nonceFound { p.FramesDup++ return false } if _, p.nonceFound = p.nonceBucket0[p.NonceRecv]; p.nonceFound { p.FramesDup++ return false } p.nonceBucket0[p.NonceRecv] = struct{}{} p.nonceBucketN++ if p.nonceBucketN == NonceBucketSize { p.nonceBucket1 = p.nonceBucket0 p.nonceBucket0 = make(map[uint64]struct{}, NonceBucketSize) p.nonceBucketN = 0 } p.FramesIn++ p.BytesIn += int64(p.size) p.LastPing = time.Now() p.NonceRecv = p.nonceRecv p.pktSize, _ = binary.Uvarint(p.buf[S20BS : S20BS+PktSizeSize]) if p.pktSize == 0 { p.HeartbeatRecv++ return true } p.frame = p.buf[S20BS+PktSizeSize : S20BS+PktSizeSize+p.pktSize] p.BytesPayloadIn += int64(p.pktSize) tap.Write(p.frame) return true }
func encrypt_data(plain, keys []byte) ([]byte, error) { var iv, key []byte var block cipher.Block var stream cipher.Stream iv_offset := TotalIVLen res := make([]byte, len(plain)+iv_offset) iv = res[iv_offset-SalsaIVLen : iv_offset] _, err := rand.Read(iv) if err != nil { return nil, err } // For some reason salsa20 API is different key_array := new([32]byte) copy(key_array[:], keys[cipherKeyLen*2:]) salsa20.XORKeyStream(res[iv_offset:], plain, iv, key_array) iv_offset -= SalsaIVLen iv = res[iv_offset-IVLen : iv_offset] _, err = rand.Read(iv) if err != nil { return nil, err } key = keys[cipherKeyLen : cipherKeyLen*2] block, err = twofish.NewCipher(key) if err != nil { return nil, err } stream = cipher.NewCTR(block, iv) stream.XORKeyStream(res[iv_offset:], res[iv_offset:]) iv_offset -= IVLen iv = res[iv_offset-IVLen : iv_offset] _, err = rand.Read(iv) if err != nil { return nil, err } key = keys[:cipherKeyLen] block, err = aes.NewCipher(key) if err != nil { return nil, err } stream = cipher.NewCTR(block, iv) stream.XORKeyStream(res[iv_offset:], res[iv_offset:]) iv_offset -= IVLen if iv_offset != 0 { panic(fmt.Errorf("something went terribly wrong: iv_offset final value non-zero")) } return res, nil }
func (rng salsaRNG) Read(buf []byte) (n int, err error) { for i := range buf { buf[i] = 0 } var nonce [8]byte var key [32]byte binary.LittleEndian.PutUint32(key[:], uint32(rng.seed)) rng.seed++ salsa20.XORKeyStream(buf, buf, nonce[:], &key) return len(buf), nil }
func newNonceCipher(key *[32]byte) *xtea.Cipher { nonceKey := make([]byte, 16) salsa20.XORKeyStream( nonceKey, make([]byte, 32), make([]byte, xtea.BlockSize), key, ) ciph, err := xtea.NewCipher(nonceKey) if err != nil { panic(err) } return ciph }
// Encode the data, produce AONT package. Data size will be larger than // the original one for 48 bytes. func Encode(r *[RSize]byte, in []byte) ([]byte, error) { out := make([]byte, len(in)+HSize+RSize) copy(out, in) h := blake2b.New256() h.Write(r[:]) h.Write(in) copy(out[len(in):], h.Sum(nil)) salsaKey := new([32]byte) copy(salsaKey[:], r[:]) salsa20.XORKeyStream(out, out, dummyNonce, salsaKey) h.Reset() h.Write(out[:len(in)+32]) for i, b := range h.Sum(nil)[:RSize] { out[len(in)+32+i] = b ^ r[i] } return out, nil }
// Process incoming Ethernet packet. // ready channel is TAPListen's synchronization channel used to tell him // that he is free to receive new packets. Encrypted and authenticated // packets will be sent to remote Peer side immediately. func (p *Peer) EthProcess(data []byte, ready chan struct{}) { p.now = time.Now() p.size = len(data) // If this heartbeat is necessary if p.size == 0 && !p.LastSent.Add(p.Timeout).Before(p.now) { return } copy(p.buf, Emptiness) if p.size > 0 { copy(p.buf[S20BS+PktSizeSize:], data) ready <- struct{}{} binary.PutUvarint(p.buf[S20BS:S20BS+PktSizeSize], uint64(p.size)) p.BytesPayloadOut += int64(p.size) } else { p.HeartbeatSent++ } p.NonceOur += 2 copy(p.nonce, Emptiness) binary.PutUvarint(p.nonce, p.NonceOur) p.NonceCipher.Encrypt(p.nonce, p.nonce) salsa20.XORKeyStream(p.buf, p.buf, p.nonce, p.Key) copy(p.buf[S20BS-NonceSize:S20BS], p.nonce) copy(p.keyAuth[:], p.buf[:SSize]) if p.NoiseEnable { p.frame = p.buf[S20BS-NonceSize : S20BS+MTU-NonceSize-poly1305.TagSize] } else { p.frame = p.buf[S20BS-NonceSize : S20BS+PktSizeSize+p.size] } poly1305.Sum(p.tag, p.frame, p.keyAuth) p.BytesOut += int64(len(p.frame) + poly1305.TagSize) p.FramesOut++ if p.CPRCycle != time.Duration(0) { p.willSentCycle = p.LastSent.Add(p.CPRCycle) if p.willSentCycle.After(p.now) { time.Sleep(p.willSentCycle.Sub(p.now)) p.now = p.willSentCycle } } p.LastSent = p.now p.Conn.Write(append(p.frame, p.tag[:]...)) }
// Decode the data from AONT package. Data size will be smaller than the // original one for 48 bytes. func Decode(in []byte) ([]byte, error) { if len(in) < HSize+RSize { return nil, errors.New("Too small input buffer") } h := blake2b.New256() h.Write(in[:len(in)-RSize]) salsaKey := new([32]byte) for i, b := range h.Sum(nil)[:RSize] { salsaKey[i] = b ^ in[len(in)-RSize+i] } h.Reset() h.Write(salsaKey[:RSize]) out := make([]byte, len(in)-RSize) salsa20.XORKeyStream(out, in[:len(in)-RSize], dummyNonce, salsaKey) h.Write(out[:len(out)-HSize]) if subtle.ConstantTimeCompare(h.Sum(nil), out[len(out)-HSize:]) != 1 { return nil, errors.New("Invalid checksum") } return out[:len(out)-HSize], nil }
func decrypt_data(dst, data, keys []byte) error { var iv, key []byte var block cipher.Block var stream cipher.Stream var err error buffer := append([]byte{}, data...) iv_offset := IVLen iv = buffer[:iv_offset] key = keys[:cipherKeyLen] block, err = aes.NewCipher(key) if err != nil { return err } stream = cipher.NewCTR(block, iv) stream.XORKeyStream(buffer[iv_offset:], buffer[iv_offset:]) iv_offset += IVLen iv = buffer[iv_offset-IVLen : iv_offset] key = keys[cipherKeyLen : cipherKeyLen*2] block, err = twofish.NewCipher(key) if err != nil { return err } stream = cipher.NewCTR(block, iv) stream.XORKeyStream(buffer[iv_offset:], buffer[iv_offset:]) iv_offset += SalsaIVLen iv = buffer[iv_offset-SalsaIVLen : iv_offset] key_array := new([32]byte) copy(key_array[:], keys[cipherKeyLen*2:]) salsa20.XORKeyStream(dst, buffer[iv_offset:], iv, key_array) if len(buffer[iv_offset:]) != len(data)-TotalIVLen { return fmt.Errorf("something went terribly wrong: bufsz is wrong") } return nil }
// Process handshake message on the server side. // This function is intended to be called on server's side. // If this is the final handshake message, then new Peer object // will be created and used as a transport. If no mutually // authenticated Peer is ready, then return nil. func (h *Handshake) Server(data []byte) *Peer { // R + ENC(H(DSAPub), R, El(CDHPub)) + IDtag if h.rNonce == nil { // Generate DH keypair var dhPubRepr *[32]byte h.dhPriv, dhPubRepr = dhKeypairGen() h.rNonce = new([RSize]byte) copy(h.rNonce[:], data[:RSize]) // Decrypt remote public key and compute shared key cDHRepr := new([32]byte) salsa20.XORKeyStream( cDHRepr[:], data[RSize:RSize+32], h.rNonce[:], h.dsaPubH, ) cDH := new([32]byte) extra25519.RepresentativeToPublicKey(cDH, cDHRepr) h.key = dhKeyGen(h.dhPriv, cDH) encPub := make([]byte, 32) salsa20.XORKeyStream(encPub, dhPubRepr[:], h.rNonceNext(1), h.dsaPubH) // Generate R* and encrypt them h.rServer = new([RSize]byte) if err := randRead(h.rServer[:]); err != nil { log.Fatalln("Error reading random for R:", err) } h.sServer = new([SSize]byte) if err := randRead(h.sServer[:]); err != nil { log.Fatalln("Error reading random for S:", err) } var encRs []byte if h.Conf.Noise { encRs = make([]byte, MTU-len(encPub)-xtea.BlockSize) } else { encRs = make([]byte, RSize+SSize) } copy(encRs, append(h.rServer[:], h.sServer[:]...)) salsa20.XORKeyStream(encRs, encRs, h.rNonce[:], h.key) // Send that to client h.conn.Write(append(encPub, append(encRs, idTag(h.Conf.Id, encPub)...)...)) h.LastPing = time.Now() } else // ENC(K, R+1, RS + RC + SC + Sign(DSAPriv, K)) + IDtag if h.rClient == nil { // Decrypted Rs compare rServer dec := make([]byte, RSize+RSize+SSize+ed25519.SignatureSize) salsa20.XORKeyStream( dec, data[:RSize+RSize+SSize+ed25519.SignatureSize], h.rNonceNext(1), h.key, ) if subtle.ConstantTimeCompare(dec[:RSize], h.rServer[:]) != 1 { log.Println("Invalid server's random number with", h.addr) return nil } sign := new([ed25519.SignatureSize]byte) copy(sign[:], dec[RSize+RSize+SSize:]) if !ed25519.Verify(h.Conf.DSAPub, h.key[:], sign) { log.Println("Invalid signature from", h.addr) return nil } // Send final answer to client var enc []byte if h.Conf.Noise { enc = make([]byte, MTU-xtea.BlockSize) } else { enc = make([]byte, RSize) } copy(enc, dec[RSize:RSize+RSize]) salsa20.XORKeyStream(enc, enc, h.rNonceNext(2), h.key) h.conn.Write(append(enc, idTag(h.Conf.Id, enc)...)) // Switch peer peer := newPeer( false, h.addr, h.conn, h.Conf, keyFromSecrets(h.sServer[:], dec[RSize+RSize:RSize+RSize+SSize])) h.LastPing = time.Now() return peer } else { log.Println("Invalid handshake message from", h.addr) } return nil }
func EncryptEvent(buf *bytes.Buffer, ev Event, ctx *CryptoContext) error { start := buf.Len() buf.Write(make([]byte, 4)) var header EventHeader header.Type = GetRegistType(ev) header.Id = ev.GetId() header.Flags = 0 header.Encode(buf) method := ctx.Method ev.Encode(buf) elen := uint32(buf.Len() - start) eventContent := buf.Bytes()[start+4:] var nonce []byte encryptIV := ctx.EncryptIV if header.Type == EventAuth { method = uint8(Salsa20Encrypter) encryptIV = 0 if elen > 256 { log.Fatalf("Too large auth event with length:%d", elen) } } switch method { case Salsa20Encrypter: fallthrough case Chacha20Encrypter: nonce = make([]byte, 8) case AES256Encrypter: nonce = make([]byte, 12) elen += uint32(aes256gcm.Overhead()) } if len(nonce) > 0 { iv := encryptIV ^ uint64(elen) binary.LittleEndian.PutUint64(nonce, iv) } switch method { case Salsa20Encrypter: salsa20.XORKeyStream(eventContent, eventContent, nonce, &salsa20Key) case RC4Encrypter: rc4Cipher, _ := rc4.NewCipher(secretKey) rc4Cipher.XORKeyStream(eventContent, eventContent) case AES256Encrypter: bb := aes256gcm.Seal(eventContent[:0], nonce, eventContent, nil) if len(bb)-len(eventContent) != aes256gcm.Overhead() { log.Printf("Expected aes bytes %d after encrypt %d bytes", len(bb), len(eventContent)) } copy(eventContent, bb[0:len(eventContent)]) if len(bb) > len(eventContent) { buf.Write(bb[len(eventContent):]) } case Chacha20Encrypter: chacha20XOR(nonce, eventContent, eventContent) //chacha20Cipher, _ := chacha20.New(secretKey, nonce) //chacha20Cipher.XORKeyStream(eventContent, eventContent) } //log.Printf("Enc event(%d):%T with iv:%d with len:%d_%d %d", ev.GetId(), ev, encryptIV, elen, len(eventContent), method) if header.Type == EventAuth { base := rand.Int31n(0xFFFFFF) elen = (uint32(base) << 8) + elen } else { elen = elen ^ uint32(encryptIV) } binary.LittleEndian.PutUint32(buf.Bytes()[start:start+4], elen) if header.Type != EventAuth { ctx.EncryptIV++ } return nil }
func DecryptEvent(buf *bytes.Buffer, ctx *CryptoContext) (err error, ev Event) { if buf.Len() < 4 { return EBNR, nil } elen := binary.LittleEndian.Uint32(buf.Bytes()[0:4]) method := ctx.Method if method == 0 && ctx.DecryptIV == 0 { method = Salsa20Encrypter elen = elen & uint32(0xFF) } else { elen = elen ^ uint32(ctx.DecryptIV) } if elen > uint32(buf.Len()) { return EBNR, nil } if elen >= largeEventLimit { return ErrToolargeEvent, nil } buf.Next(4) body := buf.Next(int(elen - 4)) var nonce []byte switch method { case Salsa20Encrypter: fallthrough case Chacha20Encrypter: nonce = make([]byte, 8) case AES256Encrypter: nonce = make([]byte, 12) } if len(nonce) > 0 { iv := ctx.DecryptIV ^ uint64(elen) binary.LittleEndian.PutUint64(nonce, iv) } switch method { case Salsa20Encrypter: salsa20.XORKeyStream(body, body, nonce, &salsa20Key) case RC4Encrypter: rc4Cipher, _ := rc4.NewCipher(secretKey) rc4Cipher.XORKeyStream(body, body) case AES256Encrypter: bb, err := aes256gcm.Open(body[:0], nonce, body, nil) if nil != err { return err, nil } body = bb case Chacha20Encrypter: chacha20XOR(nonce, body, body) //cipher, _ := chacha20.New(secretKey, nonce) //cipher.XORKeyStream(body, body) } ebuf := bytes.NewBuffer(body) var header EventHeader if err = header.Decode(ebuf); nil != err { log.Printf("Failed to decode event header") return } //log.Printf("Dec event(%d) with iv:%d with len:%d_%d %d %d", header.Id, ctx.DecryptIV, elen, len(body), method, header.Type) var tmp interface{} if err, tmp = NewEventInstance(header.Type); nil != err { log.Printf("Failed to decode event with err:%v with len:%d", err, elen) return } ev = tmp.(Event) ev.SetId(header.Id) err = ev.Decode(ebuf) if nil != err { log.Printf("Failed to decode event:%T with err:%v with len:%d", tmp, err, elen) } if header.Type != EventAuth { ctx.DecryptIV++ } return }
func (session *CryptoSession) Decrypt(ciphertext []byte) string { out := make([]byte, len(ciphertext)) salsa20.XORKeyStream(out, ciphertext, Session.nonce, Session.sessionKey) return string(out) }
// Process handshake message on the client side. // This function is intended to be called on client's side. // If this is the final handshake message, then new Peer object // will be created and used as a transport. If no mutually // authenticated Peer is ready, then return nil. func (h *Handshake) Client(data []byte) *Peer { // ENC(H(DSAPub), R+1, El(SDHPub)) + ENC(K, R, RS + SS) + IDtag if h.rServer == nil && h.key == nil { // Decrypt remote public key and compute shared key sDHRepr := new([32]byte) salsa20.XORKeyStream(sDHRepr[:], data[:32], h.rNonceNext(1), h.dsaPubH) sDH := new([32]byte) extra25519.RepresentativeToPublicKey(sDH, sDHRepr) h.key = dhKeyGen(h.dhPriv, sDH) // Decrypt Rs decRs := make([]byte, RSize+SSize) salsa20.XORKeyStream(decRs, data[SSize:32+RSize+SSize], h.rNonce[:], h.key) h.rServer = new([RSize]byte) copy(h.rServer[:], decRs[:RSize]) h.sServer = new([SSize]byte) copy(h.sServer[:], decRs[RSize:]) // Generate R* and signature and encrypt them h.rClient = new([RSize]byte) if err := randRead(h.rClient[:]); err != nil { log.Fatalln("Error reading random for R:", err) } h.sClient = new([SSize]byte) if err := randRead(h.sClient[:]); err != nil { log.Fatalln("Error reading random for S:", err) } sign := ed25519.Sign(h.Conf.DSAPriv, h.key[:]) var enc []byte if h.Conf.Noise { enc = make([]byte, MTU-xtea.BlockSize) } else { enc = make([]byte, RSize+RSize+SSize+ed25519.SignatureSize) } copy(enc, append(h.rServer[:], append(h.rClient[:], append(h.sClient[:], sign[:]...)...)...)) salsa20.XORKeyStream(enc, enc, h.rNonceNext(1), h.key) // Send that to server h.conn.Write(append(enc, idTag(h.Conf.Id, enc)...)) h.LastPing = time.Now() } else // ENC(K, R+2, RC) + IDtag if h.key != nil { // Decrypt rClient dec := make([]byte, RSize) salsa20.XORKeyStream(dec, data[:RSize], h.rNonceNext(2), h.key) if subtle.ConstantTimeCompare(dec, h.rClient[:]) != 1 { log.Println("Invalid client's random number with", h.addr) return nil } // Switch peer peer := newPeer( true, h.addr, h.conn, h.Conf, keyFromSecrets(h.sServer[:], h.sClient[:]), ) h.LastPing = time.Now() return peer } else { log.Println("Invalid handshake stage from", h.addr) } return nil }
// Process handshake message on the client side. // This function is intended to be called on client's side. // If this is the final handshake message, then new Peer object // will be created and used as a transport. If no mutually // authenticated Peer is ready, then return nil. func (h *Handshake) Client(data []byte) *Peer { // ENC(H(DSAPub), R+1, El(SDHPub)) + ENC(K, R, RS + SS) + IDtag if h.rServer == nil && h.key == nil && ((!h.Conf.Encless && len(data) >= 80) || (h.Conf.Encless && len(data) == 2*(EnclessEnlargeSize+h.Conf.MTU))) { // Decrypt remote public key sDHRepr := new([32]byte) var tmp []byte var err error if h.Conf.Encless { tmp, err = EnclessDecode( h.dsaPubH, h.rNonceNext(1), data[:len(data)/2], ) if err != nil { log.Println("Unable to decode packet from", h.addr, err) return nil } copy(sDHRepr[:], tmp[:32]) } else { salsa20.XORKeyStream( sDHRepr[:], data[:32], h.rNonceNext(1), h.dsaPubH, ) } // Compute shared key sDH := new([32]byte) extra25519.RepresentativeToPublicKey(sDH, sDHRepr) h.key = dhKeyGen(h.dhPriv, sDH) // Decrypt Rs h.rServer = new([RSize]byte) h.sServer = new([SSize]byte) if h.Conf.Encless { tmp, err = EnclessDecode( h.key, h.rNonce[:], data[len(data)/2:len(data)-xtea.BlockSize], ) if err != nil { log.Println("Unable to decode packet from", h.addr, err) return nil } copy(h.rServer[:], tmp[:RSize]) copy(h.sServer[:], tmp[RSize:RSize+SSize]) } else { decRs := make([]byte, RSize+SSize) salsa20.XORKeyStream( decRs, data[SSize:SSize+RSize+SSize], h.rNonce[:], h.key, ) copy(h.rServer[:], decRs[:RSize]) copy(h.sServer[:], decRs[RSize:]) } // Generate R* and signature and encrypt them h.rClient = new([RSize]byte) if _, err = Rand.Read(h.rClient[:]); err != nil { log.Fatalln("Error reading random for R:", err) } h.sClient = new([SSize]byte) if _, err = Rand.Read(h.sClient[:]); err != nil { log.Fatalln("Error reading random for S:", err) } sign := ed25519.Sign(h.Conf.DSAPriv, h.key[:]) var enc []byte if h.Conf.Noise { enc = make([]byte, h.Conf.MTU-xtea.BlockSize) } else { enc = make([]byte, RSize+RSize+SSize+ed25519.SignatureSize) } copy(enc, h.rServer[:]) copy(enc[RSize:], h.rClient[:]) copy(enc[RSize+RSize:], h.sClient[:]) copy(enc[RSize+RSize+SSize:], sign[:]) if h.Conf.Encless { enc, err = EnclessEncode(h.key, h.rNonceNext(1), enc) if err != nil { panic(err) } } else { salsa20.XORKeyStream(enc, enc, h.rNonceNext(1), h.key) } // Send that to server h.conn.Write(append(enc, idTag(h.Conf.Id, h.Conf.TimeSync, enc)...)) h.LastPing = time.Now() } else // ENC(K, R+2, RC) + IDtag if h.key != nil && ((!h.Conf.Encless && len(data) >= 16) || (h.Conf.Encless && len(data) == EnclessEnlargeSize+h.Conf.MTU)) { var err error // Decrypt rClient var dec []byte if h.Conf.Encless { dec, err = EnclessDecode( h.key, h.rNonceNext(2), data[:len(data)-xtea.BlockSize], ) if err != nil { log.Println("Unable to decode packet from", h.addr, err) return nil } dec = dec[:RSize] } else { dec = make([]byte, RSize) salsa20.XORKeyStream(dec, data[:RSize], h.rNonceNext(2), h.key) } if subtle.ConstantTimeCompare(dec, h.rClient[:]) != 1 { log.Println("Invalid client's random number with", h.addr) return nil } // Switch peer peer := newPeer( true, h.addr, h.conn, h.Conf, keyFromSecrets(h.sServer[:], h.sClient[:]), ) h.LastPing = time.Now() return peer } else { log.Println("Invalid handshake stage from", h.addr) } return nil }
// Process handshake message on the server side. // This function is intended to be called on server's side. // If this is the final handshake message, then new Peer object // will be created and used as a transport. If no mutually // authenticated Peer is ready, then return nil. func (h *Handshake) Server(data []byte) *Peer { // R + ENC(H(DSAPub), R, El(CDHPub)) + IDtag if h.rNonce == nil && ((!h.Conf.Encless && len(data) >= 48) || (h.Conf.Encless && len(data) == EnclessEnlargeSize+h.Conf.MTU)) { h.rNonce = new([RSize]byte) copy(h.rNonce[:], data[:RSize]) // Decrypt remote public key cDHRepr := new([32]byte) if h.Conf.Encless { out, err := EnclessDecode( h.dsaPubH, h.rNonce[:], data[RSize:len(data)-xtea.BlockSize], ) if err != nil { log.Println("Unable to decode packet from", h.addr, err) return nil } copy(cDHRepr[:], out) } else { salsa20.XORKeyStream( cDHRepr[:], data[RSize:RSize+32], h.rNonce[:], h.dsaPubH, ) } // Generate DH keypair var dhPubRepr *[32]byte h.dhPriv, dhPubRepr = dhKeypairGen() // Compute shared key cDH := new([32]byte) extra25519.RepresentativeToPublicKey(cDH, cDHRepr) h.key = dhKeyGen(h.dhPriv, cDH) var encPub []byte var err error if h.Conf.Encless { encPub = make([]byte, h.Conf.MTU) copy(encPub, dhPubRepr[:]) encPub, err = EnclessEncode(h.dsaPubH, h.rNonceNext(1), encPub) if err != nil { panic(err) } } else { encPub = make([]byte, 32) salsa20.XORKeyStream(encPub, dhPubRepr[:], h.rNonceNext(1), h.dsaPubH) } // Generate R* and encrypt them h.rServer = new([RSize]byte) if _, err = Rand.Read(h.rServer[:]); err != nil { log.Fatalln("Error reading random for R:", err) } h.sServer = new([SSize]byte) if _, err = Rand.Read(h.sServer[:]); err != nil { log.Fatalln("Error reading random for S:", err) } var encRs []byte if h.Conf.Noise && !h.Conf.Encless { encRs = make([]byte, h.Conf.MTU-len(encPub)-xtea.BlockSize) } else if h.Conf.Encless { encRs = make([]byte, h.Conf.MTU-xtea.BlockSize) } else { encRs = make([]byte, RSize+SSize) } copy(encRs, append(h.rServer[:], h.sServer[:]...)) if h.Conf.Encless { encRs, err = EnclessEncode(h.key, h.rNonce[:], encRs) if err != nil { panic(err) } } else { salsa20.XORKeyStream(encRs, encRs, h.rNonce[:], h.key) } // Send that to client h.conn.Write(append(encPub, append( encRs, idTag(h.Conf.Id, h.Conf.TimeSync, encPub)..., )...)) h.LastPing = time.Now() } else // ENC(K, R+1, RS + RC + SC + Sign(DSAPriv, K)) + IDtag if h.rClient == nil && ((!h.Conf.Encless && len(data) >= 120) || (h.Conf.Encless && len(data) == EnclessEnlargeSize+h.Conf.MTU)) { var dec []byte var err error if h.Conf.Encless { dec, err = EnclessDecode( h.key, h.rNonceNext(1), data[:len(data)-xtea.BlockSize], ) if err != nil { log.Println("Unable to decode packet from", h.addr, err) return nil } dec = dec[:RSize+RSize+SSize+ed25519.SignatureSize] } else { dec = make([]byte, RSize+RSize+SSize+ed25519.SignatureSize) salsa20.XORKeyStream( dec, data[:RSize+RSize+SSize+ed25519.SignatureSize], h.rNonceNext(1), h.key, ) } if subtle.ConstantTimeCompare(dec[:RSize], h.rServer[:]) != 1 { log.Println("Invalid server's random number with", h.addr) return nil } sign := new([ed25519.SignatureSize]byte) copy(sign[:], dec[RSize+RSize+SSize:]) if !ed25519.Verify(h.Conf.Verifier.Pub, h.key[:], sign) { log.Println("Invalid signature from", h.addr) return nil } // Send final answer to client var enc []byte if h.Conf.Noise { enc = make([]byte, h.Conf.MTU-xtea.BlockSize) } else { enc = make([]byte, RSize) } copy(enc, dec[RSize:RSize+RSize]) if h.Conf.Encless { enc, err = EnclessEncode(h.key, h.rNonceNext(2), enc) if err != nil { panic(err) } } else { salsa20.XORKeyStream(enc, enc, h.rNonceNext(2), h.key) } h.conn.Write(append(enc, idTag(h.Conf.Id, h.Conf.TimeSync, enc)...)) // Switch peer peer := newPeer( false, h.addr, h.conn, h.Conf, keyFromSecrets(h.sServer[:], dec[RSize+RSize:RSize+RSize+SSize])) h.LastPing = time.Now() return peer } else { log.Println("Invalid handshake message from", h.addr) } return nil }
// Process incoming Ethernet packet. // ready channel is TAPListen's synchronization channel used to tell him // that he is free to receive new packets. Encrypted and authenticated // packets will be sent to remote Peer side immediately. func (p *Peer) EthProcess(data []byte) { p.now = time.Now() p.BusyT.Lock() // Zero size is a heartbeat packet if len(data) == 0 { // If this heartbeat is necessary if !p.LastSent.Add(p.Timeout).Before(p.now) { p.BusyT.Unlock() return } p.bufT[S20BS+0] = byte(0) p.bufT[S20BS+1] = byte(0) p.HeartbeatSent++ } else { // Copy payload to our internal buffer and we are ready to // accept the next one binary.BigEndian.PutUint16( p.bufT[S20BS:S20BS+PktSizeSize], uint16(len(data)), ) copy(p.bufT[S20BS+PktSizeSize:], data) p.BytesPayloadOut += int64(len(data)) } if p.NoiseEnable { p.frameT = p.bufT[S20BS : S20BS+MTU-TagSize] } else { p.frameT = p.bufT[S20BS : S20BS+PktSizeSize+len(data)+NonceSize] } p.nonceOur += 2 binary.BigEndian.PutUint64(p.frameT[len(p.frameT)-NonceSize:], p.nonceOur) p.NonceCipher.Encrypt( p.frameT[len(p.frameT)-NonceSize:], p.frameT[len(p.frameT)-NonceSize:], ) for i := 0; i < SSize; i++ { p.bufT[i] = byte(0) } salsa20.XORKeyStream( p.bufT[:S20BS+len(p.frameT)-NonceSize], p.bufT[:S20BS+len(p.frameT)-NonceSize], p.frameT[len(p.frameT)-NonceSize:], p.Key, ) copy(p.keyAuthT[:], p.bufT[:SSize]) poly1305.Sum(p.tagT, p.frameT, p.keyAuthT) atomic.AddInt64(&p.BytesOut, int64(len(p.frameT)+TagSize)) p.FramesOut++ if p.CPRCycle != time.Duration(0) { p.willSentCycle = p.LastSent.Add(p.CPRCycle) if p.willSentCycle.After(p.now) { time.Sleep(p.willSentCycle.Sub(p.now)) p.now = p.willSentCycle } } p.LastSent = p.now p.Conn.Write(append(p.tagT[:], p.frameT...)) p.BusyT.Unlock() }
// Pack locks a given payload using the golang.org/x/crypto/salsa20 implementation func (m SalsaManager) Pack(payload []byte) string { out := make([]byte, len(payload)) salsa20.XORKeyStream(out, payload, iv, &m.key) return base64.StdEncoding.EncodeToString(out) }
func (p *Peer) PktProcess(data []byte, tap io.Writer, reorderable bool) bool { if len(data) < MinPktLength { return false } p.BusyR.Lock() for i := 0; i < SSize; i++ { p.bufR[i] = byte(0) } copy(p.bufR[S20BS:], data[TagSize:]) salsa20.XORKeyStream( p.bufR[:S20BS+len(data)-TagSize-NonceSize], p.bufR[:S20BS+len(data)-TagSize-NonceSize], data[len(data)-NonceSize:], p.Key, ) copy(p.keyAuthR[:], p.bufR[:SSize]) copy(p.tagR[:], data[:TagSize]) if !poly1305.Verify(p.tagR, data[TagSize:], p.keyAuthR) { p.FramesUnauth++ p.BusyR.Unlock() return false } // Check if received nonce is known to us in either of two buckets. // If yes, then this is ignored duplicate. // Check from the oldest bucket, as in most cases this will result // in constant time check. // If Bucket0 is filled, then it becomes Bucket1. p.NonceCipher.Decrypt( data[len(data)-NonceSize:], data[len(data)-NonceSize:], ) p.nonceRecv = binary.BigEndian.Uint64(data[len(data)-NonceSize:]) if reorderable { _, p.nonceFound0 = p.nonceBucket0[p.nonceRecv] _, p.nonceFound1 = p.nonceBucket1[p.nonceRecv] if p.nonceFound0 || p.nonceFound1 || p.nonceRecv+2*NonceBucketSize < p.nonceLatest { p.FramesDup++ p.BusyR.Unlock() return false } p.nonceBucket0[p.nonceRecv] = struct{}{} p.nonceBucketN++ if p.nonceBucketN == NonceBucketSize { p.nonceBucket1 = p.nonceBucket0 p.nonceBucket0 = make(map[uint64]struct{}, NonceBucketSize) p.nonceBucketN = 0 } } else { if p.nonceRecv != p.NonceExpect { p.FramesDup++ p.BusyR.Unlock() return false } p.NonceExpect += 2 } if p.nonceRecv > p.nonceLatest { p.nonceLatest = p.nonceRecv } p.FramesIn++ atomic.AddInt64(&p.BytesIn, int64(len(data))) p.LastPing = time.Now() p.pktSizeR = binary.BigEndian.Uint16(p.bufR[S20BS : S20BS+PktSizeSize]) if p.pktSizeR == 0 { p.HeartbeatRecv++ p.BusyR.Unlock() return true } if int(p.pktSizeR) > len(data)-MinPktLength { return false } p.BytesPayloadIn += int64(p.pktSizeR) tap.Write(p.bufR[S20BS+PktSizeSize : S20BS+PktSizeSize+p.pktSizeR]) p.BusyR.Unlock() return true }
// Unpack unlocks a given payload using the golang.org/x/crypto/salsa20 implementation func (m SalsaManager) Unpack(payload string) []byte { in, _ := base64.StdEncoding.DecodeString(payload) out := make([]byte, len(in)) salsa20.XORKeyStream(out, in, iv, &m.key) return out }
// Process incoming Ethernet packet. // ready channel is TAPListen's synchronization channel used to tell him // that he is free to receive new packets. Encrypted and authenticated // packets will be sent to remote Peer side immediately. func (p *Peer) EthProcess(data []byte) { if len(data) > p.MTU-1 { // 1 is for padding byte log.Println("Padded data packet size", len(data)+1, "is bigger than MTU", p.MTU, p) return } p.now = time.Now() p.BusyT.Lock() // Zero size is a heartbeat packet SliceZero(p.bufT) if len(data) == 0 { // If this heartbeat is necessary if !p.LastSent.Add(p.Timeout).Before(p.now) { p.BusyT.Unlock() return } p.bufT[S20BS+0] = PadByte p.HeartbeatSent++ } else { // Copy payload to our internal buffer and we are ready to // accept the next one copy(p.bufT[S20BS:], data) p.bufT[S20BS+len(data)] = PadByte p.BytesPayloadOut += uint64(len(data)) } if p.NoiseEnable && !p.Encless { p.frameT = p.bufT[S20BS : S20BS+p.MTU-TagSize] } else if p.Encless { p.frameT = p.bufT[S20BS : S20BS+p.MTU] } else { p.frameT = p.bufT[S20BS : S20BS+len(data)+1+NonceSize] } p.nonceOur += 2 binary.BigEndian.PutUint64(p.frameT[len(p.frameT)-NonceSize:], p.nonceOur) p.NonceCipher.Encrypt( p.frameT[len(p.frameT)-NonceSize:], p.frameT[len(p.frameT)-NonceSize:], ) var out []byte if p.Encless { var err error out, err = EnclessEncode( p.Key, p.frameT[len(p.frameT)-NonceSize:], p.frameT[:len(p.frameT)-NonceSize], ) if err != nil { panic(err) } out = append(out, p.frameT[len(p.frameT)-NonceSize:]...) } else { salsa20.XORKeyStream( p.bufT[:S20BS+len(p.frameT)-NonceSize], p.bufT[:S20BS+len(p.frameT)-NonceSize], p.frameT[len(p.frameT)-NonceSize:], p.Key, ) copy(p.keyAuthT[:], p.bufT[:SSize]) poly1305.Sum(p.tagT, p.frameT, p.keyAuthT) atomic.AddUint64(&p.BytesOut, uint64(len(p.frameT)+TagSize)) out = append(p.tagT[:], p.frameT...) } p.FramesOut++ if p.CPRCycle != time.Duration(0) { p.willSentCycle = p.LastSent.Add(p.CPRCycle) if p.willSentCycle.After(p.now) { time.Sleep(p.willSentCycle.Sub(p.now)) p.now = p.willSentCycle } } p.LastSent = p.now p.Conn.Write(out) p.BusyT.Unlock() }