github.com/amitbet/vnc2video@v0.0.0-20190616012314-9d50b9dab1d9/security_vnc.go (about)

     1  package vnc2video
     2  
     3  import (
     4  	"bytes"
     5  	"crypto/des"
     6  	"encoding/binary"
     7  	"fmt"
     8  )
     9  
    10  // ServerAuthVNC is the standard password authentication. See 7.2.2.
    11  type ServerAuthVNC struct {
    12  	Challenge []byte
    13  	Password  []byte
    14  	Crypted   []byte
    15  }
    16  
    17  func (*ServerAuthVNC) Type() SecurityType {
    18  	return SecTypeVNC
    19  }
    20  func (*ServerAuthVNC) SubType() SecuritySubType {
    21  	return SecSubTypeUnknown
    22  }
    23  
    24  func (auth *ServerAuthVNC) WriteChallenge(c Conn) error {
    25  	if err := binary.Write(c, binary.BigEndian, auth.Challenge); err != nil {
    26  		return err
    27  	}
    28  	return c.Flush()
    29  }
    30  
    31  func (auth *ServerAuthVNC) ReadChallenge(c Conn) error {
    32  	var crypted [16]byte
    33  	if err := binary.Read(c, binary.BigEndian, &crypted); err != nil {
    34  		return err
    35  	}
    36  	auth.Crypted = crypted[:]
    37  	return nil
    38  }
    39  
    40  func (auth *ServerAuthVNC) Auth(c Conn) error {
    41  	if err := auth.WriteChallenge(c); err != nil {
    42  		return err
    43  	}
    44  
    45  	if err := auth.ReadChallenge(c); err != nil {
    46  		return err
    47  	}
    48  
    49  	encrypted, err := AuthVNCEncode(auth.Password, auth.Challenge)
    50  	if err != nil {
    51  		return err
    52  	}
    53  	if !bytes.Equal(encrypted, auth.Crypted) {
    54  		return fmt.Errorf("password invalid")
    55  	}
    56  	return nil
    57  }
    58  
    59  // ClientAuthVNC is the standard password authentication. See 7.2.2.
    60  type ClientAuthVNC struct {
    61  	Challenge []byte
    62  	Password  []byte
    63  }
    64  
    65  func (*ClientAuthVNC) Type() SecurityType {
    66  	return SecTypeVNC
    67  }
    68  func (*ClientAuthVNC) SubType() SecuritySubType {
    69  	return SecSubTypeUnknown
    70  }
    71  
    72  func (auth *ClientAuthVNC) Auth(c Conn) error {
    73  	if len(auth.Password) == 0 {
    74  		return fmt.Errorf("Security Handshake failed; no password provided for VNCAuth.")
    75  	}
    76  
    77  	var challenge [16]byte
    78  	if err := binary.Read(c, binary.BigEndian, &challenge); err != nil {
    79  		return err
    80  	}
    81  
    82  	encrypted, err := AuthVNCEncode(auth.Password, challenge[:])
    83  	if err != nil {
    84  		return err
    85  	}
    86  	// Send the encrypted challenge back to server
    87  	if err := binary.Write(c, binary.BigEndian, encrypted); err != nil {
    88  		return err
    89  	}
    90  
    91  	return c.Flush()
    92  }
    93  
    94  func AuthVNCEncode(password []byte, challenge []byte) ([]byte, error) {
    95  	if len(challenge) != 16 {
    96  		return nil, fmt.Errorf("challenge size not 16 byte long")
    97  	}
    98  	// Copy password string to 8 byte 0-padded slice
    99  	key := make([]byte, 8)
   100  	copy(key, password)
   101  
   102  	// Each byte of the password needs to be reversed. This is a
   103  	// non RFC-documented behaviour of VNC clients and servers
   104  	for i := range key {
   105  		key[i] = (key[i]&0x55)<<1 | (key[i]&0xAA)>>1 // Swap adjacent bits
   106  		key[i] = (key[i]&0x33)<<2 | (key[i]&0xCC)>>2 // Swap adjacent pairs
   107  		key[i] = (key[i]&0x0F)<<4 | (key[i]&0xF0)>>4 // Swap the 2 halves
   108  	}
   109  
   110  	// Encrypt challenge with key.
   111  	cipher, err := des.NewCipher(key)
   112  	if err != nil {
   113  		return nil, err
   114  	}
   115  	for i := 0; i < len(challenge); i += cipher.BlockSize() {
   116  		cipher.Encrypt(challenge[i:i+cipher.BlockSize()], challenge[i:i+cipher.BlockSize()])
   117  	}
   118  
   119  	return challenge, nil
   120  }