1 /*
2  * Copyright 2019 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 android.security.identity;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 
22 import java.security.InvalidKeyException;
23 import java.security.KeyPair;
24 import java.security.PublicKey;
25 import java.security.cert.X509Certificate;
26 import java.time.Instant;
27 import java.util.Collection;
28 import java.util.Map;
29 
30 /**
31  * Class used to read data from a previously provisioned credential.
32  *
33  * Use {@link IdentityCredentialStore#getCredentialByName(String, int)} to get a
34  * {@link IdentityCredential} instance.
35  */
36 public abstract class IdentityCredential {
37     /**
38      * @hide
39      */
IdentityCredential()40     protected IdentityCredential() {}
41 
42     /**
43      * Create an ephemeral key pair to use to establish a secure channel with a reader.
44      *
45      * <p>Applications should use this key-pair for the communications channel with the reader
46      * using a protocol / cipher-suite appropriate for the application. One example of such a
47      * protocol is the one used for Mobile Driving Licenses, see ISO 18013-5 section 9.2.1 "Session
48      * encryption".
49      *
50      * @return ephemeral key pair to use to establish a secure channel with a reader.
51      */
createEphemeralKeyPair()52     public @NonNull abstract KeyPair createEphemeralKeyPair();
53 
54     /**
55      * Set the ephemeral public key provided by the reader. If called, this must be called before
56      * {@link #getEntries(byte[], Map, byte[], byte[])} is called.
57      *
58      * @param readerEphemeralPublicKey The ephemeral public key provided by the reader to
59      *                                 establish a secure session.
60      * @throws InvalidKeyException if the given key is invalid.
61      */
setReaderEphemeralPublicKey(@onNull PublicKey readerEphemeralPublicKey)62     public abstract void setReaderEphemeralPublicKey(@NonNull PublicKey readerEphemeralPublicKey)
63             throws InvalidKeyException;
64 
65     /**
66      * Encrypt a message for transmission to the reader.
67      *
68      * <p>Do not use. In this version of the API, this method produces an incorrect
69      * result. Instead, applications should implement message encryption/decryption themselves as
70      * detailed in the {@link #createEphemeralKeyPair()} method. In a future API-level, this
71      * method will be deprecated.
72      *
73      * @param messagePlaintext unencrypted message to encrypt.
74      * @return encrypted message.
75      */
encryptMessageToReader(@onNull byte[] messagePlaintext)76     public @NonNull abstract byte[] encryptMessageToReader(@NonNull byte[] messagePlaintext);
77 
78     /**
79      * Decrypt a message received from the reader.
80      *
81      * <p>Do not use. In this version of the API, this method produces an incorrect
82      * result. Instead, applications should implement message encryption/decryption themselves as
83      * detailed in the {@link #createEphemeralKeyPair()} method. In a future API-level, this
84      * method will be deprecated.
85      *
86      * @param messageCiphertext encrypted message to decrypt.
87      * @return decrypted message.
88      * @throws MessageDecryptionException if the ciphertext couldn't be decrypted.
89      */
decryptMessageFromReader(@onNull byte[] messageCiphertext)90     public @NonNull abstract byte[] decryptMessageFromReader(@NonNull byte[] messageCiphertext)
91             throws MessageDecryptionException;
92 
93     /**
94      * Gets the X.509 certificate chain for the CredentialKey which identifies this
95      * credential to the issuing authority. This is the same certificate chain that
96      * was returned by {@link WritableIdentityCredential#getCredentialKeyCertificateChain(byte[])}
97      * when the credential was first created and its Android Keystore extension will
98      * contain the <code>challenge</code> data set at that time. See the documentation
99      * for that method for important information about this certificate chain.
100      *
101      * @return the certificate chain for this credential's CredentialKey.
102      */
getCredentialKeyCertificateChain()103     public @NonNull abstract Collection<X509Certificate> getCredentialKeyCertificateChain();
104 
105     /**
106      * Sets whether to allow using an authentication key which use count has been exceeded if no
107      * other key is available. This must be called prior to calling
108      * {@link #getEntries(byte[], Map, byte[], byte[])}.
109      *
110      * By default this is set to true.
111      *
112      * @param allowUsingExhaustedKeys whether to allow using an authentication key which use count
113      *                                has been exceeded if no other key is available.
114      */
setAllowUsingExhaustedKeys(boolean allowUsingExhaustedKeys)115     public abstract void setAllowUsingExhaustedKeys(boolean allowUsingExhaustedKeys);
116 
117     /**
118      * Sets whether to allow using an authentication key which has been expired if no
119      * other key is available. This must be called prior to calling
120      * {@link #getEntries(byte[], Map, byte[], byte[])}.
121      *
122      * <p>By default this is set to false.
123      *
124      * <p>This is only implemented in feature version 202101 or later. If not implemented, the call
125      * fails with {@link UnsupportedOperationException}. See
126      * {@link android.content.pm.PackageManager#FEATURE_IDENTITY_CREDENTIAL_HARDWARE} for known
127      * feature versions.
128      *
129      * @param allowUsingExpiredKeys whether to allow using an authentication key which use count
130      *                              has been exceeded if no other key is available.
131      */
setAllowUsingExpiredKeys(boolean allowUsingExpiredKeys)132     public void setAllowUsingExpiredKeys(boolean allowUsingExpiredKeys) {
133         throw new UnsupportedOperationException();
134     }
135 
136     /**
137      * Called by android.hardware.biometrics.CryptoObject#getOpId() to get an
138      * operation handle.
139      *
140      * @hide
141      */
getCredstoreOperationHandle()142     public abstract long getCredstoreOperationHandle();
143 
144     /**
145      * Retrieve data entries and associated data from this {@code IdentityCredential}.
146      *
147      * <p>If an access control check fails for one of the requested entries or if the entry
148      * doesn't exist, the entry is simply not returned. The application can detect this
149      * by using the {@link ResultData#getStatus(String, String)} method on each of the requested
150      * entries.
151      *
152      * <p>It is the responsibility of the calling application to know if authentication is needed
153      * and use e.g. {@link android.hardware.biometrics.BiometricPrompt} to make the user
154      * authenticate using a {@link android.hardware.biometrics.BiometricPrompt.CryptoObject} which
155      * references this object. If needed, this must be done before calling
156      * {@link #getEntries(byte[], Map, byte[], byte[])}.
157      *
158      * <p>It is permissible to call this method multiple times using the same instance but if this
159      * is done, the {@code sessionTranscript} parameter must be identical for each call. If this is
160      * not the case, the {@link SessionTranscriptMismatchException} exception is thrown.
161      *
162      * <p>If not {@code null} the {@code requestMessage} parameter must contain data for the request
163      * from the verifier. The content can be defined in the way appropriate for the credential, byt
164      * there are three requirements that must be met to work with this API:
165      * <ul>
166      * <li>The content must be a CBOR-encoded structure.</li>
167      * <li>The CBOR structure must be a map.</li>
168      * <li>The map must contain a tstr key "nameSpaces" whose value contains a map, as described in
169      *     the example below.</li>
170      * </ul>
171      *
172      * <p>If these requirements are not met the {@link InvalidRequestMessageException} exception
173      * is thrown.
174      *
175      * <p>Here's an example of CBOR which conforms to this requirement:
176      * <pre>
177      *   ItemsRequest = {
178      *     ? "docType" : DocType,
179      *     "nameSpaces" : NameSpaces,
180      *     ? "RequestInfo" : {* tstr => any} ; Additional info the reader wants to provide
181      *   }
182      *
183      *   DocType = tstr
184      *
185      *   NameSpaces = {
186      *     + NameSpace => DataElements    ; Requested data elements for each NameSpace
187      *   }
188      *
189      *   NameSpace = tstr
190      *
191      *   DataElements = {
192      *     + DataElement => IntentToRetain
193      *   }
194      *
195      *   DataElement = tstr
196      *   IntentToRetain = bool
197      * </pre>
198      *
199      * <p>If the {@code sessionTranscript} parameter is not {@code null}, the X and Y coordinates
200      * of the public part of the key-pair previously generated by {@link #createEphemeralKeyPair()}
201      * must appear somewhere in the bytes of the CBOR. Each of these coordinates must appear
202      * encoded with the most significant bits first and use the exact amount of bits indicated by
203      * the key size of the ephemeral keys. For example, if the ephemeral key is using the P-256
204      * curve then the 32 bytes for the X coordinate encoded with the most significant bits first
205      * must appear somewhere in {@code sessionTranscript} and ditto for the 32 bytes for the Y
206      * coordinate.
207      *
208      * <p>If {@code readerAuth} is not {@code null} it must be the bytes of a {@code COSE_Sign1}
209      * structure as defined in RFC 8152. For the payload nil shall be used and the
210      * detached payload is the ReaderAuthenticationBytes CBOR described below.
211      * <pre>
212      *     ReaderAuthentication = [
213      *       "ReaderAuthentication",
214      *       SessionTranscript,
215      *       ItemsRequestBytes
216      *     ]
217      *
218      *     ItemsRequestBytes = #6.24(bstr .cbor ItemsRequest)
219      *
220      *     ReaderAuthenticationBytes = #6.24(bstr .cbor ReaderAuthentication)
221      * </pre>
222      *
223      * <p>where {@code ItemsRequestBytes} are the bytes in the {@code requestMessage} parameter.
224      *
225      * <p>The public key corresponding to the key used to make the signature, can be found in the
226      * {@code x5chain} unprotected header element of the {@code COSE_Sign1} structure (as as
227      * described in
228      * <a href="https://tools.ietf.org/html/draft-ietf-cose-x509-04">draft-ietf-cose-x509-04</a>).
229      * There will be at least one certificate in said element and there may be more (and if so,
230      * each certificate must be signed by its successor).
231      *
232      * <p>Data elements protected by reader authentication are returned if, and only if, they are
233      * mentioned in {@code requestMessage}, {@code requestMessage} is signed by the top-most
234      * certificate in the reader's certificate chain, and the data element is configured
235      * with an {@link AccessControlProfile} configured with an X.509 certificate which appears
236      * in the certificate chain.
237      *
238      * <p>Note that only items referenced in {@code entriesToRequest} are returned - the
239      * {@code requestMessage} parameter is used only for enforcing reader authentication.
240      *
241      * <p>The reason for having {@code requestMessage} and {@code entriesToRequest} as separate
242      * parameters is that the former represents a request from the remote verifier device
243      * (optionally signed) and this allows the application to filter the request to not include
244      * data elements which the user has not consented to sharing.
245      *
246      * @param requestMessage         If not {@code null}, must contain CBOR data conforming to
247      *                               the schema mentioned above.
248      * @param entriesToRequest       The entries to request, organized as a map of namespace
249      *                               names with each value being a collection of data elements
250      *                               in the given namespace.
251      * @param readerSignature        A {@code COSE_Sign1} structure as described above or
252      *                               {@code null} if reader authentication is not being used.
253      * @return A {@link ResultData} object containing entry data organized by namespace and a
254      *         cryptographically authenticated representation of the same data.
255      * @throws SessionTranscriptMismatchException     Thrown when trying use multiple different
256      *                                                session transcripts.
257      * @throws NoAuthenticationKeyAvailableException  if authentication keys were never
258      *                                                provisioned, the method
259      *                                             {@link #setAvailableAuthenticationKeys(int, int)}
260      *                                                was called with {@code keyCount} set to 0,
261      *                                                the method
262      *                                                {@link #setAllowUsingExhaustedKeys(boolean)}
263      *                                                was called with {@code false} and all
264      *                                                available authentication keys have been
265      *                                                exhausted.
266      * @throws InvalidReaderSignatureException        if the reader signature is invalid, or it
267      *                                                doesn't contain a certificate chain, or if
268      *                                                the signature failed to validate.
269      * @throws InvalidRequestMessageException         if the requestMessage is malformed.
270      * @throws EphemeralPublicKeyNotFoundException    if the ephemeral public key was not found in
271      *                                                the session transcript.
272      */
getEntries( @ullable byte[] requestMessage, @NonNull Map<String, Collection<String>> entriesToRequest, @Nullable byte[] sessionTranscript, @Nullable byte[] readerSignature)273     public abstract @NonNull ResultData getEntries(
274             @Nullable byte[] requestMessage,
275             @NonNull Map<String, Collection<String>> entriesToRequest,
276             @Nullable byte[] sessionTranscript,
277             @Nullable byte[] readerSignature)
278             throws SessionTranscriptMismatchException, NoAuthenticationKeyAvailableException,
279             InvalidReaderSignatureException, EphemeralPublicKeyNotFoundException,
280             InvalidRequestMessageException;
281 
282     /**
283      * Sets the number of dynamic authentication keys the {@code IdentityCredential} will maintain,
284      * and the number of times each should be used.
285      *
286      * <p>The Identity Credential system will select the least-used dynamic authentication key each
287      * time {@link #getEntries(byte[], Map, byte[], byte[])} is called. Identity Credentials
288      * for which this method has not been called behave as though it had been called wit
289      * {@code keyCount} 0 and {@code maxUsesPerKey} 1.
290      *
291      * @param keyCount      The number of active, certified dynamic authentication keys the
292      *                      {@code IdentityCredential} will try to keep available. This value
293      *                      must be non-negative.
294      * @param maxUsesPerKey The maximum number of times each of the keys will be used before it's
295      *                      eligible for replacement. This value must be greater than zero.
296      */
setAvailableAuthenticationKeys(int keyCount, int maxUsesPerKey)297     public abstract void setAvailableAuthenticationKeys(int keyCount, int maxUsesPerKey);
298 
299     /**
300      * Gets a collection of dynamic authentication keys that need certification.
301      *
302      * <p>When there aren't enough certified dynamic authentication keys, either because the key
303      * count has been increased or because one or more keys have reached their usage count, this
304      * method will generate replacement keys and certificates and return them for issuer
305      * certification.  The issuer certificates and associated static authentication data must then
306      * be provided back to the Identity Credential using
307      * {@link #storeStaticAuthenticationData(X509Certificate, byte[])}.  The private part of
308      * each authentication key never leaves secure hardware.
309      *
310      * <p>Each X.509 certificate is signed by CredentialKey. The certificate chain for CredentialKey
311      * can be obtained using the {@link #getCredentialKeyCertificateChain()} method.
312 
313      * <p>If the implementation is feature version 202101 or later,
314      * each X.509 certificate contains an X.509 extension at OID 1.3.6.1.4.1.11129.2.1.26 which
315      * contains a DER encoded OCTET STRING with the bytes of the CBOR with the following CDDL:
316      * <pre>
317      *   ProofOfBinding = [
318      *     "ProofOfBinding",
319      *     bstr,              // Contains SHA-256(ProofOfProvisioning)
320      *   ]
321      * </pre>
322      * <p>This CBOR enables an issuer to determine the exact state of the credential it
323      * returns issuer-signed data for.
324      *
325      * <p> See {@link android.content.pm.PackageManager#FEATURE_IDENTITY_CREDENTIAL_HARDWARE} for
326      * known feature versions.
327      *
328      * @return A collection of X.509 certificates for dynamic authentication keys that need issuer
329      * certification.
330      */
getAuthKeysNeedingCertification()331     public @NonNull abstract Collection<X509Certificate> getAuthKeysNeedingCertification();
332 
333     /**
334      * Store authentication data associated with a dynamic authentication key.
335      *
336      * This should only be called for an authenticated key returned by
337      * {@link #getAuthKeysNeedingCertification()}.
338      *
339      * @param authenticationKey The dynamic authentication key for which certification and
340      *                          associated static
341      *                          authentication data is being provided.
342      * @param staticAuthData    Static authentication data provided by the issuer that validates
343      *                          the authenticity
344      *                          and integrity of the credential data fields.
345      * @throws UnknownAuthenticationKeyException If the given authentication key is not recognized.
346      * @deprecated Use {@link #storeStaticAuthenticationData(X509Certificate, Instant, byte[])}
347      *     instead.
348      */
349     @Deprecated
storeStaticAuthenticationData( @onNull X509Certificate authenticationKey, @NonNull byte[] staticAuthData)350     public abstract void storeStaticAuthenticationData(
351             @NonNull X509Certificate authenticationKey,
352             @NonNull byte[] staticAuthData)
353             throws UnknownAuthenticationKeyException;
354 
355     /**
356      * Store authentication data associated with a dynamic authentication key.
357      *
358      * This should only be called for an authenticated key returned by
359      * {@link #getAuthKeysNeedingCertification()}.
360      *
361      * <p>This is only implemented in feature version 202101 or later. If not implemented, the call
362      * fails with {@link UnsupportedOperationException}. See
363      * {@link android.content.pm.PackageManager#FEATURE_IDENTITY_CREDENTIAL_HARDWARE} for known
364      * feature versions.
365      *
366      * @param authenticationKey The dynamic authentication key for which certification and
367      *                          associated static
368      *                          authentication data is being provided.
369      * @param expirationDate    The expiration date of the static authentication data.
370      * @param staticAuthData    Static authentication data provided by the issuer that validates
371      *                          the authenticity
372      *                          and integrity of the credential data fields.
373      * @throws UnknownAuthenticationKeyException If the given authentication key is not recognized.
374      */
storeStaticAuthenticationData( @onNull X509Certificate authenticationKey, @NonNull Instant expirationDate, @NonNull byte[] staticAuthData)375     public void storeStaticAuthenticationData(
376             @NonNull X509Certificate authenticationKey,
377             @NonNull Instant expirationDate,
378             @NonNull byte[] staticAuthData)
379             throws UnknownAuthenticationKeyException {
380         throw new UnsupportedOperationException();
381     }
382 
383     /**
384      * Get the number of times the dynamic authentication keys have been used.
385      *
386      * @return int array of dynamic authentication key usage counts.
387      */
getAuthenticationDataUsageCount()388     public @NonNull abstract int[] getAuthenticationDataUsageCount();
389 
390     /**
391      * Proves ownership of a credential.
392      *
393      * <p>This method returns a COSE_Sign1 data structure signed by the CredentialKey
394      * with payload set to {@code ProofOfDeletion} as defined below.</p>
395      *
396      * <p>The returned CBOR is the following:</p>
397      * <pre>
398      *     ProofOfOwnership = [
399      *          "ProofOfOwnership",           ; tstr
400      *          tstr,                         ; DocType
401      *          bstr,                         ; Challenge
402      *          bool                          ; true if this is a test credential, should
403      *                                        ; always be false.
404      *      ]
405      * </pre>
406      *
407      * <p>This is only implemented in feature version 202101 or later. If not implemented, the call
408      * fails with {@link UnsupportedOperationException}. See
409      * {@link android.content.pm.PackageManager#FEATURE_IDENTITY_CREDENTIAL_HARDWARE} for known
410      * feature versions.
411      *
412      * @param challenge is a non-empty byte array whose contents should be unique, fresh and
413      *                  provided by the issuing authority. The value provided is embedded in the
414      *                  generated CBOR and enables the issuing authority to verify that the
415      *                  returned proof is fresh.
416      * @return the COSE_Sign1 data structure above
417      */
proveOwnership(@onNull byte[] challenge)418     public @NonNull byte[] proveOwnership(@NonNull byte[] challenge)  {
419         throw new UnsupportedOperationException();
420     }
421 
422     /**
423      * Deletes a credential.
424      *
425      * <p>This method returns a COSE_Sign1 data structure signed by the CredentialKey
426      * with payload set to {@code ProofOfDeletion} as defined below.</p>
427      *
428      * <pre>
429      *     ProofOfDeletion = [
430      *          "ProofOfDeletion",            ; tstr
431      *          tstr,                         ; DocType
432      *          bstr,                         ; Challenge
433      *          bool                          ; true if this is a test credential, should
434      *                                        ; always be false.
435      *      ]
436      * </pre>
437      *
438      * <p>This is only implemented in feature version 202101 or later. If not implemented, the call
439      * fails with {@link UnsupportedOperationException}. See
440      * {@link android.content.pm.PackageManager#FEATURE_IDENTITY_CREDENTIAL_HARDWARE} for known
441      * feature versions.
442      *
443      * @param challenge is a non-empty byte array whose contents should be unique, fresh and
444      *                  provided by the issuing authority. The value provided is embedded in the
445      *                  generated CBOR and enables the issuing authority to verify that the
446      *                  returned proof is fresh.
447      * @return the COSE_Sign1 data structure above
448      */
delete(@onNull byte[] challenge)449     public @NonNull byte[] delete(@NonNull byte[] challenge)  {
450         throw new UnsupportedOperationException();
451     }
452 
453     /**
454      * Updates the credential with new access control profiles and data items.
455      *
456      * <p>This method is similar to
457      * {@link WritableIdentityCredential#personalize(PersonalizationData)} except that it operates
458      * on an existing credential, see the documentation for that method for the format of the
459      * returned data.
460      *
461      * <p>If this call succeeds an side-effect is that all dynamic authentication keys for the
462      * credential are deleted. The application will need to use
463      * {@link #getAuthKeysNeedingCertification()} to generate replacement keys and return
464      * them for issuer certification.
465      *
466      * <p>This is only implemented in feature version 202101 or later. If not implemented, the call
467      * fails with {@link UnsupportedOperationException}. See
468      * {@link android.content.pm.PackageManager#FEATURE_IDENTITY_CREDENTIAL_HARDWARE} for known
469      * feature versions.
470      *
471      * @param personalizationData   The data to update, including access control profiles
472      *                              and data elements and their values, grouped into namespaces.
473      * @return A COSE_Sign1 data structure, see above.
474      */
update(@onNull PersonalizationData personalizationData)475     public @NonNull byte[] update(@NonNull PersonalizationData personalizationData) {
476         throw new UnsupportedOperationException();
477     }
478 }
479