Пример #1
0
def test_x448_unsupported(backend):
    with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM):
        X448PublicKey.from_public_bytes(b"0" * 56)

    with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM):
        X448PrivateKey.from_private_bytes(b"0" * 56)

    with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM):
        X448PrivateKey.generate()
Пример #2
0
def test_x448_unsupported(backend):
    with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM):
        X448PublicKey.from_public_bytes(b"0" * 56)

    with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM):
        X448PrivateKey.from_private_bytes(b"0" * 56)

    with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM):
        X448PrivateKey.generate()
Пример #3
0
    def generate_key(
            algorithm: CoseAlgorithms,
            key_ops: KeyOps,
            curve_type: CoseEllipticCurves = CoseEllipticCurves.X25519
    ) -> 'OKP':
        """
        Generate a random OKP COSE key object.

        :param algorithm: Specify the CoseAlgorithm to use.
        :param key_ops: Specify the key operation (KeyOps).
        :param curve_type: Specify curve, must be X25519 or X448.
        """

        if curve_type == CoseEllipticCurves.X25519:
            private_key = X25519PrivateKey.generate()
        elif curve_type == CoseEllipticCurves.X448:
            private_key = X448PrivateKey.generate()
        else:
            raise CoseIllegalCurve(
                f"curve must be of type {CoseEllipticCurves.X25519} or {CoseEllipticCurves.X448}"
            )

        encoding = Encoding(serialization.Encoding.Raw)
        private_format = PrivateFormat(serialization.PrivateFormat.Raw)
        public_format = PublicFormat(serialization.PublicFormat.Raw)
        encryption = serialization.NoEncryption()

        return OKP(alg=CoseAlgorithms(algorithm),
                   key_ops=KeyOps(key_ops),
                   crv=CoseEllipticCurves(curve_type),
                   x=private_key.public_key().public_bytes(
                       encoding, public_format),
                   d=private_key.private_bytes(encoding, private_format,
                                               encryption))
Пример #4
0
 def pack_x448(self) -> bytes:
     private_key: X448PrivateKey = X448PrivateKey.generate()
     public_key: X448PublicKey = private_key.public_key()
     self.private = private_key
     self.public = public_key.public_bytes(encoding=serialization.Encoding.Raw,
                                           format=serialization.PublicFormat.Raw)
     return self.public
Пример #5
0
 def test_round_trip_private_serialization(
     self, encoding, fmt, encryption, passwd, load_func, backend
 ):
     key = X448PrivateKey.generate()
     serialized = key.private_bytes(encoding, fmt, encryption)
     loaded_key = load_func(serialized, passwd, backend)
     assert isinstance(loaded_key, X448PrivateKey)
Пример #6
0
 def test_incorrect_public_key_length_raises_critical_error(self):
     sk = X448PrivateKey.generate()
     for key in [
             key_len * b'a' for key_len in range(1, 100)
             if key_len != TFC_PUBLIC_KEY_LENGTH
     ]:
         with self.assertRaises(SystemExit):
             X448.shared_key(sk, key)
Пример #7
0
 def __init__(self, dh_pair=None):
     if dh_pair:
         if not isinstance(dh_pair, X448PrivateKey):
             raise TypeError("dh_pair must be of type: X448PrivateKey")
         self._private_key = dh_pair
     else:
         self._private_key = X448PrivateKey.generate()
     self._public_key = self._private_key.public_key()
Пример #8
0
    def test_invalid_public_bytes(self, backend):
        key = X448PrivateKey.generate().public_key()
        with pytest.raises(ValueError):
            key.public_bytes(serialization.Encoding.Raw,
                             serialization.PublicFormat.SubjectPublicKeyInfo)

        with pytest.raises(ValueError):
            key.public_bytes(serialization.Encoding.PEM,
                             serialization.PublicFormat.PKCS1)

        with pytest.raises(ValueError):
            key.public_bytes(serialization.Encoding.PEM,
                             serialization.PublicFormat.Raw)
