Giter Site home page Giter Site logo

Comments (7)

sirex avatar sirex commented on August 16, 2024

The task is to read more about crypthogrphy.io.

from internet-voting.

strazdas avatar strazdas commented on August 16, 2024

RSA can only encrypt data blocks that are shorter than the key length.

Usually, RSA is only used to transfer a symmetric key (at the start of the stream for example) and then the bulk data is encrypted with that key.

Asymmetric encryption isn't efficient enough to transfer a lot of data.
http://security.stackexchange.com/a/33445

from internet-voting.

sirex avatar sirex commented on August 16, 2024

Padding is a way to take data that may or may not be a multiple of the block size for a cipher and extend it out so that it is. This is required for many block cipher modes as they require the data to be encrypted to be an exact multiple of the block size.

https://cryptography.io/en/latest/hazmat/primitives/padding/

from internet-voting.

sirex avatar sirex commented on August 16, 2024

Very well explained what is RSA: http://unix.stackexchange.com/a/12265

It looks like we have to use RSA just to encrypt asymmetric key, and the whole message has to be encrypted using that asymmetric key. See https://cryptography.io/en/latest/fernet/

from internet-voting.

sirex avatar sirex commented on August 16, 2024

I just read more about PKI and yes, asymmetric algorithms usually are used only for symmetric key exchange. Then, rest of the encryption is done by symmetric algorithms using that exchanged key.

So your task will bet to encrypt message multiple time with list of given symmetric keys, using Fernet protocol. And the key exchange protocol is a different task.

from internet-voting.

sirex avatar sirex commented on August 16, 2024

After thinking more about this, I figured out, that you still need public keys. So the API stays the same:

envelope = create_envelope(nodes, content)

Here nodes is a list of tuples, each tuple has mix-net node id (an int) and public key instance.

When creating multi-layered envelope, for each layer you need a JSON list with two elements:

  1. Fernet key encoded with node's public key.
  2. The message encoded with Fernet key. Message should be JSON list with two elements:
    1. The inner envelope.
    2. Next node Id (int). Node Id of first layer should be None.

The envelope could look something like this (pseudocode):

envelope = json([
    json([
        node[1].public_key.encrypt(node[0].fernet_key),
        node[1].fernet_key.encrypt(json([
            json([
                node[0].public_key.encrypt(node[0].fernet_key),
                node[0].fernet_key.encrypt(message),
            ]),
            node[0].id,

        ]))
    ]),
    node[1].id,
])

Here node[0] would be CEC.

For decoding envelope this API can be used:

content, next_node_id = decrypt_envelope(primary_key, envelope)

Here is what it should do:

  1. Deserialize envelope using json.loads.
  2. Decrypt first element, using primary_key to get Fernet key.
  3. Decrypt second element using decrypted Fernet key.
  4. Deserialize JSON to get content (inner envelope) and next_node_id (address).

For the serialization, I'm not sure if JSON is a good choice, since we will be dealing with binary data. Maybe we should use MessagePack instead of JSON?

from internet-voting.

sirex avatar sirex commented on August 16, 2024

Today decided to look into crypto things, and assembled set of functions, that should cover most of crypto needs. It would be good, if you wood put into some module and use these functions for your task.

import os
import json
import base64
import hashlib
import binascii

from cryptography.fernet import Fernet
from cryptography.exceptions import InvalidSignature
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives.asymmetric import padding as asymmetric_padding


def generate(size: int=4096):
    return rsa.generate_private_key(
        public_exponent=65537,
        key_size=size,
        backend=default_backend(),
    )


def dump_base64(data: bytes) -> str:
    return base64.encodebytes(data).replace(b'\n', b'').decode('ascii')


def load_base64(data: str) -> bytes:
    return base64.decodebytes(data.encode('ascii'))


def dump_private_key(private_key, password: str=None) -> str:
    if password is None:
        encryption_algorithm = serialization.NoEncryption()
    else:
        encryption_algorithm = serialization.BestAvailableEncryption(password.encode('utf-8'))

    private_key_bytes = private_key.private_bytes(
        encoding=serialization.Encoding.DER,
        format=serialization.PrivateFormat.PKCS8,
        encryption_algorithm=encryption_algorithm,
    )

    return dump_base64(private_key_bytes)


