/*
 * Copyright 2020, The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#if !defined(EIC_INSIDE_LIBEIC_H) && !defined(EIC_COMPILATION)
#error "Never include this file directly, include libeic.h instead."
#endif

#ifndef ANDROID_HARDWARE_IDENTITY_EIC_PRESENTATION_H
#define ANDROID_HARDWARE_IDENTITY_EIC_PRESENTATION_H

#ifdef __cplusplus
extern "C" {
#endif

#include "EicCbor.h"

// The maximum size we support for public keys in reader certificates.
#define EIC_PRESENTATION_MAX_READER_PUBLIC_KEY_SIZE 65

// Constant used to convey that no session is associated with a presentation.
#define EIC_PRESENTATION_ID_UNSET 0

typedef struct {
    // A non-zero number unique for this EicPresentation instance
    uint32_t id;

    int featureLevel;

    uint8_t storageKey[EIC_AES_128_KEY_SIZE];
    uint8_t credentialPrivateKey[EIC_P256_PRIV_KEY_SIZE];

    uint8_t ephemeralPrivateKey[EIC_P256_PRIV_KEY_SIZE];

    // If non-zero (not EIC_PRESENTATION_ID_UNSET), the id of the EicSession object this
    // presentation object is associated with.
    uint32_t sessionId;

    // The challenge generated with eicPresentationCreateAuthChallenge()
    uint64_t authChallenge;

    // Set by eicPresentationSetAuthToken() and contains the fields
    // from the passed in authToken and verificationToken.
    //
    uint64_t authTokenChallenge;
    uint64_t authTokenSecureUserId;
    uint64_t authTokenTimestamp;
    uint64_t verificationTokenTimestamp;

    // The public key for the reader.
    //
    // (During the process of pushing reader certificates, this is also used to store
    // the public key of the previously pushed certificate.)
    //
    uint8_t readerPublicKey[EIC_PRESENTATION_MAX_READER_PUBLIC_KEY_SIZE];
    size_t readerPublicKeySize;

    // This is set to true only if eicPresentationValidateRequestMessage() successfully
    // validated the requestMessage.
    //
    // Why even record this? Because there's no requirement the HAL actually calls that
    // function and we validate ACPs before it's called... so it's possible that a
    // compromised HAL could trick us into marking ACPs as authorized while they in fact
    // aren't.
    bool requestMessageValidated;
    bool buildCbor;
    bool buildCborEcdsa;

    // Set to true initialized as a test credential.
    bool testCredential;

    // Set to true if the evaluation of access control checks in
    // eicPresentationStartRetrieveEntryValue() resulted EIC_ACCESS_CHECK_RESULT_OK
    bool accessCheckOk;

    // These are bitmasks indicating which of the possible 32 access control profiles are
    // authorized. They are built up by eicPresentationValidateAccessControlProfile().
    //
    uint32_t accessControlProfileMaskValidated;         // True if the profile was validated.
    uint32_t accessControlProfileMaskUsesReaderAuth;    // True if the ACP is using reader auth
    uint32_t accessControlProfileMaskFailedReaderAuth;  // True if failed reader auth
    uint32_t accessControlProfileMaskFailedUserAuth;    // True if failed user auth

    // SHA-256 for AdditionalData, updated for each entry.
    uint8_t additionalDataSha256[EIC_SHA256_DIGEST_SIZE];

    // SHA-256 of ProofOfProvisioning. Set to NUL-bytes or initialized from CredentialKeys data
    // if credential was created with feature version 202101 or later.
    uint8_t proofOfProvisioningSha256[EIC_SHA256_DIGEST_SIZE];

    size_t expectedCborSizeAtEnd;
    EicCbor cbor;

    // The selected DeviceKey / AuthKey
    uint8_t deviceKeyPriv[EIC_P256_PRIV_KEY_SIZE];

    EicCbor cborEcdsa;
    size_t expectedCborEcdsaSizeAtEnd;
} EicPresentation;

// If sessionId is zero (EIC_PRESENTATION_ID_UNSET), the presentation object is not associated
// with a session object. Otherwise it's the id of the session object.
//
bool eicPresentationInit(EicPresentation* ctx, uint32_t sessionId, bool testCredential,
                         const char* docType, size_t docTypeLength,
                         const uint8_t* encryptedCredentialKeys,
                         size_t encryptedCredentialKeysSize);

bool eicPresentationShutdown(EicPresentation* ctx);

bool eicPresentationGetId(EicPresentation* ctx, uint32_t* outId);

bool eicPresentationGenerateSigningKeyPair(EicPresentation* ctx, const char* docType,
                                           size_t docTypeLength, time_t now,
                                           uint8_t* publicKeyCert, size_t* publicKeyCertSize,
                                           uint8_t signingKeyBlob[60]);

// Create an ephemeral key-pair.
//
// The private key is stored in |ctx->ephemeralPrivateKey| and also returned in
// |ephemeralPrivateKey|.
//
bool eicPresentationCreateEphemeralKeyPair(EicPresentation* ctx,
                                           uint8_t ephemeralPrivateKey[EIC_P256_PRIV_KEY_SIZE]);

// Returns a non-zero challenge in |authChallenge|.
bool eicPresentationCreateAuthChallenge(EicPresentation* ctx, uint64_t* authChallenge);

// Starts retrieveing entries.
//
bool eicPresentationStartRetrieveEntries(EicPresentation* ctx);

// Sets the auth-token.
bool eicPresentationSetAuthToken(EicPresentation* ctx, uint64_t challenge, uint64_t secureUserId,
                                 uint64_t authenticatorId, int hardwareAuthenticatorType,
                                 uint64_t timeStamp, const uint8_t* mac, size_t macSize,
                                 uint64_t verificationTokenChallenge,
                                 uint64_t verificationTokenTimeStamp,
                                 int verificationTokenSecurityLevel,
                                 const uint8_t* verificationTokenMac,
                                 size_t verificationTokenMacSize);

// Function to push certificates in the reader certificate chain.
//
// This should start with the root certificate (e.g. the last in the chain) and
// continue up the chain, ending with the certificate for the reader.
//
// Calls to this function should be interleaved with calls to the
// eicPresentationValidateAccessControlProfile() function, see below.
//
bool eicPresentationPushReaderCert(EicPresentation* ctx, const uint8_t* certX509,
                                   size_t certX509Size);

// Checks an access control profile.
//
// Returns false if an error occurred while checking the profile (e.g. MAC doesn't check out).
//
// Returns in |accessGranted| whether access is granted.
//
// If |readerCertificate| is non-empty and the public key of one of those
// certificates appear in the chain presented by the reader, this function must
// be called after pushing that certificate using
// eicPresentationPushReaderCert().
//
// The scratchSpace should be set to a buffer at least 512 bytes. It's done
// this way to avoid allocating stack space.
//
bool eicPresentationValidateAccessControlProfile(EicPresentation* ctx, int id,
                                                 const uint8_t* readerCertificate,
                                                 size_t readerCertificateSize,
                                                 bool userAuthenticationRequired, int timeoutMillis,
                                                 uint64_t secureUserId, const uint8_t mac[28],
                                                 bool* accessGranted,
                                                 uint8_t* scratchSpace,
                                                 size_t scratchSpaceSize);

// Validates that the given requestMessage is signed by the public key in the
// certificate last set with eicPresentationPushReaderCert().
//
// The format of the signature is the same encoding as the 'signature' field of
// COSE_Sign1 - that is, it's the R and S integers both with the same length as
// the key-size.
//
// Must be called after eicPresentationPushReaderCert() have been used to push
// the final certificate. Which is the certificate of the reader itself.
//
bool eicPresentationValidateRequestMessage(EicPresentation* ctx, const uint8_t* sessionTranscript,
                                           size_t sessionTranscriptSize,
                                           const uint8_t* requestMessage, size_t requestMessageSize,
                                           int coseSignAlg,
                                           const uint8_t* readerSignatureOfToBeSigned,
                                           size_t readerSignatureOfToBeSignedSize);

typedef enum {
    // Returned if access is granted.
    EIC_ACCESS_CHECK_RESULT_OK,

    // Returned if an error occurred checking for access.
    EIC_ACCESS_CHECK_RESULT_FAILED,

    // Returned if access was denied because item is configured without any
    // access control profiles.
    EIC_ACCESS_CHECK_RESULT_NO_ACCESS_CONTROL_PROFILES,

    // Returned if access was denied because of user authentication.
    EIC_ACCESS_CHECK_RESULT_USER_AUTHENTICATION_FAILED,

    // Returned if access was denied because of reader authentication.
    EIC_ACCESS_CHECK_RESULT_READER_AUTHENTICATION_FAILED,
} EicAccessCheckResult;

// Passes enough information to calculate the MACing key and/or prepare ECDSA signing
//
bool eicPresentationPrepareDeviceAuthentication(
        EicPresentation* ctx, const uint8_t* sessionTranscript, size_t sessionTranscriptSize,
        const uint8_t* readerEphemeralPublicKey, size_t readerEphemeralPublicKeySize,
        const uint8_t signingKeyBlob[60], const char* docType, size_t docTypeLength,
        unsigned int numNamespacesWithValues, size_t expectedDeviceNamespacesSize);

// The scratchSpace should be set to a buffer at least 512 bytes (ideally 1024
// bytes, the bigger the better). It's done this way to avoid allocating stack
// space.
//
EicAccessCheckResult eicPresentationStartRetrieveEntryValue(
        EicPresentation* ctx, const char* nameSpace, size_t nameSpaceLength,
        const char* name, size_t nameLength,
        unsigned int newNamespaceNumEntries, int32_t entrySize,
        const uint8_t* accessControlProfileIds, size_t numAccessControlProfileIds,
        uint8_t* scratchSpace, size_t scratchSpaceSize);

// Note: |content| must be big enough to hold |encryptedContentSize| - 28 bytes.
//
// The scratchSpace should be set to a buffer at least 512 bytes. It's done this way to
// avoid allocating stack space.
//
bool eicPresentationRetrieveEntryValue(EicPresentation* ctx, const uint8_t* encryptedContent,
                                       size_t encryptedContentSize, uint8_t* content,
                                       const char* nameSpace, size_t nameSpaceLength,
                                       const char* name, size_t nameLength,
                                       const uint8_t* accessControlProfileIds,
                                       size_t numAccessControlProfileIds,
                                       uint8_t* scratchSpace,
                                       size_t scratchSpaceSize);

// Returns the HMAC-SHA256 of |ToBeMaced| as per RFC 8051 "6.3. How to Compute
// and Verify a MAC".
bool eicPresentationFinishRetrieval(EicPresentation* ctx, uint8_t* digestToBeMaced,
                                    size_t* digestToBeMacedSize);

// Like eicPresentationFinishRetrieval() but also returns an ECDSA signature.
//
bool eicPresentationFinishRetrievalWithSignature(EicPresentation* ctx, uint8_t* digestToBeMaced,
                                                 size_t* digestToBeMacedSize,
                                                 uint8_t* signatureOfToBeSigned,
                                                 size_t* signatureOfToBeSignedSize);

// The data returned in |signatureOfToBeSigned| contains the ECDSA signature of
// the ToBeSigned CBOR from RFC 8051 "4.4. Signing and Verification Process"
// where content is set to the ProofOfDeletion CBOR.
//
bool eicPresentationDeleteCredential(EicPresentation* ctx, const char* docType, size_t docTypeLength,
                                     const uint8_t* challenge, size_t challengeSize,
                                     bool includeChallenge, size_t proofOfDeletionCborSize,
                                     uint8_t signatureOfToBeSigned[EIC_ECDSA_P256_SIGNATURE_SIZE]);

// The data returned in |signatureOfToBeSigned| contains the ECDSA signature of
// the ToBeSigned CBOR from RFC 8051 "4.4. Signing and Verification Process"
// where content is set to the ProofOfOwnership CBOR.
//
bool eicPresentationProveOwnership(EicPresentation* ctx, const char* docType, size_t docTypeLength,
                                   bool testCredential, const uint8_t* challenge, size_t challengeSize,
                                   size_t proofOfOwnershipCborSize,
                                   uint8_t signatureOfToBeSigned[EIC_ECDSA_P256_SIGNATURE_SIZE]);

#ifdef __cplusplus
}
#endif

#endif  // ANDROID_HARDWARE_IDENTITY_EIC_PRESENTATION_H