Пример #9
0
    def test_invalid_private_bytes(self, backend):
        key = X448PrivateKey.generate()
        with pytest.raises(ValueError):
            key.private_bytes(serialization.Encoding.Raw,
                              serialization.PrivateFormat.Raw, None)

        with pytest.raises(ValueError):
            key.private_bytes(serialization.Encoding.Raw,
                              serialization.PrivateFormat.PKCS8, None)

        with pytest.raises(ValueError):
            key.private_bytes(serialization.Encoding.PEM,
                              serialization.PrivateFormat.Raw,
                              serialization.NoEncryption())
Пример #10
0
    def derive_key(
        self,
        context: Union[List[Any], Dict[str, Any]],
        material: bytes = b"",
        public_key: Optional[COSEKeyInterface] = None,
    ) -> COSEKeyInterface:

        if self._public_key:
            raise ValueError("Public key cannot be used for key derivation.")
        if not public_key:
            raise ValueError("public_key should be set.")
        if not isinstance(public_key.key, X25519PublicKey) and not isinstance(
                public_key.key, X448PublicKey):
            raise ValueError("public_key should be x25519/x448 public key.")
        # if self._alg not in COSE_ALGORITHMS_CKDM_KEY_AGREEMENT.values():
        #     raise ValueError(f"Invalid alg for key derivation: {self._alg}.")

        # Validate context information.
        if isinstance(context, dict):
            context = to_cis(context, self._alg)
        else:
            self._validate_context(context)

        # Derive key.
        if self._private_key:
            self._key = self._private_key
        else:
            self._key = X25519PrivateKey.generate(
            ) if self._crv == 4 else X448PrivateKey.generate()
        shared_key = self._key.exchange(public_key.key)
        hkdf = HKDF(
            algorithm=self._hash_alg(),
            length=COSE_KEY_LEN[context[0]] // 8,
            salt=None,
            info=self._dumps(context),
        )
        cose_key = {
            1: 4,
            3: context[0],
            -1: hkdf.derive(shared_key),
        }
        if cose_key[3] in [1, 2, 3]:
            return AESGCMKey(cose_key)
        if cose_key[3] in [4, 5, 6, 7]:
            return HMACKey(cose_key)
        if cose_key[3] in [10, 11, 12, 13, 30, 31, 32, 33]:
            return AESCCMKey(cose_key)
        # cose_key[3] == 24:
        return ChaCha20Key(cose_key)
 def generate_x_curve_keys(x_curve_type):
     if x_curve_type == "x25519":
         private_key = X25519PrivateKey.generate()
     elif x_curve_type == "x448":
         private_key = X448PrivateKey.generate()
     else:
         raise Exception("Unknown x curve type {}".format(x_curve_type))
     public_key = private_key.public_key()
     private_key_bytes = private_key.private_bytes(
         encoding=Encoding.Raw,
         format=PrivateFormat.Raw,
         encryption_algorithm=NoEncryption(),
     )
     public_key_bytes = public_key.public_bytes(encoding=Encoding.Raw,
                                                format=PublicFormat.Raw)
     return private_key_bytes, public_key_bytes, public_key_bytes
