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 InvalidSignature, _Reasons 13from cryptography.hazmat.primitives import serialization 14from cryptography.hazmat.primitives.asymmetric.ed25519 import ( 15 Ed25519PrivateKey, 16 Ed25519PublicKey, 17) 18 19from ...utils import load_vectors_from_file, raises_unsupported_algorithm 20 21 22def load_ed25519_vectors(vector_data): 23 """ 24 djb's ed25519 vectors are structured as a colon delimited array: 25 0: secret key (32 bytes) + public key (32 bytes) 26 1: public key (32 bytes) 27 2: message (0+ bytes) 28 3: signature + message (64+ bytes) 29 """ 30 data = [] 31 for line in vector_data: 32 secret_key, public_key, message, signature, _ = line.split(":") 33 secret_key = secret_key[0:64] 34 signature = signature[0:128] 35 data.append( 36 { 37 "secret_key": secret_key, 38 "public_key": public_key, 39 "message": message, 40 "signature": signature, 41 } 42 ) 43 return data 44 45 46@pytest.mark.supported( 47 only_if=lambda backend: not backend.ed25519_supported(), 48 skip_message="Requires OpenSSL without Ed25519 support", 49) 50def test_ed25519_unsupported(backend): 51 with raises_unsupported_algorithm( 52 _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM 53 ): 54 Ed25519PublicKey.from_public_bytes(b"0" * 32) 55 56 with raises_unsupported_algorithm( 57 _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM 58 ): 59 Ed25519PrivateKey.from_private_bytes(b"0" * 32) 60 61 with raises_unsupported_algorithm( 62 _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM 63 ): 64 Ed25519PrivateKey.generate() 65 66 67@pytest.mark.supported( 68 only_if=lambda backend: backend.ed25519_supported(), 69 skip_message="Requires OpenSSL with Ed25519 support", 70) 71class TestEd25519Signing(object): 72 @pytest.mark.parametrize( 73 "vector", 74 load_vectors_from_file( 75 os.path.join("asymmetric", "Ed25519", "sign.input"), 76 load_ed25519_vectors, 77 ), 78 ) 79 def test_sign_verify_input(self, vector, backend): 80 sk = binascii.unhexlify(vector["secret_key"]) 81 pk = binascii.unhexlify(vector["public_key"]) 82 message = binascii.unhexlify(vector["message"]) 83 signature = binascii.unhexlify(vector["signature"]) 84 private_key = Ed25519PrivateKey.from_private_bytes(sk) 85 computed_sig = private_key.sign(message) 86 assert computed_sig == signature 87 public_key = private_key.public_key() 88 assert ( 89 public_key.public_bytes( 90 serialization.Encoding.Raw, serialization.PublicFormat.Raw 91 ) 92 == pk 93 ) 94 public_key.verify(signature, message) 95 96 def test_invalid_signature(self, backend): 97 key = Ed25519PrivateKey.generate() 98 signature = key.sign(b"test data") 99 with pytest.raises(InvalidSignature): 100 key.public_key().verify(signature, b"wrong data") 101 102 with pytest.raises(InvalidSignature): 103 key.public_key().verify(b"0" * 64, b"test data") 104 105 def test_generate(self, backend): 106 key = Ed25519PrivateKey.generate() 107 assert key 108 assert key.public_key() 109 110 def test_load_public_bytes(self, backend): 111 public_key = Ed25519PrivateKey.generate().public_key() 112 public_bytes = public_key.public_bytes( 113 serialization.Encoding.Raw, serialization.PublicFormat.Raw 114 ) 115 public_key2 = Ed25519PublicKey.from_public_bytes(public_bytes) 116 assert public_bytes == public_key2.public_bytes( 117 serialization.Encoding.Raw, serialization.PublicFormat.Raw 118 ) 119 120 def test_invalid_type_public_bytes(self, backend): 121 with pytest.raises(TypeError): 122 Ed25519PublicKey.from_public_bytes(object()) 123 124 def test_invalid_type_private_bytes(self, backend): 125 with pytest.raises(TypeError): 126 Ed25519PrivateKey.from_private_bytes(object()) 127 128 def test_invalid_length_from_public_bytes(self, backend): 129 with pytest.raises(ValueError): 130 Ed25519PublicKey.from_public_bytes(b"a" * 31) 131 with pytest.raises(ValueError): 132 Ed25519PublicKey.from_public_bytes(b"a" * 33) 133 134 def test_invalid_length_from_private_bytes(self, backend): 135 with pytest.raises(ValueError): 136 Ed25519PrivateKey.from_private_bytes(b"a" * 31) 137 with pytest.raises(ValueError): 138 Ed25519PrivateKey.from_private_bytes(b"a" * 33) 139 140 def test_invalid_private_bytes(self, backend): 141 key = Ed25519PrivateKey.generate() 142 with pytest.raises(ValueError): 143 key.private_bytes( 144 serialization.Encoding.Raw, 145 serialization.PrivateFormat.Raw, 146 None, 147 ) 148 149 with pytest.raises(ValueError): 150 key.private_bytes( 151 serialization.Encoding.Raw, 152 serialization.PrivateFormat.PKCS8, 153 None, 154 ) 155 156 with pytest.raises(ValueError): 157 key.private_bytes( 158 serialization.Encoding.PEM, 159 serialization.PrivateFormat.Raw, 160 serialization.NoEncryption(), 161 ) 162 163 def test_invalid_public_bytes(self, backend): 164 key = Ed25519PrivateKey.generate().public_key() 165 with pytest.raises(ValueError): 166 key.public_bytes( 167 serialization.Encoding.Raw, 168 serialization.PublicFormat.SubjectPublicKeyInfo, 169 ) 170 171 with pytest.raises(ValueError): 172 key.public_bytes( 173 serialization.Encoding.PEM, serialization.PublicFormat.PKCS1 174 ) 175 176 with pytest.raises(ValueError): 177 key.public_bytes( 178 serialization.Encoding.PEM, serialization.PublicFormat.Raw 179 ) 180 181 @pytest.mark.parametrize( 182 ("encoding", "fmt", "encryption", "passwd", "load_func"), 183 [ 184 ( 185 serialization.Encoding.PEM, 186 serialization.PrivateFormat.PKCS8, 187 serialization.BestAvailableEncryption(b"password"), 188 b"password", 189 serialization.load_pem_private_key, 190 ), 191 ( 192 serialization.Encoding.DER, 193 serialization.PrivateFormat.PKCS8, 194 serialization.BestAvailableEncryption(b"password"), 195 b"password", 196 serialization.load_der_private_key, 197 ), 198 ( 199 serialization.Encoding.PEM, 200 serialization.PrivateFormat.PKCS8, 201 serialization.NoEncryption(), 202 None, 203 serialization.load_pem_private_key, 204 ), 205 ( 206 serialization.Encoding.DER, 207 serialization.PrivateFormat.PKCS8, 208 serialization.NoEncryption(), 209 None, 210 serialization.load_der_private_key, 211 ), 212 ], 213 ) 214 def test_round_trip_private_serialization( 215 self, encoding, fmt, encryption, passwd, load_func, backend 216 ): 217 key = Ed25519PrivateKey.generate() 218 serialized = key.private_bytes(encoding, fmt, encryption) 219 loaded_key = load_func(serialized, passwd, backend) 220 assert isinstance(loaded_key, Ed25519PrivateKey) 221 222 def test_buffer_protocol(self, backend): 223 private_bytes = os.urandom(32) 224 key = Ed25519PrivateKey.from_private_bytes(bytearray(private_bytes)) 225 assert ( 226 key.private_bytes( 227 serialization.Encoding.Raw, 228 serialization.PrivateFormat.Raw, 229 serialization.NoEncryption(), 230 ) 231 == private_bytes 232 ) 233