From 215f7bad529b793fc0369fec0dad541d1f93ca7e Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Wed, 5 Jun 2024 18:33:27 +0700 Subject: Refactoring in Asn1.Cmp --- crypto/src/asn1/cmp/CAKeyUpdAnnContent.cs | 8 +- crypto/src/asn1/cmp/CertOrEncCert.cs | 3 +- crypto/src/asn1/cmp/CertRepMessage.cs | 13 +- crypto/src/asn1/cmp/CertReqTemplateContent.cs | 25 ++-- crypto/src/asn1/cmp/CertResponse.cs | 36 ++--- crypto/src/asn1/cmp/CertStatus.cs | 36 ++--- crypto/src/asn1/cmp/CertifiedKeyPair.cs | 66 ++++----- crypto/src/asn1/cmp/Challenge.cs | 17 +-- crypto/src/asn1/cmp/CrlStatus.cs | 24 ++-- crypto/src/asn1/cmp/DhbmParameter.cs | 15 +- crypto/src/asn1/cmp/ErrorMsgContent.cs | 22 ++- crypto/src/asn1/cmp/InfoTypeAndValue.cs | 13 +- crypto/src/asn1/cmp/KemBMParameter.cs | 11 +- crypto/src/asn1/cmp/KemCiphertextInfo.cs | 9 +- crypto/src/asn1/cmp/KemOtherInfo.cs | 50 ++----- crypto/src/asn1/cmp/KeyRecRepContent.cs | 30 ++-- crypto/src/asn1/cmp/OobCertHash.cs | 34 ++--- crypto/src/asn1/cmp/PKIFreeText.cs | 15 ++ crypto/src/asn1/cmp/PKIHeader.cs | 189 +++++++++----------------- crypto/src/asn1/cmp/PKIHeaderBuilder.cs | 32 ++--- crypto/src/asn1/cmp/PKIMessage.cs | 32 ++--- crypto/src/asn1/cmp/PKIStatusInfo.cs | 45 +++--- crypto/src/asn1/cmp/PbmParameter.cs | 16 ++- crypto/src/asn1/cmp/ProtectedPart.cs | 12 +- crypto/src/asn1/cmp/RevAnnContent.cs | 29 ++-- crypto/src/asn1/cmp/RevDetails.cs | 24 ++-- crypto/src/asn1/cmp/RevRepContent.cs | 25 ++-- crypto/src/asn1/cmp/RootCaKeyUpdateContent.cs | 30 ++-- crypto/src/asn1/cms/Time.cs | 19 +++ crypto/src/asn1/crmf/Controls.cs | 3 + crypto/src/asn1/crmf/PKIArchiveOptions.cs | 3 + crypto/src/asn1/crmf/PopoPrivKey.cs | 3 + crypto/src/asn1/crmf/ProofOfPossession.cs | 3 + crypto/src/asn1/x509/AlgorithmIdentifier.cs | 15 ++ crypto/src/asn1/x509/Time.cs | 19 +++ crypto/src/asn1/x509/X509Extensions.cs | 15 ++ 36 files changed, 459 insertions(+), 482 deletions(-) diff --git a/crypto/src/asn1/cmp/CAKeyUpdAnnContent.cs b/crypto/src/asn1/cmp/CAKeyUpdAnnContent.cs index 4460c8265..88c3b6e40 100644 --- a/crypto/src/asn1/cmp/CAKeyUpdAnnContent.cs +++ b/crypto/src/asn1/cmp/CAKeyUpdAnnContent.cs @@ -1,3 +1,5 @@ +using System; + namespace Org.BouncyCastle.Asn1.Cmp { public class CAKeyUpdAnnContent @@ -23,7 +25,11 @@ namespace Org.BouncyCastle.Asn1.Cmp private CAKeyUpdAnnContent(Asn1Sequence seq) { - m_oldWithNew = CmpCertificate.GetInstance(seq[0]); + int count = seq.Count; + if (count != 3) + throw new ArgumentException("Bad sequence size: " + count, nameof(seq)); + + m_oldWithNew = CmpCertificate.GetInstance(seq[0]); m_newWithOld = CmpCertificate.GetInstance(seq[1]); m_newWithNew = CmpCertificate.GetInstance(seq[2]); } diff --git a/crypto/src/asn1/cmp/CertOrEncCert.cs b/crypto/src/asn1/cmp/CertOrEncCert.cs index 94df00006..430c1fd4f 100644 --- a/crypto/src/asn1/cmp/CertOrEncCert.cs +++ b/crypto/src/asn1/cmp/CertOrEncCert.cs @@ -46,7 +46,8 @@ namespace Org.BouncyCastle.Asn1.Cmp m_certificate = certificate ?? throw new ArgumentNullException(nameof(certificate)); } - public CertOrEncCert(EncryptedValue encryptedValue) + [Obsolete("Use constructor with EncryptedKey instead")] + public CertOrEncCert(EncryptedValue encryptedValue) { m_encryptedCert = new EncryptedKey( encryptedValue ?? throw new ArgumentNullException(nameof(encryptedValue))); diff --git a/crypto/src/asn1/cmp/CertRepMessage.cs b/crypto/src/asn1/cmp/CertRepMessage.cs index 9c260eadb..9e7fae2e2 100644 --- a/crypto/src/asn1/cmp/CertRepMessage.cs +++ b/crypto/src/asn1/cmp/CertRepMessage.cs @@ -24,14 +24,15 @@ namespace Org.BouncyCastle.Asn1.Cmp private CertRepMessage(Asn1Sequence seq) { - int index = 0; + int count = seq.Count, pos = 0; + if (count < 1 || count > 2) + throw new ArgumentException("Bad sequence size: " + count, nameof(seq)); - if (seq.Count > 1) - { - m_caPubs = Asn1Sequence.GetInstance((Asn1TaggedObject)seq[index++], true); - } + m_caPubs = Asn1Utilities.ReadOptionalContextTagged(seq, ref pos, 1, true, Asn1Sequence.GetInstance); + m_response = Asn1Sequence.GetInstance(seq[pos++]); - m_response = Asn1Sequence.GetInstance(seq[index]); + if (pos != count) + throw new ArgumentException("Unexpected elements in sequence", nameof(seq)); } public CertRepMessage(CmpCertificate[] caPubs, CertResponse[] response) diff --git a/crypto/src/asn1/cmp/CertReqTemplateContent.cs b/crypto/src/asn1/cmp/CertReqTemplateContent.cs index ae35bc5e8..118f23a4c 100644 --- a/crypto/src/asn1/cmp/CertReqTemplateContent.cs +++ b/crypto/src/asn1/cmp/CertReqTemplateContent.cs @@ -35,30 +35,33 @@ namespace Org.BouncyCastle.Asn1.Cmp } private readonly CertTemplate m_certTemplate; - private readonly Asn1Sequence m_keySpec; + private readonly Controls m_keySpec; private CertReqTemplateContent(Asn1Sequence seq) { - if (seq.Count != 1 && seq.Count != 2) - throw new ArgumentException("expected sequence size of 1 or 2"); + int count = seq.Count, pos = 0; + if (count < 1 || count > 2) + throw new ArgumentException("Bad sequence size: " + count, nameof(seq)); - m_certTemplate = CertTemplate.GetInstance(seq[0]); + m_certTemplate = CertTemplate.GetInstance(seq[pos++]); + m_keySpec = Asn1Utilities.ReadOptional(seq, ref pos, Controls.GetOptional); - if (seq.Count > 1) - { - m_keySpec = Asn1Sequence.GetInstance(seq[1]); - } + if (pos != count) + throw new ArgumentException("Unexpected elements in sequence", nameof(seq)); } public CertReqTemplateContent(CertTemplate certTemplate, Asn1Sequence keySpec) { - m_certTemplate = certTemplate; - m_keySpec = keySpec; + m_certTemplate = certTemplate ?? throw new ArgumentNullException(nameof(certTemplate)); + m_keySpec = Controls.GetInstance(keySpec); } public virtual CertTemplate CertTemplate => m_certTemplate; - public virtual Asn1Sequence KeySpec => m_keySpec; + [Obsolete("Use 'KeySpecControls' property instead")] + public virtual Asn1Sequence KeySpec => Asn1Sequence.GetInstance(m_keySpec?.ToAsn1Object()); + + public virtual Controls KeySpecControls => m_keySpec; public override Asn1Object ToAsn1Object() { diff --git a/crypto/src/asn1/cmp/CertResponse.cs b/crypto/src/asn1/cmp/CertResponse.cs index ce3355270..0bebb3a01 100644 --- a/crypto/src/asn1/cmp/CertResponse.cs +++ b/crypto/src/asn1/cmp/CertResponse.cs @@ -26,32 +26,20 @@ namespace Org.BouncyCastle.Asn1.Cmp private CertResponse(Asn1Sequence seq) { - m_certReqId = DerInteger.GetInstance(seq[0]); - m_status = PkiStatusInfo.GetInstance(seq[1]); + int count = seq.Count, pos = 0; + if (count < 2 || count > 4) + throw new ArgumentException("Bad sequence size: " + count, nameof(seq)); - if (seq.Count >= 3) - { - if (seq.Count == 3) - { - Asn1Encodable o = seq[2]; - if (o is Asn1OctetString octetString) - { - m_rspInfo = octetString; - } - else - { - m_certifiedKeyPair = CertifiedKeyPair.GetInstance(o); - } - } - else - { - m_certifiedKeyPair = CertifiedKeyPair.GetInstance(seq[2]); - m_rspInfo = Asn1OctetString.GetInstance(seq[3]); - } - } - } + m_certReqId = DerInteger.GetInstance(seq[pos++]); + m_status = PkiStatusInfo.GetInstance(seq[pos++]); + m_certifiedKeyPair = Asn1Utilities.ReadOptional(seq, ref pos, CertifiedKeyPair.GetOptional); + m_rspInfo = Asn1Utilities.ReadOptional(seq, ref pos, Asn1OctetString.GetOptional); + + if (pos != count) + throw new ArgumentException("Unexpected elements in sequence", nameof(seq)); + } - public CertResponse(DerInteger certReqId, PkiStatusInfo status) + public CertResponse(DerInteger certReqId, PkiStatusInfo status) : this(certReqId, status, null, null) { } diff --git a/crypto/src/asn1/cmp/CertStatus.cs b/crypto/src/asn1/cmp/CertStatus.cs index 488f78376..5002b8811 100644 --- a/crypto/src/asn1/cmp/CertStatus.cs +++ b/crypto/src/asn1/cmp/CertStatus.cs @@ -29,31 +29,21 @@ namespace Org.BouncyCastle.Asn1.Cmp private CertStatus(Asn1Sequence seq) { - m_certHash = Asn1OctetString.GetInstance(seq[0]); - m_certReqID = DerInteger.GetInstance(seq[1]); - - if (seq.Count > 2) - { - for (int t = 2; t < seq.Count; t++) - { - Asn1Object p = seq[t].ToAsn1Object(); - if (p is Asn1Sequence s) - { - m_statusInfo = PkiStatusInfo.GetInstance(s); - } - if (p is Asn1TaggedObject dto) - { - if (!dto.HasContextTag(0)) - throw new ArgumentException("unknown tag: " + Asn1Utilities.GetTagText(dto)); - - m_hashAlg = AlgorithmIdentifier.GetInstance(dto, true); - } - } - } - } + int count = seq.Count, pos = 0; + if (count < 2 || count > 4) + throw new ArgumentException("Bad sequence size: " + count, nameof(seq)); + + m_certHash = Asn1OctetString.GetInstance(seq[pos++]); + m_certReqID = DerInteger.GetInstance(seq[pos++]); + m_statusInfo = Asn1Utilities.ReadOptional(seq, ref pos, PkiStatusInfo.GetOptional); + m_hashAlg = Asn1Utilities.ReadOptionalContextTagged(seq, ref pos, 0, true, AlgorithmIdentifier.GetInstance); + + if (pos != count) + throw new ArgumentException("Unexpected elements in sequence", nameof(seq)); + } public CertStatus(byte[] certHash, BigInteger certReqID) - : this(certHash, new DerInteger(certReqID)) + : this(certHash, certReqID, null, null) { } diff --git a/crypto/src/asn1/cmp/CertifiedKeyPair.cs b/crypto/src/asn1/cmp/CertifiedKeyPair.cs index 9c7fd8175..61da1d37a 100644 --- a/crypto/src/asn1/cmp/CertifiedKeyPair.cs +++ b/crypto/src/asn1/cmp/CertifiedKeyPair.cs @@ -21,36 +21,37 @@ namespace Org.BouncyCastle.Asn1.Cmp return new CertifiedKeyPair(Asn1Sequence.GetInstance(taggedObject, declaredExplicit)); } + public static CertifiedKeyPair GetOptional(Asn1Encodable element) + { + if (element == null) + throw new ArgumentNullException(nameof(element)); + + if (element is CertifiedKeyPair certifiedKeyPair) + return certifiedKeyPair; + + Asn1Sequence asn1Sequence = Asn1Sequence.GetOptional(element); + if (asn1Sequence != null) + return new CertifiedKeyPair(asn1Sequence); + + return null; + } + private readonly CertOrEncCert m_certOrEncCert; private readonly EncryptedKey m_privateKey; private readonly PkiPublicationInfo m_publicationInfo; private CertifiedKeyPair(Asn1Sequence seq) { - m_certOrEncCert = CertOrEncCert.GetInstance(seq[0]); - - if (seq.Count >= 2) - { - if (seq.Count == 2) - { - Asn1TaggedObject tagged = Asn1TaggedObject.GetInstance(seq[1], Asn1Tags.ContextSpecific); - if (tagged.TagNo == 0) - { - m_privateKey = EncryptedKey.GetInstance(tagged.GetExplicitBaseObject()); - } - else - { - m_publicationInfo = PkiPublicationInfo.GetInstance(tagged.GetExplicitBaseObject()); - } - } - else - { - m_privateKey = EncryptedKey.GetInstance( - Asn1TaggedObject.GetInstance(seq[1], Asn1Tags.ContextSpecific).GetExplicitBaseObject()); - m_publicationInfo = PkiPublicationInfo.GetInstance( - Asn1TaggedObject.GetInstance(seq[2], Asn1Tags.ContextSpecific).GetExplicitBaseObject()); - } - } + int count = seq.Count, pos = 0; + if (count < 1 || count > 3) + throw new ArgumentException("Bad sequence size: " + count, nameof(seq)); + + m_certOrEncCert = CertOrEncCert.GetInstance(seq[pos++]); + m_privateKey = Asn1Utilities.ReadOptionalContextTagged(seq, ref pos, 0, true, EncryptedKey.GetInstance); + m_publicationInfo = Asn1Utilities.ReadOptionalContextTagged(seq, ref pos, 1, true, PkiPublicationInfo.GetInstance); + + if (pos != count) + throw new ArgumentException("Unexpected elements in sequence", nameof(seq)); } public CertifiedKeyPair(CertOrEncCert certOrEncCert) @@ -58,6 +59,7 @@ namespace Org.BouncyCastle.Asn1.Cmp { } + [Obsolete("Use constructor with EncryptedKey instead")] public CertifiedKeyPair(CertOrEncCert certOrEncCert, EncryptedValue privateKey, PkiPublicationInfo publicationInfo) : this(certOrEncCert, privateKey == null ? null : new EncryptedKey(privateKey), publicationInfo) @@ -67,10 +69,7 @@ namespace Org.BouncyCastle.Asn1.Cmp public CertifiedKeyPair(CertOrEncCert certOrEncCert, EncryptedKey privateKey, PkiPublicationInfo publicationInfo) { - if (certOrEncCert == null) - throw new ArgumentNullException(nameof(certOrEncCert)); - - m_certOrEncCert = certOrEncCert; + m_certOrEncCert = certOrEncCert ?? throw new ArgumentNullException(nameof(certOrEncCert)); m_privateKey = privateKey; m_publicationInfo = publicationInfo; } @@ -82,13 +81,14 @@ namespace Org.BouncyCastle.Asn1.Cmp public virtual PkiPublicationInfo PublicationInfo => m_publicationInfo; /** + * RFC 9480 *
 		 * CertifiedKeyPair ::= SEQUENCE {
-		 *                                  certOrEncCert       CertOrEncCert,
-		 *                                  privateKey      [0] EncryptedValue      OPTIONAL,
-		 *                                  -- see [CRMF] for comment on encoding
-		 *                                  publicationInfo [1] PKIPublicationInfo  OPTIONAL
-		 *       }
+         *     certOrEncCert       CertOrEncCert,
+         *     privateKey      [0] EncryptedKey        OPTIONAL,
+         *     -- See [RFC4211] for comments on encoding.
+         *     publicationInfo [1] PKIPublicationInfo  OPTIONAL
+         * }
 		 * 
* @return a basic ASN.1 object representation. */ diff --git a/crypto/src/asn1/cmp/Challenge.cs b/crypto/src/asn1/cmp/Challenge.cs index 4e30dcb55..0bc1992b0 100644 --- a/crypto/src/asn1/cmp/Challenge.cs +++ b/crypto/src/asn1/cmp/Challenge.cs @@ -53,16 +53,17 @@ namespace Org.BouncyCastle.Asn1.Cmp private Challenge(Asn1Sequence seq) { - int index = 0; + int count = seq.Count, pos = 0; + if (count < 2 || count > 3) + throw new ArgumentException("Bad sequence size: " + count, nameof(seq)); - if (seq.Count == 3) - { - m_owf = AlgorithmIdentifier.GetInstance(seq[index++]); - } + m_owf = Asn1Utilities.ReadOptional(seq, ref pos, AlgorithmIdentifier.GetOptional); + m_witness = Asn1OctetString.GetInstance(seq[pos++]); + m_challenge = Asn1OctetString.GetInstance(seq[pos++]); - m_witness = Asn1OctetString.GetInstance(seq[index++]); - m_challenge = Asn1OctetString.GetInstance(seq[index]); - } + if (pos != count) + throw new ArgumentException("Unexpected elements in sequence", nameof(seq)); + } public Challenge(byte[] witness, byte[] challenge) : this(null, witness, challenge) diff --git a/crypto/src/asn1/cmp/CrlStatus.cs b/crypto/src/asn1/cmp/CrlStatus.cs index f5cf091f4..87760b9a2 100644 --- a/crypto/src/asn1/cmp/CrlStatus.cs +++ b/crypto/src/asn1/cmp/CrlStatus.cs @@ -29,23 +29,22 @@ namespace Org.BouncyCastle.Asn1.Cmp private readonly CrlSource m_source; private readonly Time m_thisUpdate; - private CrlStatus(Asn1Sequence sequence) + private CrlStatus(Asn1Sequence seq) { - int count = sequence.Count; + int count = seq.Count, pos = 0; if (count < 1 || count > 2) - throw new ArgumentException("expected sequence size of 1 or 2, got " + count); + throw new ArgumentException("Bad sequence size: " + count, nameof(seq)); - m_source = CrlSource.GetInstance(sequence[0]); + m_source = CrlSource.GetInstance(seq[pos++]); + m_thisUpdate = Asn1Utilities.ReadOptional(seq, ref pos, Time.GetOptional); - if (sequence.Count == 2) - { - m_thisUpdate = Time.GetInstance(sequence[1]); - } + if (pos != count) + throw new ArgumentException("Unexpected elements in sequence", nameof(seq)); } public CrlStatus(CrlSource source, Time thisUpdate) { - m_source = source; + m_source = source ?? throw new ArgumentNullException(nameof(source)); m_thisUpdate = thisUpdate; } @@ -55,10 +54,9 @@ namespace Org.BouncyCastle.Asn1.Cmp public override Asn1Object ToAsn1Object() { - if (m_thisUpdate == null) - return new DerSequence(m_source); - - return new DerSequence(m_source, m_thisUpdate); + return m_thisUpdate == null + ? new DerSequence(m_source) + : new DerSequence(m_source, m_thisUpdate); } } } diff --git a/crypto/src/asn1/cmp/DhbmParameter.cs b/crypto/src/asn1/cmp/DhbmParameter.cs index 1b44b732e..71773f535 100644 --- a/crypto/src/asn1/cmp/DhbmParameter.cs +++ b/crypto/src/asn1/cmp/DhbmParameter.cs @@ -32,19 +32,20 @@ namespace Org.BouncyCastle.Asn1.Cmp private readonly AlgorithmIdentifier m_owf; private readonly AlgorithmIdentifier m_mac; - private DhbmParameter(Asn1Sequence sequence) + private DhbmParameter(Asn1Sequence seq) { - if (sequence.Count != 2) - throw new ArgumentException("expecting sequence size of 2"); + int count = seq.Count; + if (count != 2) + throw new ArgumentException("Bad sequence size: " + count, nameof(seq)); - m_owf = AlgorithmIdentifier.GetInstance(sequence[0]); - m_mac = AlgorithmIdentifier.GetInstance(sequence[1]); + m_owf = AlgorithmIdentifier.GetInstance(seq[0]); + m_mac = AlgorithmIdentifier.GetInstance(seq[1]); } public DhbmParameter(AlgorithmIdentifier owf, AlgorithmIdentifier mac) { - m_owf = owf; - m_mac = mac; + m_owf = owf ?? throw new ArgumentNullException(nameof(owf)); + m_mac = mac ?? throw new ArgumentNullException(nameof(mac)); } public virtual AlgorithmIdentifier Owf => m_owf; diff --git a/crypto/src/asn1/cmp/ErrorMsgContent.cs b/crypto/src/asn1/cmp/ErrorMsgContent.cs index 44646313e..1af52e65c 100644 --- a/crypto/src/asn1/cmp/ErrorMsgContent.cs +++ b/crypto/src/asn1/cmp/ErrorMsgContent.cs @@ -36,20 +36,16 @@ namespace Org.BouncyCastle.Asn1.Cmp private ErrorMsgContent(Asn1Sequence seq) { - m_pkiStatusInfo = PkiStatusInfo.GetInstance(seq[0]); + int count = seq.Count, pos = 0; + if (count < 1 || count > 3) + throw new ArgumentException("Bad sequence size: " + count, nameof(seq)); - for (int pos = 1; pos < seq.Count; ++pos) - { - Asn1Encodable ae = seq[pos]; - if (ae is DerInteger) - { - m_errorCode = DerInteger.GetInstance(ae); - } - else - { - m_errorDetails = PkiFreeText.GetInstance(ae); - } - } + m_pkiStatusInfo = PkiStatusInfo.GetInstance(seq[pos++]); + m_errorCode = Asn1Utilities.ReadOptional(seq, ref pos, DerInteger.GetOptional); + m_errorDetails = Asn1Utilities.ReadOptional(seq, ref pos, PkiFreeText.GetOptional); + + if (pos != count) + throw new ArgumentException("Unexpected elements in sequence", nameof(seq)); } public ErrorMsgContent(PkiStatusInfo pkiStatusInfo) diff --git a/crypto/src/asn1/cmp/InfoTypeAndValue.cs b/crypto/src/asn1/cmp/InfoTypeAndValue.cs index 03e055f10..fbe1293bc 100644 --- a/crypto/src/asn1/cmp/InfoTypeAndValue.cs +++ b/crypto/src/asn1/cmp/InfoTypeAndValue.cs @@ -69,9 +69,13 @@ namespace Org.BouncyCastle.Asn1.Cmp private InfoTypeAndValue(Asn1Sequence seq) { + int count = seq.Count; + if (count < 1 || count > 2) + throw new ArgumentException("Bad sequence size: " + count, nameof(seq)); + m_infoType = DerObjectIdentifier.GetInstance(seq[0]); - if (seq.Count > 1) + if (count > 1) { m_infoValue = seq[1]; } @@ -103,10 +107,9 @@ namespace Org.BouncyCastle.Asn1.Cmp */ public override Asn1Object ToAsn1Object() { - if (m_infoValue == null) - return new DerSequence(m_infoType); - - return new DerSequence(m_infoType, m_infoValue); + return m_infoValue == null + ? new DerSequence(m_infoType) + : new DerSequence(m_infoType, m_infoValue); } } } diff --git a/crypto/src/asn1/cmp/KemBMParameter.cs b/crypto/src/asn1/cmp/KemBMParameter.cs index 846233054..a49dc4181 100644 --- a/crypto/src/asn1/cmp/KemBMParameter.cs +++ b/crypto/src/asn1/cmp/KemBMParameter.cs @@ -34,8 +34,9 @@ namespace Org.BouncyCastle.Asn1.Cmp private KemBMParameter(Asn1Sequence seq) { - if (seq.Count != 3) - throw new ArgumentException("sequence size should 3", nameof(seq)); + int count = seq.Count; + if (count != 3) + throw new ArgumentException("Bad sequence size: " + count, nameof(seq)); m_kdf = AlgorithmIdentifier.GetInstance(seq[0]); m_len = DerInteger.GetInstance(seq[1]); @@ -44,9 +45,9 @@ namespace Org.BouncyCastle.Asn1.Cmp public KemBMParameter(AlgorithmIdentifier kdf, DerInteger len, AlgorithmIdentifier mac) { - m_kdf = kdf; - m_len = len; - m_mac = mac; + m_kdf = kdf ?? throw new ArgumentNullException(nameof(kdf)); + m_len = len ?? throw new ArgumentNullException(nameof(len)); + m_mac = mac ?? throw new ArgumentNullException(nameof(mac)); } public KemBMParameter(AlgorithmIdentifier kdf, long len, AlgorithmIdentifier mac) diff --git a/crypto/src/asn1/cmp/KemCiphertextInfo.cs b/crypto/src/asn1/cmp/KemCiphertextInfo.cs index 7a6c3b25e..0fa68aa59 100644 --- a/crypto/src/asn1/cmp/KemCiphertextInfo.cs +++ b/crypto/src/asn1/cmp/KemCiphertextInfo.cs @@ -32,8 +32,9 @@ namespace Org.BouncyCastle.Asn1.Cmp private KemCiphertextInfo(Asn1Sequence seq) { - if (seq.Count != 2) - throw new ArgumentException("sequence size should 2", nameof(seq)); + int count = seq.Count; + if (count != 2) + throw new ArgumentException("Bad sequence size: " + count, nameof(seq)); m_kem = AlgorithmIdentifier.GetInstance(seq[0]); m_ct = Asn1OctetString.GetInstance(seq[1]); @@ -41,8 +42,8 @@ namespace Org.BouncyCastle.Asn1.Cmp public KemCiphertextInfo(AlgorithmIdentifier kem, Asn1OctetString ct) { - m_kem = kem; - m_ct = ct; + m_kem = kem ?? throw new ArgumentNullException(nameof(kem)); + m_ct = ct ?? throw new ArgumentNullException(nameof(ct)); } public virtual AlgorithmIdentifier Kem => m_kem; diff --git a/crypto/src/asn1/cmp/KemOtherInfo.cs b/crypto/src/asn1/cmp/KemOtherInfo.cs index 3185495fc..7b46dd398 100644 --- a/crypto/src/asn1/cmp/KemOtherInfo.cs +++ b/crypto/src/asn1/cmp/KemOtherInfo.cs @@ -62,47 +62,23 @@ namespace Org.BouncyCastle.Asn1.Cmp private KemOtherInfo(Asn1Sequence seq) { - if (seq.Count < 4 || seq.Count > 7) - throw new ArgumentException("sequence size should be between 4 and 7 inclusive", nameof(seq)); + int count = seq.Count, pos = 0; + if (count < 4 || count > 7) + throw new ArgumentException("Bad sequence size: " + count, nameof(seq)); - int seqPos = 0; - - m_staticString = PkiFreeText.GetInstance(seq[seqPos]); + m_staticString = PkiFreeText.GetInstance(seq[pos++]); if (!DEFAULT_staticString.Equals(m_staticString)) throw new ArgumentException("staticString field should be " + DEFAULT_staticString); - Asn1TaggedObject tagged = seq[++seqPos] as Asn1TaggedObject; - - if (tagged != null && - Asn1Utilities.TryGetContextBaseUniversal(tagged, 0, true, Asn1Tags.OctetString, out var transactionID)) - { - m_transactionID = (Asn1OctetString)transactionID; - tagged = seq[++seqPos] as Asn1TaggedObject; - } - - if (tagged != null && - Asn1Utilities.TryGetContextBaseUniversal(tagged, 1, true, Asn1Tags.OctetString, out var senderNonce)) - { - m_senderNonce = (Asn1OctetString)senderNonce; - tagged = seq[++seqPos] as Asn1TaggedObject; - } - - if (tagged != null && - Asn1Utilities.TryGetContextBaseUniversal(tagged, 2, true, Asn1Tags.OctetString, out var recipNonce)) - { - m_recipNonce = (Asn1OctetString)recipNonce; - tagged = seq[++seqPos] as Asn1TaggedObject; - } - - if (tagged != null) - throw new ArgumentException("unknown tag: " + Asn1Utilities.GetTagText(tagged)); - - m_len = DerInteger.GetInstance(seq[seqPos]); - m_mac = AlgorithmIdentifier.GetInstance(seq[++seqPos]); - m_ct = Asn1OctetString.GetInstance(seq[++seqPos]); - - if (++seqPos != seq.Count) - throw new ArgumentException("unexpected data at end of sequence", nameof(seq)); + m_transactionID = Asn1Utilities.ReadOptionalContextTagged(seq, ref pos, 0, true, Asn1OctetString.GetInstance); + m_senderNonce = Asn1Utilities.ReadOptionalContextTagged(seq, ref pos, 1, true, Asn1OctetString.GetInstance); + m_recipNonce = Asn1Utilities.ReadOptionalContextTagged(seq, ref pos, 2, true, Asn1OctetString.GetInstance); + m_len = DerInteger.GetInstance(seq[pos++]); + m_mac = AlgorithmIdentifier.GetInstance(seq[pos++]); + m_ct = Asn1OctetString.GetInstance(seq[pos++]); + + if (pos != count) + throw new ArgumentException("Unexpected elements in sequence", nameof(seq)); } public virtual Asn1OctetString TransactionID => m_transactionID; diff --git a/crypto/src/asn1/cmp/KeyRecRepContent.cs b/crypto/src/asn1/cmp/KeyRecRepContent.cs index e465346eb..ec604f644 100644 --- a/crypto/src/asn1/cmp/KeyRecRepContent.cs +++ b/crypto/src/asn1/cmp/KeyRecRepContent.cs @@ -26,28 +26,18 @@ namespace Org.BouncyCastle.Asn1.Cmp private KeyRecRepContent(Asn1Sequence seq) { - m_status = PkiStatusInfo.GetInstance(seq[0]); + int count = seq.Count, pos = 0; + if (count < 1 || count > 4) + throw new ArgumentException("Bad sequence size: " + count, nameof(seq)); - for (int pos = 1; pos < seq.Count; ++pos) - { - Asn1TaggedObject tObj = Asn1TaggedObject.GetInstance(seq[pos], Asn1Tags.ContextSpecific); + m_status = PkiStatusInfo.GetInstance(seq[pos++]); + m_newSigCert = Asn1Utilities.ReadOptionalContextTagged(seq, ref pos, 0, true, CmpCertificate.GetInstance); + m_caCerts = Asn1Utilities.ReadOptionalContextTagged(seq, ref pos, 1, true, Asn1Sequence.GetInstance); + m_keyPairHist = Asn1Utilities.ReadOptionalContextTagged(seq, ref pos, 2, true, Asn1Sequence.GetInstance); - switch (tObj.TagNo) - { - case 0: - m_newSigCert = CmpCertificate.GetInstance(tObj.GetExplicitBaseObject()); - break; - case 1: - m_caCerts = Asn1Sequence.GetInstance(tObj.GetExplicitBaseObject()); - break; - case 2: - m_keyPairHist = Asn1Sequence.GetInstance(tObj.GetExplicitBaseObject()); - break; - default: - throw new ArgumentException("unknown tag number: " + tObj.TagNo); - } - } - } + if (pos != count) + throw new ArgumentException("Unexpected elements in sequence", nameof(seq)); + } public virtual PkiStatusInfo Status => m_status; diff --git a/crypto/src/asn1/cmp/OobCertHash.cs b/crypto/src/asn1/cmp/OobCertHash.cs index 209113167..f1531f309 100644 --- a/crypto/src/asn1/cmp/OobCertHash.cs +++ b/crypto/src/asn1/cmp/OobCertHash.cs @@ -40,29 +40,25 @@ namespace Org.BouncyCastle.Asn1.Cmp private OobCertHash(Asn1Sequence seq) { - int index = seq.Count - 1; + int count = seq.Count, pos = 0; + if (count < 1 || count > 3) + throw new ArgumentException("Bad sequence size: " + count, nameof(seq)); - m_hashVal = DerBitString.GetInstance(seq[index--]); + m_hashAlg = Asn1Utilities.ReadOptionalContextTagged(seq, ref pos, 0, true, AlgorithmIdentifier.GetInstance); + m_certId = Asn1Utilities.ReadOptionalContextTagged(seq, ref pos, 1, true, CertId.GetInstance); + m_hashVal = DerBitString.GetInstance(seq[pos++]); - for (int i = index; i >= 0; i--) - { - Asn1TaggedObject tObj = (Asn1TaggedObject)seq[i]; - - if (tObj.HasContextTag(0)) - { - m_hashAlg = AlgorithmIdentifier.GetInstance(tObj, true); - } - else if (tObj.HasContextTag(1)) - { - m_certId = CertId.GetInstance(tObj, true); - } - else - { - throw new ArgumentException("unknown tag: " + Asn1Utilities.GetTagText(tObj)); - } - } + if (pos != count) + throw new ArgumentException("Unexpected elements in sequence", nameof(seq)); } + public OobCertHash(AlgorithmIdentifier hashAlg, CertId certId, DerBitString hashVal) + { + m_hashAlg = hashAlg; + m_certId = certId; + m_hashVal = hashVal ?? throw new ArgumentNullException(nameof(hashVal)); + } + public virtual CertId CertID => m_certId; public virtual AlgorithmIdentifier HashAlg => m_hashAlg; diff --git a/crypto/src/asn1/cmp/PKIFreeText.cs b/crypto/src/asn1/cmp/PKIFreeText.cs index a567852e4..e2845c529 100644 --- a/crypto/src/asn1/cmp/PKIFreeText.cs +++ b/crypto/src/asn1/cmp/PKIFreeText.cs @@ -19,6 +19,21 @@ namespace Org.BouncyCastle.Asn1.Cmp return new PkiFreeText(Asn1Sequence.GetInstance(taggedObject, declaredExplicit)); } + public static PkiFreeText GetOptional(Asn1Encodable element) + { + if (element == null) + throw new ArgumentNullException(nameof(element)); + + if (element is PkiFreeText pkiFreeText) + return pkiFreeText; + + Asn1Sequence asn1Sequence = Asn1Sequence.GetOptional(element); + if (asn1Sequence != null) + return new PkiFreeText(asn1Sequence); + + return null; + } + private readonly Asn1Sequence m_strings; internal PkiFreeText(Asn1Sequence seq) diff --git a/crypto/src/asn1/cmp/PKIHeader.cs b/crypto/src/asn1/cmp/PKIHeader.cs index c000c8b98..71dc18e7d 100644 --- a/crypto/src/asn1/cmp/PKIHeader.cs +++ b/crypto/src/asn1/cmp/PKIHeader.cs @@ -10,7 +10,7 @@ namespace Org.BouncyCastle.Asn1.Cmp /** * Value for a "null" recipient or sender. */ - public static readonly GeneralName NULL_NAME = new GeneralName(X509Name.GetInstance(new DerSequence())); + public static readonly GeneralName NULL_NAME = new GeneralName(X509Name.GetInstance(DerSequence.Empty)); public static readonly int CMP_1999 = 1; public static readonly int CMP_2000 = 2; @@ -29,143 +29,77 @@ namespace Org.BouncyCastle.Asn1.Cmp return new PkiHeader(Asn1Sequence.GetInstance(taggedObject, declaredExplicit)); } - private readonly DerInteger pvno; - private readonly GeneralName sender; - private readonly GeneralName recipient; - private readonly Asn1GeneralizedTime messageTime; - private readonly AlgorithmIdentifier protectionAlg; - private readonly Asn1OctetString senderKID; // KeyIdentifier - private readonly Asn1OctetString recipKID; // KeyIdentifier - private readonly Asn1OctetString transactionID; - private readonly Asn1OctetString senderNonce; - private readonly Asn1OctetString recipNonce; - private readonly PkiFreeText freeText; - private readonly Asn1Sequence generalInfo; + private readonly DerInteger m_pvno; + private readonly GeneralName m_sender; + private readonly GeneralName m_recipient; + private readonly Asn1GeneralizedTime m_messageTime; + private readonly AlgorithmIdentifier m_protectionAlg; + private readonly Asn1OctetString m_senderKID; // KeyIdentifier + private readonly Asn1OctetString m_recipKID; // KeyIdentifier + private readonly Asn1OctetString m_transactionID; + private readonly Asn1OctetString m_senderNonce; + private readonly Asn1OctetString m_recipNonce; + private readonly PkiFreeText m_freeText; + private readonly Asn1Sequence m_generalInfo; private PkiHeader(Asn1Sequence seq) { - pvno = DerInteger.GetInstance(seq[0]); - sender = GeneralName.GetInstance(seq[1]); - recipient = GeneralName.GetInstance(seq[2]); - - for (int pos = 3; pos < seq.Count; ++pos) - { - Asn1TaggedObject tObj = Asn1TaggedObject.GetInstance(seq[pos]); - if (!tObj.HasContextTag()) - throw new ArgumentException("unknown tag: " + Asn1Utilities.GetTagText(tObj)); - - switch (tObj.TagNo) - { - case 0: - messageTime = Asn1GeneralizedTime.GetInstance(tObj, true); - break; - case 1: - protectionAlg = AlgorithmIdentifier.GetInstance(tObj, true); - break; - case 2: - senderKID = Asn1OctetString.GetInstance(tObj, true); - break; - case 3: - recipKID = Asn1OctetString.GetInstance(tObj, true); - break; - case 4: - transactionID = Asn1OctetString.GetInstance(tObj, true); - break; - case 5: - senderNonce = Asn1OctetString.GetInstance(tObj, true); - break; - case 6: - recipNonce = Asn1OctetString.GetInstance(tObj, true); - break; - case 7: - freeText = PkiFreeText.GetInstance(tObj, true); - break; - case 8: - generalInfo = Asn1Sequence.GetInstance(tObj, true); - break; - default: - throw new ArgumentException("unknown tag number: " + tObj.TagNo); - } - } - } - - public PkiHeader( - int pvno, - GeneralName sender, - GeneralName recipient) + int count = seq.Count, pos = 0; + if (count < 3 || count > 12) + throw new ArgumentException("Bad sequence size: " + count, nameof(seq)); + + m_pvno = DerInteger.GetInstance(seq[pos++]); + m_sender = GeneralName.GetInstance(seq[pos++]); + m_recipient = GeneralName.GetInstance(seq[pos++]); + m_messageTime = Asn1Utilities.ReadOptionalContextTagged(seq, ref pos, 0, true, Asn1GeneralizedTime.GetInstance); + m_protectionAlg = Asn1Utilities.ReadOptionalContextTagged(seq, ref pos, 1, true, AlgorithmIdentifier.GetInstance); + m_senderKID = Asn1Utilities.ReadOptionalContextTagged(seq, ref pos, 2, true, Asn1OctetString.GetInstance); + m_recipKID = Asn1Utilities.ReadOptionalContextTagged(seq, ref pos, 3, true, Asn1OctetString.GetInstance); + m_transactionID = Asn1Utilities.ReadOptionalContextTagged(seq, ref pos, 4, true, Asn1OctetString.GetInstance); + m_senderNonce = Asn1Utilities.ReadOptionalContextTagged(seq, ref pos, 5, true, Asn1OctetString.GetInstance); + m_recipNonce = Asn1Utilities.ReadOptionalContextTagged(seq, ref pos, 6, true, Asn1OctetString.GetInstance); + m_freeText = Asn1Utilities.ReadOptionalContextTagged(seq, ref pos, 7, true, PkiFreeText.GetInstance); + m_generalInfo = Asn1Utilities.ReadOptionalContextTagged(seq, ref pos, 8, true, Asn1Sequence.GetInstance); + + if (pos != count) + throw new ArgumentException("Unexpected elements in sequence", nameof(seq)); + } + + public PkiHeader(int pvno, GeneralName sender, GeneralName recipient) : this(new DerInteger(pvno), sender, recipient) { } - private PkiHeader( - DerInteger pvno, - GeneralName sender, - GeneralName recipient) + private PkiHeader(DerInteger pvno, GeneralName sender, GeneralName recipient) { - this.pvno = pvno; - this.sender = sender; - this.recipient = recipient; + m_pvno = pvno ?? throw new ArgumentNullException(nameof(pvno)); + m_sender = sender ?? throw new ArgumentNullException(nameof(sender)); + m_recipient = recipient ?? throw new ArgumentNullException(nameof(recipient)); } - public virtual DerInteger Pvno - { - get { return pvno; } - } + public virtual DerInteger Pvno => m_pvno; - public virtual GeneralName Sender - { - get { return sender; } - } + public virtual GeneralName Sender => m_sender; - public virtual GeneralName Recipient - { - get { return recipient; } - } + public virtual GeneralName Recipient => m_recipient; - public virtual Asn1GeneralizedTime MessageTime - { - get { return messageTime; } - } + public virtual Asn1GeneralizedTime MessageTime => m_messageTime; - public virtual AlgorithmIdentifier ProtectionAlg - { - get { return protectionAlg; } - } + public virtual AlgorithmIdentifier ProtectionAlg => m_protectionAlg; - public virtual Asn1OctetString SenderKID - { - get { return senderKID; } - } + public virtual Asn1OctetString SenderKID => m_senderKID; - public virtual Asn1OctetString RecipKID - { - get { return recipKID; } - } + public virtual Asn1OctetString RecipKID => m_recipKID; - public virtual Asn1OctetString TransactionID - { - get { return transactionID; } - } + public virtual Asn1OctetString TransactionID => m_transactionID; - public virtual Asn1OctetString SenderNonce - { - get { return senderNonce; } - } + public virtual Asn1OctetString SenderNonce => m_senderNonce; - public virtual Asn1OctetString RecipNonce - { - get { return recipNonce; } - } + public virtual Asn1OctetString RecipNonce => m_recipNonce; - public virtual PkiFreeText FreeText - { - get { return freeText; } - } + public virtual PkiFreeText FreeText => m_freeText; - public virtual InfoTypeAndValue[] GetGeneralInfo() - { - return generalInfo?.MapElements(InfoTypeAndValue.GetInstance); - } + public virtual InfoTypeAndValue[] GetGeneralInfo() => m_generalInfo?.MapElements(InfoTypeAndValue.GetInstance); /** *
@@ -207,16 +141,17 @@ namespace Org.BouncyCastle.Asn1.Cmp
          */
         public override Asn1Object ToAsn1Object()
         {
-            Asn1EncodableVector v = new Asn1EncodableVector(pvno, sender, recipient);
-            v.AddOptionalTagged(true, 0, messageTime);
-            v.AddOptionalTagged(true, 1, protectionAlg);
-            v.AddOptionalTagged(true, 2, senderKID);
-            v.AddOptionalTagged(true, 3, recipKID);
-            v.AddOptionalTagged(true, 4, transactionID);
-            v.AddOptionalTagged(true, 5, senderNonce);
-            v.AddOptionalTagged(true, 6, recipNonce);
-            v.AddOptionalTagged(true, 7, freeText);
-            v.AddOptionalTagged(true, 8, generalInfo);
+            Asn1EncodableVector v = new Asn1EncodableVector(12);
+            v.Add(m_pvno, m_sender, m_recipient);
+            v.AddOptionalTagged(true, 0, m_messageTime);
+            v.AddOptionalTagged(true, 1, m_protectionAlg);
+            v.AddOptionalTagged(true, 2, m_senderKID);
+            v.AddOptionalTagged(true, 3, m_recipKID);
+            v.AddOptionalTagged(true, 4, m_transactionID);
+            v.AddOptionalTagged(true, 5, m_senderNonce);
+            v.AddOptionalTagged(true, 6, m_recipNonce);
+            v.AddOptionalTagged(true, 7, m_freeText);
+            v.AddOptionalTagged(true, 8, m_generalInfo);
             return new DerSequence(v);
         }
     }
