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 exceptions, utils
8from cryptography.hazmat.primitives import serialization
9from cryptography.hazmat.primitives.asymmetric.ed25519 import (
10    Ed25519PrivateKey,
11    Ed25519PublicKey,
12    _ED25519_KEY_SIZE,
13    _ED25519_SIG_SIZE,
14)
15
16
17@utils.register_interface(Ed25519PublicKey)
18class _Ed25519PublicKey(object):
19    def __init__(self, backend, evp_pkey):
20        self._backend = backend
21        self._evp_pkey = evp_pkey
22
23    def public_bytes(self, encoding, format):
24        if (
25            encoding is serialization.Encoding.Raw
26            or format is serialization.PublicFormat.Raw
27        ):
28            if (
29                encoding is not serialization.Encoding.Raw
30                or format is not serialization.PublicFormat.Raw
31            ):
32                raise ValueError(
33                    "When using Raw both encoding and format must be Raw"
34                )
35
36            return self._raw_public_bytes()
37
38        return self._backend._public_key_bytes(
39            encoding, format, self, self._evp_pkey, None
40        )
41
42    def _raw_public_bytes(self):
43        buf = self._backend._ffi.new("unsigned char []", _ED25519_KEY_SIZE)
44        buflen = self._backend._ffi.new("size_t *", _ED25519_KEY_SIZE)
45        res = self._backend._lib.EVP_PKEY_get_raw_public_key(
46            self._evp_pkey, buf, buflen
47        )
48        self._backend.openssl_assert(res == 1)
49        self._backend.openssl_assert(buflen[0] == _ED25519_KEY_SIZE)
50        return self._backend._ffi.buffer(buf, _ED25519_KEY_SIZE)[:]
51
52    def verify(self, signature, data):
53        evp_md_ctx = self._backend._lib.EVP_MD_CTX_new()
54        self._backend.openssl_assert(evp_md_ctx != self._backend._ffi.NULL)
55        evp_md_ctx = self._backend._ffi.gc(
56            evp_md_ctx, self._backend._lib.EVP_MD_CTX_free
57        )
58        res = self._backend._lib.EVP_DigestVerifyInit(
59            evp_md_ctx,
60            self._backend._ffi.NULL,
61            self._backend._ffi.NULL,
62            self._backend._ffi.NULL,
63            self._evp_pkey,
64        )
65        self._backend.openssl_assert(res == 1)
66        res = self._backend._lib.EVP_DigestVerify(
67            evp_md_ctx, signature, len(signature), data, len(data)
68        )
69        if res != 1:
70            self._backend._consume_errors()
71            raise exceptions.InvalidSignature
72
73
74@utils.register_interface(Ed25519PrivateKey)
75class _Ed25519PrivateKey(object):
76    def __init__(self, backend, evp_pkey):
77        self._backend = backend
78        self._evp_pkey = evp_pkey
79
80    def public_key(self):
81        buf = self._backend._ffi.new("unsigned char []", _ED25519_KEY_SIZE)
82        buflen = self._backend._ffi.new("size_t *", _ED25519_KEY_SIZE)
83        res = self._backend._lib.EVP_PKEY_get_raw_public_key(
84            self._evp_pkey, buf, buflen
85        )
86        self._backend.openssl_assert(res == 1)
87        self._backend.openssl_assert(buflen[0] == _ED25519_KEY_SIZE)
88        public_bytes = self._backend._ffi.buffer(buf)[:]
89        return self._backend.ed25519_load_public_bytes(public_bytes)
90
91    def sign(self, data):
92        evp_md_ctx = self._backend._lib.EVP_MD_CTX_new()
93        self._backend.openssl_assert(evp_md_ctx != self._backend._ffi.NULL)
94        evp_md_ctx = self._backend._ffi.gc(
95            evp_md_ctx, self._backend._lib.EVP_MD_CTX_free
96        )
97        res = self._backend._lib.EVP_DigestSignInit(
98            evp_md_ctx,
99            self._backend._ffi.NULL,
100            self._backend._ffi.NULL,
101            self._backend._ffi.NULL,
102            self._evp_pkey,
103        )
104        self._backend.openssl_assert(res == 1)
105        buf = self._backend._ffi.new("unsigned char[]", _ED25519_SIG_SIZE)
106        buflen = self._backend._ffi.new("size_t *", len(buf))
107        res = self._backend._lib.EVP_DigestSign(
108            evp_md_ctx, buf, buflen, data, len(data)
109        )
110        self._backend.openssl_assert(res == 1)
111        self._backend.openssl_assert(buflen[0] == _ED25519_SIG_SIZE)
112        return self._backend._ffi.buffer(buf, buflen[0])[:]
113
114    def private_bytes(self, encoding, format, encryption_algorithm):
115        if (
116            encoding is serialization.Encoding.Raw
117            or format is serialization.PublicFormat.Raw
118        ):
119            if (
120                format is not serialization.PrivateFormat.Raw
121                or encoding is not serialization.Encoding.Raw
122                or not isinstance(
123                    encryption_algorithm, serialization.NoEncryption
124                )
125            ):
126                raise ValueError(
127                    "When using Raw both encoding and format must be Raw "
128                    "and encryption_algorithm must be NoEncryption()"
129                )
130
131            return self._raw_private_bytes()
132
133        return self._backend._private_key_bytes(
134            encoding, format, encryption_algorithm, self, self._evp_pkey, None
135        )
136
137    def _raw_private_bytes(self):
138        buf = self._backend._ffi.new("unsigned char []", _ED25519_KEY_SIZE)
139        buflen = self._backend._ffi.new("size_t *", _ED25519_KEY_SIZE)
140        res = self._backend._lib.EVP_PKEY_get_raw_private_key(
141            self._evp_pkey, buf, buflen
142        )
143        self._backend.openssl_assert(res == 1)
144        self._backend.openssl_assert(buflen[0] == _ED25519_KEY_SIZE)
145        return self._backend._ffi.buffer(buf, _ED25519_KEY_SIZE)[:]
146