mirror of
https://codeberg.org/PGPainless/sop-java.git
synced 2024-11-29 18:42:08 +01:00
Implement validate-userid command
This commit is contained in:
parent
1945411712
commit
0ec2961cbe
8 changed files with 216 additions and 17 deletions
|
@ -30,6 +30,7 @@ import sop.exception.SOPGPException
|
|||
UpdateKeyCmd::class,
|
||||
MergeCertsCmd::class,
|
||||
CertifyUserIdCmd::class,
|
||||
ValidateUserIdCmd::class,
|
||||
// Messaging subcommands
|
||||
SignCmd::class,
|
||||
VerifyCmd::class,
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
// SPDX-FileCopyrightText: 2024 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package sop.cli.picocli.commands
|
||||
|
||||
import java.io.IOException
|
||||
import picocli.CommandLine.Command
|
||||
import picocli.CommandLine.Option
|
||||
import picocli.CommandLine.Parameters
|
||||
import sop.cli.picocli.SopCLI
|
||||
import sop.exception.SOPGPException
|
||||
import sop.util.HexUtil.Companion.bytesToHex
|
||||
|
||||
@Command(
|
||||
name = "validate-userid",
|
||||
resourceBundle = "msg_validate-userid",
|
||||
exitCodeOnInvalidInput = SOPGPException.MissingArg.EXIT_CODE)
|
||||
class ValidateUserIdCmd : AbstractSopCmd() {
|
||||
|
||||
@Option(names = ["--addr-spec-only"]) var addrSpecOnly: Boolean = false
|
||||
|
||||
@Parameters(index = "0", arity = "1", paramLabel = "USERID") lateinit var userId: String
|
||||
|
||||
@Parameters(index = "1..*", arity = "1..*", paramLabel = "CERTS")
|
||||
var authorities: List<String> = listOf()
|
||||
|
||||
override fun run() {
|
||||
val validateUserId =
|
||||
throwIfUnsupportedSubcommand(SopCLI.getSop().validateUserId(), "validate-userid")
|
||||
|
||||
if (addrSpecOnly) {
|
||||
validateUserId.addrSpecOnly()
|
||||
}
|
||||
|
||||
validateUserId.userId(userId)
|
||||
|
||||
for (authority in authorities) {
|
||||
try {
|
||||
getInput(authority).use { validateUserId.authorities(it) }
|
||||
} catch (e: IOException) {
|
||||
throw RuntimeException(e)
|
||||
} catch (b: SOPGPException.BadData) {
|
||||
val errorMsg = getMsg("sop.error.input.not_a_certificate", authority)
|
||||
throw SOPGPException.BadData(errorMsg, b)
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
val valid = validateUserId.subjects(System.`in`)
|
||||
|
||||
if (!valid) {
|
||||
val errorMsg = getMsg("sop.error.runtime.any_cert_user_id_no_match", userId)
|
||||
throw SOPGPException.CertUserIdNoMatch(errorMsg)
|
||||
}
|
||||
} catch (e: SOPGPException.CertUserIdNoMatch) {
|
||||
val errorMsg =
|
||||
if (e.fingerprint != null) {
|
||||
getMsg(
|
||||
"sop.error.runtime.cert_user_id_no_match",
|
||||
bytesToHex(e.fingerprint!!),
|
||||
userId)
|
||||
} else {
|
||||
getMsg("sop.error.runtime.any_cert_user_id_no_match", userId)
|
||||
}
|
||||
throw SOPGPException.CertUserIdNoMatch(errorMsg, e)
|
||||
} catch (e: SOPGPException.BadData) {
|
||||
val errorMsg = getMsg("sop.error.input.not_a_certificate", "STDIN")
|
||||
throw SOPGPException.BadData(errorMsg, e)
|
||||
} catch (e: IOException) {
|
||||
throw RuntimeException(e)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -80,6 +80,8 @@ sop.error.runtime.cert_cannot_encrypt=Certificate from input '%s' cannot encrypt
|
|||
sop.error.runtime.no_session_key_extracted=Session key not extracted. Feature potentially not supported.
|
||||
sop.error.runtime.no_verifiable_signature_found=No verifiable signature found.
|
||||
sop.error.runtime.cannot_decrypt_message=Message could not be decrypted.
|
||||
sop.error.runtime.cert_user_id_no_match=Certificate '%s' does not contain a valid binding for user id '%s'.
|
||||
sop.error.runtime.any_cert_user_id_no_match=Any certificate does not contain a valid binding for user id '%s'.
|
||||
## Usage errors
|
||||
sop.error.usage.password_or_cert_required=At least one password file or cert file required for encryption.
|
||||
sop.error.usage.argument_required=Argument '%s' is required.
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
# SPDX-FileCopyrightText: 2024 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
usage.header=Validate a UserID in an OpenPGP certificate
|
||||
addr-spec-only=Treat the USERID as an email address, match only against the email address part of each correctly bound UserID
|
||||
USERID[0]=UserID
|
||||
CERTS[1..*]=Authority OpenPGP certificates
|
||||
|
||||
standardInput=CERTS
|
||||
standardInputDescription=OpenPGP certificates in which UserID bindings shall be validated
|
||||
|
||||
stacktrace=Print stacktrace
|
||||
# Generic TODO: Remove when bumping picocli to 4.7.0
|
||||
usage.parameterListHeading=%nParameters:%n
|
||||
usage.synopsisHeading=Usage:\u0020
|
||||
usage.commandListHeading=%nCommands:%n
|
||||
usage.optionListHeading=%nOptions:%n
|
||||
usage.footerHeading=Powered by picocli%n
|
|
@ -0,0 +1,18 @@
|
|||
# SPDX-FileCopyrightText: 2024 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
usage.header=Validiere eine UserID auf OpenPGP Zertifikaten
|
||||
addr-spec-only=Behandle die USERID als E-Mail-Adresse, vergleiche sie nur mit dem E-Mail-Adressen-Teil jeder korrekten UserID
|
||||
USERID[0]=UserID
|
||||
CERTS[1..*]=Autoritäre OpenPGP Zertifikate
|
||||
|
||||
standardInput=CERTS
|
||||
standardInputDescription=OpenPGP Zertifikate auf denen UserIDs validiert werden sollen
|
||||
|
||||
stacktrace=Print stacktrace
|
||||
# Generic TODO: Remove when bumping picocli to 4.7.0
|
||||
usage.parameterListHeading=%nParameter:%n
|
||||
usage.synopsisHeading=Aufruf:\u0020
|
||||
usage.commandListHeading=%nBefehle:%n
|
||||
usage.optionListHeading=%nOptionen:%n
|
||||
usage.footerHeading=Powered by Picocli%n
|
|
@ -60,18 +60,15 @@ interface SOP : SOPV {
|
|||
/** Update a key's password. */
|
||||
fun changeKeyPassword(): ChangeKeyPassword
|
||||
|
||||
/**
|
||||
* Keep a secret key up-to-date.
|
||||
*/
|
||||
/** Keep a secret key up-to-date. */
|
||||
fun updateKey(): UpdateKey
|
||||
|
||||
/**
|
||||
* Merge OpenPGP certificates.
|
||||
*/
|
||||
/** Merge OpenPGP certificates. */
|
||||
fun mergeCerts(): MergeCerts
|
||||
|
||||
/**
|
||||
* Certify OpenPGP Certificate User-IDs.
|
||||
*/
|
||||
/** Certify OpenPGP Certificate User-IDs. */
|
||||
fun certifyUserId(): CertifyUserId
|
||||
|
||||
/** Validate a UserID in an OpenPGP certificate. */
|
||||
fun validateUserId(): ValidateUserId
|
||||
}
|
||||
|
|
|
@ -338,9 +338,7 @@ abstract class SOPGPException : RuntimeException {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The primary key of a KEYS object is too weak or revoked.
|
||||
*/
|
||||
/** The primary key of a KEYS object is too weak or revoked. */
|
||||
class PrimaryKeyBad : SOPGPException {
|
||||
constructor() : super()
|
||||
|
||||
|
@ -353,13 +351,26 @@ abstract class SOPGPException : RuntimeException {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The CERTS object has no matching User ID.
|
||||
*/
|
||||
/** The CERTS object has no matching User ID. */
|
||||
class CertUserIdNoMatch : SOPGPException {
|
||||
constructor() : super()
|
||||
|
||||
constructor(errorMsg: String) : super(errorMsg)
|
||||
val fingerprint: ByteArray?
|
||||
|
||||
constructor() : super() {
|
||||
fingerprint = null
|
||||
}
|
||||
|
||||
constructor(fingerprint: ByteArray) : super() {
|
||||
this.fingerprint = fingerprint
|
||||
}
|
||||
|
||||
constructor(errorMsg: String) : super(errorMsg) {
|
||||
fingerprint = null
|
||||
}
|
||||
|
||||
constructor(errorMsg: String, cause: Throwable) : super(errorMsg, cause) {
|
||||
fingerprint = null
|
||||
}
|
||||
|
||||
override fun getExitCode(): Int = EXIT_CODE
|
||||
|
||||
|
|
78
sop-java/src/main/kotlin/sop/operation/ValidateUserId.kt
Normal file
78
sop-java/src/main/kotlin/sop/operation/ValidateUserId.kt
Normal file
|
@ -0,0 +1,78 @@
|
|||
// SPDX-FileCopyrightText: 2024 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package sop.operation
|
||||
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
import sop.exception.SOPGPException
|
||||
|
||||
/** Subcommand to validate UserIDs on certificates. */
|
||||
interface ValidateUserId {
|
||||
|
||||
/**
|
||||
* If this is set, then the USERID is treated as an e-mail address, and matched only against the
|
||||
* e-mail address part of each correctly bound User ID. The rest of each correctly bound User ID
|
||||
* is ignored.
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
@Throws(SOPGPException.UnsupportedOption::class) fun addrSpecOnly(): ValidateUserId
|
||||
|
||||
/**
|
||||
* Set the UserID to validate. To match only the email address, call [addrSpecOnly].
|
||||
*
|
||||
* @param userId UserID or email address
|
||||
* @return this
|
||||
*/
|
||||
fun userId(userId: String): ValidateUserId
|
||||
|
||||
/**
|
||||
* Add certificates, which act as authorities. The [userId] is only considered correctly bound,
|
||||
* if it was bound by an authoritative certificate.
|
||||
*
|
||||
* @param certs authoritative certificates
|
||||
* @return this
|
||||
*/
|
||||
@Throws(SOPGPException.BadData::class, IOException::class)
|
||||
fun authorities(certs: InputStream): ValidateUserId
|
||||
|
||||
/**
|
||||
* Add certificates, which act as authorities. The [userId] is only considered correctly bound,
|
||||
* if it was bound by an authoritative certificate.
|
||||
*
|
||||
* @param certs authoritative certificates
|
||||
* @return this
|
||||
*/
|
||||
@Throws(SOPGPException.BadData::class, IOException::class)
|
||||
fun authorities(certs: ByteArray): ValidateUserId = authorities(certs.inputStream())
|
||||
|
||||
/**
|
||||
* Add subject certificates, on which UserID bindings are validated.
|
||||
*
|
||||
* @param certs subject certificates
|
||||
* @return true if all subject certificates have a correct binding to the UserID.
|
||||
* @throws SOPGPException.BadData if the subject certificates are malformed
|
||||
* @throws IOException if a parser exception happens
|
||||
* @throws SOPGPException.CertUserIdNoMatch if any subject certificate does not have a correctly
|
||||
* bound UserID that matches [userId].
|
||||
*/
|
||||
@Throws(
|
||||
SOPGPException.BadData::class, IOException::class, SOPGPException.CertUserIdNoMatch::class)
|
||||
fun subjects(certs: InputStream): Boolean
|
||||
|
||||
/**
|
||||
* Add subject certificates, on which UserID bindings are validated.
|
||||
*
|
||||
* @param certs subject certificates
|
||||
* @return true if all subject certificates have a correct binding to the UserID.
|
||||
* @throws SOPGPException.BadData if the subject certificates are malformed
|
||||
* @throws IOException if a parser exception happens
|
||||
* @throws SOPGPException.CertUserIdNoMatch if any subject certificate does not have a correctly
|
||||
* bound UserID that matches [userId].
|
||||
*/
|
||||
@Throws(
|
||||
SOPGPException.BadData::class, IOException::class, SOPGPException.CertUserIdNoMatch::class)
|
||||
fun subjects(certs: ByteArray): Boolean = subjects(certs.inputStream())
|
||||
}
|
Loading…
Reference in a new issue