Пример #12
0
    def generate_private_key() -> 'X448PrivateKey':
        """Generate the X448 private key.

        The pyca/cryptography's key generation process is as follows:

        1. When `X448PrivateKey.generate()` is called by this method,
           the `generate()` class method imports the OpenSSL backend[1].

        2. Importing the backend causes Python to call the Backend
           class[2] that runs the `__init__()` method of the class[3],
           which then calls the `activate_osrandom_engine()` instance
           method[4].

        3. Calling the `activate_osrandom_engine()` disables the default
           OpenSSL CSPRNG, and activates the pyca/cryptography
           "OS random engine".[5]

        4. Unlike OpenSSL user-space CSPRNG that only seeds from
           /dev/urandom, the OS random engine uses GETRANDOM(0) syscall
           that sources all of its entropy directly from the ChaCha20
           DRNG. The OS random engine does not suffer from the fork()
           weakness where forked process is not automatically reseeded,
           and it's also safe from issues with OpenSSL CSPRNG
           initialization.[6]

        5. The fallback option (/dev/urandom) of OS random engine might
           be problematic on pre-3.17 kernels if the CSPRNG has not been
           properly seeded. However, TFC checks that the kernel version
           of the OS it's running on is at least 4.17. This means that
           the used source of entropy is always GETRANDOM(0).[7] This
           can be verified from the source code as well: The last
           parameter `0` of the GETRANDOM syscall[8] indicates
           GRND_NONBLOCK flag is not set. This means the ChaCha20 DRNG
           is used, and that it does not yield entropy until it has been
           fully seeded. This is the same case as with TFC's `csprng()`
           function.

         [1] https://github.com/pyca/cryptography/blob/2.7/src/cryptography/hazmat/primitives/asymmetric/x448.py#L38
         [2] https://github.com/pyca/cryptography/blob/2.7/src/cryptography/hazmat/backends/openssl/backend.py#L2445
         [3] https://github.com/pyca/cryptography/blob/2.7/src/cryptography/hazmat/backends/openssl/backend.py#L115
         [4] https://github.com/pyca/cryptography/blob/2.7/src/cryptography/hazmat/backends/openssl/backend.py#L122
         [5] https://cryptography.io/en/latest/hazmat/backends/openssl/#activate_osrandom_engine
         [6] https://cryptography.io/en/latest/hazmat/backends/openssl/#os-random-engine
         [7] https://cryptography.io/en/latest/hazmat/backends/openssl/#os-random-sources
         [8] https://github.com/pyca/cryptography/blob/master/src/_cffi_src/openssl/src/osrandom_engine.c#L391
        """
        return X448PrivateKey.generate()
Пример #13
0
    def test_invalid_public_bytes(self, backend):
        key = X448PrivateKey.generate().public_key()
        with pytest.raises(ValueError):
            key.public_bytes(
                serialization.Encoding.Raw,
                serialization.PublicFormat.SubjectPublicKeyInfo
            )

        with pytest.raises(ValueError):
            key.public_bytes(
                serialization.Encoding.PEM,
                serialization.PublicFormat.PKCS1
            )

        with pytest.raises(ValueError):
            key.public_bytes(
                serialization.Encoding.PEM,
                serialization.PublicFormat.Raw
            )
Пример #14
0
    def test_invalid_private_bytes(self, backend):
        key = X448PrivateKey.generate()
        with pytest.raises(ValueError):
            key.private_bytes(
                serialization.Encoding.Raw,
                serialization.PrivateFormat.Raw,
                None
            )

        with pytest.raises(ValueError):
            key.private_bytes(
                serialization.Encoding.Raw,
                serialization.PrivateFormat.PKCS8,
                None
            )

        with pytest.raises(ValueError):
            key.private_bytes(
                serialization.Encoding.PEM,
                serialization.PrivateFormat.Raw,
                serialization.NoEncryption()
            )
Пример #15
0
    def generate_key(crv: Union[Type['CoseCurve'], str, int],
                     optional_params: dict = None) -> 'OKPKey':
        """
        Generate a random OKPKey COSE key object.

        :param crv: Specify an elliptic curve.
        :param optional_params: Optional key attributes for the :class:`~cose.keys.okp.OKPKey` object, e.g., \
        :class:`~cose.keys.keyparam.KpAlg` or  :class:`~cose.keys.keyparam.KpKid`.

        :returns: A COSE `OKPKey` key.
        """

        if type(crv) == str or type(crv) == int:
            crv = CoseCurve.from_id(crv)

        if crv == X25519:
            private_key = X25519PrivateKey.generate()
        elif crv == Ed25519:
            private_key = Ed25519PrivateKey.generate()
        elif crv == Ed448:
            private_key = Ed448PrivateKey.generate()
        elif crv == X448:
            private_key = X448PrivateKey.generate()
        else:
            raise CoseIllegalCurve(
                f"Curve must be of type {X25519}, {X448}, {Ed25519}, or {Ed448}"
            )

        encoding = Encoding(serialization.Encoding.Raw)
        private_format = PrivateFormat(serialization.PrivateFormat.Raw)
        public_format = PublicFormat(serialization.PublicFormat.Raw)
        encryption = serialization.NoEncryption()

        return OKPKey(crv=crv,
                      x=private_key.public_key().public_bytes(
                          encoding, public_format),
                      d=private_key.private_bytes(encoding, private_format,
                                                  encryption),
                      optional_params=optional_params)