diff --git a/crypto/src/asn1/cmp/PKIHeaderBuilder.cs b/crypto/src/asn1/cmp/PKIHeaderBuilder.cs
index 914c8a8fa..3426c8f0e 100644
--- a/crypto/src/asn1/cmp/PKIHeaderBuilder.cs
+++ b/crypto/src/asn1/cmp/PKIHeaderBuilder.cs
@@ -6,9 +6,9 @@ namespace Org.BouncyCastle.Asn1.Cmp
 {
 	public class PkiHeaderBuilder
 	{
-		private DerInteger pvno;
-		private GeneralName sender;
-		private GeneralName recipient;
+		private readonly DerInteger pvno;
+		private readonly GeneralName sender;
+		private readonly GeneralName recipient;
 		private Asn1GeneralizedTime messageTime;
 		private AlgorithmIdentifier protectionAlg;
 		private Asn1OctetString senderKID;       // KeyIdentifier
@@ -19,22 +19,16 @@ namespace Org.BouncyCastle.Asn1.Cmp
 		private PkiFreeText     freeText;
 		private Asn1Sequence    generalInfo;
 
-		public PkiHeaderBuilder(
-			int			pvno,
-			GeneralName	sender,
-			GeneralName	recipient)
-			: this(new DerInteger(pvno), sender, recipient)
-		{
-		}
+        public PkiHeaderBuilder(int pvno, GeneralName sender, GeneralName recipient)
+            : this(new DerInteger(pvno), sender, recipient)
+        {
+        }
 
-		private PkiHeaderBuilder(
-			DerInteger	pvno,
-			GeneralName	sender,
-			GeneralName	recipient)
-		{
-			this.pvno = pvno;
-			this.sender = sender;
-			this.recipient = recipient;
+        private PkiHeaderBuilder(DerInteger pvno, GeneralName sender, GeneralName recipient)
+        {
+            this.pvno = pvno ?? throw new ArgumentNullException(nameof(pvno));
+			this.sender = sender ?? throw new ArgumentNullException(nameof(sender));
+			this.recipient = recipient ?? throw new ArgumentNullException(nameof(recipient));
 		}
 
 		public virtual PkiHeaderBuilder SetMessageTime(Asn1GeneralizedTime time)
@@ -133,7 +127,7 @@ namespace Org.BouncyCastle.Asn1.Cmp
 
 		private static Asn1Sequence MakeGeneralInfoSeq(InfoTypeAndValue[] generalInfos)
 		{
-			return generalInfos == null ? null : new DerSequence(generalInfos);
+			return generalInfos == null ? null : DerSequence.FromElements(generalInfos);
 		}
 
 		/**
diff --git a/crypto/src/asn1/cmp/PKIMessage.cs b/crypto/src/asn1/cmp/PKIMessage.cs
index e835b6f16..4b5acad77 100644
--- a/crypto/src/asn1/cmp/PKIMessage.cs
+++ b/crypto/src/asn1/cmp/PKIMessage.cs
@@ -26,22 +26,17 @@ namespace Org.BouncyCastle.Asn1.Cmp
 
         private PkiMessage(Asn1Sequence seq)
         {
-            m_header = PkiHeader.GetInstance(seq[0]);
-            m_body = PkiBody.GetInstance(seq[1]);
+            int count = seq.Count, pos = 0;
+            if (count < 2 || count > 4)
+                throw new ArgumentException("Bad sequence size: " + count, nameof(seq));
 
-            for (int pos = 2; pos < seq.Count; ++pos)
-            {
-                Asn1TaggedObject tObj = Asn1TaggedObject.GetInstance(seq[pos]);
+            m_header = PkiHeader.GetInstance(seq[pos++]);
+            m_body = PkiBody.GetInstance(seq[pos++]);
+            m_protection = Asn1Utilities.ReadOptionalContextTagged(seq, ref pos, 0, true, DerBitString.GetInstance);
+            m_extraCerts = Asn1Utilities.ReadOptionalContextTagged(seq, ref pos, 1, true, Asn1Sequence.GetInstance);
 
-                if (tObj.HasContextTag(0))
-                {
-                    m_protection = DerBitString.GetInstance(tObj, true);
-                }
-                else if (tObj.HasContextTag(1))
-                {
-                    m_extraCerts = Asn1Sequence.GetInstance(tObj, true);
-                }
-            }
+            if (pos != count)
+                throw new ArgumentException("Unexpected elements in sequence", nameof(seq));
         }
 
         /**
@@ -54,13 +49,10 @@ namespace Org.BouncyCastle.Asn1.Cmp
          */
         public PkiMessage(PkiHeader header, PkiBody body, DerBitString protection, CmpCertificate[] extraCerts)
         {
-            m_header = header;
-            m_body = body;
+            m_header = header ?? throw new ArgumentNullException(nameof(header));
+            m_body = body ?? throw new ArgumentNullException(nameof(body));
             m_protection = protection;
-            if (extraCerts != null)
-            {
-                m_extraCerts = new DerSequence(extraCerts);
-            }
+            m_extraCerts = extraCerts == null ? null : DerSequence.FromElements(extraCerts);
         }
 
         public PkiMessage(PkiHeader header, PkiBody body, DerBitString protection)
diff --git a/crypto/src/asn1/cmp/PKIStatusInfo.cs b/crypto/src/asn1/cmp/PKIStatusInfo.cs
index fbd180c6d..f88651008 100644
--- a/crypto/src/asn1/cmp/PKIStatusInfo.cs
+++ b/crypto/src/asn1/cmp/PKIStatusInfo.cs
@@ -25,6 +25,23 @@ namespace Org.BouncyCastle.Asn1.Cmp
 #pragma warning restore CS0618 // Type or member is obsolete
         }
 
+        public static PkiStatusInfo GetOptional(Asn1Encodable element)
+        {
+            if (element == null)
+                throw new ArgumentNullException(nameof(element));
+
+            if (element is PkiStatusInfo pkiStatusInfo)
+                return pkiStatusInfo;
+
+            Asn1Sequence asn1Sequence = Asn1Sequence.GetOptional(element);
+            if (asn1Sequence != null)
+#pragma warning disable CS0618 // Type or member is obsolete
+                return new PkiStatusInfo(asn1Sequence);
+#pragma warning restore CS0618 // Type or member is obsolete
+
+            return null;
+        }
+
         private readonly DerInteger m_status;
 		private readonly PkiFreeText m_statusString;
 		private readonly DerBitString m_failInfo;
@@ -32,28 +49,16 @@ namespace Org.BouncyCastle.Asn1.Cmp
         [Obsolete("Use 'GetInstance' instead")]
         public PkiStatusInfo(Asn1Sequence seq)
 		{
-			m_status = DerInteger.GetInstance(seq[0]);
+            int count = seq.Count, pos = 0;
+            if (count < 1 || count > 3)
+                throw new ArgumentException("Bad sequence size: " + count, nameof(seq));
 
-			m_statusString = null;
-			m_failInfo = null;
+			m_status = DerInteger.GetInstance(seq[pos++]);
+			m_statusString = Asn1Utilities.ReadOptional(seq, ref pos, PkiFreeText.GetOptional);
+            m_failInfo = Asn1Utilities.ReadOptional(seq, ref pos, DerBitString.GetOptional);
 
-			if (seq.Count > 2)
-			{
-				m_statusString = PkiFreeText.GetInstance(seq[1]);
-				m_failInfo = DerBitString.GetInstance(seq[2]);
-			}
-			else if (seq.Count > 1)
-			{
-				object obj = seq[1];
-				if (obj is DerBitString)
-				{
-					m_failInfo = DerBitString.GetInstance(obj);
-				}
-				else
-				{
-					m_statusString = PkiFreeText.GetInstance(obj);
-				}
-			}
+            if (pos != count)
+                throw new ArgumentException("Unexpected elements in sequence", nameof(seq));
 		}
 
 		public PkiStatusInfo(int status)
diff --git a/crypto/src/asn1/cmp/PbmParameter.cs b/crypto/src/asn1/cmp/PbmParameter.cs
index aeb91d7d0..8119e4faf 100644
--- a/crypto/src/asn1/cmp/PbmParameter.cs
+++ b/crypto/src/asn1/cmp/PbmParameter.cs
@@ -1,4 +1,6 @@
-using Org.BouncyCastle.Asn1.X509;
+using System;
+
+using Org.BouncyCastle.Asn1.X509;
 
 namespace Org.BouncyCastle.Asn1.Cmp
 {
@@ -43,6 +45,10 @@ namespace Org.BouncyCastle.Asn1.Cmp
 
         private PbmParameter(Asn1Sequence seq)
         {
+            int count = seq.Count;
+            if (count != 4)
+                throw new ArgumentException("Bad sequence size: " + count, nameof(seq));
+
             m_salt = Asn1OctetString.GetInstance(seq[0]);
             m_owf = AlgorithmIdentifier.GetInstance(seq[1]);
             m_iterationCount = DerInteger.GetInstance(seq[2]);
@@ -57,10 +63,10 @@ namespace Org.BouncyCastle.Asn1.Cmp
         public PbmParameter(Asn1OctetString salt, AlgorithmIdentifier owf, DerInteger iterationCount,
             AlgorithmIdentifier mac)
         {
-            m_salt = salt;
-            m_owf = owf;
-            m_iterationCount = iterationCount;
-            m_mac = mac;
+            m_salt = salt ?? throw new ArgumentNullException(nameof(salt));
+            m_owf = owf ?? throw new ArgumentNullException(nameof(owf));
+            m_iterationCount = iterationCount ?? throw new ArgumentNullException(nameof(iterationCount));
+            m_mac = mac ?? throw new ArgumentNullException(nameof(mac));
         }
 
         public virtual DerInteger IterationCount => m_iterationCount;
diff --git a/crypto/src/asn1/cmp/ProtectedPart.cs b/crypto/src/asn1/cmp/ProtectedPart.cs
index e6b6311df..7c7110517 100644
--- a/crypto/src/asn1/cmp/ProtectedPart.cs
+++ b/crypto/src/asn1/cmp/ProtectedPart.cs
@@ -1,3 +1,5 @@
+using System;
+
 namespace Org.BouncyCastle.Asn1.Cmp
 {
 	public class ProtectedPart
@@ -22,14 +24,18 @@ namespace Org.BouncyCastle.Asn1.Cmp
 
 		private ProtectedPart(Asn1Sequence seq)
 		{
-			m_header = PkiHeader.GetInstance(seq[0]);
+            int count = seq.Count;
+            if (count != 2)
+                throw new ArgumentException("Bad sequence size: " + count, nameof(seq));
+
+            m_header = PkiHeader.GetInstance(seq[0]);
 			m_body = PkiBody.GetInstance(seq[1]);
 		}
 
 		public ProtectedPart(PkiHeader header, PkiBody body)
 		{
-			m_header = header;
-			m_body = body;
+			m_header = header ?? throw new ArgumentNullException(nameof(header));
+			m_body = body ?? throw new ArgumentNullException(nameof(body));
 		}
 
 		public virtual PkiHeader Header => m_header;
diff --git a/crypto/src/asn1/cmp/RevAnnContent.cs b/crypto/src/asn1/cmp/RevAnnContent.cs
index 5ac731fb8..e94beb0df 100644
--- a/crypto/src/asn1/cmp/RevAnnContent.cs
+++ b/crypto/src/asn1/cmp/RevAnnContent.cs
@@ -1,3 +1,5 @@
+using System;
+
 using Org.BouncyCastle.Asn1.Crmf;
 using Org.BouncyCastle.Asn1.X509;
 
@@ -35,24 +37,27 @@ namespace Org.BouncyCastle.Asn1.Cmp
         public RevAnnContent(PkiStatusEncodable status, CertId certID, Asn1GeneralizedTime willBeRevokedAt,
             Asn1GeneralizedTime badSinceDate, X509Extensions crlDetails)
         {
-            m_status = status;
-            m_certID = certID;
-            m_willBeRevokedAt = willBeRevokedAt;
-            m_badSinceDate = badSinceDate;
+            m_status = status ?? throw new ArgumentNullException(nameof(status));
+            m_certID = certID ?? throw new ArgumentNullException(nameof(certID));
+            m_willBeRevokedAt = willBeRevokedAt ?? throw new ArgumentNullException(nameof(willBeRevokedAt));
+            m_badSinceDate = badSinceDate ?? throw new ArgumentNullException(nameof(badSinceDate));
             m_crlDetails = crlDetails;
         }
 
         private RevAnnContent(Asn1Sequence seq)
 		{
-			m_status = PkiStatusEncodable.GetInstance(seq[0]);
-			m_certID = CertId.GetInstance(seq[1]);
-			m_willBeRevokedAt = Asn1GeneralizedTime.GetInstance(seq[2]);
-			m_badSinceDate = Asn1GeneralizedTime.GetInstance(seq[3]);
+            int count = seq.Count, pos = 0;
+            if (count < 4 || count > 5)
+                throw new ArgumentException("Bad sequence size: " + count, nameof(seq));
+
+            m_status = PkiStatusEncodable.GetInstance(seq[pos++]);
+            m_certID = CertId.GetInstance(seq[pos++]);
+            m_willBeRevokedAt = Asn1GeneralizedTime.GetInstance(seq[pos++]);
+            m_badSinceDate = Asn1GeneralizedTime.GetInstance(seq[pos++]);
+            m_crlDetails = Asn1Utilities.ReadOptional(seq, ref pos, X509Extensions.GetOptional);
 
-			if (seq.Count > 4)
-			{
-				m_crlDetails = X509Extensions.GetInstance(seq[4]);
-			}
+            if (pos != count)
+                throw new ArgumentException("Unexpected elements in sequence", nameof(seq));
 		}
 
 		public virtual PkiStatusEncodable Status => m_status;
diff --git a/crypto/src/asn1/cmp/RevDetails.cs b/crypto/src/asn1/cmp/RevDetails.cs
index 718a9ef71..b1b5981b1 100644
--- a/crypto/src/asn1/cmp/RevDetails.cs
+++ b/crypto/src/asn1/cmp/RevDetails.cs
@@ -1,3 +1,5 @@
+using System;
+
 using Org.BouncyCastle.Asn1.Crmf;
 using Org.BouncyCastle.Asn1.X509;
 
@@ -37,12 +39,15 @@ namespace Org.BouncyCastle.Asn1.Cmp
 
         private RevDetails(Asn1Sequence seq)
 		{
-			m_certDetails = CertTemplate.GetInstance(seq[0]);
+            int count = seq.Count, pos = 0;
+            if (count < 1 || count > 2)
+                throw new ArgumentException("Bad sequence size: " + count, nameof(seq));
+
+            m_certDetails = CertTemplate.GetInstance(seq[pos++]);
+            m_crlEntryDetails = Asn1Utilities.ReadOptional(seq, ref pos, X509Extensions.GetOptional);
 
-            if (seq.Count > 1)
-            {
-                m_crlEntryDetails = X509Extensions.GetInstance(seq[1]);
-            }
+            if (pos != count)
+                throw new ArgumentException("Unexpected elements in sequence", nameof(seq));
 		}
 
 		public RevDetails(CertTemplate certDetails)
@@ -52,7 +57,7 @@ namespace Org.BouncyCastle.Asn1.Cmp
 
         public RevDetails(CertTemplate certDetails, X509Extensions crlEntryDetails)
 		{
-            m_certDetails = certDetails;
+            m_certDetails = certDetails ?? throw new ArgumentNullException(nameof(certDetails));
             m_crlEntryDetails = crlEntryDetails;
 		}
 
@@ -75,10 +80,9 @@ namespace Org.BouncyCastle.Asn1.Cmp
 		*/
 		public override Asn1Object ToAsn1Object()
 		{
-            Asn1EncodableVector v = new Asn1EncodableVector(2);
-            v.Add(m_certDetails);
-			v.AddOptional(m_crlEntryDetails);
-			return new DerSequence(v);
+            return m_crlEntryDetails == null
+                ?  new DerSequence(m_certDetails)
+                :  new DerSequence(m_certDetails, m_crlEntryDetails);
 		}
 	}
 }
diff --git a/crypto/src/asn1/cmp/RevRepContent.cs b/crypto/src/asn1/cmp/RevRepContent.cs
index ae1d0baed..e9b3230c8 100644
--- a/crypto/src/asn1/cmp/RevRepContent.cs
+++ b/crypto/src/asn1/cmp/RevRepContent.cs
@@ -1,3 +1,5 @@
+using System;
+
 using Org.BouncyCastle.Asn1.Crmf;
 using Org.BouncyCastle.Asn1.X509;
 
@@ -40,24 +42,19 @@ namespace Org.BouncyCastle.Asn1.Cmp
 
 		private RevRepContent(Asn1Sequence seq)
 		{
-			m_status = Asn1Sequence.GetInstance(seq[0]);
+            int count = seq.Count, pos = 0;
+            if (count < 1 || count > 3)
+                throw new ArgumentException("Bad sequence size: " + count, nameof(seq));
 
-			for (int pos = 1; pos < seq.Count; ++pos)
-			{
-				Asn1TaggedObject tObj = Asn1TaggedObject.GetInstance(seq[pos]);
+            m_status = Asn1Sequence.GetInstance(seq[pos++]);
+			m_revCerts = Asn1Utilities.ReadOptionalContextTagged(seq, ref pos, 0, true, Asn1Sequence.GetInstance);
+            m_crls = Asn1Utilities.ReadOptionalContextTagged(seq, ref pos, 1, true, Asn1Sequence.GetInstance);
 
-				if (tObj.HasContextTag(0))
-				{
-					m_revCerts = Asn1Sequence.GetInstance(tObj, true);
-				}
-				else if (tObj.HasContextTag(1))
-				{
-					m_crls = Asn1Sequence.GetInstance(tObj, true);
-				}
-			}
+            if (pos != count)
+                throw new ArgumentException("Unexpected elements in sequence", nameof(seq));
 		}
 
-		public virtual PkiStatusInfo[] GetStatus() => m_status.MapElements(PkiStatusInfo.GetInstance);
+        public virtual PkiStatusInfo[] GetStatus() => m_status.MapElements(PkiStatusInfo.GetInstance);
 
 		public virtual CertId[] GetRevCerts() => m_revCerts?.MapElements(CertId.GetInstance);
 
diff --git a/crypto/src/asn1/cmp/RootCaKeyUpdateContent.cs b/crypto/src/asn1/cmp/RootCaKeyUpdateContent.cs
index f00090f23..c9782bfd9 100644
--- a/crypto/src/asn1/cmp/RootCaKeyUpdateContent.cs
+++ b/crypto/src/asn1/cmp/RootCaKeyUpdateContent.cs
@@ -47,30 +47,16 @@ namespace Org.BouncyCastle.Asn1.Cmp
 
         private RootCaKeyUpdateContent(Asn1Sequence seq)
         {
-            if (seq.Count < 1 || seq.Count > 3)
-                throw new ArgumentException("expected sequence of 1 to 3 elements only");
+            int count = seq.Count, pos = 0;
+            if (count < 1 || count > 3)
+                throw new ArgumentException("Bad sequence size: " + count, nameof(seq));
 
-            CmpCertificate newWithNew = CmpCertificate.GetInstance(seq[0]);
-            CmpCertificate newWithOld = null;
-            CmpCertificate oldWithNew = null;
+            m_newWithNew = CmpCertificate.GetInstance(seq[pos++]);
+            m_newWithOld = Asn1Utilities.ReadOptionalContextTagged(seq, ref pos, 0, true, CmpCertificate.GetInstance);
+            m_oldWithNew = Asn1Utilities.ReadOptionalContextTagged(seq, ref pos, 1, true, CmpCertificate.GetInstance);
 
-            for (int pos = 1; pos < seq.Count; ++pos)
-            {
-                Asn1TaggedObject ato = Asn1TaggedObject.GetInstance(seq[pos]);
-
-                if (ato.HasContextTag(0))
-                {
-                    newWithOld = CmpCertificate.GetInstance(ato, true);
-                }
-                else if (ato.HasContextTag(1))
-                {
-                    oldWithNew = CmpCertificate.GetInstance(ato, true);
-                }
-            }
-
-            m_newWithNew = newWithNew;
-            m_newWithOld = newWithOld;
-            m_oldWithNew = oldWithNew;
+            if (pos != count)
+                throw new ArgumentException("Unexpected elements in sequence", nameof(seq));
         }
 
         public virtual CmpCertificate NewWithNew => m_newWithNew;
diff --git a/crypto/src/asn1/cms/Time.cs b/crypto/src/asn1/cms/Time.cs
index 66fa578af..8dbca7a27 100644
--- a/crypto/src/asn1/cms/Time.cs
+++ b/crypto/src/asn1/cms/Time.cs
@@ -28,6 +28,25 @@ namespace Org.BouncyCastle.Asn1.Cms
             return Asn1Utilities.GetInstanceFromChoice(taggedObject, declaredExplicit, GetInstance);
         }
 
+        public static Time GetOptional(Asn1Encodable element)
+        {
+            if (element == null)
+                throw new ArgumentNullException(nameof(element));
+
+            if (element is Time time)
+                return time;
+
+            Asn1UtcTime utcTime = Asn1UtcTime.GetOptional(element);
+            if (utcTime != null)
+                return new Time(utcTime);
+
+            Asn1GeneralizedTime generalizedTime = Asn1GeneralizedTime.GetOptional(element);
+            if (generalizedTime != null)
+                return new Time(generalizedTime);
+
+            return null;
+        }
+
         private readonly Asn1Object m_timeObject;
 
         public Time(Asn1GeneralizedTime generalizedTime)
diff --git a/crypto/src/asn1/crmf/Controls.cs b/crypto/src/asn1/crmf/Controls.cs
index 12954f5e8..fd3609cf1 100644
--- a/crypto/src/asn1/crmf/Controls.cs
+++ b/crypto/src/asn1/crmf/Controls.cs
@@ -23,11 +23,14 @@ namespace Org.BouncyCastle.Asn1.Crmf
         {
             if (element == null)
                 throw new ArgumentNullException(nameof(element));
+
             if (element is Controls controls)
                 return controls;
+
             Asn1Sequence asn1Sequence = Asn1Sequence.GetOptional(element);
             if (asn1Sequence != null)
                 return new Controls(asn1Sequence);
+
             return null;
         }
 
diff --git a/crypto/src/asn1/crmf/PKIArchiveOptions.cs b/crypto/src/asn1/crmf/PKIArchiveOptions.cs
index 7f756b39a..a702d6dc2 100644
--- a/crypto/src/asn1/crmf/PKIArchiveOptions.cs
+++ b/crypto/src/asn1/crmf/PKIArchiveOptions.cs
@@ -35,14 +35,17 @@ namespace Org.BouncyCastle.Asn1.Crmf
         {
             if (element == null)
                 throw new ArgumentNullException(nameof(element));
+
             if (element is PkiArchiveOptions pkiArchiveOptions)
                 return pkiArchiveOptions;
+
             if (element is Asn1TaggedObject taggedObject)
             {
                 Asn1Encodable baseObject = GetOptionalBaseObject(taggedObject);
                 if (baseObject != null)
                     return new PkiArchiveOptions(taggedObject.TagNo, baseObject);
             }
+
             return null;
         }
 
diff --git a/crypto/src/asn1/crmf/PopoPrivKey.cs b/crypto/src/asn1/crmf/PopoPrivKey.cs
index 13359d5bf..91b1ddba4 100644
--- a/crypto/src/asn1/crmf/PopoPrivKey.cs
+++ b/crypto/src/asn1/crmf/PopoPrivKey.cs
@@ -38,14 +38,17 @@ namespace Org.BouncyCastle.Asn1.Crmf
         {
             if (element == null)
                 throw new ArgumentNullException(nameof(element));
+
             if (element is PopoPrivKey popoPrivKey)
                 return popoPrivKey;
+
             if (element is Asn1TaggedObject taggedObject)
             {
                 Asn1Encodable baseObject = GetOptionalBaseObject(taggedObject);
                 if (baseObject != null)
                     return new PopoPrivKey(taggedObject.TagNo, baseObject);
             }
+
             return null;
         }
 
diff --git a/crypto/src/asn1/crmf/ProofOfPossession.cs b/crypto/src/asn1/crmf/ProofOfPossession.cs
index 0e6a64dac..d7d9fe7b1 100644
--- a/crypto/src/asn1/crmf/ProofOfPossession.cs
+++ b/crypto/src/asn1/crmf/ProofOfPossession.cs
@@ -37,14 +37,17 @@ namespace Org.BouncyCastle.Asn1.Crmf
         {
             if (element == null)
                 throw new ArgumentNullException(nameof(element));
+
             if (element is ProofOfPossession proofOfPossession)
                 return proofOfPossession;
+
             if (element is Asn1TaggedObject taggedObject)
             {
                 Asn1Encodable baseObject = GetOptionalBaseObject(taggedObject);
                 if (baseObject != null)
                     return new ProofOfPossession(taggedObject.TagNo, baseObject);
             }
+
             return null;
         }
 
diff --git a/crypto/src/asn1/x509/AlgorithmIdentifier.cs b/crypto/src/asn1/x509/AlgorithmIdentifier.cs
index 486772c16..f73401b63 100644
--- a/crypto/src/asn1/x509/AlgorithmIdentifier.cs
+++ b/crypto/src/asn1/x509/AlgorithmIdentifier.cs
@@ -22,6 +22,21 @@ namespace Org.BouncyCastle.Asn1.X509
             return GetInstance(Asn1Sequence.GetInstance(obj, explicitly));
         }
 
+        public static AlgorithmIdentifier GetOptional(Asn1Encodable element)
+        {
+            if (element == null)
+                throw new ArgumentNullException(nameof(element));
+
+            if (element is AlgorithmIdentifier algorithmIdentifier)
+                return algorithmIdentifier;
+
+            Asn1Sequence asn1Sequence = Asn1Sequence.GetOptional(element);
+            if (asn1Sequence != null)
+                return new AlgorithmIdentifier(asn1Sequence);
+
+            return null;
+        }
+
         public AlgorithmIdentifier(
             DerObjectIdentifier algorithm)
         {
diff --git a/crypto/src/asn1/x509/Time.cs b/crypto/src/asn1/x509/Time.cs
index e504d0573..b3a997c33 100644
--- a/crypto/src/asn1/x509/Time.cs
+++ b/crypto/src/asn1/x509/Time.cs
@@ -27,6 +27,25 @@ namespace Org.BouncyCastle.Asn1.X509
             return Asn1Utilities.GetInstanceFromChoice(taggedObject, declaredExplicit, GetInstance);
         }
 
+        public static Time GetOptional(Asn1Encodable element)
+        {
+            if (element == null)
+                throw new ArgumentNullException(nameof(element));
+
+            if (element is Time time)
+                return time;
+
+            Asn1UtcTime utcTime = Asn1UtcTime.GetOptional(element);
+            if (utcTime != null)
+                return new Time(utcTime);
+
+            Asn1GeneralizedTime generalizedTime = Asn1GeneralizedTime.GetOptional(element);
+            if (generalizedTime != null)
+                return new Time(generalizedTime);
+
+            return null;
+        }
+
         private readonly Asn1Object m_timeObject;
 
         public Time(Asn1GeneralizedTime generalizedTime)
diff --git a/crypto/src/asn1/x509/X509Extensions.cs b/crypto/src/asn1/x509/X509Extensions.cs
index 2893f8b79..193ac8188 100644
--- a/crypto/src/asn1/x509/X509Extensions.cs
+++ b/crypto/src/asn1/x509/X509Extensions.cs
@@ -220,6 +220,21 @@ namespace Org.BouncyCastle.Asn1.X509
             throw new ArgumentException("unknown object in factory: " + Platform.GetTypeName(obj), nameof(obj));
         }
 
+        public static X509Extensions GetOptional(Asn1Encodable element)
+        {
+            if (element == null)
+                throw new ArgumentNullException(nameof(element));
+
+            if (element is X509Extensions existing)
+                return existing;
+
+            Asn1Sequence asn1Sequence = Asn1Sequence.GetOptional(element);
+            if (asn1Sequence != null)
+                return new X509Extensions(asn1Sequence);
+
+            return null;
+        }
+
         /**
          * Constructor from Asn1Sequence.
          *
-- 
cgit 1.4.1