mirror of
https://github.com/pgpainless/pgpainless.git
synced 2024-12-23 03:17:58 +01:00
Kotlin conversion: RevocationAttributes
This commit is contained in:
parent
68ac5af255
commit
a6198aadb3
2 changed files with 155 additions and 253 deletions
|
@ -1,253 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: 2020 Paul Schaub <vanitasvitae@fsfe.org>
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
package org.pgpainless.key.util;
|
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
|
|
||||||
public final class RevocationAttributes {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reason for revocation.
|
|
||||||
* There are two kinds of reasons: hard and soft reason.
|
|
||||||
*
|
|
||||||
* Soft revocation reasons gracefully disable keys or user-ids.
|
|
||||||
* Softly revoked keys can no longer be used to encrypt data to or to generate signatures.
|
|
||||||
* Any signature made after a key has been soft revoked is deemed invalid.
|
|
||||||
* Any signature made before the key has been soft revoked stays valid.
|
|
||||||
* Soft revoked info can be re-certified at a later point.
|
|
||||||
*
|
|
||||||
* Hard revocation reasons on the other hand renders the key or user-id invalid immediately.
|
|
||||||
* Hard reasons are suitable to use if for example a key got compromised.
|
|
||||||
* Any signature made before or after a key has been hard revoked is no longer considered valid.
|
|
||||||
* Hard revoked information can also not be re-certified.
|
|
||||||
*/
|
|
||||||
public enum Reason {
|
|
||||||
/**
|
|
||||||
* The key or certification is being revoked without a reason.
|
|
||||||
* This is a HARD revocation reason and cannot be undone.
|
|
||||||
*/
|
|
||||||
NO_REASON((byte) 0),
|
|
||||||
/**
|
|
||||||
* The key was superseded by another key.
|
|
||||||
* This is a SOFT revocation reason and can be undone.
|
|
||||||
*/
|
|
||||||
KEY_SUPERSEDED((byte) 1),
|
|
||||||
/**
|
|
||||||
* The key has potentially been compromised.
|
|
||||||
* This is a HARD revocation reason and cannot be undone.
|
|
||||||
*/
|
|
||||||
KEY_COMPROMISED((byte) 2),
|
|
||||||
/**
|
|
||||||
* The key was retired and shall no longer be used.
|
|
||||||
* This is a SOFT revocation reason can can be undone.
|
|
||||||
*/
|
|
||||||
KEY_RETIRED((byte) 3),
|
|
||||||
/**
|
|
||||||
* The user-id is no longer valid.
|
|
||||||
* This is a SOFT revocation reason and can be undone.
|
|
||||||
*/
|
|
||||||
USER_ID_NO_LONGER_VALID((byte) 32),
|
|
||||||
;
|
|
||||||
|
|
||||||
private static final Map<Byte, Reason> MAP = new ConcurrentHashMap<>();
|
|
||||||
static {
|
|
||||||
for (Reason r : Reason.values()) {
|
|
||||||
MAP.put(r.reasonCode, r);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decode a machine-readable reason code.
|
|
||||||
*
|
|
||||||
* @param code byte
|
|
||||||
* @return reason
|
|
||||||
*/
|
|
||||||
public static Reason fromCode(byte code) {
|
|
||||||
Reason reason = MAP.get(code);
|
|
||||||
if (reason == null) {
|
|
||||||
throw new IllegalArgumentException("Invalid revocation reason: " + code);
|
|
||||||
}
|
|
||||||
return reason;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return true if the {@link Reason} the provided code encodes is a hard revocation reason, false
|
|
||||||
* otherwise.
|
|
||||||
* Hard revocations cannot be undone, while keys or certifications with soft revocations can be
|
|
||||||
* re-certified by placing another signature on them.
|
|
||||||
*
|
|
||||||
* @param code reason code
|
|
||||||
* @return is hard
|
|
||||||
*/
|
|
||||||
public static boolean isHardRevocation(byte code) {
|
|
||||||
Reason reason = MAP.get(code);
|
|
||||||
return reason != KEY_SUPERSEDED && reason != KEY_RETIRED && reason != USER_ID_NO_LONGER_VALID;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return true if the given {@link Reason} is a hard revocation, false otherwise.
|
|
||||||
* Hard revocations cannot be undone, while keys or certifications with soft revocations can be
|
|
||||||
* re-certified by placing another signature on them.
|
|
||||||
*
|
|
||||||
* @param reason reason
|
|
||||||
* @return is hard
|
|
||||||
*/
|
|
||||||
public static boolean isHardRevocation(@Nonnull Reason reason) {
|
|
||||||
return isHardRevocation(reason.reasonCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return true if the given {@link Reason} denotes a key revocation.
|
|
||||||
* @param reason reason
|
|
||||||
* @return is key revocation
|
|
||||||
*/
|
|
||||||
public static boolean isKeyRevocation(@Nonnull Reason reason) {
|
|
||||||
return isKeyRevocation(reason.code());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return true if the given reason code denotes a key revocation.
|
|
||||||
* @param code reason code
|
|
||||||
* @return is key revocation
|
|
||||||
*/
|
|
||||||
public static boolean isKeyRevocation(byte code) {
|
|
||||||
Reason reason = MAP.get(code);
|
|
||||||
return reason != USER_ID_NO_LONGER_VALID;
|
|
||||||
}
|
|
||||||
|
|
||||||
private final byte reasonCode;
|
|
||||||
|
|
||||||
Reason(byte reasonCode) {
|
|
||||||
this.reasonCode = reasonCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte code() {
|
|
||||||
return reasonCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return code() + " - " + name();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum RevocationType {
|
|
||||||
KEY_REVOCATION,
|
|
||||||
CERT_REVOCATION
|
|
||||||
}
|
|
||||||
|
|
||||||
private final Reason reason;
|
|
||||||
private final String description;
|
|
||||||
|
|
||||||
private RevocationAttributes(Reason reason, String description) {
|
|
||||||
this.reason = reason;
|
|
||||||
this.description = description;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the machine-readable reason for revocation.
|
|
||||||
*
|
|
||||||
* @return reason
|
|
||||||
*/
|
|
||||||
public @Nonnull Reason getReason() {
|
|
||||||
return reason;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the human-readable description for the revocation reason.
|
|
||||||
* @return description
|
|
||||||
*/
|
|
||||||
public @Nonnull String getDescription() {
|
|
||||||
return description;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Build a {@link RevocationAttributes} object suitable for key revocations.
|
|
||||||
* Key revocations are revocations for keys or subkeys.
|
|
||||||
*
|
|
||||||
* @return builder
|
|
||||||
*/
|
|
||||||
public static WithReason createKeyRevocation() {
|
|
||||||
return new WithReason(RevocationType.KEY_REVOCATION);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Build a {@link RevocationAttributes} object suitable for certification (e.g. user-id) revocations.
|
|
||||||
*
|
|
||||||
* @return builder
|
|
||||||
*/
|
|
||||||
public static WithReason createCertificateRevocation() {
|
|
||||||
return new WithReason(RevocationType.CERT_REVOCATION);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final class WithReason {
|
|
||||||
|
|
||||||
private final RevocationType type;
|
|
||||||
|
|
||||||
private WithReason(RevocationType type) {
|
|
||||||
this.type = type;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the machine-readable reason.
|
|
||||||
* Note that depending on whether this is a key-revocation or certification-revocation,
|
|
||||||
* only certain reason codes are valid.
|
|
||||||
* Invalid input will result in an {@link IllegalArgumentException} to be thrown.
|
|
||||||
*
|
|
||||||
* @param reason reason
|
|
||||||
* @throws IllegalArgumentException in case of an invalid revocation reason
|
|
||||||
* @return builder
|
|
||||||
*/
|
|
||||||
public WithDescription withReason(Reason reason) {
|
|
||||||
throwIfReasonTypeMismatch(reason, type);
|
|
||||||
return new WithDescription(reason);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void throwIfReasonTypeMismatch(Reason reason, RevocationType type) {
|
|
||||||
if (type == RevocationType.KEY_REVOCATION) {
|
|
||||||
if (reason == Reason.USER_ID_NO_LONGER_VALID) {
|
|
||||||
throw new IllegalArgumentException("Reason " + reason + " can only be used for certificate revocations, not to revoke keys.");
|
|
||||||
}
|
|
||||||
} else if (type == RevocationType.CERT_REVOCATION) {
|
|
||||||
switch (reason) {
|
|
||||||
case KEY_SUPERSEDED:
|
|
||||||
case KEY_COMPROMISED:
|
|
||||||
case KEY_RETIRED:
|
|
||||||
throw new IllegalArgumentException("Reason " + reason + " can only be used for key revocations, not to revoke certificates.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final class WithDescription {
|
|
||||||
|
|
||||||
private final Reason reason;
|
|
||||||
|
|
||||||
private WithDescription(Reason reason) {
|
|
||||||
this.reason = reason;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set a human-readable description of the revocation reason.
|
|
||||||
*
|
|
||||||
* @param description description
|
|
||||||
* @return revocation attributes
|
|
||||||
*/
|
|
||||||
public RevocationAttributes withDescription(@Nonnull String description) {
|
|
||||||
return new RevocationAttributes(reason, description);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set an empty human-readable description.
|
|
||||||
* @return revocation attributes
|
|
||||||
*/
|
|
||||||
public RevocationAttributes withoutDescription() {
|
|
||||||
return withDescription("");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,155 @@
|
||||||
|
// SPDX-FileCopyrightText: 2023 Paul Schaub <vanitasvitae@fsfe.org>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package org.pgpainless.key.util
|
||||||
|
|
||||||
|
class RevocationAttributes(
|
||||||
|
val reason: Reason,
|
||||||
|
val description: String) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reason for revocation.
|
||||||
|
* There are two kinds of reasons: hard and soft reason.
|
||||||
|
*
|
||||||
|
* Soft revocation reasons gracefully disable keys or user-ids.
|
||||||
|
* Softly revoked keys can no longer be used to encrypt data to or to generate signatures.
|
||||||
|
* Any signature made after a key has been soft revoked is deemed invalid.
|
||||||
|
* Any signature made before the key has been soft revoked stays valid.
|
||||||
|
* Soft revoked info can be re-certified at a later point.
|
||||||
|
*
|
||||||
|
* Hard revocation reasons on the other hand renders the key or user-id invalid immediately.
|
||||||
|
* Hard reasons are suitable to use if for example a key got compromised.
|
||||||
|
* Any signature made before or after a key has been hard revoked is no longer considered valid.
|
||||||
|
* Hard revoked information can also not be re-certified.
|
||||||
|
*/
|
||||||
|
enum class Reason(val code: Byte) {
|
||||||
|
/**
|
||||||
|
* The key or certification is being revoked without a reason.
|
||||||
|
* This is a HARD revocation reason and cannot be undone.
|
||||||
|
*/
|
||||||
|
NO_REASON(0),
|
||||||
|
/**
|
||||||
|
* The key was superseded by another key.
|
||||||
|
* This is a SOFT revocation reason and can be undone.
|
||||||
|
*/
|
||||||
|
KEY_SUPERSEDED(1),
|
||||||
|
/**
|
||||||
|
* The key has potentially been compromised.
|
||||||
|
* This is a HARD revocation reason and cannot be undone.
|
||||||
|
*/
|
||||||
|
KEY_COMPROMISED(2),
|
||||||
|
/**
|
||||||
|
* The key was retired and shall no longer be used.
|
||||||
|
* This is a SOFT revocation reason can can be undone.
|
||||||
|
*/
|
||||||
|
KEY_RETIRED(3),
|
||||||
|
/**
|
||||||
|
* The user-id is no longer valid.
|
||||||
|
* This is a SOFT revocation reason and can be undone.
|
||||||
|
*/
|
||||||
|
USER_ID_NO_LONGER_VALID(32),
|
||||||
|
;
|
||||||
|
|
||||||
|
fun code() = code
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
return "$code - $name"
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
private val MAP = values().associateBy { it.code }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode a machine-readable reason code.
|
||||||
|
*
|
||||||
|
* @param code byte
|
||||||
|
* @return reason
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
fun fromCode(code: Byte) = MAP[code] ?: throw IllegalArgumentException("Invalid revocation reason: $code")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if the [Reason] the provided code encodes is a hard revocation reason, false
|
||||||
|
* otherwise.
|
||||||
|
* Hard revocations cannot be undone, while keys or certifications with soft revocations can be
|
||||||
|
* re-certified by placing another signature on them.
|
||||||
|
*
|
||||||
|
* @param code reason code
|
||||||
|
* @return is hard
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
fun isHardRevocation(code: Byte) = MAP[code]?.let { isHardRevocation(it) } ?: true
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if the given [Reason] is a hard revocation, false otherwise.
|
||||||
|
* Hard revocations cannot be undone, while keys or certifications with soft revocations can be
|
||||||
|
* re-certified by placing another signature on them.
|
||||||
|
*
|
||||||
|
* @param reason reason
|
||||||
|
* @return is hard
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
fun isHardRevocation(reason: Reason) = when (reason) {
|
||||||
|
KEY_SUPERSEDED, KEY_RETIRED, USER_ID_NO_LONGER_VALID -> false
|
||||||
|
else -> true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if the given reason code denotes a key revocation.
|
||||||
|
* @param code reason code
|
||||||
|
* @return is key revocation
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
fun isKeyRevocation(code: Byte) = MAP[code]?.let { isKeyRevocation(it) } ?: false
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if the given [Reason] denotes a key revocation.
|
||||||
|
* @param reason reason
|
||||||
|
* @return is key revocation
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
fun isKeyRevocation(reason: Reason) = when (reason) {
|
||||||
|
USER_ID_NO_LONGER_VALID -> false
|
||||||
|
else -> true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class RevocationType {
|
||||||
|
KEY_REVOCATION,
|
||||||
|
CERT_REVOCATION
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@JvmStatic
|
||||||
|
fun createKeyRevocation() = WithReason(RevocationType.KEY_REVOCATION)
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun createCertificateRevocation() = WithReason(RevocationType.CERT_REVOCATION)
|
||||||
|
}
|
||||||
|
|
||||||
|
class WithReason(val type: RevocationType) {
|
||||||
|
|
||||||
|
fun withReason(reason: Reason): WithDescription {
|
||||||
|
require(reasonTypeMatches(reason, type)) {
|
||||||
|
"Reason $reason can only be used for ${if (type == RevocationType.KEY_REVOCATION) "certificate" else "key"} revocations."
|
||||||
|
}
|
||||||
|
return WithDescription(reason)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun reasonTypeMatches(reason: Reason, type: RevocationType): Boolean {
|
||||||
|
return when (type) {
|
||||||
|
RevocationType.KEY_REVOCATION -> reason != Reason.USER_ID_NO_LONGER_VALID
|
||||||
|
RevocationType.CERT_REVOCATION -> reason == Reason.USER_ID_NO_LONGER_VALID || reason == Reason.NO_REASON
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class WithDescription(val reason: Reason) {
|
||||||
|
fun withDescription(description: String): RevocationAttributes = RevocationAttributes(reason, description)
|
||||||
|
fun withoutDescription() = RevocationAttributes(reason, "")
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue