@@ -334,3 +334,101 @@ def sha256_encrypt(text) -> str:
334
334
h = [(x + y ) & 0xFFFFFFFF for x , y in zip (h , [a , b , c , d , e , f , g , h0 ])]
335
335
336
336
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