All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.at.asitplus.signum.indispensable.cosef.CoseHeader.kt Maven / Gradle / Ivy

The newest version!
package at.asitplus.signum.indispensable.cosef

import at.asitplus.catching
import at.asitplus.signum.indispensable.cosef.io.Base16Strict
import at.asitplus.signum.indispensable.cosef.io.coseCompliantSerializer
import io.matthewnelson.encoding.core.Encoder.Companion.encodeToString
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.cbor.ByteString
import kotlinx.serialization.cbor.CborLabel
import kotlinx.serialization.decodeFromByteArray
import kotlinx.serialization.encodeToByteArray

/**
 * Protected header of a [CoseSigned].
 *
 * See [RFC 9052](https://www.rfc-editor.org/rfc/rfc9052.html).
 */
@OptIn(ExperimentalSerializationApi::class)
@Serializable
data class CoseHeader(
    /**
     * This header parameter is used to indicate the algorithm used for the security processing. This header parameter
     * MUST be authenticated where the ability to do so exists. This support is provided by AEAD algorithms or
     * construction (e.g., COSE_Sign and COSE_Mac0). This authentication can be done either by placing the header
     * parameter in the protected-header-parameters bucket or as part of the externally supplied data (Section 4.3).
     * The value is taken from the "COSE Algorithms" registry.
     */
    @CborLabel(1)
    @SerialName("alg")
    val algorithm: CoseAlgorithm? = null,

    /**
     * This header parameter is used to indicate which protected header parameters an application that is processing a
     * message is required to understand. Header parameters defined in this document do not need to be included, as they
     * should be understood by all implementations. Additionally, the header parameter "counter signature" (label 7)
     * defined by RFC8152 must be understood by new implementations, to remain compatible with senders that adhere to
     * that document and assume all implementations will understand it. When present, the "crit" header parameter MUST
     * be placed in the protected-header-parameters bucket. The array MUST have at least one value in it.
     */
    @CborLabel(2)
    @SerialName("crit")
    val criticalHeaders: String? = null,

    /**
     * This header parameter is used to indicate the content type of the data in the "payload" or "ciphertext" field.
     * Integers are from the "CoAP Content-Formats" IANA registry table. Text values follow the syntax of
     * "/", where  and  are defined in Section 4.2 of RFC6838.
     * Leading and trailing whitespace is not permitted. Textual content type values, along with parameters and
     * subparameters, can be located using the IANA "Media Types" registry. Applications SHOULD provide this header
     * parameter if the content structure is potentially ambiguous.
     */
    @CborLabel(3)
    @SerialName("content type")
    val contentType: String? = null,

    /**
     * This header parameter identifies one piece of data that can be used as input to find the needed cryptographic
     * key. The value of this header parameter can be matched against the "kid" member in a COSE_Key structure. Other
     * methods of key distribution can define an equivalent field to be matched. Applications MUST NOT assume that "kid"
     * values are unique. There may be more than one key with the same "kid" value, so all of the keys associated with
     * this "kid" may need to be checked. The internal structure of "kid" values is not defined and cannot be relied on
     * by applications. Key identifier values are hints about which key to use. This is not a security-critical field.
     * For this reason, it can be placed in the unprotected-header-parameters bucket.
     */
    @CborLabel(4)
    @SerialName("kid")
    @ByteString
    val kid: ByteArray? = null,

    /**
     * This header parameter holds the Initialization Vector (IV) value. For some symmetric encryption algorithms, this
     * may be referred to as a nonce. The IV can be placed in the unprotected bucket, since for AE and AEAD algorithms,
     * modifying the IV will cause the decryption to fail.
     */
    @CborLabel(5)
    @SerialName("IV")
    @ByteString
    val iv: ByteArray? = null,

    /**
     * This header parameter holds a part of the IV value. When using the COSE_Encrypt0 structure, a portion of the IV
     * can be part of the context associated with the key (Context IV), while a portion can be changed with each message
     * (Partial IV). This field is used to carry a value that causes the IV to be changed for each message. The Partial
     * IV can be placed in the unprotected bucket, as modifying the value will cause the decryption to yield plaintext
     * that is readily detectable as garbled. The "Initialization Vector" and "Partial Initialization Vector" header
     * parameters MUST NOT both be present in the same security layer.
     */
    @CborLabel(6)
    @SerialName("Partial IV")
    @ByteString
    val partialIv: ByteArray? = null,

    /**
     * OID4VCI: COSE key material the new Credential shall be bound to.
     */
    @SerialName("COSE_Key")
    @ByteString
    val coseKey: ByteArray? = null,

    /**
     * This header parameter contains an ordered array of X.509 certificates. The certificates are to be ordered
     * starting with the certificate containing the end-entity key followed by the certificate that signed it, and so
     * on. There is no requirement for the entire chain to be present in the element if there is reason to believe that
     * the relying party already has, or can locate, the missing certificates. This means that the relying party is
     * still required to do path building but that a candidate path is proposed in this header parameter.
     *
     * This header parameter allows for a single X.509 certificate or a chain of X.509 certificates to be carried in
     * the message.
     *
     * See [RFC9360](https://www.rfc-editor.org/rfc/rfc9360.html)
     */
    @CborLabel(33)
    @SerialName("x5chain")
    @ByteString
    // TODO Might also be an array, if there is a real chain, not only one cert
    val certificateChain: ByteArray? = null,
) {

    fun serialize() = coseCompliantSerializer.encodeToByteArray(this)

    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (other == null || this::class != other::class) return false

        other as CoseHeader

        if (algorithm != other.algorithm) return false
        if (criticalHeaders != other.criticalHeaders) return false
        if (contentType != other.contentType) return false
        if (kid != null) {
            if (other.kid == null) return false
            if (!kid.contentEquals(other.kid)) return false
        } else if (other.kid != null) return false
        if (iv != null) {
            if (other.iv == null) return false
            if (!iv.contentEquals(other.iv)) return false
        } else if (other.iv != null) return false
        if (partialIv != null) {
            if (other.partialIv == null) return false
            if (!partialIv.contentEquals(other.partialIv)) return false
        } else if (other.partialIv != null) return false
        if (coseKey != null) {
            if (other.coseKey == null) return false
            if (!coseKey.contentEquals(other.coseKey)) return false
        } else if (other.coseKey != null) return false
        if (certificateChain != null) {
            if (other.certificateChain == null) return false
            if (!certificateChain.contentEquals(other.certificateChain)) return false
        } else if (other.certificateChain != null) return false

        return true
    }

    override fun hashCode(): Int {
        var result = algorithm?.hashCode() ?: 0
        result = 31 * result + (criticalHeaders?.hashCode() ?: 0)
        result = 31 * result + (contentType?.hashCode() ?: 0)
        result = 31 * result + (kid?.contentHashCode() ?: 0)
        result = 31 * result + (iv?.contentHashCode() ?: 0)
        result = 31 * result + (partialIv?.contentHashCode() ?: 0)
        result = 31 * result + (coseKey?.contentHashCode() ?: 0)
        result = 31 * result + (certificateChain?.contentHashCode() ?: 0)
        return result
    }

    override fun toString(): String {
        return "CoseHeader(algorithm=$algorithm," +
                " criticalHeaders=$criticalHeaders," +
                " contentType=$contentType," +
                " kid=${kid?.encodeToString(Base16Strict)}," +
                " iv=${iv?.encodeToString(Base16Strict)}," +
                " partialIv=${partialIv?.encodeToString(Base16Strict)}," +
                " coseKey=${coseKey?.encodeToString(Base16Strict)}," +
                " certificateChain=${certificateChain?.encodeToString(Base16Strict)})"
    }


    companion object {
        fun deserialize(it: ByteArray) = catching {
            coseCompliantSerializer.decodeFromByteArray(it)
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy