From 3c824da236bb73c806b0507bd6b75bea3134fdf3 Mon Sep 17 00:00:00 2001 From: David Zeuthen Date: Fri, 6 Mar 2020 07:57:02 -0500 Subject: Update Identity Credential API docs. This change contains no actual syntactical or semantic changes, just clarifications on the inputs and outputs. Test: N/A Bug: 151082886 Change-Id: Ic7797aa53d292abdeb779cb55b404f8a433bce79 --- .../security/identity/IdentityCredential.java | 69 ++++++++++++---------- .../security/identity/IdentityCredentialStore.java | 35 ++++++++--- .../java/android/security/identity/ResultData.java | 43 +++++++------- .../identity/WritableIdentityCredential.java | 15 ++--- 4 files changed, 95 insertions(+), 67 deletions(-) (limited to 'identity') diff --git a/identity/java/android/security/identity/IdentityCredential.java b/identity/java/android/security/identity/IdentityCredential.java index 1db2f6357308..b351b3d77430 100644 --- a/identity/java/android/security/identity/IdentityCredential.java +++ b/identity/java/android/security/identity/IdentityCredential.java @@ -95,9 +95,7 @@ public abstract class IdentityCredential { /** * Sets whether to allow using an authentication key which use count has been exceeded if no * other key is available. This must be called prior to calling - * {@link #getEntries(byte[], Map, byte[], byte[])} or using a - * {@link android.hardware.biometrics.BiometricPrompt.CryptoObject} which references this - * object. + * {@link #getEntries(byte[], Map, byte[], byte[])}. * * By default this is set to true. * @@ -123,13 +121,14 @@ public abstract class IdentityCredential { * entries. * *

It is the responsibility of the calling application to know if authentication is needed - * and use e.g. {@link android.hardware.biometrics.BiometricPrompt}) to make the user + * and use e.g. {@link android.hardware.biometrics.BiometricPrompt} to make the user * authenticate using a {@link android.hardware.biometrics.BiometricPrompt.CryptoObject} which * references this object. If needed, this must be done before calling * {@link #getEntries(byte[], Map, byte[], byte[])}. * - *

If this method returns successfully (i.e. without throwing an exception), it must not be - * called again on this instance. + *

It is permissible to call this method multiple times using the same instance but if this + * is done, the {@code sessionTranscript} parameter must be identical for each call. If this is + * not the case, the {@link SessionTranscriptMismatchException} exception is thrown. * *

If not {@code null} the {@code requestMessage} parameter must contain data for the request * from the verifier. The content can be defined in the way appropriate for the credential, byt @@ -141,6 +140,9 @@ public abstract class IdentityCredential { * the example below. * * + *

If these requirements are not met the {@link InvalidRequestMessageException} exception + * is thrown. + * *

Here's an example of CBOR which conforms to this requirement: *

      *   ItemsRequest = {
@@ -149,6 +151,8 @@ public abstract class IdentityCredential {
      *     ? "RequestInfo" : {* tstr => any} ; Additional info the reader wants to provide
      *   }
      *
+     *   DocType = tstr
+     *
      *   NameSpaces = {
      *     + NameSpace => DataElements    ; Requested data elements for each NameSpace
      *   }
@@ -172,16 +176,18 @@ public abstract class IdentityCredential {
      *     EReaderKeyBytes
      *   ]
      *
-     *   DeviceEngagementBytes = #6.24(bstr .cbor DeviceEngagement)
-     *   EReaderKeyBytes = #6.24(bstr .cbor EReaderKey.Pub)
+     *   DeviceEngagementBytes = #6.24(bstr .cbor DeviceEngagement)  ; Bytes of DeviceEngagement
+     *   EReaderKeyBytes = #6.24(bstr .cbor EReaderKey.Pub)  ; Bytes of EReaderKey.pub
+     *
+     *   EReaderKey.Pub = COSE_Key    ; Ephemeral public key provided by reader
      * 
* - *

If the SessionTranscript is not empty, a COSE_Key structure for the public part - * of the key-pair previously generated by {@link #createEphemeralKeyPair()} must appear - * somewhere in {@code DeviceEngagement} and the X and Y coordinates must both be present + *

where a {@code COSE_Key} structure for the public part of the key-pair previously + * generated by {@link #createEphemeralKeyPair()} must appear somewhere in + * {@code DeviceEngagement} and the X and Y coordinates must both be present * in uncompressed form. * - *

If {@code readerAuth} is not {@code null} it must be the bytes of a COSE_Sign1 + *

If {@code readerAuth} is not {@code null} it must be the bytes of a {@code COSE_Sign1} * structure as defined in RFC 8152. For the payload nil shall be used and the * detached payload is the ReaderAuthentication CBOR described below. *

@@ -194,20 +200,23 @@ public abstract class IdentityCredential {
      *     ItemsRequestBytes = #6.24(bstr .cbor ItemsRequest)   ; Bytes of ItemsRequest
      * 
* - *

The public key corresponding to the key used to made signature, can be - * found in the {@code x5chain} unprotected header element of the COSE_Sign1 - * structure (as as described in 'draft-ietf-cose-x509-04'). There will be at - * least one certificate in said element and there may be more (and if so, + *

where {@code ItemsRequestBytes} are the bytes in the {@code requestMessage} parameter. + * + *

The public key corresponding to the key used to make the signature, can be found in the + * {@code x5chain} unprotected header element of the {@code COSE_Sign1} structure (as as + * described in + * draft-ietf-cose-x509-04). + * There will be at least one certificate in said element and there may be more (and if so, * each certificate must be signed by its successor). * - *

Data elements protected by reader authentication is returned if, and only if, they are + *

Data elements protected by reader authentication are returned if, and only if, they are * mentioned in {@code requestMessage}, {@code requestMessage} is signed by the top-most - * certificate in {@code readerCertificateChain}, and the data element is configured - * with an {@link AccessControlProfile} with a {@link X509Certificate} in - * {@code readerCertificateChain}. + * certificate in the reader's certificate chain, and the data element is configured + * with an {@link AccessControlProfile} configured with an X.509 certificate which appears + * in the certificate chain. * *

Note that only items referenced in {@code entriesToRequest} are returned - the - * {@code requestMessage} parameter is only used to for enforcing reader authentication. + * {@code requestMessage} parameter is used only for enforcing reader authentication. * *

The reason for having {@code requestMessage} and {@code entriesToRequest} as separate * parameters is that the former represents a request from the remote verifier device @@ -219,13 +228,12 @@ public abstract class IdentityCredential { * @param entriesToRequest The entries to request, organized as a map of namespace * names with each value being a collection of data elements * in the given namespace. - * @param readerSignature COSE_Sign1 structure as described above or {@code null} - * if reader authentication is not being used. + * @param readerSignature A {@code COSE_Sign1} structure as described above or + * {@code null} if reader authentication is not being used. * @return A {@link ResultData} object containing entry data organized by namespace and a * cryptographically authenticated representation of the same data. * @throws SessionTranscriptMismatchException Thrown when trying use multiple different - * session transcripts in the same presentation - * session. + * session transcripts. * @throws NoAuthenticationKeyAvailableException if authentication keys were never * provisioned, the method * {@link #setAvailableAuthenticationKeys(int, int)} @@ -255,8 +263,8 @@ public abstract class IdentityCredential { * Sets the number of dynamic authentication keys the {@code IdentityCredential} will maintain, * and the number of times each should be used. * - *

{@code IdentityCredential}s will select the least-used dynamic authentication key each - * time {@link #getEntries(byte[], Map, byte[], byte[])} is called. {@code IdentityCredential}s + *

The Identity Credential system will select the least-used dynamic authentication key each + * time {@link #getEntries(byte[], Map, byte[], byte[])} is called. Identity Credentials * for which this method has not been called behave as though it had been called wit * {@code keyCount} 0 and {@code maxUsesPerKey} 1. * @@ -274,9 +282,10 @@ public abstract class IdentityCredential { *

When there aren't enough certified dynamic authentication keys, either because the key * count has been increased or because one or more keys have reached their usage count, this * method will generate replacement keys and certificates and return them for issuer - * certification. The issuer certificates and associated static authentication data must then - * be provided back to the {@code IdentityCredential} using - * {@link #storeStaticAuthenticationData(X509Certificate, byte[])}. + * certification. The issuer certificates and associated static authentication data must then + * be provided back to the Identity Credential using + * {@link #storeStaticAuthenticationData(X509Certificate, byte[])}. The private part of + * each authentication key never leaves secure hardware. * *

Each X.509 certificate is signed by CredentialKey. The certificate chain for CredentialKey * can be obtained using the {@link #getCredentialKeyCertificateChain()} method. diff --git a/identity/java/android/security/identity/IdentityCredentialStore.java b/identity/java/android/security/identity/IdentityCredentialStore.java index a1dfc77adb29..4f834d2b87b5 100644 --- a/identity/java/android/security/identity/IdentityCredentialStore.java +++ b/identity/java/android/security/identity/IdentityCredentialStore.java @@ -78,17 +78,21 @@ public abstract class IdentityCredentialStore { /** * Specifies that the cipher suite that will be used to secure communications between the reader - * is: + * and the prover is using the following primitives * *

* + *

The exact way these primitives are combined to derive the session key is specified in + * section 9.2.1.4 of ISO/IEC 18013-5 (see description of cipher suite '1').

+ * *

* At present this is the only supported cipher suite. */ @@ -135,9 +139,20 @@ public abstract class IdentityCredentialStore { /** * Creates a new credential. * + *

When a credential is created, a cryptographic key-pair - CredentialKey - is created which + * is used to authenticate the store to the Issuing Authority. The private part of this + * key-pair never leaves secure hardware and the public part can be obtained using + * {@link WritableIdentityCredential#getCredentialKeyCertificateChain(byte[])} on the + * returned object. + * + *

In addition, all of the Credential data content is imported and a certificate for the + * CredentialKey and a signature produced with the CredentialKey are created. These latter + * values may be checked by an issuing authority to verify that the data was imported into + * secure hardware and that it was imported unmodified. + * * @param credentialName The name used to identify the credential. * @param docType The document type for the credential. - * @return A @{link WritableIdentityCredential} that can be used to create a new credential. + * @return A {@link WritableIdentityCredential} that can be used to create a new credential. * @throws AlreadyPersonalizedException if a credential with the given name already exists. * @throws DocTypeNotSupportedException if the given document type isn't supported by the store. */ @@ -148,6 +163,10 @@ public abstract class IdentityCredentialStore { /** * Retrieve a named credential. * + *

The cipher suite used to communicate with the remote verifier must also be specified. + * Currently only a single cipher-suite is supported. Support for other cipher suites may be + * added in a future version of this API. + * * @param credentialName the name of the credential to retrieve. * @param cipherSuite the cipher suite to use for communicating with the verifier. * @return The named credential, or null if not found. diff --git a/identity/java/android/security/identity/ResultData.java b/identity/java/android/security/identity/ResultData.java index 13552d619e05..37de2c4a50ea 100644 --- a/identity/java/android/security/identity/ResultData.java +++ b/identity/java/android/security/identity/ResultData.java @@ -34,23 +34,23 @@ public abstract class ResultData { /** Value was successfully retrieved. */ public static final int STATUS_OK = 0; - /** Requested entry does not exist. */ + /** The entry does not exist. */ public static final int STATUS_NO_SUCH_ENTRY = 1; - /** Requested entry was not requested. */ + /** The entry was not requested. */ public static final int STATUS_NOT_REQUESTED = 2; - /** Requested entry wasn't in the request message. */ + /** The entry wasn't in the request message. */ public static final int STATUS_NOT_IN_REQUEST_MESSAGE = 3; - /** The requested entry was not retrieved because user authentication wasn't performed. */ + /** The entry was not retrieved because user authentication failed. */ public static final int STATUS_USER_AUTHENTICATION_FAILED = 4; - /** The requested entry was not retrieved because reader authentication wasn't performed. */ + /** The entry was not retrieved because reader authentication failed. */ public static final int STATUS_READER_AUTHENTICATION_FAILED = 5; /** - * The requested entry was not retrieved because it was configured without any access + * The entry was not retrieved because it was configured without any access * control profile. */ public static final int STATUS_NO_ACCESS_CONTROL_PROFILES = 6; @@ -88,11 +88,10 @@ public abstract class ResultData { * * DeviceEngagementBytes = #6.24(bstr .cbor DeviceEngagement) * EReaderKeyBytes = #6.24(bstr .cbor EReaderKey.Pub) - * * DeviceNameSpacesBytes = #6.24(bstr .cbor DeviceNameSpaces) * * - * where + *

where * *

      *   DeviceNameSpaces = {
@@ -116,15 +115,16 @@ public abstract class ResultData {
     public abstract @NonNull byte[] getAuthenticatedData();
 
     /**
-     * Returns a message authentication code over the data returned by
-     * {@link #getAuthenticatedData}, to prove to the reader that the data is from a trusted
-     * credential.
+     * Returns a message authentication code over the {@code DeviceAuthentication} CBOR
+     * specified in {@link #getAuthenticatedData()}, to prove to the reader that the data
+     * is from a trusted credential.
      *
      * 

The MAC proves to the reader that the data is from a trusted credential. This code is * produced by using the key agreement and key derivation function from the ciphersuite * with the authentication private key and the reader ephemeral public key to compute a * shared message authentication code (MAC) key, then using the MAC function from the - * ciphersuite to compute a MAC of the authenticated data. + * ciphersuite to compute a MAC of the authenticated data. See section 9.2.3.5 of + * ISO/IEC 18013-5 for details of this operation. * *

If the {@code sessionTranscript} parameter passed to * {@link IdentityCredential#getEntries(byte[], Map, byte[], byte[])} was {@code null} @@ -157,7 +157,7 @@ public abstract class ResultData { /** * Get the names of all entries. * - * This includes the name of entries that wasn't successfully retrieved. + *

This includes the name of entries that wasn't successfully retrieved. * * @param namespaceName the namespace name to get entries for. * @return A collection of names or {@code null} if there are no entries for the given @@ -168,7 +168,7 @@ public abstract class ResultData { /** * Get the names of all entries that was successfully retrieved. * - * This only return entries for which {@link #getStatus(String, String)} will return + *

This only return entries for which {@link #getStatus(String, String)} will return * {@link #STATUS_OK}. * * @param namespaceName the namespace name to get entries for. @@ -181,16 +181,15 @@ public abstract class ResultData { /** * Gets the status of an entry. * - * This returns {@link #STATUS_OK} if the value was retrieved, {@link #STATUS_NO_SUCH_ENTRY} + *

This returns {@link #STATUS_OK} if the value was retrieved, {@link #STATUS_NO_SUCH_ENTRY} * if the given entry wasn't retrieved, {@link #STATUS_NOT_REQUESTED} if it wasn't requested, * {@link #STATUS_NOT_IN_REQUEST_MESSAGE} if the request message was set but the entry wasn't - * present in the request message, - * {@link #STATUS_USER_AUTHENTICATION_FAILED} if the value + * present in the request message, {@link #STATUS_USER_AUTHENTICATION_FAILED} if the value * wasn't retrieved because the necessary user authentication wasn't performed, - * {@link #STATUS_READER_AUTHENTICATION_FAILED} if the supplied reader certificate chain - * didn't match the set of certificates the entry was provisioned with, or - * {@link #STATUS_NO_ACCESS_CONTROL_PROFILES} if the entry was configured without any - * access control profiles. + * {@link #STATUS_READER_AUTHENTICATION_FAILED} if the supplied reader certificate chain didn't + * match the set of certificates the entry was provisioned with, or + * {@link #STATUS_NO_ACCESS_CONTROL_PROFILES} if the entry was configured without any access + * control profiles. * * @param namespaceName the namespace name of the entry. * @param name the name of the entry to get the value for. @@ -201,7 +200,7 @@ public abstract class ResultData { /** * Gets the raw CBOR data for the value of an entry. * - * This should only be called on an entry for which the {@link #getStatus(String, String)} + *

This should only be called on an entry for which the {@link #getStatus(String, String)} * method returns {@link #STATUS_OK}. * * @param namespaceName the namespace name of the entry. diff --git a/identity/java/android/security/identity/WritableIdentityCredential.java b/identity/java/android/security/identity/WritableIdentityCredential.java index e2a389bfd4da..c7aa32855abc 100644 --- a/identity/java/android/security/identity/WritableIdentityCredential.java +++ b/identity/java/android/security/identity/WritableIdentityCredential.java @@ -41,15 +41,16 @@ public abstract class WritableIdentityCredential { * Android Keystore * attestation extension which describes the key and the security hardware in which it lives. * - *

Additionally, the attestation extension will contain the tag TODO_IC_KEY which indicates - * it is an Identity Credential key (which can only sign/MAC very specific messages) and not - * an Android Keystore key (which can be used to sign/MAC anything). + *

Additionally, the attestation extension will contain the tag Tag::IDENTITY_CREDENTIAL_KEY + * which indicates it is an Identity Credential key (which can only sign/MAC very specific + * messages) and not an Android Keystore key (which can be used to sign/MAC anything). * *

The issuer MUST carefully examine this certificate chain including (but not - * limited to) checking that the root certificate is well-known, the tag TODO_IC_KEY is - * present, the passed in challenge is present, the device has verified boot enabled, that each - * certificate in the chain is signed by its successor, that none of the certificates have been - * revoked and so on. + * limited to) checking that the root certificate is well-known, the tag + * Tag::IDENTITY_CREDENTIAL_KEY present, the passed in challenge is present, the tag + * Tag::ATTESTATION_APPLICATION_ID is set to the expected Android application, the device + * has verified boot enabled, each certificate in the chain is signed by its successor, + * none of the certificates have been revoked, and so on. * *

It is not strictly necessary to use this method to provision a credential if the issuing * authority doesn't care about the nature of the security hardware. If called, however, this -- cgit v1.2.3