diff options
author | Peter Dettman <peter.dettman@bouncycastle.org> | 2021-11-12 01:54:30 +0700 |
---|---|---|
committer | Peter Dettman <peter.dettman@bouncycastle.org> | 2021-11-12 01:54:30 +0700 |
commit | 8c32dba3fc4f2033e7597ee1999e793cf6552584 (patch) | |
tree | c48d45537cfbea6f7e0ab92de4793a2b35204573 | |
parent | Add new Equals method (diff) | |
download | BouncyCastle.NET-ed25519-8c32dba3fc4f2033e7597ee1999e793cf6552584.tar.xz |
Reimplement application-specific over tagged object
-rw-r--r-- | crypto/BouncyCastle.Android.csproj | 1 | ||||
-rw-r--r-- | crypto/BouncyCastle.csproj | 1 | ||||
-rw-r--r-- | crypto/BouncyCastle.iOS.csproj | 1 | ||||
-rw-r--r-- | crypto/crypto.csproj | 5 | ||||
-rw-r--r-- | crypto/src/asn1/Asn1TaggedObject.cs | 229 | ||||
-rw-r--r-- | crypto/src/asn1/BerApplicationSpecific.cs | 44 | ||||
-rw-r--r-- | crypto/src/asn1/BerTaggedObject.cs | 57 | ||||
-rw-r--r-- | crypto/src/asn1/DLApplicationSpecific.cs | 28 | ||||
-rw-r--r-- | crypto/src/asn1/DLTaggedObject.cs | 23 | ||||
-rw-r--r-- | crypto/src/asn1/DerApplicationSpecific.cs | 334 | ||||
-rw-r--r-- | crypto/src/asn1/DerTaggedObject.cs | 55 | ||||
-rw-r--r-- | crypto/src/asn1/util/Asn1Dump.cs | 92 |
12 files changed, 514 insertions, 356 deletions
diff --git a/crypto/BouncyCastle.Android.csproj b/crypto/BouncyCastle.Android.csproj index 0cececb3f..c3d897efb 100644 --- a/crypto/BouncyCastle.Android.csproj +++ b/crypto/BouncyCastle.Android.csproj @@ -132,6 +132,7 @@ <Compile Include="src\asn1\DerUniversalString.cs" /> <Compile Include="src\asn1\DerVideotexString.cs" /> <Compile Include="src\asn1\DerVisibleString.cs" /> + <Compile Include="src\asn1\DLApplicationSpecific.cs" /> <Compile Include="src\asn1\DLSequence.cs" /> <Compile Include="src\asn1\DLSet.cs" /> <Compile Include="src\asn1\DLTaggedObject.cs" /> diff --git a/crypto/BouncyCastle.csproj b/crypto/BouncyCastle.csproj index 2e82e2432..0ffedcb1a 100644 --- a/crypto/BouncyCastle.csproj +++ b/crypto/BouncyCastle.csproj @@ -126,6 +126,7 @@ <Compile Include="src\asn1\DerUniversalString.cs" /> <Compile Include="src\asn1\DerVideotexString.cs" /> <Compile Include="src\asn1\DerVisibleString.cs" /> + <Compile Include="src\asn1\DLApplicationSpecific.cs" /> <Compile Include="src\asn1\DLSequence.cs" /> <Compile Include="src\asn1\DLSet.cs" /> <Compile Include="src\asn1\DLTaggedObject.cs" /> diff --git a/crypto/BouncyCastle.iOS.csproj b/crypto/BouncyCastle.iOS.csproj index 29031b947..3cd17d407 100644 --- a/crypto/BouncyCastle.iOS.csproj +++ b/crypto/BouncyCastle.iOS.csproj @@ -127,6 +127,7 @@ <Compile Include="src\asn1\DerUniversalString.cs" /> <Compile Include="src\asn1\DerVideotexString.cs" /> <Compile Include="src\asn1\DerVisibleString.cs" /> + <Compile Include="src\asn1\DLApplicationSpecific.cs" /> <Compile Include="src\asn1\DLSequence.cs" /> <Compile Include="src\asn1\DLSet.cs" /> <Compile Include="src\asn1\DLTaggedObject.cs" /> diff --git a/crypto/crypto.csproj b/crypto/crypto.csproj index b490065e0..870cafbe9 100644 --- a/crypto/crypto.csproj +++ b/crypto/crypto.csproj @@ -519,6 +519,11 @@ BuildAction = "Compile" /> <File + RelPath = "src\asn1\DLApplicationSpecific.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File RelPath = "src\asn1\DLSequence.cs" SubType = "Code" BuildAction = "Compile" diff --git a/crypto/src/asn1/Asn1TaggedObject.cs b/crypto/src/asn1/Asn1TaggedObject.cs index aaa719ecc..13d1ff283 100644 --- a/crypto/src/asn1/Asn1TaggedObject.cs +++ b/crypto/src/asn1/Asn1TaggedObject.cs @@ -13,18 +13,13 @@ namespace Org.BouncyCastle.Asn1 public abstract class Asn1TaggedObject : Asn1Object, Asn1TaggedObjectParser { - // TODO[asn1] Rewrite DerApplicationSpecific in terms of Asn1TaggedObject and remove this - internal static bool IsConstructed(bool isExplicit, Asn1Object obj) - { - if (isExplicit || obj is Asn1Sequence || obj is Asn1Set) - return true; - Asn1TaggedObject tagged = obj as Asn1TaggedObject; - if (tagged == null) - return false; - return IsConstructed(tagged.IsExplicit(), tagged.GetObject()); - } + private const int DeclaredExplicit = 1; + private const int DeclaredImplicit = 2; + // TODO It will probably be better to track parsing constructed vs primitive instead + private const int ParsedExplicit = 3; + private const int ParsedImplicit = 4; - public static Asn1TaggedObject GetInstance(object obj) + public static Asn1TaggedObject GetInstance(object obj) { if (obj == null || obj is Asn1TaggedObject) { @@ -57,69 +52,91 @@ namespace Org.BouncyCastle.Asn1 if (Asn1Tags.ContextSpecific != taggedObject.TagClass) throw new InvalidOperationException("this method only valid for CONTEXT_SPECIFIC tags"); - if (!declaredExplicit) - throw new ArgumentException("this method not valid for implicitly tagged tagged objects"); + if (declaredExplicit) + return taggedObject.GetExplicitBaseTagged(); - return taggedObject.GetExplicitBaseTagged(); + throw new ArgumentException("this method not valid for implicitly tagged tagged objects"); } - internal readonly int tagClass = Asn1Tags.ContextSpecific; + internal readonly int explicitness; + internal readonly int tagClass; internal readonly int tagNo; - internal readonly bool explicitly; internal readonly Asn1Encodable obj; - /** + /** + * @param explicitly true if the object is explicitly tagged. * @param tagNo the tag number for this object. * @param obj the tagged object. */ - protected Asn1TaggedObject(int tagNo, Asn1Encodable obj) - : this(true, tagNo, obj) + protected Asn1TaggedObject(bool isExplicit, int tagNo, Asn1Encodable obj) + : this(isExplicit, Asn1Tags.ContextSpecific, tagNo, obj) { } - /** - * @param explicitly true if the object is explicitly tagged. - * @param tagNo the tag number for this object. - * @param obj the tagged object. - */ - protected Asn1TaggedObject(bool explicitly, int tagNo, Asn1Encodable obj) + protected Asn1TaggedObject(bool isExplicit, int tagClass, int tagNo, Asn1Encodable obj) + : this(isExplicit ? DeclaredExplicit : DeclaredImplicit, tagClass, tagNo, obj) + { + } + + internal Asn1TaggedObject(int explicitness, int tagClass, int tagNo, Asn1Encodable obj) { if (null == obj) throw new ArgumentNullException("obj"); + if (Asn1Tags.Universal == tagClass || (tagClass & Asn1Tags.Private) != tagClass) + throw new ArgumentException("invalid tag class: " + tagClass, "tagClass"); - // IAsn1Choice marker interface 'insists' on explicit tagging - this.explicitly = explicitly || (obj is IAsn1Choice); + this.explicitness = (obj is IAsn1Choice) ? DeclaredExplicit : explicitness; + this.tagClass = tagClass; this.tagNo = tagNo; this.obj = obj; } - protected override bool Asn1Equals(Asn1Object asn1Object) + protected override bool Asn1Equals(Asn1Object asn1Object) { if (asn1Object is DerApplicationSpecific) return asn1Object.CallAsn1Equals(this); Asn1TaggedObject that = asn1Object as Asn1TaggedObject; - return null != that - && this.tagClass == that.tagClass - && this.tagNo == that.tagNo - && this.explicitly == that.explicitly // TODO Should this be part of equality? - && this.GetObject().Equals(that.GetObject()); - } + if (null == that || this.tagNo != that.tagNo || this.tagClass != that.tagClass) + return false; - protected override int Asn1GetHashCode() - { - int code = (tagClass * 7919) ^ tagNo; + if (this.explicitness != that.explicitness) + { + /* + * TODO This seems incorrect for some cases of implicit tags e.g. if one is a + * declared-implicit SET and the other a parsed object. + */ + if (this.IsExplicit() != that.IsExplicit()) + return false; + } - // TODO: actually this is wrong - the problem is that a re-encoded - // object may end up with a different hashCode due to implicit - // tagging. As implicit tagging is ambiguous if a sequence is involved - // it seems the only correct method for both equals and hashCode is to - // compare the encodings... -// code ^= explicitly.GetHashCode(); + Asn1Object p1 = this.obj.ToAsn1Object(); + Asn1Object p2 = that.obj.ToAsn1Object(); - code ^= obj.GetHashCode(); + if (p1 == p2) + return true; + + if (!this.IsExplicit()) + { + try + { + byte[] d1 = this.GetEncoded(); + byte[] d2 = that.GetEncoded(); - return code; + return Arrays.AreEqual(d1, d2); + } + catch (IOException) + { + return false; + } + } + + return p1.CallAsn1Equals(p2); + } + + protected override int Asn1GetHashCode() + { + return (tagClass * 7919) ^ tagNo ^ (IsExplicit() ? 0x0F : 0xF0) ^ obj.ToAsn1Object().CallAsn1GetHashCode(); } public int TagClass @@ -142,6 +159,12 @@ namespace Org.BouncyCastle.Asn1 return this.tagClass == tagClass && this.tagNo == tagNo; } + [Obsolete("Will be removed. Replace with constant return value of 'false'")] + public bool IsEmpty() + { + return false; + } + /** * return whether or not the object may be explicitly tagged. * <p> @@ -153,13 +176,27 @@ namespace Org.BouncyCastle.Asn1 */ public bool IsExplicit() { - return explicitly; + // TODO New methods like 'IsKnownExplicit' etc. to distinguish uncertain cases? + switch (explicitness) + { + case DeclaredExplicit: + case ParsedExplicit: + return true; + default: + return false; + } } - [Obsolete("Will be removed. Replace with constant return value of 'false'")] - public bool IsEmpty() + internal bool IsParsed() { - return false; + switch (explicitness) + { + case ParsedExplicit: + case ParsedImplicit: + return true; + default: + return false; + } } /** @@ -198,6 +235,18 @@ namespace Org.BouncyCastle.Asn1 } /** + * Return true if the object is marked as constructed, false otherwise. + * + * @return true if constructed, otherwise false. + */ + // TODO Need this public if/when DerApplicationSpecific extends Asn1TaggedObject + internal bool IsConstructed() + { + int encoding = Asn1Encoding == Ber ? Asn1OutputStream.EncodingBer : Asn1OutputStream.EncodingDer; + return EncodeConstructed(encoding); + } + + /** * return whatever was following the tag. * <p> * Note: tagged objects are generally context dependent if you're @@ -245,6 +294,34 @@ namespace Org.BouncyCastle.Asn1 return CheckedCast(obj.ToAsn1Object()); } + public Asn1TaggedObject GetImplicitBaseTagged(int baseTagClass, int baseTagNo) + { + if (Asn1Tags.Universal == baseTagClass || (baseTagClass & Asn1Tags.Private) != baseTagClass) + throw new ArgumentException("invalid base tag class: " + baseTagClass, "baseTagClass"); + + switch (explicitness) + { + case DeclaredExplicit: + throw new InvalidOperationException("object explicit - implicit expected."); + + case DeclaredImplicit: + { + Asn1TaggedObject declared = CheckedCast(obj.ToAsn1Object()); + if (!declared.HasTag(baseTagClass, baseTagNo)) + { + string expected = Asn1Utilities.GetTagText(baseTagClass, baseTagNo); + string found = Asn1Utilities.GetTagText(declared); + throw new InvalidOperationException("Expected " + expected + " tag but found " + found); + } + return declared; + } + + // Parsed; return a virtual tag (i.e. that couldn't have been present in the encoding) + default: + return ReplaceTag(baseTagClass, baseTagNo); + } + } + /** * Return the object held in this tagged object as a parser assuming it has * the type of the passed in tag. If the object doesn't have a parser @@ -267,10 +344,8 @@ namespace Org.BouncyCastle.Asn1 return Asn1Set.GetInstance(this, isExplicit).Parser; } - if (isExplicit) - { - return GetObject(); - } + if (isExplicit) + return obj.ToAsn1Object(); throw Platform.CreateNotImplementedException("implicit tagging for tag: " + tag); } @@ -284,6 +359,8 @@ namespace Org.BouncyCastle.Asn1 internal abstract Asn1Sequence RebuildConstructed(Asn1Object asn1Object); + internal abstract Asn1TaggedObject ReplaceTag(int tagClass, int tagNo); + internal static Asn1Object CreateConstructed(int tagClass, int tagNo, bool isIL, Asn1EncodableVector contentsElements) { @@ -291,29 +368,47 @@ namespace Org.BouncyCastle.Asn1 if (isIL) { - if (Asn1Tags.Application == tagClass) - return new BerApplicationSpecific(tagNo, contentsElements); + Asn1TaggedObject taggedObject = maybeExplicit + ? new BerTaggedObject(ParsedExplicit, tagClass, tagNo, contentsElements[0]) + : new BerTaggedObject(ParsedImplicit, tagClass, tagNo, BerSequence.FromVector(contentsElements)); - return maybeExplicit - ? new BerTaggedObject(true, tagNo, contentsElements[0]) - : new BerTaggedObject(false, tagNo, BerSequence.FromVector(contentsElements)); + switch (tagClass) + { + case Asn1Tags.Application: + return new BerApplicationSpecific(taggedObject); + default: + return taggedObject; + } } + else + { + Asn1TaggedObject taggedObject = maybeExplicit + ? new DLTaggedObject(ParsedExplicit, tagClass, tagNo, contentsElements[0]) + : new DLTaggedObject(ParsedImplicit, tagClass, tagNo, DLSequence.FromVector(contentsElements)); - if (Asn1Tags.Application == tagClass) - return new DerApplicationSpecific(tagNo, contentsElements); - - return maybeExplicit - ? new DLTaggedObject(true, tagNo, contentsElements[0]) - : new DLTaggedObject(false, tagNo, DLSequence.FromVector(contentsElements)); + switch (tagClass) + { + case Asn1Tags.Application: + return new DLApplicationSpecific(taggedObject); + default: + return taggedObject; + } + } } internal static Asn1Object CreatePrimitive(int tagClass, int tagNo, byte[] contentsOctets) { // Note: !CONSTRUCTED => IMPLICIT - if (Asn1Tags.Application == tagClass) - return new DerApplicationSpecific(false, tagNo, contentsOctets); + Asn1TaggedObject taggedObject = new DLTaggedObject(ParsedImplicit, tagClass, tagNo, + new DerOctetString(contentsOctets)); - return new DLTaggedObject(false, tagNo, new DerOctetString(contentsOctets)); + switch (tagClass) + { + case Asn1Tags.Application: + return new DLApplicationSpecific(taggedObject); + default: + return taggedObject; + } } private static Asn1TaggedObject CheckedCast(Asn1Object asn1Object) diff --git a/crypto/src/asn1/BerApplicationSpecific.cs b/crypto/src/asn1/BerApplicationSpecific.cs index 65fbecbe1..2a874984f 100644 --- a/crypto/src/asn1/BerApplicationSpecific.cs +++ b/crypto/src/asn1/BerApplicationSpecific.cs @@ -5,11 +5,43 @@ namespace Org.BouncyCastle.Asn1 public class BerApplicationSpecific : DerApplicationSpecific { - public BerApplicationSpecific( - int tagNo, - Asn1EncodableVector vec) - : base(tagNo, vec) - { - } + /** + * Create an application specific object with an explicit tag + * + * @param tagNo the tag number for this object. + * @param baseEncodable the object to be contained. + */ + public BerApplicationSpecific(int tagNo, Asn1Encodable baseEncodable) + : this(true, tagNo, baseEncodable) + { + } + + /** + * Create an application specific object with the tagging style given by the value of explicit. + * + * @param explicit true if the object is explicitly tagged. + * @param tagNo the tag number for this object. + * @param baseEncodable the object to be contained. + */ + public BerApplicationSpecific(bool isExplicit, int tagNo, Asn1Encodable baseEncodable) + : base(new BerTaggedObject(isExplicit, Asn1Tags.Application, tagNo, baseEncodable)) + { + } + + /** + * Create an application specific object which is marked as constructed + * + * @param tagNo the tag number for this object. + * @param contentsElements the objects making up the application specific object. + */ + public BerApplicationSpecific(int tagNo, Asn1EncodableVector contentsElements) + : base(new BerTaggedObject(false, Asn1Tags.Application, tagNo, BerSequence.FromVector(contentsElements))) + { + } + + internal BerApplicationSpecific(Asn1TaggedObject taggedObject) + : base(taggedObject) + { + } } } diff --git a/crypto/src/asn1/BerTaggedObject.cs b/crypto/src/asn1/BerTaggedObject.cs index fc4d1835a..a97a8e143 100644 --- a/crypto/src/asn1/BerTaggedObject.cs +++ b/crypto/src/asn1/BerTaggedObject.cs @@ -10,39 +10,49 @@ namespace Org.BouncyCastle.Asn1 public class BerTaggedObject : DerTaggedObject { - /** + /** + * create an implicitly tagged object that contains a zero + * length sequence. + */ + [Obsolete("Will be removed")] + public BerTaggedObject(int tagNo) + : base(false, tagNo, BerSequence.Empty) + { + } + + /** * @param tagNo the tag number for this object. * @param obj the tagged object. */ - public BerTaggedObject( - int tagNo, - Asn1Encodable obj) - : base(tagNo, obj) + public BerTaggedObject(int tagNo, Asn1Encodable obj) + : base(true, tagNo, obj) { } - /** - * @param explicitly true if an explicitly tagged object. + public BerTaggedObject(int tagClass, int tagNo, Asn1Encodable obj) + : base(true, tagClass, tagNo, obj) + { + } + + /** + * @param isExplicit true if an explicitly tagged object. * @param tagNo the tag number for this object. * @param obj the tagged object. */ - public BerTaggedObject( - bool explicitly, - int tagNo, - Asn1Encodable obj) - : base(explicitly, tagNo, obj) + public BerTaggedObject(bool isExplicit, int tagNo, Asn1Encodable obj) + : base(isExplicit, tagNo, obj) { } - /** - * create an implicitly tagged object that contains a zero - * length sequence. - */ - public BerTaggedObject( - int tagNo) - : base(false, tagNo, BerSequence.Empty) - { - } + public BerTaggedObject(bool isExplicit, int tagClass, int tagNo, Asn1Encodable obj) + : base(isExplicit, tagClass, tagNo, obj) + { + } + + internal BerTaggedObject(int explicitness, int tagClass, int tagNo, Asn1Encodable obj) + : base(explicitness, tagClass, tagNo, obj) + { + } internal override string Asn1Encoding { @@ -116,5 +126,10 @@ namespace Org.BouncyCastle.Asn1 { return new BerSequence(asn1Object); } + + internal override Asn1TaggedObject ReplaceTag(int tagClass, int tagNo) + { + return new BerTaggedObject(explicitness, tagClass, tagNo, obj); + } } } diff --git a/crypto/src/asn1/DLApplicationSpecific.cs b/crypto/src/asn1/DLApplicationSpecific.cs new file mode 100644 index 000000000..8fffcf65b --- /dev/null +++ b/crypto/src/asn1/DLApplicationSpecific.cs @@ -0,0 +1,28 @@ +using System; + +namespace Org.BouncyCastle.Asn1 +{ + internal class DLApplicationSpecific + : DerApplicationSpecific + { + internal DLApplicationSpecific(int tagNo, Asn1Encodable baseEncodable) + : this(true, tagNo, baseEncodable) + { + } + + internal DLApplicationSpecific(bool isExplicit, int tagNo, Asn1Encodable baseEncodable) + : base(new DLTaggedObject(isExplicit, Asn1Tags.Application, tagNo, baseEncodable)) + { + } + + internal DLApplicationSpecific(int tagNo, Asn1EncodableVector contentsElements) + : base(new DLTaggedObject(false, Asn1Tags.Application, tagNo, DLSequence.FromVector(contentsElements))) + { + } + + internal DLApplicationSpecific(Asn1TaggedObject taggedObject) + : base(taggedObject) + { + } + } +} diff --git a/crypto/src/asn1/DLTaggedObject.cs b/crypto/src/asn1/DLTaggedObject.cs index 1c5e990b4..314b42799 100644 --- a/crypto/src/asn1/DLTaggedObject.cs +++ b/crypto/src/asn1/DLTaggedObject.cs @@ -12,13 +12,23 @@ namespace Org.BouncyCastle.Asn1 { } - internal DLTaggedObject(bool explicitly, int tagNo, Asn1Encodable obj) - : base(explicitly, tagNo, obj) + internal DLTaggedObject(int tagClass, int tagNo, Asn1Encodable obj) + : base(tagClass, tagNo, obj) { } - internal DLTaggedObject(int tagNo) - : base(false, tagNo, DLSequence.Empty) + internal DLTaggedObject(bool isExplicit, int tagNo, Asn1Encodable obj) + : base(isExplicit, tagNo, obj) + { + } + + internal DLTaggedObject(bool isExplicit, int tagClass, int tagNo, Asn1Encodable obj) + : base(isExplicit, tagClass, tagNo, obj) + { + } + + internal DLTaggedObject(int explicitness, int tagClass, int tagNo, Asn1Encodable obj) + : base(explicitness, tagClass, tagNo, obj) { } @@ -97,6 +107,11 @@ namespace Org.BouncyCastle.Asn1 return new DLSequence(asn1Object); } + internal override Asn1TaggedObject ReplaceTag(int tagClass, int tagNo) + { + return new DLTaggedObject(explicitness, tagClass, tagNo, obj); + } + private int GetContentsLengthDL(Asn1Object baseObject, bool withBaseID) { if (m_contentsLengthDL < 0) diff --git a/crypto/src/asn1/DerApplicationSpecific.cs b/crypto/src/asn1/DerApplicationSpecific.cs index bc48d24e5..39c2aec28 100644 --- a/crypto/src/asn1/DerApplicationSpecific.cs +++ b/crypto/src/asn1/DerApplicationSpecific.cs @@ -11,225 +11,219 @@ namespace Org.BouncyCastle.Asn1 public class DerApplicationSpecific : Asn1Object { - private readonly bool isConstructed; - private readonly int tag; - private readonly byte[] octets; - - internal DerApplicationSpecific( - bool isConstructed, - int tag, - byte[] octets) - { - this.isConstructed = isConstructed; - this.tag = tag; - this.octets = octets; - } - - public DerApplicationSpecific( - int tag, - byte[] octets) - : this(false, tag, octets) - { - } - - public DerApplicationSpecific( - int tag, - Asn1Encodable obj) - : this(true, tag, obj) - { - } - - public DerApplicationSpecific( - bool isExplicit, - int tag, - Asn1Encodable obj) - { - Asn1Object asn1Obj = obj.ToAsn1Object(); - - byte[] data = asn1Obj.GetDerEncoded(); - - this.isConstructed = Asn1TaggedObject.IsConstructed(isExplicit, asn1Obj); - this.tag = tag; - - if (isExplicit) - { - this.octets = data; - } - else - { - int lenBytes = GetLengthOfHeader(data); - byte[] tmp = new byte[data.Length - lenBytes]; - Array.Copy(data, lenBytes, tmp, 0, tmp.Length); - this.octets = tmp; - } - } - - public DerApplicationSpecific( - int tagNo, - Asn1EncodableVector vec) - { - this.tag = tagNo; - this.isConstructed = true; - MemoryStream bOut = new MemoryStream(); - - for (int i = 0; i != vec.Count; i++) - { - try - { - byte[] bs = vec[i].GetDerEncoded(); - bOut.Write(bs, 0, bs.Length); - } - catch (IOException e) - { - throw new InvalidOperationException("malformed object", e); - } - } - this.octets = bOut.ToArray(); - } - - private int GetLengthOfHeader( - byte[] data) - { - int length = data[1]; // TODO: assumes 1 byte tag - - if (length == 0x80) + public static DerApplicationSpecific GetInstance(object obj) + { + if (obj == null || obj is DerApplicationSpecific) { - return 2; // indefinite-length encoding + return (DerApplicationSpecific)obj; } - - if (length > 127) + else if (obj is byte[]) { - int size = length & 0x7f; - - // Note: The invalid long form "0xff" (see X.690 8.1.3.5c) will be caught here - if (size > 4) + try { - throw new InvalidOperationException("DER length more than 4 bytes: " + size); + return GetInstance(FromByteArray((byte[])obj)); + } + catch (IOException e) + { + throw new ArgumentException("failed to construct application-specific from byte[]: " + e.Message); } - - return size + 2; } - return 2; + throw new ArgumentException("illegal object in GetInstance: " + Platform.GetTypeName(obj), "obj"); } - public bool IsConstructed() + internal readonly Asn1TaggedObject m_taggedObject; + + /** + * Create an application specific object from the passed in data. This will assume + * the data does not represent a constructed object. + * + * @param tagNo the tag number for this object. + * @param contentsOctets the encoding of the object's body. + */ + public DerApplicationSpecific(int tagNo, byte[] contentsOctets) + : this(new DerTaggedObject(false, Asn1Tags.Application, tagNo, new DerOctetString(contentsOctets))) { - return isConstructed; } - public byte[] GetContents() + /** + * Create an application specific object with a tagging of explicit/constructed. + * + * @param tag the tag number for this object. + * @param object the object to be contained. + */ + public DerApplicationSpecific(int tag, Asn1Encodable baseEncodable) + : this(true, tag, baseEncodable) { - return octets; } - public int ApplicationTag + /** + * Create an application specific object with the tagging style given by the value of explicit. + * + * @param explicit true if the object is explicitly tagged. + * @param tagNo the tag number for this object. + * @param baseEncodable the object to be contained. + */ + public DerApplicationSpecific(bool isExplicit, int tagNo, Asn1Encodable baseEncodable) + : this(new DerTaggedObject(isExplicit, Asn1Tags.Application, tagNo, baseEncodable)) { - get { return tag; } } - /** - * Return the enclosed object assuming explicit tagging. - * - * @return the resulting object - * @throws IOException if reconstruction fails. - */ - public Asn1Object GetObject() + /** + * Create an application specific object which is marked as constructed + * + * @param tagNo the tag number for this object. + * @param contentsElements the objects making up the application specific object. + */ + public DerApplicationSpecific(int tagNo, Asn1EncodableVector contentsElements) + : this(new DerTaggedObject(false, Asn1Tags.Application, tagNo, DerSequence.FromVector(contentsElements))) { - return FromByteArray(GetContents()); - } + } - /** - * Return the enclosed object assuming implicit tagging. - * - * @param derTagNo the type tag that should be applied to the object's contents. - * @return the resulting object - * @throws IOException if reconstruction fails. - */ - public Asn1Object GetObject( - int derTagNo) - { - if (derTagNo >= 0x1f) - throw new IOException("unsupported tag number"); + internal DerApplicationSpecific(Asn1TaggedObject taggedObject) + //: base(taggedObject.explicitness, CheckTagClass(taggedObject.tagClass), taggedObject.tagNo, + // taggedObject.obj) + { + CheckTagClass(taggedObject.TagClass); - byte[] orig = this.GetEncoded(); - byte[] tmp = ReplaceTagNumber(derTagNo, orig); + this.m_taggedObject = taggedObject; + } - if ((orig[0] & Asn1Tags.Constructed) != 0) - { - tmp[0] |= Asn1Tags.Constructed; - } + public int ApplicationTag + { + get { return m_taggedObject.TagNo; } + } - return FromByteArray(tmp); - } + public byte[] GetContents() + { + return m_taggedObject.GetContents(); + } - internal override bool EncodeConstructed(int encoding) + public Asn1Object GetEnclosedObject() { - return isConstructed; + return m_taggedObject.GetBaseObject().ToAsn1Object(); } - internal override int EncodedLength(int encoding, bool withID) + [Obsolete("Use GetEnclosedObject instead")] + public Asn1Object GetObject() { - return Asn1OutputStream.GetLengthOfEncodingDL(withID, tag, octets.Length); + return GetEnclosedObject(); } - internal override void Encode(Asn1OutputStream asn1Out, bool withID) + public Asn1Object GetObject(int tagNo) + { + // TODO[asn1] Implement Asn1TaggedObject.GetBaseUniversal + //return taggedObject.GetBaseUniversal(false, tagNo); + + if (tagNo >= 0x1F) + throw new IOException("unsupported tag number"); + + byte[] orig = this.GetEncoded(); + byte[] tmp = ReplaceTagNumber(tagNo, orig); + + if ((orig[0] & Asn1Tags.Constructed) != 0) + { + tmp[0] |= Asn1Tags.Constructed; + } + + return FromByteArray(tmp); + } + + public bool HasApplicationTag(int tagNo) + { + return m_taggedObject.HasTag(Asn1Tags.Application, tagNo); + } + + public bool IsConstructed() + { + return m_taggedObject.IsConstructed(); + } + + /** + * DerApplicationSpecific uses an internal Asn1TaggedObject for the + * implementation, and will soon be deprecated in favour of using + * Asn1TaggedObject with a tag class of {@link Asn1Tags#Application}. This method + * lets you get the internal Asn1TaggedObject so that client code can begin the + * migration. + */ + public Asn1TaggedObject TaggedObject { - int flags = Asn1Tags.Application; - if (isConstructed) - { - flags |= Asn1Tags.Constructed; - } + get { return m_taggedObject; } + } - asn1Out.WriteEncodingDL(withID, flags, tag, octets); - } + protected override bool Asn1Equals(Asn1Object asn1Object) + { + Asn1TaggedObject that; + if (asn1Object is DerApplicationSpecific) + { + that = ((DerApplicationSpecific)asn1Object).m_taggedObject; + } + else if (asn1Object is Asn1TaggedObject) + { + that = (Asn1TaggedObject)asn1Object; + } + else + { + return false; + } + + return m_taggedObject.Equals(that); + } - protected override bool Asn1Equals( - Asn1Object asn1Object) + protected override int Asn1GetHashCode() { - DerApplicationSpecific other = asn1Object as DerApplicationSpecific; + return m_taggedObject.CallAsn1GetHashCode(); + } - if (other == null) - return false; + internal override bool EncodeConstructed(int encoding) + { + return m_taggedObject.EncodeConstructed(encoding); + } - return this.isConstructed == other.isConstructed - && this.tag == other.tag - && Arrays.AreEqual(this.octets, other.octets); + internal override int EncodedLength(int encoding, bool withID) + { + return m_taggedObject.EncodedLength(encoding, withID); } - protected override int Asn1GetHashCode() - { - return isConstructed.GetHashCode() ^ tag.GetHashCode() ^ Arrays.GetHashCode(octets); + internal override void Encode(Asn1OutputStream asn1Out, bool withID) + { + m_taggedObject.Encode(asn1Out, withID); } - private byte[] ReplaceTagNumber( - int newTag, - byte[] input) - { - int tagNo = input[0] & 0x1f; - int index = 1; + private byte[] ReplaceTagNumber(int newTag, byte[] input) + { + int tagNo = input[0] & 0x1f; + int index = 1; // with tagged object tag number is bottom 5 bits, or stored at the start of the content - if (tagNo == 0x1f) - { - int b = input[index++]; + if (tagNo == 0x1f) + { + int b = input[index++]; // X.690-0207 8.1.2.4.2 - // "c) bits 7 to 1 of the first subsequent octet shall not all be zero." - if ((b & 0x7f) == 0) // Note: -1 will pass - throw new IOException("corrupted stream - invalid high tag number found"); + // "c) bits 7 to 1 of the first subsequent octet shall not all be zero." + if ((b & 0x7f) == 0) // Note: -1 will pass + throw new IOException("corrupted stream - invalid high tag number found"); while ((b & 0x80) != 0) - { - b = input[index++]; - } - } + { + b = input[index++]; + } + } int remaining = input.Length - index; byte[] tmp = new byte[1 + remaining]; tmp[0] = (byte)newTag; - Array.Copy(input, index, tmp, 1, remaining); - return tmp; - } + Array.Copy(input, index, tmp, 1, remaining); + return tmp; + } + + private static int CheckTagClass(int tagClass) + { + if (Asn1Tags.Application != tagClass) + throw new ArgumentException(); + + return tagClass; + } } } diff --git a/crypto/src/asn1/DerTaggedObject.cs b/crypto/src/asn1/DerTaggedObject.cs index e58590c8d..66f804ebb 100644 --- a/crypto/src/asn1/DerTaggedObject.cs +++ b/crypto/src/asn1/DerTaggedObject.cs @@ -13,38 +13,44 @@ namespace Org.BouncyCastle.Asn1 private int m_contentsLengthDer = -1; /** - * @param tagNo the tag number for this object. - * @param obj the tagged object. + * create an implicitly tagged object that contains a zero + * length sequence. */ - public DerTaggedObject( - int tagNo, - Asn1Encodable obj) - : base(tagNo, obj) + [Obsolete("Will be removed")] + public DerTaggedObject(int tagNo) + : base(false, tagNo, DerSequence.Empty) + { + } + + public DerTaggedObject(int tagNo, Asn1Encodable obj) + : base(true, tagNo, obj) { } - /** - * @param explicitly true if an explicitly tagged object. + public DerTaggedObject(int tagClass, int tagNo, Asn1Encodable obj) + : base(true, tagClass, tagNo, obj) + { + } + + /** + * @param isExplicit true if an explicitly tagged object. * @param tagNo the tag number for this object. * @param obj the tagged object. */ - public DerTaggedObject( - bool explicitly, - int tagNo, - Asn1Encodable obj) - : base(explicitly, tagNo, obj) + public DerTaggedObject(bool isExplicit, int tagNo, Asn1Encodable obj) + : base(isExplicit, tagNo, obj) { } - /** - * create an implicitly tagged object that contains a zero - * length sequence. - */ - public DerTaggedObject( - int tagNo) - : base(false, tagNo, DerSequence.Empty) - { - } + public DerTaggedObject(bool isExplicit, int tagClass, int tagNo, Asn1Encodable obj) + : base(isExplicit, tagClass, tagNo, obj) + { + } + + internal DerTaggedObject(int explicitness, int tagClass, int tagNo, Asn1Encodable obj) + : base(explicitness, tagClass, tagNo, obj) + { + } internal override string Asn1Encoding { @@ -108,6 +114,11 @@ namespace Org.BouncyCastle.Asn1 return new DerSequence(asn1Object); } + internal override Asn1TaggedObject ReplaceTag(int tagClass, int tagNo) + { + return new DerTaggedObject(explicitness, tagClass, tagNo, obj); + } + private int GetContentsLengthDer(Asn1Object baseObject, bool withBaseID) { if (m_contentsLengthDer < 0) diff --git a/crypto/src/asn1/util/Asn1Dump.cs b/crypto/src/asn1/util/Asn1Dump.cs index 0d3382d00..f573d3663 100644 --- a/crypto/src/asn1/util/Asn1Dump.cs +++ b/crypto/src/asn1/util/Asn1Dump.cs @@ -23,11 +23,7 @@ namespace Org.BouncyCastle.Asn1.Utilities * * @param obj the Asn1Object to be dumped out. */ - private static void AsString( - string indent, - bool verbose, - Asn1Object obj, - StringBuilder buf) + private static void AsString(string indent, bool verbose, Asn1Object obj, StringBuilder buf) { if (obj is Asn1Null) { @@ -42,7 +38,7 @@ namespace Org.BouncyCastle.Asn1.Utilities { buf.Append("BER Sequence"); } - else if (obj is DerSequence) + else if (!(obj is DLSequence)) { buf.Append("DER Sequence"); } @@ -67,7 +63,7 @@ namespace Org.BouncyCastle.Asn1.Utilities { buf.Append("BER Set"); } - else if (obj is DerSet) + else if (!(obj is DLSet)) { buf.Append("DER Set"); } @@ -85,23 +81,29 @@ namespace Org.BouncyCastle.Asn1.Utilities AsString(elementsIndent, verbose, set[i].ToAsn1Object(), buf); } } + else if (obj is DerApplicationSpecific) + { + AsString(indent, verbose, ((DerApplicationSpecific)obj).TaggedObject, buf); + } else if (obj is Asn1TaggedObject) { - string tab = indent + Tab; buf.Append(indent); if (obj is BerTaggedObject) { - buf.Append("BER Tagged ["); + buf.Append("BER Tagged "); + } + else if (!(obj is DLTaggedObject)) + { + buf.Append("DER Tagged "); } else { - buf.Append("Tagged ["); + buf.Append("Tagged "); } Asn1TaggedObject o = (Asn1TaggedObject)obj; - buf.Append(o.TagNo.ToString()); - buf.Append(']'); + buf.Append(Asn1Utilities.GetTagText(o)); if (!o.IsExplicit()) { @@ -110,7 +112,9 @@ namespace Org.BouncyCastle.Asn1.Utilities buf.Append(NewLine); - AsString(tab, verbose, o.GetObject(), buf); + string baseIndent = indent + Tab; + + AsString(baseIndent, verbose, o.GetBaseObject().ToAsn1Object(), buf); } else if (obj is DerObjectIdentifier) { @@ -131,20 +135,20 @@ namespace Org.BouncyCastle.Asn1.Utilities else if (obj is BerOctetString) { byte[] octets = ((Asn1OctetString)obj).GetOctets(); - string extra = verbose ? dumpBinaryDataAsString(indent, octets) : ""; + string extra = verbose ? DumpBinaryDataAsString(indent, octets) : ""; buf.Append(indent + "BER Octet String" + "[" + octets.Length + "] " + extra + NewLine); } else if (obj is DerOctetString) { byte[] octets = ((Asn1OctetString)obj).GetOctets(); - string extra = verbose ? dumpBinaryDataAsString(indent, octets) : ""; + string extra = verbose ? DumpBinaryDataAsString(indent, octets) : ""; buf.Append(indent + "DER Octet String" + "[" + octets.Length + "] " + extra + NewLine); } else if (obj is DerBitString) { DerBitString bt = (DerBitString)obj; byte[] bytes = bt.GetBytes(); - string extra = verbose ? dumpBinaryDataAsString(indent, bytes) : ""; + string extra = verbose ? DumpBinaryDataAsString(indent, bytes) : ""; buf.Append(indent + "DER Bit String" + "[" + bytes.Length + ", " + bt.PadBits + "] " + extra + NewLine); } else if (obj is DerIA5String) @@ -187,14 +191,6 @@ namespace Org.BouncyCastle.Asn1.Utilities { buf.Append(indent + "GeneralizedTime(" + ((DerGeneralizedTime)obj).GetTime() + ") " + NewLine); } - else if (obj is BerApplicationSpecific) - { - buf.Append(outputApplicationSpecific("BER", indent, verbose, (BerApplicationSpecific)obj)); - } - else if (obj is DerApplicationSpecific) - { - buf.Append(outputApplicationSpecific("DER", indent, verbose, (DerApplicationSpecific)obj)); - } else if (obj is DerEnumerated) { DerEnumerated en = (DerEnumerated)obj; @@ -227,44 +223,13 @@ namespace Org.BouncyCastle.Asn1.Utilities } } - private static string outputApplicationSpecific( - string type, - string indent, - bool verbose, - DerApplicationSpecific app) - { - StringBuilder buf = new StringBuilder(); - - if (app.IsConstructed()) - { - try - { - Asn1Sequence s = Asn1Sequence.GetInstance(app.GetObject(Asn1Tags.Sequence)); - buf.Append(indent + type + " ApplicationSpecific[" + app.ApplicationTag + "]" + NewLine); - foreach (Asn1Encodable ae in s) - { - AsString(indent + Tab, verbose, ae.ToAsn1Object(), buf); - } - } - catch (IOException e) - { - buf.Append(e); - } - return buf.ToString(); - } - - return indent + type + " ApplicationSpecific[" + app.ApplicationTag + "] (" - + Hex.ToHexString(app.GetContents()) + ")" + NewLine; - } - /** * dump out a DER object as a formatted string, in non-verbose mode * * @param obj the Asn1Encodable to be dumped out. * @return the resulting string. */ - public static string DumpAsString( - Asn1Encodable obj) + public static string DumpAsString(Asn1Encodable obj) { return DumpAsString(obj, false); } @@ -276,16 +241,14 @@ namespace Org.BouncyCastle.Asn1.Utilities * @param verbose if true, dump out the contents of octet and bit strings. * @return the resulting string. */ - public static string DumpAsString( - Asn1Encodable obj, - bool verbose) + public static string DumpAsString(Asn1Encodable obj, bool verbose) { StringBuilder buf = new StringBuilder(); AsString("", verbose, obj.ToAsn1Object(), buf); return buf.ToString(); } - private static string dumpBinaryDataAsString(string indent, byte[] bytes) + private static string DumpBinaryDataAsString(string indent, byte[] bytes) { indent += Tab; @@ -298,7 +261,7 @@ namespace Org.BouncyCastle.Asn1.Utilities buf.Append(indent); buf.Append(Hex.ToHexString(bytes, i, SampleSize)); buf.Append(Tab); - buf.Append(calculateAscString(bytes, i, SampleSize)); + buf.Append(CalculateAscString(bytes, i, SampleSize)); buf.Append(NewLine); } else @@ -310,7 +273,7 @@ namespace Org.BouncyCastle.Asn1.Utilities buf.Append(" "); } buf.Append(Tab); - buf.Append(calculateAscString(bytes, i, bytes.Length - i)); + buf.Append(CalculateAscString(bytes, i, bytes.Length - i)); buf.Append(NewLine); } } @@ -318,10 +281,7 @@ namespace Org.BouncyCastle.Asn1.Utilities return buf.ToString(); } - private static string calculateAscString( - byte[] bytes, - int off, - int len) + private static string CalculateAscString(byte[] bytes, int off, int len) { StringBuilder buf = new StringBuilder(); |