Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2021-2023 Appmattus Limited
* Copyright 2021-2024 Appmattus Limited
* Copyright 2019 Babylon Partners Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
Expand All @@ -21,7 +21,6 @@
package com.appmattus.certificatetransparency

import com.appmattus.certificatetransparency.internal.verifier.model.SignedCertificateTimestamp
import java.time.Instant

/**
* Abstract class providing the results of verifying a Signed Certificate Timestamp
Expand Down Expand Up @@ -66,7 +65,7 @@ public sealed interface SctVerificationResult {
* @property timestamp The timestamp of the SCT
* @property now The time now
*/
public data class FutureTimestamp(val timestamp: Instant, val now: Instant) : Invalid {
public data class FutureTimestamp(val timestamp: Long, val now: Long) : Invalid {
/**
* Returns a string representation of the object.
*/
Expand All @@ -78,7 +77,7 @@ public sealed interface SctVerificationResult {
* @property timestamp The timestamp of the SCT
* @property logServerValidUntil The time the log server was valid till
*/
public data class LogServerUntrusted(val timestamp: Instant, val logServerValidUntil: Instant) : Invalid {
public data class LogServerUntrusted(val timestamp: Long, val logServerValidUntil: Long) : Invalid {
/**
* Returns a string representation of the object.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2021-2023 Appmattus Limited
* Copyright 2021-2024 Appmattus Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -16,19 +16,18 @@

package com.appmattus.certificatetransparency.internal.loglist.deserializer

import com.appmattus.certificatetransparency.internal.utils.toRfc3339Instant
import com.appmattus.certificatetransparency.internal.utils.toRfc3339Long
import kotlinx.serialization.KSerializer
import kotlinx.serialization.descriptors.PrimitiveKind
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import java.time.Instant

internal class Rfc3339Deserializer : KSerializer<Instant> {
internal class Rfc3339Deserializer : KSerializer<Long> {

override val descriptor = PrimitiveSerialDescriptor("Rfc3339", PrimitiveKind.STRING)

override fun deserialize(decoder: Decoder) = decoder.decodeString().toRfc3339Instant()
override fun deserialize(decoder: Decoder) = decoder.decodeString().toRfc3339Long()

override fun serialize(encoder: Encoder, value: Instant) = error("Serialization not supported")
override fun serialize(encoder: Encoder, value: Long) = error("Serialization not supported")

Check warning on line 32 in certificatetransparency/src/main/kotlin/com/appmattus/certificatetransparency/internal/loglist/deserializer/Rfc3339Deserializer.kt

View check run for this annotation

Codecov / codecov/patch

certificatetransparency/src/main/kotlin/com/appmattus/certificatetransparency/internal/loglist/deserializer/Rfc3339Deserializer.kt#L32

Added line #L32 was not covered by tests
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2021-2023 Appmattus Limited
* Copyright 2021-2024 Appmattus Limited
* Copyright 2019 Babylon Partners Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
Expand All @@ -23,7 +23,6 @@ package com.appmattus.certificatetransparency.internal.loglist.model.v3
import com.appmattus.certificatetransparency.internal.loglist.deserializer.Rfc3339Deserializer
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import java.time.Instant

/**
* @property logListTimestamp The time at which this version of the log list was published.
Expand All @@ -34,7 +33,7 @@ import java.time.Instant
internal data class LogListV3(
@Serializable(with = Rfc3339Deserializer::class)
@SerialName("log_list_timestamp")
val logListTimestamp: Instant,
val logListTimestamp: Long,
@SerialName("version") val version: String,
@SerialName("operators") val operators: List<Operator>
)
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2023 Appmattus Limited
* Copyright 2024 Appmattus Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -19,7 +19,6 @@ package com.appmattus.certificatetransparency.internal.loglist.model.v3
import com.appmattus.certificatetransparency.internal.loglist.deserializer.Rfc3339Deserializer
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import java.time.Instant

/**
* @property name Name of the log operator
Expand All @@ -30,6 +29,6 @@ import java.time.Instant
internal data class PreviousOperator(
@Serializable(with = Rfc3339Deserializer::class)
@SerialName("end_time")
val endDate: Instant,
val endDate: Long,
@SerialName("name") val name: String
)
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2021-2023 Appmattus Limited
* Copyright 2021-2024 Appmattus Limited
* Copyright 2019 Babylon Partners Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
Expand All @@ -23,14 +23,13 @@ package com.appmattus.certificatetransparency.internal.loglist.model.v3
import com.appmattus.certificatetransparency.internal.loglist.deserializer.Rfc3339Deserializer
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import java.time.Instant

/**
* @property timestamp The time at which the log entered this state.
*/
@Serializable
internal sealed class State {
abstract val timestamp: Instant
abstract val timestamp: Long

/**
* An SCT associated with this log server would be treated as untrusted
Expand All @@ -40,7 +39,7 @@ internal sealed class State {
data class Pending(
@Serializable(with = Rfc3339Deserializer::class)
@SerialName("timestamp")
override val timestamp: Instant
override val timestamp: Long
) : State()

/**
Expand All @@ -51,7 +50,7 @@ internal sealed class State {
data class Qualified(
@Serializable(with = Rfc3339Deserializer::class)
@SerialName("timestamp")
override val timestamp: Instant
override val timestamp: Long
) : State()

/**
Expand All @@ -62,7 +61,7 @@ internal sealed class State {
data class Usable(
@Serializable(with = Rfc3339Deserializer::class)
@SerialName("timestamp")
override val timestamp: Instant
override val timestamp: Long
) : State()

/**
Expand All @@ -74,7 +73,7 @@ internal sealed class State {
data class ReadOnly(
@Serializable(with = Rfc3339Deserializer::class)
@SerialName("timestamp")
override val timestamp: Instant,
override val timestamp: Long,
@SerialName("final_tree_head") val finalTreeHead: FinalTreeHead
) : State()

Expand All @@ -86,7 +85,7 @@ internal sealed class State {
data class Retired(
@Serializable(with = Rfc3339Deserializer::class)
@SerialName("timestamp")
override val timestamp: Instant
override val timestamp: Long
) : State()

/**
Expand All @@ -97,6 +96,6 @@ internal sealed class State {
data class Rejected(
@Serializable(with = Rfc3339Deserializer::class)
@SerialName("timestamp")
override val timestamp: Instant
override val timestamp: Long
) : State()
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2021-2023 Appmattus Limited
* Copyright 2021-2024 Appmattus Limited
* Copyright 2019 Babylon Partners Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
Expand All @@ -23,7 +23,6 @@ package com.appmattus.certificatetransparency.internal.loglist.model.v3
import com.appmattus.certificatetransparency.internal.loglist.deserializer.Rfc3339Deserializer
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import java.time.Instant

/**
* @property startInclusive All certificates must expire on this date or later. (format: date-time)
Expand All @@ -33,8 +32,8 @@ import java.time.Instant
internal data class TemporalInterval(
@Serializable(with = Rfc3339Deserializer::class)
@SerialName("start_inclusive")
val startInclusive: Instant,
val startInclusive: Long,
@Serializable(with = Rfc3339Deserializer::class)
@SerialName("end_exclusive")
val endExclusive: Instant
val endExclusive: Long
)
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2021-2023 Appmattus Limited
* Copyright 2021-2024 Appmattus Limited
* Copyright 2020 Babylon Partners Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
Expand Down Expand Up @@ -27,7 +27,6 @@ import com.appmattus.certificatetransparency.internal.verifier.model.SignedCerti
import com.appmattus.certificatetransparency.internal.verifier.model.Version
import java.io.IOException
import java.io.InputStream
import java.time.Instant
import kotlin.math.ceil
import kotlin.math.log2

Expand All @@ -50,7 +49,7 @@ internal object Deserializer {

val keyId = inputStream.readFixedLength(CTConstants.KEY_ID_LENGTH)

val timestamp = Instant.ofEpochMilli(inputStream.readNumber(CTConstants.TIMESTAMP_LENGTH))
val timestamp = inputStream.readNumber(CTConstants.TIMESTAMP_LENGTH)

val extensions = inputStream.readVariableLength(CTConstants.MAX_EXTENSIONS_LENGTH)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,18 @@
* Derived from https://github.com/googleapis/google-http-java-client/blob/dev/google-http-client/src/main/java/com/google/api/client/util/DateTime.java
*
* Modified 2018 by Babylon Partners Limited
* Modified 2021-2023 by Appmattus Limited
* Modified 2021-2024 by Appmattus Limited
*/

package com.appmattus.certificatetransparency.internal.utils

import java.time.Duration
import java.time.Instant
import java.time.LocalDate
import java.time.LocalTime
import java.time.ZoneOffset
import java.util.Calendar
import java.util.GregorianCalendar
import java.util.TimeZone
import kotlin.math.pow

private val GMT = TimeZone.getTimeZone("GMT")

/** Regular expression for parsing RFC3339 date/times. */
private val Rfc3339Pattern = Regex(
// yyyy-MM-dd
Expand Down Expand Up @@ -53,50 +53,53 @@ private val Rfc3339Pattern = Regex(
*/
// Magic numbers accepted as very much linked to the pattern
@Suppress("MagicNumber")
internal fun String.toRfc3339Instant(): Instant {
val results = Rfc3339Pattern.matchEntire(this) ?: throw NumberFormatException(
"Invalid RFC3339 date/time format: $this"
)
internal fun String.toRfc3339Long(): Long {
val results = Rfc3339Pattern.matchEntire(this) ?: throw NumberFormatException("Invalid RFC3339 date/time format: $this")

val localDate = LocalDate.of(
results.groupValues[1].toInt(), // yyyy
results.groupValues[2].toInt(), // MM
results.groupValues[3].toInt() // dd
)
val year = results.groupValues[1].toInt() // yyyy
val month = results.groupValues[2].toInt() - 1 // MM
val day = results.groupValues[3].toInt() // dd
val isTimeGiven = results.groupValues[4].isNotEmpty() // 'T'HH:mm:ss.milliseconds
val tzShiftRegexGroup = results.groupValues[9] // 'Z', or time zone shift HH:mm following '+'/'-'
val isTzShiftGiven = tzShiftRegexGroup.isNotEmpty()
var hourOfDay = 0
var minute = 0
var second = 0
var milliseconds = 0

if (isTzShiftGiven && !isTimeGiven) {
throw NumberFormatException(
"Invalid RFC33339 date/time format, cannot specify time zone shift without specifying time: $this"
)
throw NumberFormatException("Invalid RFC33339 date/time format, cannot specify time zone shift without specifying time: $this")
}

val localTime = if (isTimeGiven) {
LocalTime.of(
results.groupValues[5].toInt(), // HH
results.groupValues[6].toInt(), // mm
results.groupValues[7].toInt(), // ss
results.groupValues[8].ifEmpty { ".000" }.substring(1).let { // nanoseconds
// The number of digits after the dot may not be 3. Need to renormalize.
val fractionDigits = it.length - 3
(it.toDouble() / 10.0.pow(fractionDigits.toDouble())).toInt() * 1_000_000
}
)
} else {
LocalTime.MIN
if (isTimeGiven) {
hourOfDay = results.groupValues[5].toInt() // HH
minute = results.groupValues[6].toInt() // mm
second = results.groupValues[7].toInt() // ss
if (results.groupValues[8].isNotEmpty()) { // contains .milliseconds?
milliseconds = results.groupValues[8].substring(1).toInt() // milliseconds
// The number of digits after the dot may not be 3. Need to renormalize.
val fractionDigits = results.groupValues[8].substring(1).length - 3
milliseconds = (milliseconds.toDouble() / 10.0.pow(fractionDigits.toDouble())).toInt()
}
}
val dateTime = GregorianCalendar(GMT)
dateTime.set(year, month, day, hourOfDay, minute, second)
dateTime.set(Calendar.MILLISECOND, milliseconds)
var value = dateTime.timeInMillis

val tzShift = if (isTzShiftGiven && tzShiftRegexGroup[0].uppercaseChar() != 'Z') {
// time zone shift HH
(results.groupValues[11].toInt() * 60 + results.groupValues[12].toInt()) *
// time zone shift + or -
(if (results.groupValues[10][0] == '-') -1 else 1)
} else {
0
if (isTimeGiven && isTzShiftGiven) {
@Suppress("EXPERIMENTAL_API_USAGE_ERROR")
if (tzShiftRegexGroup[0].uppercaseChar() != 'Z') {
var tzShift = (
results.groupValues[11].toInt() * 60 + // time zone shift HH
results.groupValues[12].toInt()
) // time zone shift mm
if (results.groupValues[10][0] == '-') { // time zone shift + or -
tzShift = -tzShift
}
value -= tzShift * 60000L // e.g. if 1 hour ahead of UTC, subtract an hour to get UTC time
}
}

// e.g. if 1 hour ahead of UTC, subtract an hour to get UTC time
return localDate.atTime(localTime).toInstant(ZoneOffset.UTC) - Duration.ofMinutes(tzShift.toLong())
return value
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2023 Appmattus Limited
* Copyright 2023-2024 Appmattus Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -41,7 +41,7 @@
return ASN1Header(tag, headerLength.offset, headerLength.length)
}

@Suppress("MagicNumber")
@Suppress("MagicNumber", "CyclomaticComplexMethod")

Check warning on line 44 in certificatetransparency/src/main/kotlin/com/appmattus/certificatetransparency/internal/utils/asn1/ASN1.kt

View check run for this annotation

Codecov / codecov/patch

certificatetransparency/src/main/kotlin/com/appmattus/certificatetransparency/internal/utils/asn1/ASN1.kt#L44

Added line #L44 was not covered by tests
internal fun ByteBuffer.toAsn1(logger: ASN1Logger = EmptyLogger): ASN1Object {
val header = header(logger)

Expand All @@ -58,7 +58,8 @@
tag.isUniversal(0x0c) -> ASN1PrintableStringUS.create(tag, encoded, logger)
tag.isUniversal(0x10) || tag.isUniversal(0x11) -> ASN1Sequence.create(tag, encoded, logger)
tag.isUniversal(0x13) -> ASN1PrintableStringTeletex.create(tag, encoded, logger)
tag.isUniversal(0x17) -> ASN1Time.create(tag, encoded, logger)
tag.isUniversal(0x17) -> UTCTime.create(tag, encoded, logger)
tag.isUniversal(0x18) -> GeneralizedTime.create(tag, encoded, logger)
tag.isContextSpecific(0x00) -> Version.create(tag, encoded, logger)
tag.isContextSpecific(0x03) -> Extensions.create(tag, encoded, logger)
else -> ASN1Unspecified.create(tag, encoded, logger)
Expand Down
Loading