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 // 15 //////////////////////////////////////////////////////////////////////////////// 16 17 package com.google.crypto.tink.jwt; 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.Base64; 24 import java.math.BigInteger; 25 import java.security.GeneralSecurityException; 26 import java.util.Optional; 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 JwtRsaSsaPkcs1PublicKeyTest { 33 34 // Test vector from https://datatracker.ietf.org/doc/html/rfc7515#appendix-A.2 35 static final BigInteger MODULUS = 36 new BigInteger( 37 1, 38 Base64.urlSafeDecode( 39 "ofgWCuLjybRlzo0tZWJjNiuSfb4p4fAkd_wWJcyQoTbji9k0l8W26mPddx" 40 + "HmfHQp-Vaw-4qPCJrcS2mJPMEzP1Pt0Bm4d4QlL-yRT-SFd2lZS-pCgNMs" 41 + "D1W_YpRPEwOWvG6b32690r2jZ47soMZo9wGzjb_7OMg0LOL-bSf63kpaSH" 42 + "SXndS5z5rexMdbBYUsLA9e-KXBdQOS-UTo7WTBEMa2R2CapHg665xsmtdV" 43 + "MTBQY4uDZlxvb3qCo5ZwKh9kG4LT6_I5IhlJH7aGhyxXFvUK-DWNmoudF8" 44 + "NAco9_h9iaGNj8q2ethFkMLs91kzk2PAcDTW9gb54h4FRWyuXpoQ")); 45 46 @Test build_kidStrategyIgnored_hasExpectedValues()47 public void build_kidStrategyIgnored_hasExpectedValues() throws Exception { 48 JwtRsaSsaPkcs1Parameters parameters = 49 JwtRsaSsaPkcs1Parameters.builder() 50 .setModulusSizeBits(2048) 51 .setPublicExponent(JwtRsaSsaPkcs1Parameters.F4) 52 .setKidStrategy(JwtRsaSsaPkcs1Parameters.KidStrategy.IGNORED) 53 .setAlgorithm(JwtRsaSsaPkcs1Parameters.Algorithm.RS256) 54 .build(); 55 JwtRsaSsaPkcs1PublicKey key = 56 JwtRsaSsaPkcs1PublicKey.builder().setParameters(parameters).setModulus(MODULUS).build(); 57 assertThat(key.getParameters()).isEqualTo(parameters); 58 assertThat(key.getModulus()).isEqualTo(MODULUS); 59 assertThat(key.getKid()).isEqualTo(Optional.empty()); 60 assertThat(key.getIdRequirementOrNull()).isNull(); 61 } 62 63 @Test build_kidStrategyIgnored_setCustomKid_fails()64 public void build_kidStrategyIgnored_setCustomKid_fails() throws Exception { 65 JwtRsaSsaPkcs1Parameters parameters = 66 JwtRsaSsaPkcs1Parameters.builder() 67 .setModulusSizeBits(2048) 68 .setPublicExponent(JwtRsaSsaPkcs1Parameters.F4) 69 .setKidStrategy(JwtRsaSsaPkcs1Parameters.KidStrategy.IGNORED) 70 .setAlgorithm(JwtRsaSsaPkcs1Parameters.Algorithm.RS256) 71 .build(); 72 JwtRsaSsaPkcs1PublicKey.Builder builder = 73 JwtRsaSsaPkcs1PublicKey.builder() 74 .setParameters(parameters) 75 .setModulus(MODULUS) 76 .setCustomKid("customKid23"); 77 assertThrows(GeneralSecurityException.class, builder::build); 78 } 79 80 @Test build_kidStrategyIgnored_setIdRequirement_fails()81 public void build_kidStrategyIgnored_setIdRequirement_fails() throws Exception { 82 JwtRsaSsaPkcs1Parameters parameters = 83 JwtRsaSsaPkcs1Parameters.builder() 84 .setModulusSizeBits(2048) 85 .setPublicExponent(JwtRsaSsaPkcs1Parameters.F4) 86 .setKidStrategy(JwtRsaSsaPkcs1Parameters.KidStrategy.IGNORED) 87 .setAlgorithm(JwtRsaSsaPkcs1Parameters.Algorithm.RS256) 88 .build(); 89 JwtRsaSsaPkcs1PublicKey.Builder builder = 90 JwtRsaSsaPkcs1PublicKey.builder() 91 .setParameters(parameters) 92 .setIdRequirement(123) 93 .setModulus(MODULUS); 94 assertThrows(GeneralSecurityException.class, builder::build); 95 } 96 97 @Test build_kidStrategyCustom_hasExpectedValues()98 public void build_kidStrategyCustom_hasExpectedValues() throws Exception { 99 JwtRsaSsaPkcs1Parameters parameters = 100 JwtRsaSsaPkcs1Parameters.builder() 101 .setModulusSizeBits(2048) 102 .setPublicExponent(JwtRsaSsaPkcs1Parameters.F4) 103 .setKidStrategy(JwtRsaSsaPkcs1Parameters.KidStrategy.CUSTOM) 104 .setAlgorithm(JwtRsaSsaPkcs1Parameters.Algorithm.RS256) 105 .build(); 106 JwtRsaSsaPkcs1PublicKey key = 107 JwtRsaSsaPkcs1PublicKey.builder() 108 .setParameters(parameters) 109 .setModulus(MODULUS) 110 .setCustomKid("customKid777") 111 .build(); 112 assertThat(key.getParameters()).isEqualTo(parameters); 113 assertThat(key.getModulus()).isEqualTo(MODULUS); 114 assertThat(key.getKid().get()).isEqualTo("customKid777"); 115 assertThat(key.getIdRequirementOrNull()).isNull(); 116 } 117 118 @Test build_kidStrategyCustom_setIdRequirement_fails()119 public void build_kidStrategyCustom_setIdRequirement_fails() throws Exception { 120 JwtRsaSsaPkcs1Parameters parameters = 121 JwtRsaSsaPkcs1Parameters.builder() 122 .setModulusSizeBits(2048) 123 .setPublicExponent(JwtRsaSsaPkcs1Parameters.F4) 124 .setKidStrategy(JwtRsaSsaPkcs1Parameters.KidStrategy.CUSTOM) 125 .setAlgorithm(JwtRsaSsaPkcs1Parameters.Algorithm.RS256) 126 .build(); 127 JwtRsaSsaPkcs1PublicKey.Builder builder = 128 JwtRsaSsaPkcs1PublicKey.builder() 129 .setParameters(parameters) 130 .setIdRequirement(123) 131 .setCustomKid("customKid777") 132 .setModulus(MODULUS); 133 assertThrows(GeneralSecurityException.class, builder::build); 134 } 135 136 @Test buildKidStrategyCustom_missingCustomKid_fails()137 public void buildKidStrategyCustom_missingCustomKid_fails() throws Exception { 138 JwtRsaSsaPkcs1Parameters parameters = 139 JwtRsaSsaPkcs1Parameters.builder() 140 .setModulusSizeBits(2048) 141 .setPublicExponent(JwtRsaSsaPkcs1Parameters.F4) 142 .setKidStrategy(JwtRsaSsaPkcs1Parameters.KidStrategy.CUSTOM) 143 .setAlgorithm(JwtRsaSsaPkcs1Parameters.Algorithm.RS256) 144 .build(); 145 JwtRsaSsaPkcs1PublicKey.Builder builder = 146 JwtRsaSsaPkcs1PublicKey.builder().setParameters(parameters).setModulus(MODULUS); 147 assertThrows(GeneralSecurityException.class, builder::build); 148 } 149 150 @Test build_kidStrategyBase64_getProperties_succeeds()151 public void build_kidStrategyBase64_getProperties_succeeds() throws Exception { 152 JwtRsaSsaPkcs1Parameters parameters = 153 JwtRsaSsaPkcs1Parameters.builder() 154 .setModulusSizeBits(2048) 155 .setPublicExponent(JwtRsaSsaPkcs1Parameters.F4) 156 .setKidStrategy(JwtRsaSsaPkcs1Parameters.KidStrategy.BASE64_ENCODED_KEY_ID) 157 .setAlgorithm(JwtRsaSsaPkcs1Parameters.Algorithm.RS256) 158 .build(); 159 JwtRsaSsaPkcs1PublicKey key = 160 JwtRsaSsaPkcs1PublicKey.builder() 161 .setParameters(parameters) 162 .setModulus(MODULUS) 163 .setIdRequirement(0x1ac6a944) 164 .build(); 165 assertThat(key.getParameters()).isEqualTo(parameters); 166 assertThat(key.getIdRequirementOrNull()).isEqualTo(0x1ac6a944); 167 // See JwtFormatTest.getKidFromTinkOutputPrefixType_success 168 assertThat(key.getKid()).isEqualTo(Optional.of("GsapRA")); 169 } 170 171 @Test build_kidStrategyBase64_noIdRequirement_throws()172 public void build_kidStrategyBase64_noIdRequirement_throws() throws Exception { 173 JwtRsaSsaPkcs1Parameters parameters = 174 JwtRsaSsaPkcs1Parameters.builder() 175 .setModulusSizeBits(2048) 176 .setPublicExponent(JwtRsaSsaPkcs1Parameters.F4) 177 .setKidStrategy(JwtRsaSsaPkcs1Parameters.KidStrategy.BASE64_ENCODED_KEY_ID) 178 .setAlgorithm(JwtRsaSsaPkcs1Parameters.Algorithm.RS256) 179 .build(); 180 JwtRsaSsaPkcs1PublicKey.Builder builder = 181 JwtRsaSsaPkcs1PublicKey.builder().setParameters(parameters).setModulus(MODULUS); 182 assertThrows(GeneralSecurityException.class, builder::build); 183 } 184 185 @Test build_kidStrategyBase64_setCustomKid_throws()186 public void build_kidStrategyBase64_setCustomKid_throws() throws Exception { 187 JwtRsaSsaPkcs1Parameters parameters = 188 JwtRsaSsaPkcs1Parameters.builder() 189 .setModulusSizeBits(2048) 190 .setPublicExponent(JwtRsaSsaPkcs1Parameters.F4) 191 .setKidStrategy(JwtRsaSsaPkcs1Parameters.KidStrategy.BASE64_ENCODED_KEY_ID) 192 .setAlgorithm(JwtRsaSsaPkcs1Parameters.Algorithm.RS256) 193 .build(); 194 JwtRsaSsaPkcs1PublicKey.Builder builder = 195 JwtRsaSsaPkcs1PublicKey.builder() 196 .setParameters(parameters) 197 .setIdRequirement(0x89abcdef) 198 .setCustomKid("customKid") 199 .setModulus(MODULUS); 200 assertThrows(GeneralSecurityException.class, builder::build); 201 } 202 203 @Test emptyBuild_fails()204 public void emptyBuild_fails() throws Exception { 205 assertThrows(GeneralSecurityException.class, () -> JwtRsaSsaPkcs1PublicKey.builder().build()); 206 } 207 208 @Test buildWithoutParameters_fails()209 public void buildWithoutParameters_fails() throws Exception { 210 assertThrows( 211 GeneralSecurityException.class, 212 () -> JwtRsaSsaPkcs1PublicKey.builder().setModulus(MODULUS).build()); 213 } 214 215 @Test build_withoutModulus_fails()216 public void build_withoutModulus_fails() throws Exception { 217 JwtRsaSsaPkcs1Parameters parameters = 218 JwtRsaSsaPkcs1Parameters.builder() 219 .setModulusSizeBits(2048) 220 .setPublicExponent(JwtRsaSsaPkcs1Parameters.F4) 221 .setKidStrategy(JwtRsaSsaPkcs1Parameters.KidStrategy.IGNORED) 222 .setAlgorithm(JwtRsaSsaPkcs1Parameters.Algorithm.RS256) 223 .build(); 224 assertThrows( 225 GeneralSecurityException.class, 226 () -> JwtRsaSsaPkcs1PublicKey.builder().setParameters(parameters).build()); 227 } 228 229 @Test build_invalidModulusSize_fails()230 public void build_invalidModulusSize_fails() throws Exception { 231 JwtRsaSsaPkcs1Parameters parameters = 232 JwtRsaSsaPkcs1Parameters.builder() 233 .setModulusSizeBits(3456) 234 .setPublicExponent(JwtRsaSsaPkcs1Parameters.F4) 235 .setKidStrategy(JwtRsaSsaPkcs1Parameters.KidStrategy.IGNORED) 236 .setAlgorithm(JwtRsaSsaPkcs1Parameters.Algorithm.RS256) 237 .build(); 238 239 // Modulus between 2^3455 and 2^3456 are valid. 240 BigInteger tooSmall = BigInteger.valueOf(2).pow(3455).subtract(BigInteger.ONE); 241 BigInteger tooBig = BigInteger.valueOf(2).pow(3456).add(BigInteger.ONE); 242 243 assertThrows( 244 GeneralSecurityException.class, 245 () -> 246 JwtRsaSsaPkcs1PublicKey.builder() 247 .setParameters(parameters) 248 .setModulus(tooSmall) 249 .build()); 250 assertThrows( 251 GeneralSecurityException.class, 252 () -> 253 JwtRsaSsaPkcs1PublicKey.builder().setParameters(parameters).setModulus(tooBig).build()); 254 } 255 256 @Test testEqualities()257 public void testEqualities() throws Exception { 258 JwtRsaSsaPkcs1Parameters kidStrategyIgnoredParameters = 259 JwtRsaSsaPkcs1Parameters.builder() 260 .setModulusSizeBits(2048) 261 .setPublicExponent(JwtRsaSsaPkcs1Parameters.F4) 262 .setKidStrategy(JwtRsaSsaPkcs1Parameters.KidStrategy.IGNORED) 263 .setAlgorithm(JwtRsaSsaPkcs1Parameters.Algorithm.RS256) 264 .build(); 265 JwtRsaSsaPkcs1Parameters kidStrategyIgnoredParametersCopy = 266 JwtRsaSsaPkcs1Parameters.builder() 267 .setModulusSizeBits(2048) 268 .setPublicExponent(JwtRsaSsaPkcs1Parameters.F4) 269 .setKidStrategy(JwtRsaSsaPkcs1Parameters.KidStrategy.IGNORED) 270 .setAlgorithm(JwtRsaSsaPkcs1Parameters.Algorithm.RS256) 271 .build(); 272 273 JwtRsaSsaPkcs1Parameters kidStrategyCustomParameters = 274 JwtRsaSsaPkcs1Parameters.builder() 275 .setModulusSizeBits(2048) 276 .setPublicExponent(JwtRsaSsaPkcs1Parameters.F4) 277 .setKidStrategy(JwtRsaSsaPkcs1Parameters.KidStrategy.CUSTOM) 278 .setAlgorithm(JwtRsaSsaPkcs1Parameters.Algorithm.RS256) 279 .build(); 280 281 JwtRsaSsaPkcs1Parameters kidStrategyBase64Parameters = 282 JwtRsaSsaPkcs1Parameters.builder() 283 .setModulusSizeBits(2048) 284 .setPublicExponent(JwtRsaSsaPkcs1Parameters.F4) 285 .setKidStrategy(JwtRsaSsaPkcs1Parameters.KidStrategy.BASE64_ENCODED_KEY_ID) 286 .setAlgorithm(JwtRsaSsaPkcs1Parameters.Algorithm.RS256) 287 .build(); 288 289 new KeyTester() 290 .addEqualityGroup( 291 "KID Ignored, R256", 292 JwtRsaSsaPkcs1PublicKey.builder() 293 .setParameters(kidStrategyIgnoredParameters) 294 .setModulus(MODULUS) 295 .build(), 296 // the same key built twice must be equal 297 JwtRsaSsaPkcs1PublicKey.builder() 298 .setParameters(kidStrategyIgnoredParameters) 299 .setModulus(MODULUS) 300 .build(), 301 // the same key built with a copy of parameters must be equal 302 JwtRsaSsaPkcs1PublicKey.builder() 303 .setParameters(kidStrategyIgnoredParametersCopy) 304 .setModulus(MODULUS) 305 .build()) 306 // This group checks that keys with different key bytes are not equal 307 .addEqualityGroup( 308 "KID Ignored, different modulus", 309 JwtRsaSsaPkcs1PublicKey.builder() 310 .setParameters(kidStrategyIgnoredParameters) 311 .setModulus(MODULUS.add(BigInteger.ONE)) 312 .build()) 313 // These groups checks that keys with different customKid are not equal 314 .addEqualityGroup( 315 "KID Custom, customKid1", 316 JwtRsaSsaPkcs1PublicKey.builder() 317 .setParameters(kidStrategyCustomParameters) 318 .setModulus(MODULUS) 319 .setCustomKid("customKid1") 320 .build()) 321 .addEqualityGroup( 322 "KID Custom, customKid2", 323 JwtRsaSsaPkcs1PublicKey.builder() 324 .setParameters(kidStrategyCustomParameters) 325 .setModulus(MODULUS) 326 .setCustomKid("customKid2") 327 .build()) 328 // These groups checks that keys with different ID Requirements are not equal 329 .addEqualityGroup( 330 "Tink with key id 1907", 331 JwtRsaSsaPkcs1PublicKey.builder() 332 .setParameters(kidStrategyBase64Parameters) 333 .setModulus(MODULUS) 334 .setIdRequirement(1907) 335 .build()) 336 .addEqualityGroup( 337 "Tink with key id 1908", 338 JwtRsaSsaPkcs1PublicKey.builder() 339 .setParameters(kidStrategyBase64Parameters) 340 .setModulus(MODULUS) 341 .setIdRequirement(1908) 342 .build()) 343 .doTests(); 344 } 345 } 346