@@ -334,3 +334,101 @@ def sha256_encrypt(text) -> str:
334334 h = [(x + y ) & 0xFFFFFFFF for x , y in zip (h , [a , b , c , d , e , f , g , h0 ])]
335335
336336 return '' .join (f'{ value :08x} ' for value in h )
337+
338+ @staticmethod
339+ def yield_chacha20_xor_stream (key , iv , position = 0 ):
340+ """Generate the xor stream with the ChaCha20 cipher."""
341+ if not isinstance (position , int ):
342+ raise TypeError
343+ if position & ~ 0xFFFFFFFF :
344+ raise ValueError ("Position is not uint32." )
345+ if not isinstance (key , bytes ):
346+ raise TypeError
347+ if not isinstance (iv , bytes ):
348+ raise TypeError
349+ if len (key ) != 32 :
350+ raise ValueError
351+ if len (iv ) != 8 :
352+ raise ValueError
353+
354+ def rotate (v , c ):
355+ return ((v << c ) & 0xFFFFFFFF ) | v >> (32 - c )
356+
357+ def quarter_round (x , a , b , c , d ):
358+ x [a ] = (x [a ] + x [b ]) & 0xFFFFFFFF
359+ x [d ] = rotate (x [d ] ^ x [a ], 16 )
360+ x [c ] = (x [c ] + x [d ]) & 0xFFFFFFFF
361+ x [b ] = rotate (x [b ] ^ x [c ], 12 )
362+ x [a ] = (x [a ] + x [b ]) & 0xFFFFFFFF
363+ x [d ] = rotate (x [d ] ^ x [a ], 8 )
364+ x [c ] = (x [c ] + x [d ]) & 0xFFFFFFFF
365+ x [b ] = rotate (x [b ] ^ x [c ], 7 )
366+
367+ ctx = [0 ] * 16
368+ ctx [:4 ] = (1634760805 , 857760878 , 2036477234 , 1797285236 )
369+ ctx [4 :12 ] = struct .unpack ("<8L" , key )
370+ ctx [12 ] = ctx [13 ] = position
371+ ctx [14 :16 ] = struct .unpack ("<LL" , iv )
372+ while 1 :
373+ x = list (ctx )
374+ for i in range (10 ):
375+ quarter_round (x , 0 , 4 , 8 , 12 )
376+ quarter_round (x , 1 , 5 , 9 , 13 )
377+ quarter_round (x , 2 , 6 , 10 , 14 )
378+ quarter_round (x , 3 , 7 , 11 , 15 )
379+ quarter_round (x , 0 , 5 , 10 , 15 )
380+ quarter_round (x , 1 , 6 , 11 , 12 )
381+ quarter_round (x , 2 , 7 , 8 , 13 )
382+ quarter_round (x , 3 , 4 , 9 , 14 )
383+ for c in struct .pack (
384+ "<16L" , * ((x [i ] + ctx [i ]) & 0xFFFFFFFF for i in range (16 ))
385+ ):
386+ yield c
387+ ctx [12 ] = (ctx [12 ] + 1 ) & 0xFFFFFFFF
388+ if ctx [12 ] == 0 :
389+ ctx [13 ] = (ctx [13 ] + 1 ) & 0xFFFFFFFF
390+
391+ @staticmethod
392+ def chacha20 (data :bytes , key :bytes , nonce = None , position = 0 ):
393+ """
394+ Encrypt (or decrypt) data using the ChaCha20 stream cipher.
395+
396+ Parameters
397+ ==========
398+ data : bytes
399+ Input data to encrypt/decrypt
400+ key : bytes
401+ 32-byte encryption key. Shorter keys will be padded by repetition.
402+ nonce : bytes, optional
403+ 8-byte nonce
404+
405+ Returns
406+ =======
407+ bytes
408+ Processed output
409+
410+ Notes
411+ =====
412+ Chacha20 is symmetric - same operation encrypts and decrypts.
413+
414+ References
415+ ==========
416+
417+ .. [1] https://en.wikipedia.org/wiki/ChaCha20-Poly1305
418+ .. [2] https://github.com/pts/chacha20/blob/master/chacha20_python3.py
419+
420+ """
421+
422+ if nonce is None :
423+ nonce = b"\0 " * 8
424+
425+ if not key :
426+ raise ValueError ("Key is empty." )
427+ if len (key ) < 32 :
428+ key = (key * (32 // len (key ) + 1 ))[:32 ]
429+ if len (key ) > 32 :
430+ key = key [:32 ]
431+
432+ return bytes (
433+ a ^ b for a , b in zip (data , Crypto .yield_chacha20_xor_stream (key , nonce , position ))
434+ )
0 commit comments