/*
 * Copyright (C) 2016 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_KEYSTORE_H_
#define KEYSTORE_KEYSTORE_H_

#include <android/hardware/keymaster/3.0/IKeymasterDevice.h>
#include <keymasterV4_0/Keymaster.h>
#include <utils/Vector.h>

#include <keystore/keymaster_types.h>

#include "auth_token_table.h"
#include "blob.h"
#include "confirmation_manager.h"
#include "grant_store.h"
#include "keymaster_worker.h"
#include "keystore_keymaster_enforcement.h"
#include "operation.h"
#include "user_state.h"

#include <array>
#include <optional>
#include <tuple>

namespace keystore {

using ::android::sp;
using keymaster::support::Keymaster;

template <typename T, size_t count> class Devices : public std::array<T, count> {
  public:
    T& operator[](SecurityLevel secLevel) {
        static_assert(uint32_t(SecurityLevel::SOFTWARE) == 0 &&
                          uint32_t(SecurityLevel::TRUSTED_ENVIRONMENT) == 1 &&
                          uint32_t(SecurityLevel::STRONGBOX) == 2,
                      "Numeric values of security levels have changed");
        return std::array<T, count>::at(static_cast<uint32_t>(secLevel));
    }
    T operator[](SecurityLevel secLevel) const {
        if (static_cast<uint32_t>(secLevel) > static_cast<uint32_t>(SecurityLevel::STRONGBOX)) {
            LOG(ERROR) << "Invalid security level requested";
            return {};
        }
        return (*const_cast<Devices*>(this))[secLevel];
    }
};

}  // namespace keystore

namespace std {
template <typename T, size_t count> struct tuple_size<keystore::Devices<T, count>> {
  public:
    static constexpr size_t value = std::tuple_size<std::array<T, count>>::value;
};
}  // namespace std

namespace keystore {

using KeymasterWorkers = Devices<std::shared_ptr<KeymasterWorker>, 3>;
using KeymasterDevices = Devices<sp<Keymaster>, 3>;

class KeyStore : public ::android::IBinder::DeathRecipient {
  public:
    KeyStore(const KeymasterDevices& kmDevices,
             SecurityLevel minimalAllowedSecurityLevelForNewKeys);
    ~KeyStore();

    std::shared_ptr<KeymasterWorker> getDevice(SecurityLevel securityLevel) const {
        return mKmDevices[securityLevel];
    }

    std::shared_ptr<KeymasterWorker> getFallbackDevice() const {
        // we only return the fallback device if the creation of new fallback key blobs is
        // allowed. (also see getDevice below)
        if (mAllowNewFallback) {
            return mKmDevices[SecurityLevel::SOFTWARE];
        } else {
            return nullptr;
        }
    }

    std::shared_ptr<KeymasterWorker> getDevice(const Blob& blob) {
        return mKmDevices[blob.getSecurityLevel()];
    }

    ResponseCode initialize();

    State getState(uid_t userId) { return mUserStateDB.getUserState(userId)->getState(); }

    ResponseCode initializeUser(const android::String8& pw, uid_t userId);

    ResponseCode copyMasterKey(uid_t srcUser, uid_t dstUser);
    ResponseCode writeMasterKey(const android::String8& pw, uid_t userId);
    ResponseCode readMasterKey(const android::String8& pw, uid_t userId);

    LockedKeyBlobEntry getLockedBlobEntryIfNotExists(const std::string& alias, uid_t uid);
    std::optional<KeyBlobEntry> getBlobEntryIfExists(const std::string& alias, uid_t uid);
    LockedKeyBlobEntry getLockedBlobEntryIfExists(const std::string& alias, uid_t uid);
    /*
     * Delete entries owned by userId. If keepUnencryptedEntries is true
     * then only encrypted entries will be removed, otherwise all entries will
     * be removed.
     */
    void resetUser(uid_t userId, bool keepUnenryptedEntries);
    bool isEmpty(uid_t userId) const;

    void lock(uid_t userId);

    std::tuple<ResponseCode, Blob, Blob> get(const LockedKeyBlobEntry& blobfile);
    ResponseCode put(const LockedKeyBlobEntry& blobfile, Blob keyBlob, Blob characteristicsBlob);
    ResponseCode del(const LockedKeyBlobEntry& blobfile);

    std::string addGrant(const LockedKeyBlobEntry& blobfile, uid_t granteeUid);
    bool removeGrant(const LockedKeyBlobEntry& blobfile, const uid_t granteeUid);
    void removeAllGrantsToUid(const uid_t granteeUid);

    ResponseCode importKey(const uint8_t* key, size_t keyLen, const LockedKeyBlobEntry& blobfile,
                           uid_t userId, int32_t flags);

    bool isHardwareBacked(const android::String16& keyType) const;

    std::tuple<ResponseCode, Blob, Blob, LockedKeyBlobEntry>
    getKeyForName(const android::String8& keyName, const uid_t uid, const BlobType type);

    void binderDied(const ::android::wp<IBinder>& who) override;

    UserStateDB& getUserStateDB() { return mUserStateDB; }
    AuthTokenTable& getAuthTokenTable() { return mAuthTokenTable; }
    KeystoreKeymasterEnforcement& getEnforcementPolicy() { return mEnforcementPolicy; }
    ConfirmationManager& getConfirmationManager() { return *mConfirmationManager; }

  private:
    static const char* kOldMasterKey;
    static const char* kMetaDataFile;
    static const android::String16 kRsaKeyType;
    static const android::String16 kEcKeyType;

    KeymasterWorkers mKmDevices;

    bool mAllowNewFallback;

    UserStateDB mUserStateDB;
    AuthTokenTable mAuthTokenTable;
    KeystoreKeymasterEnforcement mEnforcementPolicy;
    sp<ConfirmationManager> mConfirmationManager;

    ::keystore::GrantStore mGrants;

    typedef struct { uint32_t version; } keystore_metadata_t;

    keystore_metadata_t mMetaData;

    /**
     * Upgrade the key from the current version to whatever is newest.
     */
    bool upgradeBlob(Blob* blob, const uint8_t oldVersion);

    void readMetaData();
    void writeMetaData();

    bool upgradeKeystore();
};

}  // namespace keystore

#endif  // KEYSTORE_KEYSTORE_H_