From 7023946bde369c756887b958d348a8e3d2811d90 Mon Sep 17 00:00:00 2001 From: Stefan Santesson Date: Fri, 14 Feb 2025 03:04:01 +0100 Subject: [PATCH 1/2] Javadoc updates Update to 0.0.7-SNAPSHOT --- .../datatypes/common/PresentationInput.java | 7 ++ .../datatypes/common/TokenAttribute.java | 9 ++ .../common/TokenAttributeNameSpace.java | 13 +++ .../datatypes/common/TokenAttributeType.java | 19 ++++ .../common/TokenDigestAlgorithm.java | 20 ++++ .../wallet/datatypes/common/TokenInput.java | 5 +- .../wallet/datatypes/common/TokenIssuer.java | 1 + .../common/TokenIssuingException.java | 15 ++- .../common/TokenParsingException.java | 28 +++++ .../common/TokenPresentationException.java | 30 +++++ .../datatypes/common/TokenPresenter.java | 3 + .../common/TokenSigningAlgorithm.java | 36 ++++++ .../common/TokenValidationException.java | 14 ++- .../common/TokenValidationResult.java | 8 ++ .../datatypes/common/TokenValidator.java | 1 + .../digg/wallet/datatypes/common/Utils.java | 10 ++ .../wallet/datatypes/mdl/data/CBORUtils.java | 13 +++ .../datatypes/mdl/data/DeviceResponse.java | 68 ++++++++++-- .../datatypes/mdl/data/IssuerSigned.java | 91 +++++++++++++++- .../datatypes/mdl/data/IssuerSignedItem.java | 57 ++++++++++ .../wallet/datatypes/mdl/data/MdlDocType.java | 11 +- .../mdl/data/MdlPresentationInput.java | 69 ++++++++++++ .../data/MdlPresentationValidationInput.java | 20 +++- .../mdl/data/MobileSecurityObject.java | 70 +++++++++++- .../datatypes/mdl/data/SessionTranscript.java | 32 ++++++ .../MdlIssuerSignedValidationResult.java | 10 ++ .../MdlPresentationValidationResult.java | 24 +++- .../mdl/process/MdlPresentationValidator.java | 9 ++ .../datatypes/mdl/process/MdlTokenIssuer.java | 19 ++-- .../mdl/process/MdlTokenPresenter.java | 23 ++++ .../wallet/datatypes/sdjwt/JSONUtils.java | 69 ++++++++++++ .../sdjwt/data/ClaimsWithDisclosure.java | 88 +++++++++++++-- .../datatypes/sdjwt/data/Disclosure.java | 36 +++++- .../wallet/datatypes/sdjwt/data/SdJwt.java | 79 ++++++++++++++ .../sdjwt/data/SdJwtPresentationInput.java | 61 +++++++++++ .../SdJwtPresentationValidationInput.java | 15 +++ .../sdjwt/process/SdJwtTokenInput.java | 101 ++++++++++++++++- .../sdjwt/process/SdJwtTokenIssuer.java | 8 ++ .../process/SdJwtTokenValidationResult.java | 4 + .../sdjwt/process/SdJwtTokenValidator.java | 103 ++++++++++++++++++ 40 files changed, 1256 insertions(+), 43 deletions(-) diff --git a/src/main/java/se/digg/wallet/datatypes/common/PresentationInput.java b/src/main/java/se/digg/wallet/datatypes/common/PresentationInput.java index 069d356..a4319c6 100644 --- a/src/main/java/se/digg/wallet/datatypes/common/PresentationInput.java +++ b/src/main/java/se/digg/wallet/datatypes/common/PresentationInput.java @@ -3,6 +3,13 @@ import lombok.Getter; import lombok.NoArgsConstructor; +/** + * A generic class representing input data required for a presentation. This class is used to encapsulate + * details such as tokens, cryptographic parameters, and disclosures that may be needed during the + * presentation process. + * + * @param the type of the disclosures associated with this presentation input + */ @Getter @NoArgsConstructor public class PresentationInput { diff --git a/src/main/java/se/digg/wallet/datatypes/common/TokenAttribute.java b/src/main/java/se/digg/wallet/datatypes/common/TokenAttribute.java index a1ea6eb..0840154 100644 --- a/src/main/java/se/digg/wallet/datatypes/common/TokenAttribute.java +++ b/src/main/java/se/digg/wallet/datatypes/common/TokenAttribute.java @@ -9,6 +9,15 @@ import lombok.Data; import lombok.NoArgsConstructor; +/** + * Represents a token attribute used in various token operations. + *

+ * This class encapsulates an attribute with a specific type and value. The type + * of the attribute is defined by {@link TokenAttributeType}, which specifies the + * namespace and attribute name. The value is stored as an object, allowing flexibility + * to represent various data types such as String, Integer, or LocalDate depending on the + * context of the attribute. + */ @Data @AllArgsConstructor @NoArgsConstructor diff --git a/src/main/java/se/digg/wallet/datatypes/common/TokenAttributeNameSpace.java b/src/main/java/se/digg/wallet/datatypes/common/TokenAttributeNameSpace.java index 657d6fa..c7a03c6 100644 --- a/src/main/java/se/digg/wallet/datatypes/common/TokenAttributeNameSpace.java +++ b/src/main/java/se/digg/wallet/datatypes/common/TokenAttributeNameSpace.java @@ -3,11 +3,24 @@ import lombok.AllArgsConstructor; import lombok.Getter; +/** + * Enum representing namespaces associated with token attributes. + * + * Each namespace is identified by a unique string identifier (id), + * which groups attributes under a specific category or standard. + * This allows for better organization and management of token attributes + * across different implementations or frameworks. + */ @Getter @AllArgsConstructor public enum TokenAttributeNameSpace { + /** EUDI wallet PID attribute name space */ EUDI_WALLET_PID("eu.europa.ec.eudi.pid.1"), + /** Mdoc mDL attribute namespace */ MDOC_MDL("org.iso.18013.5.1"); + /** + * The string identifier representing a namespace associated with token attributes. + */ final String id; } diff --git a/src/main/java/se/digg/wallet/datatypes/common/TokenAttributeType.java b/src/main/java/se/digg/wallet/datatypes/common/TokenAttributeType.java index 29a3f91..7bfe36c 100644 --- a/src/main/java/se/digg/wallet/datatypes/common/TokenAttributeType.java +++ b/src/main/java/se/digg/wallet/datatypes/common/TokenAttributeType.java @@ -4,19 +4,38 @@ import lombok.Data; import lombok.NoArgsConstructor; +/** + * Represents a structured type for a token attribute, which can be used in various + * token-related operations such as selective disclosure, validation, or issuance. + * + * This class encapsulates the namespace and the attribute name associated with + * a specific token attribute. The namespace may define a specific scope or context, + * while the attribute name identifies the attribute within that namespace. If the + * namespace is not specified, only the attribute name is considered. + */ @Data @AllArgsConstructor @NoArgsConstructor public class TokenAttributeType { + /** Optional name space for this attribute */ private String nameSpace; + /** The unique name of the attribute within its name space if applicable */ private String attributeName; + /** + * Constructs a TokenAttributeType instance with the specified attribute name. + * + * @param attributeName the name of the token attribute to be associated with this type. + * This parameter specifies the name of the attribute and it is + * required while creating the instance. The namespace is set to null. + */ public TokenAttributeType(String attributeName) { this.attributeName = attributeName; this.nameSpace = null; } + /** {@inheritDoc} */ @Override public String toString() { if (nameSpace == null) { diff --git a/src/main/java/se/digg/wallet/datatypes/common/TokenDigestAlgorithm.java b/src/main/java/se/digg/wallet/datatypes/common/TokenDigestAlgorithm.java index a0679f8..b3bf667 100644 --- a/src/main/java/se/digg/wallet/datatypes/common/TokenDigestAlgorithm.java +++ b/src/main/java/se/digg/wallet/datatypes/common/TokenDigestAlgorithm.java @@ -16,14 +16,27 @@ @Getter @AllArgsConstructor public enum TokenDigestAlgorithm { + /** SHA 256 */ SHA_256("SHA-256", "SHA-256", "sha-256"), + /** SHA 384 */ SHA_384("SHA-384", "SHA-384", "sha-384"), + /** SHA 512 */ SHA_512("SHA-512", "SHA-512", "sha-512"); + /** Name of hash algorithm used in mDL documents */ private final String mdlName; + /** Name of hash algorithm used in JDK MessageDigest instantiations */ private final String jdkName; + /** Name of hash algorithm used in SD JWT representations (sd_alg) */ private final String sdJwtName; + /** + * Converts a given mDL hash algorithm name to a corresponding {@code TokenDigestAlgorithm} instance. + * + * @param mdlName the name of the hash algorithm as specified in mDL documents. + * @return the corresponding {@code TokenDigestAlgorithm} instance. + * @throws NoSuchAlgorithmException if the provided mDL hash algorithm name is not supported. + */ public static TokenDigestAlgorithm fromMdlName(String mdlName) throws NoSuchAlgorithmException { return Arrays.stream(values()) @@ -35,6 +48,13 @@ public static TokenDigestAlgorithm fromMdlName(String mdlName) .orElseThrow(() -> new NoSuchAlgorithmException("Unsupported mDL hash algorithm")); } + /** + * Converts a given SD-JWT hash algorithm name to a corresponding {@code TokenDigestAlgorithm} instance. + * + * @param sdJwtName the name of the hash algorithm as specified in SD-JWT representations. + * @return the corresponding {@code TokenDigestAlgorithm} instance. + * @throws NoSuchAlgorithmException if the provided SD-JWT hash algorithm name is not supported. + */ public static TokenDigestAlgorithm fromSdJwtName(String sdJwtName) throws NoSuchAlgorithmException { return Arrays.stream(values()) .filter( diff --git a/src/main/java/se/digg/wallet/datatypes/common/TokenInput.java b/src/main/java/se/digg/wallet/datatypes/common/TokenInput.java index 5176dfe..f876178 100644 --- a/src/main/java/se/digg/wallet/datatypes/common/TokenInput.java +++ b/src/main/java/se/digg/wallet/datatypes/common/TokenInput.java @@ -4,7 +4,6 @@ package se.digg.wallet.datatypes.common; -import com.nimbusds.jose.JWSAlgorithm; import java.security.PublicKey; import java.time.Duration; import java.util.List; @@ -14,12 +13,16 @@ import lombok.NoArgsConstructor; import se.swedenconnect.security.credential.PkiCredential; +/** + * Represents the input data required for issuing a token. + */ @Data @AllArgsConstructor @NoArgsConstructor @Builder public class TokenInput { + /** Issuer name */ protected String issuer; /** Attributes enabled for selective disclosure */ protected List attributes; diff --git a/src/main/java/se/digg/wallet/datatypes/common/TokenIssuer.java b/src/main/java/se/digg/wallet/datatypes/common/TokenIssuer.java index 8cc7721..33c3f23 100644 --- a/src/main/java/se/digg/wallet/datatypes/common/TokenIssuer.java +++ b/src/main/java/se/digg/wallet/datatypes/common/TokenIssuer.java @@ -23,6 +23,7 @@ public interface TokenIssuer { * @param tokenInput the token input object containing attributes, issuer credential, expiration duration, * signing algorithm, and wallet public key * @return a byte array representing the issued token + * @throws TokenIssuingException if an error occurs during token issuance */ byte[] issueToken(T tokenInput) throws TokenIssuingException; } diff --git a/src/main/java/se/digg/wallet/datatypes/common/TokenIssuingException.java b/src/main/java/se/digg/wallet/datatypes/common/TokenIssuingException.java index 183bb17..0e5e695 100644 --- a/src/main/java/se/digg/wallet/datatypes/common/TokenIssuingException.java +++ b/src/main/java/se/digg/wallet/datatypes/common/TokenIssuingException.java @@ -7,19 +7,28 @@ import java.io.Serial; /** - * Exception caugth while issuing a token + * Exception caught while issuing a token */ public class TokenIssuingException extends Exception { @Serial private static final long serialVersionUID = -3234309120112902779L; - /** {@inheritDoc} */ + /** + * Constructs a new TokenIssuingException with the specified detail message. + * + * @param message the detail message explaining the reason for the exception + */ public TokenIssuingException(String message) { super(message); } - /** {@inheritDoc} */ + /** + * Constructs a new TokenIssuingException with the specified detail message and cause. + * + * @param message the detail message explaining the reason for the exception + * @param cause the cause of the exception, which can be retrieved later using the {@code getCause()} method + */ public TokenIssuingException(String message, Throwable cause) { super(message, cause); } diff --git a/src/main/java/se/digg/wallet/datatypes/common/TokenParsingException.java b/src/main/java/se/digg/wallet/datatypes/common/TokenParsingException.java index fb3e5dd..221f7a9 100644 --- a/src/main/java/se/digg/wallet/datatypes/common/TokenParsingException.java +++ b/src/main/java/se/digg/wallet/datatypes/common/TokenParsingException.java @@ -2,18 +2,46 @@ import java.io.Serial; +/** + * A custom exception that is thrown when an error occurs during token parsing. + * + * This exception can be used to indicate issues such as invalid token structure, + * parsing failures, or other errors encountered while processing a token. + */ public class TokenParsingException extends Exception { @Serial private static final long serialVersionUID = -150091799709439631L; + /** + * Default constructor for the TokenParsingException class. + * + * This constructor creates a new instance of TokenParsingException without any + * specific error message or cause. It is typically used when there is a token + * parsing error that does not require additional context. + */ public TokenParsingException() { } + /** + * Constructs a new TokenParsingException with the specified detail message. + * + * @param message the detail message, which provides additional information + * about the token parsing error. This message is typically + * intended for debugging or logging purposes. + */ public TokenParsingException(String message) { super(message); } + /** + * Constructs a TokenParsingException with the specified detail message and cause. + * + * @param message the detail message, which provides additional context about the token parsing error. + * This message is typically useful for debugging or logging purposes. + * @param cause the cause of the token parsing error. This can be another throwable that represents + * the underlying issue that led to this exception. + */ public TokenParsingException(String message, Throwable cause) { super(message, cause); } diff --git a/src/main/java/se/digg/wallet/datatypes/common/TokenPresentationException.java b/src/main/java/se/digg/wallet/datatypes/common/TokenPresentationException.java index 21b513e..390a8c0 100644 --- a/src/main/java/se/digg/wallet/datatypes/common/TokenPresentationException.java +++ b/src/main/java/se/digg/wallet/datatypes/common/TokenPresentationException.java @@ -2,21 +2,51 @@ import java.io.Serial; +/** + * Exception thrown to indicate an error during the token presentation process. + * + * This exception is used in cases where the process of creating a verifiable token + * presentation with selective disclosures fails due to invalid inputs, cryptographic + * errors, or other issues that prevent successful presentation. It provides constructors + * for specifying detailed error messages and causes. + */ public class TokenPresentationException extends Exception { @Serial private static final long serialVersionUID = 942635978985209161L; + /** + * Default constructor for the TokenPresentationException. + * This constructor initializes a new instance of the exception without any + * specific message or cause, indicating a generic error during the token + * presentation process. + */ public TokenPresentationException() { } + /** + * Constructs a new TokenPresentationException with the specified detail message. + * + * @param message the detail message, which provides additional information about the error + */ public TokenPresentationException(String message) { super(message); } + /** + * Constructs a new TokenPresentationException with the specified detail message and cause. + * + * @param message the detail message providing additional context about the error + * @param cause the underlying cause of the exception, typically another throwable + */ public TokenPresentationException(String message, Throwable cause) { super(message, cause); } + /** + * Constructs a new TokenPresentationException with the specified cause. + * + * @param cause the underlying cause of this exception, typically another throwable + */ public TokenPresentationException(Throwable cause) { super(cause); } diff --git a/src/main/java/se/digg/wallet/datatypes/common/TokenPresenter.java b/src/main/java/se/digg/wallet/datatypes/common/TokenPresenter.java index 3dab10d..816993a 100644 --- a/src/main/java/se/digg/wallet/datatypes/common/TokenPresenter.java +++ b/src/main/java/se/digg/wallet/datatypes/common/TokenPresenter.java @@ -16,7 +16,10 @@ public interface TokenPresenter> { * Creates a presentation token with selective disclosures * * @param presentationInput the verifiable presentation token input + * @param privateKey the wallet private key used for generating device proof signature * @return token with disclosures and device provided key proof + * @throws TokenPresentationException if the presentation process fails due to invalid input, cryptographic + * errors, or any other processing issues */ byte[] presentToken(PresentationInput presentationInput, PrivateKey privateKey) throws TokenPresentationException; diff --git a/src/main/java/se/digg/wallet/datatypes/common/TokenSigningAlgorithm.java b/src/main/java/se/digg/wallet/datatypes/common/TokenSigningAlgorithm.java index b5c955b..840fb9c 100644 --- a/src/main/java/se/digg/wallet/datatypes/common/TokenSigningAlgorithm.java +++ b/src/main/java/se/digg/wallet/datatypes/common/TokenSigningAlgorithm.java @@ -30,39 +30,48 @@ @Getter @AllArgsConstructor public enum TokenSigningAlgorithm { + /** ECDSA with SHA 256 */ ECDSA_256( AlgorithmID.ECDSA_256, TokenDigestAlgorithm.SHA_256, JWSAlgorithm.ES256 ), + /** ECDSA with SHA 384 */ ECDSA_384( AlgorithmID.ECDSA_384, TokenDigestAlgorithm.SHA_384, JWSAlgorithm.ES384 ), + /** ECDSA with SHA 512 */ ECDSA_512( AlgorithmID.ECDSA_512, TokenDigestAlgorithm.SHA_512, JWSAlgorithm.ES512 ), + /** RSA PSS with SHA 256 */ RSA_PSS_256( AlgorithmID.RSA_PSS_256, TokenDigestAlgorithm.SHA_256, JWSAlgorithm.PS256 ), + /** RSA PSS with SHA 384 */ RSA_PSS_384( AlgorithmID.RSA_PSS_384, TokenDigestAlgorithm.SHA_384, JWSAlgorithm.PS384 ), + /** RSA PSS with SHA 512 */ RSA_PSS_512( AlgorithmID.RSA_PSS_512, TokenDigestAlgorithm.SHA_512, JWSAlgorithm.PS512 ); + /** Algorithm ID */ private final AlgorithmID algorithmID; + /** Digest algorithm */ private final TokenDigestAlgorithm digestAlgorithm; + /** JWS algorithm */ private final JWSAlgorithm jwsAlgorithm; /** @@ -70,6 +79,7 @@ public enum TokenSigningAlgorithm { * * @param signingKey the signing key used to determine the TokenSigningAlgorithm * @return the TokenSigningAlgorithm that corresponds to the signing key + * @throws NoSuchAlgorithmException if no supported algorithm matches the given COSE key */ public static TokenSigningAlgorithm fromCOSEKey(COSEKey signingKey) throws NoSuchAlgorithmException { @@ -87,6 +97,13 @@ public static TokenSigningAlgorithm fromCOSEKey(COSEKey signingKey) ); } + /** + * Maps a given {@code JWSAlgorithm} to a corresponding {@code TokenSigningAlgorithm}. + * + * @param jwsAlgorithm the JWSAlgorithm that needs to be mapped + * @return the TokenSigningAlgorithm that corresponds to the specified JWSAlgorithm + * @throws NoSuchAlgorithmException if no supported TokenSigningAlgorithm matches the specified JWSAlgorithm + */ public static TokenSigningAlgorithm fromJWSAlgorithm( JWSAlgorithm jwsAlgorithm ) throws NoSuchAlgorithmException { @@ -104,6 +121,14 @@ public static TokenSigningAlgorithm fromJWSAlgorithm( ); } + /** + * Creates and returns a {@link JWSSigner} based on the provided private key and the current signing algorithm. + * + * @param privateKey the private key used for creating the signer. It must match the algorithm type expected by the signer. + * For EC algorithms, the key must be of type {@link ECPrivateKey}. + * @return an instance of {@link JWSSigner} that corresponds to the current signing algorithm. + * @throws JOSEException if an error occurs during the creation of the signer, such as invalid key type or unsupported algorithm. + */ public JWSSigner jwsSigner(PrivateKey privateKey) throws JOSEException { return switch (this) { case ECDSA_256, ECDSA_384, ECDSA_512 -> new ECDSASigner( @@ -115,6 +140,17 @@ public JWSSigner jwsSigner(PrivateKey privateKey) throws JOSEException { }; } + /** + * Creates a JWSVerifier instance based on the algorithm type. + * + * This method returns a specific verifier implementation depending on the token + * signing algorithm (e.g., ECDSA or RSA_PSS). It requires a public key and throws + * a JOSEException if the verifier cannot be instantiated. + * + * @param publicKey the public key used to verify the JWS signature. + * @return a JWSVerifier instance tailored to the specified algorithm type. + * @throws JOSEException if a JWSVerifier instance cannot be created. + */ public JWSVerifier jwsVerifier(PublicKey publicKey) throws JOSEException { return switch (this) { case ECDSA_256, ECDSA_384, ECDSA_512 -> new ECDSAVerifier( diff --git a/src/main/java/se/digg/wallet/datatypes/common/TokenValidationException.java b/src/main/java/se/digg/wallet/datatypes/common/TokenValidationException.java index 270bf63..9df0038 100644 --- a/src/main/java/se/digg/wallet/datatypes/common/TokenValidationException.java +++ b/src/main/java/se/digg/wallet/datatypes/common/TokenValidationException.java @@ -14,12 +14,22 @@ public class TokenValidationException extends Exception { @Serial private static final long serialVersionUID = -3706726881712381049L; - /** {@inheritDoc} */ + /** + * Constructs a new TokenValidationException with the specified detail message. + * + * @param message the detail message explaining the reason for the exception + */ public TokenValidationException(String message) { super(message); } - /** {@inheritDoc} */ + /** + * Constructs a new TokenValidationException with the specified detail message and cause. + * + * @param message the detail message explaining the reason for the exception + * @param cause the cause of the exception, which can be retrieved later by the + * {@link Throwable#getCause()} method + */ public TokenValidationException(String message, Throwable cause) { super(message, cause); } diff --git a/src/main/java/se/digg/wallet/datatypes/common/TokenValidationResult.java b/src/main/java/se/digg/wallet/datatypes/common/TokenValidationResult.java index b3d635c..974cde7 100644 --- a/src/main/java/se/digg/wallet/datatypes/common/TokenValidationResult.java +++ b/src/main/java/se/digg/wallet/datatypes/common/TokenValidationResult.java @@ -22,12 +22,20 @@ @NoArgsConstructor public class TokenValidationResult{ + /** Key used to validate the signature */ protected PublicKey validationKey; + /** Certificate used to validate the signature */ protected X509Certificate validationCertificate; + /** Certificate chain used to validate the signature */ protected List validationChain; + /** Wallet public key */ protected PublicKey walletPublicKey; + /** Issue time */ protected Instant issueTime; + /** Expiration time */ protected Instant expirationTime; + /** Nonce specified in presentation request */ protected String presentationRequestNonce; + /** A list of disclosed attribute values provided in a map with attribute type as key */ protected Map disclosedAttributes; } diff --git a/src/main/java/se/digg/wallet/datatypes/common/TokenValidator.java b/src/main/java/se/digg/wallet/datatypes/common/TokenValidator.java index 4476db6..e0d6259 100644 --- a/src/main/java/se/digg/wallet/datatypes/common/TokenValidator.java +++ b/src/main/java/se/digg/wallet/datatypes/common/TokenValidator.java @@ -18,6 +18,7 @@ public interface TokenValidator { * @param token The token to be validated as a byte array. * @param trustedKeys optional list of trusted keys used for validation. * @return An instance of TokenValidationResult containing information about the validation result. + * @throws TokenValidationException if the token validation process fails. */ TokenValidationResult validateToken( byte[] token, diff --git a/src/main/java/se/digg/wallet/datatypes/common/Utils.java b/src/main/java/se/digg/wallet/datatypes/common/Utils.java index bea7c8b..de2f4a5 100644 --- a/src/main/java/se/digg/wallet/datatypes/common/Utils.java +++ b/src/main/java/se/digg/wallet/datatypes/common/Utils.java @@ -24,6 +24,16 @@ public static Map ensureStringObjectMap(Object value) { throw new IllegalArgumentException("Invalid Map structure: keys must be Strings"); } + /** + * Ensures that the provided object is a List containing only String elements + * and returns it as a {@code List}. If the input object is not a List + * with all String elements, an IllegalArgumentException is thrown. + * + * @param value the object to validate and cast + * @return the validated and cast {@code List} + * @throws IllegalArgumentException if the provided object is not a List or if the List's + * elements are not all Strings + */ @SuppressWarnings("unchecked") public static List ensureStringList(Object value) { if (value instanceof List list && list.stream().allMatch(item -> item instanceof String)) { diff --git a/src/main/java/se/digg/wallet/datatypes/mdl/data/CBORUtils.java b/src/main/java/se/digg/wallet/datatypes/mdl/data/CBORUtils.java index 41c3f83..aec5dad 100644 --- a/src/main/java/se/digg/wallet/datatypes/mdl/data/CBORUtils.java +++ b/src/main/java/se/digg/wallet/datatypes/mdl/data/CBORUtils.java @@ -183,6 +183,19 @@ public static String cborToPrettyJson(byte[] cborBytes) throws IOException { .writeValueAsString(objectMapper.readValue(jsonString, Object.class)); } + /** + * Signs the provided data using the specified key and algorithm, producing a Sign1 COSE signature. + * + * @param toBeSigned the byte array representing the content to be signed + * @param key the COSEKey used for signing the content + * @param algorithmID the algorithm identifier specifying the signing algorithm + * @param kid the key identifier to be added to the COSE header, or null if not applicable + * @param chain a list of X509Certificates representing the certificate chain to be added to the COSE header, or null if not applicable + * @param protectedKid a flag indicating whether the key identifier should be placed in the protected header + * @return a Sign1COSEObject representing the signed content with associated headers + * @throws CoseException if an error occurs during the COSE signing process + * @throws CertificateEncodingException if an encoding error occurs with the provided certificates + */ public static Sign1COSEObject sign(byte[] toBeSigned, COSEKey key, AlgorithmID algorithmID, String kid, List chain, boolean protectedKid) throws CoseException, CertificateEncodingException { Sign1COSEObject coseSignature = new Sign1COSEObject(false); coseSignature.SetContent(toBeSigned); diff --git a/src/main/java/se/digg/wallet/datatypes/mdl/data/DeviceResponse.java b/src/main/java/se/digg/wallet/datatypes/mdl/data/DeviceResponse.java index 846eef3..050477b 100644 --- a/src/main/java/se/digg/wallet/datatypes/mdl/data/DeviceResponse.java +++ b/src/main/java/se/digg/wallet/datatypes/mdl/data/DeviceResponse.java @@ -14,11 +14,30 @@ import java.io.IOException; +/** + * The DeviceResponse class represents a mDL presentation token as it is returned as response to a presentation request + * with information such as document type, status, version, and associated cryptographic data. + * It provides functionality for serialization and deserialization using CBOR (Concise Binary Object Representation). + * The object can be converted into and parsed from a CBOR-encoded byte array for data transmission. + *

+ * This class includes the following key components: + * - Metadata about the response, such as document type, version, and status. + * - Cryptographic elements, including issuer-signed data, device signature, and optional device MAC key. + * - A serializer implementation for converting the object to CBOR format. + */ @Data @AllArgsConstructor @JsonSerialize(using = DeviceResponse.Serializer.class) public class DeviceResponse { + /** + * Constructor for the DeviceResponse class. + * Initializes a new instance of the DeviceResponse with the specified parameters. + * + * @param docType the document type associated with the response. + * @param issuerSigned the issuer-signed data associated with the device response. + * @param deviceSignature the byte array representing the device signature. + */ public DeviceResponse(String docType, IssuerSigned issuerSigned, byte[] deviceSignature) { this.issuerSigned = issuerSigned; this.deviceSignature = deviceSignature; @@ -29,16 +48,42 @@ public DeviceResponse(String docType, IssuerSigned issuerSigned, byte[] deviceSi this.deviceMac = null; } - int status; - String docType; - String version; - IssuerSigned issuerSigned; - CBORObject deviceNameSpaces; - byte[] deviceSignature; - byte[] deviceMac; - + /** Status code. Default 0 for successful responses */ + private final int status; + /** DocType for the response document */ + private final String docType; + /** Version. Shall be 1.0 */ + private final String version; + /** The IssuerSigned object */ + private final IssuerSigned issuerSigned; + /** The object providing the name spaces data for the device signature. By default, this is an empty map. */ + private final CBORObject deviceNameSpaces; + /** The bytes of the device signature */ + private final byte[] deviceSignature; + /** The bytes of a wallet provided MAC (Currently not supported) */ + private final byte[] deviceMac; + + /** + * A custom serializer for the {@code DeviceResponse} class that converts a {@code DeviceResponse} + * object into its CBOR representation. This class extends the {@code JsonSerializer} to provide + * specific serialization logic for {@code DeviceResponse} objects. + *

+ * The serialization process involves the creation and encoding of a CBOR object that encapsulates + * key fields from the {@code DeviceResponse} instance, including device signature, device MAC, + * namespaces, document type, issuer-signed data, and version details. + *

+ * The serializer explicitly supports CBOR output, leveraging a {@code CBORGenerator} to output + * the serialized bytes. If a non-CBOR generator is provided, an exception is thrown. + *

+ * Exception Handling: + *

    + *
  • Throws {@link IOException} for errors during the serialization process or CBOR encoding.
  • + *
  • Throws {@link JsonGenerationException} if a non-CBOR generator is used.
  • + *
+ */ public static class Serializer extends JsonSerializer { + /** {@inheritDoc} **/ @Override public void serialize( DeviceResponse deviceResponse, @@ -87,6 +132,13 @@ public void serialize( } } + /** + * Deserializes a CBOR-encoded byte array into a DeviceResponse object. + * + * @param cborEncoded the byte array containing the CBOR-encoded data to be deserialized. + * @return a DeviceResponse object with the deserialized data. + * @throws TokenParsingException if an error occurs during the deserialization process. + */ public static DeviceResponse deserialize(byte[] cborEncoded) throws TokenParsingException { diff --git a/src/main/java/se/digg/wallet/datatypes/mdl/data/IssuerSigned.java b/src/main/java/se/digg/wallet/datatypes/mdl/data/IssuerSigned.java index 4b14a1c..75a21ba 100644 --- a/src/main/java/se/digg/wallet/datatypes/mdl/data/IssuerSigned.java +++ b/src/main/java/se/digg/wallet/datatypes/mdl/data/IssuerSigned.java @@ -27,6 +27,13 @@ import se.digg.cose.CoseException; import se.swedenconnect.security.credential.PkiCredential; +/** + * Represents an issuer-signed data structure representing the token issuer contribution to the + * presentation of a token with disclosure data. The Issuer-signed object contains a map of namespaces + * and their corresponding attributes, along with a COSE signature. + * This class is designed to serialize and deserialize data in CBOR format + * while maintaining cryptographic integrity through COSE signatures. + */ @Data @AllArgsConstructor @NoArgsConstructor @@ -38,28 +45,57 @@ public class IssuerSigned { /** Utagged Sign1 COSE signature where payload is CBOR encoding of @{@link MobileSecurityObject} */ byte[] issuerAuth; + /** + * Creates a new builder instance for constructing an {@code IssuerSigned} object. + * + * @return a new {@code IssuerSignedBuilder} instance for building an {@code IssuerSigned} object + */ public static IssuerSignedBuilder builder() { return new IssuerSignedBuilder(); } + /** + * A builder class for constructing instances of {@code IssuerSigned}. This builder enables the configuration + * of namespaces, issuer authentication details, document type, version, signing key, and other related + * properties. It provides an encapsulated approach to creating and initializing an {@code IssuerSigned} object, + * ensuring the consistency of its configuration. + */ public static class IssuerSignedBuilder { + /** The object being built */ private final IssuerSigned issuerSigned; + /** The credential of the issuer being used to sign data */ private PkiCredential issuerCredential; + /** The signing algorithm */ private TokenSigningAlgorithm signingAlgorithm; + /** A builder for creating the data to be signed by the issuer */ private MobileSecurityObject.MobileSecurityObjectBuilder msoBuilder; - + /** Version information for the issuer-signed object */ private String version; + /** DocType declaration */ private String docType; - + /** Key identifier to include in the signature header */ private String signingKid; - + /** true if the key identifier should be present in a protected header */ private boolean protectedKid = false; + /** + * Default private constructor for IssuerSignedBuilder. + * Initializes the builder by creating a new instance of the IssuerSigned object. + * This constructor is used internally and prevents direct instantiation of the IssuerSignedBuilder class + * from outside the class. Use the provided public methods to configure and build an IssuerSigned object. + */ private IssuerSignedBuilder() { this.issuerSigned = new IssuerSigned(); } + /** + * Adds or updates a namespace in the issuerSigned object with the provided list of IssuerSignedItem objects. + * + * @param namespace the name of the namespace to be added or updated + * @param issuerSignedItems the list of IssuerSignedItem objects to associate with the specified namespace + * @return the current instance of the IssuerSignedBuilder with the updated namespace configuration + */ public IssuerSignedBuilder nameSpace( String namespace, List issuerSignedItems @@ -86,6 +122,18 @@ public IssuerSignedBuilder namespaces( return this; } + /** + * Provide the necessary input required to create the issuer signature in the issuerAuth component of the + * IssuerSigned object. + * + * @param issuerCredential the PKI credential of the issuer, which is required to sign the objects + * @param signingAlgorithm the cryptographic signing algorithm to be used for token signing + * @param walletPublicKey the public key of the wallet used for the authentication process + * @param validity the duration for which the signed object will remain valid + * @param signingKid the key identifier (KID) for the signing key + * @return the updated instance of IssuerSignedBuilder after applying the authentication input configuration + * @throws CoseException if an error occurs during the construction of cryptographic components + */ public IssuerSignedBuilder issuerAuthInput( PkiCredential issuerCredential, TokenSigningAlgorithm signingAlgorithm, @@ -104,6 +152,20 @@ public IssuerSignedBuilder issuerAuthInput( ); } + /** + * Configures the issuer authentication inputs required to create the issuer signature + * in the issuerAuth component of the IssuerSigned object. + * + * @param issuerCredential the PKI credential of the issuer, necessary for signing objects + * @param signingAlgorithm the cryptographic signing algorithm to be used for token signing + * @param walletPublicKey the public key of the wallet used in the authentication process; can be null + * @param validity the duration for which the signed object will remain valid + * @param docType the document type associated with the IssuerSigned object + * @param version the version of the document to be signed + * @param signingKid the key identifier (KID) for the signing key + * @return the updated instance of IssuerSignedBuilder with the configured issuer authentication inputs + * @throws CoseException if an error occurs during the construction of cryptographic components + */ public IssuerSignedBuilder issuerAuthInput( PkiCredential issuerCredential, TokenSigningAlgorithm signingAlgorithm, @@ -149,6 +211,16 @@ public IssuerSignedBuilder protectedKid(boolean protectedKid) { return this; } + /** + * Builds and returns the {@link IssuerSigned} object using the provided configurations. + * Validates that all necessary fields have been set and computes any required cryptographic + * signatures using the issuer credentials and signing algorithm if provided. + * + * @return the fully constructed {@link IssuerSigned} object + * @throws CoseException if an error occurs during the creation or signing of cryptographic components + * @throws IOException if an input/output error occurs + * @throws CertificateEncodingException if there is an error in encoding the issuer's certificate + */ public IssuerSigned build() throws CoseException, IOException, CertificateEncodingException { Map> nameSpaces = @@ -211,8 +283,14 @@ public IssuerSigned build() } } + /** + * Serializer class for serializing {@link IssuerSigned} objects into CBOR format. + * This class extends the {@link JsonSerializer} to provide custom serialization logic + * for {@code IssuerSigned} objects. + */ public static class Serializer extends JsonSerializer { + /** {@inheritDoc} */ @Override public void serialize( IssuerSigned issuerSigned, @@ -250,6 +328,13 @@ public void serialize( } } + /** + * Deserializes a CBOR-encoded byte array into an {@code IssuerSigned} object. + * + * @param cborEncoded the CBOR-encoded byte array representing the {@code IssuerSigned} object + * @return the deserialized {@code IssuerSigned} object + * @throws TokenParsingException if the byte array could not be parsed or if the deserialization process fails + */ public static IssuerSigned deserialize(byte[] cborEncoded) throws TokenParsingException { // Parse CBOR diff --git a/src/main/java/se/digg/wallet/datatypes/mdl/data/IssuerSignedItem.java b/src/main/java/se/digg/wallet/datatypes/mdl/data/IssuerSignedItem.java index 75b2f61..c104f02 100644 --- a/src/main/java/se/digg/wallet/datatypes/mdl/data/IssuerSignedItem.java +++ b/src/main/java/se/digg/wallet/datatypes/mdl/data/IssuerSignedItem.java @@ -22,6 +22,28 @@ import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; +/** + * Represents an issuer-signed item containing an instance of disclosed attribute data associated with an mDL document. + * This class is serialized and deserialized using CBOR (Concise Binary Object Representation), + * adhering to specific encoding and decoding mechanisms provided via custom serializers + * and deserializers. + *

+ * Fields: + *

    + *
  • `digestID`: An integer identifier for the digest provided in the Mobile Security Object (MSO)
  • + *
  • `random`: A random salt mixed with attribute data during hashing
  • + *
  • `elementIdentifier`: A name uniquely identifying the attribute
  • + *
  • `elementValue`: The disclosed attribute value
  • + *
+ * + * Nested Classes: + *
    + *
  • `Serializer`: Custom serializer for transforming an IssuerSignedItem instance + * into the CBOR binary format, implementing {@link JsonSerializer}.
  • + *
  • `Deserializer`: Custom deserializer for reconstructing an IssuerSignedItem instance + * from the CBOR binary format, implementing {@link JsonDeserializer}.
  • + *
+ */ @Data @AllArgsConstructor @NoArgsConstructor @@ -31,16 +53,31 @@ @JsonDeserialize(using = IssuerSignedItem.Deserializer.class) public class IssuerSignedItem { + /** An integer identifier for the digest provided in the Mobile Security Object (MSO) */ int digestID; + /** A random salt mixed with attribute data during hashing */ byte[] random; + /** A name uniquely identifying the attribute */ String elementIdentifier; + /** The disclosed attribute value */ Object elementValue; + /** + * Converts the current object into its CBOR (Concise Binary Object Representation) byte array representation. + * This includes wrapping the object inside a CBOR tag 24 (CBOR encoded data) + * + * @return a byte array containing the CBOR-encoded representation of the object. + * @throws JsonProcessingException if an error occurs during the processing of the object to CBOR format. + */ @JsonIgnore public byte[] toBeHashedBytes() throws JsonProcessingException { return CBORUtils.CBOR_MAPPER.writeValueAsBytes(this); } + /** + * Serializer class for serializing IssuerSignedItem objects into a CBOR representation. + * Extends the {@code JsonSerializer} class to provide custom serialization logic. + */ public static class Serializer extends JsonSerializer { @Override @@ -74,8 +111,28 @@ public void serialize( } } + /** + * Deserializer class is responsible for deserializing JSON data into an + * IssuerSignedItem object, specifically handling data in CBOR (Concise Binary + * Object Representation) format. + *

+ * This class extends the JsonDeserializer class provided by Jackson, and it + * overrides the {@code deserialize} method to provide custom deserialization logic. + * If the input data is not in CBOR format, an exception is thrown. + *

+ * The deserialization process involves: + * - Parsing the input binary value into a CBORObject. + * - Extracting individual fields such as digestID, random, elementIdentifier, + * and elementValue from the CBORObject. + * - Constructing an IssuerSignedItem object using the extracted values. + *

+ * Throws: + * - JsonParseException if the input parser is not a CBORParser. + * - IOException in case of I/O errors during deserialization. + */ public static class Deserializer extends JsonDeserializer { + /** {@inheritDoc} */ @Override public IssuerSignedItem deserialize( JsonParser gen, diff --git a/src/main/java/se/digg/wallet/datatypes/mdl/data/MdlDocType.java b/src/main/java/se/digg/wallet/datatypes/mdl/data/MdlDocType.java index e65c3b1..2d627c2 100644 --- a/src/main/java/se/digg/wallet/datatypes/mdl/data/MdlDocType.java +++ b/src/main/java/se/digg/wallet/datatypes/mdl/data/MdlDocType.java @@ -3,11 +3,20 @@ import lombok.AllArgsConstructor; import lombok.Getter; +/** + * Enum representing different types of document identifiers. + * Each enum constant has an associated identifier string. + */ @Getter @AllArgsConstructor public enum MdlDocType { - mDL(""), + /** Standard mDL document type identifier */ + mDL("org.iso.18013.5.1.mDL"), + /** EDUI Wallet document type identifier */ EUDI_WALLET_PID("eu.europa.ec.eudi.pid.1"); + /** + * The unique identifier string associated with the document type. + */ private final String id; } diff --git a/src/main/java/se/digg/wallet/datatypes/mdl/data/MdlPresentationInput.java b/src/main/java/se/digg/wallet/datatypes/mdl/data/MdlPresentationInput.java index 6a39bc3..b93046e 100644 --- a/src/main/java/se/digg/wallet/datatypes/mdl/data/MdlPresentationInput.java +++ b/src/main/java/se/digg/wallet/datatypes/mdl/data/MdlPresentationInput.java @@ -7,60 +7,129 @@ import java.util.List; import java.util.Map; +/** + * MdlPresentationInput is a specialized implementation of the PresentationInput + * class tailored for mDL (mobile Driver's License) presentations. It encapsulates + * specific inputs and metadata required for constructing an mDL presentation. + * This class also provides a builder for convenient construction of its instances. + */ @Getter public class MdlPresentationInput extends PresentationInput>> { + /** The presentation requester client ID (See OpenID4VP) */ private String clientId; + /** A nonce value generated by the presenting wallet included as the apu header parameter value of the response JWT (OpenID4VP) */ private String mdocGeneratedNonce; + /** The response URL where the presentation response is delivered */ private String responseUri; + /** + * Creates and returns a new instance of the {@link MdlPresentationInputBuilder} class. + * The builder enables a step-by-step construction of an {@link MdlPresentationInput} object. + * + * @return a {@code MdlPresentationInputBuilder} instance for building {@code MdlPresentationInput} objects. + */ public static MdlPresentationInputBuilder builder() { return new MdlPresentationInputBuilder(); } + /** + * Builder for creating instances of the {@link MdlPresentationInput} class. + */ public static class MdlPresentationInputBuilder { + /** The object being built */ final MdlPresentationInput mdlPresentationInput; + /** + * Constructs a new instance of the {@code MdlPresentationInputBuilder} class. + */ public MdlPresentationInputBuilder() { mdlPresentationInput = new MdlPresentationInput(); } + /** + * Sets the token to be used in the {@code MdlPresentationInput} object being built. + * + * @param token the byte array representing the token + * @return the {@code MdlPresentationInputBuilder} instance for method chaining + */ public MdlPresentationInputBuilder token(byte[] token) { mdlPresentationInput.token = token; return this; } + /** + * Sets the nonce to be used in the {@code MdlPresentationInput} object being built. + * + * @param nonce the string value representing the nonce + * @return the {@code MdlPresentationInputBuilder} instance for method chaining + */ public MdlPresentationInputBuilder nonce(String nonce) { mdlPresentationInput.nonce = nonce; return this; } + /** + * Sets the client ID to be used in the {@code MdlPresentationInput} object being built. + * + * @param walletId the client ID represented as a string + * @return the {@code MdlPresentationInputBuilder} instance for method chaining + */ public MdlPresentationInputBuilder clientId(String walletId) { mdlPresentationInput.clientId = walletId; return this; } + /** + * Sets the mdoc generated nonce to be used in the {@code MdlPresentationInput} object being built. + * + * @param mdocGeneratedNonce the string value representing the mdoc generated nonce + * @return the {@code MdlPresentationInputBuilder} instance for method chaining + */ public MdlPresentationInputBuilder mdocGeneratedNonce(String mdocGeneratedNonce) { mdlPresentationInput.mdocGeneratedNonce = mdocGeneratedNonce; return this; } + /** + * Sets the response URI to be used in the {@code MdlPresentationInput} object being built. + * + * @param responseUri the response URI represented as a string + * @return the {@code MdlPresentationInputBuilder} instance for method chaining + */ public MdlPresentationInputBuilder responseUri(String responseUri) { mdlPresentationInput.responseUri = responseUri; return this; } + /** + * Sets the disclosures to be used in the {@code MdlPresentationInput} object being built. + * + * @param disclosures a map where the key is a namespace, and the value is a list of attribute names being disclosed + * @return the {@code MdlPresentationInputBuilder} instance for method chaining + */ public MdlPresentationInputBuilder disclosures(Map> disclosures) { mdlPresentationInput.disclosures = disclosures; return this; } + /** + * Sets the wallet signing algorithm to be used in the {@code MdlPresentationInput} object being built. + * + * @param algorithm the {@code TokenSigningAlgorithm} specifying the signing algorithm to be used by the wallet + * @return the {@code MdlPresentationInputBuilder} instance for method chaining + */ public MdlPresentationInputBuilder algorithm(TokenSigningAlgorithm algorithm) { mdlPresentationInput.algorithm = algorithm; return this; } + /** + * Builds and returns the fully constructed {@code MdlPresentationInput} object. + * + * @return the constructed {@code MdlPresentationInput} instance containing all the set properties. + */ public MdlPresentationInput build() { return mdlPresentationInput; } diff --git a/src/main/java/se/digg/wallet/datatypes/mdl/data/MdlPresentationValidationInput.java b/src/main/java/se/digg/wallet/datatypes/mdl/data/MdlPresentationValidationInput.java index bfd9604..a042663 100644 --- a/src/main/java/se/digg/wallet/datatypes/mdl/data/MdlPresentationValidationInput.java +++ b/src/main/java/se/digg/wallet/datatypes/mdl/data/MdlPresentationValidationInput.java @@ -3,14 +3,29 @@ import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; -import se.digg.wallet.datatypes.common.PresentationInput; import se.digg.wallet.datatypes.common.PresentationValidationInput; +/** + * MdlPresentationValidationInput is a specialized subclass of PresentationValidationInput, + * designed to handle the validation input for mDL (mobile Driver's License) presentations. + * It extends the functionality of the parent class by encapsulating additional fields + * specific to mDL validation requirements, such as clientId, responseUri, and mdocGeneratedNonce. + */ @EqualsAndHashCode(callSuper = true) @Data @NoArgsConstructor public class MdlPresentationValidationInput extends PresentationValidationInput { + /** + * Constructs a new instance of {@code MdlPresentationValidationInput} by extracting relevant + * fields from the provided {@code MdlPresentationInput} object. The constructor initializes + * the parent class with the request nonce and sets the client ID, response URI, and mdoc + * generated nonce specific to mDL presentations. + * + * @param presentationInput the {@code MdlPresentationInput} object containing the input data + * required to initialize this instance, including the client ID, + * response URI, and mdoc generated nonce + */ public MdlPresentationValidationInput(MdlPresentationInput presentationInput) { super(presentationInput.getNonce()); this.clientId = presentationInput.getClientId(); @@ -18,7 +33,10 @@ public MdlPresentationValidationInput(MdlPresentationInput presentationInput) { this.mdocGeneratedNonce = presentationInput.getMdocGeneratedNonce(); } + /** The presentation requester client ID (OpenID4VP) */ private String clientId; + /** The return URL for the presentation response */ private String responseUri; + /** The wallet generated nonce included as the apu header parameter in the presentation response JWT */ private String mdocGeneratedNonce; } diff --git a/src/main/java/se/digg/wallet/datatypes/mdl/data/MobileSecurityObject.java b/src/main/java/se/digg/wallet/datatypes/mdl/data/MobileSecurityObject.java index d94dde6..e0956d7 100644 --- a/src/main/java/se/digg/wallet/datatypes/mdl/data/MobileSecurityObject.java +++ b/src/main/java/se/digg/wallet/datatypes/mdl/data/MobileSecurityObject.java @@ -109,34 +109,62 @@ public static class MobileSecurityObjectBuilder {} //lombok workaround @NoArgsConstructor @Builder public static class DeviceKeyInfo { - + /** Wallet public key */ private COSEKey deviceKey; + /** Key authorizations */ private KeyAuthorizations keyAuthorizations; + /** Optional key info */ private Map keyInfo; } + /** + * Represents the validity information of a digital security object. + * This class contains the timestamps related to the signing, validity period, + * and expected update of the associated security object. + */ @Data @AllArgsConstructor @NoArgsConstructor @Builder public static class ValidityInfo { + /** Signing time */ private Instant signed; + /** Valid from time */ private Instant validFrom; + /** Expiration time */ private Instant validUntil; + /** Optional time for expected update */ private Instant expectedUpdate; } + /** + * The KeyAuthorizations class represents key authorization details, + * specifying access permissions for certain namespaces and data elements. + */ @Data @AllArgsConstructor @NoArgsConstructor @Builder public static class KeyAuthorizations { + /** Name spaces this key is authorized for */ private List nameSpaces; + /** Data element this key is authorized for */ private List dataElements; } + /** + * Signs the Mobile Security Object (MSO) using the specified certificate chain, key, and algorithm ID. + * + * @param chain the list of {@link X509Certificate} representing the certificate chain used for signing + * @param key the {@link COSEKey} used to create the digital signature + * @param algorithmID the {@link AlgorithmID} representing the algorithm used for signing + * @return the signed object as a {@link CBORObject} + * @throws IOException if an I/O error occurs during the signing process + * @throws CoseException if an error related to the COSE signing process occurs + * @throws CertificateEncodingException if an error occurs while encoding the certificates in the chain + */ @JsonIgnore public CBORObject sign( List chain, @@ -147,6 +175,20 @@ public CBORObject sign( return sign(chain, key, algorithmID, null, false); } + /** + * Signs the Mobile Security Object (MSO) using the specified certificate chain, key, algorithm ID, + * key identifier (KID), and KID protection flag. + * + * @param chain the list of {@link X509Certificate} representing the certificate chain used for signing + * @param key the {@link COSEKey} used to create the digital signature + * @param algorithmID the {@link AlgorithmID} representing the algorithm used for signing + * @param kid a string that specifies the Key Identifier (KID) to include in the COSE structure + * @param protectedKid a boolean flag indicating whether the KID is included as a protected attribute + * @return the signed object as a {@link CBORObject} + * @throws IOException if an I/O error occurs during the signing process + * @throws CoseException if an error related to the COSE signing process occurs + * @throws CertificateEncodingException if an error occurs while encoding the certificates in the chain + */ @JsonIgnore public CBORObject sign( List chain, @@ -161,8 +203,27 @@ public CBORObject sign( return msg.EncodeToCBORObject(); } + /** + * The Serializer class is a custom implementation of the {@link JsonSerializer} + * specifically for the {@link MobileSecurityObject} class. It is responsible for + * serializing an instance of {@link MobileSecurityObject} into a CBOR encoded + * representation using the {@link JsonGenerator}. + *

+ * The serialization process involves encoding various fields of the + * {@link MobileSecurityObject}, such as version, digest algorithm, value digests, + * device key information, document type, and validity information, into their + * respective CBOR representations. + *

+ * This class ensures that the serialization adheres to the required format and + * structure, and includes detailed handling of nested objects like + * {@link MobileSecurityObject.DeviceKeyInfo} and + * {@link MobileSecurityObject.KeyAuthorizations}. It also takes care of encoding + * maps, lists, and byte arrays in a structured and deterministic manner using + * CBOR utilities. + */ public static class Serializer extends JsonSerializer { + /** {@inheritDoc} */ @Override public void serialize( MobileSecurityObject mso, @@ -299,6 +360,13 @@ public void serialize( } } + /** + * Deserializes the provided CBOR-encoded byte array into a MobileSecurityObject instance. + * + * @param cborBytes the CBOR-encoded byte array containing the data to be deserialized + * @return the deserialized MobileSecurityObject + * @throws TokenParsingException if an error occurs while parsing the CBOR byte array + */ public static MobileSecurityObject deserialize(byte[] cborBytes) throws TokenParsingException { // Initialize CBORObject from bytes diff --git a/src/main/java/se/digg/wallet/datatypes/mdl/data/SessionTranscript.java b/src/main/java/se/digg/wallet/datatypes/mdl/data/SessionTranscript.java index d6fe5b2..041504c 100644 --- a/src/main/java/se/digg/wallet/datatypes/mdl/data/SessionTranscript.java +++ b/src/main/java/se/digg/wallet/datatypes/mdl/data/SessionTranscript.java @@ -13,11 +13,23 @@ @Getter public class SessionTranscript { + /** Requester client ID */ private final String clientId; + /** Presentation response URL */ private final String responseUri; + /** Request nonce */ private final String nonce; + /** Wallet generated response nonce */ private final String mdocGeneratedNonce; + /** + * Constructs a new SessionTranscript with the provided parameters. + * + * @param clientId the identifier for the requester client + * @param responseUri the URL used for presenting the response + * @param nonce the request nonce + * @param mdocGeneratedNonce the nonce generated by the wallet in the response + */ public SessionTranscript(String clientId, String responseUri, String nonce, String mdocGeneratedNonce) { this.clientId = clientId; this.responseUri = responseUri; @@ -25,6 +37,16 @@ public SessionTranscript(String clientId, String responseUri, String nonce, Stri this.mdocGeneratedNonce = mdocGeneratedNonce; } + /** + * Converts the session transcript data into a CBOR object representation. + * This includes generating a handover array containing hashed items of + * required fields, as well as structuring the session transcript array. + * + * @return a CBORObject representation of the session transcript, + * containing the handover data and session structure. + * @throws NullPointerException if any required field (clientId, responseUri, + * nonce, or mdocGeneratedNonce) is null. + */ public CBORObject toCborObject() { Objects.requireNonNull(this.clientId, "clientId must not be null"); Objects.requireNonNull(this.responseUri, "responseUri must not be null"); @@ -44,6 +66,16 @@ public CBORObject toCborObject() { return sessionTranscript; } + /** + * Generates a CBOR object containing a hash derived from the provided client ID and + * mdoc-generated nonce using the SHA-256 algorithm. + * + * @param clientId the ID of the client, used as one of the input parameters for the hash + * @param mdocGeneratedNonce the nonce generated by the mobile document (mdoc), + * used as the other input parameter for the hash + * @return a CBORObject representing the SHA-256 hash of the combined inputs + * @throws RuntimeException if the SHA-256 algorithm is not available + */ private CBORObject getHashItem(String clientId, String mdocGeneratedNonce) { try { MessageDigest messageDigest = MessageDigest.getInstance("SHA-256"); diff --git a/src/main/java/se/digg/wallet/datatypes/mdl/process/MdlIssuerSignedValidationResult.java b/src/main/java/se/digg/wallet/datatypes/mdl/process/MdlIssuerSignedValidationResult.java index 698dc97..d203599 100644 --- a/src/main/java/se/digg/wallet/datatypes/mdl/process/MdlIssuerSignedValidationResult.java +++ b/src/main/java/se/digg/wallet/datatypes/mdl/process/MdlIssuerSignedValidationResult.java @@ -9,10 +9,20 @@ import se.digg.wallet.datatypes.mdl.data.IssuerSigned; import se.digg.wallet.datatypes.mdl.data.MobileSecurityObject; +/** + * Represents the result of validation for an issuer-signed Mobile Driving License (mDL) token. + * This class holds additional data specific to issuer-signed validation results, beyond the + * general token validation data provided by the parent class {@code TokenValidationResult}. + *

+ * The {@code MdlIssuerSignedValidationResult} class extends {@code TokenValidationResult} + * by including data fields for the issuer-signed content and the Mobile Security Object (MSO). + */ @EqualsAndHashCode(callSuper = true) @Data @NoArgsConstructor public class MdlIssuerSignedValidationResult extends TokenValidationResult { + /** Represents the token or data structure containing information signed by the issuer */ protected IssuerSigned issuerSigned; + /** Represents the Mobile Security Object, which encapsulates relevant signed data used in token validation */ protected MobileSecurityObject mso; } diff --git a/src/main/java/se/digg/wallet/datatypes/mdl/process/MdlPresentationValidationResult.java b/src/main/java/se/digg/wallet/datatypes/mdl/process/MdlPresentationValidationResult.java index 9b76613..07ab889 100644 --- a/src/main/java/se/digg/wallet/datatypes/mdl/process/MdlPresentationValidationResult.java +++ b/src/main/java/se/digg/wallet/datatypes/mdl/process/MdlPresentationValidationResult.java @@ -1,18 +1,33 @@ package se.digg.wallet.datatypes.mdl.process; +import java.util.Map; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import se.digg.wallet.datatypes.common.TokenAttributeType; -import java.util.Map; -import java.util.Objects; - +/** + * Represents the result of validation for an mDL (Mobile Driving License) presentation. + * This class extends the {@code MdlIssuerSignedValidationResult} and includes additional + * fields to capture specific information and status relevant to the mDL presentation. + */ @EqualsAndHashCode(callSuper = true) @Data @NoArgsConstructor public class MdlPresentationValidationResult extends MdlIssuerSignedValidationResult { + /** + * Constructs a new instance of MdlPresentationValidationResult with specified parameters. + * + * @param issuerSignedValidationResult the issuer-signed validation result containing validation key, + * certificate, chain, wallet public key, issue time, expiration time, + * presentation request nonce, issuer signed data, and Mobile Security Object (MSO). + * @param docType the document type of the Mobile Driving License (mDL) presentation + * @param status the status code representing the validation result of the mDL presentation + * @param version the version of the mDL presentation + * @param disclosedAttributes a map of attributes disclosed during the validation, where the key is the + * attribute type and the value is the attribute's value + */ public MdlPresentationValidationResult(MdlIssuerSignedValidationResult issuerSignedValidationResult, String docType, int status, String version, Map disclosedAttributes) { super(); @@ -31,7 +46,10 @@ public MdlPresentationValidationResult(MdlIssuerSignedValidationResult issuerSig this.version = version; } + /** Document type identifier */ String docType; + /** Status (always 0 on success) */ int status; + /** Version (shall be 1.0) */ String version; } diff --git a/src/main/java/se/digg/wallet/datatypes/mdl/process/MdlPresentationValidator.java b/src/main/java/se/digg/wallet/datatypes/mdl/process/MdlPresentationValidator.java index 707ae90..bd2b1b9 100644 --- a/src/main/java/se/digg/wallet/datatypes/mdl/process/MdlPresentationValidator.java +++ b/src/main/java/se/digg/wallet/datatypes/mdl/process/MdlPresentationValidator.java @@ -39,12 +39,21 @@ @Slf4j public class MdlPresentationValidator implements PresentationValidator { + /** The maximum allowed time deviation (Duration) for verifying the validity of the data */ private final Duration timeSkew; + /** + * Constructs a new instance of the MdlPresentationValidator with a default timeout duration of 30 seconds. + */ public MdlPresentationValidator() { this(Duration.ofSeconds(30)); } + /** + * Constructs a new instance of the MdlPresentationValidator with the specified time skew. + * + * @param timeSkew the maximum allowed time deviation (Duration) for verifying the validity of the data. + */ public MdlPresentationValidator(Duration timeSkew) { this.timeSkew = timeSkew; } diff --git a/src/main/java/se/digg/wallet/datatypes/mdl/process/MdlTokenIssuer.java b/src/main/java/se/digg/wallet/datatypes/mdl/process/MdlTokenIssuer.java index 7e49cdf..27732a6 100644 --- a/src/main/java/se/digg/wallet/datatypes/mdl/process/MdlTokenIssuer.java +++ b/src/main/java/se/digg/wallet/datatypes/mdl/process/MdlTokenIssuer.java @@ -5,23 +5,20 @@ package se.digg.wallet.datatypes.mdl.process; import com.fasterxml.jackson.core.JsonProcessingException; - import java.io.IOException; import java.math.BigInteger; -import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; import java.security.cert.CertificateEncodingException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import lombok.Setter; -import se.digg.wallet.datatypes.common.*; -import se.digg.wallet.datatypes.common.TokenSigningAlgorithm; +import org.bouncycastle.crypto.CryptoServicesRegistrar; +import se.digg.cose.CoseException; +import se.digg.wallet.datatypes.common.TokenAttribute; +import se.digg.wallet.datatypes.common.TokenInput; +import se.digg.wallet.datatypes.common.TokenIssuer; +import se.digg.wallet.datatypes.common.TokenIssuingException; import se.digg.wallet.datatypes.mdl.data.CBORUtils; import se.digg.wallet.datatypes.mdl.data.IssuerSigned; import se.digg.wallet.datatypes.mdl.data.IssuerSignedItem; -import se.digg.cose.CoseException; /** * mDL token issuer implementing the common TokenIssuer interface producing the IssuerSigned part @@ -30,7 +27,7 @@ public class MdlTokenIssuer implements TokenIssuer { /** Random source for hash salts */ - private static final SecureRandom RNG = new SecureRandom(); + private static final Random RNG = CryptoServicesRegistrar.getSecureRandom(); /** mDL version for this token issuer */ private static final String MDL_VERSION = "1.0"; diff --git a/src/main/java/se/digg/wallet/datatypes/mdl/process/MdlTokenPresenter.java b/src/main/java/se/digg/wallet/datatypes/mdl/process/MdlTokenPresenter.java index db6c3de..739c3b9 100644 --- a/src/main/java/se/digg/wallet/datatypes/mdl/process/MdlTokenPresenter.java +++ b/src/main/java/se/digg/wallet/datatypes/mdl/process/MdlTokenPresenter.java @@ -12,12 +12,28 @@ import java.util.List; import java.util.Map; +/** + * MdlTokenPresenter is an implementation of the {@code TokenPresenter} interface + * specialized for processing mDL (mobile Driver's License) tokens. This class is responsible + * for verifying the provided mDL token, applying selective disclosures, and producing + * a cryptographically signed response suitable for presentation. + * + * The class validates the token and disclosures provided in the {@code MdlPresentationInput}, + * ensures the integrity of the token data, and generates a presentation response using the given + * wallet private key. Additionally, it utilizes namespaces and selective disclosures to construct a + * tailored response based on the disclosed attributes, ensuring privacy and compliance with the + * specifications. + */ public class MdlTokenPresenter implements TokenPresenter { + /** + * Default constructor for the MdlTokenPresenter class. + */ public MdlTokenPresenter() { } + /** {@inheritDoc} */ @Override public byte[] presentToken(PresentationInput presentationInput, PrivateKey privateKey) throws TokenPresentationException { @@ -59,6 +75,13 @@ public byte[] presentToken(PresentationInput presentationInput, PrivateKey pr } } + /** + * Filters and returns a subset of the provided namespaces based on the disclosed element identifiers. + * + * @param nameSpaces a map where keys represent namespace names, and values are lists of IssuerSignedItem instances representing signed attributes in those namespaces + * @param disclosures a map where keys represent namespace names, and values are lists of attribute name identifiers that should be disclosed + * @return a map containing only the entries from the input nameSpaces that match the disclosed namespaces and element identifiers provided in disclosures + */ private Map> getDisclosedNameSpaces(Map> nameSpaces, Map> disclosures) { if (disclosures == null || disclosures.isEmpty()) { return nameSpaces; diff --git a/src/main/java/se/digg/wallet/datatypes/sdjwt/JSONUtils.java b/src/main/java/se/digg/wallet/datatypes/sdjwt/JSONUtils.java index 0ed0499..999ceb7 100644 --- a/src/main/java/se/digg/wallet/datatypes/sdjwt/JSONUtils.java +++ b/src/main/java/se/digg/wallet/datatypes/sdjwt/JSONUtils.java @@ -28,13 +28,35 @@ import org.bouncycastle.jce.spec.ECNamedCurveSpec; import se.digg.wallet.datatypes.sdjwt.data.Disclosure; +/** + * Utility class providing methods related to JSON data processing. + */ public class JSONUtils { + /** + * A globally accessible and preconfigured instance of the Jackson {@code ObjectMapper} for handling JSON serialization and deserialization. + * + *

    + *
  • Configured to disable writing dates as timestamps ({@code SerializationFeature.WRITE_DATES_AS_TIMESTAMPS}).
  • + *
  • Supports Java 8 date and time types through the registration of {@code JavaTimeModule}.
  • + *
  • Excludes properties with {@code null} values during serialization, using {@code JsonInclude.Include.NON_NULL}.
  • + *
+ * + * This object is intended to be reused anywhere in the application to ensure consistent JSON processing behavior. + */ public static final ObjectMapper JSON_MAPPER = new ObjectMapper() .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false) .registerModule(new JavaTimeModule()) .setSerializationInclusion(JsonInclude.Include.NON_NULL); + /** + * Generates a base64 URL-encoded hash string for the given disclosure object using the specified hashing algorithm. + * + * @param disclosure the disclosure object containing the data to be hashed + * @param hashAlgo the name of the hashing algorithm to use (e.g., "SHA-256") + * @return a base64 URL-encoded string representing the hash of the disclosure + * @throws NoSuchAlgorithmException if the specified hashing algorithm is not available + */ public static String disclosureHashString( Disclosure disclosure, String hashAlgo @@ -42,6 +64,14 @@ public static String disclosureHashString( return base64URLString(disclosureHash(disclosure, hashAlgo)); } + /** + * Computes a hash of the base64 URL-encoded disclosure string using the specified hashing algorithm. + * + * @param disclosure the disclosure object containing the base64 URL-encoded disclosure string to be hashed + * @param hashAlgo the name of the hashing algorithm to use (e.g., "SHA-256") + * @return a byte array representing the computed hash + * @throws NoSuchAlgorithmException if the specified hashing algorithm is not available + */ public static byte[] disclosureHash(Disclosure disclosure, String hashAlgo) throws NoSuchAlgorithmException { return hash( @@ -52,21 +82,50 @@ public static byte[] disclosureHash(Disclosure disclosure, String hashAlgo) ); } + /** + * Encodes the given byte array into a Base64 URL-encoded string without padding. + * + * @param bytes the byte array to be encoded + * @return a Base64 URL-encoded string representation of the input byte array + */ public static String base64URLString(byte[] bytes) { return Base64.getUrlEncoder().withoutPadding().encodeToString(bytes); } + /** + * Computes the hash of the given byte array using the specified hashing algorithm. + * + * @param input the input byte array to be hashed + * @param algo the name of the hashing algorithm to use (e.g., "SHA-256") + * @return a byte array representing the hash of the input + * @throws NoSuchAlgorithmException if the specified hashing algorithm is not available + */ public static byte[] hash(byte[] input, String algo) throws NoSuchAlgorithmException { MessageDigest digest = MessageDigest.getInstance(algo); return digest.digest(input); } + /** + * Computes a Base64 URL-encoded hash string of the given byte array input using the specified hashing algorithm. + * + * @param input the byte array to be hashed + * @param alg the name of the hashing algorithm to use (e.g., "SHA-256") + * @return a Base64 URL-encoded string representation of the computed hash + * @throws NoSuchAlgorithmException if the specified hashing algorithm is not available + */ public static String b64UrlHash(byte[] input, String alg) throws NoSuchAlgorithmException { return base64URLString(hash(input, alg)); } + /** + * Converts a given public key into a JSON Web Key (JWK) representation. + * + * @param publicKey the public key to convert, which can be either an RSA or EC public key + * @return the JWK representation of the given public key + * @throws NoSuchAlgorithmException if the type of the provided public key is not supported + */ public static JWK getJWKfromPublicKey(PublicKey publicKey) throws NoSuchAlgorithmException { if (publicKey instanceof RSAPublicKey) { @@ -82,6 +141,16 @@ public static JWK getJWKfromPublicKey(PublicKey publicKey) throw new NoSuchAlgorithmException("Public key type not supported"); } + /** + * Retrieves a public key from the given JSON Web Key (JWK) object. + * The JWK can either be of type RSAKey or ECKey. + * + * @param jwk the JSON Web Key (JWK) object from which the public key should be extracted. + * Must be an instance of RSAKey or ECKey. + * @return the extracted public key as a {@code PublicKey} object. + * @throws JOSEException if an error occurs during the public key extraction process. + * @throws IllegalArgumentException if the provided JWK is of an unsupported type. + */ public static PublicKey getPublicKeyFromJWK(JWK jwk) throws JOSEException { if (jwk instanceof RSAKey) { return ((RSAKey) jwk).toRSAPublicKey(); diff --git a/src/main/java/se/digg/wallet/datatypes/sdjwt/data/ClaimsWithDisclosure.java b/src/main/java/se/digg/wallet/datatypes/sdjwt/data/ClaimsWithDisclosure.java index 3dfbe31..d5c374f 100644 --- a/src/main/java/se/digg/wallet/datatypes/sdjwt/data/ClaimsWithDisclosure.java +++ b/src/main/java/se/digg/wallet/datatypes/sdjwt/data/ClaimsWithDisclosure.java @@ -50,12 +50,24 @@ public class ClaimsWithDisclosure { private Map openClaims; /** - * Any members here are only included in case there is a sublevel of discloseable items. + * Any members here are only included in case there is a sublevel of disclosable items. * This could be the case, for example, if a claim "Address" * has a substructure of individual subclaims each supporting selectable disclosure. */ private Map claimsWithDisclosure; + /** + * Parses a map of claims into an object that maps claims to their corresponding disclosures + * using a provided list of disclosures and a specific token digest algorithm. + * + * @param claimsMap a map containing claim keys and their respective values, including potentially + * nested claims and disclosures. + * @param disclosureList a list of disclosures used for associating claim entries within the map. + * @param sdAlg the token digest algorithm used for generating or validating hash strings of disclosures. + * @return an instance of ClaimsWithDisclosure containing processed claims and their associated disclosures. + * @throws NoSuchAlgorithmException if the token digest algorithm provided is not supported. + * @throws IllegalArgumentException if the `_sd` parameter in the claims map is malformed. + */ public static ClaimsWithDisclosure parse( Map claimsMap, List disclosureList, @@ -82,12 +94,7 @@ public static ClaimsWithDisclosure parse( ClaimsWithDisclosure subCwd = parse(subMap, disclosureList, sdAlg); cwdBuilder.claimsWithDisclosure(entry.getKey(), subCwd); } else { - subMap - .entrySet() - .forEach( - subEntry -> - cwdBuilder.openClaim(subEntry.getKey(), subEntry.getValue()) - ); + subMap.forEach(cwdBuilder::openClaim); } } if (entry.getValue() instanceof List) { @@ -107,6 +114,12 @@ public static ClaimsWithDisclosure parse( return cwdBuilder.build(); } + /** + * Retrieves all disclosures associated with the current object, including disclosures + * stored in the list and any nested disclosures within claims. + * + * @return a list of all disclosures, combining both direct and nested disclosures. + */ public List getAllDisclosures() { List allDiscosures = new ArrayList<>(disclosures); if (claimsWithDisclosure != null) { @@ -124,7 +137,9 @@ public List getAllDisclosures() { /** * Get all claims that need to be present in addition to the _sd selectably discolable claims and values + * * @return claims to include in the issuer signed jwt with selectable disclosure + * @throws NoSuchAlgorithmException if an unsupported algorithm is encountered while computing hashes. */ public Map getAllSupportingClaims() throws NoSuchAlgorithmException { @@ -154,14 +169,35 @@ public Map getAllSupportingClaims() return allSupportingClaims; } + /** + * Creates a new instance of ClaimsWithDisclosureBuilder using the specified token digest algorithm. + * + * @param hashAlgo the token digest algorithm to be used for handling claim disclosures. + * @return a new instance of ClaimsWithDisclosureBuilder initialized with the provided token digest algorithm. + */ public static ClaimsWithDisclosureBuilder builder(TokenDigestAlgorithm hashAlgo) { return new ClaimsWithDisclosureBuilder(hashAlgo); } + /** + * A builder class for constructing an instance of ClaimsWithDisclosure. + * Provides methods for adding claims, disclosures, and other related data + * to create a finalized ClaimsWithDisclosure object. + */ public static class ClaimsWithDisclosureBuilder { + /** + * The object being built by this builder + */ private final ClaimsWithDisclosure claimsWithDisclosure; + /** + * Constructs a new ClaimsWithDisclosureBuilder instance. + * Initializes an empty ClaimsWithDisclosure object and sets up + * the hashing algorithm for disclosures based on the provided TokenDigestAlgorithm. + * + * @param hashAlgo the TokenDigestAlgorithm used for defining the hash function for disclosures. + */ public ClaimsWithDisclosureBuilder(TokenDigestAlgorithm hashAlgo) { this.claimsWithDisclosure = new ClaimsWithDisclosure(); this.claimsWithDisclosure.hashAlgo = hashAlgo.getSdJwtName(); @@ -171,11 +207,28 @@ public ClaimsWithDisclosureBuilder(TokenDigestAlgorithm hashAlgo) { this.claimsWithDisclosure.claimsWithDisclosure = new HashMap<>(); } + /** + * Adds a disclosure object to the list of disclosures in the ClaimsWithDisclosure being built. + * + * @param disclosures the {@link Disclosure} object containing disclosure data to be added + * @return the current instance of {@link ClaimsWithDisclosureBuilder} for method chaining + */ public ClaimsWithDisclosureBuilder disclosure(Disclosure disclosures) { this.claimsWithDisclosure.disclosures.add(disclosures); return this; } + /** + * Adds an entry to the specified claim as an array. If the value provided + * is a {@link Disclosure} object, it will be hashed using the defined + * hashing algorithm and stored as a reference. Otherwise, the value is + * directly added to the list for the claim. + * + * @param claimName the name of the claim to which the value or disclosure will be added + * @param valueOrDisclosure the value or {@link Disclosure} object to be added to the claim's array + * @return the current instance of {@link ClaimsWithDisclosureBuilder} for method chaining + * @throws NoSuchAlgorithmException if the hashing algorithm for the disclosure is not supported + */ public ClaimsWithDisclosureBuilder arrayEntry( String claimName, Object valueOrDisclosure @@ -200,11 +253,27 @@ public ClaimsWithDisclosureBuilder arrayEntry( return this; } + /** + * Adds an entry to the open claims within the ClaimsWithDisclosure being built. + * The specified key-value pair is stored in the open claims map, meaning that this claim + * is presented in clear text and will be available always without any disclosure data. + * + * @param key the key for the open claim to be added + * @param value the value associated with the specified key for the open claim + * @return the current instance of {@link ClaimsWithDisclosureBuilder} for method chaining + */ public ClaimsWithDisclosureBuilder openClaim(String key, Object value) { this.claimsWithDisclosure.openClaims.put(key, value); return this; } + /** + * Adds a sublevel ClaimsWithDisclosure structure to this ClaimsWithDisclosure object under a claim name as key. + * + * @param key the claim name to associate with the ClaimsWithDisclosure value + * @param value the ClaimsWithDisclosure value to be added to the map + * @return the current instance of ClaimsWithDisclosureBuilder for method chaining + */ public ClaimsWithDisclosureBuilder claimsWithDisclosure( String key, ClaimsWithDisclosure value @@ -213,6 +282,11 @@ public ClaimsWithDisclosureBuilder claimsWithDisclosure( return this; } + /** + * Builds and returns the constructed ClaimsWithDisclosure object. + * + * @return the constructed instance of {@link ClaimsWithDisclosure} + */ public ClaimsWithDisclosure build() { return claimsWithDisclosure; } diff --git a/src/main/java/se/digg/wallet/datatypes/sdjwt/data/Disclosure.java b/src/main/java/se/digg/wallet/datatypes/sdjwt/data/Disclosure.java index 6dd7c68..062297b 100644 --- a/src/main/java/se/digg/wallet/datatypes/sdjwt/data/Disclosure.java +++ b/src/main/java/se/digg/wallet/datatypes/sdjwt/data/Disclosure.java @@ -11,9 +11,11 @@ import java.security.SecureRandom; import java.util.Base64; import java.util.List; +import java.util.Random; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; +import org.bouncycastle.crypto.CryptoServicesRegistrar; import se.digg.wallet.datatypes.common.TokenAttribute; import se.digg.wallet.datatypes.common.TokenValidationException; import se.digg.wallet.datatypes.sdjwt.JSONUtils; @@ -26,13 +28,32 @@ @NoArgsConstructor public class Disclosure { - public static final SecureRandom RNG = new SecureRandom(); + /** Random source */ + public static final Random RNG = CryptoServicesRegistrar.getSecureRandom(); + /** The random salt value of the disclosure */ private String salt; + /** The name of the disclosed attribute when applicable */ private String name; + /** The disclosed attribute value */ private Object value; + /** The base64 disclosure string being attached to an SD JWT structure representing this disclosure */ private String disclosure; + /** + * Constructs a new Disclosure instance using the provided TokenAttribute. + * This constructor generates a random salt, extracts relevant information + * from the TokenAttribute, and creates a base64-encoded disclosure string. + * + * @param tokenAttribute the TokenAttribute object containing the type (name) + * and value of the attribute to be disclosed. If the + * type is not null, its name will be included in the + * disclosure along with the value; otherwise, only the + * value will be included + * @throws JsonProcessingException if there is an error during the JSON + * serialization process while creating the + * disclosure string + */ public Disclosure(TokenAttribute tokenAttribute) throws JsonProcessingException { this.salt = Base64.getUrlEncoder() @@ -49,6 +70,19 @@ public Disclosure(TokenAttribute tokenAttribute) ); } + /** + * Constructs a new Disclosure instance using the provided base64 URL-encoded disclosure string. + * This constructor decodes the string, parses it into components, and initializes + * the salt, name, and value fields based on the content of the parsed disclosure string. + * + * @param disclosureB64Url the base64 URL-encoded string representing the disclosure. This string + * must contain the salt as the first value and can optionally include + * the name of the attribute (as the second value) and the attribute value + * + * @throws JsonProcessingException if there is an error during the JSON parsing process + * @throws TokenValidationException if the disclosure format is invalid, i.e., the decoded + * and parsed data does not meet the expected structure + */ public Disclosure(String disclosureB64Url) throws JsonProcessingException, TokenValidationException { this.disclosure = new String( diff --git a/src/main/java/se/digg/wallet/datatypes/sdjwt/data/SdJwt.java b/src/main/java/se/digg/wallet/datatypes/sdjwt/data/SdJwt.java index ca1db4b..8b0576c 100644 --- a/src/main/java/se/digg/wallet/datatypes/sdjwt/data/SdJwt.java +++ b/src/main/java/se/digg/wallet/datatypes/sdjwt/data/SdJwt.java @@ -106,6 +106,13 @@ public class SdJwt { /** Key binding proof signed by the wallet */ private SignedJWT walletSigned; + /** + * Creates a new instance of SdJwtBuilder to initialize and build an SdJwt object. + * + * @param issuer the issuer of the SD-JWT, which must not be null. + * @param sdAlg the token digest algorithm to be used for the SD-JWT, which must not be null. + * @return an SdJwtBuilder instance for configuring and building an SdJwt object. + */ public static SdJwtBuilder builder(String issuer, TokenDigestAlgorithm sdAlg) { return new SdJwtBuilder(issuer, sdAlg); } @@ -234,8 +241,16 @@ public String protectedPresentation( */ public static class SdJwtBuilder { + /** The object being built */ private final SdJwt sdJwt; + /** + * Constructs a new SdJwtBuilder with the specified issuer and token digest algorithm. + * + * @param issuer the identifier of the entity issuing the SD-JWT. Must not be null. + * @param sdAlg the token digest algorithm to be used in the SD-JWT. Must not be null. + * @throws NullPointerException if the issuer or sdAlg is null. + */ public SdJwtBuilder(String issuer, TokenDigestAlgorithm sdAlg) { Objects.requireNonNull(issuer, "issuer must not be null"); Objects.requireNonNull(sdAlg, "sd_alg must not be null"); @@ -244,6 +259,14 @@ public SdJwtBuilder(String issuer, TokenDigestAlgorithm sdAlg) { sdJwt.setSdAlgorithm(sdAlg); } + /** + * Sets the ClaimsWithDisclosure object for the SdJwt being built. + * The ClaimsWithDisclosure represents the claims and their corresponding disclosures + * to be included in the token. + * + * @param claimsWithDisclosure the ClaimsWithDisclosure object containing claims and disclosures + * @return the current SdJwtBuilder instance + */ public SdJwtBuilder claimsWithDisclosure( ClaimsWithDisclosure claimsWithDisclosure ) { @@ -251,30 +274,86 @@ public SdJwtBuilder claimsWithDisclosure( return this; } + /** + * Sets the confirmation key for the SD-JWT being built. + * The confirmation key typically corresponds to a public key from a wallet or other entity + * and is used to bind the token to a specific cryptographic key. + * + * @param walletPublic the JWK (JSON Web Key) object representing the public key to be set as the confirmation key + * @return the current SdJwtBuilder instance, allowing for method chaining + */ public SdJwtBuilder confirmationKey(JWK walletPublic) { sdJwt.setConfirmationKey(walletPublic); return this; } + /** + * Sets the Verifiable Credential (VC) type for the SD-JWT being built. + * The VC type specifies the classification or schema of the verifiable credential + * associated with this SD-JWT. + * + * @param vcType the type of verifiable credential to be associated with the SD-JWT + * @return the current SdJwtBuilder instance, allowing for method chaining + */ public SdJwtBuilder verifiableCredentialType(String vcType) { sdJwt.setVcType(vcType); return this; } + /** + * Sets the status for the SD-JWT being built. + * The status represents a user-defined object to indicate the state or condition + * of the SD-JWT. + * + * @param status the status object to be set for the SD-JWT + * @return the current SdJwtBuilder instance, allowing for method chaining + */ public SdJwtBuilder status(Object status) { sdJwt.setStatus(status); return this; } + /** + * Sets the optional subject for the SD-JWT being built. + * The subject typically represents the entity that the token is issued to + * and serves as a key claim in the SD-JWT payload. + * + * @param subject the subject to associate with the SD-JWT + * @return the current SdJwtBuilder instance, allowing for method chaining + */ public SdJwtBuilder subject(String subject) { sdJwt.setSubject(subject); return this; } + + /** + * Configures the SD-JWT being built to use either the legacy or standard JWT type. + * The method determines the JWT type based on the provided boolean flag. + * + * @param legacySdJwtType a boolean indicating whether the legacy SD-JWT type should be used. + * If true, the legacy type is set; otherwise, the standard type is used. + * @return the current SdJwtBuilder instance, allowing for method chaining. + */ public SdJwtBuilder legacySdJwtType(boolean legacySdJwtType) { sdJwt.setJwtType(legacySdJwtType ? SD_JWT_TYPE_LEGACY : SD_JWT_TYPE); return this; } + /** + * Builds and signs an SD-JWT (Selective Disclosure JWT) using the provided configuration. + * The method prepares the claims, adds necessary metadata, signs the token using the provided + * signer, and returns the constructed SD-JWT object. + * + * @param issuerCredential the PKI credential of the issuer, used to provide the signing certificate + * @param validity the duration for which the token is valid, from the current time + * @param algorithm the JWS (JSON Web Signature) algorithm used for signing the token + * @param signer the JWS signer instance responsible for signing the token + * @param kid the Key ID (KID) used to identify the signing key + * @return the constructed and signed SdJwt object + * @throws JOSEException if an error occurs during the signing process + * @throws NoSuchAlgorithmException if a specified algorithm is not available in the environment + * @throws CertificateEncodingException if an error occurs encoding the certificate + */ public SdJwt build( PkiCredential issuerCredential, Duration validity, diff --git a/src/main/java/se/digg/wallet/datatypes/sdjwt/data/SdJwtPresentationInput.java b/src/main/java/se/digg/wallet/datatypes/sdjwt/data/SdJwtPresentationInput.java index 9878b8d..e771f5c 100644 --- a/src/main/java/se/digg/wallet/datatypes/sdjwt/data/SdJwtPresentationInput.java +++ b/src/main/java/se/digg/wallet/datatypes/sdjwt/data/SdJwtPresentationInput.java @@ -7,45 +7,106 @@ import java.util.List; +/** + * A specialized implementation of the {@link PresentationInput} class designed to handle + * specific requirements for SD-JWT presentations. This class includes additional fields and methods + * tailored to SD-JWT workflows, such as specifying an audience and associated disclosures. + */ @EqualsAndHashCode(callSuper = true) @Data public class SdJwtPresentationInput extends PresentationInput> { + /** The audience of the SD JWT */ private String audience; + /** + * Creates and returns a new builder instance of {@code SdJwtPresentationInputbuilder}, which + * facilitates the construction of {@code SdJwtPresentationInput} objects by allowing chained + * method calls to set various properties. + * + * @return a new instance of {@code SdJwtPresentationInputbuilder} + */ public static SdJwtPresentationInputbuilder builder() { return new SdJwtPresentationInputbuilder(); } + /** + * Builder class for creating instances of {@link SdJwtPresentationInput}. + */ public static class SdJwtPresentationInputbuilder { + /** + * The object being built + */ private final SdJwtPresentationInput presentationInput; + /** + * Constructs a new instance of SdJwtPresentationInputbuilder. + */ public SdJwtPresentationInputbuilder() { presentationInput = new SdJwtPresentationInput(); } + /** + * Sets the token for the {@link SdJwtPresentationInput} being built. + * + * @param token the token to set, represented as a byte array + * @return the current instance of the SdJwtPresentationInputbuilder for method chaining + */ public SdJwtPresentationInputbuilder token(byte[] token) { presentationInput.token = token; return this; } + + /** + * Sets the nonce for the {@link SdJwtPresentationInput} being built. + * + * @param nonce the nonce to set, represented as a String + * @return the current instance of the SdJwtPresentationInputbuilder for method chaining + */ public SdJwtPresentationInputbuilder nonce(String nonce) { presentationInput.nonce = nonce; return this; } + + /** + * Sets the cryptographic signing algorithm for the {@code SdJwtPresentationInput} being built. + * + * @param algorithm the cryptographic signing algorithm to set, represented as an instance of {@link TokenSigningAlgorithm} + * @return the current instance of {@code SdJwtPresentationInputbuilder} for method chaining + */ public SdJwtPresentationInputbuilder algorithm(TokenSigningAlgorithm algorithm) { presentationInput.algorithm = algorithm; return this; } + + /** + * Sets the disclosures for the {@link SdJwtPresentationInput} being built. + * + * @param disclosures the list of disclosures to set, represented as a List of Strings + * @return the current instance of the SdJwtPresentationInputbuilder for method chaining + */ public SdJwtPresentationInputbuilder disclosures(List disclosures) { presentationInput.disclosures = disclosures; return this; } + + /** + * Sets the audience for the {@code SdJwtPresentationInput} being built. + * + * @param audience the audience to set, represented as a String + * @return the current instance of the {@code SdJwtPresentationInputbuilder} for method chaining + */ public SdJwtPresentationInputbuilder audience(String audience) { presentationInput.audience = audience; return this; } + /** + * Finalizes the building process and returns the constructed instance of {@code SdJwtPresentationInput}. + * + * @return the fully constructed {@code SdJwtPresentationInput} with all specified properties set + */ public SdJwtPresentationInput build() { return presentationInput; } diff --git a/src/main/java/se/digg/wallet/datatypes/sdjwt/data/SdJwtPresentationValidationInput.java b/src/main/java/se/digg/wallet/datatypes/sdjwt/data/SdJwtPresentationValidationInput.java index 0a979de..cb41a50 100644 --- a/src/main/java/se/digg/wallet/datatypes/sdjwt/data/SdJwtPresentationValidationInput.java +++ b/src/main/java/se/digg/wallet/datatypes/sdjwt/data/SdJwtPresentationValidationInput.java @@ -4,15 +4,30 @@ import lombok.EqualsAndHashCode; import se.digg.wallet.datatypes.common.PresentationValidationInput; +/** + * Represents the input required for validating a presentation in the SD-JWT context. + * Extends the functionality of PresentationValidationInput to include additional + * properties specific to SD-JWT validation. + */ @EqualsAndHashCode(callSuper = true) @Data public class SdJwtPresentationValidationInput extends PresentationValidationInput { + /** + * Constructs an instance of SdJwtPresentationValidationInput, initializing the input + * required for validating an SD-JWT presentation with the provided request nonce and audience. + * + * @param requestNonce the unique identifier provided by the relying party, used to + * verify the request's authenticity and prevent replay attacks. + * @param audience the intended recipient or audience for the SD-JWT, used to + * validate that the token is presented to the correct party. + */ public SdJwtPresentationValidationInput(String requestNonce, String audience) { super(requestNonce); this.audience = audience; } + /** The intended recipient or audience for the SD-JWT */ private String audience; } diff --git a/src/main/java/se/digg/wallet/datatypes/sdjwt/process/SdJwtTokenInput.java b/src/main/java/se/digg/wallet/datatypes/sdjwt/process/SdJwtTokenInput.java index 53f22d4..e5280b2 100644 --- a/src/main/java/se/digg/wallet/datatypes/sdjwt/process/SdJwtTokenInput.java +++ b/src/main/java/se/digg/wallet/datatypes/sdjwt/process/SdJwtTokenInput.java @@ -4,7 +4,6 @@ package se.digg.wallet.datatypes.sdjwt.process; -import com.nimbusds.jose.JWSAlgorithm; import java.security.PublicKey; import java.time.Duration; import java.util.List; @@ -16,13 +15,32 @@ import se.digg.wallet.datatypes.sdjwt.data.ClaimsWithDisclosure; import se.swedenconnect.security.credential.PkiCredential; +/** + * Represents an input object for generating an SD-JWT (Selective Disclosure-JSON Web Token). + * Extends the functionality provided by the {@code TokenInput} class to include additional + * fields and methods specific to SD-JWTs. + */ @EqualsAndHashCode(callSuper = true) @Data public class SdJwtTokenInput extends TokenInput { + /** Claims with associated disclosure data */ private ClaimsWithDisclosure claimsWithDisclosure; + /** The type identifier for the verifiable credential type represented in this SD-JWT */ private String verifiableCredentialType; + /** + * Constructor for creating an SdJwtTokenInput instance. + * + * @param issuer the issuer of the token. + * @param attributes the list of token attributes that are private and require disclosure by the wallet. + * @param openAttributes the list of token attributes that are publicly accessible without disclosure. + * @param issuerCredential the credential used by the issuer for signing and verification. + * @param expirationDuration the duration after which the token expires. + * @param algorithm the signing algorithm used for creating the token signature. + * @param walletPublicKey the public key of the wallet intended to interact with the token. + * @param claimsWithDisclosure the claims along with their respective disclosures. + */ public SdJwtTokenInput( String issuer, List attributes, @@ -45,20 +63,44 @@ public SdJwtTokenInput( this.claimsWithDisclosure = claimsWithDisclosure; } + /** + * Default constructor for the SdJwtTokenInput class. + */ public SdJwtTokenInput() {} + /** + * Provides a new instance of the SdJwtTokenInputBuilder, which is used for + * configuring and building an SdJwtTokenInput object with the desired properties. + * + * @return a new instance of SdJwtTokenInputBuilder for constructing an SdJwtTokenInput. + */ public static SdJwtTokenInputBuilder sdJwtINputBuilder() { return new SdJwtTokenInputBuilder(); } + /** + * Builder class for constructing instances of SdJwtTokenInput. + */ public static class SdJwtTokenInputBuilder { + /** + * The object being built + */ private final SdJwtTokenInput tokenInput; + /** + * Constructs a new instance of the SdJwtTokenInputBuilder class. + */ public SdJwtTokenInputBuilder() { tokenInput = new SdJwtTokenInput(); } + /** + * Sets the claims with disclosure for the SdJwtTokenInput being built. + * + * @param claimsWithDisclosure the claims with disclosure object to set + * @return the current instance of SdJwtTokenInputBuilder for method chaining + */ public SdJwtTokenInputBuilder claimsWithDisclosure( ClaimsWithDisclosure claimsWithDisclosure ) { @@ -66,6 +108,12 @@ public SdJwtTokenInputBuilder claimsWithDisclosure( return this; } + /** + * Sets the verifiable credential type for the SdJwtTokenInput being built. + * + * @param verifiableCredentialType the type of the verifiable credential to set + * @return the current instance of SdJwtTokenInputBuilder for method chaining + */ public SdJwtTokenInputBuilder verifiableCredentialType( String verifiableCredentialType ) { @@ -73,16 +121,35 @@ public SdJwtTokenInputBuilder verifiableCredentialType( return this; } + /** + * Sets the issuer for the SdJwtTokenInput being built. + * + * @param issuer the issuer of the token to set + * @return the current instance of SdJwtTokenInputBuilder for method chaining + */ public SdJwtTokenInputBuilder issuer(String issuer) { tokenInput.issuer = issuer; return this; } + /** + * Sets the list of attributes for the SdJwtTokenInput being built. + * + * @param attributes a list of {@link TokenAttribute} objects to set as the token attributes + * @return the current instance of SdJwtTokenInputBuilder for method chaining + */ public SdJwtTokenInputBuilder attributes(List attributes) { tokenInput.attributes = attributes; return this; } + /** + * Sets a list of open attributes for the SdJwtTokenInput being built. + * + * @param openAttributes a list of {@link TokenAttribute} objects to define open claims + * that will be included in the token without selective disclosure + * @return the current instance of SdJwtTokenInputBuilder for method chaining + */ public SdJwtTokenInputBuilder openAttributes( List openAttributes ) { @@ -90,6 +157,13 @@ public SdJwtTokenInputBuilder openAttributes( return this; } + /** + * Sets the issuer credential for the SdJwtTokenInput being built. + * + * @param issuerCredential the {@link PkiCredential} representing the issuer's credentials to be used for + * signing and verification of the token + * @return the current instance of SdJwtTokenInputBuilder for method chaining + */ public SdJwtTokenInputBuilder issuerCredential( PkiCredential issuerCredential ) { @@ -97,6 +171,12 @@ public SdJwtTokenInputBuilder issuerCredential( return this; } + /** + * Sets the expiration duration for the SdJwtTokenInput being built. + * + * @param expirationDuration the {@link Duration} representing the time period until the token expires + * @return the current instance of {@code SdJwtTokenInputBuilder} for method chaining + */ public SdJwtTokenInputBuilder expirationDuration( Duration expirationDuration ) { @@ -104,16 +184,35 @@ public SdJwtTokenInputBuilder expirationDuration( return this; } + /** + * Sets the token signing algorithm for the SdJwtTokenInput being built. + * + * @param algorithm the {@link TokenSigningAlgorithm} to be used for signing the token + * @return the current instance of {@code SdJwtTokenInputBuilder} for method chaining + */ public SdJwtTokenInputBuilder algorithm(TokenSigningAlgorithm algorithm) { tokenInput.algorithm = algorithm; return this; } + /** + * Sets the wallet public key for the SdJwtTokenInput being built. + * + * @param walletPublicKey the {@link PublicKey} to set as the wallet's public key, + * which will be used for token confirmation. + * @return the current instance of {@code SdJwtTokenInputBuilder} for method chaining. + */ public SdJwtTokenInputBuilder walletPublicKey(PublicKey walletPublicKey) { tokenInput.walletPublicKey = walletPublicKey; return this; } + /** + * Creates and returns a new instance of {@code SdJwtTokenInput} with the properties + * configured using the builder methods of {@code SdJwtTokenInputBuilder}. + * + * @return a new and configured instance of {@code SdJwtTokenInput} + */ public SdJwtTokenInput build() { // Here you might want to validate your tokenInput object // to ensure its consistency before returning it. diff --git a/src/main/java/se/digg/wallet/datatypes/sdjwt/process/SdJwtTokenIssuer.java b/src/main/java/se/digg/wallet/datatypes/sdjwt/process/SdJwtTokenIssuer.java index ca91a2f..ddda106 100644 --- a/src/main/java/se/digg/wallet/datatypes/sdjwt/process/SdJwtTokenIssuer.java +++ b/src/main/java/se/digg/wallet/datatypes/sdjwt/process/SdJwtTokenIssuer.java @@ -21,6 +21,14 @@ import se.digg.wallet.datatypes.sdjwt.data.SdJwt; import se.swedenconnect.security.credential.PkiCredential; +/** + * A concrete implementation of the {@link TokenIssuer} interface responsible for issuing SD-JWT tokens. + *

+ * This class handles the generation of signed (SD-JWT) tokens with selective disclosure. + * The implementation ensures tokens are generated in compliance with the specified signing algorithm, + * issuer credentials, and attributes provided in the {@link SdJwtTokenInput}. It supports customizing + * legacy behavior for the SD-JWT header type. + */ @Setter public class SdJwtTokenIssuer implements TokenIssuer { diff --git a/src/main/java/se/digg/wallet/datatypes/sdjwt/process/SdJwtTokenValidationResult.java b/src/main/java/se/digg/wallet/datatypes/sdjwt/process/SdJwtTokenValidationResult.java index a01311c..14491cb 100644 --- a/src/main/java/se/digg/wallet/datatypes/sdjwt/process/SdJwtTokenValidationResult.java +++ b/src/main/java/se/digg/wallet/datatypes/sdjwt/process/SdJwtTokenValidationResult.java @@ -18,8 +18,12 @@ @Data @NoArgsConstructor public class SdJwtTokenValidationResult extends TokenValidationResult { + /** The parsed validated token */ private SdJwt vcToken; + /** The payload with disclosed data */ private Payload disclosedTokenPayload; + /** true if the token supports wallet key binding */ private boolean keyBindingProtection; + /** the audience declared in the token */ private List audience; } diff --git a/src/main/java/se/digg/wallet/datatypes/sdjwt/process/SdJwtTokenValidator.java b/src/main/java/se/digg/wallet/datatypes/sdjwt/process/SdJwtTokenValidator.java index 2ee35ab..00effd6 100644 --- a/src/main/java/se/digg/wallet/datatypes/sdjwt/process/SdJwtTokenValidator.java +++ b/src/main/java/se/digg/wallet/datatypes/sdjwt/process/SdJwtTokenValidator.java @@ -34,20 +34,51 @@ import se.digg.wallet.datatypes.sdjwt.data.Disclosure; import se.digg.wallet.datatypes.sdjwt.data.SdJwt; +/** + * The SdJwtTokenValidator class is responsible for the validation of SD-JWT tokens. It implements the + * {@code TokenValidator} interface and provides functionality to parse, verify, and validate tokens + * following the SD-JWT specification, including the reconstruction of selective disclosure claims, + * time-based validations, key binding proof verification, and certificate chain validation. + */ public class SdJwtTokenValidator implements TokenValidator { + /** Max allowed time skew */ private final Duration timeSkew; + + /** Valid SD JWT header typ values */ @Setter private List validSdJweHeaderTypes = List.of(SdJwt.SD_JWT_TYPE, SdJwt.SD_JWT_TYPE_LEGACY); + /** + * Constructs an SdJwtTokenValidator with a specified time skew. + * + * @param timeSkew The accepted time skew duration for token validation to account for clock differences. + */ public SdJwtTokenValidator(Duration timeSkew) { this.timeSkew = timeSkew; } + /** + * Default constructor for the SdJwtTokenValidator class. + * Initializes the validator with a default time skew of 30 seconds. + * The time skew is used to account for clock differences during token validation. + */ public SdJwtTokenValidator() { this.timeSkew = Duration.ofSeconds(30); } + /** + * Validates an SD-JWT token against provided trusted keys and internal rules. + * Performs signature verification, time validation, key binding validation, + * and payload restoration. + * + * @param token The SD-JWT token as a byte array. + * @param trustedKeys A list of optional trusted keys to validate the token against. May be null to trust all keys. + * @return An instance of {@code SdJwtTokenValidationResult} that contains + * the details of the validated token and any extracted data. + * @throws TokenValidationException If token validation fails due to invalid signature, expired token, + * untrusted signing certificate, or violation of key binding mechanisms. + */ @Override public SdJwtTokenValidationResult validateToken( byte[] token, @@ -122,6 +153,14 @@ public SdJwtTokenValidationResult validateToken( } } + /** + * Restores the disclosed payload of an SD-JWT token by processing its claims and disclosures. + * + * @param parsedToken The parsed SD-JWT token containing the signed claims and disclosures. + * @return A {@code Payload} object containing the restored claims after processing. + * @throws ParseException If the claims or disclosures cannot be parsed. + * @throws NoSuchAlgorithmException If the specified hash algorithm is not available. + */ private Payload restorePayload(SdJwt parsedToken) throws ParseException, NoSuchAlgorithmException { ClaimsWithDisclosure claimsWithDisclosure = @@ -134,6 +173,18 @@ private Payload restorePayload(SdJwt parsedToken) return new Payload(claims); } + /** + * Expands the claims in a map by resolving selective disclosure fields and integrating matching disclosures. + * The method processes nested claim structures and iteratively replaces selective disclosure references + * with their corresponding values from a list of provided disclosures. + * + * @param claims a map containing the original claims of the token which + * may include selective disclosure references that need to be resolved + * @param allDisclosures a list of Disclosure objects representing the available + * disclosures from which values can be extracted + * @param hashAlgo the hash algorithm used to compute and validate the selective disclosure hashes + * @throws NoSuchAlgorithmException if the specified hash algorithm is not supported + */ private void expandClaims( Map claims, List allDisclosures, @@ -205,6 +256,17 @@ private void expandClaims( claims.remove("_sd"); } + /** + * Finds and returns a matching {@code Disclosure} instance from a list of disclosures based on + * a base64 URL-encoded hash value and a specified hash algorithm. If no matching disclosure is + * found, the method returns {@code null}. + * + * @param b64UrlHash the base64 URL-encoded hash value to find a matching disclosure for + * @param allDisclosures a list of {@code Disclosure} objects to search through + * @param hashAlgo the hash algorithm used for computing and comparing the hash values + * @return the matching {@code Disclosure} instance if found, {@code null} otherwise + * @throws NoSuchAlgorithmException if the specified hash algorithm is not available + */ private Disclosure getMatchingDisclosure( String b64UrlHash, List allDisclosures, @@ -269,6 +331,15 @@ private boolean validateKeyBinding(PublicKey walletPublic, SdJwt parsedToken) return true; } + /** + * Retrieves the public key of the wallet from the provided issuer-signed claims. + * + * @param issuerSignedClaims The JWT claims set signed by the issuer containing + * the wallet public key information. + * @return The wallet's {@code PublicKey} extracted and parsed from the claims. + * @throws TokenValidationException If no wallet public key is found or if an + * error occurs during parsing of the wallet public key. + */ private PublicKey getWalletPublic(JWTClaimsSet issuerSignedClaims) throws TokenValidationException { try { @@ -287,6 +358,15 @@ private PublicKey getWalletPublic(JWTClaimsSet issuerSignedClaims) } } + /** + * Validates the time-related claims of a JWT to ensure the token is within its valid time window. + * This includes checking the issue time, not-before time, and expiration time of the token. + * + * @param jwtClaimsSet The JWT claims set containing the time-based claims such as issue time, + * not-before time, and expiration time. + * @throws TokenValidationException If the token's issue time or expiration time is missing, + * or if the current time is outside the valid time window. + */ private void timeValidation(JWTClaimsSet jwtClaimsSet) throws TokenValidationException { Instant issueTime = Optional.ofNullable( @@ -316,6 +396,19 @@ private void timeValidation(JWTClaimsSet jwtClaimsSet) } } + /** + * Retrieves the public key used to validate a token by determining whether the provided + * key in the certificate chain matches any of the trusted keys or key IDs. + * + * @param chain A list of X509 certificate chain whose public key may be used for validation. + * If the chain is empty, no public key is provided. + * @param kid The key identifier (key ID) associated with the token for which validation is being performed. + * This is used to locate a matching trusted key. + * @param trustedKeys A list of trusted keys to validate the provided key against. If null, all keys are + * considered as trusted. + * @return The public key to be used for validation if the provided key matches a trusted key or key ID. + * @throws TokenValidationException If no matching trusted key is found and trusted keys were explicitly provided. + */ private PublicKey getValidationKey( List chain, String kid, @@ -348,6 +441,16 @@ private PublicKey getValidationKey( } } + /** + * Converts a list of Base64-encoded certificates into a list of X509Certificate objects. + * Decodes each Base64 entry, parses the certificate data, and constructs the corresponding X509Certificate. + * If the input list is null, an empty list is returned. + * + * @param x5chain a list of Base64-encoded certificates. May be null or empty + * @return a list of X509Certificate objects representing the decoded and parsed certificates + * @throws IOException if an I/O error occurs during certificate stream processing + * @throws CertificateException if the certificate parsing or validation fails + */ private List getChain(List x5chain) throws IOException, CertificateException { List chain = new ArrayList<>(); From b8b16691a93c8019726d4c1b3a8636a9e9a5e9c4 Mon Sep 17 00:00:00 2001 From: Stefan Santesson Date: Fri, 14 Feb 2025 03:09:16 +0100 Subject: [PATCH 2/2] Update pom.xml --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 97e15a6..310deb0 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ SPDX-License-Identifier: EUPL-1.2 se.digg.wallet eudiw-wallet-token-lib - 0.0.6-SNAPSHOT + 0.0.7-SNAPSHOT EUDI Wallet -- Token Library Library for handling data types in the EUDI Wallet PoC project. https://github.com/diggsweden/eudiw-wallet-token-lib