// Tests whether link ciphers are initializes correctly. func TestCiphers(t *testing.T) { t.Parallel() // Generate a secret key for the HKDF secret := make([]byte, 16) io.ReadFull(rand.Reader, secret) // Create the server and client links (no connection between them) clientHKDF := hkdf.New(sha1.New, secret, []byte("HKDF salt"), []byte("HKDF info")) serverHKDF := hkdf.New(sha1.New, secret, []byte("HKDF salt"), []byte("HKDF info")) client := New(nil, clientHKDF, false) server := New(nil, serverHKDF, true) // Create some random data to operate on clientData := make([]byte, 4096) serverData := make([]byte, 4096) io.ReadFull(rand.Reader, clientData) copy(serverData, clientData) // Check that encryption and MACing match on the two sides for i := 0; i < 1000; i++ { client.inCipher.XORKeyStream(clientData, clientData) server.outCipher.XORKeyStream(serverData, serverData) if !bytes.Equal(clientData, serverData) { t.Fatalf("cipher mismatch on the session endpoints") } client.outCipher.XORKeyStream(clientData, clientData) server.inCipher.XORKeyStream(serverData, serverData) if !bytes.Equal(clientData, serverData) { t.Fatalf("cipher mismatch on the session endpoints") } client.inMacer.Write(clientData) server.outMacer.Write(serverData) clientData = client.inMacer.Sum(nil) serverData = server.outMacer.Sum(nil) if !bytes.Equal(clientData, serverData) { t.Fatalf("macer mismatch on the session endpoints") } client.outMacer.Write(clientData) server.inMacer.Write(serverData) clientData = client.outMacer.Sum(nil) serverData = server.inMacer.Sum(nil) if !bytes.Equal(clientData, serverData) { t.Fatalf("macer mismatch on the session endpoints") } } }
// Initializes a stream into an encrypted tunnel link. func (c *Connection) initClientTunnel(strm *stream.Stream, remote uint64, id uint64, key []byte, deadline time.Time) (*link.Link, error) { // Set a socket deadline for finishing the handshake strm.Sock().SetDeadline(deadline) defer strm.Sock().SetDeadline(time.Time{}) // Send the unencrypted tunnel id to associate with the remote tunnel init := &initPacket{ConnId: remote, TunId: id} if err := strm.Send(init); err != nil { return nil, err } // Create the encrypted link and authorize it hasher := func() hash.Hash { return config.HkdfHash.New() } hkdf := hkdf.New(hasher, key, config.HkdfSalt, config.HkdfInfo) conn := link.New(strm, hkdf, false) // Send and retrieve an authorization to verify both directions auth := &proto.Message{ Head: proto.Header{ Meta: &authPacket{Id: id}, }, } if err := conn.SendDirect(auth); err != nil { return nil, err } if msg, err := conn.RecvDirect(); err != nil { return nil, err } else if auth, ok := msg.Head.Meta.(*authPacket); !ok || auth.Id != id { return nil, errors.New("protocol violation") } conn.Start(config.IrisTunnelBuffer) // Return the initialized link return conn, nil }
// Extracts a usable sized symmetric key and IV for the stream cipher from the huge master key, and // creates a CTR stream cipher. func (s *Session) makeCipher() (cipher.Stream, error) { // Create the key derivation function hasher := func() hash.Hash { return s.hash.New() } hkdf := hkdf.New(hasher, s.secret.Bytes(), hkdfSalt, hkdfInfo) // Extract the symmetric key key := make([]byte, s.keybits/8) n, err := io.ReadFull(hkdf, key) if n != len(key) || err != nil { return nil, err } // Create the block cipher block, err := s.crypter(key) if err != nil { return nil, err } // Extract the IV for the counter mode iv := make([]byte, block.BlockSize()) n, err = io.ReadFull(hkdf, iv) if n != len(iv) || err != nil { return nil, err } // Create the stream cipher return cipher.NewCTR(block, iv), nil }
// Creates a new, double link session for authenticated data transfer. The // initiator is used to decide the key derivation order for the channels. func newSession(conn *stream.Stream, secret []byte, server bool) *Session { // Create the key derivation function hasher := func() hash.Hash { return config.HkdfHash.New() } hkdf := hkdf.New(hasher, secret, config.HkdfSalt, config.HkdfInfo) // Create the encrypted control link return &Session{ kdf: hkdf, CtrlLink: link.New(conn, hkdf, server), } }
// Initializes a stream into an encrypted tunnel link. func (o *Overlay) initServerTunnel(strm *stream.Stream) error { // Set a socket deadline for finishing the handshake strm.Sock().SetDeadline(time.Now().Add(config.IrisTunnelInitTimeout)) defer strm.Sock().SetDeadline(time.Time{}) // Fetch the unencrypted client initiator init := new(initPacket) if err := strm.Recv(init); err != nil { return err } o.lock.RLock() c, ok := o.conns[init.ConnId] o.lock.RUnlock() if !ok { return errors.New("connection not found") } c.tunLock.RLock() tun, ok := c.tunLive[init.TunId] c.tunLock.RUnlock() if !ok { return errors.New("tunnel not found") } // Create the encrypted link hasher := func() hash.Hash { return config.HkdfHash.New() } hkdf := hkdf.New(hasher, tun.secret, config.HkdfSalt, config.HkdfInfo) conn := link.New(strm, hkdf, true) // Send and retrieve an authorization to verify both directions auth := &proto.Message{ Head: proto.Header{ Meta: &authPacket{Id: tun.id}, }, } if err := conn.SendDirect(auth); err != nil { return err } if msg, err := conn.RecvDirect(); err != nil { return err } else if auth, ok := msg.Head.Meta.(*authPacket); !ok || auth.Id != tun.id { return errors.New("protocol violation") } conn.Start(config.IrisTunnelBuffer) // Send back the initialized link to the pending tunnel select { case tun.init <- conn: // Connection handled by initiator return nil default: // Initiator timed out or terminated, close conn.Close() return nil // No error, since tunnel was handled, albeit not as expected } }
// Usage example that expands one master key into three other cryptographically // secure keys. func Example_usage() { // Underlying hash function to use hash := sha256.New // Cryptographically secure master key. master := []byte{0x00, 0x01, 0x02, 0x03} // i.e. NOT this. // Non secret salt, optional (can be nil) // Recommended: hash-length sized random salt := make([]byte, hash().Size()) n, err := io.ReadFull(rand.Reader, salt) if n != len(salt) || err != nil { fmt.Println("error:", err) return } // Non secret context specific info, optional (can be nil). // Note, independent from the master key. info := []byte{0x03, 0x14, 0x15, 0x92, 0x65} // Create the key derivation function hkdf := hkdf.New(hash, master, salt, info) // Generate the required keys keys := make([][]byte, 3) for i := 0; i < len(keys); i++ { keys[i] = make([]byte, 24) n, err := io.ReadFull(hkdf, keys[i]) if n != len(keys[i]) || err != nil { fmt.Println("error:", err) return } } // Keys should contain 192 bit random keys for i := 1; i <= len(keys); i++ { fmt.Printf("Key #%d: %v\n", i, !bytes.Equal(keys[i-1], make([]byte, 24))) } // Output: // Key #1: true // Key #2: true // Key #3: true }
// Tests the low level send and receive methods. func TestDirectSendRecv(t *testing.T) { t.Parallel() // Start a stream listener addr, err := net.ResolveTCPAddr("tcp", "localhost:0") if err != nil { t.Fatalf("failed to resolve local address: %v.", err) } listener, err := stream.Listen(addr) if err != nil { t.Fatalf("failed to listen for incoming streams: %v.", err) } listener.Accept(10 * time.Millisecond) defer listener.Close() // Establish a stream connection to the listener host := fmt.Sprintf("%s:%d", "localhost", addr.Port) clientStrm, err := stream.Dial(host, time.Millisecond) if err != nil { t.Fatalf("failed to connect to stream listener: %v.", err) } serverStrm := <-listener.Sink defer clientStrm.Close() defer serverStrm.Close() // Initialize the stream based encrypted links secret := make([]byte, 16) io.ReadFull(rand.Reader, secret) clientHKDF := hkdf.New(sha1.New, secret, []byte("HKDF salt"), []byte("HKDF info")) serverHKDF := hkdf.New(sha1.New, secret, []byte("HKDF salt"), []byte("HKDF info")) clientLink := New(clientStrm, clientHKDF, false) serverLink := New(serverStrm, serverHKDF, true) // Generate some random messages and pass around both ways for i := 0; i < 1000; i++ { // Generate the message to send send := &proto.Message{ Head: proto.Header{ Meta: make([]byte, 32), }, Data: make([]byte, 32), } io.ReadFull(rand.Reader, send.Head.Meta.([]byte)) io.ReadFull(rand.Reader, send.Data) send.Encrypt() // Send the message from client to server if err := clientLink.SendDirect(send); err != nil { t.Fatalf("failed to send message to server: %v.", err) } if recv, err := serverLink.RecvDirect(); err != nil { t.Fatalf("failed to receive message from client: %v.", err) } else if bytes.Compare(send.Head.Meta.([]byte), recv.Head.Meta.([]byte)) != 0 || bytes.Compare(send.Data, recv.Data) != 0 { t.Fatalf("send/receive mismatch: have %+v, want %+v.", recv, send) } // Send the message from server to client if err := serverLink.SendDirect(send); err != nil { t.Fatalf("failed to send message to client: %v.", err) } if recv, err := clientLink.RecvDirect(); err != nil { t.Fatalf("failed to receive message from server: %v.", err) } else if bytes.Compare(send.Head.Meta.([]byte), recv.Head.Meta.([]byte)) != 0 || bytes.Compare(send.Data, recv.Data) != 0 { t.Fatalf("send/receive mismatch: have %+v, want %+v.", recv, send) } } }