1# Copyright 2023 Google LLC 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15from typing import List, Tuple 16 17from absl.testing import absltest 18from absl.testing import parameterized 19 20import tink 21from tink import signature 22 23from tink.proto import common_pb2 24from tink.proto import rsa_ssa_pkcs1_pb2 25from tink.proto import tink_pb2 26import tink_config 27from util import testing_servers 28 29# 2048-bit modulus of the first test vector in 30# https://github.com/google/wycheproof/blob/master/testvectors/rsa_pkcs1_2048_test.json 31# This modulus uses the minimal two's-complement big-endian encoding, that's 32# why it starts with a zero and has a leading 0 byte and has 257 bytes. 33MODULUS_BYTES = bytes.fromhex( 34 '00b3510a2bcd4ce644c5b594ae5059e12b2f054b658d5da5959a2fdf1871b808' 35 'bc3df3e628d2792e51aad5c124b43bda453dca5cde4bcf28e7bd4effba0cb4b7' 36 '42bbb6d5a013cb63d1aa3a89e02627ef5398b52c0cfd97d208abeb8d7c9bce0b' 37 'beb019a86ddb589beb29a5b74bf861075c677c81d430f030c265247af9d3c914' 38 '0ccb65309d07e0adc1efd15cf17e7b055d7da3868e4648cc3a180f0ee7f8e1e7' 39 'b18098a3391b4ce7161e98d57af8a947e201a463e2d6bbca8059e5706e9dfed8' 40 'f4856465ffa712ed1aa18e888d12dc6aa09ce95ecfca83cc5b0b15db09c8647f' 41 '5d524c0f2e7620a3416b9623cadc0f097af573261c98c8400aa12af38e43cad84d' 42) 43 44# Same as MODULUS_BYTES, but with the least significant byte set to 0. 45# Hence this modulus has 256 as a factor. 46WEIRD_MODULUS_BYTES = bytes.fromhex( 47 '00b3510a2bcd4ce644c5b594ae5059e12b2f054b658d5da5959a2fdf1871b808' 48 'bc3df3e628d2792e51aad5c124b43bda453dca5cde4bcf28e7bd4effba0cb4b7' 49 '42bbb6d5a013cb63d1aa3a89e02627ef5398b52c0cfd97d208abeb8d7c9bce0b' 50 'beb019a86ddb589beb29a5b74bf861075c677c81d430f030c265247af9d3c914' 51 '0ccb65309d07e0adc1efd15cf17e7b055d7da3868e4648cc3a180f0ee7f8e1e7' 52 'b18098a3391b4ce7161e98d57af8a947e201a463e2d6bbca8059e5706e9dfed8' 53 'f4856465ffa712ed1aa18e888d12dc6aa09ce95ecfca83cc5b0b15db09c8647f' 54 '5d524c0f2e7620a3416b9623cadc0f097af573261c98c8400aa12af38e43cad800' 55) 56 57# Same as MODULUS_BYTES, but with the most significant bit set to 0, and 58# the 2nd most significant bit set to 1. So this modulus has 2047 bits. 59SHORT_MODULUS_BYTES = bytes.fromhex( 60 '0073510a2bcd4ce644c5b594ae5059e12b2f054b658d5da5959a2fdf1871b808' 61 'bc3df3e628d2792e51aad5c124b43bda453dca5cde4bcf28e7bd4effba0cb4b7' 62 '42bbb6d5a013cb63d1aa3a89e02627ef5398b52c0cfd97d208abeb8d7c9bce0b' 63 'beb019a86ddb589beb29a5b74bf861075c677c81d430f030c265247af9d3c914' 64 '0ccb65309d07e0adc1efd15cf17e7b055d7da3868e4648cc3a180f0ee7f8e1e7' 65 'b18098a3391b4ce7161e98d57af8a947e201a463e2d6bbca8059e5706e9dfed8' 66 'f4856465ffa712ed1aa18e888d12dc6aa09ce95ecfca83cc5b0b15db09c8647f' 67 '5d524c0f2e7620a3416b9623cadc0f097af573261c98c8400aa12af38e43cad84d' 68) 69 70# big-endian encoding of 4th Fermat number F4 = 65537 = 2^16 + 1 71F4_BYTES = bytes.fromhex('010001') 72 73RSA_SSA_PKCS1_PUBLIC_KEY_TYPE_URL = ( 74 'type.googleapis.com/google.crypto.tink.RsaSsaPkcs1PublicKey' 75) 76 77 78def setUpModule(): 79 signature.register() 80 testing_servers.start('aes_ctr_hmac_streaming_key_test') 81 82 83def tearDownModule(): 84 testing_servers.stop() 85 86 87def public_key_to_keyset( 88 public_key: rsa_ssa_pkcs1_pb2.RsaSsaPkcs1PublicKey, 89 output_prefix_type: tink_pb2.OutputPrefixType, 90) -> tink_pb2.Keyset: 91 """Embeds a RsaSsaPkcs1PrivateKey with the output_prefix_type in a keyset.""" 92 return tink_pb2.Keyset( 93 primary_key_id=1234, 94 key=[ 95 tink_pb2.Keyset.Key( 96 key_data=tink_pb2.KeyData( 97 type_url=RSA_SSA_PKCS1_PUBLIC_KEY_TYPE_URL, 98 value=public_key.SerializeToString(), 99 key_material_type=tink_pb2.KeyData.ASYMMETRIC_PUBLIC, 100 ), 101 output_prefix_type=output_prefix_type, 102 status=tink_pb2.KeyStatusType.ENABLED, 103 key_id=1234, 104 ) 105 ], 106 ) 107 108 109def valid_public_keys() -> ( 110 List[Tuple[str, rsa_ssa_pkcs1_pb2.RsaSsaPkcs1PublicKey]] 111): 112 return [ 113 ( 114 '2048-bit public key with SHA256', 115 rsa_ssa_pkcs1_pb2.RsaSsaPkcs1PublicKey( 116 version=0, 117 n=MODULUS_BYTES, 118 e=F4_BYTES, 119 params=rsa_ssa_pkcs1_pb2.RsaSsaPkcs1Params( 120 hash_type=common_pb2.HashType.SHA256 121 ), 122 ), 123 ), 124 ( 125 '2048-bit public key with SHA384', 126 rsa_ssa_pkcs1_pb2.RsaSsaPkcs1PublicKey( 127 version=0, 128 n=MODULUS_BYTES, 129 e=F4_BYTES, 130 params=rsa_ssa_pkcs1_pb2.RsaSsaPkcs1Params( 131 hash_type=common_pb2.HashType.SHA384 132 ), 133 ), 134 ), 135 ( 136 '2048-bit public key with SHA512', 137 rsa_ssa_pkcs1_pb2.RsaSsaPkcs1PublicKey( 138 version=0, 139 n=MODULUS_BYTES, 140 e=F4_BYTES, 141 params=rsa_ssa_pkcs1_pb2.RsaSsaPkcs1Params( 142 hash_type=common_pb2.HashType.SHA512 143 ), 144 ), 145 ), 146 ( 147 '2048-bit public key with SHA1', 148 rsa_ssa_pkcs1_pb2.RsaSsaPkcs1PublicKey( 149 version=0, 150 n=MODULUS_BYTES, 151 e=F4_BYTES, 152 params=rsa_ssa_pkcs1_pb2.RsaSsaPkcs1Params( 153 hash_type=common_pb2.HashType.SHA256 154 ), 155 ), 156 ), 157 ( 158 '2048-bit public key with e=2^16+3', 159 rsa_ssa_pkcs1_pb2.RsaSsaPkcs1PublicKey( 160 version=0, 161 n=MODULUS_BYTES, 162 e=bytes.fromhex('010003'), 163 params=rsa_ssa_pkcs1_pb2.RsaSsaPkcs1Params( 164 hash_type=common_pb2.HashType.SHA256 165 ), 166 ), 167 ), 168 ( 169 '2048-bit public key with e=2^32-1', 170 rsa_ssa_pkcs1_pb2.RsaSsaPkcs1PublicKey( 171 version=0, 172 n=MODULUS_BYTES, 173 e=bytes.fromhex('ffffffff'), 174 params=rsa_ssa_pkcs1_pb2.RsaSsaPkcs1Params( 175 hash_type=common_pb2.HashType.SHA256 176 ), 177 ), 178 ), 179 ( 180 '2048-bit public key with many leading zeros in the modulus', 181 rsa_ssa_pkcs1_pb2.RsaSsaPkcs1PublicKey( 182 version=0, 183 n=bytes.fromhex('00000000') + MODULUS_BYTES, 184 e=F4_BYTES, 185 params=rsa_ssa_pkcs1_pb2.RsaSsaPkcs1Params( 186 hash_type=common_pb2.HashType.SHA256 187 ), 188 ), 189 ), 190 ( 191 '2048-bit public key without any leading zeros in the modulus', 192 rsa_ssa_pkcs1_pb2.RsaSsaPkcs1PublicKey( 193 version=0, 194 n=MODULUS_BYTES[1:], 195 e=F4_BYTES, 196 params=rsa_ssa_pkcs1_pb2.RsaSsaPkcs1Params( 197 hash_type=common_pb2.HashType.SHA256 198 ), 199 ), 200 ), 201 ( 202 '2048-bit public key with modulus divisible by 256', 203 rsa_ssa_pkcs1_pb2.RsaSsaPkcs1PublicKey( 204 version=0, 205 n=WEIRD_MODULUS_BYTES, 206 e=F4_BYTES, 207 params=rsa_ssa_pkcs1_pb2.RsaSsaPkcs1Params( 208 hash_type=common_pb2.HashType.SHA256 209 ), 210 ), 211 ), 212 ] 213 214 215def invalid_public_keys() -> ( 216 List[Tuple[str, rsa_ssa_pkcs1_pb2.RsaSsaPkcs1PublicKey]] 217): 218 return [ 219 ( 220 '2048-bit public key with SHA224', 221 rsa_ssa_pkcs1_pb2.RsaSsaPkcs1PublicKey( 222 version=0, 223 n=MODULUS_BYTES, 224 e=F4_BYTES, 225 params=rsa_ssa_pkcs1_pb2.RsaSsaPkcs1Params( 226 hash_type=common_pb2.HashType.SHA224 227 ), 228 ), 229 ), 230 ( 231 '2048-bit public key with small e', 232 rsa_ssa_pkcs1_pb2.RsaSsaPkcs1PublicKey( 233 version=0, 234 n=MODULUS_BYTES, 235 e=bytes.fromhex('03'), 236 params=rsa_ssa_pkcs1_pb2.RsaSsaPkcs1Params( 237 hash_type=common_pb2.HashType.SHA1 238 ), 239 ), 240 ), 241 ( 242 '2048-bit public key with 2^16-1', 243 rsa_ssa_pkcs1_pb2.RsaSsaPkcs1PublicKey( 244 version=0, 245 n=MODULUS_BYTES, 246 e=bytes.fromhex('00ffff'), 247 params=rsa_ssa_pkcs1_pb2.RsaSsaPkcs1Params( 248 hash_type=common_pb2.HashType.SHA1 249 ), 250 ), 251 ), 252 ( 253 '2048-bit public key with an invalid e', 254 rsa_ssa_pkcs1_pb2.RsaSsaPkcs1PublicKey( 255 version=0, 256 n=MODULUS_BYTES, 257 # This e is even, which is invalid since e must be co-prime 258 # with p-1 and q-1, which both are also even. 259 e=bytes.fromhex('010002'), 260 params=rsa_ssa_pkcs1_pb2.RsaSsaPkcs1Params( 261 hash_type=common_pb2.HashType.SHA256 262 ), 263 ), 264 ), 265 ( 266 '2048-bit public key with e=2^32+1', 267 rsa_ssa_pkcs1_pb2.RsaSsaPkcs1PublicKey( 268 version=0, 269 n=MODULUS_BYTES, 270 # BoringSSL (which gets used in C++, Python and Go) rejects values 271 # for e with more than 32 bits. 272 e=bytes.fromhex('0100000001'), 273 params=rsa_ssa_pkcs1_pb2.RsaSsaPkcs1Params( 274 hash_type=common_pb2.HashType.SHA256 275 ), 276 ), 277 ), 278 ( 279 '2047-bit public key', 280 rsa_ssa_pkcs1_pb2.RsaSsaPkcs1PublicKey( 281 version=0, 282 n=SHORT_MODULUS_BYTES, 283 e=F4_BYTES, 284 params=rsa_ssa_pkcs1_pb2.RsaSsaPkcs1Params( 285 hash_type=common_pb2.HashType.SHA256 286 ), 287 ), 288 ), 289 ( 290 '2048-bit public key with version 1', 291 rsa_ssa_pkcs1_pb2.RsaSsaPkcs1PublicKey( 292 version=1, 293 n=MODULUS_BYTES, 294 e=F4_BYTES, 295 params=rsa_ssa_pkcs1_pb2.RsaSsaPkcs1Params( 296 hash_type=common_pb2.HashType.SHA256 297 ), 298 ), 299 ), 300 ] 301 302 303def valid_key_testcases(): 304 for lang in tink_config.supported_languages_for_key_type( 305 'RsaSsaPkcs1PublicKey' 306 ): 307 for key_desc, key in valid_public_keys(): 308 if lang == 'go' and ( 309 key_desc == '2048-bit public key with e=2^16+3' 310 or key_desc == '2048-bit public key with e=2^32-1' 311 ): 312 # Go only accepts e = F4 = 2^16 + 1 = 65537. See also b/274605582. 313 continue 314 yield ('%s: %s' % (key_desc, lang), lang, key) 315 316 317def invalid_key_testcases(): 318 for lang in tink_config.supported_languages_for_key_type( 319 'RsaSsaPkcs1PublicKey' 320 ): 321 for key_desc, key in invalid_public_keys(): 322 if lang == 'java' and key_desc == '2048-bit public key with e=2^32+1': 323 # Java accepts large values for e. See also b/274605582. 324 continue 325 yield ('%s: %s' % (key_desc, lang), lang, key) 326 327 328class RsaSsaPkcs1PublicKeyTest(parameterized.TestCase): 329 """Tests specific for keys of type RsaSsaPkcs1PublicKey.""" 330 331 @parameterized.named_parameters(valid_key_testcases()) 332 def test_create_signature_verify_with_valid_key_success( 333 self, lang: str, key: rsa_ssa_pkcs1_pb2.RsaSsaPkcs1PublicKey 334 ): 335 keyset = public_key_to_keyset(key, tink_pb2.OutputPrefixType.TINK) 336 testing_servers.remote_primitive( 337 lang, keyset.SerializeToString(), signature.PublicKeyVerify 338 ) 339 340 @parameterized.named_parameters(invalid_key_testcases()) 341 def test_create_signature_verify_with_invalid_key_fails( 342 self, lang: str, key: rsa_ssa_pkcs1_pb2.RsaSsaPkcs1PublicKey 343 ): 344 keyset = public_key_to_keyset(key, tink_pb2.OutputPrefixType.TINK) 345 with self.assertRaises(tink.TinkError): 346 testing_servers.remote_primitive( 347 lang, keyset.SerializeToString(), signature.PublicKeyVerify 348 ) 349 350 def test_golang_rejects_f4_plus_2(self): 351 """See also b/274605582.""" 352 key = rsa_ssa_pkcs1_pb2.RsaSsaPkcs1PublicKey( 353 version=0, 354 n=MODULUS_BYTES, 355 e=bytes.fromhex('010003'), 356 params=rsa_ssa_pkcs1_pb2.RsaSsaPkcs1Params( 357 hash_type=common_pb2.HashType.SHA256 358 ), 359 ) 360 keyset = public_key_to_keyset(key, tink_pb2.OutputPrefixType.TINK) 361 with self.assertRaises(tink.TinkError): 362 testing_servers.remote_primitive( 363 'go', keyset.SerializeToString(), signature.PublicKeyVerify 364 ) 365 366 def test_java_accepts_large_e(self): 367 """See also b/274605582.""" 368 key = rsa_ssa_pkcs1_pb2.RsaSsaPkcs1PublicKey( 369 version=0, 370 n=MODULUS_BYTES, 371 # 2^32 + 1 372 e=bytes.fromhex('0100000001'), 373 params=rsa_ssa_pkcs1_pb2.RsaSsaPkcs1Params( 374 hash_type=common_pb2.HashType.SHA256 375 ), 376 ) 377 keyset = public_key_to_keyset(key, tink_pb2.OutputPrefixType.TINK) 378 testing_servers.remote_primitive( 379 'java', keyset.SerializeToString(), signature.PublicKeyVerify 380 ) 381 382 383if __name__ == '__main__': 384 absltest.main() 385