/*
 * Copyright (C) 2014 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.
 */

#ifndef KEYSTORE_KEYMASTER_ENFORCEMENT_H
#define KEYSTORE_KEYMASTER_ENFORCEMENT_H

#include <stdio.h>

#include <keystore/keymaster_types.h>

#include <list>
#include <mutex>
#include <optional>

namespace keystore {

typedef uint64_t km_id_t;

class KeymasterEnforcementContext {
  public:
    virtual ~KeymasterEnforcementContext() {}
    /*
     * Get current time.
     */
};

class AccessTimeMap {
  public:
    explicit AccessTimeMap(uint32_t max_size) : max_size_(max_size) {}

    /* If the key is found, returns true and fills \p last_access_time.  If not found returns
     * false. */
    bool LastKeyAccessTime(km_id_t keyid, uint32_t* last_access_time) const;

    /* Updates the last key access time with the currentTime parameter.  Adds the key if
     * needed, returning false if key cannot be added because list is full. */
    bool UpdateKeyAccessTime(km_id_t keyid, uint32_t current_time, uint32_t timeout);

  private:
    mutable std::mutex list_lock_;
    struct AccessTime {
        km_id_t keyid;
        uint32_t access_time;
        uint32_t timeout;
    };
    std::list<AccessTime> last_access_list_;
    const uint32_t max_size_;
};

class AccessCountMap {
  public:
    explicit AccessCountMap(uint32_t max_size) : max_size_(max_size) {}

    /* If the key is found, returns true and fills \p count.  If not found returns
     * false. */
    bool KeyAccessCount(km_id_t keyid, uint32_t* count) const;

    /* Increments key access count, adding an entry if the key has never been used.  Returns
     * false if the list has reached maximum size. */
    bool IncrementKeyAccessCount(km_id_t keyid);

  private:
    mutable std::mutex list_lock_;
    struct AccessCount {
        km_id_t keyid;
        uint64_t access_count;
    };
    std::list<AccessCount> access_count_list_;
    const uint32_t max_size_;
};

class KeymasterEnforcement {
  public:
    /**
     * Construct a KeymasterEnforcement.
     */
    KeymasterEnforcement(uint32_t max_access_time_map_size, uint32_t max_access_count_map_size);
    virtual ~KeymasterEnforcement();

    /**
     * Iterates through the authorization set and returns the corresponding keymaster error. Will
     * return KM_ERROR_OK if all criteria is met for the given purpose in the authorization set with
     * the given operation params and handle. Used for encrypt, decrypt sign, and verify.
     */
    ErrorCode AuthorizeOperation(const KeyPurpose purpose, const km_id_t keyid,
                                 const AuthorizationSet& auth_set,
                                 const AuthorizationSet& operation_params,
                                 const HardwareAuthToken& auth_token, uint64_t op_handle,
                                 bool is_begin_operation);

    /**
     * Iterates through the authorization set and returns the corresponding keymaster error. Will
     * return KM_ERROR_OK if all criteria is met for the given purpose in the authorization set with
     * the given operation params. Used for encrypt, decrypt sign, and verify.
     */
    ErrorCode AuthorizeBegin(const KeyPurpose purpose, const km_id_t keyid,
                             const AuthorizationSet& auth_set,
                             const AuthorizationSet& operation_params,
                             NullOr<const HardwareAuthToken&> auth_token);

    /**
     * Iterates through the authorization set and returns the corresponding keymaster error. Will
     * return KM_ERROR_OK if all criteria is met for the given purpose in the authorization set with
     * the given operation params and handle. Used for encrypt, decrypt sign, and verify.
     */
    ErrorCode AuthorizeUpdate(const AuthorizationSet& auth_set, const HardwareAuthToken& auth_token,
                              uint64_t op_handle) {
        return AuthorizeUpdateOrFinish(auth_set, auth_token, op_handle);
    }

    /**
     * Iterates through the authorization set and returns the corresponding keymaster error. Will
     * return KM_ERROR_OK if all criteria is met for the given purpose in the authorization set with
     * the given operation params and handle. Used for encrypt, decrypt sign, and verify.
     */
    ErrorCode AuthorizeFinish(const AuthorizationSet& auth_set, const HardwareAuthToken& auth_token,
                              uint64_t op_handle) {
        return AuthorizeUpdateOrFinish(auth_set, auth_token, op_handle);
    }

    /**
     * Creates a key ID for use in subsequent calls to AuthorizeOperation.  Clients needn't use this
     * method of creating key IDs, as long as they use something consistent and unique.  This method
     * hashes the key blob.
     *
     * Returns false if an error in the crypto library prevents creation of an ID.
     */
    static std::optional<km_id_t> CreateKeyId(const hidl_vec<uint8_t>& key_blob);

    //
    // Methods that must be implemented by subclasses
    //
    // The time-related methods address the fact that different enforcement contexts may have
    // different time-related capabilities.  In particular:
    //
    // - They may or may not be able to check dates against real-world clocks.
    //
    // - They may or may not be able to check timestampls against authentication trustlets (minters
    //   of hw_auth_token_t structs).
    //
    // - They must have some time source for relative times, but may not be able to provide more
    //   than reliability and monotonicity.

    /*
     * Returns true if the specified activation date has passed, or if activation cannot be
     * enforced.
     */
    virtual bool activation_date_valid(uint64_t activation_date) const = 0;

    /*
     * Returns true if the specified expiration date has passed.  Returns false if it has not, or if
     * expiration cannot be enforced.
     */
    virtual bool expiration_date_passed(uint64_t expiration_date) const = 0;

    /*
     * Returns true if the specified auth_token is older than the specified timeout.
     */
    virtual bool auth_token_timed_out(const HardwareAuthToken& token, uint32_t timeout) const = 0;

    /*
     * Get current time in seconds from some starting point.  This value is used to compute relative
     * times between events.  It must be monotonically increasing, and must not skip or lag.  It
     * need not have any relation to any external time standard (other than the duration of
     * "second").
     *
     * On POSIX systems, it's recommented to use clock_gettime(CLOCK_MONOTONIC, ...) to implement
     * this method.
     */
    virtual uint32_t get_current_time() const = 0;

    /*
     * Returns true if the specified auth_token has a valid signature, or if signature validation is
     * not available.
     */
    virtual bool ValidateTokenSignature(const HardwareAuthToken& token) const = 0;

    /*
     * Returns true if the device screen is currently locked for the specified user.
     */
    virtual bool is_device_locked(int32_t userId) const = 0;

  private:
    ErrorCode AuthorizeUpdateOrFinish(const AuthorizationSet& auth_set,
                                      const HardwareAuthToken& auth_token, uint64_t op_handle);

    bool MinTimeBetweenOpsPassed(uint32_t min_time_between, const km_id_t keyid);
    bool MaxUsesPerBootNotExceeded(const km_id_t keyid, uint32_t max_uses);
    bool AuthTokenMatches(const AuthorizationSet& auth_set, const HardwareAuthToken& auth_token,
                          const uint64_t user_secure_id, const int auth_type_index,
                          const int auth_timeout_index, const uint64_t op_handle,
                          bool is_begin_operation) const;

    AccessTimeMap access_time_map_;
    AccessCountMap access_count_map_;
};

}; /* namespace keystore */

#endif  // KEYSTORE_KEYMASTER_ENFORCEMENT_H