1 // Copyright 2022 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 // 15 //////////////////////////////////////////////////////////////////////////////// 16 17 package com.google.crypto.tink.signature; 18 19 import static com.google.common.truth.Truth.assertThat; 20 import static org.junit.Assert.assertThrows; 21 22 import com.google.crypto.tink.internal.KeyTester; 23 import com.google.crypto.tink.subtle.Hex; 24 import com.google.crypto.tink.util.Bytes; 25 import java.math.BigInteger; 26 import java.security.GeneralSecurityException; 27 import org.junit.Test; 28 import org.junit.runner.RunWith; 29 import org.junit.runners.JUnit4; 30 31 @RunWith(JUnit4.class) 32 public final class RsaSsaPssPublicKeyTest { 33 34 // Test vector from 35 // https://github.com/google/wycheproof/blob/master/testvectors/rsa_pss_2048_sha256_mgf1_32_test.json 36 static final BigInteger MODULUS = 37 new BigInteger( 38 "00a2b451a07d0aa5f96e455671513550514a8a5b462ebef717094fa1fee82224e637f9746d3f7cafd31878d8" 39 + "0325b6ef5a1700f65903b469429e89d6eac8845097b5ab393189db92512ed8a7711a1253facd20f79c" 40 + "15e8247f3d3e42e46e48c98e254a2fe9765313a03eff8f17e1a029397a1fa26a8dce26f490ed812996" 41 + "15d9814c22da610428e09c7d9658594266f5c021d0fceca08d945a12be82de4d1ece6b4c03145b5d34" 42 + "95d4ed5411eb878daf05fd7afc3e09ada0f1126422f590975a1969816f48698bcbba1b4d9cae79d460" 43 + "d8f9f85e7975005d9bc22c4e5ac0f7c1a45d12569a62807d3b9a02e5a530e773066f453d1f5b4c2e9c" 44 + "f7820283f742b9d5", 45 16); 46 static final BigInteger EXPONENT = BigInteger.valueOf(65537); 47 48 @Test buildNoPrefixVariantAndGetProperties()49 public void buildNoPrefixVariantAndGetProperties() throws Exception { 50 RsaSsaPssParameters parameters = 51 RsaSsaPssParameters.builder() 52 .setModulusSizeBits(2048) 53 .setPublicExponent(EXPONENT) 54 .setSigHashType(RsaSsaPssParameters.HashType.SHA256) 55 .setMgf1HashType(RsaSsaPssParameters.HashType.SHA256) 56 .setVariant(RsaSsaPssParameters.Variant.NO_PREFIX) 57 .setSaltLengthBytes(32) 58 .build(); 59 assertThat(parameters.hasIdRequirement()).isFalse(); 60 RsaSsaPssPublicKey key = 61 RsaSsaPssPublicKey.builder().setParameters(parameters).setModulus(MODULUS).build(); 62 assertThat(key.getParameters()).isEqualTo(parameters); 63 assertThat(key.getModulus()).isEqualTo(MODULUS); 64 assertThat(key.getOutputPrefix()).isEqualTo(Bytes.copyFrom(new byte[] {})); 65 assertThat(key.getIdRequirementOrNull()).isNull(); 66 } 67 68 @Test buildTinkVariantAndGetProperties()69 public void buildTinkVariantAndGetProperties() throws Exception { 70 RsaSsaPssParameters parameters = 71 RsaSsaPssParameters.builder() 72 .setModulusSizeBits(2048) 73 .setPublicExponent(EXPONENT) 74 .setSigHashType(RsaSsaPssParameters.HashType.SHA256) 75 .setMgf1HashType(RsaSsaPssParameters.HashType.SHA256) 76 .setVariant(RsaSsaPssParameters.Variant.TINK) 77 .setSaltLengthBytes(32) 78 .build(); 79 assertThat(parameters.hasIdRequirement()).isTrue(); 80 RsaSsaPssPublicKey key = 81 RsaSsaPssPublicKey.builder() 82 .setParameters(parameters) 83 .setModulus(MODULUS) 84 .setIdRequirement(0x66AABBCC) 85 .build(); 86 assertThat(key.getParameters()).isEqualTo(parameters); 87 assertThat(key.getModulus()).isEqualTo(MODULUS); 88 assertThat(key.getOutputPrefix()).isEqualTo(Bytes.copyFrom(Hex.decode("0166AABBCC"))); 89 assertThat(key.getIdRequirementOrNull()).isEqualTo(0x66AABBCC); 90 } 91 92 @Test buildLegacyVariantAndGetProperties()93 public void buildLegacyVariantAndGetProperties() throws Exception { 94 RsaSsaPssParameters parameters = 95 RsaSsaPssParameters.builder() 96 .setModulusSizeBits(2048) 97 .setPublicExponent(EXPONENT) 98 .setSigHashType(RsaSsaPssParameters.HashType.SHA256) 99 .setMgf1HashType(RsaSsaPssParameters.HashType.SHA256) 100 .setVariant(RsaSsaPssParameters.Variant.LEGACY) 101 .setSaltLengthBytes(32) 102 .build(); 103 assertThat(parameters.hasIdRequirement()).isTrue(); 104 RsaSsaPssPublicKey key = 105 RsaSsaPssPublicKey.builder() 106 .setParameters(parameters) 107 .setModulus(MODULUS) 108 .setIdRequirement(0x66AABBCC) 109 .build(); 110 assertThat(key.getParameters()).isEqualTo(parameters); 111 assertThat(key.getModulus()).isEqualTo(MODULUS); 112 assertThat(key.getOutputPrefix()).isEqualTo(Bytes.copyFrom(Hex.decode("0066AABBCC"))); 113 assertThat(key.getIdRequirementOrNull()).isEqualTo(0x66AABBCC); 114 } 115 116 @Test buildCrunchyVariantAndGetProperties()117 public void buildCrunchyVariantAndGetProperties() throws Exception { 118 RsaSsaPssParameters parameters = 119 RsaSsaPssParameters.builder() 120 .setModulusSizeBits(2048) 121 .setPublicExponent(EXPONENT) 122 .setSigHashType(RsaSsaPssParameters.HashType.SHA256) 123 .setMgf1HashType(RsaSsaPssParameters.HashType.SHA256) 124 .setVariant(RsaSsaPssParameters.Variant.CRUNCHY) 125 .setSaltLengthBytes(32) 126 .build(); 127 assertThat(parameters.hasIdRequirement()).isTrue(); 128 RsaSsaPssPublicKey key = 129 RsaSsaPssPublicKey.builder() 130 .setParameters(parameters) 131 .setModulus(MODULUS) 132 .setIdRequirement(0x66AABBCC) 133 .build(); 134 assertThat(key.getParameters()).isEqualTo(parameters); 135 assertThat(key.getModulus()).isEqualTo(MODULUS); 136 assertThat(key.getOutputPrefix()).isEqualTo(Bytes.copyFrom(Hex.decode("0066AABBCC"))); 137 assertThat(key.getIdRequirementOrNull()).isEqualTo(0x66AABBCC); 138 } 139 140 @Test emptyBuild_fails()141 public void emptyBuild_fails() throws Exception { 142 assertThrows(GeneralSecurityException.class, () -> RsaSsaPssPublicKey.builder().build()); 143 } 144 145 @Test buildWithoutParameters_fails()146 public void buildWithoutParameters_fails() throws Exception { 147 assertThrows( 148 GeneralSecurityException.class, 149 () -> RsaSsaPssPublicKey.builder().setModulus(MODULUS).build()); 150 } 151 152 @Test buildWithoutPublicPoint_fails()153 public void buildWithoutPublicPoint_fails() throws Exception { 154 RsaSsaPssParameters parameters = 155 RsaSsaPssParameters.builder() 156 .setModulusSizeBits(2048) 157 .setPublicExponent(EXPONENT) 158 .setSigHashType(RsaSsaPssParameters.HashType.SHA256) 159 .setMgf1HashType(RsaSsaPssParameters.HashType.SHA256) 160 .setVariant(RsaSsaPssParameters.Variant.NO_PREFIX) 161 .setSaltLengthBytes(32) 162 .build(); 163 assertThrows( 164 GeneralSecurityException.class, 165 () -> RsaSsaPssPublicKey.builder().setParameters(parameters).build()); 166 } 167 168 @Test parametersRequireIdButIdIsNotSetInBuild_fails()169 public void parametersRequireIdButIdIsNotSetInBuild_fails() throws Exception { 170 RsaSsaPssParameters parametersWithIdRequirement = 171 RsaSsaPssParameters.builder() 172 .setModulusSizeBits(2048) 173 .setPublicExponent(EXPONENT) 174 .setSigHashType(RsaSsaPssParameters.HashType.SHA256) 175 .setMgf1HashType(RsaSsaPssParameters.HashType.SHA256) 176 .setVariant(RsaSsaPssParameters.Variant.TINK) 177 .setSaltLengthBytes(32) 178 .build(); 179 assertThat(parametersWithIdRequirement.hasIdRequirement()).isTrue(); 180 assertThrows( 181 GeneralSecurityException.class, 182 () -> 183 RsaSsaPssPublicKey.builder() 184 .setParameters(parametersWithIdRequirement) 185 .setModulus(MODULUS) 186 .build()); 187 } 188 189 @Test parametersDoesNotRequireIdButIdIsSetInBuild_fails()190 public void parametersDoesNotRequireIdButIdIsSetInBuild_fails() throws Exception { 191 RsaSsaPssParameters parametersWithoutIdRequirement = 192 RsaSsaPssParameters.builder() 193 .setModulusSizeBits(2048) 194 .setPublicExponent(EXPONENT) 195 .setSigHashType(RsaSsaPssParameters.HashType.SHA256) 196 .setMgf1HashType(RsaSsaPssParameters.HashType.SHA256) 197 .setVariant(RsaSsaPssParameters.Variant.NO_PREFIX) 198 .setSaltLengthBytes(32) 199 .build(); 200 assertThat(parametersWithoutIdRequirement.hasIdRequirement()).isFalse(); 201 assertThrows( 202 GeneralSecurityException.class, 203 () -> 204 RsaSsaPssPublicKey.builder() 205 .setParameters(parametersWithoutIdRequirement) 206 .setModulus(MODULUS) 207 .setIdRequirement(0x66AABBCC) 208 .build()); 209 } 210 211 @Test modulusSizeIsValidated()212 public void modulusSizeIsValidated() throws Exception { 213 RsaSsaPssParameters parameters = 214 RsaSsaPssParameters.builder() 215 .setModulusSizeBits(3456) 216 .setPublicExponent(EXPONENT) 217 .setSigHashType(RsaSsaPssParameters.HashType.SHA256) 218 .setMgf1HashType(RsaSsaPssParameters.HashType.SHA256) 219 .setVariant(RsaSsaPssParameters.Variant.NO_PREFIX) 220 .setSaltLengthBytes(32) 221 .build(); 222 // Modulus between 2^3455 and 2^3456 are valid. 223 BigInteger tooSmall = BigInteger.valueOf(2).pow(3455).subtract(BigInteger.ONE); 224 BigInteger smallest = BigInteger.valueOf(2).pow(3455).add(BigInteger.ONE); 225 BigInteger biggest = BigInteger.valueOf(2).pow(3456).subtract(BigInteger.ONE); 226 BigInteger tooBig = BigInteger.valueOf(2).pow(3456).add(BigInteger.ONE); 227 assertThrows( 228 GeneralSecurityException.class, 229 () -> RsaSsaPssPublicKey.builder().setParameters(parameters).setModulus(tooSmall).build()); 230 RsaSsaPssPublicKey publicKeyWithSmallestModulus = 231 RsaSsaPssPublicKey.builder().setParameters(parameters).setModulus(smallest).build(); 232 assertThat(publicKeyWithSmallestModulus.getModulus()).isEqualTo(smallest); 233 RsaSsaPssPublicKey publicKeyWithBiggestModulus = 234 RsaSsaPssPublicKey.builder().setParameters(parameters).setModulus(biggest).build(); 235 assertThat(publicKeyWithBiggestModulus.getModulus()).isEqualTo(biggest); 236 assertThrows( 237 GeneralSecurityException.class, 238 () -> RsaSsaPssPublicKey.builder().setParameters(parameters).setModulus(tooBig).build()); 239 } 240 241 @Test testEqualities()242 public void testEqualities() throws Exception { 243 RsaSsaPssParameters noPrefixParameters = 244 RsaSsaPssParameters.builder() 245 .setModulusSizeBits(2048) 246 .setPublicExponent(EXPONENT) 247 .setSigHashType(RsaSsaPssParameters.HashType.SHA256) 248 .setMgf1HashType(RsaSsaPssParameters.HashType.SHA256) 249 .setVariant(RsaSsaPssParameters.Variant.NO_PREFIX) 250 .setSaltLengthBytes(32) 251 .build(); 252 RsaSsaPssParameters noPrefixParametersCopy = 253 RsaSsaPssParameters.builder() 254 .setModulusSizeBits(2048) 255 .setPublicExponent(EXPONENT) 256 .setSigHashType(RsaSsaPssParameters.HashType.SHA256) 257 .setMgf1HashType(RsaSsaPssParameters.HashType.SHA256) 258 .setVariant(RsaSsaPssParameters.Variant.NO_PREFIX) 259 .setSaltLengthBytes(32) 260 .build(); 261 RsaSsaPssParameters tinkPrefixParameters = 262 RsaSsaPssParameters.builder() 263 .setModulusSizeBits(2048) 264 .setPublicExponent(EXPONENT) 265 .setSigHashType(RsaSsaPssParameters.HashType.SHA256) 266 .setMgf1HashType(RsaSsaPssParameters.HashType.SHA256) 267 .setVariant(RsaSsaPssParameters.Variant.TINK) 268 .setSaltLengthBytes(32) 269 .build(); 270 RsaSsaPssParameters legacyPrefixParameters = 271 RsaSsaPssParameters.builder() 272 .setModulusSizeBits(2048) 273 .setPublicExponent(EXPONENT) 274 .setSigHashType(RsaSsaPssParameters.HashType.SHA256) 275 .setMgf1HashType(RsaSsaPssParameters.HashType.SHA256) 276 .setVariant(RsaSsaPssParameters.Variant.LEGACY) 277 .setSaltLengthBytes(32) 278 .build(); 279 RsaSsaPssParameters crunchyPrefixParameters = 280 RsaSsaPssParameters.builder() 281 .setModulusSizeBits(2048) 282 .setPublicExponent(EXPONENT) 283 .setSigHashType(RsaSsaPssParameters.HashType.SHA256) 284 .setMgf1HashType(RsaSsaPssParameters.HashType.SHA256) 285 .setVariant(RsaSsaPssParameters.Variant.CRUNCHY) 286 .setSaltLengthBytes(32) 287 .build(); 288 RsaSsaPssParameters noPrefixParametersExponent65539 = 289 RsaSsaPssParameters.builder() 290 .setModulusSizeBits(2048) 291 .setPublicExponent(BigInteger.valueOf(65539)) 292 .setSigHashType(RsaSsaPssParameters.HashType.SHA256) 293 .setMgf1HashType(RsaSsaPssParameters.HashType.SHA256) 294 .setVariant(RsaSsaPssParameters.Variant.NO_PREFIX) 295 .setSaltLengthBytes(32) 296 .build(); 297 RsaSsaPssParameters noPrefixParametersSha512 = 298 RsaSsaPssParameters.builder() 299 .setModulusSizeBits(2048) 300 .setPublicExponent(EXPONENT) 301 .setSigHashType(RsaSsaPssParameters.HashType.SHA512) 302 .setMgf1HashType(RsaSsaPssParameters.HashType.SHA512) 303 .setVariant(RsaSsaPssParameters.Variant.NO_PREFIX) 304 .setSaltLengthBytes(32) 305 .build(); 306 new KeyTester() 307 .addEqualityGroup( 308 "No prefix, P256", 309 RsaSsaPssPublicKey.builder() 310 .setParameters(noPrefixParameters) 311 .setModulus(MODULUS) 312 .build(), 313 // the same key built twice must be equal 314 RsaSsaPssPublicKey.builder() 315 .setParameters(noPrefixParametersCopy) 316 .setModulus(MODULUS) 317 .build(), 318 // setting id requirement to null is equal to not setting it 319 RsaSsaPssPublicKey.builder() 320 .setParameters(noPrefixParameters) 321 .setModulus(MODULUS) 322 .setIdRequirement(null) 323 .build()) 324 // This group checks that keys with different key bytes are not equal 325 .addEqualityGroup( 326 "No prefix, different modulus", 327 RsaSsaPssPublicKey.builder() 328 .setParameters(noPrefixParameters) 329 .setModulus(MODULUS.add(BigInteger.ONE)) 330 .build()) 331 // These groups checks that keys with different parameters are not equal 332 .addEqualityGroup( 333 "No prefix, e=65539", 334 RsaSsaPssPublicKey.builder() 335 .setParameters(noPrefixParametersExponent65539) 336 .setModulus(MODULUS) 337 .build()) 338 .addEqualityGroup( 339 "No prefix, SHA512", 340 RsaSsaPssPublicKey.builder() 341 .setParameters(noPrefixParametersSha512) 342 .setModulus(MODULUS) 343 .build()) 344 .addEqualityGroup( 345 "Tink with key id 1907", 346 RsaSsaPssPublicKey.builder() 347 .setParameters(tinkPrefixParameters) 348 .setModulus(MODULUS) 349 .setIdRequirement(1907) 350 .build()) 351 // This group checks that keys with different key ids are not equal 352 .addEqualityGroup( 353 "Tink with key id 1908", 354 RsaSsaPssPublicKey.builder() 355 .setParameters(tinkPrefixParameters) 356 .setModulus(MODULUS) 357 .setIdRequirement(1908) 358 .build()) 359 // These 2 groups check that keys with different output prefix types are not equal 360 .addEqualityGroup( 361 "Legacy with key id 1907", 362 RsaSsaPssPublicKey.builder() 363 .setParameters(legacyPrefixParameters) 364 .setModulus(MODULUS) 365 .setIdRequirement(1907) 366 .build()) 367 .addEqualityGroup( 368 "Crunchy with key id 1907", 369 RsaSsaPssPublicKey.builder() 370 .setParameters(crunchyPrefixParameters) 371 .setModulus(MODULUS) 372 .setIdRequirement(1907) 373 .build()) 374 .doTests(); 375 } 376 } 377