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