• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2020 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.InsecureSecretKeyAccess;
23 import com.google.crypto.tink.KeyTemplate;
24 import com.google.crypto.tink.KeyTemplates;
25 import com.google.crypto.tink.KeysetHandle;
26 import com.google.crypto.tink.KeysetManager;
27 import com.google.crypto.tink.RegistryConfiguration;
28 import com.google.crypto.tink.TinkProtoKeysetFormat;
29 import com.google.crypto.tink.internal.MonitoringAnnotations;
30 import com.google.crypto.tink.internal.MutableMonitoringRegistry;
31 import com.google.crypto.tink.internal.testing.FakeMonitoringClient;
32 import com.google.crypto.tink.proto.Keyset;
33 import com.google.crypto.tink.proto.OutputPrefixType;
34 import com.google.protobuf.ExtensionRegistryLite;
35 import java.security.GeneralSecurityException;
36 import java.time.Clock;
37 import java.time.Instant;
38 import java.time.temporal.ChronoUnit;
39 import java.util.List;
40 import org.junit.Before;
41 import org.junit.Test;
42 import org.junit.runner.RunWith;
43 import org.junit.runners.JUnit4;
44 
45 /** Tests for JwtMacWrapper. */
46 @RunWith(JUnit4.class)
47 public class JwtMacWrapperTest {
48 
49   @Before
setUp()50   public void setUp() throws GeneralSecurityException {
51     JwtMacConfig.register();
52   }
53 
54   @Test
test_wrapNoPrimary_throws()55   public void test_wrapNoPrimary_throws() throws Exception {
56     // The old KeysetManager API allows keysets without primary key.
57     // The KeysetHandle.Builder does not allow this and can't be used in this test.
58     KeyTemplate template = KeyTemplates.get("JWT_HS256");
59     KeysetManager manager = KeysetManager.withEmptyKeyset().add(template);
60     KeysetHandle handle = manager.getKeysetHandle();
61     assertThrows(
62         GeneralSecurityException.class,
63         () -> handle.getPrimitive(RegistryConfiguration.get(), JwtMac.class));
64   }
65 
66   @Test
test_wrapLegacy_throws()67   public void test_wrapLegacy_throws() throws Exception {
68     KeyTemplate rawTemplate = KeyTemplates.get("JWT_HS256_RAW");
69     // Convert the normal, raw template into a template with output prefix type LEGACY
70     KeysetHandle handle = KeysetHandle.generateNew(rawTemplate);
71     Keyset keyset =
72         Keyset.parseFrom(
73             TinkProtoKeysetFormat.serializeKeyset(handle, InsecureSecretKeyAccess.get()),
74             ExtensionRegistryLite.getEmptyRegistry());
75     Keyset.Builder legacyKeysetBuilder = keyset.toBuilder();
76     legacyKeysetBuilder.setKey(
77         0, legacyKeysetBuilder.getKey(0).toBuilder().setOutputPrefixType(OutputPrefixType.LEGACY));
78     KeysetHandle legacyHandle =
79         TinkProtoKeysetFormat.parseKeyset(
80             legacyKeysetBuilder.build().toByteArray(), InsecureSecretKeyAccess.get());
81     assertThrows(
82         GeneralSecurityException.class,
83         () -> legacyHandle.getPrimitive(RegistryConfiguration.get(), JwtMac.class));
84   }
85 
86   @Test
test_wrapSingleRawKey_works()87   public void test_wrapSingleRawKey_works() throws Exception {
88     KeyTemplate template = KeyTemplates.get("JWT_HS256_RAW");
89     KeysetHandle handle = KeysetHandle.generateNew(template);
90 
91     JwtMac jwtMac = handle.getPrimitive(RegistryConfiguration.get(), JwtMac.class);
92     RawJwt rawToken = RawJwt.newBuilder().setJwtId("id123").withoutExpiration().build();
93     String signedCompact = jwtMac.computeMacAndEncode(rawToken);
94     JwtValidator validator = JwtValidator.newBuilder().allowMissingExpiration().build();
95     VerifiedJwt verifiedToken = jwtMac.verifyMacAndDecode(signedCompact, validator);
96     assertThat(verifiedToken.getJwtId()).isEqualTo("id123");
97   }
98 
99   @Test
test_wrapSingleTinkKey_works()100   public void test_wrapSingleTinkKey_works() throws Exception {
101     KeyTemplate tinkTemplate = KeyTemplates.get("JWT_HS256");
102     KeysetHandle handle = KeysetHandle.generateNew(tinkTemplate);
103     JwtMac jwtMac = handle.getPrimitive(RegistryConfiguration.get(), JwtMac.class);
104     RawJwt rawJwt = RawJwt.newBuilder().setJwtId("id123").withoutExpiration().build();
105     String signedCompact = jwtMac.computeMacAndEncode(rawJwt);
106     JwtValidator validator = JwtValidator.newBuilder().allowMissingExpiration().build();
107     VerifiedJwt verifiedToken = jwtMac.verifyMacAndDecode(signedCompact, validator);
108     assertThat(verifiedToken.getJwtId()).isEqualTo("id123");
109   }
110 
111   @Test
test_wrapMultipleRawKeys()112   public void test_wrapMultipleRawKeys() throws Exception {
113     KeysetHandle oldHandle =
114         KeysetHandle.newBuilder()
115             .addEntry(
116                 KeysetHandle.generateEntryFromParametersName("JWT_HS256_RAW")
117                     .withRandomId()
118                     .makePrimary())
119             .build();
120     KeysetHandle newHandle =
121         KeysetHandle.newBuilder(oldHandle)
122             .addEntry(
123                 KeysetHandle.generateEntryFromParametersName("JWT_HS256_RAW")
124                     .withRandomId()
125                     .makePrimary())
126             .build();
127 
128     JwtMac oldJwtMac = oldHandle.getPrimitive(RegistryConfiguration.get(), JwtMac.class);
129     JwtMac newJwtMac = newHandle.getPrimitive(RegistryConfiguration.get(), JwtMac.class);
130 
131     RawJwt rawToken = RawJwt.newBuilder().setJwtId("jwtId").withoutExpiration().build();
132     String oldSignedCompact = oldJwtMac.computeMacAndEncode(rawToken);
133     String newSignedCompact = newJwtMac.computeMacAndEncode(rawToken);
134 
135     JwtValidator validator = JwtValidator.newBuilder().allowMissingExpiration().build();
136     assertThat(oldJwtMac.verifyMacAndDecode(oldSignedCompact, validator).getJwtId())
137         .isEqualTo("jwtId");
138     assertThat(newJwtMac.verifyMacAndDecode(oldSignedCompact, validator).getJwtId())
139         .isEqualTo("jwtId");
140     assertThat(newJwtMac.verifyMacAndDecode(newSignedCompact, validator).getJwtId())
141         .isEqualTo("jwtId");
142     assertThrows(
143         GeneralSecurityException.class,
144         () -> oldJwtMac.verifyMacAndDecode(newSignedCompact, validator));
145   }
146 
147   @Test
test_wrapMultipleTinkKeys()148   public void test_wrapMultipleTinkKeys() throws Exception {
149     KeysetHandle oldHandle =
150         KeysetHandle.newBuilder()
151             .addEntry(
152                 KeysetHandle.generateEntryFromParametersName("JWT_HS256")
153                     .withRandomId()
154                     .makePrimary())
155             .build();
156     KeysetHandle newHandle =
157         KeysetHandle.newBuilder(oldHandle)
158             .addEntry(
159                 KeysetHandle.generateEntryFromParametersName("JWT_HS256")
160                     .withRandomId()
161                     .makePrimary())
162             .build();
163 
164     JwtMac oldJwtMac = oldHandle.getPrimitive(RegistryConfiguration.get(), JwtMac.class);
165     JwtMac newJwtMac = newHandle.getPrimitive(RegistryConfiguration.get(), JwtMac.class);
166 
167     RawJwt rawToken = RawJwt.newBuilder().setJwtId("jwtId").withoutExpiration().build();
168     String oldSignedCompact = oldJwtMac.computeMacAndEncode(rawToken);
169     String newSignedCompact = newJwtMac.computeMacAndEncode(rawToken);
170 
171     JwtValidator validator = JwtValidator.newBuilder().allowMissingExpiration().build();
172     assertThat(oldJwtMac.verifyMacAndDecode(oldSignedCompact, validator).getJwtId())
173         .isEqualTo("jwtId");
174     assertThat(newJwtMac.verifyMacAndDecode(oldSignedCompact, validator).getJwtId())
175         .isEqualTo("jwtId");
176     assertThat(newJwtMac.verifyMacAndDecode(newSignedCompact, validator).getJwtId())
177         .isEqualTo("jwtId");
178     assertThrows(
179         GeneralSecurityException.class,
180         () -> oldJwtMac.verifyMacAndDecode(newSignedCompact, validator));
181   }
182 
183   @Test
wrongKey_throwsInvalidSignatureException()184   public void wrongKey_throwsInvalidSignatureException() throws Exception {
185     KeysetHandle keysetHandle = KeysetHandle.generateNew(KeyTemplates.get("JWT_HS256"));
186     JwtMac jwtMac = keysetHandle.getPrimitive(RegistryConfiguration.get(), JwtMac.class);
187     RawJwt rawJwt = RawJwt.newBuilder().withoutExpiration().build();
188     String compact = jwtMac.computeMacAndEncode(rawJwt);
189     JwtValidator validator = JwtValidator.newBuilder().allowMissingExpiration().build();
190 
191     KeysetHandle wrongKeysetHandle = KeysetHandle.generateNew(KeyTemplates.get("JWT_HS256"));
192     JwtMac wrongJwtMac = wrongKeysetHandle.getPrimitive(RegistryConfiguration.get(), JwtMac.class);
193     assertThrows(
194         GeneralSecurityException.class, () -> wrongJwtMac.verifyMacAndDecode(compact, validator));
195   }
196 
197   @Test
wrongIssuer_throwsInvalidException()198   public void wrongIssuer_throwsInvalidException() throws Exception {
199     KeysetHandle keysetHandle = KeysetHandle.generateNew(KeyTemplates.get("JWT_HS256"));
200     JwtMac jwtMac = keysetHandle.getPrimitive(RegistryConfiguration.get(), JwtMac.class);
201     RawJwt rawJwt = RawJwt.newBuilder().setIssuer("Justus").withoutExpiration().build();
202     String compact = jwtMac.computeMacAndEncode(rawJwt);
203     JwtValidator validator =
204         JwtValidator.newBuilder().allowMissingExpiration().expectIssuer("Peter").build();
205     assertThrows(JwtInvalidException.class, () -> jwtMac.verifyMacAndDecode(compact, validator));
206   }
207 
208   @Test
expiredCompact_throwsExpiredException()209   public void expiredCompact_throwsExpiredException() throws Exception {
210     KeysetHandle keysetHandle = KeysetHandle.generateNew(KeyTemplates.get("JWT_HS256"));
211     JwtMac jwtMac = keysetHandle.getPrimitive(RegistryConfiguration.get(), JwtMac.class);
212     Instant now = Clock.systemUTC().instant().truncatedTo(ChronoUnit.SECONDS);
213     RawJwt rawJwt =
214         RawJwt.newBuilder()
215             .setExpiration(now.minusSeconds(100)) // exipired 100 seconds ago
216             .setIssuedAt(now.minusSeconds(200))
217             .build();
218     String compact = jwtMac.computeMacAndEncode(rawJwt);
219     JwtValidator validator = JwtValidator.newBuilder().build();
220     assertThrows(JwtInvalidException.class, () -> jwtMac.verifyMacAndDecode(compact, validator));
221   }
222 
223   @Test
notYetValidCompact_throwsNotBeforeException()224   public void notYetValidCompact_throwsNotBeforeException() throws Exception {
225     KeysetHandle keysetHandle = KeysetHandle.generateNew(KeyTemplates.get("JWT_HS256"));
226     JwtMac jwtMac = keysetHandle.getPrimitive(RegistryConfiguration.get(), JwtMac.class);
227 
228     Instant now = Clock.systemUTC().instant().truncatedTo(ChronoUnit.SECONDS);
229     RawJwt rawJwt =
230         RawJwt.newBuilder()
231             .setNotBefore(now.plusSeconds(3600)) // is valid in 1 hour, but not before
232             .setIssuedAt(now)
233             .withoutExpiration()
234             .build();
235     String compact = jwtMac.computeMacAndEncode(rawJwt);
236     JwtValidator validator = JwtValidator.newBuilder().allowMissingExpiration().build();
237     assertThrows(JwtInvalidException.class, () -> jwtMac.verifyMacAndDecode(compact, validator));
238   }
239 
240   @Test
testWithoutAnnotations_hasNoMonitoring()241   public void testWithoutAnnotations_hasNoMonitoring() throws Exception {
242     FakeMonitoringClient fakeMonitoringClient = new FakeMonitoringClient();
243     MutableMonitoringRegistry.globalInstance().clear();
244     MutableMonitoringRegistry.globalInstance().registerMonitoringClient(fakeMonitoringClient);
245 
246     KeysetHandle keysetHandle =
247         KeysetHandle.newBuilder()
248             .addEntry(
249                 KeysetHandle.generateEntryFromParametersName("JWT_HS256")
250                     .makePrimary()
251                     .withFixedId(42))
252             .build();
253     JwtMac jwtMac = keysetHandle.getPrimitive(RegistryConfiguration.get(), JwtMac.class);
254     RawJwt rawJwt = RawJwt.newBuilder().setJwtId("id123").withoutExpiration().build();
255     String signedCompact = jwtMac.computeMacAndEncode(rawJwt);
256     JwtValidator validator = JwtValidator.newBuilder().allowMissingExpiration().build();
257     VerifiedJwt verifiedToken = jwtMac.verifyMacAndDecode(signedCompact, validator);
258     assertThat(verifiedToken.getJwtId()).isEqualTo("id123");
259     assertThrows(
260         GeneralSecurityException.class, () -> jwtMac.verifyMacAndDecode("invalid", validator));
261 
262     assertThat(fakeMonitoringClient.getLogEntries()).isEmpty();
263     assertThat(fakeMonitoringClient.getLogFailureEntries()).isEmpty();
264   }
265 
266   @Test
testWithAnnotations_hasMonitoring()267   public void testWithAnnotations_hasMonitoring() throws Exception {
268     FakeMonitoringClient fakeMonitoringClient = new FakeMonitoringClient();
269     MutableMonitoringRegistry.globalInstance().clear();
270     MutableMonitoringRegistry.globalInstance().registerMonitoringClient(fakeMonitoringClient);
271 
272     MonitoringAnnotations annotations =
273         MonitoringAnnotations.newBuilder().add("annotation_name", "annotation_value").build();
274     KeysetHandle keysetHandle =
275         KeysetHandle.newBuilder()
276             .addEntry(
277                 KeysetHandle.generateEntryFromParametersName("JWT_HS256")
278                     .makePrimary()
279                     .withFixedId(42))
280             .setMonitoringAnnotations(annotations)
281             .build();
282 
283     JwtMac jwtMac = keysetHandle.getPrimitive(RegistryConfiguration.get(), JwtMac.class);
284     RawJwt rawJwt = RawJwt.newBuilder().setJwtId("id123").withoutExpiration().build();
285     String signedCompact = jwtMac.computeMacAndEncode(rawJwt);
286     JwtValidator validator = JwtValidator.newBuilder().allowMissingExpiration().build();
287     VerifiedJwt verifiedToken = jwtMac.verifyMacAndDecode(signedCompact, validator);
288     assertThat(verifiedToken.getJwtId()).isEqualTo("id123");
289     assertThrows(
290         GeneralSecurityException.class, () -> jwtMac.verifyMacAndDecode("invalid", validator));
291 
292     List<FakeMonitoringClient.LogEntry> logEntries = fakeMonitoringClient.getLogEntries();
293     assertThat(logEntries).hasSize(2);
294     FakeMonitoringClient.LogEntry computeEntry = logEntries.get(0);
295     assertThat(computeEntry.getKeyId()).isEqualTo(42);
296     assertThat(computeEntry.getPrimitive()).isEqualTo("jwtmac");
297     assertThat(computeEntry.getApi()).isEqualTo("compute");
298     assertThat(computeEntry.getNumBytesAsInput()).isEqualTo(1);
299     assertThat(computeEntry.getKeysetInfo().getAnnotations()).isEqualTo(annotations);
300 
301     FakeMonitoringClient.LogEntry verifyEntry = logEntries.get(1);
302     assertThat(verifyEntry.getKeyId()).isEqualTo(42);
303     assertThat(verifyEntry.getPrimitive()).isEqualTo("jwtmac");
304     assertThat(verifyEntry.getApi()).isEqualTo("verify");
305     assertThat(verifyEntry.getNumBytesAsInput()).isEqualTo(1);
306     assertThat(verifyEntry.getKeysetInfo().getAnnotations()).isEqualTo(annotations);
307 
308     List<FakeMonitoringClient.LogFailureEntry> failures =
309         fakeMonitoringClient.getLogFailureEntries();
310     assertThat(failures).hasSize(1);
311     FakeMonitoringClient.LogFailureEntry verifyFailure = failures.get(0);
312     assertThat(verifyFailure.getPrimitive()).isEqualTo("jwtmac");
313     assertThat(verifyFailure.getApi()).isEqualTo("verify");
314     assertThat(verifyFailure.getKeysetInfo().getPrimaryKeyId()).isEqualTo(42);
315     assertThat(verifyFailure.getKeysetInfo().getAnnotations()).isEqualTo(annotations);
316   }
317 }
318