From a63ca441a93c33d60c3f8b1763bee735dbd84a2f Mon Sep 17 00:00:00 2001 From: Jonathan Barrow Date: Mon, 15 Jan 2024 15:07:57 -0500 Subject: [PATCH] encryption: added QuazalRC4 --- encryption/quazal_rc4.go | 95 ++++++++++++++++++++++++++++++++++++++++ prudp_packet.go | 8 ---- prudp_server.go | 8 ---- 3 files changed, 95 insertions(+), 16 deletions(-) create mode 100644 encryption/quazal_rc4.go diff --git a/encryption/quazal_rc4.go b/encryption/quazal_rc4.go new file mode 100644 index 0000000..63c423d --- /dev/null +++ b/encryption/quazal_rc4.go @@ -0,0 +1,95 @@ +package encryption + +import ( + "crypto/rc4" +) + +// QuazalRC4 encrypts data with RC4. Each iteration uses a new cipher instance. The key is always CD&ML +type QuazalRC4 struct { + key []byte + cipher *rc4.Cipher + decipher *rc4.Cipher + cipheredCount uint64 + decipheredCount uint64 +} + +// Key returns the crypto key +func (r *QuazalRC4) Key() []byte { + return r.key +} + +// SetKey sets the crypto key and updates the ciphers +func (r *QuazalRC4) SetKey(key []byte) error { + r.key = key + + cipher, err := rc4.NewCipher(key) + if err != nil { + return err + } + + decipher, err := rc4.NewCipher(key) + if err != nil { + return err + } + + r.cipher = cipher + r.decipher = decipher + + return nil +} + +// Encrypt encrypts the payload with the outgoing QuazalRC4 stream +func (r *QuazalRC4) Encrypt(payload []byte) ([]byte, error) { + r.SetKey([]byte("CD&ML")) + + ciphered := make([]byte, len(payload)) + + r.cipher.XORKeyStream(ciphered, payload) + + r.cipheredCount += uint64(len(payload)) + + return ciphered, nil +} + +// Decrypt decrypts the payload with the incoming QuazalRC4 stream +func (r *QuazalRC4) Decrypt(payload []byte) ([]byte, error) { + r.SetKey([]byte("CD&ML")) + + deciphered := make([]byte, len(payload)) + + r.decipher.XORKeyStream(deciphered, payload) + + r.decipheredCount += uint64(len(payload)) + + return deciphered, nil +} + +// Copy returns a copy of the algorithm while retaining it's state +func (r *QuazalRC4) Copy() Algorithm { + copied := NewQuazalRC4Encryption() + + copied.SetKey(r.key) + + // * crypto/rc4 does not expose a way to directly copy streams and retain their state. + // * This just discards the number of iterations done in the original ciphers to sync + // * the copied ciphers states to the original + for i := 0; i < int(r.cipheredCount); i++ { + copied.cipher.XORKeyStream([]byte{0}, []byte{0}) + } + + for i := 0; i < int(r.decipheredCount); i++ { + copied.decipher.XORKeyStream([]byte{0}, []byte{0}) + } + + copied.cipheredCount = r.cipheredCount + copied.decipheredCount = r.decipheredCount + + return copied +} + +// NewQuazalRC4Encryption returns a new instance of the QuazalRC4 encryption +func NewQuazalRC4Encryption() *QuazalRC4 { + return &QuazalRC4{ + key: make([]byte, 0), + } +} diff --git a/prudp_packet.go b/prudp_packet.go index a29fa41..535628a 100644 --- a/prudp_packet.go +++ b/prudp_packet.go @@ -148,14 +148,6 @@ func (p *PRUDPPacket) decryptPayload() []byte { if p.packetType == DataPacket { slidingWindow := p.sender.SlidingWindow(p.SubstreamID()) - // * According to other Quazal server implementations, - // * the RC4 stream is always reset to the default key - // * regardless if the client is connecting to a secure - // * server (prudps) or not - if p.version == 0 && p.sender.Endpoint.Server.PRUDPV0Settings.IsQuazalMode { - slidingWindow.SetCipherKey([]byte("CD&ML")) - } - payload, _ = slidingWindow.streamSettings.EncryptionAlgorithm.Decrypt(payload) } diff --git a/prudp_server.go b/prudp_server.go index 7cfa971..9efdc01 100644 --- a/prudp_server.go +++ b/prudp_server.go @@ -268,14 +268,6 @@ func (s *PRUDPServer) sendPacket(packet PRUDPPacketInterface) { logger.Error(err.Error()) } - // * According to other Quazal server implementations, - // * the RC4 stream is always reset to the default key - // * regardless if the client is connecting to a secure - // * server (prudps) or not - if packet.Version() == 0 && s.PRUDPV0Settings.IsQuazalMode { - slidingWindow.SetCipherKey([]byte("CD&ML")) - } - encryptedPayload, err := slidingWindow.streamSettings.EncryptionAlgorithm.Encrypt(compressedPayload) if err != nil { logger.Error(err.Error())