1# This file is dual licensed under the terms of the Apache License, Version
2# 2.0, and the BSD License. See the LICENSE file in the root of this repository
3# for complete details.
4
5from __future__ import absolute_import, division, print_function
6
7from cryptography import utils
8from cryptography.hazmat.backends.openssl.utils import _evp_pkey_derive
9from cryptography.hazmat.primitives import serialization
10from cryptography.hazmat.primitives.asymmetric.x448 import (
11    X448PrivateKey,
12    X448PublicKey,
13)
14
15_X448_KEY_SIZE = 56
16
17
18@utils.register_interface(X448PublicKey)
19class _X448PublicKey(object):
20    def __init__(self, backend, evp_pkey):
21        self._backend = backend
22        self._evp_pkey = evp_pkey
23
24    def public_bytes(self, encoding, format):
25        if (
26            encoding is serialization.Encoding.Raw
27            or format is serialization.PublicFormat.Raw
28        ):
29            if (
30                encoding is not serialization.Encoding.Raw
31                or format is not serialization.PublicFormat.Raw
32            ):
33                raise ValueError(
34                    "When using Raw both encoding and format must be Raw"
35                )
36
37            return self._raw_public_bytes()
38
39        return self._backend._public_key_bytes(
40            encoding, format, self, self._evp_pkey, None
41        )
42
43    def _raw_public_bytes(self):
44        buf = self._backend._ffi.new("unsigned char []", _X448_KEY_SIZE)
45        buflen = self._backend._ffi.new("size_t *", _X448_KEY_SIZE)
46        res = self._backend._lib.EVP_PKEY_get_raw_public_key(
47            self._evp_pkey, buf, buflen
48        )
49        self._backend.openssl_assert(res == 1)
50        self._backend.openssl_assert(buflen[0] == _X448_KEY_SIZE)
51        return self._backend._ffi.buffer(buf, _X448_KEY_SIZE)[:]
52
53
54@utils.register_interface(X448PrivateKey)
55class _X448PrivateKey(object):
56    def __init__(self, backend, evp_pkey):
57        self._backend = backend
58        self._evp_pkey = evp_pkey
59
60    def public_key(self):
61        buf = self._backend._ffi.new("unsigned char []", _X448_KEY_SIZE)
62        buflen = self._backend._ffi.new("size_t *", _X448_KEY_SIZE)
63        res = self._backend._lib.EVP_PKEY_get_raw_public_key(
64            self._evp_pkey, buf, buflen
65        )
66        self._backend.openssl_assert(res == 1)
67        self._backend.openssl_assert(buflen[0] == _X448_KEY_SIZE)
68        return self._backend.x448_load_public_bytes(buf)
69
70    def exchange(self, peer_public_key):
71        if not isinstance(peer_public_key, X448PublicKey):
72            raise TypeError("peer_public_key must be X448PublicKey.")
73
74        return _evp_pkey_derive(self._backend, self._evp_pkey, peer_public_key)
75
76    def private_bytes(self, encoding, format, encryption_algorithm):
77        if (
78            encoding is serialization.Encoding.Raw
79            or format is serialization.PublicFormat.Raw
80        ):
81            if (
82                format is not serialization.PrivateFormat.Raw
83                or encoding is not serialization.Encoding.Raw
84                or not isinstance(
85                    encryption_algorithm, serialization.NoEncryption
86                )
87            ):
88                raise ValueError(
89                    "When using Raw both encoding and format must be Raw "
90                    "and encryption_algorithm must be NoEncryption()"
91                )
92
93            return self._raw_private_bytes()
94
95        return self._backend._private_key_bytes(
96            encoding, format, encryption_algorithm, self, self._evp_pkey, None
97        )
98
99    def _raw_private_bytes(self):
100        buf = self._backend._ffi.new("unsigned char []", _X448_KEY_SIZE)
101        buflen = self._backend._ffi.new("size_t *", _X448_KEY_SIZE)
102        res = self._backend._lib.EVP_PKEY_get_raw_private_key(
103            self._evp_pkey, buf, buflen
104        )
105        self._backend.openssl_assert(res == 1)
106        self._backend.openssl_assert(buflen[0] == _X448_KEY_SIZE)
107        return self._backend._ffi.buffer(buf, _X448_KEY_SIZE)[:]
108