veilid/scripts/earthly/secretsd/secretsd/crypto_backend.py
2022-01-09 15:17:36 -05:00

133 lines
4.7 KiB
Python

import os
__all__ = [
"AES_BLOCK_BYTES",
"aes_cbc_encrypt",
"aes_cbc_decrypt",
"aes_cfb8_encrypt",
"aes_cfb8_decrypt",
"dh_modp1024_exchange",
"hkdf_sha256_derive",
"pkcs7_pad",
"pkcs7_unpad",
]
# Second Oakley group (RFC 2409), to be used as "dh-ietf1024" in the 'Secret
# Service' API. Don't look at me. I didn't write the spec.
MODP1024_PRIME = int("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
"29024E088A67CC74020BBEA63B139B22514A08798E3404DD"
"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"
"E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED"
"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381"
"FFFFFFFFFFFFFFFF", 16)
MODP1024_GENERATOR = 2
backend = os.environ.get("CRYPTO_BACKEND", "cryptography")
if backend == "cryptodome":
from Crypto.Cipher import AES
from Crypto.Hash import SHA256
from Crypto.Protocol.KDF import HKDF
from Crypto.Random.random import randint
from Crypto.Util import Padding
AES_BLOCK_BYTES = AES.block_size
def aes_cbc_encrypt(data, key, iv):
return AES.new(key, AES.MODE_CBC, iv).encrypt(data)
def aes_cbc_decrypt(data, key, iv):
return AES.new(key, AES.MODE_CBC, iv).decrypt(data)
def aes_cfb8_encrypt(data, key, iv):
return AES.new(key, AES.MODE_CFB, iv, segment_size=8).encrypt(data)
def aes_cfb8_decrypt(data, key, iv):
return AES.new(key, AES.MODE_CFB, iv, segment_size=8).decrypt(data)
def aes_cfb128_encrypt(data, key, iv):
return AES.new(key, AES.MODE_CFB, iv, segment_size=128).encrypt(data)
def aes_cfb128_decrypt(data, key, iv):
return AES.new(key, AES.MODE_CFB, iv, segment_size=128).decrypt(data)
def dh_modp1024_exchange(peer_pubkey):
prime = MODP1024_PRIME
generator = MODP1024_GENERATOR
our_privkey = randint(1, prime-1)
our_pubkey = pow(generator, our_privkey, prime)
shared_key = pow(peer_pubkey, our_privkey, prime)
shared_key = shared_key.to_bytes(1024 // 8, "big")
return our_pubkey, shared_key
def hkdf_sha256_derive(input, nbytes):
return HKDF(input, nbytes, b"", hashmod=SHA256)
def pkcs7_pad(data, size):
return Padding.pad(data, size, style="pkcs7")
def pkcs7_unpad(data, size):
return Padding.unpad(data, size, style="pkcs7")
print("using 'PyCryptodome' as crypto backend")
elif backend == "cryptography":
from cryptography.hazmat.primitives.asymmetric import dh
from cryptography.hazmat.primitives.ciphers import Cipher
from cryptography.hazmat.primitives.ciphers.algorithms import AES
from cryptography.hazmat.primitives.ciphers.modes import CBC, CFB, CFB8
from cryptography.hazmat.primitives.hashes import SHA256
from cryptography.hazmat.primitives.kdf.hkdf import HKDF
from cryptography.hazmat.primitives.padding import PKCS7
AES_BLOCK_BYTES = AES.block_size // 8
def aes_cbc_encrypt(data, key, iv):
c = Cipher(AES(key), CBC(iv)).encryptor()
return c.update(data) + c.finalize()
def aes_cbc_decrypt(data, key, iv):
c = Cipher(AES(key), CBC(iv)).decryptor()
return c.update(data) + c.finalize()
def aes_cfb8_encrypt(data, key, iv):
c = Cipher(AES(key), CFB8(iv)).encryptor()
return c.update(data) + c.finalize()
def aes_cfb8_decrypt(data, key, iv):
c = Cipher(AES(key), CFB8(iv)).decryptor()
return c.update(data) + c.finalize()
def aes_cfb128_encrypt(data, key, iv):
c = Cipher(AES(key), CFB(iv)).encryptor()
return c.update(data) + c.finalize()
def aes_cfb128_decrypt(data, key, iv):
c = Cipher(AES(key), CFB(iv)).decryptor()
return c.update(data) + c.finalize()
def dh_modp1024_exchange(peer_pubkey):
group = dh.DHParameterNumbers(p=MODP1024_PRIME, g=MODP1024_GENERATOR)
peer_pubkey = dh.DHPublicNumbers(peer_pubkey, group).public_key()
our_privkey = group.parameters().generate_private_key()
shared_key = our_privkey.exchange(peer_pubkey)
our_pubkey = our_privkey.public_key().public_numbers().y
return our_pubkey, shared_key
def hkdf_sha256_derive(input, nbytes):
k = HKDF(algorithm=SHA256(), length=nbytes, salt=b"", info=b"")
return k.derive(input)
def pkcs7_pad(data, size):
p = PKCS7(size * 8).padder()
return p.update(data) + p.finalize()
def pkcs7_unpad(data, size):
p = PKCS7(size * 8).unpadder()
return p.update(data) + p.finalize()
print("using 'python-cryptography' as crypto backend")
else:
raise RuntimeError("unsupported crypto backend %r" % backend)