You've already forked matrix-react-sdk
							
							
				mirror of
				https://github.com/matrix-org/matrix-react-sdk.git
				synced 2025-11-03 00:33:22 +03:00 
			
		
		
		
	
		
			
				
	
	
		
			118 lines
		
	
	
		
			3.6 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			118 lines
		
	
	
		
			3.6 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
#!/usr/bin/env python
 | 
						|
 | 
						|
from __future__ import print_function
 | 
						|
 | 
						|
import base64
 | 
						|
import json
 | 
						|
import struct
 | 
						|
 | 
						|
from cryptography.hazmat import backends
 | 
						|
from cryptography.hazmat.primitives import ciphers, hashes, hmac
 | 
						|
from cryptography.hazmat.primitives.kdf import pbkdf2
 | 
						|
from cryptography.hazmat.primitives.ciphers import algorithms, modes
 | 
						|
 | 
						|
backend = backends.default_backend()
 | 
						|
 | 
						|
def parse_u128(s):
 | 
						|
    a, b = struct.unpack(">QQ", s)
 | 
						|
    return (a << 64) | b
 | 
						|
 | 
						|
def encrypt_ctr(key, iv, plaintext, counter_bits=64):
 | 
						|
    alg = algorithms.AES(key)
 | 
						|
 | 
						|
    # Some AES-CTR implementations treat some parts of the IV as a nonce (which
 | 
						|
    # remains constant throughought encryption), and some as a counter (which
 | 
						|
    # increments every block, ie 16 bytes, and wraps after a while).  Different
 | 
						|
    # implmententations use different amounts of the IV for each part.
 | 
						|
    #
 | 
						|
    # The python cryptography library uses the whole IV as a counter; to make
 | 
						|
    # it match other implementations with a given counter size, we manually
 | 
						|
    # implement wrapping the counter.
 | 
						|
 | 
						|
    # number of AES blocks between each counter wrap
 | 
						|
    limit = 1 << counter_bits
 | 
						|
 | 
						|
    # parse IV as a 128-bit int
 | 
						|
    parsed_iv = parse_u128(iv)
 | 
						|
 | 
						|
    # split IV into counter and nonce
 | 
						|
    counter = parsed_iv & (limit - 1)
 | 
						|
    nonce = parsed_iv & ~(limit - 1)
 | 
						|
 | 
						|
    # encrypt up to the first counter wraparound
 | 
						|
    size = 16 * (limit - counter)
 | 
						|
    encryptor = ciphers.Cipher(
 | 
						|
        alg,
 | 
						|
        modes.CTR(iv),
 | 
						|
        backend=backend
 | 
						|
    ).encryptor()
 | 
						|
    input = plaintext[:size]
 | 
						|
    result = encryptor.update(input) + encryptor.finalize()
 | 
						|
    offset = size
 | 
						|
 | 
						|
    # do remaining data starting with a counter of zero
 | 
						|
    iv = struct.pack(">QQ", nonce >> 64, nonce & ((1 << 64) - 1))
 | 
						|
    size = 16 * limit
 | 
						|
 | 
						|
    while offset < len(plaintext):
 | 
						|
        encryptor = ciphers.Cipher(
 | 
						|
            alg,
 | 
						|
            modes.CTR(iv),
 | 
						|
            backend=backend
 | 
						|
        ).encryptor()
 | 
						|
        input = plaintext[offset:offset+size]
 | 
						|
        result += encryptor.update(input) + encryptor.finalize()
 | 
						|
        offset += size
 | 
						|
 | 
						|
    return result
 | 
						|
 | 
						|
def hmac_sha256(key, message):
 | 
						|
     h = hmac.HMAC(key, hashes.SHA256(), backend=backend)
 | 
						|
     h.update(message)
 | 
						|
     return h.finalize()
 | 
						|
 | 
						|
def encrypt(key, iv, salt, plaintext, iterations=1000):
 | 
						|
    """
 | 
						|
    Returns:
 | 
						|
       (bytes) ciphertext
 | 
						|
    """
 | 
						|
    if len(salt) != 16:
 | 
						|
        raise Exception("Expected 128 bits of salt - got %i bits" % len((salt) * 8))
 | 
						|
    if len(iv) != 16:
 | 
						|
        raise Exception("Expected 128 bits of IV - got %i bits" % (len(iv) * 8))
 | 
						|
 | 
						|
    sha = hashes.SHA512()
 | 
						|
    kdf = pbkdf2.PBKDF2HMAC(sha, 64, salt, iterations, backend)
 | 
						|
    k = kdf.derive(key)
 | 
						|
 | 
						|
    aes_key = k[0:32]
 | 
						|
    sha_key = k[32:]
 | 
						|
 | 
						|
    packed_file = (
 | 
						|
        b"\x01"     # version
 | 
						|
        + salt
 | 
						|
        + iv
 | 
						|
        + struct.pack(">L", iterations)
 | 
						|
        + encrypt_ctr(aes_key, iv, plaintext)
 | 
						|
    )
 | 
						|
    packed_file += hmac_sha256(sha_key, packed_file)
 | 
						|
 | 
						|
    return (
 | 
						|
        b"-----BEGIN MEGOLM SESSION DATA-----\n" +
 | 
						|
        base64.encodestring(packed_file) +
 | 
						|
        b"-----END MEGOLM SESSION DATA-----"
 | 
						|
    )
 | 
						|
 | 
						|
def gen(password, iv, salt, plaintext, iterations=1000):
 | 
						|
    ciphertext = encrypt(
 | 
						|
        password.encode('utf-8'), iv, salt, plaintext.encode('utf-8'), iterations
 | 
						|
    )
 | 
						|
    return (plaintext, password, ciphertext.decode('utf-8'))
 | 
						|
 | 
						|
print (json.dumps([
 | 
						|
    gen("password", b"\x88"*16, b"saltsaltsaltsalt", "plain", 10),
 | 
						|
    gen("betterpassword", b"\xFF"*8 + b"\x00"*8, b"moresaltmoresalt", "Hello, World"),
 | 
						|
    gen("SWORDFISH", b"\xFF"*8 + b"\x00"*8, b"yessaltygoodness", "alphanumerically" * 4),
 | 
						|
    gen("password"*32, b"\xFF"*16, b"\xFF"*16, "alphanumerically" * 4),
 | 
						|
], indent=4))
 |