def load_private_key(private_key_data: str, password: str=None):
    return serialization.load_der_private_key(
        load_base64(private_key_data),
        password=password.encode('utf-8'),
        backend=default_backend()
    )


def dump_public_key(public_key) -> str:
    public_key_bytes = public_key.public_bytes(
        encoding=serialization.Encoding.DER,
        format=serialization.PublicFormat.SubjectPublicKeyInfo,
    )
    return dump_base64(public_key_bytes)


def load_public_key(public_key_data: str):
    return serialization.load_der_public_key(
        load_base64(public_key_data),
        backend=default_backend(),
    )


def public_key_fingerprint(public_key) -> str:
    public_key_bytes = public_key.public_bytes(
        encoding=serialization.Encoding.DER,
        format=serialization.PublicFormat.SubjectPublicKeyInfo,
    )
    return hashlib.sha1(public_key_bytes).hexdigest()


def encrypt(public_key, message: str) -> (str, str):
    key = Fernet.generate_key()
    fernet = Fernet(key)
    cipher = fernet.encrypt(message.encode('utf-8'))
    key = public_key.encrypt(key, asymmetric_padding.OAEP(
        mgf=asymmetric_padding.MGF1(algorithm=hashes.SHA1()),
        algorithm=hashes.SHA1(),
        label=None,
    ))
    return dump_base64(key), cipher.decode('ascii')


def decrypt(private_key, key: str, cipher: str) -> str:
    key = private_key.decrypt(load_base64(key), asymmetric_padding.OAEP(
        mgf=asymmetric_padding.MGF1(algorithm=hashes.SHA1()),
        algorithm=hashes.SHA1(),
        label=None,
    ))
    fernet = Fernet(key)
    message = fernet.decrypt(cipher.encode('ascii'))
    return message.decode('utf-8')


def sign(private_key, message: str) -> str:
    signer = private_key.signer(
        asymmetric_padding.PSS(
            mgf=asymmetric_padding.MGF1(hashes.SHA256()),
            salt_length=asymmetric_padding.PSS.MAX_LENGTH
        ),
        hashes.SHA256()
    )
    signer.update(message.encode('utf-8'))
    return dump_base64(signer.finalize())


def verify(public_key, message: str, signature: str) -> bool:
    verifier = public_key.verifier(
        load_base64(signature),
        asymmetric_padding.PSS(
            mgf=asymmetric_padding.MGF1(hashes.SHA256()),
            salt_length=asymmetric_padding.PSS.MAX_LENGTH
        ),
        hashes.SHA256()
    )
    verifier.update(message.encode('utf-8'))

    try:
        verifier.verify()
    except InvalidSignature:
        return False
    else:
        return True


def get_random_bytes(n: int) -> bytes:
    return os.urandom(n)


def apply_hash_function(message: str, salt: bytes) -> str:
    hash_bytes = hashlib.pbkdf2_hmac('sha1', message.encode('utf-8'), salt, 1000000)
    return binascii.hexlify(hash_bytes).decode('utf-8')


def password_to_fernet_key(password: str, salt: bytes) -> bytes:
    key = hashlib.pbkdf2_hmac('sha256', password.encode('utf-8'), salt, 1000000)
    return base64.urlsafe_b64encode(key)


def fernet_encrypt(password: str, salt: bytes, data: str) -> bytes:
    fernet = Fernet(password_to_fernet_key(password, salt))
    return base64.urlsafe_b64decode(fernet.encrypt(data.encode('utf-8')).decode('ascii'))


def fernet_decrypt(password: str, salt: bytes, cipher: str) -> str:
    fernet = Fernet(password_to_fernet_key(password, salt))
    return fernet.decrypt(base64.urlsafe_b64encode(cipher)).decode('utf-8')


def dump_sensitive_data(password: str, data) -> bytes:
    salt = get_random_bytes(32)
    cipher = fernet_encrypt(password, salt, json.dumps(data))
    return salt + cipher


def load_sensitive_data(password: str, cipher: bytes):
    return json.loads(fernet_decrypt(password, cipher[:32], cipher[32:]))

from internet-voting.

Related Issues (10)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.