Пример #16
0
 def test_round_trip_private_serialization(self, encoding, fmt, encryption,
                                           passwd, load_func, backend):
     key = X448PrivateKey.generate()
     serialized = key.private_bytes(encoding, fmt, encryption)
     loaded_key = load_func(serialized, passwd, backend)
     assert isinstance(loaded_key, X448PrivateKey)
Пример #17
0
 def test_invalid_type_exchange(self, backend):
     key = X448PrivateKey.generate()
     with pytest.raises(TypeError):
         key.exchange(object())  # type: ignore[arg-type]
Пример #18
0
 def test_generate(self, backend):
     key = X448PrivateKey.generate()
     assert key
     assert key.public_key()
Пример #19
0
 def test_invalid_type_exchange(self, backend):
     key = X448PrivateKey.generate()
     with pytest.raises(TypeError):
         key.exchange(object())
Пример #20
0
def data():
    mensaje= b"89392679556575597635196565386081702718512260749406173579320076828061414488077821523722981888265393212848183094"

    #X25519 bench
    start_time= time()
    private_key = X25519PrivateKey.generate()
    peer_public_key = X25519PrivateKey.generate().public_key()
    k255=time()-start_time
    #print("Generación llave X25519 en: " + str(k255) + " sec con tamaño: "+ str(sys.getsizeof(private_key))+" bytes")
    private_key=0

    #25519 sign
    start_time= time()
    private_key = Ed25519PrivateKey.generate()
    signature = private_key.sign(mensaje)
    s255=time()-start_time
    #print("Generación firma X25519 en: " + str(s255) + " sec")

    start_time= time()
    public_key = private_key.public_key()
    public_key.verify(signature, mensaje)
    ver255=time()-start_time
    #print("Verificación firma X25519 en: " + str(ver255) + " sec")
    private_key=0
    #448 Key Exchange
    start_time=time()
    private_key = X448PrivateKey.generate()
    peer_public_key = X448PrivateKey.generate().public_key()
    k448=time()-start_time
    #print("Generación 448 llave en: " + str(k448) + " sec con tamaño: "+ str(sys.getsizeof(private_key))+" bytes")
    private_key=0
    #448 sign

    private_key = Ed448PrivateKey.generate()
    start_time= time()
    signature = private_key.sign(mensaje)
    s448=time()-start_time
    #print("Generación 448 firma en: " + str(s448) + " sec")
    start_time=time()
    public_key = private_key.public_key()
    public_key.verify(signature, mensaje)
    ver448=time()-start_time
    #print("Verificación 448 firma en: " + str(ver448) + " sec")
    private_key=0

    #RSA GENERATION
    start_time=time()
    private_key = rsa.generate_private_key(
        public_exponent=65537,
        key_size=2048,
    )
    krsa=time()-start_time
    #print("Generación RSA llave en: " + str(krsa) + " sec con tamaño: "+ str(sys.getsizeof(private_key))+" bytes")

    #RSA SINGING
    start_time=time()
    signature = private_key.sign(
        mensaje,
        padding.PSS(
            mgf=padding.MGF1(hashes.SHA256()),
            salt_length=padding.PSS.MAX_LENGTH
        ),
        hashes.SHA256()
    )
    srsa=time()-start_time
    #print("Generación RSA firma en: " + str(srsa) + " sec")
    private_key=0
    #DSA SINGING
    start_time=time()
    private_key = dsa.generate_private_key(
        key_size=1024,
    )
    kdsa=time()-start_time
    #print("Generación DSA llave en: " + str(kdsa) + " sec con tamaño: "+ str(sys.getsizeof(private_key))+" bytes")
    start_time =time()
    signature = private_key.sign(
        mensaje,
        hashes.SHA256()
    )
    sdsa=time()-start_time
    #print("Generación DSA firma en: " + str(sdsa) + " sec")


    return [k255,s255,ver255,k448,s448,ver448,krsa,srsa,kdsa,sdsa]
Пример #21
0
 def generate_dh(cls):
     return cls(X448PrivateKey.generate())
