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

arden.2.1.3.source-code.Throwables.kt Maven / Gradle / Ivy

The newest version!
package at.asitplus.attestation

import at.asitplus.attestation.AttestationException.Certificate
import at.asitplus.attestation.AttestationException.Content
import at.asitplus.attestation.android.exceptions.AndroidAttestationException
import at.asitplus.attestation.android.exceptions.AttestationValueException
import kotlinx.datetime.Clock
import java.security.PublicKey


/**
 * Indicated the platform an attestation check failed for.
 */
enum class Platform {
    IOS,
    ANDROID,

    /**
     * Indicates that the attestation data provided was utterly incomprehensible and no indication of platform is possible
     */
    UNKNOWN
}

/**
 * Provides additional details on why an attestation attempt failed, indicating which [platform] was being attested.
 * Although many reasons exist why an attestation may fail, all of them can be put into two categories:
 *  * [Content] e.g. version, app name, challenge, or key mismatch
 *  * [Certificate] e.g. chain rooted in an untrusted certificate, revocation, validity period
 *      * [Certificate.Trust] for trust issues
 *      * [Certificate.Time] for clock offset-related issues
 *
 *  More specific details about why the attestation failed are communicated to humans in [message] and are differentiated in [cause].
 *  A dedicated lazy property [platformSpecificCause] is present to evaluate and act upon such details (which is just an alias for [cause]).
 *  On Android, [platformSpecificCause] will always be an instance of [AndroidAttestationException] containing an
 *  [enumerable reason](https://a-sit-plus.github.io/android-attestation/-android%20%20-attestation%20-library/at.asitplus.attestation.android.exceptions/index.html)
 *  which will provide insights.
 *  
* For iOS, less verbose details are communicated in [platformSpecificCause] due to a lack of a common root for all throwables. * * Concrete examples of platform-specific error cases and their corresponding exceptions and reason codes, refer to [Content], [Certificate.Trust], and [Certificate.Time] * */ sealed class AttestationException(val platform: Platform, message: String? = null, cause: Throwable) : Throwable(message, cause) { /** * Alias for [cause] (with more accurate semantics) */ val platformSpecificCause by lazy { cause } /** * Indicates that some value (key to be attests, bundle identifier or package name, team identified or signer certificate, etc.) failed to verify * This usually means that either the client's OS or the app was compromised/modified (or an implementation error occurred). *
* For Android, [platformSpecificCause] is always an [AttestationValueException], on iOS it is always an [IosAttestationException], * both of which have enumerable `reason` property, which is documented. * * ### Android Examples: * * Invalid package name: `platformSpecificCause.shouldBeInstanceOf().reason shouldBe AttestationValueException.Reason.PACKAGE_NAME` * * Challenge mismatch: `platformSpecificCause.shouldBeInstanceOf().reason shouldBe AttestationValueException.Reason.CHALLENGE` * * OS version too low: `platformSpecificCause.shouldBeInstanceOf().reason shouldBe AttestationValueException.Reason.OS_VERSION` * * Public Key mismatch: `platformSpecificCause.shouldBeInstanceOf().reason shouldBe AttestationValueException.Reason.APP_UNEXPECTED` * * System integrity: `platformSpecificCause.shouldBeInstanceOf().reason shouldBe AttestationValueException.Reason.SYSTEM_INTEGRITY` * * A mismatch in security level can either result in the [platformSpecificCause] being [Content] and reason being [AttestationValueException.Reason.SEC_LEVEL] * or a [Certificate.Trust] exception due to mismatching root certificates * * ### iOS Examples * * Invalid bundle identifier / team id / stage: `platformSpecificCause.shouldBeInstanceOf().reason shouldBe IosAttestationException.Reason.IDENTIFIER` * * Challenge mismatch: `platformSpecificCause.shouldBeInstanceOf().reason shouldBe IosAttestationException.Reason.CHALLENGE` * * OS version too low: `platformSpecificCause.shouldBeInstanceOf().reason shouldBe IosAttestationException.Reason.OS_VERSION` * * Public Key mismatch: `platformSpecificCause.shouldBeInstanceOf().reason shouldBe IosAttestationException.Reason.APP_UNEXPECTED` * * System integrity mismatch won't result in a valid attestation object obtained on the client, so this can never reach the back-end, except as a bogus attestation proof * that will fail the attestation check in various ways depending on how this fake proof was constructed. * * */ open class Content private constructor(platform: Platform, message: String?, cause: Throwable) : AttestationException(platform, message = message, cause = cause) { companion object { fun Android(message: String? = null, cause: AttestationValueException) = Content(Platform.ANDROID, message, cause) fun iOS(message: String? = null, cause: IosAttestationException) = Content(Platform.IOS, message, cause) fun Unknown(message: String? = null, cause: Throwable) = Content(Platform.UNKNOWN, message, cause) } } /** * Indicates a problem verifying the certificate chain the attestation statement is built upon. * This can either be just a borked certificate chain (i.e. just a wrong certificate somewhere in the chain), * or the chain not being rooted in a valid trust anchor. * Most probably, however, a legitimate app on a trusted OS will simply have fallen victim to the system clock * being too far ahead. This heavily depends on the OS a device was originally shipped with, * (or just Samsung being Samsung and being unable to correctly encode a timestamp conforming to ASN.1) */ sealed class Certificate(platform: Platform, message: String?, cause: Throwable) : AttestationException(platform, message = message, cause = cause) { /** * Indicates that temporal certificate chain verification failed * (i.e. the client's clock is not synchronised with the back-end) */ class Time private constructor(platform: Platform, message: String?, cause: Throwable) : Certificate(platform, message, cause) { companion object { fun Android(message: String? = null, cause: AndroidAttestationException) = Time(Platform.ANDROID, message, cause) fun iOS( message: String? = null, cause: Throwable ) = Time(Platform.IOS, message, cause) } } /** * Indicates either a borked certificate chain or one that is not rooted in one of the configured trust anchors */ class Trust private constructor(platform: Platform, message: String?, cause: Throwable) : Certificate(platform, message, cause) { companion object { fun Android(message: String? = null, cause: AndroidAttestationException) = Trust(Platform.ANDROID, message, cause) fun iOS( message: String? = null, cause: Throwable ) = Trust(Platform.IOS, message, cause) } } } /** * Thrown on instantiation, for illegal configurations (e.g. no apps configured) */ class Configuration(platform: Platform, message: String? = null, cause: Throwable) : AttestationException(platform, message = message, cause = cause) override fun toString() = "AttestationException.${this::class.simpleName}: platform: $platform, message: ${message ?: cause?.message}, cause: $cause" } class IosAttestationException(msg: String? = null, cause: Throwable? = null, val reason: Reason) : Throwable(msg ?: cause?.message, cause) { enum class Reason { /** * Version number does not satisfy constraints (e.g. iOS version is too old) */ OS_VERSION, /** * Signature counter in the assertion is too high. This could mean either an implementation error on the client, or a compromised client app. */ SIG_CTR, /** * Team ID and/or bundle ID and/or stage (development, production) mismatch */ IDENTIFIER, /** * Happens if the challenge in the attestation record does not match the expected challenge */ CHALLENGE, /** * Generic case, which must not happen for an authentic app. could be borked assertion data * (initial counter not zero, invalid attestation data, etc, KID mismatch). * In general, it is hard to tell whether app developers made a mistake or something fishy is going on */ APP_UNEXPECTED, } } internal fun logicalError( keyToBeAttested: T, attestationProof: List, expectedChallenge: ByteArray ) = RuntimeException("Logical Error attesting key ${ keyToBeAttested.encoded.encodeBase64() } for attestation proof ${ attestationProof.joinToString { it.encodeBase64() } } with challenge ${expectedChallenge.encodeBase64()} at ${Clock.System.now()}")




© 2015 - 2024 Weber Informatics LLC | Privacy Policy