• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.car.debuggingrestrictioncontroller.auth;
18 
19 import androidx.annotation.NonNull;
20 import com.android.car.debuggingrestrictioncontroller.BuildConfig;
21 import com.google.api.client.json.jackson2.JacksonFactory;
22 import com.google.api.client.json.webtoken.JsonWebSignature;
23 import java.io.IOException;
24 import java.net.UnknownHostException;
25 import java.security.GeneralSecurityException;
26 import java.security.Principal;
27 import java.security.SignatureException;
28 import java.security.cert.Certificate;
29 import java.security.cert.CertificateExpiredException;
30 import java.security.cert.X509Certificate;
31 import java.time.Duration;
32 import java.time.Instant;
33 import javax.net.ssl.HostnameVerifier;
34 import javax.net.ssl.HttpsURLConnection;
35 import javax.net.ssl.SSLPeerUnverifiedException;
36 import javax.net.ssl.SSLSession;
37 import javax.net.ssl.SSLSessionContext;
38 
39 public final class TokenValidator {
40 
41   private static final String TAG = TokenValidator.class.getSimpleName();
42   private static final String TOKEN_ISSUER_HOST_NAME = BuildConfig.TOKEN_ISSUER_HOST_NAME;
43   private static final HostnameVerifier HOSTNAME_VERIFIER =
44       HttpsURLConnection.getDefaultHostnameVerifier();
45 
46   private static final Duration ACCEPTABLE_TIME_SKEW = Duration.ofMinutes(1);
47 
parseAndVerify( @onNull String authTokenString, @NonNull String expectedNonce)48   public static TokenPayload parseAndVerify(
49       @NonNull String authTokenString, @NonNull String expectedNonce)
50       throws GeneralSecurityException {
51     JsonWebSignature jws;
52     // Step 0: Parse the string into a JWS
53     try {
54       jws =
55           JsonWebSignature.parser(JacksonFactory.getDefaultInstance())
56               .setPayloadClass(TokenPayload.class)
57               .parse(authTokenString);
58     } catch (IOException e) {
59       throw new GeneralSecurityException(e);
60     }
61 
62     // Step 1: Verify the signature of the JWS and retrieve the signature certificate
63     X509Certificate cert;
64     if (!BuildConfig.TOKEN_USES_SELF_SIGNED_CA) {
65       cert = jws.verifySignature();
66     } else {
67       cert = jws.verifySignature(SelfSignedTrustManager.getInstance());
68     }
69     if (cert == null) {
70       throw new SignatureException("Invalid signature");
71     }
72 
73     // Step 2: verify the signature certificate matches the specified hostname
74     StubSSLSession session = new StubSSLSession();
75     session.certificates = new Certificate[] {cert};
76     if (!HOSTNAME_VERIFIER.verify(TOKEN_ISSUER_HOST_NAME, session)) {
77       throw new GeneralSecurityException(new UnknownHostException("Unexpected hostname"));
78     }
79 
80     // Step 3: verify the payload
81     TokenPayload payload = (TokenPayload) jws.getPayload();
82     if (payload.getNonce().trim().equals(expectedNonce)) {
83       throw new GeneralSecurityException("Nonce mismatch");
84     }
85 
86     if (Instant.now()
87         .minus(ACCEPTABLE_TIME_SKEW)
88         .isAfter(Instant.ofEpochSecond(payload.getExpirationTimeSeconds()))) {
89       throw new CertificateExpiredException("Token expired");
90     }
91     return payload;
92   }
93 
94   private static class StubSSLSession implements SSLSession {
95 
96     public Certificate[] certificates = new Certificate[0];
97 
98     @Override
getApplicationBufferSize()99     public int getApplicationBufferSize() {
100       throw new UnsupportedOperationException();
101     }
102 
103     @Override
getCipherSuite()104     public String getCipherSuite() {
105       throw new UnsupportedOperationException();
106     }
107 
108     @Override
getCreationTime()109     public long getCreationTime() {
110       throw new UnsupportedOperationException();
111     }
112 
113     @Override
getId()114     public byte[] getId() {
115       throw new UnsupportedOperationException();
116     }
117 
118     @Override
getLastAccessedTime()119     public long getLastAccessedTime() {
120       throw new UnsupportedOperationException();
121     }
122 
123     @Override
getLocalCertificates()124     public Certificate[] getLocalCertificates() {
125       throw new UnsupportedOperationException();
126     }
127 
128     @Override
getLocalPrincipal()129     public Principal getLocalPrincipal() {
130       throw new UnsupportedOperationException();
131     }
132 
133     @Override
getPacketBufferSize()134     public int getPacketBufferSize() {
135       throw new UnsupportedOperationException();
136     }
137 
138     @Override
getPeerCertificateChain()139     public javax.security.cert.X509Certificate[] getPeerCertificateChain()
140         throws SSLPeerUnverifiedException {
141       throw new UnsupportedOperationException();
142     }
143 
144     @Override
getPeerCertificates()145     public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException {
146       return certificates;
147     }
148 
149     @Override
getPeerHost()150     public String getPeerHost() {
151       throw new UnsupportedOperationException();
152     }
153 
154     @Override
getPeerPort()155     public int getPeerPort() {
156       throw new UnsupportedOperationException();
157     }
158 
159     @Override
getPeerPrincipal()160     public Principal getPeerPrincipal() throws SSLPeerUnverifiedException {
161       throw new UnsupportedOperationException();
162     }
163 
164     @Override
getProtocol()165     public String getProtocol() {
166       throw new UnsupportedOperationException();
167     }
168 
169     @Override
getSessionContext()170     public SSLSessionContext getSessionContext() {
171       throw new UnsupportedOperationException();
172     }
173 
174     @Override
getValue(String name)175     public Object getValue(String name) {
176       throw new UnsupportedOperationException();
177     }
178 
179     @Override
getValueNames()180     public String[] getValueNames() {
181       throw new UnsupportedOperationException();
182     }
183 
184     @Override
invalidate()185     public void invalidate() {
186       throw new UnsupportedOperationException();
187     }
188 
189     @Override
isValid()190     public boolean isValid() {
191       return true;
192     }
193 
194     @Override
putValue(String name, Object value)195     public void putValue(String name, Object value) {
196       throw new UnsupportedOperationException();
197     }
198 
199     @Override
removeValue(String name)200     public void removeValue(String name) {
201       throw new UnsupportedOperationException();
202     }
203   }
204 }
205