Пример #22
0
Файл: crypto.py Проект: gtog/tfc
    def generate_private_key() -> 'X448PrivateKey':
        """Generate the X448 private key.

        The size of the private key is 56 bytes (448 bits).
        """
        return X448PrivateKey.generate()
Пример #23
0
def main() -> None:
    """Load persistent settings and launch the Relay Program.

    This function loads settings from the settings database and launches
    processes for the Relay Program. It then monitors the EXIT_QUEUE for
    EXIT/WIPE signals and each process in case one of them dies.

    If you're reading this code to get the big picture on how TFC works,
    start by looking at `tfc.py` for Transmitter Program functionality.
    After you have reviewed the Transmitter Program's code, revisit the
    code of this program.

    The Relay Program operates multiple processes to enable real time IO
    between multiple data sources and destinations.

    Symbols:
        process_name    denotes the name of the process

        ─>, <─, ↑, ↓    denotes the direction of data passed from one
                        process to another

        (Description)   denotes the description of data passed from one
                        process to another

        ┈, ┊            denotes the link between a description and path
                        of data matching the description

        ▶|, |◀          denotes the gateways where the direction of data
                        flow is enforced with hardware data diodes


                                         Relay Program (Networked Computer)
                    ┏━━  ━━  ━━  ━━  ━━  ━━  ━━  ━━  ━━  ━━  ━━  ━━  ━━  ━━  ━━  ━━  ━━  ━━ ━━┓
                    ┃                                                                         ┃
                                       (Contact management commands)
                    ┃  ┌─────────────────────────────┬─────────────────────┐                  ┃
                       |                             |                     ↓
                    ┃  |                    ┌─────> relay_command   ┌───> c_req_manager       ┃
                       |                    │                  │    |
                    ┃  |                    │   (Onion Service┈│    |┈(Contact requests)      ┃
                       |                    │     private key) │    |
                    ┃  |                    │                  ↓    |                         ┃
                       |                    │            onion_service ───────────────────────────> client on contact's
                    ┃  |     (Relay Program┈│               ↑                ┊                ┃     Networked Computer
                       |          commands) │               │┈(Outgoing msg/file/public key)
                    ┃  |                    │               │                                 ┃
      Source ───▶|─────(── gateway_loop ─> src_incoming ─> flask_server <─┐
    Computer        ┃  |                            |                     |                   ┃
                       |                            |                     |
                    ┃  |    (Local keys, commands,  |                     |                   ┃
                       |    and copies of messages)┄|                     |
                    ┃  |             ┊              ↓                     |                   ┃
 Destination <──|◀─────(────────────────────── dst_outgoing               |
    Computer        ┃  |                    ┊       ↑                     |                   ┃
                       ├──> g_msg_manager   ┊       │                     |
                    ┃  |               ↑    ┊       │                     |                   ┃
                       |        (Group┈│  (Incoming┈│         (URL token)┈|
                    ┃  |    management │  messages) │                     |                   ┃
                       │     messages) │            │                     |
                    ┃  ↓               │            │                     |                   ┃
                      client_scheduler │            │                     |
                    ┃         └──> client ──────────┴─────────────────────┘                   ┃
                                       ↑
                    ┃                  │                                                      ┃
                                       └─────────────────────────────────────────────────────────── flask_server on
                    ┃                                        ┊                                ┃     contact's Networked
                                (Incoming message/file/public key/group management message)         Computer
                    ┃                                                                         ┃
                    ┗━━  ━━  ━━  ━━  ━━  ━━  ━━  ━━  ━━  ━━  ━━  ━━  ━━  ━━  ━━  ━━  ━━  ━━ ━━┛


    The diagram above gives a rough overview of the structure of the
    Relay Program. The Relay Program acts as a protocol converter that
    reads datagrams from the Source Computer. Outgoing
    message/file/public key datagrams are made available in the user's
    Tor v3 Onion Service. Copies of sent message datagrams as well as
    datagrams from contacts' Onion Services are forwarded to the
    Destination Computer. The Relay-to-Relay encrypted datagrams from
    contacts such as contact requests, public keys and group management
    messages are displayed by the Relay Program.

    Outgoing message datagrams are loaded by contacts from the user's
    Flask web server. To request messages intended for them, each
    contact uses a contact-specific URL token to load the messages.
    The URL token is the X448 shared secret derived from the per-session
    ephemeral X448 values of the two conversing parties. The private
    value stays on the Relay Program -- the public value is obtained by
    connecting to the root domain of contact's Onion Service.
    """
    working_dir = f'{os.getenv("HOME")}/{DIR_TFC}'
    ensure_dir(working_dir)
    os.chdir(working_dir)

    _, local_test, data_diode_sockets = process_arguments()

    gateway = Gateway(NC, local_test, data_diode_sockets)

    print_title(NC)

    url_token_private_key = X448PrivateKey.generate()
    url_token_public_key = url_token_private_key.public_key().public_bytes(
        encoding=Encoding.Raw, format=PublicFormat.Raw).hex()  # type: str

    queues = \
        {GATEWAY_QUEUE:      Queue(),  # All     datagrams           from `gateway_loop`          to `src_incoming`
         DST_MESSAGE_QUEUE:  Queue(),  # Message datagrams           from `src_incoming`/`client` to `dst_outgoing`
         M_TO_FLASK_QUEUE:   Queue(),  # Message/pubkey datagrams    from `src_incoming`          to `flask_server`
         F_TO_FLASK_QUEUE:   Queue(),  # File datagrams              from `src_incoming`          to `flask_server`
         SRC_TO_RELAY_QUEUE: Queue(),  # Command datagrams           from `src_incoming`          to `relay_command`
         DST_COMMAND_QUEUE:  Queue(),  # Command datagrams           from `src_incoming`          to `dst_outgoing`
         CONTACT_MGMT_QUEUE: Queue(),  # Contact management commands from `relay_command`         to `client_scheduler`
         C_REQ_STATE_QUEUE:  Queue(),  # Contact req. notify setting from `relay_command`         to `c_req_manager`
         URL_TOKEN_QUEUE:    Queue(),  # URL tokens                  from `client`                to `flask_server`
         GROUP_MSG_QUEUE:    Queue(),  # Group management messages   from `client`                to `g_msg_manager`
         CONTACT_REQ_QUEUE:  Queue(),  # Contact requests            from `flask_server`          to `c_req_manager`
         C_REQ_MGMT_QUEUE:   Queue(),  # Contact list management     from `relay_command`         to `c_req_manager`
         GROUP_MGMT_QUEUE:   Queue(),  # Contact list management     from `relay_command`         to `g_msg_manager`
         ONION_CLOSE_QUEUE:  Queue(),  # Onion Service close command from `relay_command`         to `onion_service`
         ONION_KEY_QUEUE:    Queue(),  # Onion Service private key   from `relay_command`         to `onion_service`
         TOR_DATA_QUEUE:     Queue(),  # Open port for Tor           from `onion_service`         to `client_scheduler`
         EXIT_QUEUE:         Queue()   # EXIT/WIPE signal            from `relay_command`         to `main`
         }  # type: Dict[bytes, Queue[Any]]

    process_list = [
        Process(target=gateway_loop, args=(queues, gateway)),
        Process(target=src_incoming, args=(queues, gateway)),
        Process(target=dst_outgoing, args=(queues, gateway)),
        Process(target=client_scheduler,
                args=(queues, gateway, url_token_private_key)),
        Process(target=g_msg_manager, args=(queues, )),
        Process(target=c_req_manager, args=(queues, )),
        Process(target=flask_server, args=(queues, url_token_public_key)),
        Process(target=onion_service, args=(queues, )),
        Process(target=relay_command,
                args=(queues, gateway, sys.stdin.fileno()))
    ]

    for p in process_list:
        p.start()

    monitor_processes(process_list, NC, queues)
Пример #24
0
 def test_generate(self, backend):
     key = X448PrivateKey.generate()
     assert key
     assert key.public_key()
Пример #25
0
 def test_zero_public_key_raises_critical_error(self):
     with self.assertRaises(SystemExit):
         X448.shared_key(X448PrivateKey.generate(),
                         bytes(TFC_PUBLIC_KEY_LENGTH))