• 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
7import warnings
8
9from cryptography import utils
10from cryptography.hazmat.backends.openssl.utils import _evp_pkey_derive
11from cryptography.hazmat.primitives import serialization
12from cryptography.hazmat.primitives.asymmetric.x25519 import (
13    X25519PrivateKey, X25519PublicKey
14)
15
16
17_X25519_KEY_SIZE = 32
18
19
20@utils.register_interface(X25519PublicKey)
21class _X25519PublicKey(object):
22    def __init__(self, backend, evp_pkey):
23        self._backend = backend
24        self._evp_pkey = evp_pkey
25
26    def public_bytes(self, encoding=None, format=None):
27        if encoding is None or format is None:
28            if encoding is not None or format is not None:
29                raise ValueError("Both encoding and format are required")
30            else:
31                warnings.warn(
32                    "public_bytes now requires encoding and format arguments. "
33                    "Support for calling without arguments will be removed in "
34                    "cryptography 2.7",
35                    utils.DeprecatedIn25,
36                )
37                encoding = serialization.Encoding.Raw
38                format = serialization.PublicFormat.Raw
39        if (
40            encoding is serialization.Encoding.Raw or
41            format is serialization.PublicFormat.Raw
42        ):
43            if (
44                encoding is not serialization.Encoding.Raw or
45                format is not serialization.PublicFormat.Raw
46            ):
47                raise ValueError(
48                    "When using Raw both encoding and format must be Raw"
49                )
50
51            return self._raw_public_bytes()
52
53        if (
54            encoding in serialization._PEM_DER and
55            format is not serialization.PublicFormat.SubjectPublicKeyInfo
56        ):
57            raise ValueError(
58                "format must be SubjectPublicKeyInfo when encoding is PEM or "
59                "DER"
60            )
61
62        return self._backend._public_key_bytes(
63            encoding, format, self, self._evp_pkey, None
64        )
65
66    def _raw_public_bytes(self):
67        ucharpp = self._backend._ffi.new("unsigned char **")
68        res = self._backend._lib.EVP_PKEY_get1_tls_encodedpoint(
69            self._evp_pkey, ucharpp
70        )
71        self._backend.openssl_assert(res == 32)
72        self._backend.openssl_assert(ucharpp[0] != self._backend._ffi.NULL)
73        data = self._backend._ffi.gc(
74            ucharpp[0], self._backend._lib.OPENSSL_free
75        )
76        return self._backend._ffi.buffer(data, res)[:]
77
78
79@utils.register_interface(X25519PrivateKey)
80class _X25519PrivateKey(object):
81    def __init__(self, backend, evp_pkey):
82        self._backend = backend
83        self._evp_pkey = evp_pkey
84
85    def public_key(self):
86        bio = self._backend._create_mem_bio_gc()
87        res = self._backend._lib.i2d_PUBKEY_bio(bio, self._evp_pkey)
88        self._backend.openssl_assert(res == 1)
89        evp_pkey = self._backend._lib.d2i_PUBKEY_bio(
90            bio, self._backend._ffi.NULL
91        )
92        self._backend.openssl_assert(evp_pkey != self._backend._ffi.NULL)
93        evp_pkey = self._backend._ffi.gc(
94            evp_pkey, self._backend._lib.EVP_PKEY_free
95        )
96        return _X25519PublicKey(self._backend, evp_pkey)
97
98    def exchange(self, peer_public_key):
99        if not isinstance(peer_public_key, X25519PublicKey):
100            raise TypeError("peer_public_key must be X25519PublicKey.")
101
102        return _evp_pkey_derive(
103            self._backend, self._evp_pkey, peer_public_key
104        )
105
106    def private_bytes(self, encoding, format, encryption_algorithm):
107        if (
108            encoding is serialization.Encoding.Raw or
109            format is serialization.PublicFormat.Raw
110        ):
111            if (
112                format is not serialization.PrivateFormat.Raw or
113                encoding is not serialization.Encoding.Raw or not
114                isinstance(encryption_algorithm, serialization.NoEncryption)
115            ):
116                raise ValueError(
117                    "When using Raw both encoding and format must be Raw "
118                    "and encryption_algorithm must be NoEncryption"
119                )
120
121            return self._raw_private_bytes()
122
123        if (
124            encoding in serialization._PEM_DER and
125            format is not serialization.PrivateFormat.PKCS8
126        ):
127            raise ValueError(
128                "format must be PKCS8 when encoding is PEM or DER"
129            )
130
131        return self._backend._private_key_bytes(
132            encoding, format, encryption_algorithm, self._evp_pkey, None
133        )
134
135    def _raw_private_bytes(self):
136        # When we drop support for CRYPTOGRAPHY_OPENSSL_LESS_THAN_111 we can
137        # switch this to EVP_PKEY_new_raw_private_key
138        # The trick we use here is serializing to a PKCS8 key and just
139        # using the last 32 bytes, which is the key itself.
140        bio = self._backend._create_mem_bio_gc()
141        res = self._backend._lib.i2d_PKCS8PrivateKey_bio(
142            bio, self._evp_pkey,
143            self._backend._ffi.NULL, self._backend._ffi.NULL,
144            0, self._backend._ffi.NULL, self._backend._ffi.NULL
145        )
146        self._backend.openssl_assert(res == 1)
147        pkcs8 = self._backend._read_mem_bio(bio)
148        self._backend.openssl_assert(len(pkcs8) == 48)
149        return pkcs8[-_X25519_KEY_SIZE:]
150