|  |  |  | @ -2,12 +2,15 @@ package pnyx | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | import ( | 
		
	
		
			
				|  |  |  |  | 	"bytes" | 
		
	
		
			
				|  |  |  |  | 	"crypto/aes" | 
		
	
		
			
				|  |  |  |  | 	"crypto/cipher" | 
		
	
		
			
				|  |  |  |  | 	"crypto/ed25519" | 
		
	
		
			
				|  |  |  |  | 	"crypto/rand" | 
		
	
		
			
				|  |  |  |  | 	"crypto/sha512" | 
		
	
		
			
				|  |  |  |  | 	"encoding/binary" | 
		
	
		
			
				|  |  |  |  | 	"hash/crc32" | 
		
	
		
			
				|  |  |  |  | 	"io" | 
		
	
		
			
				|  |  |  |  | 	mrand "math/rand" | 
		
	
		
			
				|  |  |  |  | 	"net" | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | 	"fmt" | 
		
	
	
		
			
				
					|  |  |  | @ -19,6 +22,7 @@ import ( | 
		
	
		
			
				|  |  |  |  | type PacketType uint8 | 
		
	
		
			
				|  |  |  |  | const ( | 
		
	
		
			
				|  |  |  |  |   ID_LENGTH = 16 | 
		
	
		
			
				|  |  |  |  |   IV_LENGTH = aes.BlockSize | 
		
	
		
			
				|  |  |  |  |   PUBKEY_LENGTH = 32 | 
		
	
		
			
				|  |  |  |  |   ECDH_LENGTH = 32 | 
		
	
		
			
				|  |  |  |  |   SIGNATURE_LENGTH = 64 | 
		
	
	
		
			
				
					|  |  |  | @ -27,11 +31,11 @@ const ( | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   SESSION_OPEN_LENGTH = PUBKEY_LENGTH + ECDH_LENGTH + SIGNATURE_LENGTH | 
		
	
		
			
				|  |  |  |  |   SESSION_CONNECT_LENGTH = 2 + HMAC_LENGTH // + return addr string length
 | 
		
	
		
			
				|  |  |  |  |   SESSION_CLOSE_LENGTH = HMAC_LENGTH | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   SESSION_OPEN PacketType = iota | 
		
	
		
			
				|  |  |  |  |   SESSION_CONNECT | 
		
	
		
			
				|  |  |  |  |   SESSION_CLOSE | 
		
	
		
			
				|  |  |  |  |   SESSION_CLOSED | 
		
	
		
			
				|  |  |  |  |   SESSION_DATA | 
		
	
		
			
				|  |  |  |  | ) | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
	
		
			
				
					|  |  |  | @ -82,9 +86,9 @@ func NewSessionOpen(key ed25519.PrivateKey) ([]byte, ed25519.PrivateKey, error) | 
		
	
		
			
				|  |  |  |  |   return packet, private, nil | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | func ParseSessionOpen(session_open []byte) (ed25519.PublicKey, ed25519.PublicKey, error) { | 
		
	
		
			
				|  |  |  |  | func ParseSessionOpen(ecdh ed25519.PrivateKey, session_open []byte) (Session, error) { | 
		
	
		
			
				|  |  |  |  |   if len(session_open) != SESSION_OPEN_LENGTH { | 
		
	
		
			
				|  |  |  |  |     return nil, nil, fmt.Errorf("Bad SESSION_OPEN length: %d/%d", len(session_open), SESSION_OPEN_LENGTH) | 
		
	
		
			
				|  |  |  |  |     return Session{}, fmt.Errorf("Bad SESSION_OPEN length: %d/%d", len(session_open), SESSION_OPEN_LENGTH) | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   cur := 0 | 
		
	
	
		
			
				
					|  |  |  | @ -100,10 +104,38 @@ func ParseSessionOpen(session_open []byte) (ed25519.PublicKey, ed25519.PublicKey | 
		
	
		
			
				|  |  |  |  |   cur += SIGNATURE_LENGTH | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   if ed25519.Verify(client_pubkey, digest, signature) == false { | 
		
	
		
			
				|  |  |  |  |     return nil, nil, fmt.Errorf("SESSION_OPEN signature verification failed") | 
		
	
		
			
				|  |  |  |  |     return Session{}, fmt.Errorf("SESSION_OPEN signature verification failed") | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   return client_pubkey, client_ecdh, nil | 
		
	
		
			
				|  |  |  |  |   session_secret, err := ECDH(client_ecdh, ecdh) | 
		
	
		
			
				|  |  |  |  |   if err != nil { | 
		
	
		
			
				|  |  |  |  |     return Session{}, err | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   session_id := ID[SessionID](session_secret) | 
		
	
		
			
				|  |  |  |  |   client_id := ID[PeerID](client_pubkey) | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   session_cipher, err := aes.NewCipher(session_secret) | 
		
	
		
			
				|  |  |  |  |   if err != nil { | 
		
	
		
			
				|  |  |  |  |     return Session{}, err | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   seed_bytes := make([]byte, 8) | 
		
	
		
			
				|  |  |  |  |   read, err := rand.Read(seed_bytes) | 
		
	
		
			
				|  |  |  |  |   if err != nil { | 
		
	
		
			
				|  |  |  |  |     return Session{}, err | 
		
	
		
			
				|  |  |  |  |   } else if read != 8 { | 
		
	
		
			
				|  |  |  |  |     return Session{}, fmt.Errorf("Not enough entropy to create session") | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   return Session{ | 
		
	
		
			
				|  |  |  |  |     ID: session_id, | 
		
	
		
			
				|  |  |  |  |     remote: nil, | 
		
	
		
			
				|  |  |  |  |     Peer: client_id, | 
		
	
		
			
				|  |  |  |  |     secret: session_secret, | 
		
	
		
			
				|  |  |  |  |     cipher: session_cipher, | 
		
	
		
			
				|  |  |  |  |     iv_generator: mrand.NewSource(int64(binary.BigEndian.Uint64(seed_bytes))).(mrand.Source64), | 
		
	
		
			
				|  |  |  |  |   }, nil | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | func NewSessionConnect(address *net.UDPAddr, session_secret []byte) []byte { | 
		
	
	
		
			
				
					|  |  |  | @ -166,20 +198,22 @@ func ParseSessionConnect(session_connect []byte, session_secret []byte) (*net.UD | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | func NewSessionData(session *Session, packet []byte) ([]byte, error) { | 
		
	
		
			
				|  |  |  |  |   iv := make([]byte, 32) | 
		
	
		
			
				|  |  |  |  |   for i := 0; i < 4; i++ { | 
		
	
		
			
				|  |  |  |  |   iv := make([]byte, IV_LENGTH) | 
		
	
		
			
				|  |  |  |  |   for i := 0; i < IV_LENGTH/8; i++ { | 
		
	
		
			
				|  |  |  |  |     binary.BigEndian.PutUint64(iv[i*8:], session.iv_generator.Uint64()) | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   stream := cipher.NewOFB(session.cipher, iv[:]) | 
		
	
		
			
				|  |  |  |  |   header := make([]byte, COMMAND_LENGTH + ID_LENGTH) | 
		
	
		
			
				|  |  |  |  |   header := make([]byte, COMMAND_LENGTH + ID_LENGTH + IV_LENGTH) | 
		
	
		
			
				|  |  |  |  |   header[0] = byte(SESSION_DATA) | 
		
	
		
			
				|  |  |  |  |   copy(header[1:], session.ID[:]) | 
		
	
		
			
				|  |  |  |  |   copy(header[COMMAND_LENGTH:], session.ID[:]) | 
		
	
		
			
				|  |  |  |  |   copy(header[COMMAND_LENGTH+ID_LENGTH:], iv) | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   packet_encrypted := bytes.NewBuffer(header) | 
		
	
		
			
				|  |  |  |  |   writer := &cipher.StreamWriter{S: stream, W: packet_encrypted} | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   _, err := io.Copy(writer, bytes.NewBuffer(packet)) | 
		
	
		
			
				|  |  |  |  |   // Encrypt the packet with a crc32 checksum appended to the end
 | 
		
	
		
			
				|  |  |  |  |   _, err := io.Copy(writer, bytes.NewBuffer(binary.BigEndian.AppendUint32(packet, crc32.ChecksumIEEE(packet)))) | 
		
	
		
			
				|  |  |  |  |   if err != nil { | 
		
	
		
			
				|  |  |  |  |     return nil, err | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
	
		
			
				
					|  |  |  | @ -187,16 +221,44 @@ func NewSessionData(session *Session, packet []byte) ([]byte, error) { | 
		
	
		
			
				|  |  |  |  |   return packet_encrypted.Bytes(), nil | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | func ParseSessionData(session *Session, data []byte) ([]byte, error) { | 
		
	
		
			
				|  |  |  |  |   iv := data[0:32] | 
		
	
		
			
				|  |  |  |  | func ParseSessionData(session *Session, encrypted []byte) ([]byte, error) { | 
		
	
		
			
				|  |  |  |  |   iv := encrypted[0:IV_LENGTH] | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   stream := cipher.NewOFB(session.cipher, iv) | 
		
	
		
			
				|  |  |  |  |   var packet_clear bytes.Buffer | 
		
	
		
			
				|  |  |  |  |   reader := &cipher.StreamReader{S: stream, R: bytes.NewBuffer(data)} | 
		
	
		
			
				|  |  |  |  |   _, err := io.Copy(&packet_clear, reader) | 
		
	
		
			
				|  |  |  |  |   var packet bytes.Buffer | 
		
	
		
			
				|  |  |  |  |   reader := &cipher.StreamReader{S: stream, R: bytes.NewBuffer(encrypted[IV_LENGTH:])} | 
		
	
		
			
				|  |  |  |  |   _, err := io.Copy(&packet, reader) | 
		
	
		
			
				|  |  |  |  |   if err != nil { | 
		
	
		
			
				|  |  |  |  |     return nil, err | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   return packet_clear.Bytes(), nil | 
		
	
		
			
				|  |  |  |  |   packet_clear := packet.Bytes() | 
		
	
		
			
				|  |  |  |  |   checksum := binary.BigEndian.Uint32(packet_clear[len(packet_clear)-4:]) | 
		
	
		
			
				|  |  |  |  |   data := packet_clear[:len(packet_clear)-4] | 
		
	
		
			
				|  |  |  |  |   calculated := crc32.ChecksumIEEE(data) | 
		
	
		
			
				|  |  |  |  |   if checksum != calculated { | 
		
	
		
			
				|  |  |  |  |     return nil, fmt.Errorf("SESSION_DATA checksum mismatch: %x != %x", checksum, calculated) | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   return data, nil | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | func NewSessionClose(session *Session) []byte { | 
		
	
		
			
				|  |  |  |  |   packet := make([]byte, COMMAND_LENGTH + ID_LENGTH + SESSION_CLOSE_LENGTH) | 
		
	
		
			
				|  |  |  |  |   packet[0] = COMMAND_LENGTH | 
		
	
		
			
				|  |  |  |  |   copy(packet[1:], session.ID[:]) | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   hmac := sha512.Sum512(append(session.ID[:], session.secret...)) | 
		
	
		
			
				|  |  |  |  |   copy(packet[COMMAND_LENGTH + ID_LENGTH:], hmac[:]) | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  |   return packet | 
		
	
		
			
				|  |  |  |  | } | 
		
	
		
			
				|  |  |  |  | 
 | 
		
	
		
			
				|  |  |  |  | func ParseSessionClose(session *Session, hmac []byte) error { | 
		
	
		
			
				|  |  |  |  |   calculated_hmac := sha512.Sum512(append(session.ID[:], session.secret...)) | 
		
	
		
			
				|  |  |  |  |   if slices.Compare(hmac, calculated_hmac[:]) != 0 { | 
		
	
		
			
				|  |  |  |  |     return fmt.Errorf("Session Close HMAC validation failed") | 
		
	
		
			
				|  |  |  |  |   } else { | 
		
	
		
			
				|  |  |  |  |     return nil | 
		
	
		
			
				|  |  |  |  |   } | 
		
	
		
			
				|  |  |  |  | } | 
		
	
	
		
			
				
					|  |  |  | 
 |