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 binascii 8import os 9 10import pytest 11 12from cryptography.exceptions import _Reasons 13from cryptography.hazmat.backends.interfaces import DHBackend 14from cryptography.hazmat.primitives import serialization 15from cryptography.hazmat.primitives.asymmetric.x448 import ( 16 X448PrivateKey, X448PublicKey 17) 18 19from ...utils import ( 20 load_nist_vectors, load_vectors_from_file, raises_unsupported_algorithm 21) 22 23 24@pytest.mark.supported( 25 only_if=lambda backend: not backend.x448_supported(), 26 skip_message="Requires OpenSSL without X448 support" 27) 28@pytest.mark.requires_backend_interface(interface=DHBackend) 29def test_x448_unsupported(backend): 30 with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM): 31 X448PublicKey.from_public_bytes(b"0" * 56) 32 33 with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM): 34 X448PrivateKey.from_private_bytes(b"0" * 56) 35 36 with raises_unsupported_algorithm(_Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM): 37 X448PrivateKey.generate() 38 39 40@pytest.mark.supported( 41 only_if=lambda backend: backend.x448_supported(), 42 skip_message="Requires OpenSSL with X448 support" 43) 44@pytest.mark.requires_backend_interface(interface=DHBackend) 45class TestX448Exchange(object): 46 @pytest.mark.parametrize( 47 "vector", 48 load_vectors_from_file( 49 os.path.join("asymmetric", "X448", "rfc7748.txt"), 50 load_nist_vectors 51 ) 52 ) 53 def test_rfc7748(self, vector, backend): 54 private = binascii.unhexlify(vector["input_scalar"]) 55 public = binascii.unhexlify(vector["input_u"]) 56 shared_key = binascii.unhexlify(vector["output_u"]) 57 private_key = X448PrivateKey.from_private_bytes(private) 58 public_key = X448PublicKey.from_public_bytes(public) 59 computed_shared_key = private_key.exchange(public_key) 60 assert computed_shared_key == shared_key 61 62 def test_rfc7748_1000_iteration(self, backend): 63 old_private = private = public = binascii.unhexlify( 64 b"05000000000000000000000000000000000000000000000000000000" 65 b"00000000000000000000000000000000000000000000000000000000" 66 ) 67 shared_key = binascii.unhexlify( 68 b"aa3b4749d55b9daf1e5b00288826c467274ce3ebbdd5c17b975e09d4" 69 b"af6c67cf10d087202db88286e2b79fceea3ec353ef54faa26e219f38" 70 ) 71 private_key = X448PrivateKey.from_private_bytes(private) 72 public_key = X448PublicKey.from_public_bytes(public) 73 for _ in range(1000): 74 computed_shared_key = private_key.exchange(public_key) 75 private_key = X448PrivateKey.from_private_bytes( 76 computed_shared_key 77 ) 78 public_key = X448PublicKey.from_public_bytes(old_private) 79 old_private = computed_shared_key 80 81 assert computed_shared_key == shared_key 82 83 # These vectors are also from RFC 7748 84 # https://tools.ietf.org/html/rfc7748#section-6.2 85 @pytest.mark.parametrize( 86 ("private_bytes", "public_bytes"), 87 [ 88 ( 89 binascii.unhexlify( 90 b"9a8f4925d1519f5775cf46b04b5800d4ee9ee8bae8bc5565d498c28d" 91 b"d9c9baf574a9419744897391006382a6f127ab1d9ac2d8c0a598726b" 92 ), 93 binascii.unhexlify( 94 b"9b08f7cc31b7e3e67d22d5aea121074a273bd2b83de09c63faa73d2c" 95 b"22c5d9bbc836647241d953d40c5b12da88120d53177f80e532c41fa0" 96 ) 97 ), 98 ( 99 binascii.unhexlify( 100 b"1c306a7ac2a0e2e0990b294470cba339e6453772b075811d8fad0d1d" 101 b"6927c120bb5ee8972b0d3e21374c9c921b09d1b0366f10b65173992d" 102 ), 103 binascii.unhexlify( 104 b"3eb7a829b0cd20f5bcfc0b599b6feccf6da4627107bdb0d4f345b430" 105 b"27d8b972fc3e34fb4232a13ca706dcb57aec3dae07bdc1c67bf33609" 106 ) 107 ) 108 ] 109 ) 110 def test_pub_priv_bytes_raw(self, private_bytes, public_bytes, backend): 111 private_key = X448PrivateKey.from_private_bytes(private_bytes) 112 assert private_key.private_bytes( 113 serialization.Encoding.Raw, 114 serialization.PrivateFormat.Raw, 115 serialization.NoEncryption() 116 ) == private_bytes 117 assert private_key.public_key().public_bytes( 118 serialization.Encoding.Raw, serialization.PublicFormat.Raw 119 ) == public_bytes 120 public_key = X448PublicKey.from_public_bytes(public_bytes) 121 assert public_key.public_bytes( 122 serialization.Encoding.Raw, serialization.PublicFormat.Raw 123 ) == public_bytes 124 125 @pytest.mark.parametrize( 126 ("encoding", "fmt", "encryption", "passwd", "load_func"), 127 [ 128 ( 129 serialization.Encoding.PEM, 130 serialization.PrivateFormat.PKCS8, 131 serialization.BestAvailableEncryption(b"password"), 132 b"password", 133 serialization.load_pem_private_key 134 ), 135 ( 136 serialization.Encoding.DER, 137 serialization.PrivateFormat.PKCS8, 138 serialization.BestAvailableEncryption(b"password"), 139 b"password", 140 serialization.load_der_private_key 141 ), 142 ( 143 serialization.Encoding.PEM, 144 serialization.PrivateFormat.PKCS8, 145 serialization.NoEncryption(), 146 None, 147 serialization.load_pem_private_key 148 ), 149 ( 150 serialization.Encoding.DER, 151 serialization.PrivateFormat.PKCS8, 152 serialization.NoEncryption(), 153 None, 154 serialization.load_der_private_key 155 ), 156 ] 157 ) 158 def test_round_trip_private_serialization(self, encoding, fmt, encryption, 159 passwd, load_func, backend): 160 key = X448PrivateKey.generate() 161 serialized = key.private_bytes(encoding, fmt, encryption) 162 loaded_key = load_func(serialized, passwd, backend) 163 assert isinstance(loaded_key, X448PrivateKey) 164 165 def test_generate(self, backend): 166 key = X448PrivateKey.generate() 167 assert key 168 assert key.public_key() 169 170 def test_invalid_type_exchange(self, backend): 171 key = X448PrivateKey.generate() 172 with pytest.raises(TypeError): 173 key.exchange(object()) 174 175 def test_invalid_length_from_public_bytes(self, backend): 176 with pytest.raises(ValueError): 177 X448PublicKey.from_public_bytes(b"a" * 55) 178 179 with pytest.raises(ValueError): 180 X448PublicKey.from_public_bytes(b"a" * 57) 181 182 def test_invalid_length_from_private_bytes(self, backend): 183 with pytest.raises(ValueError): 184 X448PrivateKey.from_private_bytes(b"a" * 55) 185 186 with pytest.raises(ValueError): 187 X448PrivateKey.from_private_bytes(b"a" * 57) 188 189 def test_invalid_private_bytes(self, backend): 190 key = X448PrivateKey.generate() 191 with pytest.raises(ValueError): 192 key.private_bytes( 193 serialization.Encoding.Raw, 194 serialization.PrivateFormat.Raw, 195 None 196 ) 197 198 with pytest.raises(ValueError): 199 key.private_bytes( 200 serialization.Encoding.Raw, 201 serialization.PrivateFormat.PKCS8, 202 None 203 ) 204 205 with pytest.raises(ValueError): 206 key.private_bytes( 207 serialization.Encoding.PEM, 208 serialization.PrivateFormat.Raw, 209 serialization.NoEncryption() 210 ) 211 212 def test_invalid_public_bytes(self, backend): 213 key = X448PrivateKey.generate().public_key() 214 with pytest.raises(ValueError): 215 key.public_bytes( 216 serialization.Encoding.Raw, 217 serialization.PublicFormat.SubjectPublicKeyInfo 218 ) 219 220 with pytest.raises(ValueError): 221 key.public_bytes( 222 serialization.Encoding.PEM, 223 serialization.PublicFormat.PKCS1 224 ) 225 226 with pytest.raises(ValueError): 227 key.public_bytes( 228 serialization.Encoding.PEM, 229 serialization.PublicFormat.Raw 230 ) 231 232 def test_buffer_protocol(self, backend): 233 private_bytes = binascii.unhexlify( 234 b"9a8f4925d1519f5775cf46b04b5800d4ee9ee8bae8bc5565d498c28d" 235 b"d9c9baf574a9419744897391006382a6f127ab1d9ac2d8c0a598726b" 236 ) 237 key = X448PrivateKey.from_private_bytes(bytearray(private_bytes)) 238 assert key.private_bytes( 239 serialization.Encoding.Raw, 240 serialization.PrivateFormat.Raw, 241 serialization.NoEncryption() 242 ) == private_bytes 243