/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "Licence").
 * You may not use this file except in compliance with the Licence.
 *
 * You can obtain a copy of the licence at @.^.^.LICENSE.
 * See the Licence for the specific language governing permissions
 * and limitations under the Licence.
 *
 * CDDL HEADER END
 *
 * Copyright (c) RISC OS Developments 2026. All rights reserved.
 * SPDX-License-Identifier: CDDL-1.0
 */

#include <string.h>

#include "manifest.h"

static unsigned int get32(const unsigned char *p)
{
    return ((unsigned int)p[0] << 24) |
           ((unsigned int)p[1] << 16) |
           ((unsigned int)p[2] << 8) |
           (unsigned int)p[3];
}

static int counter_compare(tlsmanager_counter a, tlsmanager_counter b)
{
    if(a.high < b.high) return -1;
    if(a.high > b.high) return 1;
    if(a.low < b.low) return -1;
    if(a.low > b.low) return 1;
    return 0;
}

tlsmanager_manifest_result TLSManagerManifestParse(
    const unsigned char *data,
    size_t size,
    tlsmanager_counter now,
    tlsmanager_counter minimum_sequence,
    tlsmanager_signature_verifier verifier,
    void *verifier_opaque,
    tlsmanager_manifest *manifest)
{
    unsigned int count;
    unsigned int payload_size;
    unsigned int i;
    tlsmanager_manifest candidate;

    if(!data || !manifest || size < TLSMANAGER_MANIFEST_HEADER_SIZE)
        return TLSMANAGER_MANIFEST_TRUNCATED;
    if(memcmp(data, "TLSM", 4) != 0) return TLSMANAGER_MANIFEST_MAGIC;
    if(get32(data + 4) != TLSMANAGER_MANIFEST_VERSION ||
       get32(data + 8) != TLSMANAGER_MANIFEST_HEADER_SIZE)
        return TLSMANAGER_MANIFEST_VERSION_ERROR;

    count = get32(data + 40);
    payload_size = get32(data + 44);
    if(count == 0 || count > TLSMANAGER_MANIFEST_MAX_ANCHORS)
        return TLSMANAGER_MANIFEST_COUNT;
    if(payload_size != TLSMANAGER_MANIFEST_HEADER_SIZE +
                       count * TLSMANAGER_SHA256_SIZE)
        return TLSMANAGER_MANIFEST_LENGTH;
    if(size != (size_t)payload_size + TLSMANAGER_MANIFEST_SIGNATURE_SIZE)
        return TLSMANAGER_MANIFEST_LENGTH;

    candidate.sequence.high = get32(data + 12);
    candidate.sequence.low = get32(data + 16);
    candidate.created.high = get32(data + 20);
    candidate.created.low = get32(data + 24);
    candidate.expires.high = get32(data + 28);
    candidate.expires.low = get32(data + 32);
    candidate.key_id = get32(data + 36);
    candidate.anchor_count = count;
    candidate.anchors = data + TLSMANAGER_MANIFEST_HEADER_SIZE;

    if(counter_compare(candidate.sequence, minimum_sequence) <= 0)
        return TLSMANAGER_MANIFEST_ROLLBACK;
    if(counter_compare(now, candidate.created) < 0)
        return TLSMANAGER_MANIFEST_NOT_YET_VALID;
    if(counter_compare(now, candidate.expires) >= 0)
        return TLSMANAGER_MANIFEST_EXPIRED;

    for(i = 1; i < count; i++)
        if(memcmp(candidate.anchors + (i - 1) * TLSMANAGER_SHA256_SIZE,
                  candidate.anchors + i * TLSMANAGER_SHA256_SIZE,
                  TLSMANAGER_SHA256_SIZE) >= 0)
            return TLSMANAGER_MANIFEST_ORDER;

    if(!verifier) return TLSMANAGER_MANIFEST_KEY;
    if(!verifier(candidate.key_id, data, payload_size,
                 data + payload_size, verifier_opaque))
        return TLSMANAGER_MANIFEST_SIGNATURE;

    *manifest = candidate;
    return TLSMANAGER_MANIFEST_OK;
}

const char *TLSManagerManifestResultText(tlsmanager_manifest_result result)
{
    static const char *messages[] =
    {
        "valid", "truncated manifest", "bad manifest magic",
        "unsupported manifest version", "bad manifest length",
        "bad anchor count", "anchors are duplicated or unsorted",
        "manifest rollback", "manifest is not yet valid",
        "manifest has expired", "unknown signing key", "bad signature"
    };
    unsigned int index;

    index = (unsigned int)result;
    if(index >= sizeof(messages) / sizeof(messages[0]))
        return "unknown manifest error";
    return messages[index];
}
