• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.x25519 import (
11    X25519PrivateKey,
12    X25519PublicKey,
13)
14
15
16_X25519_KEY_SIZE = 32
17
18
19@utils.register_interface(X25519PublicKey)
20class _X25519PublicKey(object):
21    def __init__(self, backend, evp_pkey):
22        self._backend = backend
23        self._evp_pkey = evp_pkey
24
25    def public_bytes(self, encoding, format):
26        if (
27            encoding is serialization.Encoding.Raw
28            or format is serialization.PublicFormat.Raw
29        ):
30            if (
31                encoding is not serialization.Encoding.Raw
32                or format is not serialization.PublicFormat.Raw
33            ):
34                raise ValueError(
35                    "When using Raw both encoding and format must be Raw"
36                )
37
38            return self._raw_public_bytes()
39
40        return self._backend._public_key_bytes(
41            encoding, format, self, self._evp_pkey, None
42        )
43
44    def _raw_public_bytes(self):
45        ucharpp = self._backend._ffi.new("unsigned char **")
46        res = self._backend._lib.EVP_PKEY_get1_tls_encodedpoint(
47            self._evp_pkey, ucharpp
48        )
49        self._backend.openssl_assert(res == 32)
50        self._backend.openssl_assert(ucharpp[0] != self._backend._ffi.NULL)
51        data = self._backend._ffi.gc(
52            ucharpp[0], self._backend._lib.OPENSSL_free
53        )
54        return self._backend._ffi.buffer(data, res)[:]
55
56
57@utils.register_interface(X25519PrivateKey)
58class _X25519PrivateKey(object):
59    def __init__(self, backend, evp_pkey):
60        self._backend = backend
61        self._evp_pkey = evp_pkey
62
63    def public_key(self):
64        bio = self._backend._create_mem_bio_gc()
65        res = self._backend._lib.i2d_PUBKEY_bio(bio, self._evp_pkey)
66        self._backend.openssl_assert(res == 1)
67        evp_pkey = self._backend._lib.d2i_PUBKEY_bio(
68            bio, self._backend._ffi.NULL
69        )
70        self._backend.openssl_assert(evp_pkey != self._backend._ffi.NULL)
71        evp_pkey = self._backend._ffi.gc(
72            evp_pkey, self._backend._lib.EVP_PKEY_free
73        )
74        return _X25519PublicKey(self._backend, evp_pkey)
75
76    def exchange(self, peer_public_key):
77        if not isinstance(peer_public_key, X25519PublicKey):
78            raise TypeError("peer_public_key must be X25519PublicKey.")
79
80        return _evp_pkey_derive(self._backend, self._evp_pkey, peer_public_key)
81
82    def private_bytes(self, encoding, format, encryption_algorithm):
83        if (
84            encoding is serialization.Encoding.Raw
85            or format is serialization.PublicFormat.Raw
86        ):
87            if (
88                format is not serialization.PrivateFormat.Raw
89                or encoding is not serialization.Encoding.Raw
90                or not isinstance(
91                    encryption_algorithm, serialization.NoEncryption
92                )
93            ):
94                raise ValueError(
95                    "When using Raw both encoding and format must be Raw "
96                    "and encryption_algorithm must be NoEncryption()"
97                )
98
99            return self._raw_private_bytes()
100
101        return self._backend._private_key_bytes(
102            encoding, format, encryption_algorithm, self, self._evp_pkey, None
103        )
104
105    def _raw_private_bytes(self):
106        # When we drop support for CRYPTOGRAPHY_OPENSSL_LESS_THAN_111 we can
107        # switch this to EVP_PKEY_new_raw_private_key
108        # The trick we use here is serializing to a PKCS8 key and just
109        # using the last 32 bytes, which is the key itself.
110        bio = self._backend._create_mem_bio_gc()
111        res = self._backend._lib.i2d_PKCS8PrivateKey_bio(
112            bio,
113            self._evp_pkey,
114            self._backend._ffi.NULL,
115            self._backend._ffi.NULL,
116            0,
117            self._backend._ffi.NULL,
118            self._backend._ffi.NULL,
119        )
120        self._backend.openssl_assert(res == 1)
121        pkcs8 = self._backend._read_mem_bio(bio)
122        self._backend.openssl_assert(len(pkcs8) == 48)
123        return pkcs8[-_X25519_KEY_SIZE:]
124