/*
**
** Copyright 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.
*/

#include "keystore_aidl_hidl_marshalling_utils.h"

#include <keystore/ExportResult.h>
#include <keystore/KeyCharacteristics.h>
#include <keystore/KeymasterBlob.h>
#include <keystore/KeymasterCertificateChain.h>
#include <keystore/keymaster_types.h>
#include <keystore/keystore_hidl_support.h>

namespace keystore {

// reads byte[]
hidl_vec<uint8_t> readKeymasterBlob(const android::Parcel& in) {

    ssize_t length = in.readInt32();
    if (length <= 0) {
        return {};
    }

    const void* buf = in.readInplace(length);
    if (!buf) return {};

    return blob2hidlVec(reinterpret_cast<const uint8_t*>(buf), size_t(length));
}

android::status_t writeKeymasterBlob(const hidl_vec<uint8_t>& blob, android::Parcel* out) {
    int32_t size = int32_t(std::min<size_t>(blob.size(), std::numeric_limits<int32_t>::max()));

    auto rc = out->writeInt32(size);
    if (rc != ::android::OK) return rc;

    if (!size) return ::android::OK;

    return out->write(blob.data(), size);
}

android::status_t writeKeymasterBlob(const ::std::vector<int32_t>& blob, android::Parcel* out) {

    int32_t size = int32_t(std::min<size_t>(blob.size(), std::numeric_limits<int32_t>::max()));

    auto rc = out->writeInt32(size);
    if (rc != ::android::OK) return rc;

    if (!size) return ::android::OK;

    return out->write(blob.data(), size);
}

NullOr<KeyParameter> readKeyParameterFromParcel(const android::Parcel& in) {
    // Method must be in sync with KeymasterArgument.java
    if (in.readInt32() == 0) {
        return {};
    }
    KeyParameter result;

    Tag tag = static_cast<Tag>(in.readInt32());
    result.tag = tag;
    switch (typeFromTag(tag)) {
    case TagType::ENUM:
    case TagType::ENUM_REP:
    case TagType::UINT:
    case TagType::UINT_REP:
        result.f.integer = in.readInt32();
        break;
    case TagType::ULONG:
    case TagType::ULONG_REP:
    case TagType::DATE:
        result.f.longInteger = in.readInt64();
        break;
    case TagType::BOOL:
        result.f.boolValue = true;
        break;
    case TagType::BIGNUM:
    case TagType::BYTES:
        result.blob = readKeymasterBlob(in);  // byte array
        break;
    default:
        ALOGE("Unsupported KeyParameter tag %d", tag);
        return {};
    }
    return result;
}

android::status_t writeKeyParameterToParcel(const KeyParameter& param, android::Parcel* out) {
    // Method must be in sync with with KeymasterArgument.java
    // Presence flag must be written by caller.

    auto tag = param.tag;
    auto rc = out->writeInt32(uint32_t(tag));
    if (rc != ::android::OK) return rc;
    switch (typeFromTag(param.tag)) {
    case TagType::ENUM:
    case TagType::ENUM_REP:
    case TagType::UINT:
    case TagType::UINT_REP:
        rc = out->writeInt32(param.f.integer);
        break;
    case TagType::ULONG:
    case TagType::ULONG_REP:
    case TagType::DATE:
        rc = out->writeInt64(param.f.longInteger);
        break;
    case TagType::BOOL:
        // nothing to do here presence indicates true
        break;
    case TagType::BIGNUM:
    case TagType::BYTES:
        rc = writeKeymasterBlob(param.blob, out);
        break;
    default:
        ALOGE("Failed to write KeyParameter: Unsupported tag %d", param.tag);
        rc = android::BAD_VALUE;
        break;
    }
    return rc;
}

hidl_vec<KeyParameter> readParamSetFromParcel(const android::Parcel& in) {

    ssize_t length = in.readInt32();  // -1 for null
    size_t ulength = (size_t)length;
    if (length < 0) {
        ulength = 0;
    }
    hidl_vec<KeyParameter> result;
    result.resize(ulength);
    for (size_t i = 0; i < ulength; ++i) {
        auto param = readKeyParameterFromParcel(in);
        if (!param.isOk()) {
            ALOGE("Error reading KeyParameter from parcel");
            return {};
        }
        result[i] = param.value();
    }
    return result;
}

android::status_t writeParamSetToParcel(const hidl_vec<KeyParameter>& params,
                                        android::Parcel* out) {
    int32_t size = int32_t(std::min<size_t>(params.size(), std::numeric_limits<int32_t>::max()));

    auto rc = out->writeInt32(size);
    if (rc != ::android::OK) return rc;
    for (int32_t i = 0; i < size; ++i) {
        rc = out->writeInt32(1);  // writeTypedObject presence flag.
        if (rc != ::android::OK) return rc;
        rc = writeKeyParameterToParcel(params[i], out);
        if (rc != ::android::OK) return rc;
    }
    return rc;
}

hidl_vec<hidl_vec<uint8_t>> readCertificateChainFromParcel(const android::Parcel& in) {
    hidl_vec<hidl_vec<uint8_t>> result;

    ssize_t count = in.readInt32();
    size_t ucount = count;
    if (count <= 0) {
        return result;
    }

    result.resize(ucount);

    for (size_t i = 0; i < ucount; ++i) {
        result[i] = readKeymasterBlob(in);
    }
    return result;
};

android::status_t writeCertificateChainToParcel(const hidl_vec<hidl_vec<uint8_t>>& certs,
                                                android::Parcel* out) {
    int32_t count = int32_t(std::min<size_t>(certs.size(), std::numeric_limits<int32_t>::max()));
    auto rc = out->writeInt32(count);

    for (int32_t i = 0; i < count; ++i) {
        rc = writeKeymasterBlob(certs[i], out);
        if (rc != ::android::OK) return rc;
    }
    return rc;
}

};  // namespace keystore

// Implementation for  keystore parcelables.
// TODO: split implementation into separate classes
namespace android {
namespace security {
namespace keymaster {

using ::android::status_t;
using ::keystore::keymaster::ErrorCode;

ExportResult::ExportResult() : resultCode() {}

ExportResult::~ExportResult() {}

status_t ExportResult::readFromParcel(const Parcel* inn) {
    const Parcel& in = *inn;
    resultCode = ErrorCode(in.readInt32());
    exportData = keystore::readKeymasterBlob(in);
    return OK;
}

status_t ExportResult::writeToParcel(Parcel* out) const {
    out->writeInt32(resultCode.getErrorCode());
    return keystore::writeKeymasterBlob(exportData, out);
}

status_t KeyCharacteristics::readFromParcel(const Parcel* in) {
    softwareEnforced.readFromParcel(in);
    return hardwareEnforced.readFromParcel(in);
}

status_t KeyCharacteristics::writeToParcel(Parcel* out) const {
    softwareEnforced.writeToParcel(out);
    return hardwareEnforced.writeToParcel(out);
}

status_t KeymasterBlob::readFromParcel(const Parcel* in) {
    data_ = keystore::readKeymasterBlob(*in);
    return OK;
}

status_t KeymasterBlob::writeToParcel(Parcel* out) const {
    return keystore::writeKeymasterBlob(data_, out);
}

status_t KeymasterCertificateChain::readFromParcel(const Parcel* in) {
    chain = keystore::readCertificateChainFromParcel(*in);
    return OK;
}

status_t KeymasterCertificateChain::writeToParcel(Parcel* out) const {
    return keystore::writeCertificateChainToParcel(chain, out);
}

}  // namespace keymaster
}  // namespace security

}  // namespace android