GordianCoreAgreementMessageASN1.java
/*
* GordianKnot: Security Suite
* Copyright 2012-2026. Tony Washer
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package io.github.tonywasher.joceanus.gordianknot.impl.core.agree;
import io.github.tonywasher.joceanus.gordianknot.api.base.GordianException;
import io.github.tonywasher.joceanus.gordianknot.api.cert.GordianCertificate;
import io.github.tonywasher.joceanus.gordianknot.api.factory.GordianFactory;
import io.github.tonywasher.joceanus.gordianknot.impl.core.base.GordianASN1Util.GordianASN1Object;
import io.github.tonywasher.joceanus.gordianknot.impl.core.cert.GordianCertificateASN1;
import io.github.tonywasher.joceanus.gordianknot.impl.core.exc.GordianDataException;
import io.github.tonywasher.joceanus.gordianknot.impl.core.exc.GordianIOException;
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.ASN1OctetString;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.ASN1TaggedObject;
import org.bouncycastle.asn1.BEROctetString;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.DERTaggedObject;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import java.io.IOException;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.Objects;
/**
* ASN1 Encoding of Agreement Messages.
* <pre>
* GordianAgreementMessageASN1 ::= SEQUENCE {
* Id Identification
* Algs Specifications
* clientCertificate [1] GordianCertificateASN1 OPTIONAL
* serverCertificate [2] GordianCertificateASN1 OPTIONAL
* signerCertificate [3] GordianCertificateASN1 OPTIONAL
* initVector [4] OCTET STRING OPTIONAL
* encapsulated [5] OCTET STRING OPTIONAL
* ephemeral [6] SubjectPublicKeyInfo OPTIONAL
* confirmation [7] OCTET STRING OPTIONAL
* signature [8] OCTET STRING OPTIONAL
* }
*
* Identification ::= SEQUENCE {
* messageType INTEGER
* clientId [1] INTEGER OPTIONAL
* serverId [2] INTEGER OPTIONAL
* }
*
* Specification ::= SEQUENCE {
* agreeId AlgorithmIdentifier
* resultId [1] AlgorithmIdentifier OPTIONAL
* signId [2] lgorithmIdentifier OPTIONAL
* }
* </pre>
*/
public final class GordianCoreAgreementMessageASN1
extends GordianASN1Object {
/**
* The clientCertificate tag.
*/
private static final int TAG_CLIENTCERTIFICATE = 1;
/**
* The serverCertificate tag.
*/
private static final int TAG_SERVERCERTIFICATE = 2;
/**
* The signingCertificate tag.
*/
private static final int TAG_SIGNERCERTIFICATE = 3;
/**
* The initVector tag.
*/
private static final int TAG_INITVECTOR = 4;
/**
* The encapsulated tag.
*/
private static final int TAG_ENCAPSULATED = 5;
/**
* The ephemeral tag.
*/
private static final int TAG_EPHEMERAL = 6;
/**
* The confirmation tag.
*/
private static final int TAG_CONFIRMATION = 7;
/**
* The signature tag.
*/
private static final int TAG_SIGNATURE = 8;
/**
* The clientId tag.
*/
private static final int TAG_CLIENTID = 1;
/**
* The serverId tag.
*/
private static final int TAG_SERVERID = 2;
/**
* The resultAlgId tag.
*/
private static final int TAG_RESULTALGID = 1;
/**
* The signAlgId tag.
*/
private static final int TAG_SIGNALGID = 2;
/**
* The MessageType.
*/
private final GordianAgreementMessageType theMessageType;
/**
* The ClientId.
*/
private Long theClientId;
/**
* The ServerId.
*/
private Long theServerId;
/**
* The Agreement AlgorithmId.
*/
private AlgorithmIdentifier theAgreementId;
/**
* The ResultId.
*/
private AlgorithmIdentifier theResultId;
/**
* The SignatureId.
*/
private AlgorithmIdentifier theSignatureId;
/**
* The Client Certificate.
*/
private GordianCertificateASN1 theClientCertificate;
/**
* The Server Certificate.
*/
private GordianCertificateASN1 theServerCertificate;
/**
* The Signer Certificate.
*/
private GordianCertificateASN1 theSignerCertificate;
/**
* The InitVector.
*/
private byte[] theInitVector;
/**
* The Encapsulated.
*/
private byte[] theEncapsulated;
/**
* The Ephemeral.
*/
private X509EncodedKeySpec theEphemeral;
/**
* The Confirmation.
*/
private byte[] theConfirmation;
/**
* The Signature.
*/
private byte[] theSignature;
/**
* Constructor.
*
* @param pType the messageType
*/
private GordianCoreAgreementMessageASN1(final GordianAgreementMessageType pType) {
theMessageType = pType;
}
/**
* Constructor.
*
* @param pSequence the Sequence
* @throws GordianException on error
*/
private GordianCoreAgreementMessageASN1(final ASN1Sequence pSequence) throws GordianException {
/* Protect against exceptions */
try {
/* Access the sequence */
final ASN1Sequence mySequence = ASN1Sequence.getInstance(pSequence);
final Enumeration<?> en = mySequence.getObjects();
/* Access id element */
final ASN1Sequence myId = ASN1Sequence.getInstance(en.nextElement());
final Enumeration<?> enIds = myId.getObjects();
theMessageType = GordianAgreementMessageType.determineType(ASN1Integer.getInstance(enIds.nextElement()).intValueExact());
/* Loop through the optional id elements */
while (enIds.hasMoreElements()) {
final ASN1TaggedObject myTagged = ASN1TaggedObject.getInstance(enIds.nextElement());
switch (myTagged.getTagNo()) {
case TAG_CLIENTID:
theClientId = ASN1Integer.getInstance(myTagged, false).longValueExact();
break;
case TAG_SERVERID:
theServerId = ASN1Integer.getInstance(myTagged, false).longValueExact();
break;
default:
throw new GordianDataException("Unexpected id tag");
}
}
/* Access algorithms element */
final ASN1Sequence myAlgs = ASN1Sequence.getInstance(en.nextElement());
final Enumeration<?> enAlgs = myAlgs.getObjects();
theAgreementId = AlgorithmIdentifier.getInstance(enAlgs.nextElement());
/* Loop through the optional algorithm elements */
while (enAlgs.hasMoreElements()) {
final ASN1TaggedObject myTagged = ASN1TaggedObject.getInstance(enAlgs.nextElement());
switch (myTagged.getTagNo()) {
case TAG_RESULTALGID:
theResultId = AlgorithmIdentifier.getInstance(myTagged, false);
break;
case TAG_SIGNALGID:
theSignatureId = AlgorithmIdentifier.getInstance(myTagged, false);
break;
default:
throw new GordianDataException("Unexpected algorithm tag");
}
}
/* Loop through the optional elements */
while (en.hasMoreElements()) {
final ASN1TaggedObject myTagged = ASN1TaggedObject.getInstance(en.nextElement());
switch (myTagged.getTagNo()) {
case TAG_CLIENTCERTIFICATE:
theClientCertificate = GordianCertificateASN1.getInstance(myTagged, false);
break;
case TAG_SERVERCERTIFICATE:
theServerCertificate = GordianCertificateASN1.getInstance(myTagged, false);
break;
case TAG_SIGNERCERTIFICATE:
theSignerCertificate = GordianCertificateASN1.getInstance(myTagged, false);
break;
case TAG_INITVECTOR:
theInitVector = ASN1OctetString.getInstance(myTagged, false).getOctets();
break;
case TAG_ENCAPSULATED:
theEncapsulated = ASN1OctetString.getInstance(myTagged, false).getOctets();
break;
case TAG_EPHEMERAL:
theEphemeral = new X509EncodedKeySpec(SubjectPublicKeyInfo.getInstance(myTagged, false).getEncoded());
break;
case TAG_CONFIRMATION:
theConfirmation = ASN1OctetString.getInstance(myTagged, false).getOctets();
break;
case TAG_SIGNATURE:
theSignature = ASN1OctetString.getInstance(myTagged, false).getOctets();
break;
default:
throw new GordianDataException("Unexpected tag");
}
}
/* handle exceptions */
} catch (IllegalArgumentException
| IOException e) {
throw new GordianIOException("Unable to parse ASN1 sequence", e);
}
}
/**
* Create a client hello.
*
* @return the clientHello
*/
public static GordianCoreAgreementMessageASN1 newClientHello() {
return new GordianCoreAgreementMessageASN1(GordianAgreementMessageType.CLIENTHELLO);
}
/**
* Create a server hello.
*
* @return the serverHello
*/
public static GordianCoreAgreementMessageASN1 newServerHello() {
return new GordianCoreAgreementMessageASN1(GordianAgreementMessageType.SERVERHELLO);
}
/**
* Create a client confirm.
*
* @return the clientConfirm
*/
public static GordianCoreAgreementMessageASN1 newClientConfirm() {
return new GordianCoreAgreementMessageASN1(GordianAgreementMessageType.CLIENTCONFIRM);
}
/**
* Parse the ASN1 object.
*
* @param pObject the object to parse
* @return the parsed object
* @throws GordianException on error
*/
public static GordianCoreAgreementMessageASN1 getInstance(final Object pObject) throws GordianException {
if (pObject instanceof GordianCoreAgreementMessageASN1 myASN1) {
return myASN1;
} else if (pObject != null) {
return new GordianCoreAgreementMessageASN1(ASN1Sequence.getInstance(pObject));
}
throw new GordianDataException("Null sequence");
}
/**
* Get the messageType.
*
* @return the messageType
*/
GordianAgreementMessageType getMessageType() {
return theMessageType;
}
/**
* Check the message type.
*
* @param pMessageType the message type
* @throws GordianException on error
*/
public void checkMessageType(final GordianAgreementMessageType pMessageType) throws GordianException {
if (!theMessageType.equals(pMessageType)) {
throw new GordianDataException("Unexpected Message type: " + pMessageType);
}
}
/**
* Get the clientId.
*
* @return the clientId
*/
Long getClientId() {
return theClientId;
}
/**
* Set the clientId.
*
* @param pClientId the clientId
* @return this object
*/
GordianCoreAgreementMessageASN1 setClientId(final Long pClientId) {
theClientId = pClientId;
return this;
}
/**
* Get the clientId.
*
* @return the clientId
*/
Long getServerId() {
return theServerId;
}
/**
* Set the serverId.
*
* @param pServerId the serverId
* @return this object
*/
GordianCoreAgreementMessageASN1 setServerId(final Long pServerId) {
theServerId = pServerId;
return this;
}
/**
* Get the agreement algorithmId.
*
* @return the agreement algorithmId (or null)
*/
AlgorithmIdentifier getAgreementId() {
return theAgreementId;
}
/**
* Set the agreement algorithmId.
*
* @param pAgreeId the agreementId
* @return this object
*/
GordianCoreAgreementMessageASN1 setAgreementId(final AlgorithmIdentifier pAgreeId) {
theAgreementId = pAgreeId;
return this;
}
/**
* Get the result algorithmId.
*
* @return the result algorithmId (or null)
*/
AlgorithmIdentifier getResultId() {
return theResultId;
}
/**
* Set the result algorithmId.
*
* @param pResultId the resultId
* @return this object
*/
GordianCoreAgreementMessageASN1 setResultId(final AlgorithmIdentifier pResultId) {
theResultId = pResultId;
return this;
}
/**
* Get the client certificate.
*
* @param pFactory the factory
* @return the certificate (or null)
* @throws GordianException on error
*/
GordianCertificate getClientCertificate(final GordianFactory pFactory) throws GordianException {
return theClientCertificate == null ? null : theClientCertificate.getCertificate(pFactory);
}
/**
* Set the client certificate.
*
* @param pCertificate the certificate
* @return this object
* @throws GordianException on error
*/
GordianCoreAgreementMessageASN1 setClientCertificate(final GordianCertificate pCertificate) throws GordianException {
theClientCertificate = pCertificate == null ? null : new GordianCertificateASN1(pCertificate);
return this;
}
/**
* Get the server certificate.
*
* @param pFactory the factory
* @return the certificate (or null)
* @throws GordianException on error
*/
GordianCertificate getServerCertificate(final GordianFactory pFactory) throws GordianException {
return theServerCertificate == null ? null : theServerCertificate.getCertificate(pFactory);
}
/**
* Set the server certificate.
*
* @param pCertificate the certificate
* @return this object
* @throws GordianException on error
*/
GordianCoreAgreementMessageASN1 setServerCertificate(final GordianCertificate pCertificate) throws GordianException {
theServerCertificate = pCertificate == null ? null : new GordianCertificateASN1(pCertificate);
return this;
}
/**
* Get the signer certificate.
*
* @param pFactory the factory
* @return the certificate (or null)
* @throws GordianException on error
*/
GordianCertificate getSignerCertificate(final GordianFactory pFactory) throws GordianException {
return theSignerCertificate == null ? null : theSignerCertificate.getCertificate(pFactory);
}
/**
* Set the signer certificate.
*
* @param pCertificate the certificate
* @return this object
* @throws GordianException on error
*/
GordianCoreAgreementMessageASN1 setSignerCertificate(final GordianCertificate pCertificate) throws GordianException {
theSignerCertificate = pCertificate == null ? null : new GordianCertificateASN1(pCertificate);
return this;
}
/**
* Get the initVector.
*
* @return the initVector (or null)
*/
byte[] getInitVector() {
return theInitVector;
}
/**
* Set the initVector.
*
* @param pInitVector the initVector Tag
* @return this object
*/
GordianCoreAgreementMessageASN1 setInitVector(final byte[] pInitVector) {
theInitVector = pInitVector;
return this;
}
/**
* Get the encapsulated.
*
* @return the encapsulated (or null)
*/
public byte[] getEncapsulated() {
return theEncapsulated;
}
/**
* Set the encapsulated.
*
* @param pEncapsulated the encapsulated
* @return this object
*/
GordianCoreAgreementMessageASN1 setEncapsulated(final byte[] pEncapsulated) {
theEncapsulated = pEncapsulated;
return this;
}
/**
* Get the ephemeral.
*
* @return the ephemeral (or null)
*/
public X509EncodedKeySpec getEphemeral() {
return theEphemeral;
}
/**
* Set the ephemeral.
*
* @param pEphemeral the ephemeral
* @return this object
*/
GordianCoreAgreementMessageASN1 setEphemeral(final X509EncodedKeySpec pEphemeral) {
theEphemeral = pEphemeral;
return this;
}
/**
* Get the confirmation.
*
* @return the confirmation Tag (or null)
*/
byte[] getConfirmation() {
return theConfirmation;
}
/**
* Set the confirmation.
*
* @param pConfirmation the confirmation Tag
* @return this object
*/
GordianCoreAgreementMessageASN1 setConfirmation(final byte[] pConfirmation) {
theConfirmation = pConfirmation;
return this;
}
/**
* Get the signature algorithmId.
*
* @return the signature algorithmId (or null)
*/
AlgorithmIdentifier getSignatureId() {
return theSignatureId;
}
/**
* Get the signature.
*
* @return the signature (or null)
*/
byte[] getSignature() {
return theSignature;
}
/**
* Set the signature and algorithmId.
*
* @param pSignatureId the signature algorithmId
* @param pSignature the signature
* @return this object
*/
GordianCoreAgreementMessageASN1 setSignature(final AlgorithmIdentifier pSignatureId,
final byte[] pSignature) {
theSignatureId = pSignatureId;
theSignature = pSignature;
return this;
}
@Override
public ASN1Primitive toASN1Primitive() {
/* Create the vector */
final ASN1EncodableVector v = new ASN1EncodableVector();
/* Add id section */
final ASN1EncodableVector vId = new ASN1EncodableVector();
vId.add(new ASN1Integer(theMessageType.getId()));
if (theClientId != null) {
vId.add(new DERTaggedObject(false, TAG_CLIENTID, new ASN1Integer(theClientId)));
}
if (theServerId != null) {
vId.add(new DERTaggedObject(false, TAG_SERVERID, new ASN1Integer(theServerId)));
}
v.add(new DERSequence(vId));
/* Add algorithm section */
final ASN1EncodableVector vAlg = new ASN1EncodableVector();
vAlg.add(theAgreementId);
if (theResultId != null) {
vAlg.add(new DERTaggedObject(false, TAG_RESULTALGID, theResultId));
}
if (theSignatureId != null) {
vAlg.add(new DERTaggedObject(false, TAG_SIGNALGID, theSignatureId));
}
v.add(new DERSequence(vAlg));
/* Add optional components */
if (theClientCertificate != null) {
v.add(new DERTaggedObject(false, TAG_CLIENTCERTIFICATE, theClientCertificate));
}
if (theServerCertificate != null) {
v.add(new DERTaggedObject(false, TAG_SERVERCERTIFICATE, theServerCertificate));
}
if (theSignerCertificate != null) {
v.add(new DERTaggedObject(false, TAG_SIGNERCERTIFICATE, theSignerCertificate));
}
if (theInitVector != null) {
v.add(new DERTaggedObject(false, TAG_INITVECTOR, new BEROctetString(theInitVector)));
}
if (theEncapsulated != null) {
v.add(new DERTaggedObject(false, TAG_ENCAPSULATED, new BEROctetString(theEncapsulated)));
}
if (theEphemeral != null) {
v.add(new DERTaggedObject(false, TAG_EPHEMERAL, SubjectPublicKeyInfo.getInstance(theEphemeral.getEncoded())));
}
if (theConfirmation != null) {
v.add(new DERTaggedObject(false, TAG_CONFIRMATION, new BEROctetString(theConfirmation)));
}
if (theSignature != null) {
v.add(new DERTaggedObject(false, TAG_SIGNATURE, new BEROctetString(theSignature)));
}
return new DERSequence(v);
}
@Override
public boolean equals(final Object pThat) {
/* Handle trivial cases */
if (this == pThat) {
return true;
}
if (pThat == null) {
return false;
}
/* Check that the fields are equal */
return pThat instanceof GordianCoreAgreementMessageASN1 myThat
&& Objects.equals(getMessageType(), myThat.getMessageType())
&& Objects.equals(getClientId(), myThat.getClientId())
&& Objects.equals(getServerId(), myThat.getServerId())
&& Objects.equals(getAgreementId(), myThat.getAgreementId())
&& Objects.equals(getResultId(), myThat.getResultId())
&& Objects.equals(getSignatureId(), myThat.getSignatureId())
&& Objects.equals(getEphemeral(), myThat.getEphemeral())
&& Objects.equals(theClientCertificate, myThat.theClientCertificate)
&& Objects.equals(theServerCertificate, myThat.theServerCertificate)
&& Objects.equals(theSignerCertificate, myThat.theSignerCertificate)
&& Arrays.equals(getSignature(), myThat.getSignature())
&& Arrays.equals(getEncapsulated(), myThat.getEncapsulated())
&& Arrays.equals(getConfirmation(), myThat.getConfirmation())
&& Arrays.equals(getInitVector(), myThat.getInitVector());
}
@Override
public int hashCode() {
return Objects.hash(getMessageType(), getClientId(), getServerId(),
getAgreementId(), getResultId(), getSignatureId(), getEphemeral(),
theClientCertificate, theServerCertificate, theSignerCertificate)
^ Arrays.hashCode(getSignature())
^ Arrays.hashCode(getEncapsulated())
^ Arrays.hashCode(getConfirmation())
^ Arrays.hashCode(getInitVector());
}
/**
* The messageType.
*/
public enum GordianAgreementMessageType {
/**
* ClientHello.
*/
CLIENTHELLO(1),
/**
* ServerHello.
*/
SERVERHELLO(2),
/**
* ClientConfirm.
*/
CLIENTCONFIRM(3);
/**
* The messageId.
*/
private final int theId;
/**
* Constructor.
*
* @param pId the id
*/
GordianAgreementMessageType(final int pId) {
theId = pId;
}
/**
* Obtain id for messageType.
*
* @return the id
*/
int getId() {
return theId;
}
/**
* Determine the MessageType from the id.
*
* @param pId the id
* @return the messageType
* @throws GordianException on error
*/
private static GordianAgreementMessageType determineType(final int pId) throws GordianException {
for (GordianAgreementMessageType myType : values()) {
if (pId == myType.getId()) {
return myType;
}
}
throw new GordianDataException("Unexpected messageType: " + pId);
}
}
}