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 JwtRsaSsaPssPublicKeyTest { 33 34 // Test vector from https://www.rfc-editor.org/rfc/rfc7517#appendix-C.1 35 static final BigInteger MODULUS = 36 new BigInteger( 37 1, 38 Base64.urlSafeDecode( 39 "t6Q8PWSi1dkJj9hTP8hNYFlvadM7DflW9mWepOJhJ66w7nyoK1gPNqFMSQRy" 40 + "O125Gp-TEkodhWr0iujjHVx7BcV0llS4w5ACGgPrcAd6ZcSR0-Iqom-QFcNP" 41 + "8Sjg086MwoqQU_LYywlAGZ21WSdS_PERyGFiNnj3QQlO8Yns5jCtLCRwLHL0" 42 + "Pb1fEv45AuRIuUfVcPySBWYnDyGxvjYGDSM-AqWS9zIQ2ZilgT-GqUmipg0X" 43 + "OC0Cc20rgLe2ymLHjpHciCKVAbY5-L32-lSeZO-Os6U15_aXrk9Gw8cPUaX1" 44 + "_I8sLGuSiVdt3C_Fn2PZ3Z8i744FPFGGcG1qs2Wz-Q")); 45 46 @Test build_kidStrategyIgnored_hasExpectedValues()47 public void build_kidStrategyIgnored_hasExpectedValues() throws Exception { 48 JwtRsaSsaPssParameters parameters = 49 JwtRsaSsaPssParameters.builder() 50 .setModulusSizeBits(2048) 51 .setPublicExponent(JwtRsaSsaPssParameters.F4) 52 .setKidStrategy(JwtRsaSsaPssParameters.KidStrategy.IGNORED) 53 .setAlgorithm(JwtRsaSsaPssParameters.Algorithm.PS256) 54 .build(); 55 JwtRsaSsaPssPublicKey key = 56 JwtRsaSsaPssPublicKey.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 JwtRsaSsaPssParameters parameters = 66 JwtRsaSsaPssParameters.builder() 67 .setModulusSizeBits(2048) 68 .setPublicExponent(JwtRsaSsaPssParameters.F4) 69 .setKidStrategy(JwtRsaSsaPssParameters.KidStrategy.IGNORED) 70 .setAlgorithm(JwtRsaSsaPssParameters.Algorithm.PS256) 71 .build(); 72 JwtRsaSsaPssPublicKey.Builder builder = 73 JwtRsaSsaPssPublicKey.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 JwtRsaSsaPssParameters parameters = 83 JwtRsaSsaPssParameters.builder() 84 .setModulusSizeBits(2048) 85 .setPublicExponent(JwtRsaSsaPssParameters.F4) 86 .setKidStrategy(JwtRsaSsaPssParameters.KidStrategy.IGNORED) 87 .setAlgorithm(JwtRsaSsaPssParameters.Algorithm.PS256) 88 .build(); 89 JwtRsaSsaPssPublicKey.Builder builder = 90 JwtRsaSsaPssPublicKey.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 JwtRsaSsaPssParameters parameters = 100 JwtRsaSsaPssParameters.builder() 101 .setModulusSizeBits(2048) 102 .setPublicExponent(JwtRsaSsaPssParameters.F4) 103 .setKidStrategy(JwtRsaSsaPssParameters.KidStrategy.CUSTOM) 104 .setAlgorithm(JwtRsaSsaPssParameters.Algorithm.PS256) 105 .build(); 106 JwtRsaSsaPssPublicKey key = 107 JwtRsaSsaPssPublicKey.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 JwtRsaSsaPssParameters parameters = 121 JwtRsaSsaPssParameters.builder() 122 .setModulusSizeBits(2048) 123 .setPublicExponent(JwtRsaSsaPssParameters.F4) 124 .setKidStrategy(JwtRsaSsaPssParameters.KidStrategy.CUSTOM) 125 .setAlgorithm(JwtRsaSsaPssParameters.Algorithm.PS256) 126 .build(); 127 JwtRsaSsaPssPublicKey.Builder builder = 128 JwtRsaSsaPssPublicKey.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 JwtRsaSsaPssParameters parameters = 139 JwtRsaSsaPssParameters.builder() 140 .setModulusSizeBits(2048) 141 .setPublicExponent(JwtRsaSsaPssParameters.F4) 142 .setKidStrategy(JwtRsaSsaPssParameters.KidStrategy.CUSTOM) 143 .setAlgorithm(JwtRsaSsaPssParameters.Algorithm.PS256) 144 .build(); 145 JwtRsaSsaPssPublicKey.Builder builder = 146 JwtRsaSsaPssPublicKey.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 JwtRsaSsaPssParameters parameters = 153 JwtRsaSsaPssParameters.builder() 154 .setModulusSizeBits(2048) 155 .setPublicExponent(JwtRsaSsaPssParameters.F4) 156 .setKidStrategy(JwtRsaSsaPssParameters.KidStrategy.BASE64_ENCODED_KEY_ID) 157 .setAlgorithm(JwtRsaSsaPssParameters.Algorithm.PS256) 158 .build(); 159 JwtRsaSsaPssPublicKey key = 160 JwtRsaSsaPssPublicKey.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 JwtRsaSsaPssParameters parameters = 174 JwtRsaSsaPssParameters.builder() 175 .setModulusSizeBits(2048) 176 .setPublicExponent(JwtRsaSsaPssParameters.F4) 177 .setKidStrategy(JwtRsaSsaPssParameters.KidStrategy.BASE64_ENCODED_KEY_ID) 178 .setAlgorithm(JwtRsaSsaPssParameters.Algorithm.PS256) 179 .build(); 180 JwtRsaSsaPssPublicKey.Builder builder = 181 JwtRsaSsaPssPublicKey.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 JwtRsaSsaPssParameters parameters = 188 JwtRsaSsaPssParameters.builder() 189 .setModulusSizeBits(2048) 190 .setPublicExponent(JwtRsaSsaPssParameters.F4) 191 .setKidStrategy(JwtRsaSsaPssParameters.KidStrategy.BASE64_ENCODED_KEY_ID) 192 .setAlgorithm(JwtRsaSsaPssParameters.Algorithm.PS256) 193 .build(); 194 JwtRsaSsaPssPublicKey.Builder builder = 195 JwtRsaSsaPssPublicKey.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, () -> JwtRsaSsaPssPublicKey.builder().build()); 206 } 207 208 @Test buildWithoutParameters_fails()209 public void buildWithoutParameters_fails() throws Exception { 210 assertThrows( 211 GeneralSecurityException.class, 212 () -> JwtRsaSsaPssPublicKey.builder().setModulus(MODULUS).build()); 213 } 214 215 @Test build_withoutModulus_fails()216 public void build_withoutModulus_fails() throws Exception { 217 JwtRsaSsaPssParameters parameters = 218 JwtRsaSsaPssParameters.builder() 219 .setModulusSizeBits(2048) 220 .setPublicExponent(JwtRsaSsaPssParameters.F4) 221 .setKidStrategy(JwtRsaSsaPssParameters.KidStrategy.IGNORED) 222 .setAlgorithm(JwtRsaSsaPssParameters.Algorithm.PS256) 223 .build(); 224 assertThrows( 225 GeneralSecurityException.class, 226 () -> JwtRsaSsaPssPublicKey.builder().setParameters(parameters).build()); 227 } 228 229 @Test build_invalidModulusSize_fails()230 public void build_invalidModulusSize_fails() throws Exception { 231 JwtRsaSsaPssParameters parameters = 232 JwtRsaSsaPssParameters.builder() 233 .setModulusSizeBits(3456) 234 .setPublicExponent(JwtRsaSsaPssParameters.F4) 235 .setKidStrategy(JwtRsaSsaPssParameters.KidStrategy.IGNORED) 236 .setAlgorithm(JwtRsaSsaPssParameters.Algorithm.PS256) 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 JwtRsaSsaPssPublicKey.builder().setParameters(parameters).setModulus(tooSmall).build()); 247 assertThrows( 248 GeneralSecurityException.class, 249 () -> JwtRsaSsaPssPublicKey.builder().setParameters(parameters).setModulus(tooBig).build()); 250 } 251 252 @Test testEqualities()253 public void testEqualities() throws Exception { 254 JwtRsaSsaPssParameters kidStrategyIgnoredParameters = 255 JwtRsaSsaPssParameters.builder() 256 .setModulusSizeBits(2048) 257 .setPublicExponent(JwtRsaSsaPssParameters.F4) 258 .setKidStrategy(JwtRsaSsaPssParameters.KidStrategy.IGNORED) 259 .setAlgorithm(JwtRsaSsaPssParameters.Algorithm.PS256) 260 .build(); 261 JwtRsaSsaPssParameters kidStrategyIgnoredParametersCopy = 262 JwtRsaSsaPssParameters.builder() 263 .setModulusSizeBits(2048) 264 .setPublicExponent(JwtRsaSsaPssParameters.F4) 265 .setKidStrategy(JwtRsaSsaPssParameters.KidStrategy.IGNORED) 266 .setAlgorithm(JwtRsaSsaPssParameters.Algorithm.PS256) 267 .build(); 268 269 JwtRsaSsaPssParameters kidStrategyCustomParameters = 270 JwtRsaSsaPssParameters.builder() 271 .setModulusSizeBits(2048) 272 .setPublicExponent(JwtRsaSsaPssParameters.F4) 273 .setKidStrategy(JwtRsaSsaPssParameters.KidStrategy.CUSTOM) 274 .setAlgorithm(JwtRsaSsaPssParameters.Algorithm.PS256) 275 .build(); 276 277 JwtRsaSsaPssParameters kidStrategyBase64Parameters = 278 JwtRsaSsaPssParameters.builder() 279 .setModulusSizeBits(2048) 280 .setPublicExponent(JwtRsaSsaPssParameters.F4) 281 .setKidStrategy(JwtRsaSsaPssParameters.KidStrategy.BASE64_ENCODED_KEY_ID) 282 .setAlgorithm(JwtRsaSsaPssParameters.Algorithm.PS256) 283 .build(); 284 285 new KeyTester() 286 .addEqualityGroup( 287 "KID Ignored, R256", 288 JwtRsaSsaPssPublicKey.builder() 289 .setParameters(kidStrategyIgnoredParameters) 290 .setModulus(MODULUS) 291 .build(), 292 // the same key built twice must be equal 293 JwtRsaSsaPssPublicKey.builder() 294 .setParameters(kidStrategyIgnoredParameters) 295 .setModulus(MODULUS) 296 .build(), 297 // the same key built with a copy of parameters must be equal 298 JwtRsaSsaPssPublicKey.builder() 299 .setParameters(kidStrategyIgnoredParametersCopy) 300 .setModulus(MODULUS) 301 .build()) 302 // This group checks that keys with different key bytes are not equal 303 .addEqualityGroup( 304 "KID Ignored, different modulus", 305 JwtRsaSsaPssPublicKey.builder() 306 .setParameters(kidStrategyIgnoredParameters) 307 .setModulus(MODULUS.add(BigInteger.ONE)) 308 .build()) 309 // These groups checks that keys with different customKid are not equal 310 .addEqualityGroup( 311 "KID Custom, customKid1", 312 JwtRsaSsaPssPublicKey.builder() 313 .setParameters(kidStrategyCustomParameters) 314 .setModulus(MODULUS) 315 .setCustomKid("customKid1") 316 .build()) 317 .addEqualityGroup( 318 "KID Custom, customKid2", 319 JwtRsaSsaPssPublicKey.builder() 320 .setParameters(kidStrategyCustomParameters) 321 .setModulus(MODULUS) 322 .setCustomKid("customKid2") 323 .build()) 324 // These groups checks that keys with different ID Requirements are not equal 325 .addEqualityGroup( 326 "Tink with key id 1907", 327 JwtRsaSsaPssPublicKey.builder() 328 .setParameters(kidStrategyBase64Parameters) 329 .setModulus(MODULUS) 330 .setIdRequirement(1907) 331 .build()) 332 .addEqualityGroup( 333 "Tink with key id 1908", 334 JwtRsaSsaPssPublicKey.builder() 335 .setParameters(kidStrategyBase64Parameters) 336 .setModulus(MODULUS) 337 .setIdRequirement(1908) 338 .build()) 339 .doTests(); 340 } 341 } 342