Salsa20 and ChaCha20 were designed by Daniel J. Bernstein and are stream ciphers [here]. They have been benchmarked to be more than three times faster the AES. Google have defined ChaCha20 as a standard for stream encryption [RFC 7539] and is included in TLS standards [RFC 7505]. It uses a 256-bit key, a 32-bit counter, and a 96-bit nonce, with 10 rounds of permutations.
Salsa20 and Chacha20 stream ciphersTheoryChaCha20 and Salsa take a 256-bit key (or a 128-bit version) and a 32-bit nonce This creates a key stream, which is then XORed with the plaintext stream. In software, it is more than three times faster than AES, and is well suited to lower-powered devices and in real-time communications. ChaCha operates on 32-bit bits with a key of 256 bits (\(K = (k_0,k_1,k_2,k_3,k_4,k_5,k_6,k_7)\). This output blocks of 512-bits for the key stream (\(Z\), and which is EX-ORed with the plaintext stream. The state of the encryption is stored with 16 32-bit word values within a 4x4 matrix: \(\begin{bmatrix} x_{0} & x_{1} & x_{2} & x_{3} \\ x_{4} & x_{5} & x_{6} & x_{7} \\ x_{8} & x_{9} & x_{10} & x_{11} \\ x_{12} & x_{13} & x_{14} & x_{15} \\ \end{bmatrix}\) The initial state contains 16 32-bit values with constant values (0x61707865, 0x3320646e, 0x79622d32, 0x6b206574) the key (\(k_0,k_1,k_2,k_3,k_4,k_5,k_6,k_7\)), the counter (\(c_0\)) and the nonce (\(n_0, n_1, n_2, n_3\)): \(\begin{bmatrix} 0x61707865 & 0x3320646e & 0x79622d32 & 0x6b206574 \\ k_{0} & k_{1} & k_{2} & k_{3} \\ k_{4} & k_{5} & k_{6} & k_{7} \\ c_{0} & n_{0} & n_{1} & n_{2} \\ \end{bmatrix}\) The counter thus has 32-bits (1 x 32 bits), and the nonce has 96-bits (3 x 32 bits). ChaCha then defines a quarter round as: QR(a,b,c,d) and where this is defined as: a = a + b There are then 20 rounds (10 for column rounds and 10 for diagonal rounds): X is created with K, c and n y ← X for i ← 0 to 9 do /* Column Round */ (x0, x4, x8, x12) ← QR(x0, x4, x8, x12) (x5, x9, x13, x1) ← QR(x5, x9, x13, x1) (x10, x14, x2, x6) ← QR(x10, x14, x2, x6) (x15, x3, x7, x11) ← QR(x15, x3, x7, x11) /* Diagonal Round */ (x0, x5, x10, x15) ← QR(x0, x5, x10, x15) (x1, x6, x11, x12) ← QR(x1, x6, x11, x12) (x2, x7, x8, x13) ← QR(x2, x7, x8, x13) (x3, x4, x9, x14) ← QR(x3, x4, x9, x14) end for Z ← X + y Z is the resultant key stream. CodingIn the following we take a SHA-256 hash of the key string and generate the 256-bit key: # https://asecuritysite.com/encryption/salsa20 from Crypto.Cipher import Salsa20 import hashlib from base64 import b64encode from Crypto.Cipher import ChaCha20 plaintext = b'Attack at dawn' key= b'Test' print("Plain key:\t",plaintext) print("Secret key:\t",key) secret = hashlib.sha256() secret.update(key) print("Key used:\t",b64encode(secret.digest())) cipher = Salsa20.new(key=secret.digest()) enc=cipher.encrypt(plaintext) ct = b64encode(enc).decode('utf-8') nonce = b64encode(cipher.nonce).decode('utf-8') print("\n---Salsa20 Encrypt") print(" Nonce:",nonce) print(" Cipher:",ct) print("\n---Salsa20 Decrypt") cipher = Salsa20.new(key=secret.digest(), nonce=cipher.nonce) plaintext = cipher.decrypt(enc) print(" Decrypted:\t",plaintext) cipher = ChaCha20.new(key=secret.digest()) ciphertext = cipher.encrypt(plaintext) nonce = b64encode(cipher.nonce).decode('utf-8') ct = b64encode(ciphertext).decode('utf-8') print("\n---ChaCha20 Encrypt") print(" Nonce:",nonce) print(" Cipher:",ct) print("\n---ChaCha20 Decrypt") cipher = ChaCha20.new(key=secret.digest(), nonce=cipher.nonce) plaintext = cipher.decrypt(ciphertext) print(" Decrypted:\t",plaintext) Google have defined ChaCha20 as a standard for stream encryption [RFC 7539], for IPSec/IKE [RFC 7634] and for TLS standards [RFC 7905]: TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 = {0xCC, 0xA8} TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 = {0xCC, 0xA9} TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 = {0xCC, 0xAA} TLS_PSK_WITH_CHACHA20_POLY1305_SHA256 = {0xCC, 0xAB} TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256 = {0xCC, 0xAC} TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256 = {0xCC, 0xAD} TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256 = {0xCC, 0xAE} |