diff options
author | Peter Dettman <peter.dettman@bouncycastle.org> | 2021-11-18 13:46:18 +0700 |
---|---|---|
committer | Peter Dettman <peter.dettman@bouncycastle.org> | 2021-11-18 13:46:18 +0700 |
commit | 4fccb8f490eefe7181558b2c3376ab23c33632ee (patch) | |
tree | fe8c1877bb5567cb4823823af77e5c8a38423db8 | |
parent | ASN.1: Staged encoding (diff) | |
download | BouncyCastle.NET-ed25519-4fccb8f490eefe7181558b2c3376ab23c33632ee.tar.xz |
ASN.1: Port of bc-java TYPE instances
- we use Meta.Instance here due to syntax restrictions - also reworked some ASN.1 string types
40 files changed, 1662 insertions, 929 deletions
diff --git a/crypto/BouncyCastle.Android.csproj b/crypto/BouncyCastle.Android.csproj index 40f468144..c13fd13aa 100644 --- a/crypto/BouncyCastle.Android.csproj +++ b/crypto/BouncyCastle.Android.csproj @@ -76,8 +76,12 @@ <Compile Include="src\asn1\Asn1RelativeOid.cs" /> <Compile Include="src\asn1\Asn1Sequence.cs" /> <Compile Include="src\asn1\Asn1Set.cs" /> + <Compile Include="src\asn1\Asn1Tag.cs" /> <Compile Include="src\asn1\Asn1TaggedObject.cs" /> <Compile Include="src\asn1\Asn1Tags.cs" /> + <Compile Include="src\asn1\Asn1Type.cs" /> + <Compile Include="src\asn1\Asn1UniversalType.cs" /> + <Compile Include="src\asn1\Asn1UniversalTypes.cs" /> <Compile Include="src\asn1\Asn1Utilities.cs" /> <Compile Include="src\asn1\BERBitString.cs" /> <Compile Include="src\asn1\BERGenerator.cs" /> diff --git a/crypto/BouncyCastle.csproj b/crypto/BouncyCastle.csproj index 387e634ca..9745dca60 100644 --- a/crypto/BouncyCastle.csproj +++ b/crypto/BouncyCastle.csproj @@ -70,8 +70,12 @@ <Compile Include="src\asn1\Asn1RelativeOid.cs" /> <Compile Include="src\asn1\Asn1Sequence.cs" /> <Compile Include="src\asn1\Asn1Set.cs" /> + <Compile Include="src\asn1\Asn1Tag.cs" /> <Compile Include="src\asn1\Asn1TaggedObject.cs" /> <Compile Include="src\asn1\Asn1Tags.cs" /> + <Compile Include="src\asn1\Asn1Type.cs" /> + <Compile Include="src\asn1\Asn1UniversalType.cs" /> + <Compile Include="src\asn1\Asn1UniversalTypes.cs" /> <Compile Include="src\asn1\Asn1Utilities.cs" /> <Compile Include="src\asn1\BERBitString.cs" /> <Compile Include="src\asn1\BERGenerator.cs" /> diff --git a/crypto/BouncyCastle.iOS.csproj b/crypto/BouncyCastle.iOS.csproj index 8c964d6e6..f718b9238 100644 --- a/crypto/BouncyCastle.iOS.csproj +++ b/crypto/BouncyCastle.iOS.csproj @@ -71,8 +71,12 @@ <Compile Include="src\asn1\Asn1RelativeOid.cs" /> <Compile Include="src\asn1\Asn1Sequence.cs" /> <Compile Include="src\asn1\Asn1Set.cs" /> + <Compile Include="src\asn1\Asn1Tag.cs" /> <Compile Include="src\asn1\Asn1TaggedObject.cs" /> <Compile Include="src\asn1\Asn1Tags.cs" /> + <Compile Include="src\asn1\Asn1Type.cs" /> + <Compile Include="src\asn1\Asn1UniversalType.cs" /> + <Compile Include="src\asn1\Asn1UniversalTypes.cs" /> <Compile Include="src\asn1\Asn1Utilities.cs" /> <Compile Include="src\asn1\BERBitString.cs" /> <Compile Include="src\asn1\BERGenerator.cs" /> diff --git a/crypto/crypto.csproj b/crypto/crypto.csproj index 55686e355..69a44f9dc 100644 --- a/crypto/crypto.csproj +++ b/crypto/crypto.csproj @@ -244,11 +244,31 @@ BuildAction = "Compile" /> <File + RelPath = "src\asn1\ASN1Tag.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File RelPath = "src\asn1\ASN1Tags.cs" SubType = "Code" BuildAction = "Compile" /> <File + RelPath = "src\asn1\Asn1Type.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "src\asn1\Asn1UniversalType.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File + RelPath = "src\asn1\Asn1UniversalTypes.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File RelPath = "src\asn1\Asn1Utilities.cs" SubType = "Code" BuildAction = "Compile" diff --git a/crypto/src/asn1/Asn1InputStream.cs b/crypto/src/asn1/Asn1InputStream.cs index 3f7a65cc9..d03c7a6b5 100644 --- a/crypto/src/asn1/Asn1InputStream.cs +++ b/crypto/src/asn1/Asn1InputStream.cs @@ -401,11 +401,12 @@ namespace Org.BouncyCastle.Asn1 switch (tagNo) { case Asn1Tags.BmpString: - return new DerBmpString(GetBmpCharBuffer(defIn)); + return DerBmpString.CreatePrimitive(GetBmpCharBuffer(defIn)); case Asn1Tags.Boolean: - return DerBoolean.FromOctetString(GetBuffer(defIn, tmpBuffers)); + return DerBoolean.CreatePrimitive(GetBuffer(defIn, tmpBuffers)); case Asn1Tags.Enumerated: - return DerEnumerated.FromOctetString(GetBuffer(defIn, tmpBuffers)); + // TODO Ideally only clone if we used a buffer + return DerEnumerated.CreatePrimitive(GetBuffer(defIn, tmpBuffers), true); case Asn1Tags.ObjectIdentifier: // TODO Ideally only clone if we used a buffer return DerObjectIdentifier.CreatePrimitive(GetBuffer(defIn, tmpBuffers), true); @@ -418,44 +419,39 @@ namespace Org.BouncyCastle.Asn1 case Asn1Tags.BitString: return DerBitString.CreatePrimitive(bytes); case Asn1Tags.GeneralizedTime: - return new DerGeneralizedTime(bytes); + return DerGeneralizedTime.CreatePrimitive(bytes); case Asn1Tags.GeneralString: - return new DerGeneralString(bytes); + return DerGeneralString.CreatePrimitive(bytes); case Asn1Tags.GraphicString: return DerGraphicString.CreatePrimitive(bytes); case Asn1Tags.IA5String: - return new DerIA5String(bytes); + return DerIA5String.CreatePrimitive(bytes); case Asn1Tags.Integer: - return new DerInteger(bytes, false); + return DerInteger.CreatePrimitive(bytes); case Asn1Tags.Null: - { - if (0 != bytes.Length) - throw new InvalidOperationException("malformed NULL encoding encountered"); - - return DerNull.Instance; - } + return Asn1Null.CreatePrimitive(bytes); case Asn1Tags.NumericString: - return new DerNumericString(bytes); + return DerNumericString.CreatePrimitive(bytes); case Asn1Tags.ObjectDescriptor: return Asn1ObjectDescriptor.CreatePrimitive(bytes); case Asn1Tags.OctetString: - return new DerOctetString(bytes); + return Asn1OctetString.CreatePrimitive(bytes); case Asn1Tags.PrintableString: - return new DerPrintableString(bytes); + return DerPrintableString.CreatePrimitive(bytes); case Asn1Tags.RelativeOid: return Asn1RelativeOid.CreatePrimitive(bytes, false); case Asn1Tags.T61String: - return new DerT61String(bytes); + return DerT61String.CreatePrimitive(bytes); case Asn1Tags.UniversalString: - return new DerUniversalString(bytes); + return DerUniversalString.CreatePrimitive(bytes); case Asn1Tags.UtcTime: - return new DerUtcTime(bytes); + return DerUtcTime.CreatePrimitive(bytes); case Asn1Tags.Utf8String: - return new DerUtf8String(bytes); + return DerUtf8String.CreatePrimitive(bytes); case Asn1Tags.VideotexString: - return new DerVideotexString(bytes); + return DerVideotexString.CreatePrimitive(bytes); case Asn1Tags.VisibleString: - return new DerVisibleString(bytes); + return DerVisibleString.CreatePrimitive(bytes); default: throw new IOException("unknown tag " + tagNo + " encountered"); } diff --git a/crypto/src/asn1/Asn1Null.cs b/crypto/src/asn1/Asn1Null.cs index d54019f67..9ea9b4375 100644 --- a/crypto/src/asn1/Asn1Null.cs +++ b/crypto/src/asn1/Asn1Null.cs @@ -1,3 +1,8 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Utilities; + namespace Org.BouncyCastle.Asn1 { /** @@ -6,13 +11,65 @@ namespace Org.BouncyCastle.Asn1 public abstract class Asn1Null : Asn1Object { + internal class Meta : Asn1UniversalType + { + internal static readonly Asn1UniversalType Instance = new Meta(); + + private Meta() : base(typeof(Asn1Null), Asn1Tags.Null) {} + + internal override Asn1Object FromImplicitPrimitive(DerOctetString octetString) + { + return CreatePrimitive(octetString.GetOctets()); + } + } + + public static Asn1Null GetInstance(object obj) + { + if (obj == null || obj is Asn1Null) + { + return (Asn1Null)obj; + } + else if (obj is IAsn1Convertible) + { + Asn1Object asn1Object = ((IAsn1Convertible)obj).ToAsn1Object(); + if (asn1Object is Asn1Null) + return (Asn1Null)asn1Object; + } + else if (obj is byte[]) + { + try + { + return (Asn1Null)Meta.Instance.FromByteArray((byte[])obj); + } + catch (IOException e) + { + throw new ArgumentException("failed to construct NULL from byte[]: " + e.Message); + } + } + + throw new ArgumentException("illegal object in GetInstance: " + Platform.GetTypeName(obj)); + } + + public static Asn1Null GetInstance(Asn1TaggedObject taggedObject, bool declaredExplicit) + { + return (Asn1Null)Meta.Instance.GetContextInstance(taggedObject, declaredExplicit); + } + internal Asn1Null() { } - public override string ToString() - { - return "NULL"; - } + public override string ToString() + { + return "NULL"; + } + + internal static Asn1Null CreatePrimitive(byte[] contents) + { + if (0 != contents.Length) + throw new InvalidOperationException("malformed NULL encoding encountered"); + + return DerNull.Instance; + } } } diff --git a/crypto/src/asn1/Asn1ObjectDescriptor.cs b/crypto/src/asn1/Asn1ObjectDescriptor.cs index 289a0e16f..9c99f441e 100644 --- a/crypto/src/asn1/Asn1ObjectDescriptor.cs +++ b/crypto/src/asn1/Asn1ObjectDescriptor.cs @@ -8,6 +8,25 @@ namespace Org.BouncyCastle.Asn1 public sealed class Asn1ObjectDescriptor : Asn1Object { + internal class Meta : Asn1UniversalType + { + internal static readonly Asn1UniversalType Instance = new Meta(); + + private Meta() : base(typeof(Asn1ObjectDescriptor), Asn1Tags.ObjectDescriptor) {} + + internal override Asn1Object FromImplicitPrimitive(DerOctetString octetString) + { + return new Asn1ObjectDescriptor( + (DerGraphicString)DerGraphicString.Meta.Instance.FromImplicitPrimitive(octetString)); + } + + internal override Asn1Object FromImplicitConstructed(Asn1Sequence sequence) + { + return new Asn1ObjectDescriptor( + (DerGraphicString)DerGraphicString.Meta.Instance.FromImplicitConstructed(sequence)); + } + } + /** * Return an ObjectDescriptor from the passed in object. * @@ -31,7 +50,7 @@ namespace Org.BouncyCastle.Asn1 { try { - return GetInstance(FromByteArray((byte[])obj)); + return (Asn1ObjectDescriptor)Meta.Instance.FromByteArray((byte[])obj); } catch (IOException e) { @@ -46,21 +65,13 @@ namespace Org.BouncyCastle.Asn1 * Return an ObjectDescriptor from a tagged object. * * @param taggedObject the tagged object holding the object we want. - * @param explicit true if the object is meant to be explicitly tagged, - * false otherwise. + * @param declaredExplicit true if the object is meant to be explicitly tagged, false otherwise. * @exception IllegalArgumentException if the tagged object cannot be converted. * @return an ASN1ObjectDescriptor instance, or null. */ - public static Asn1ObjectDescriptor GetInstance(Asn1TaggedObject taggedObject, bool isExplicit) + public static Asn1ObjectDescriptor GetInstance(Asn1TaggedObject taggedObject, bool declaredExplicit) { - Asn1Object baseObject = taggedObject.GetObject(); - - if (isExplicit || baseObject is Asn1ObjectDescriptor) - { - return GetInstance(baseObject); - } - - return new Asn1ObjectDescriptor(new DerGraphicString(((Asn1OctetString)baseObject).GetOctets())); + return (Asn1ObjectDescriptor)Meta.Instance.GetContextInstance(taggedObject, declaredExplicit); } private readonly DerGraphicString m_baseGraphicString; diff --git a/crypto/src/asn1/Asn1OctetString.cs b/crypto/src/asn1/Asn1OctetString.cs index 66d6fb8d1..d34686134 100644 --- a/crypto/src/asn1/Asn1OctetString.cs +++ b/crypto/src/asn1/Asn1OctetString.cs @@ -9,6 +9,23 @@ namespace Org.BouncyCastle.Asn1 public abstract class Asn1OctetString : Asn1Object, Asn1OctetStringParser { + internal class Meta : Asn1UniversalType + { + internal static readonly Asn1UniversalType Instance = new Meta(); + + private Meta() : base(typeof(Asn1OctetString), Asn1Tags.OctetString) {} + + internal override Asn1Object FromImplicitPrimitive(DerOctetString octetString) + { + return octetString; + } + + internal override Asn1Object FromImplicitConstructed(Asn1Sequence sequence) + { + return sequence.ToAsn1OctetString(); + } + } + internal static readonly byte[] EmptyOctets = new byte[0]; /** @@ -28,15 +45,13 @@ namespace Org.BouncyCastle.Asn1 { Asn1Object asn1Object = ((IAsn1Convertible)obj).ToAsn1Object(); if (asn1Object is Asn1OctetString) - { return (Asn1OctetString)asn1Object; - } } else if (obj is byte[]) { try { - return GetInstance(FromByteArray((byte[])obj)); + return (Asn1OctetString)Meta.Instance.FromByteArray((byte[])obj); } catch (IOException e) { @@ -48,46 +63,15 @@ namespace Org.BouncyCastle.Asn1 } /** - * return an Octet string from a tagged object. + * return an octet string from a tagged object. * - * @param obj the tagged object holding the object we want. - * @param explicitly true if the object is meant to be explicitly - * tagged false otherwise. - * @exception ArgumentException if the tagged object cannot - * be converted. + * @param taggedObject the tagged object holding the object we want. + * @param declaredExplicit true if the object is meant to be explicitly tagged false otherwise. + * @exception ArgumentException if the tagged object cannot be converted. */ public static Asn1OctetString GetInstance(Asn1TaggedObject taggedObject, bool declaredExplicit) { - if (declaredExplicit) - { - if (!taggedObject.IsExplicit()) - throw new ArgumentException("object implicit - explicit expected."); - - return GetInstance(taggedObject.GetObject()); - } - - Asn1Object baseObject = taggedObject.GetObject(); - - // If parsed as explicit though declared implicit, it should have been a set of one - if (taggedObject.IsExplicit()) - { - Asn1OctetString singleSegment = GetInstance(baseObject); - - if (taggedObject is BerTaggedObject) - return new BerOctetString(new Asn1OctetString[]{ singleSegment }); - - return singleSegment; - } - - if (baseObject is Asn1OctetString) - return (Asn1OctetString)baseObject; - - // Parser assumes implicit constructed encodings are sequences - if (baseObject is Asn1Sequence) - return ((Asn1Sequence)baseObject).ToAsn1OctetString(); - - throw new ArgumentException("illegal object in GetInstance: " + Platform.GetTypeName(taggedObject), - "taggedObject"); + return (Asn1OctetString)Meta.Instance.GetContextInstance(taggedObject, declaredExplicit); } internal readonly byte[] contents; @@ -138,5 +122,10 @@ namespace Org.BouncyCastle.Asn1 { return "#" + Hex.ToHexString(contents); } - } + + internal static Asn1OctetString CreatePrimitive(byte[] contents) + { + return new DerOctetString(contents); + } + } } diff --git a/crypto/src/asn1/Asn1RelativeOid.cs b/crypto/src/asn1/Asn1RelativeOid.cs index a960b50bf..9c4f917f2 100644 --- a/crypto/src/asn1/Asn1RelativeOid.cs +++ b/crypto/src/asn1/Asn1RelativeOid.cs @@ -10,6 +10,18 @@ namespace Org.BouncyCastle.Asn1 public class Asn1RelativeOid : Asn1Object { + internal class Meta : Asn1UniversalType + { + internal static readonly Asn1UniversalType Instance = new Meta(); + + private Meta() : base(typeof(Asn1RelativeOid), Asn1Tags.RelativeOid) {} + + internal override Asn1Object FromImplicitPrimitive(DerOctetString octetString) + { + return CreatePrimitive(octetString.GetOctets(), false); + } + } + public static Asn1RelativeOid FromContents(byte[] contents) { return CreatePrimitive(contents, true); @@ -31,7 +43,7 @@ namespace Org.BouncyCastle.Asn1 { try { - return GetInstance(FromByteArray((byte[])obj)); + return (Asn1RelativeOid)FromByteArray((byte[])obj); } catch (IOException e) { @@ -44,14 +56,7 @@ namespace Org.BouncyCastle.Asn1 public static Asn1RelativeOid GetInstance(Asn1TaggedObject taggedObject, bool declaredExplicit) { - Asn1Object baseObject = taggedObject.GetObject(); - - if (declaredExplicit || baseObject is Asn1RelativeOid) - { - return GetInstance(baseObject); - } - - return FromContents(Asn1OctetString.GetInstance(baseObject).GetOctets()); + return (Asn1RelativeOid)Meta.Instance.GetContextInstance(taggedObject, declaredExplicit); } private const long LongLimit = (Int64.MaxValue >> 7) - 0x7F; diff --git a/crypto/src/asn1/Asn1Sequence.cs b/crypto/src/asn1/Asn1Sequence.cs index cfe0d37aa..d7f84d3e4 100644 --- a/crypto/src/asn1/Asn1Sequence.cs +++ b/crypto/src/asn1/Asn1Sequence.cs @@ -10,6 +10,18 @@ namespace Org.BouncyCastle.Asn1 public abstract class Asn1Sequence : Asn1Object, IEnumerable { + internal class Meta : Asn1UniversalType + { + internal static readonly Asn1UniversalType Instance = new Meta(); + + private Meta() : base(typeof(Asn1Sequence), Asn1Tags.Sequence) {} + + internal override Asn1Object FromImplicitConstructed(Asn1Sequence sequence) + { + return sequence; + } + } + /** * return an Asn1Sequence from the given object. * @@ -33,7 +45,7 @@ namespace Org.BouncyCastle.Asn1 { try { - return GetInstance(FromByteArray((byte[])obj)); + return (Asn1Sequence)Meta.Instance.FromByteArray((byte[])obj); } catch (IOException e) { @@ -55,37 +67,12 @@ namespace Org.BouncyCastle.Asn1 * be using this method. * * @param taggedObject the tagged object. - * @param declaredExplicit true if the object is meant to be explicitly tagged, - * false otherwise. - * @exception ArgumentException if the tagged object cannot - * be converted. + * @param declaredExplicit true if the object is meant to be explicitly tagged, false otherwise. + * @exception ArgumentException if the tagged object cannot be converted. */ public static Asn1Sequence GetInstance(Asn1TaggedObject taggedObject, bool declaredExplicit) { - if (declaredExplicit) - { - if (!taggedObject.IsExplicit()) - throw new ArgumentException("object implicit - explicit expected."); - - return GetInstance(taggedObject.GetObject()); - } - - Asn1Object baseObject = taggedObject.GetObject(); - - // If parsed as explicit though declared implicit, it should have been a sequence of one - if (taggedObject.IsExplicit()) - { - if (taggedObject is BerTaggedObject) - return new BerSequence(baseObject); - - return new DLSequence(baseObject); - } - - if (baseObject is Asn1Sequence) - return (Asn1Sequence)baseObject; - - throw new ArgumentException("illegal object in GetInstance: " + Platform.GetTypeName(taggedObject), - "taggedObject"); + return (Asn1Sequence)Meta.Instance.GetContextInstance(taggedObject, declaredExplicit); } // NOTE: Only non-readonly to support LazyDLSequence diff --git a/crypto/src/asn1/Asn1Set.cs b/crypto/src/asn1/Asn1Set.cs index 42180fd71..f3b94121b 100644 --- a/crypto/src/asn1/Asn1Set.cs +++ b/crypto/src/asn1/Asn1Set.cs @@ -16,6 +16,18 @@ namespace Org.BouncyCastle.Asn1 public abstract class Asn1Set : Asn1Object, IEnumerable { + internal class Meta : Asn1UniversalType + { + internal static readonly Asn1UniversalType Instance = new Meta(); + + private Meta() : base(typeof(Asn1Set), Asn1Tags.Set) {} + + internal override Asn1Object FromImplicitConstructed(Asn1Sequence sequence) + { + return sequence.ToAsn1Set(); + } + } + /** * return an ASN1Set from the given object. * @@ -39,7 +51,7 @@ namespace Org.BouncyCastle.Asn1 { try { - return GetInstance(FromByteArray((byte[])obj)); + return (Asn1Set)Meta.Instance.FromByteArray((byte[])obj); } catch (IOException e) { @@ -61,41 +73,12 @@ namespace Org.BouncyCastle.Asn1 * be using this method. * * @param taggedObject the tagged object. - * @param declaredExplicit true if the object is meant to be explicitly tagged - * false otherwise. - * @exception ArgumentException if the tagged object cannot - * be converted. + * @param declaredExplicit true if the object is meant to be explicitly tagged false otherwise. + * @exception ArgumentException if the tagged object cannot be converted. */ public static Asn1Set GetInstance(Asn1TaggedObject taggedObject, bool declaredExplicit) { - if (declaredExplicit) - { - if (!taggedObject.IsExplicit()) - throw new ArgumentException("object implicit - explicit expected."); - - return GetInstance(taggedObject.GetObject()); - } - - Asn1Object baseObject = taggedObject.GetObject(); - - // If parsed as explicit though declared implicit, it should have been a set of one - if (taggedObject.IsExplicit()) - { - if (taggedObject is BerTaggedObject) - return new BerSet(baseObject); - - return new DLSet(baseObject); - } - - if (baseObject is Asn1Set) - return (Asn1Set)baseObject; - - // Parser assumes implicit constructed encodings are sequences - if (baseObject is Asn1Sequence) - return ((Asn1Sequence)baseObject).ToAsn1Set(); - - throw new ArgumentException("illegal object in GetInstance: " + Platform.GetTypeName(taggedObject), - "taggedObject"); + return (Asn1Set)Meta.Instance.GetContextInstance(taggedObject, declaredExplicit); } // NOTE: Only non-readonly to support LazyDLSet diff --git a/crypto/src/asn1/Asn1Tag.cs b/crypto/src/asn1/Asn1Tag.cs new file mode 100644 index 000000000..b57174bdb --- /dev/null +++ b/crypto/src/asn1/Asn1Tag.cs @@ -0,0 +1,31 @@ +using System; + +namespace Org.BouncyCastle.Asn1 +{ + internal sealed class Asn1Tag + { + internal static Asn1Tag Create(int tagClass, int tagNo) + { + return new Asn1Tag(tagClass, tagNo); + } + + private readonly int m_tagClass; + private readonly int m_tagNo; + + private Asn1Tag(int tagClass, int tagNo) + { + m_tagClass = tagClass; + m_tagNo = tagNo; + } + + internal int TagClass + { + get { return m_tagClass; } + } + + internal int TagNo + { + get { return m_tagNo; } + } + } +} diff --git a/crypto/src/asn1/Asn1TaggedObject.cs b/crypto/src/asn1/Asn1TaggedObject.cs index 7fafa4c52..66835ac14 100644 --- a/crypto/src/asn1/Asn1TaggedObject.cs +++ b/crypto/src/asn1/Asn1TaggedObject.cs @@ -238,19 +238,19 @@ namespace Org.BouncyCastle.Asn1 { switch (explicitness) { - case DeclaredImplicit: - { - Asn1Object baseObject = obj.ToAsn1Object(); - if (baseObject is Asn1Sequence || baseObject is Asn1Set) - return true; - - Asn1TaggedObject baseTagged = baseObject as Asn1TaggedObject; - return null != baseTagged && baseTagged.IsConstructed(); - } - case ParsedImplicit: - return obj is Asn1Sequence; - default: + case DeclaredImplicit: + { + Asn1Object baseObject = obj.ToAsn1Object(); + if (baseObject is Asn1Sequence || baseObject is Asn1Set) return true; + + Asn1TaggedObject baseTagged = baseObject as Asn1TaggedObject; + return null != baseTagged && baseTagged.IsConstructed(); + } + case ParsedImplicit: + return obj is Asn1Sequence; + default: + return true; } } @@ -315,13 +315,7 @@ namespace Org.BouncyCastle.Asn1 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; + return Asn1Utilities.CheckTag(declared, baseTagClass, baseTagNo); } // Parsed; return a virtual tag (i.e. that couldn't have been present in the encoding) @@ -330,6 +324,45 @@ namespace Org.BouncyCastle.Asn1 } } + public Asn1Object GetBaseUniversal(bool declaredExplicit, int tagNo) + { + Asn1UniversalType universalType = Asn1UniversalTypes.Get(tagNo); + if (null == universalType) + throw new ArgumentException("unsupported UNIVERSAL tag number: " + tagNo, "tagNo"); + + return GetBaseUniversal(declaredExplicit, universalType); + } + + internal Asn1Object GetBaseUniversal(bool declaredExplicit, Asn1UniversalType universalType) + { + if (declaredExplicit) + { + if (!IsExplicit()) + throw new InvalidOperationException("object explicit - implicit expected."); + + return universalType.CheckedCast(obj.ToAsn1Object()); + } + + if (DeclaredExplicit == explicitness) + throw new InvalidOperationException("object explicit - implicit expected."); + + Asn1Object baseObject = obj.ToAsn1Object(); + switch (explicitness) + { + case ParsedExplicit: + return universalType.FromImplicitConstructed(RebuildConstructed(baseObject)); + case ParsedImplicit: + { + if (baseObject is Asn1Sequence) + return universalType.FromImplicitConstructed((Asn1Sequence)baseObject); + + return universalType.FromImplicitPrimitive((DerOctetString)baseObject); + } + default: + return universalType.CheckedCast(baseObject); + } + } + /** * 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 diff --git a/crypto/src/asn1/Asn1Type.cs b/crypto/src/asn1/Asn1Type.cs new file mode 100644 index 000000000..f44b74e64 --- /dev/null +++ b/crypto/src/asn1/Asn1Type.cs @@ -0,0 +1,29 @@ +using System; + +namespace Org.BouncyCastle.Asn1 +{ + internal abstract class Asn1Type + { + internal readonly Type m_platformType; + + internal Asn1Type(Type platformType) + { + m_platformType = platformType; + } + + internal Type PlatformType + { + get { return m_platformType; } + } + + public sealed override bool Equals(object that) + { + return this == that; + } + + public sealed override int GetHashCode() + { + return base.GetHashCode(); + } + } +} diff --git a/crypto/src/asn1/Asn1UniversalType.cs b/crypto/src/asn1/Asn1UniversalType.cs new file mode 100644 index 000000000..46cacb436 --- /dev/null +++ b/crypto/src/asn1/Asn1UniversalType.cs @@ -0,0 +1,56 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Asn1 +{ + internal abstract class Asn1UniversalType + : Asn1Type + { + internal readonly Asn1Tag m_tag; + + internal Asn1UniversalType(Type platformType, int tagNo) + : base(platformType) + { + m_tag = Asn1Tag.Create(Asn1Tags.Universal, tagNo); + } + + internal Asn1Object CheckedCast(Asn1Object asn1Object) + { + if (PlatformType.IsInstanceOfType(asn1Object)) + return asn1Object; + + throw new InvalidOperationException("unexpected object: " + Platform.GetTypeName(asn1Object)); + } + + internal virtual Asn1Object FromImplicitPrimitive(DerOctetString octetString) + { + throw new InvalidOperationException("unexpected implicit primitive encoding"); + } + + internal virtual Asn1Object FromImplicitConstructed(Asn1Sequence sequence) + { + throw new InvalidOperationException("unexpected implicit constructed encoding"); + } + + /// <exception cref="IOException"/> + internal Asn1Object FromByteArray(byte[] bytes) + { + return CheckedCast(Asn1Object.FromByteArray(bytes)); + } + + internal Asn1Object GetContextInstance(Asn1TaggedObject taggedObject, bool declaredExplicit) + { + if (Asn1Tags.ContextSpecific != taggedObject.TagClass) + throw new InvalidOperationException("this method only valid for CONTEXT_SPECIFIC tags"); + + return CheckedCast(taggedObject.GetBaseUniversal(declaredExplicit, this)); + } + + internal Asn1Tag Tag + { + get { return m_tag; } + } + } +} diff --git a/crypto/src/asn1/Asn1UniversalTypes.cs b/crypto/src/asn1/Asn1UniversalTypes.cs new file mode 100644 index 000000000..214918bcd --- /dev/null +++ b/crypto/src/asn1/Asn1UniversalTypes.cs @@ -0,0 +1,74 @@ +using System; + +namespace Org.BouncyCastle.Asn1 +{ + internal sealed class Asn1UniversalTypes + { + private Asn1UniversalTypes() + { + } + + internal static Asn1UniversalType Get(int tagNo) + { + switch (tagNo) + { + case Asn1Tags.Boolean: + return DerBoolean.Meta.Instance; + case Asn1Tags.Integer: + return DerInteger.Meta.Instance; + case Asn1Tags.BitString: + return DerBitString.Meta.Instance; + case Asn1Tags.OctetString: + return Asn1OctetString.Meta.Instance; + case Asn1Tags.Null: + return Asn1Null.Meta.Instance; + case Asn1Tags.ObjectIdentifier: + return DerObjectIdentifier.Meta.Instance; + case Asn1Tags.ObjectDescriptor: // [UNIVERSAL 7] IMPLICIT GraphicString + return Asn1ObjectDescriptor.Meta.Instance; + case Asn1Tags.External: + return DerExternal.Meta.Instance; + case Asn1Tags.Enumerated: + return DerEnumerated.Meta.Instance; + case Asn1Tags.Utf8String: // [UNIVERSAL 12] IMPLICIT OCTET STRING (encode as if) + return DerUtf8String.Meta.Instance; + case Asn1Tags.RelativeOid: + return Asn1RelativeOid.Meta.Instance; + case Asn1Tags.Sequence: + return Asn1Sequence.Meta.Instance; + case Asn1Tags.Set: + return Asn1Set.Meta.Instance; + case Asn1Tags.NumericString: // [UNIVERSAL 18] IMPLICIT OCTET STRING (encode as if) + return DerNumericString.Meta.Instance; + case Asn1Tags.PrintableString: // [UNIVERSAL 19] IMPLICIT OCTET STRING (encode as if) + return DerPrintableString.Meta.Instance; + case Asn1Tags.T61String: // [UNIVERSAL 20] IMPLICIT OCTET STRING (encode as if) + return DerT61String.Meta.Instance; + case Asn1Tags.VideotexString: // [UNIVERSAL 21] IMPLICIT OCTET STRING (encode as if) + return DerVideotexString.Meta.Instance; + case Asn1Tags.IA5String: // [UNIVERSAL 22] IMPLICIT OCTET STRING (encode as if) + return DerIA5String.Meta.Instance; + case Asn1Tags.UtcTime: // [UNIVERSAL 23] IMPLICIT VisibleString (restricted values) + return DerUtcTime.Meta.Instance; + case Asn1Tags.GeneralizedTime: // [UNIVERSAL 24] IMPLICIT VisibleString (restricted values) + return DerGeneralizedTime.Meta.Instance; + case Asn1Tags.GraphicString: // [UNIVERSAL 25] IMPLICIT OCTET STRING (encode as if) + return DerGraphicString.Meta.Instance; + case Asn1Tags.VisibleString: // [UNIVERSAL 26] IMPLICIT OCTET STRING (encode as if) + return DerVisibleString.Meta.Instance; + case Asn1Tags.GeneralString: // [UNIVERSAL 27] IMPLICIT OCTET STRING (encode as if) + return DerGeneralString.Meta.Instance; + case Asn1Tags.UniversalString: // [UNIVERSAL 28] IMPLICIT OCTET STRING (encode as if) + return DerUniversalString.Meta.Instance; + case Asn1Tags.BmpString: // [UNIVERSAL 30] IMPLICIT OCTET STRING (encode as if) + return DerBmpString.Meta.Instance; + + case Asn1Tags.Real: + case Asn1Tags.EmbeddedPdv: + case Asn1Tags.UnrestrictedString: + default: + return null; + } + } + } +} diff --git a/crypto/src/asn1/Asn1Utilities.cs b/crypto/src/asn1/Asn1Utilities.cs index fe6db2df7..7b014ef00 100644 --- a/crypto/src/asn1/Asn1Utilities.cs +++ b/crypto/src/asn1/Asn1Utilities.cs @@ -4,6 +4,23 @@ namespace Org.BouncyCastle.Asn1 { public abstract class Asn1Utilities { + internal static Asn1TaggedObject CheckTag(Asn1TaggedObject taggedObject, int tagClass, int tagNo) + { + if (!taggedObject.HasTag(tagClass, tagNo)) + { + string expected = GetTagText(tagClass, tagNo); + string found = GetTagText(taggedObject); + throw new InvalidOperationException("Expected " + expected + " tag but found " + found); + } + return taggedObject; + } + + + internal static string GetTagText(Asn1Tag tag) + { + return GetTagText(tag.TagClass, tag.TagNo); + } + public static string GetTagText(Asn1TaggedObject taggedObject) { return GetTagText(taggedObject.TagClass, taggedObject.TagNo); @@ -24,20 +41,14 @@ namespace Org.BouncyCastle.Asn1 } } + /* * Wrappers for Asn1TaggedObject.GetExplicitBaseObject */ public static Asn1Encodable GetExplicitBaseObject(Asn1TaggedObject taggedObject, int tagClass, int tagNo) { - if (!taggedObject.HasTag(tagClass, tagNo)) - { - string expected = GetTagText(tagClass, tagNo); - string found = GetTagText(taggedObject); - throw new InvalidOperationException("Expected " + expected + " tag but found " + found); - } - - return taggedObject.GetExplicitBaseObject(); + return CheckTag(taggedObject, tagClass, tagNo).GetExplicitBaseObject(); } public static Asn1Encodable GetExplicitContextBaseObject(Asn1TaggedObject taggedObject, int tagNo) @@ -65,14 +76,7 @@ namespace Org.BouncyCastle.Asn1 public static Asn1TaggedObject GetExplicitBaseTagged(Asn1TaggedObject taggedObject, int tagClass, int tagNo) { - if (!taggedObject.HasTag(tagClass, tagNo)) - { - string expected = GetTagText(tagClass, tagNo); - string found = GetTagText(taggedObject); - throw new InvalidOperationException("Expected " + expected + " tag but found " + found); - } - - return taggedObject.GetExplicitBaseTagged(); + return CheckTag(taggedObject, tagClass, tagNo).GetExplicitBaseTagged(); } public static Asn1TaggedObject GetExplicitContextBaseTagged(Asn1TaggedObject taggedObject, int tagNo) @@ -92,5 +96,73 @@ namespace Org.BouncyCastle.Asn1 { return TryGetExplicitBaseTagged(taggedObject, Asn1Tags.ContextSpecific, tagNo); } + + + /* + * Wrappers for Asn1TaggedObject.GetImplicitBaseTagged + */ + + public static Asn1TaggedObject GetImplicitBaseTagged(Asn1TaggedObject taggedObject, int tagClass, int tagNo, + int baseTagClass, int baseTagNo) + { + return CheckTag(taggedObject, tagClass, tagNo).GetImplicitBaseTagged(baseTagClass, baseTagNo); + } + + public static Asn1TaggedObject GetImplicitContextBaseTagged(Asn1TaggedObject taggedObject, int tagNo, + int baseTagClass, int baseTagNo) + { + return GetImplicitBaseTagged(taggedObject, Asn1Tags.ContextSpecific, tagNo, baseTagClass, baseTagNo); + } + + public static Asn1TaggedObject TryGetImplicitBaseTagged(Asn1TaggedObject taggedObject, int tagClass, int tagNo, + int baseTagClass, int baseTagNo) + { + if (!taggedObject.HasTag(tagClass, tagNo)) + { + return null; + } + + return taggedObject.GetImplicitBaseTagged(baseTagClass, baseTagNo); + } + + public static Asn1TaggedObject TryGetImplicitContextBaseTagged(Asn1TaggedObject taggedObject, int tagNo, + int baseTagClass, int baseTagNo) + { + return TryGetImplicitBaseTagged(taggedObject, Asn1Tags.ContextSpecific, tagNo, baseTagClass, baseTagNo); + } + + + /* + * Wrappers for Asn1TaggedObject.GetBaseUniversal + */ + + public static Asn1Object GetBaseUniversal(Asn1TaggedObject taggedObject, int tagClass, int tagNo, + bool declaredExplicit, int baseTagNo) + { + return CheckTag(taggedObject, tagClass, tagNo).GetBaseUniversal(declaredExplicit, baseTagNo); + } + + public static Asn1Object GetContextBaseUniversal(Asn1TaggedObject taggedObject, int tagNo, + bool declaredExplicit, int baseTagNo) + { + return GetBaseUniversal(taggedObject, Asn1Tags.ContextSpecific, tagNo, declaredExplicit, baseTagNo); + } + + public static Asn1Object TryGetBaseUniversal(Asn1TaggedObject taggedObject, int tagClass, int tagNo, + bool declaredExplicit, int baseTagNo) + { + if (!taggedObject.HasTag(tagClass, tagNo)) + { + return null; + } + + return taggedObject.GetBaseUniversal(declaredExplicit, baseTagNo); + } + + public static Asn1Object TryGetContextBaseUniversal(Asn1TaggedObject taggedObject, int tagNo, + bool declaredExplicit, int baseTagNo) + { + return TryGetBaseUniversal(taggedObject, Asn1Tags.ContextSpecific, tagNo, declaredExplicit, baseTagNo); + } } } diff --git a/crypto/src/asn1/DERExternal.cs b/crypto/src/asn1/DERExternal.cs index 67dad0534..28d5cc46a 100644 --- a/crypto/src/asn1/DERExternal.cs +++ b/crypto/src/asn1/DERExternal.cs @@ -11,6 +11,18 @@ namespace Org.BouncyCastle.Asn1 public class DerExternal : Asn1Object { + internal class Meta : Asn1UniversalType + { + internal static readonly Asn1UniversalType Instance = new Meta(); + + private Meta() : base(typeof(DerExternal), Asn1Tags.External) {} + + internal override Asn1Object FromImplicitConstructed(Asn1Sequence sequence) + { + return sequence.ToAsn1External(); + } + } + public static DerExternal GetInstance(object obj) { if (obj == null || obj is DerExternal) @@ -27,7 +39,7 @@ namespace Org.BouncyCastle.Asn1 { try { - return GetInstance(FromByteArray((byte[])obj)); + return (DerExternal)Meta.Instance.FromByteArray((byte[])obj); } catch (IOException e) { @@ -38,16 +50,9 @@ namespace Org.BouncyCastle.Asn1 throw new ArgumentException("illegal object in GetInstance: " + Platform.GetTypeName(obj), "obj"); } - public static DerExternal GetInstance(Asn1TaggedObject taggedObject, bool isExplicit) + public static DerExternal GetInstance(Asn1TaggedObject taggedObject, bool declaredExplicit) { - Asn1Object baseObject = taggedObject.GetObject(); - - if (isExplicit || baseObject is DerExternal) - { - return GetInstance(baseObject); - } - - return Asn1Sequence.GetInstance(baseObject).ToAsn1External(); + return (DerExternal)Meta.Instance.GetContextInstance(taggedObject, declaredExplicit); } private readonly DerObjectIdentifier directReference; diff --git a/crypto/src/asn1/DerApplicationSpecific.cs b/crypto/src/asn1/DerApplicationSpecific.cs index 13b725325..15a4cdcc2 100644 --- a/crypto/src/asn1/DerApplicationSpecific.cs +++ b/crypto/src/asn1/DerApplicationSpecific.cs @@ -94,6 +94,7 @@ namespace Org.BouncyCastle.Asn1 get { return m_taggedObject.TagNo; } } + [Obsolete("Will be removed")] public byte[] GetContents() { return m_taggedObject.GetContents(); @@ -112,21 +113,7 @@ namespace Org.BouncyCastle.Asn1 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); + return m_taggedObject.GetBaseUniversal(false, tagNo); } public bool HasApplicationTag(int tagNo) @@ -186,34 +173,6 @@ namespace Org.BouncyCastle.Asn1 return m_taggedObject.GetEncodingImplicit(encoding, tagClass, tagNo); } - 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++]; - - // 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"); - - while ((b & 0x80) != 0) - { - 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; - } - private static int CheckTagClass(int tagClass) { if (Asn1Tags.Application != tagClass) diff --git a/crypto/src/asn1/DerBMPString.cs b/crypto/src/asn1/DerBMPString.cs index 4e35dfff8..a289eed1b 100644 --- a/crypto/src/asn1/DerBMPString.cs +++ b/crypto/src/asn1/DerBMPString.cs @@ -1,4 +1,5 @@ using System; +using System.IO; using Org.BouncyCastle.Utilities; @@ -10,7 +11,17 @@ namespace Org.BouncyCastle.Asn1 public class DerBmpString : DerStringBase { - private readonly string str; + internal class Meta : Asn1UniversalType + { + internal static readonly Asn1UniversalType Instance = new Meta(); + + private Meta() : base(typeof(DerBmpString), Asn1Tags.BmpString) {} + + internal override Asn1Object FromImplicitPrimitive(DerOctetString octetString) + { + return CreatePrimitive(octetString.GetOctets()); + } + } /** * return a BMP string from the given object. @@ -18,13 +29,29 @@ namespace Org.BouncyCastle.Asn1 * @param obj the object we want converted. * @exception ArgumentException if the object cannot be converted. */ - public static DerBmpString GetInstance( - object obj) + public static DerBmpString GetInstance(object obj) { if (obj == null || obj is DerBmpString) { return (DerBmpString)obj; } + else if (obj is IAsn1Convertible) + { + Asn1Object asn1Object = ((IAsn1Convertible)obj).ToAsn1Object(); + if (asn1Object is DerBmpString) + return (DerBmpString)asn1Object; + } + else if (obj is byte[]) + { + try + { + return (DerBmpString)Meta.Instance.FromByteArray((byte[])obj); + } + catch (IOException e) + { + throw new ArgumentException("failed to construct BMP string from byte[]: " + e.Message); + } + } throw new ArgumentException("illegal object in GetInstance: " + Platform.GetTypeName(obj)); } @@ -32,48 +59,35 @@ namespace Org.BouncyCastle.Asn1 /** * return a BMP string from a tagged object. * - * @param obj the tagged object holding the object we want - * @param explicitly true if the object is meant to be explicitly - * tagged false otherwise. - * @exception ArgumentException if the tagged object cannot - * be converted. + * @param taggedObject the tagged object holding the object we want + * @param declaredExplicit true if the object is meant to be explicitly tagged false otherwise. + * @exception ArgumentException if the tagged object cannot be converted. */ - public static DerBmpString GetInstance( - Asn1TaggedObject obj, - bool isExplicit) + public static DerBmpString GetInstance(Asn1TaggedObject taggedObject, bool declaredExplicit) { - Asn1Object o = obj.GetObject(); - - if (isExplicit || o is DerBmpString) - { - return GetInstance(o); - } - - return new DerBmpString(Asn1OctetString.GetInstance(o).GetOctets()); + return (DerBmpString)Meta.Instance.GetContextInstance(taggedObject, declaredExplicit); } - /** - * basic constructor - byte encoded string. - */ - [Obsolete("Will become internal")] - public DerBmpString(byte[] str) + private readonly string m_str; + + internal DerBmpString(byte[] contents) { - if (str == null) - throw new ArgumentNullException("str"); + if (null == contents) + throw new ArgumentNullException("contents"); - int byteLen = str.Length; + int byteLen = contents.Length; if (0 != (byteLen & 1)) - throw new ArgumentException("malformed BMPString encoding encountered", "str"); + throw new ArgumentException("malformed BMPString encoding encountered", "contents"); int charLen = byteLen / 2; char[] cs = new char[charLen]; for (int i = 0; i != charLen; i++) { - cs[i] = (char)((str[2 * i] << 8) | (str[2 * i + 1] & 0xff)); + cs[i] = (char)((contents[2 * i] << 8) | (contents[2 * i + 1] & 0xff)); } - this.str = new string(cs); + m_str = new string(cs); } internal DerBmpString(char[] str) @@ -81,7 +95,7 @@ namespace Org.BouncyCastle.Asn1 if (str == null) throw new ArgumentNullException("str"); - this.str = new string(str); + m_str = new string(str); } /** @@ -92,23 +106,24 @@ namespace Org.BouncyCastle.Asn1 if (str == null) throw new ArgumentNullException("str"); - this.str = str; + m_str = str; } public override string GetString() { - return str; + return m_str; } - protected override bool Asn1Equals( - Asn1Object asn1Object) + protected override bool Asn1Equals(Asn1Object asn1Object) { - DerBmpString other = asn1Object as DerBmpString; - - if (other == null) - return false; + DerBmpString that = asn1Object as DerBmpString; + return null != that + && this.m_str.Equals(that.m_str); + } - return this.str.Equals(other.str); + protected override int Asn1GetHashCode() + { + return m_str.GetHashCode(); } internal override IAsn1Encoding GetEncoding(int encoding) @@ -123,7 +138,7 @@ namespace Org.BouncyCastle.Asn1 private byte[] GetContents() { - char[] c = str.ToCharArray(); + char[] c = m_str.ToCharArray(); byte[] b = new byte[c.Length * 2]; for (int i = 0; i != c.Length; i++) @@ -134,5 +149,16 @@ namespace Org.BouncyCastle.Asn1 return b; } + + internal static DerBmpString CreatePrimitive(byte[] contents) + { + return new DerBmpString(contents); + } + + internal static DerBmpString CreatePrimitive(char[] str) + { + // TODO[asn1] Asn1InputStream has a validator/converter that should be unified in this class somehow + return new DerBmpString(str); + } } } diff --git a/crypto/src/asn1/DerBitString.cs b/crypto/src/asn1/DerBitString.cs index 375abb479..4596fbbc8 100644 --- a/crypto/src/asn1/DerBitString.cs +++ b/crypto/src/asn1/DerBitString.cs @@ -11,7 +11,24 @@ namespace Org.BouncyCastle.Asn1 public class DerBitString : DerStringBase { - private static readonly char[] table + internal class Meta : Asn1UniversalType + { + internal static readonly Asn1UniversalType Instance = new Meta(); + + private Meta() : base(typeof(DerBitString), Asn1Tags.BitString) { } + + internal override Asn1Object FromImplicitPrimitive(DerOctetString octetString) + { + return CreatePrimitive(octetString.GetOctets()); + } + + internal override Asn1Object FromImplicitConstructed(Asn1Sequence sequence) + { + return sequence.ToAsn1BitString(); + } + } + + private static readonly char[] table = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; /** @@ -323,9 +340,7 @@ namespace Org.BouncyCastle.Asn1 byte finalOctet = contents[length - 1]; if (finalOctet != (byte)(finalOctet & (0xFF << padBits))) - { return new BerBitString(contents, false); - } } return new DerBitString(contents, false); diff --git a/crypto/src/asn1/DerBoolean.cs b/crypto/src/asn1/DerBoolean.cs index 29e2d8bd9..ad578ae80 100644 --- a/crypto/src/asn1/DerBoolean.cs +++ b/crypto/src/asn1/DerBoolean.cs @@ -1,4 +1,5 @@ using System; +using System.IO; using Org.BouncyCastle.Utilities; @@ -7,7 +8,17 @@ namespace Org.BouncyCastle.Asn1 public class DerBoolean : Asn1Object { - private readonly byte value; + internal class Meta : Asn1UniversalType + { + internal static readonly Asn1UniversalType Instance = new Meta(); + + private Meta() : base(typeof(DerBoolean), Asn1Tags.Boolean) {} + + internal override Asn1Object FromImplicitPrimitive(DerOctetString octetString) + { + return CreatePrimitive(octetString.GetOctets()); + } + } public static readonly DerBoolean False = new DerBoolean(false); public static readonly DerBoolean True = new DerBoolean(true); @@ -17,49 +28,57 @@ namespace Org.BouncyCastle.Asn1 * * @exception ArgumentException if the object cannot be converted. */ - public static DerBoolean GetInstance( - object obj) + public static DerBoolean GetInstance(object obj) { if (obj == null || obj is DerBoolean) { - return (DerBoolean) obj; + return (DerBoolean)obj; + } + else if (obj is IAsn1Convertible) + { + Asn1Object asn1Object = ((IAsn1Convertible)obj).ToAsn1Object(); + if (asn1Object is DerBoolean) + return (DerBoolean)asn1Object; + } + else if (obj is byte[]) + { + try + { + return (DerBoolean)Meta.Instance.FromByteArray((byte[])obj); + } + catch (IOException e) + { + throw new ArgumentException("failed to construct boolean from byte[]: " + e.Message); + } } throw new ArgumentException("illegal object in GetInstance: " + Platform.GetTypeName(obj)); } - /** - * return a DerBoolean from the passed in bool. - */ - public static DerBoolean GetInstance( - bool value) + public static DerBoolean GetInstance(bool value) { return value ? True : False; } + public static DerBoolean GetInstance(int value) + { + return value != 0 ? True : False; + } + /** * return a Boolean from a tagged object. * - * @param obj the tagged object holding the object we want - * @param explicitly true if the object is meant to be explicitly - * tagged false otherwise. - * @exception ArgumentException if the tagged object cannot - * be converted. + * @param taggedObject the tagged object holding the object we want + * @param declaredExplicit true if the object is meant to be explicitly tagged false otherwise. + * @exception ArgumentException if the tagged object cannot be converted. */ - public static DerBoolean GetInstance( - Asn1TaggedObject obj, - bool isExplicit) + public static DerBoolean GetInstance(Asn1TaggedObject taggedObject, bool declaredExplicit) { - Asn1Object o = obj.GetObject(); - - if (isExplicit || o is DerBoolean) - { - return GetInstance(o); - } - - return FromOctetString(((Asn1OctetString)o).GetOctets()); + return (DerBoolean)Meta.Instance.GetContextInstance(taggedObject, declaredExplicit); } + private readonly byte value; + public DerBoolean( byte[] val) { @@ -112,16 +131,14 @@ namespace Org.BouncyCastle.Asn1 return IsTrue ? "TRUE" : "FALSE"; } - internal static DerBoolean FromOctetString(byte[] value) + internal static DerBoolean CreatePrimitive(byte[] contents) { - if (value.Length != 1) - { - throw new ArgumentException("BOOLEAN value should have 1 byte in it", "value"); - } + if (contents.Length != 1) + throw new ArgumentException("BOOLEAN value should have 1 byte in it", "contents"); - byte b = value[0]; + byte b = contents[0]; - return b == 0 ? False : b == 0xFF ? True : new DerBoolean(value); + return b == 0 ? False : b == 0xFF ? True : new DerBoolean(contents); } private byte[] GetContents(int encoding) diff --git a/crypto/src/asn1/DerEnumerated.cs b/crypto/src/asn1/DerEnumerated.cs index 6fd2f9b62..920b3dc8e 100644 --- a/crypto/src/asn1/DerEnumerated.cs +++ b/crypto/src/asn1/DerEnumerated.cs @@ -1,4 +1,5 @@ using System; +using System.IO; using Org.BouncyCastle.Math; using Org.BouncyCastle.Utilities; @@ -8,21 +9,46 @@ namespace Org.BouncyCastle.Asn1 public class DerEnumerated : Asn1Object { - private readonly byte[] bytes; - private readonly int start; + internal class Meta : Asn1UniversalType + { + internal static readonly Asn1UniversalType Instance = new Meta(); + + private Meta() : base(typeof(DerEnumerated), Asn1Tags.Enumerated) {} + + internal override Asn1Object FromImplicitPrimitive(DerOctetString octetString) + { + return CreatePrimitive(octetString.GetOctets(), false); + } + } /** * return an integer from the passed in object * * @exception ArgumentException if the object cannot be converted. */ - public static DerEnumerated GetInstance( - object obj) + public static DerEnumerated GetInstance(object obj) { if (obj == null || obj is DerEnumerated) { return (DerEnumerated)obj; } + else if (obj is IAsn1Convertible) + { + Asn1Object asn1Object = ((IAsn1Convertible)obj).ToAsn1Object(); + if (asn1Object is DerEnumerated) + return (DerEnumerated)asn1Object; + } + else if (obj is byte[]) + { + try + { + return (DerEnumerated)Meta.Instance.FromByteArray((byte[])obj); + } + catch (IOException e) + { + throw new ArgumentException("failed to construct enumerated from byte[]: " + e.Message); + } + } throw new ArgumentException("illegal object in GetInstance: " + Platform.GetTypeName(obj)); } @@ -30,32 +56,24 @@ namespace Org.BouncyCastle.Asn1 /** * return an Enumerated from a tagged object. * - * @param obj the tagged object holding the object we want - * @param explicitly true if the object is meant to be explicitly - * tagged false otherwise. - * @exception ArgumentException if the tagged object cannot - * be converted. + * @param taggedObject the tagged object holding the object we want + * @param declaredExplicit true if the object is meant to be explicitly tagged false otherwise. + * @exception ArgumentException if the tagged object cannot be converted. */ - public static DerEnumerated GetInstance( - Asn1TaggedObject obj, - bool isExplicit) + public static DerEnumerated GetInstance(Asn1TaggedObject taggedObject, bool declaredExplicit) { - Asn1Object o = obj.GetObject(); - - if (isExplicit || o is DerEnumerated) - { - return GetInstance(o); - } - - return FromOctetString(((Asn1OctetString)o).GetOctets()); + return (DerEnumerated)Meta.Instance.GetContextInstance(taggedObject, declaredExplicit); } + private readonly byte[] contents; + private readonly int start; + public DerEnumerated(int val) { if (val < 0) throw new ArgumentException("enumerated must be non-negative", "val"); - this.bytes = BigInteger.ValueOf(val).ToByteArray(); + this.contents = BigInteger.ValueOf(val).ToByteArray(); this.start = 0; } @@ -64,7 +82,7 @@ namespace Org.BouncyCastle.Asn1 if (val < 0L) throw new ArgumentException("enumerated must be non-negative", "val"); - this.bytes = BigInteger.ValueOf(val).ToByteArray(); + this.contents = BigInteger.ValueOf(val).ToByteArray(); this.start = 0; } @@ -73,37 +91,42 @@ namespace Org.BouncyCastle.Asn1 if (val.SignValue < 0) throw new ArgumentException("enumerated must be non-negative", "val"); - this.bytes = val.ToByteArray(); + this.contents = val.ToByteArray(); this.start = 0; } - public DerEnumerated(byte[] bytes) + public DerEnumerated(byte[] contents) + : this(contents, true) + { + } + + internal DerEnumerated(byte[] contents, bool clone) { - if (DerInteger.IsMalformed(bytes)) - throw new ArgumentException("malformed enumerated", "bytes"); - if (0 != (bytes[0] & 0x80)) - throw new ArgumentException("enumerated must be non-negative", "bytes"); + if (DerInteger.IsMalformed(contents)) + throw new ArgumentException("malformed enumerated", "contents"); + if (0 != (contents[0] & 0x80)) + throw new ArgumentException("enumerated must be non-negative", "contents"); - this.bytes = Arrays.Clone(bytes); - this.start = DerInteger.SignBytesToSkip(bytes); + this.contents = clone ? Arrays.Clone(contents) : contents; + this.start = DerInteger.SignBytesToSkip(this.contents); } public BigInteger Value { - get { return new BigInteger(bytes); } + get { return new BigInteger(contents); } } public bool HasValue(int x) { - return (bytes.Length - start) <= 4 - && DerInteger.IntValue(bytes, start, DerInteger.SignExtSigned) == x; + return (contents.Length - start) <= 4 + && DerInteger.IntValue(contents, start, DerInteger.SignExtSigned) == x; } public bool HasValue(BigInteger x) { return null != x // Fast check to avoid allocation - && DerInteger.IntValue(bytes, start, DerInteger.SignExtSigned) == x.IntValue + && DerInteger.IntValue(contents, start, DerInteger.SignExtSigned) == x.IntValue && Value.Equals(x); } @@ -111,22 +134,22 @@ namespace Org.BouncyCastle.Asn1 { get { - int count = bytes.Length - start; + int count = contents.Length - start; if (count > 4) throw new ArithmeticException("ASN.1 Enumerated out of int range"); - return DerInteger.IntValue(bytes, start, DerInteger.SignExtSigned); + return DerInteger.IntValue(contents, start, DerInteger.SignExtSigned); } } internal override IAsn1Encoding GetEncoding(int encoding) { - return new PrimitiveEncoding(Asn1Tags.Universal, Asn1Tags.Enumerated, bytes); + return new PrimitiveEncoding(Asn1Tags.Universal, Asn1Tags.Enumerated, contents); } internal override IAsn1Encoding GetEncodingImplicit(int encoding, int tagClass, int tagNo) { - return new PrimitiveEncoding(tagClass, tagNo, bytes); + return new PrimitiveEncoding(tagClass, tagNo, contents); } protected override bool Asn1Equals(Asn1Object asn1Object) @@ -135,31 +158,31 @@ namespace Org.BouncyCastle.Asn1 if (other == null) return false; - return Arrays.AreEqual(this.bytes, other.bytes); + return Arrays.AreEqual(this.contents, other.contents); } protected override int Asn1GetHashCode() { - return Arrays.GetHashCode(bytes); + return Arrays.GetHashCode(contents); } private static readonly DerEnumerated[] cache = new DerEnumerated[12]; - internal static DerEnumerated FromOctetString(byte[] enc) + internal static DerEnumerated CreatePrimitive(byte[] contents, bool clone) { - if (enc.Length > 1) - return new DerEnumerated(enc); - if (enc.Length == 0) - throw new ArgumentException("ENUMERATED has zero length", "enc"); + if (contents.Length > 1) + return new DerEnumerated(contents, clone); + if (contents.Length == 0) + throw new ArgumentException("ENUMERATED has zero length", "contents"); - int value = enc[0]; + int value = contents[0]; if (value >= cache.Length) - return new DerEnumerated(enc); + return new DerEnumerated(contents, clone); DerEnumerated possibleMatch = cache[value]; if (possibleMatch == null) { - cache[value] = possibleMatch = new DerEnumerated(enc); + cache[value] = possibleMatch = new DerEnumerated(contents, clone); } return possibleMatch; } diff --git a/crypto/src/asn1/DerGeneralString.cs b/crypto/src/asn1/DerGeneralString.cs index f2a47c370..e6637732a 100644 --- a/crypto/src/asn1/DerGeneralString.cs +++ b/crypto/src/asn1/DerGeneralString.cs @@ -1,5 +1,5 @@ using System; -using System.Text; +using System.IO; using Org.BouncyCastle.Utilities; @@ -8,78 +8,108 @@ namespace Org.BouncyCastle.Asn1 public class DerGeneralString : DerStringBase { - private readonly string str; + internal class Meta : Asn1UniversalType + { + internal static readonly Asn1UniversalType Instance = new Meta(); + + private Meta() : base(typeof(DerGeneralString), Asn1Tags.GeneralString) {} + + internal override Asn1Object FromImplicitPrimitive(DerOctetString octetString) + { + return CreatePrimitive(octetString.GetOctets()); + } + } - public static DerGeneralString GetInstance( - object obj) + public static DerGeneralString GetInstance(object obj) { if (obj == null || obj is DerGeneralString) { return (DerGeneralString) obj; } + else if (obj is IAsn1Convertible) + { + Asn1Object asn1Object = ((IAsn1Convertible)obj).ToAsn1Object(); + if (asn1Object is DerGeneralString) + return (DerGeneralString)asn1Object; + } + else if (obj is byte[]) + { + try + { + return (DerGeneralString)Meta.Instance.FromByteArray((byte[])obj); + } + catch (IOException e) + { + throw new ArgumentException("failed to construct general string from byte[]: " + e.Message); + } + } - throw new ArgumentException("illegal object in GetInstance: " - + Platform.GetTypeName(obj)); + throw new ArgumentException("illegal object in GetInstance: " + Platform.GetTypeName(obj)); } - public static DerGeneralString GetInstance( - Asn1TaggedObject obj, - bool isExplicit) + public static DerGeneralString GetInstance(Asn1TaggedObject taggedObject, bool declaredExplicit) { - Asn1Object o = obj.GetObject(); + return (DerGeneralString)Meta.Instance.GetContextInstance(taggedObject, declaredExplicit); + } - if (isExplicit || o is DerGeneralString) - { - return GetInstance(o); - } + private readonly byte[] m_contents; - return new DerGeneralString(((Asn1OctetString)o).GetOctets()); + public DerGeneralString(string str) + { + if (str == null) + throw new ArgumentNullException("str"); + + m_contents = Strings.ToAsciiByteArray(str); } - public DerGeneralString( - byte[] str) - : this(Strings.FromAsciiByteArray(str)) + public DerGeneralString(byte[] contents) + : this(contents, true) { } - public DerGeneralString( - string str) + internal DerGeneralString(byte[] contents, bool clone) { - if (str == null) - throw new ArgumentNullException("str"); + if (null == contents) + throw new ArgumentNullException("contents"); - this.str = str; + m_contents = clone ? Arrays.Clone(contents) : contents; } public override string GetString() { - return str; + return Strings.FromAsciiByteArray(m_contents); } public byte[] GetOctets() { - return Strings.ToAsciiByteArray(str); + return Arrays.Clone(m_contents); } internal override IAsn1Encoding GetEncoding(int encoding) { - return new PrimitiveEncoding(Asn1Tags.Universal, Asn1Tags.GeneralString, GetOctets()); + return new PrimitiveEncoding(Asn1Tags.Universal, Asn1Tags.GeneralString, m_contents); } internal override IAsn1Encoding GetEncodingImplicit(int encoding, int tagClass, int tagNo) { - return new PrimitiveEncoding(tagClass, tagNo, GetOctets()); + return new PrimitiveEncoding(tagClass, tagNo, m_contents); } - protected override bool Asn1Equals( - Asn1Object asn1Object) + protected override bool Asn1Equals(Asn1Object asn1Object) { - DerGeneralString other = asn1Object as DerGeneralString; + DerGeneralString that = asn1Object as DerGeneralString; + return null != that + && Arrays.AreEqual(this.m_contents, that.m_contents); + } - if (other == null) - return false; + protected override int Asn1GetHashCode() + { + return Arrays.GetHashCode(m_contents); + } - return this.str.Equals(other.str); + internal static DerGeneralString CreatePrimitive(byte[] contents) + { + return new DerGeneralString(contents, false); } } } diff --git a/crypto/src/asn1/DerGeneralizedTime.cs b/crypto/src/asn1/DerGeneralizedTime.cs index 41c897751..ed2cb5e62 100644 --- a/crypto/src/asn1/DerGeneralizedTime.cs +++ b/crypto/src/asn1/DerGeneralizedTime.cs @@ -1,5 +1,6 @@ using System; using System.Globalization; +using System.IO; using System.Text; using Org.BouncyCastle.Utilities; @@ -12,47 +13,64 @@ namespace Org.BouncyCastle.Asn1 public class DerGeneralizedTime : Asn1Object { - private readonly string time; + internal class Meta : Asn1UniversalType + { + internal static readonly Asn1UniversalType Instance = new Meta(); + + private Meta() : base(typeof(DerGeneralizedTime), Asn1Tags.GeneralizedTime) {} + + internal override Asn1Object FromImplicitPrimitive(DerOctetString octetString) + { + return CreatePrimitive(octetString.GetOctets()); + } + } /** * return a generalized time from the passed in object * * @exception ArgumentException if the object cannot be converted. */ - public static DerGeneralizedTime GetInstance( - object obj) + public static DerGeneralizedTime GetInstance(object obj) { if (obj == null || obj is DerGeneralizedTime) { return (DerGeneralizedTime)obj; } + else if (obj is IAsn1Convertible) + { + Asn1Object asn1Object = ((IAsn1Convertible)obj).ToAsn1Object(); + if (asn1Object is DerGeneralizedTime) + return (DerGeneralizedTime)asn1Object; + } + else if (obj is byte[]) + { + try + { + return (DerGeneralizedTime)Meta.Instance.FromByteArray((byte[])obj); + } + catch (IOException e) + { + throw new ArgumentException("failed to construct generalized time from byte[]: " + e.Message); + } + } throw new ArgumentException("illegal object in GetInstance: " + Platform.GetTypeName(obj), "obj"); } /** - * return a Generalized Time object from a tagged object. + * return a generalized Time object from a tagged object. * - * @param obj the tagged object holding the object we want - * @param explicitly true if the object is meant to be explicitly - * tagged false otherwise. - * @exception ArgumentException if the tagged object cannot - * be converted. + * @param taggedObject the tagged object holding the object we want + * @param declaredExplicit true if the object is meant to be explicitly tagged false otherwise. + * @exception ArgumentException if the tagged object cannot be converted. */ - public static DerGeneralizedTime GetInstance( - Asn1TaggedObject obj, - bool isExplicit) + public static DerGeneralizedTime GetInstance(Asn1TaggedObject taggedObject, bool declaredExplicit) { - Asn1Object o = obj.GetObject(); - - if (isExplicit || o is DerGeneralizedTime) - { - return GetInstance(o); - } - - return new DerGeneralizedTime(((Asn1OctetString)o).GetOctets()); + return (DerGeneralizedTime)Meta.Instance.GetContextInstance(taggedObject, declaredExplicit); } + private readonly string time; + /** * The correct format for this is YYYYMMDDHHMMSS[.f]Z, or without the Z * for local time, or Z+-HHMM on the end, for difference between local @@ -305,20 +323,21 @@ namespace Org.BouncyCastle.Asn1 return new PrimitiveEncoding(tagClass, tagNo, GetOctets()); } - protected override bool Asn1Equals( - Asn1Object asn1Object) + protected override bool Asn1Equals(Asn1Object asn1Object) { - DerGeneralizedTime other = asn1Object as DerGeneralizedTime; - - if (other == null) - return false; - - return this.time.Equals(other.time); + DerGeneralizedTime that = asn1Object as DerGeneralizedTime; + return null != that + && this.time.Equals(that.time); } protected override int Asn1GetHashCode() { return time.GetHashCode(); } + + internal static DerGeneralizedTime CreatePrimitive(byte[] contents) + { + return new DerGeneralizedTime(contents); + } } } diff --git a/crypto/src/asn1/DerGraphicString.cs b/crypto/src/asn1/DerGraphicString.cs index 52ccb7e93..cb32d14eb 100644 --- a/crypto/src/asn1/DerGraphicString.cs +++ b/crypto/src/asn1/DerGraphicString.cs @@ -1,4 +1,5 @@ using System; +using System.IO; using Org.BouncyCastle.Utilities; @@ -7,6 +8,18 @@ namespace Org.BouncyCastle.Asn1 public class DerGraphicString : DerStringBase { + internal class Meta : Asn1UniversalType + { + internal static readonly Asn1UniversalType Instance = new Meta(); + + private Meta() : base(typeof(DerGraphicString), Asn1Tags.GraphicString) {} + + internal override Asn1Object FromImplicitPrimitive(DerOctetString octetString) + { + return CreatePrimitive(octetString.GetOctets()); + } + } + /** * return a Graphic String from the passed in object * @@ -20,16 +33,21 @@ namespace Org.BouncyCastle.Asn1 { return (DerGraphicString)obj; } - - if (obj is byte[]) + else if (obj is IAsn1Convertible) + { + Asn1Object asn1Object = ((IAsn1Convertible)obj).ToAsn1Object(); + if (asn1Object is DerGraphicString) + return (DerGraphicString)asn1Object; + } + else if (obj is byte[]) { try { - return (DerGraphicString)FromByteArray((byte[])obj); + return (DerGraphicString)Meta.Instance.FromByteArray((byte[])obj); } - catch (Exception e) + catch (IOException e) { - throw new ArgumentException("encoding error in GetInstance: " + e.ToString(), "obj"); + throw new ArgumentException("failed to construct graphic string from byte[]: " + e.Message); } } @@ -39,23 +57,14 @@ namespace Org.BouncyCastle.Asn1 /** * return a Graphic String from a tagged object. * - * @param obj the tagged object holding the object we want - * @param explicit true if the object is meant to be explicitly - * tagged false otherwise. - * @exception IllegalArgumentException if the tagged object cannot - * be converted. + * @param taggedObject the tagged object holding the object we want + * @param declaredExplicit true if the object is meant to be explicitly tagged false otherwise. + * @exception IllegalArgumentException if the tagged object cannot be converted. * @return a DerGraphicString instance, or null. */ - public static DerGraphicString GetInstance(Asn1TaggedObject obj, bool isExplicit) + public static DerGraphicString GetInstance(Asn1TaggedObject taggedObject, bool declaredExplicit) { - Asn1Object o = obj.GetObject(); - - if (isExplicit || o is DerGraphicString) - { - return GetInstance(o); - } - - return new DerGraphicString(((Asn1OctetString)o).GetOctets()); + return (DerGraphicString)Meta.Instance.GetContextInstance(taggedObject, declaredExplicit); } private readonly byte[] m_contents; diff --git a/crypto/src/asn1/DerIA5String.cs b/crypto/src/asn1/DerIA5String.cs index d608930fd..a56879831 100644 --- a/crypto/src/asn1/DerIA5String.cs +++ b/crypto/src/asn1/DerIA5String.cs @@ -1,30 +1,56 @@ using System; -using System.Text; +using System.IO; using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Asn1 { /** - * Der IA5String object - this is an ascii string. + * IA5String object - this is an Ascii string. */ public class DerIA5String : DerStringBase { - private readonly string str; + internal class Meta : Asn1UniversalType + { + internal static readonly Asn1UniversalType Instance = new Meta(); + + private Meta() : base(typeof(DerIA5String), Asn1Tags.IA5String) {} + + internal override Asn1Object FromImplicitPrimitive(DerOctetString octetString) + { + return CreatePrimitive(octetString.GetOctets()); + } + } /** - * return a IA5 string from the passed in object + * return an IA5 string from the passed in object * * @exception ArgumentException if the object cannot be converted. */ - public static DerIA5String GetInstance( - object obj) + public static DerIA5String GetInstance(object obj) { if (obj == null || obj is DerIA5String) { return (DerIA5String)obj; } + else if (obj is IAsn1Convertible) + { + Asn1Object asn1Object = ((IAsn1Convertible)obj).ToAsn1Object(); + if (asn1Object is DerIA5String) + return (DerIA5String)asn1Object; + } + else if (obj is byte[]) + { + try + { + return (DerIA5String)Meta.Instance.FromByteArray((byte[])obj); + } + catch (IOException e) + { + throw new ArgumentException("failed to construct IA5 string from byte[]: " + e.Message); + } + } throw new ArgumentException("illegal object in GetInstance: " + Platform.GetTypeName(obj)); } @@ -32,40 +58,18 @@ namespace Org.BouncyCastle.Asn1 /** * return an IA5 string from a tagged object. * - * @param obj the tagged object holding the object we want - * @param explicitly true if the object is meant to be explicitly - * tagged false otherwise. - * @exception ArgumentException if the tagged object cannot - * be converted. + * @param taggedObject the tagged object holding the object we want + * @param declaredExplicit true if the object is meant to be explicitly tagged false otherwise. + * @exception ArgumentException if the tagged object cannot be converted. */ - public static DerIA5String GetInstance( - Asn1TaggedObject obj, - bool isExplicit) + public static DerIA5String GetInstance(Asn1TaggedObject taggedObject, bool declaredExplicit) { - Asn1Object o = obj.GetObject(); - - if (isExplicit || o is DerIA5String) - { - return GetInstance(o); - } - - return new DerIA5String(((Asn1OctetString)o).GetOctets()); + return (DerIA5String)Meta.Instance.GetContextInstance(taggedObject, declaredExplicit); } - /** - * basic constructor - with bytes. - */ - public DerIA5String( - byte[] str) - : this(Strings.FromAsciiByteArray(str), false) - { - } + private readonly byte[] m_contents; - /** - * basic constructor - without validation. - */ - public DerIA5String( - string str) + public DerIA5String(string str) : this(str, false) { } @@ -78,72 +82,81 @@ namespace Org.BouncyCastle.Asn1 * @throws ArgumentException if validate is true and the string * contains characters that should not be in an IA5String. */ - public DerIA5String( - string str, - bool validate) + public DerIA5String(string str, bool validate) { if (str == null) throw new ArgumentNullException("str"); if (validate && !IsIA5String(str)) throw new ArgumentException("string contains illegal characters", "str"); - this.str = str; + m_contents = Strings.ToAsciiByteArray(str); } - public override string GetString() + public DerIA5String(byte[] contents) + : this(contents, true) + { + } + + internal DerIA5String(byte[] contents, bool clone) + { + if (null == contents) + throw new ArgumentNullException("contents"); + + m_contents = clone ? Arrays.Clone(contents) : contents; + } + + public override string GetString() { - return str; + return Strings.FromAsciiByteArray(m_contents); } public byte[] GetOctets() { - return Strings.ToAsciiByteArray(str); + return Arrays.Clone(m_contents); } internal override IAsn1Encoding GetEncoding(int encoding) { - return new PrimitiveEncoding(Asn1Tags.Universal, Asn1Tags.IA5String, GetOctets()); + return new PrimitiveEncoding(Asn1Tags.Universal, Asn1Tags.IA5String, m_contents); } internal override IAsn1Encoding GetEncodingImplicit(int encoding, int tagClass, int tagNo) { - return new PrimitiveEncoding(tagClass, tagNo, GetOctets()); + return new PrimitiveEncoding(tagClass, tagNo, m_contents); } - protected override int Asn1GetHashCode() - { - return this.str.GetHashCode(); + protected override bool Asn1Equals(Asn1Object asn1Object) + { + DerIA5String that = asn1Object as DerIA5String; + return null != that + && Arrays.AreEqual(this.m_contents, that.m_contents); } - protected override bool Asn1Equals( - Asn1Object asn1Object) + protected override int Asn1GetHashCode() { - DerIA5String other = asn1Object as DerIA5String; - - if (other == null) - return false; - - return this.str.Equals(other.str); + return Arrays.GetHashCode(m_contents); } - /** + /** * return true if the passed in String can be represented without * loss as an IA5String, false otherwise. * * @return true if in printable set, false otherwise. */ - public static bool IsIA5String( - string str) + public static bool IsIA5String(string str) { foreach (char ch in str) { if (ch > 0x007f) - { return false; - } } return true; } - } + + internal static DerIA5String CreatePrimitive(byte[] contents) + { + return new DerIA5String(contents, false); + } + } } diff --git a/crypto/src/asn1/DerInteger.cs b/crypto/src/asn1/DerInteger.cs index 8b112f693..c8d4e47df 100644 --- a/crypto/src/asn1/DerInteger.cs +++ b/crypto/src/asn1/DerInteger.cs @@ -1,4 +1,5 @@ using System; +using System.IO; using Org.BouncyCastle.Math; using Org.BouncyCastle.Utilities; @@ -8,6 +9,18 @@ namespace Org.BouncyCastle.Asn1 public class DerInteger : Asn1Object { + internal class Meta : Asn1UniversalType + { + internal static readonly Asn1UniversalType Instance = new Meta(); + + private Meta() : base(typeof(DerInteger), Asn1Tags.Integer) {} + + internal override Asn1Object FromImplicitPrimitive(DerOctetString octetString) + { + return CreatePrimitive(octetString.GetOctets()); + } + } + public const string AllowUnsafeProperty = "Org.BouncyCastle.Asn1.AllowUnsafeInteger"; internal static bool AllowUnsafe() @@ -27,13 +40,29 @@ namespace Org.BouncyCastle.Asn1 * * @exception ArgumentException if the object cannot be converted. */ - public static DerInteger GetInstance( - object obj) + public static DerInteger GetInstance(object obj) { if (obj == null || obj is DerInteger) { return (DerInteger)obj; } + else if (obj is IAsn1Convertible) + { + Asn1Object asn1Object = ((IAsn1Convertible)obj).ToAsn1Object(); + if (asn1Object is DerInteger) + return (DerInteger)asn1Object; + } + else if (obj is byte[]) + { + try + { + return (DerInteger)Meta.Instance.FromByteArray((byte[])obj); + } + catch (IOException e) + { + throw new ArgumentException("failed to construct integer from byte[]: " + e.Message); + } + } throw new ArgumentException("illegal object in GetInstance: " + Platform.GetTypeName(obj)); } @@ -41,27 +70,13 @@ namespace Org.BouncyCastle.Asn1 /** * return an Integer from a tagged object. * - * @param obj the tagged object holding the object we want - * @param isExplicit true if the object is meant to be explicitly - * tagged false otherwise. - * @exception ArgumentException if the tagged object cannot - * be converted. + * @param taggedObject the tagged object holding the object we want + * @param declaredExplicit true if the object is meant to be explicitly tagged false otherwise. + * @exception ArgumentException if the tagged object cannot be converted. */ - public static DerInteger GetInstance( - Asn1TaggedObject obj, - bool isExplicit) + public static DerInteger GetInstance(Asn1TaggedObject taggedObject, bool declaredExplicit) { - if (obj == null) - throw new ArgumentNullException("obj"); - - Asn1Object o = obj.GetObject(); - - if (isExplicit || o is DerInteger) - { - return GetInstance(o); - } - - return new DerInteger(Asn1OctetString.GetInstance(o).GetOctets()); + return (DerInteger)Meta.Instance.GetContextInstance(taggedObject, declaredExplicit); } public DerInteger(int value) @@ -198,6 +213,11 @@ namespace Org.BouncyCastle.Asn1 return Value.ToString(); } + internal static DerInteger CreatePrimitive(byte[] contents) + { + return new DerInteger(contents, false); + } + internal static int IntValue(byte[] bytes, int start, int signExt) { int length = bytes.Length; diff --git a/crypto/src/asn1/DerNumericString.cs b/crypto/src/asn1/DerNumericString.cs index bec0c3a52..693ff7d6e 100644 --- a/crypto/src/asn1/DerNumericString.cs +++ b/crypto/src/asn1/DerNumericString.cs @@ -1,5 +1,5 @@ using System; -using System.Text; +using System.IO; using Org.BouncyCastle.Utilities; @@ -11,66 +11,70 @@ namespace Org.BouncyCastle.Asn1 public class DerNumericString : DerStringBase { - private readonly string str; + internal class Meta : Asn1UniversalType + { + internal static readonly Asn1UniversalType Instance = new Meta(); + + private Meta() : base(typeof(DerNumericString), Asn1Tags.NumericString) {} + + internal override Asn1Object FromImplicitPrimitive(DerOctetString octetString) + { + return CreatePrimitive(octetString.GetOctets()); + } + } /** - * return a Numeric string from the passed in object + * return a numeric string from the passed in object * * @exception ArgumentException if the object cannot be converted. */ - public static DerNumericString GetInstance( - object obj) + public static DerNumericString GetInstance(object obj) { if (obj == null || obj is DerNumericString) { return (DerNumericString)obj; } + else if (obj is IAsn1Convertible) + { + Asn1Object asn1Object = ((IAsn1Convertible)obj).ToAsn1Object(); + if (asn1Object is DerNumericString) + return (DerNumericString)asn1Object; + } + else if (obj is byte[]) + { + try + { + return (DerNumericString)Meta.Instance.FromByteArray((byte[])obj); + } + catch (IOException e) + { + throw new ArgumentException("failed to construct numeric string from byte[]: " + e.Message); + } + } throw new ArgumentException("illegal object in GetInstance: " + Platform.GetTypeName(obj)); } /** - * return an Numeric string from a tagged object. + * return a numeric string from a tagged object. * - * @param obj the tagged object holding the object we want - * @param explicitly true if the object is meant to be explicitly - * tagged false otherwise. - * @exception ArgumentException if the tagged object cannot - * be converted. + * @param taggedObject the tagged object holding the object we want + * @param declaredExplicit true if the object is meant to be explicitly tagged false otherwise. + * @exception ArgumentException if the tagged object cannot be converted. */ - public static DerNumericString GetInstance( - Asn1TaggedObject obj, - bool isExplicit) + public static DerNumericString GetInstance(Asn1TaggedObject taggedObject, bool declaredExplicit) { - Asn1Object o = obj.GetObject(); - - if (isExplicit || o is DerNumericString) - { - return GetInstance(o); - } - - return new DerNumericString(Asn1OctetString.GetInstance(o).GetOctets()); + return (DerNumericString)Meta.Instance.GetContextInstance(taggedObject, declaredExplicit); } - /** - * basic constructor - with bytes. - */ - public DerNumericString( - byte[] str) - : this(Strings.FromAsciiByteArray(str), false) - { - } + private readonly byte[] m_contents; - /** - * basic constructor - without validation.. - */ - public DerNumericString( - string str) - : this(str, false) - { - } + public DerNumericString(string str) + : this(str, false) + { + } - /** + /** * Constructor with optional validation. * * @param string the base string to wrap. @@ -78,57 +82,68 @@ namespace Org.BouncyCastle.Asn1 * @throws ArgumentException if validate is true and the string * contains characters that should not be in a NumericString. */ - public DerNumericString( - string str, - bool validate) - { - if (str == null) - throw new ArgumentNullException("str"); - if (validate && !IsNumericString(str)) - throw new ArgumentException("string contains illegal characters", "str"); + public DerNumericString(string str, bool validate) + { + if (str == null) + throw new ArgumentNullException("str"); + if (validate && !IsNumericString(str)) + throw new ArgumentException("string contains illegal characters", "str"); + + m_contents = Strings.ToAsciiByteArray(str); + } - this.str = str; + public DerNumericString(byte[] contents) + : this(contents, true) + { } - public override string GetString() + internal DerNumericString(byte[] contents, bool clone) + { + if (null == contents) + throw new ArgumentNullException("contents"); + + m_contents = clone ? Arrays.Clone(contents) : contents; + } + + public override string GetString() { - return str; + return Strings.FromAsciiByteArray(m_contents); } public byte[] GetOctets() { - return Strings.ToAsciiByteArray(str); + return Arrays.Clone(m_contents); } internal override IAsn1Encoding GetEncoding(int encoding) { - return new PrimitiveEncoding(Asn1Tags.Universal, Asn1Tags.NumericString, GetOctets()); + return new PrimitiveEncoding(Asn1Tags.Universal, Asn1Tags.NumericString, m_contents); } internal override IAsn1Encoding GetEncodingImplicit(int encoding, int tagClass, int tagNo) { - return new PrimitiveEncoding(tagClass, tagNo, GetOctets()); + return new PrimitiveEncoding(tagClass, tagNo, m_contents); } - protected override bool Asn1Equals( - Asn1Object asn1Object) + protected override bool Asn1Equals(Asn1Object asn1Object) { - DerNumericString other = asn1Object as DerNumericString; - - if (other == null) - return false; + DerNumericString that = asn1Object as DerNumericString; + return null != that + && Arrays.AreEqual(this.m_contents, that.m_contents); + } - return this.str.Equals(other.str); + protected override int Asn1GetHashCode() + { + return Arrays.GetHashCode(m_contents); } - /** + /** * Return true if the string can be represented as a NumericString ('0'..'9', ' ') * * @param str string to validate. * @return true if numeric, fale otherwise. */ - public static bool IsNumericString( - string str) + public static bool IsNumericString(string str) { foreach (char ch in str) { @@ -138,5 +153,39 @@ namespace Org.BouncyCastle.Asn1 return true; } - } + + internal static bool IsNumericString(byte[] contents) + { + for (int i = 0; i < contents.Length; ++i) + { + switch (contents[i]) + { + case 0x20: + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + case 0x38: + case 0x39: + break; + default: + return false; + } + } + + return true; + } + + internal static DerNumericString CreatePrimitive(byte[] contents) + { + // TODO Validation - sort out exception types + //if (!IsNumericString(contents)) + + return new DerNumericString(contents, false); + } + } } diff --git a/crypto/src/asn1/DerObjectIdentifier.cs b/crypto/src/asn1/DerObjectIdentifier.cs index f91ad2150..8b77f966c 100644 --- a/crypto/src/asn1/DerObjectIdentifier.cs +++ b/crypto/src/asn1/DerObjectIdentifier.cs @@ -10,13 +10,25 @@ namespace Org.BouncyCastle.Asn1 public class DerObjectIdentifier : Asn1Object { + internal class Meta : Asn1UniversalType + { + internal static readonly Asn1UniversalType Instance = new Meta(); + + private Meta() : base(typeof(DerObjectIdentifier), Asn1Tags.ObjectIdentifier) {} + + internal override Asn1Object FromImplicitPrimitive(DerOctetString octetString) + { + return CreatePrimitive(octetString.GetOctets(), false); + } + } + public static DerObjectIdentifier FromContents(byte[] contents) { return CreatePrimitive(contents, true); } /** - * return an Oid from the passed in object + * return an OID from the passed in object * * @exception ArgumentException if the object cannot be converted. */ @@ -36,7 +48,7 @@ namespace Org.BouncyCastle.Asn1 { try { - return GetInstance(FromByteArray((byte[])obj)); + return (DerObjectIdentifier)Meta.Instance.FromByteArray((byte[])obj); } catch (IOException e) { @@ -49,14 +61,19 @@ namespace Org.BouncyCastle.Asn1 public static DerObjectIdentifier GetInstance(Asn1TaggedObject taggedObject, bool declaredExplicit) { - Asn1Object baseObject = taggedObject.GetObject(); - - if (declaredExplicit || baseObject is DerObjectIdentifier) + /* + * TODO[asn1] This block here is for backward compatibility, but should eventually be removed. + * + * - see https://github.com/bcgit/bc-java/issues/1015 + */ + if (!declaredExplicit && !taggedObject.IsParsed()) { - return GetInstance(baseObject); + Asn1Object baseObject = taggedObject.GetObject(); + if (!(baseObject is DerObjectIdentifier)) + return FromContents(Asn1OctetString.GetInstance(baseObject).GetOctets()); } - return FromContents(Asn1OctetString.GetInstance(baseObject).GetOctets()); + return (DerObjectIdentifier)Meta.Instance.GetContextInstance(taggedObject, declaredExplicit); } private const long LongLimit = (Int64.MaxValue >> 7) - 0x7F; diff --git a/crypto/src/asn1/DerPrintableString.cs b/crypto/src/asn1/DerPrintableString.cs index 30358e219..3c44a2d52 100644 --- a/crypto/src/asn1/DerPrintableString.cs +++ b/crypto/src/asn1/DerPrintableString.cs @@ -1,4 +1,5 @@ using System; +using System.IO; using Org.BouncyCastle.Utilities; @@ -10,61 +11,65 @@ namespace Org.BouncyCastle.Asn1 public class DerPrintableString : DerStringBase { - private readonly string str; + internal class Meta : Asn1UniversalType + { + internal static readonly Asn1UniversalType Instance = new Meta(); + + private Meta() : base(typeof(DerPrintableString), Asn1Tags.PrintableString) {} + + internal override Asn1Object FromImplicitPrimitive(DerOctetString octetString) + { + return CreatePrimitive(octetString.GetOctets()); + } + } /** * return a printable string from the passed in object. * * @exception ArgumentException if the object cannot be converted. */ - public static DerPrintableString GetInstance( - object obj) + public static DerPrintableString GetInstance(object obj) { if (obj == null || obj is DerPrintableString) { return (DerPrintableString)obj; } + else if (obj is IAsn1Convertible) + { + Asn1Object asn1Object = ((IAsn1Convertible)obj).ToAsn1Object(); + if (asn1Object is DerPrintableString) + return (DerPrintableString)asn1Object; + } + else if (obj is byte[]) + { + try + { + return (DerPrintableString)Meta.Instance.FromByteArray((byte[])obj); + } + catch (IOException e) + { + throw new ArgumentException("failed to construct printable string from byte[]: " + e.Message); + } + } throw new ArgumentException("illegal object in GetInstance: " + Platform.GetTypeName(obj)); } /** - * return a Printable string from a tagged object. + * return a printable string from a tagged object. * - * @param obj the tagged object holding the object we want - * @param explicitly true if the object is meant to be explicitly - * tagged false otherwise. - * @exception ArgumentException if the tagged object cannot - * be converted. + * @param taggedObject the tagged object holding the object we want + * @param declaredExplicit true if the object is meant to be explicitly tagged false otherwise. + * @exception ArgumentException if the tagged object cannot be converted. */ - public static DerPrintableString GetInstance( - Asn1TaggedObject obj, - bool isExplicit) + public static DerPrintableString GetInstance(Asn1TaggedObject taggedObject, bool declaredExplicit) { - Asn1Object o = obj.GetObject(); - - if (isExplicit || o is DerPrintableString) - { - return GetInstance(o); - } - - return new DerPrintableString(Asn1OctetString.GetInstance(o).GetOctets()); + return (DerPrintableString)Meta.Instance.GetContextInstance(taggedObject, declaredExplicit); } - /** - * basic constructor - byte encoded string. - */ - public DerPrintableString( - byte[] str) - : this(Strings.FromAsciiByteArray(str), false) - { - } + private readonly byte[] m_contents; - /** - * basic constructor - this does not validate the string - */ - public DerPrintableString( - string str) + public DerPrintableString(string str) : this(str, false) { } @@ -77,57 +82,69 @@ namespace Org.BouncyCastle.Asn1 * @throws ArgumentException if validate is true and the string * contains characters that should not be in a PrintableString. */ - public DerPrintableString( - string str, - bool validate) + public DerPrintableString(string str, bool validate) { if (str == null) throw new ArgumentNullException("str"); if (validate && !IsPrintableString(str)) throw new ArgumentException("string contains illegal characters", "str"); - this.str = str; + m_contents = Strings.ToAsciiByteArray(str); } - public override string GetString() + public DerPrintableString(byte[] contents) + : this(contents, true) + { + } + + internal DerPrintableString(byte[] contents, bool clone) + { + if (null == contents) + throw new ArgumentNullException("contents"); + + m_contents = clone ? Arrays.Clone(contents) : contents; + } + + public override string GetString() { - return str; + return Strings.FromAsciiByteArray(m_contents); } - public byte[] GetOctets() + public byte[] GetOctets() { - return Strings.ToAsciiByteArray(str); + return Arrays.Clone(m_contents); } internal override IAsn1Encoding GetEncoding(int encoding) { - return new PrimitiveEncoding(Asn1Tags.Universal, Asn1Tags.PrintableString, GetOctets()); + return new PrimitiveEncoding(Asn1Tags.Universal, Asn1Tags.PrintableString, m_contents); } internal override IAsn1Encoding GetEncodingImplicit(int encoding, int tagClass, int tagNo) { - return new PrimitiveEncoding(tagClass, tagNo, GetOctets()); + return new PrimitiveEncoding(tagClass, tagNo, m_contents); } protected override bool Asn1Equals( Asn1Object asn1Object) { - DerPrintableString other = asn1Object as DerPrintableString; - - if (other == null) - return false; + DerPrintableString that = asn1Object as DerPrintableString; + return null != that + && Arrays.AreEqual(this.m_contents, that.m_contents); + } - return this.str.Equals(other.str); + protected override int Asn1GetHashCode() + { + return Arrays.GetHashCode(m_contents); } - /** + /** * return true if the passed in String can be represented without * loss as a PrintableString, false otherwise. * * @return true if in printable set, false otherwise. */ - public static bool IsPrintableString( - string str) + public static bool IsPrintableString(string str) { foreach (char ch in str) { @@ -162,5 +179,10 @@ namespace Org.BouncyCastle.Asn1 return true; } - } + + internal static DerPrintableString CreatePrimitive(byte[] contents) + { + return new DerPrintableString(contents, false); + } + } } diff --git a/crypto/src/asn1/DerT61String.cs b/crypto/src/asn1/DerT61String.cs index 64a9bd469..a0e4f1d22 100644 --- a/crypto/src/asn1/DerT61String.cs +++ b/crypto/src/asn1/DerT61String.cs @@ -1,4 +1,5 @@ using System; +using System.IO; using Org.BouncyCastle.Utilities; @@ -10,97 +11,120 @@ namespace Org.BouncyCastle.Asn1 public class DerT61String : DerStringBase { - private readonly string str; + internal class Meta : Asn1UniversalType + { + internal static readonly Asn1UniversalType Instance = new Meta(); + + private Meta() : base(typeof(DerT61String), Asn1Tags.T61String) {} + + internal override Asn1Object FromImplicitPrimitive(DerOctetString octetString) + { + return CreatePrimitive(octetString.GetOctets()); + } + } /** * return a T61 string from the passed in object. * * @exception ArgumentException if the object cannot be converted. */ - public static DerT61String GetInstance( - object obj) + public static DerT61String GetInstance(object obj) { if (obj == null || obj is DerT61String) { return (DerT61String)obj; } + else if (obj is IAsn1Convertible) + { + Asn1Object asn1Object = ((IAsn1Convertible)obj).ToAsn1Object(); + if (asn1Object is DerT61String) + return (DerT61String)asn1Object; + } + else if (obj is byte[]) + { + try + { + return (DerT61String)Meta.Instance.FromByteArray((byte[])obj); + } + catch (IOException e) + { + throw new ArgumentException("failed to construct T61 string from byte[]: " + e.Message); + } + } throw new ArgumentException("illegal object in GetInstance: " + Platform.GetTypeName(obj)); } /** - * return an T61 string from a tagged object. + * return a T61 string from a tagged object. * - * @param obj the tagged object holding the object we want - * @param explicitly true if the object is meant to be explicitly - * tagged false otherwise. - * @exception ArgumentException if the tagged object cannot - * be converted. + * @param taggedObject the tagged object holding the object we want + * @param declaredExplicit true if the object is meant to be explicitly tagged false otherwise. + * @exception ArgumentException if the tagged object cannot be converted. */ - public static DerT61String GetInstance( - Asn1TaggedObject obj, - bool isExplicit) + public static DerT61String GetInstance(Asn1TaggedObject taggedObject, bool declaredExplicit) { - Asn1Object o = obj.GetObject(); + return (DerT61String)Meta.Instance.GetContextInstance(taggedObject, declaredExplicit); + } - if (isExplicit || o is DerT61String) - { - return GetInstance(o); - } + private readonly byte[] m_contents; - return new DerT61String(Asn1OctetString.GetInstance(o).GetOctets()); + public DerT61String(string str) + { + if (str == null) + throw new ArgumentNullException("str"); + + m_contents = Strings.ToByteArray(str); } - /** - * basic constructor - with bytes. - */ - public DerT61String( - byte[] str) - : this(Strings.FromByteArray(str)) - { + public DerT61String(byte[] contents) + : this(contents, true) + { } - /** - * basic constructor - with string. - */ - public DerT61String( - string str) + internal DerT61String(byte[] contents, bool clone) { - if (str == null) - throw new ArgumentNullException("str"); + if (null == contents) + throw new ArgumentNullException("contents"); - this.str = str; + m_contents = clone ? Arrays.Clone(contents) : contents; } - public override string GetString() + public override string GetString() { - return str; + return Strings.FromByteArray(m_contents); } internal override IAsn1Encoding GetEncoding(int encoding) { - return new PrimitiveEncoding(Asn1Tags.Universal, Asn1Tags.T61String, GetOctets()); + return new PrimitiveEncoding(Asn1Tags.Universal, Asn1Tags.T61String, m_contents); } internal override IAsn1Encoding GetEncodingImplicit(int encoding, int tagClass, int tagNo) { - return new PrimitiveEncoding(tagClass, tagNo, GetOctets()); + return new PrimitiveEncoding(tagClass, tagNo, m_contents); } public byte[] GetOctets() { - return Strings.ToByteArray(str); + return Arrays.Clone(m_contents); } - protected override bool Asn1Equals( - Asn1Object asn1Object) - { - DerT61String other = asn1Object as DerT61String; + protected override bool Asn1Equals(Asn1Object asn1Object) + { + DerT61String that = asn1Object as DerT61String; + return null != that + && Arrays.AreEqual(this.m_contents, that.m_contents); + } - if (other == null) - return false; + protected override int Asn1GetHashCode() + { + return Arrays.GetHashCode(m_contents); + } - return this.str.Equals(other.str); + internal static DerT61String CreatePrimitive(byte[] contents) + { + return new DerT61String(contents, false); } - } + } } diff --git a/crypto/src/asn1/DerUTCTime.cs b/crypto/src/asn1/DerUTCTime.cs index eaa902e71..cb3f13353 100644 --- a/crypto/src/asn1/DerUTCTime.cs +++ b/crypto/src/asn1/DerUTCTime.cs @@ -1,6 +1,6 @@ using System; using System.Globalization; -using System.Text; +using System.IO; using Org.BouncyCastle.Utilities; @@ -12,47 +12,64 @@ namespace Org.BouncyCastle.Asn1 public class DerUtcTime : Asn1Object { - private readonly string time; + internal class Meta : Asn1UniversalType + { + internal static readonly Asn1UniversalType Instance = new Meta(); + + private Meta() : base(typeof(DerUtcTime), Asn1Tags.UtcTime) {} + + internal override Asn1Object FromImplicitPrimitive(DerOctetString octetString) + { + return CreatePrimitive(octetString.GetOctets()); + } + } /** - * return an UTC Time from the passed in object. + * return a UTC Time from the passed in object. * * @exception ArgumentException if the object cannot be converted. */ - public static DerUtcTime GetInstance( - object obj) + public static DerUtcTime GetInstance(object obj) { if (obj == null || obj is DerUtcTime) { return (DerUtcTime)obj; } + else if (obj is IAsn1Convertible) + { + Asn1Object asn1Object = ((IAsn1Convertible)obj).ToAsn1Object(); + if (asn1Object is DerUtcTime) + return (DerUtcTime)asn1Object; + } + else if (obj is byte[]) + { + try + { + return (DerUtcTime)Meta.Instance.FromByteArray((byte[])obj); + } + catch (IOException e) + { + throw new ArgumentException("failed to construct UTC time from byte[]: " + e.Message); + } + } throw new ArgumentException("illegal object in GetInstance: " + Platform.GetTypeName(obj)); } /** - * return an UTC Time from a tagged object. + * return a UTC Time from a tagged object. * - * @param obj the tagged object holding the object we want - * @param explicitly true if the object is meant to be explicitly - * tagged false otherwise. - * @exception ArgumentException if the tagged object cannot - * be converted. + * @param taggedObject the tagged object holding the object we want + * @param declaredExplicit true if the object is meant to be explicitly tagged false otherwise. + * @exception ArgumentException if the tagged object cannot be converted. */ - public static DerUtcTime GetInstance( - Asn1TaggedObject obj, - bool isExplicit) + public static DerUtcTime GetInstance(Asn1TaggedObject taggedObject, bool declaredExplicit) { - Asn1Object o = obj.GetObject(); - - if (isExplicit || o is DerUtcTime) - { - return GetInstance(o); - } - - return new DerUtcTime(((Asn1OctetString)o).GetOctets()); + return (DerUtcTime)Meta.Instance.GetContextInstance(taggedObject, declaredExplicit); } + private readonly string time; + /** * The correct format for this is YYMMDDHHMMSSZ (it used to be that seconds were * never encoded. When you're creating one of these objects from scratch, that's @@ -62,8 +79,7 @@ namespace Org.BouncyCastle.Asn1 * <p> * @param time the time string.</p> */ - public DerUtcTime( - string time) + public DerUtcTime(string time) { if (time == null) throw new ArgumentNullException("time"); @@ -83,8 +99,7 @@ namespace Org.BouncyCastle.Asn1 /** * base constructor from a DateTime object */ - public DerUtcTime( - DateTime time) + public DerUtcTime(DateTime time) { #if PORTABLE this.time = time.ToUniversalTime().ToString("yyMMddHHmmss", CultureInfo.InvariantCulture) + "Z"; @@ -93,13 +108,12 @@ namespace Org.BouncyCastle.Asn1 #endif } - internal DerUtcTime( - byte[] bytes) + internal DerUtcTime(byte[] contents) { // // explicitly convert to characters // - this.time = Strings.FromAsciiByteArray(bytes); + this.time = Strings.FromAsciiByteArray(contents); } // public DateTime ToDateTime() @@ -139,9 +153,7 @@ namespace Org.BouncyCastle.Asn1 return ParseDateString(AdjustedTimeString, @"yyyyMMddHHmmss'GMT'zzz"); } - private DateTime ParseDateString( - string dateStr, - string formatStr) + private DateTime ParseDateString(string dateStr, string formatStr) { DateTime dt = DateTime.ParseExact( dateStr, @@ -247,15 +259,11 @@ namespace Org.BouncyCastle.Asn1 return new PrimitiveEncoding(tagClass, tagNo, GetOctets()); } - protected override bool Asn1Equals( - Asn1Object asn1Object) + protected override bool Asn1Equals(Asn1Object asn1Object) { - DerUtcTime other = asn1Object as DerUtcTime; - - if (other == null) - return false; - - return this.time.Equals(other.time); + DerUtcTime that = asn1Object as DerUtcTime; + return null != that + && this.time.Equals(that.time); } protected override int Asn1GetHashCode() @@ -267,5 +275,10 @@ namespace Org.BouncyCastle.Asn1 { return time; } - } + + internal static DerUtcTime CreatePrimitive(byte[] contents) + { + return new DerUtcTime(contents); + } + } } diff --git a/crypto/src/asn1/DerUTF8String.cs b/crypto/src/asn1/DerUTF8String.cs index 24023cd6c..d15a19d39 100644 --- a/crypto/src/asn1/DerUTF8String.cs +++ b/crypto/src/asn1/DerUTF8String.cs @@ -1,5 +1,5 @@ using System; -using System.Text; +using System.IO; using Org.BouncyCastle.Utilities; @@ -11,92 +11,112 @@ namespace Org.BouncyCastle.Asn1 public class DerUtf8String : DerStringBase { - private readonly string str; + internal class Meta : Asn1UniversalType + { + internal static readonly Asn1UniversalType Instance = new Meta(); + + private Meta() : base(typeof(DerUtf8String), Asn1Tags.Utf8String) {} + + internal override Asn1Object FromImplicitPrimitive(DerOctetString octetString) + { + return CreatePrimitive(octetString.GetOctets()); + } + } /** * return an UTF8 string from the passed in object. * * @exception ArgumentException if the object cannot be converted. */ - public static DerUtf8String GetInstance( - object obj) + public static DerUtf8String GetInstance(object obj) { if (obj == null || obj is DerUtf8String) { return (DerUtf8String)obj; } + else if (obj is IAsn1Convertible) + { + Asn1Object asn1Object = ((IAsn1Convertible)obj).ToAsn1Object(); + if (asn1Object is DerUtf8String) + return (DerUtf8String)asn1Object; + } + else if (obj is byte[]) + { + try + { + return (DerUtf8String)Meta.Instance.FromByteArray((byte[])obj); + } + catch (IOException e) + { + throw new ArgumentException("failed to construct UTF8 string from byte[]: " + e.Message); + } + } throw new ArgumentException("illegal object in GetInstance: " + Platform.GetTypeName(obj)); } /** - * return an UTF8 string from a tagged object. + * return a UTF8 string from a tagged object. * - * @param obj the tagged object holding the object we want - * @param explicitly true if the object is meant to be explicitly - * tagged false otherwise. - * @exception ArgumentException if the tagged object cannot - * be converted. + * @param taggedObject the tagged object holding the object we want + * @param declaredExplicit true if the object is meant to be explicitly tagged false otherwise. + * @exception ArgumentException if the tagged object cannot be converted. */ - public static DerUtf8String GetInstance( - Asn1TaggedObject obj, - bool isExplicit) + public static DerUtf8String GetInstance(Asn1TaggedObject taggedObject, bool declaredExplicit) { - Asn1Object o = obj.GetObject(); + return (DerUtf8String)Meta.Instance.GetContextInstance(taggedObject, declaredExplicit); + } - if (isExplicit || o is DerUtf8String) - { - return GetInstance(o); - } + private readonly byte[] m_contents; - return new DerUtf8String(Asn1OctetString.GetInstance(o).GetOctets()); + public DerUtf8String(string str) + : this(Strings.ToUtf8ByteArray(str), false) + { } - /** - * basic constructor - byte encoded string. - */ - public DerUtf8String( - byte[] str) - : this(Encoding.UTF8.GetString(str, 0, str.Length)) + public DerUtf8String(byte[] contents) + : this(contents, true) { } - /** - * basic constructor - */ - public DerUtf8String( - string str) + internal DerUtf8String(byte[] contents, bool clone) { - if (str == null) - throw new ArgumentNullException("str"); + if (null == contents) + throw new ArgumentNullException("contents"); - this.str = str; + m_contents = clone ? Arrays.Clone(contents) : contents; } - public override string GetString() + public override string GetString() { - return str; + return Strings.FromUtf8ByteArray(m_contents); } - protected override bool Asn1Equals( - Asn1Object asn1Object) + protected override bool Asn1Equals(Asn1Object asn1Object) { - DerUtf8String other = asn1Object as DerUtf8String; - - if (other == null) - return false; + DerUtf8String that = asn1Object as DerUtf8String; + return null != that + && Arrays.AreEqual(this.m_contents, that.m_contents); + } - return this.str.Equals(other.str); + protected override int Asn1GetHashCode() + { + return Arrays.GetHashCode(m_contents); } internal override IAsn1Encoding GetEncoding(int encoding) { - return new PrimitiveEncoding(Asn1Tags.Universal, Asn1Tags.Utf8String, Encoding.UTF8.GetBytes(str)); + return new PrimitiveEncoding(Asn1Tags.Universal, Asn1Tags.Utf8String, m_contents); } internal override IAsn1Encoding GetEncodingImplicit(int encoding, int tagClass, int tagNo) { - return new PrimitiveEncoding(tagClass, tagNo, Encoding.UTF8.GetBytes(str)); + return new PrimitiveEncoding(tagClass, tagNo, m_contents); + } + + internal static DerUtf8String CreatePrimitive(byte[] contents) + { + return new DerUtf8String(contents, false); } } } diff --git a/crypto/src/asn1/DerUniversalString.cs b/crypto/src/asn1/DerUniversalString.cs index 1ae989f93..e4e93bd7d 100644 --- a/crypto/src/asn1/DerUniversalString.cs +++ b/crypto/src/asn1/DerUniversalString.cs @@ -1,4 +1,6 @@ using System; +using System.Diagnostics; +using System.IO; using System.Text; using Org.BouncyCastle.Utilities; @@ -6,17 +8,27 @@ using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Asn1 { /** - * Der UniversalString object. + * UniversalString object. */ public class DerUniversalString : DerStringBase { - private static readonly char[] table = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + internal class Meta : Asn1UniversalType + { + internal static readonly Asn1UniversalType Instance = new Meta(); + + private Meta() : base(typeof(DerUniversalString), Asn1Tags.UniversalString) {} + + internal override Asn1Object FromImplicitPrimitive(DerOctetString octetString) + { + return CreatePrimitive(octetString.GetOctets()); + } + } - private readonly byte[] str; + private static readonly char[] table = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; /** - * return a Universal string from the passed in object. + * return a universal string from the passed in object. * * @exception ArgumentException if the object cannot be converted. */ @@ -27,85 +39,134 @@ namespace Org.BouncyCastle.Asn1 { return (DerUniversalString)obj; } + else if (obj is IAsn1Convertible) + { + Asn1Object asn1Object = ((IAsn1Convertible)obj).ToAsn1Object(); + if (asn1Object is DerUniversalString) + return (DerUniversalString)asn1Object; + } + else if (obj is byte[]) + { + try + { + return (DerUniversalString)Meta.Instance.FromByteArray((byte[])obj); + } + catch (IOException e) + { + throw new ArgumentException("failed to construct universal string from byte[]: " + e.Message); + } + } throw new ArgumentException("illegal object in GetInstance: " + Platform.GetTypeName(obj)); } /** - * return a Universal string from a tagged object. + * return a universal string from a tagged object. * - * @param obj the tagged object holding the object we want - * @param explicitly true if the object is meant to be explicitly - * tagged false otherwise. - * @exception ArgumentException if the tagged object cannot - * be converted. + * @param taggedObject the tagged object holding the object we want + * @param declaredExplicit true if the object is meant to be explicitly tagged false otherwise. + * @exception ArgumentException if the tagged object cannot be converted. */ - public static DerUniversalString GetInstance( - Asn1TaggedObject obj, - bool isExplicit) + public static DerUniversalString GetInstance(Asn1TaggedObject taggedObject, bool declaredExplicit) { - Asn1Object o = obj.GetObject(); + return (DerUniversalString)Meta.Instance.GetContextInstance(taggedObject, declaredExplicit); + } - if (isExplicit || o is DerUniversalString) - { - return GetInstance(o); - } + private readonly byte[] m_contents; - return new DerUniversalString(Asn1OctetString.GetInstance(o).GetOctets()); + public DerUniversalString(byte[] contents) + : this(contents, true) + { } - /** - * basic constructor - byte encoded string. - */ - public DerUniversalString( - byte[] str) + internal DerUniversalString(byte[] contents, bool clone) { - if (str == null) - throw new ArgumentNullException("str"); + if (null == contents) + throw new ArgumentNullException("contents"); - this.str = str; + m_contents = clone ? Arrays.Clone(contents) : contents; } public override string GetString() { - StringBuilder buffer = new StringBuilder("#"); - byte[] enc = GetDerEncoded(); + int dl = m_contents.Length; + int capacity = 3 + 2 * (Asn1OutputStream.GetLengthOfDL(dl) + dl); + StringBuilder buf = new StringBuilder("#1C", capacity); + EncodeHexDL(buf, dl); - for (int i = 0; i != enc.Length; i++) - { - uint ubyte = enc[i]; - buffer.Append(table[(ubyte >> 4) & 0xf]); - buffer.Append(table[enc[i] & 0xf]); - } + for (int i = 0; i < dl; ++i) + { + EncodeHexByte(buf, m_contents[i]); + } - return buffer.ToString(); + Debug.Assert(buf.Length == capacity); + return buf.ToString(); } - public byte[] GetOctets() + public byte[] GetOctets() { - return (byte[]) str.Clone(); + return Arrays.Clone(m_contents); } internal override IAsn1Encoding GetEncoding(int encoding) { - return new PrimitiveEncoding(Asn1Tags.Universal, Asn1Tags.UniversalString, this.str); + return new PrimitiveEncoding(Asn1Tags.Universal, Asn1Tags.UniversalString, m_contents); } internal override IAsn1Encoding GetEncodingImplicit(int encoding, int tagClass, int tagNo) { - return new PrimitiveEncoding(tagClass, tagNo, this.str); + return new PrimitiveEncoding(tagClass, tagNo, m_contents); + } + + protected override bool Asn1Equals(Asn1Object asn1Object) + { + DerUniversalString that = asn1Object as DerUniversalString; + return null != that + && Arrays.AreEqual(this.m_contents, that.m_contents); + } + + protected override int Asn1GetHashCode() + { + return Arrays.GetHashCode(m_contents); + } + + internal static DerUniversalString CreatePrimitive(byte[] contents) + { + return new DerUniversalString(contents, false); + } + + private static void EncodeHexByte(StringBuilder buf, int i) + { + buf.Append(table[(i >> 4) & 0xF]); + buf.Append(table[i & 0xF]); } - protected override bool Asn1Equals( - Asn1Object asn1Object) - { - DerUniversalString other = asn1Object as DerUniversalString; + private static void EncodeHexDL(StringBuilder buf, int dl) + { + if (dl < 128) + { + EncodeHexByte(buf, dl); + return; + } + + byte[] stack = new byte[5]; + int pos = 5; - if (other == null) - return false; + do + { + stack[--pos] = (byte)dl; + dl >>= 8; + } + while (dl != 0); + + int count = stack.Length - pos; + stack[--pos] = (byte)(0x80 | count); -// return this.GetString().Equals(other.GetString()); - return Arrays.AreEqual(this.str, other.str); + do + { + EncodeHexByte(buf, stack[pos++]); + } + while (pos < stack.Length); } } } diff --git a/crypto/src/asn1/DerVideotexString.cs b/crypto/src/asn1/DerVideotexString.cs index 3511663d2..a5fbe0602 100644 --- a/crypto/src/asn1/DerVideotexString.cs +++ b/crypto/src/asn1/DerVideotexString.cs @@ -1,4 +1,5 @@ using System; +using System.IO; using Org.BouncyCastle.Utilities; @@ -7,10 +8,20 @@ namespace Org.BouncyCastle.Asn1 public class DerVideotexString : DerStringBase { - private readonly byte[] mString; + internal class Meta : Asn1UniversalType + { + internal static readonly Asn1UniversalType Instance = new Meta(); + + private Meta() : base(typeof(DerVideotexString), Asn1Tags.VideotexString) {} + + internal override Asn1Object FromImplicitPrimitive(DerOctetString octetString) + { + return CreatePrimitive(octetString.GetOctets()); + } + } /** - * return a Videotex String from the passed in object + * return a videotex string from the passed in object * * @param obj a DERVideotexString or an object that can be converted into one. * @exception IllegalArgumentException if the object cannot be converted. @@ -22,16 +33,21 @@ namespace Org.BouncyCastle.Asn1 { return (DerVideotexString)obj; } - - if (obj is byte[]) + else if (obj is IAsn1Convertible) + { + Asn1Object asn1Object = ((IAsn1Convertible)obj).ToAsn1Object(); + if (asn1Object is DerVideotexString) + return (DerVideotexString)asn1Object; + } + else if (obj is byte[]) { try { - return (DerVideotexString)FromByteArray((byte[])obj); + return (DerVideotexString)Meta.Instance.FromByteArray((byte[])obj); } - catch (Exception e) + catch (IOException e) { - throw new ArgumentException("encoding error in GetInstance: " + e.ToString(), "obj"); + throw new ArgumentException("failed to construct videotex string from byte[]: " + e.Message); } } @@ -39,70 +55,68 @@ namespace Org.BouncyCastle.Asn1 } /** - * return a Videotex String from a tagged object. + * return a videotex string from a tagged object. * - * @param obj the tagged object holding the object we want - * @param explicit true if the object is meant to be explicitly - * tagged false otherwise. - * @exception IllegalArgumentException if the tagged object cannot - * be converted. + * @param taggedObject the tagged object holding the object we want + * @param declaredExplicit true if the object is meant to be explicitly tagged false otherwise. + * @exception IllegalArgumentException if the tagged object cannot be converted. * @return a DERVideotexString instance, or null. */ - public static DerVideotexString GetInstance(Asn1TaggedObject obj, bool isExplicit) + public static DerVideotexString GetInstance(Asn1TaggedObject taggedObject, bool declaredExplicit) { - Asn1Object o = obj.GetObject(); + return (DerVideotexString)Meta.Instance.GetContextInstance(taggedObject, declaredExplicit); + } - if (isExplicit || o is DerVideotexString) - { - return GetInstance(o); - } + private readonly byte[] m_contents; - return new DerVideotexString(((Asn1OctetString)o).GetOctets()); + public DerVideotexString(byte[] contents) + : this(contents, true) + { } - /** - * basic constructor - with bytes. - * @param string the byte encoding of the characters making up the string. - */ - public DerVideotexString(byte[] encoding) + internal DerVideotexString(byte[] contents, bool clone) { - this.mString = Arrays.Clone(encoding); + if (null == contents) + throw new ArgumentNullException("contents"); + + m_contents = clone ? Arrays.Clone(contents) : contents; } public override string GetString() { - return Strings.FromByteArray(mString); + return Strings.FromByteArray(m_contents); } public byte[] GetOctets() { - return Arrays.Clone(mString); + return Arrays.Clone(m_contents); } internal override IAsn1Encoding GetEncoding(int encoding) { - return new PrimitiveEncoding(Asn1Tags.Universal, Asn1Tags.VideotexString, mString); + return new PrimitiveEncoding(Asn1Tags.Universal, Asn1Tags.VideotexString, m_contents); } internal override IAsn1Encoding GetEncodingImplicit(int encoding, int tagClass, int tagNo) { - return new PrimitiveEncoding(tagClass, tagNo, mString); + return new PrimitiveEncoding(tagClass, tagNo, m_contents); } - protected override int Asn1GetHashCode() - { - return Arrays.GetHashCode(mString); + protected override bool Asn1Equals(Asn1Object asn1Object) + { + DerVideotexString that = asn1Object as DerVideotexString; + return null != that + && Arrays.AreEqual(this.m_contents, that.m_contents); } - protected override bool Asn1Equals( - Asn1Object asn1Object) + protected override int Asn1GetHashCode() { - DerVideotexString other = asn1Object as DerVideotexString; - - if (other == null) - return false; + return Arrays.GetHashCode(m_contents); + } - return Arrays.AreEqual(mString, other.mString); + internal static DerVideotexString CreatePrimitive(byte[] contents) + { + return new DerVideotexString(contents, false); } } } diff --git a/crypto/src/asn1/DerVisibleString.cs b/crypto/src/asn1/DerVisibleString.cs index cbbd9cc98..359370040 100644 --- a/crypto/src/asn1/DerVisibleString.cs +++ b/crypto/src/asn1/DerVisibleString.cs @@ -1,20 +1,30 @@ using System; -using System.Text; +using System.IO; using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Asn1 { /** - * Der VisibleString object. + * VisibleString object. */ public class DerVisibleString : DerStringBase { - private readonly string str; + internal class Meta : Asn1UniversalType + { + internal static readonly Asn1UniversalType Instance = new Meta(); + + private Meta() : base(typeof(DerVisibleString), Asn1Tags.VisibleString) {} + + internal override Asn1Object FromImplicitPrimitive(DerOctetString octetString) + { + return CreatePrimitive(octetString.GetOctets()); + } + } /** - * return a Visible string from the passed in object. + * return a visible string from the passed in object. * * @exception ArgumentException if the object cannot be converted. */ @@ -25,91 +35,97 @@ namespace Org.BouncyCastle.Asn1 { return (DerVisibleString)obj; } - - if (obj is Asn1OctetString) + else if (obj is IAsn1Convertible) { - return new DerVisibleString(((Asn1OctetString)obj).GetOctets()); + Asn1Object asn1Object = ((IAsn1Convertible)obj).ToAsn1Object(); + if (asn1Object is DerVisibleString) + return (DerVisibleString)asn1Object; } - - if (obj is Asn1TaggedObject) + else if (obj is byte[]) { - return GetInstance(((Asn1TaggedObject)obj).GetObject()); + try + { + return (DerVisibleString)Meta.Instance.FromByteArray((byte[])obj); + } + catch (IOException e) + { + throw new ArgumentException("failed to construct visible string from byte[]: " + e.Message); + } } throw new ArgumentException("illegal object in GetInstance: " + Platform.GetTypeName(obj)); } /** - * return a Visible string from a tagged object. + * return a visible string from a tagged object. * - * @param obj the tagged object holding the object we want - * @param explicitly true if the object is meant to be explicitly - * tagged false otherwise. - * @exception ArgumentException if the tagged object cannot - * be converted. + * @param taggedObject the tagged object holding the object we want + * @param declaredExplicit true if the object is meant to be explicitly tagged false otherwise. + * @exception ArgumentException if the tagged object cannot be converted. */ - public static DerVisibleString GetInstance( - Asn1TaggedObject obj, - bool explicitly) + public static DerVisibleString GetInstance(Asn1TaggedObject taggedObject, bool declaredExplicit) { - return GetInstance(obj.GetObject()); + return (DerVisibleString)Meta.Instance.GetContextInstance(taggedObject, declaredExplicit); } - /** - * basic constructor - byte encoded string. - */ - public DerVisibleString( - byte[] str) - : this(Strings.FromAsciiByteArray(str)) - { - } + private readonly byte[] m_contents; - /** - * basic constructor - */ - public DerVisibleString( - string str) + public DerVisibleString(string str) { if (str == null) throw new ArgumentNullException("str"); - this.str = str; + m_contents = Strings.ToAsciiByteArray(str); } - public override string GetString() + public DerVisibleString(byte[] contents) + : this(contents, true) { - return str; + } + + internal DerVisibleString(byte[] contents, bool clone) + { + if (null == contents) + throw new ArgumentNullException("contents"); + + m_contents = clone ? Arrays.Clone(contents) : contents; + } + + public override string GetString() + { + return Strings.FromAsciiByteArray(m_contents); } public byte[] GetOctets() { - return Strings.ToAsciiByteArray(str); + return Arrays.Clone(m_contents); } internal override IAsn1Encoding GetEncoding(int encoding) { - return new PrimitiveEncoding(Asn1Tags.Universal, Asn1Tags.VisibleString, GetOctets()); + return new PrimitiveEncoding(Asn1Tags.Universal, Asn1Tags.VisibleString, m_contents); } internal override IAsn1Encoding GetEncodingImplicit(int encoding, int tagClass, int tagNo) { - return new PrimitiveEncoding(tagClass, tagNo, GetOctets()); + return new PrimitiveEncoding(tagClass, tagNo, m_contents); } - protected override bool Asn1Equals( - Asn1Object asn1Object) - { - DerVisibleString other = asn1Object as DerVisibleString; - - if (other == null) - return false; + protected override bool Asn1Equals(Asn1Object asn1Object) + { + DerVisibleString that = asn1Object as DerVisibleString; + return null != that + && Arrays.AreEqual(this.m_contents, that.m_contents); + } - return this.str.Equals(other.str); + protected override int Asn1GetHashCode() + { + return Arrays.GetHashCode(m_contents); } - protected override int Asn1GetHashCode() - { - return this.str.GetHashCode(); + internal static DerVisibleString CreatePrimitive(byte[] contents) + { + return new DerVisibleString(contents, false); } } } diff --git a/crypto/src/asn1/ocsp/CertStatus.cs b/crypto/src/asn1/ocsp/CertStatus.cs index 7dd99b844..8a4d2242d 100644 --- a/crypto/src/asn1/ocsp/CertStatus.cs +++ b/crypto/src/asn1/ocsp/CertStatus.cs @@ -34,22 +34,23 @@ namespace Org.BouncyCastle.Asn1.Ocsp this.value = value; } - public CertStatus( - Asn1TaggedObject choice) + public CertStatus(Asn1TaggedObject choice) { this.tagNo = choice.TagNo; switch (choice.TagNo) { - case 1: - value = RevokedInfo.GetInstance(choice, false); - break; - case 0: - case 2: - value = DerNull.Instance; - break; - default: - throw new ArgumentException("Unknown tag encountered: " + choice.TagNo); + case 0: + value = Asn1Null.GetInstance(choice, false); + break; + case 1: + value = RevokedInfo.GetInstance(choice, false); + break; + case 2: + value = Asn1Null.GetInstance(choice, false); + break; + default: + throw new ArgumentException("Unknown tag encountered: " + Asn1Utilities.GetTagText(choice)); } } diff --git a/crypto/test/src/asn1/test/DERUTF8StringTest.cs b/crypto/test/src/asn1/test/DERUTF8StringTest.cs index be4dace79..15f98a7c4 100644 --- a/crypto/test/src/asn1/test/DERUTF8StringTest.cs +++ b/crypto/test/src/asn1/test/DERUTF8StringTest.cs @@ -66,8 +66,9 @@ namespace Org.BouncyCastle.Asn1.Tests byte[] b1 = new DerUtf8String(s).GetEncoded(); byte[] temp = new byte[b1.Length - 2]; Array.Copy(b1, 2, temp, 0, b1.Length - 2); - byte[] b2 = new DerUtf8String(new DerOctetString(temp).GetOctets()).GetEncoded(); - if (!Arrays.AreEqual(b1, b2)) + byte[] b2 = new DerUtf8String(Strings.FromUtf8ByteArray(new DerOctetString(temp).GetOctets())) + .GetEncoded(); + if (!Arrays.AreEqual(b1, b2)) { return new SimpleTestResult(false, Name + ": failed UTF-8 encoding and decoding"); } diff --git a/crypto/test/src/asn1/test/SetTest.cs b/crypto/test/src/asn1/test/SetTest.cs index 57c46603d..b7da0a9b7 100644 --- a/crypto/test/src/asn1/test/SetTest.cs +++ b/crypto/test/src/asn1/test/SetTest.cs @@ -83,7 +83,11 @@ namespace Org.BouncyCastle.Asn1.Tests // create an implicitly tagged "set" without sorting Asn1TaggedObject tag = new DerTaggedObject(false, 1, new DerSequence(v)); - s = Asn1Set.GetInstance(tag, false); + + // Encode/decode to get 'tag' as a parsed instance + tag = (Asn1TaggedObject)Asn1Object.FromByteArray(tag.GetEncoded(Asn1Encodable.Der));; + + s = Asn1Set.GetInstance(tag, false); if (s[0] is DerBoolean) { |