From 8bef2160dc81d3c29d5bf18950f4fdeaa4da06d0 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Mon, 8 Nov 2021 22:57:03 +0700 Subject: ASN.1: Add support for relative OIDs --- crypto/src/asn1/Asn1RelativeOid.cs | 296 +++++++++++++++++++++++++++++++++++++ 1 file changed, 296 insertions(+) create mode 100644 crypto/src/asn1/Asn1RelativeOid.cs (limited to 'crypto/src/asn1/Asn1RelativeOid.cs') diff --git a/crypto/src/asn1/Asn1RelativeOid.cs b/crypto/src/asn1/Asn1RelativeOid.cs new file mode 100644 index 000000000..e1ee5309a --- /dev/null +++ b/crypto/src/asn1/Asn1RelativeOid.cs @@ -0,0 +1,296 @@ +using System; +using System.IO; +using System.Text; + +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Asn1 +{ + public class Asn1RelativeOid + : Asn1Object + { + public static Asn1RelativeOid FromContents(byte[] contents) + { + return CreatePrimitive(contents, true); + } + + public static Asn1RelativeOid GetInstance(object obj) + { + if (obj == null || obj is Asn1RelativeOid) + { + return (Asn1RelativeOid)obj; + } + else if (obj is IAsn1Convertible) + { + Asn1Object asn1Object = ((IAsn1Convertible)obj).ToAsn1Object(); + if (asn1Object is Asn1RelativeOid) + return (Asn1RelativeOid)asn1Object; + } + else if (obj is byte[]) + { + try + { + return GetInstance(FromByteArray((byte[])obj)); + } + catch (IOException e) + { + throw new ArgumentException("failed to construct relative OID from byte[]: " + e.Message); + } + } + + throw new ArgumentException("illegal object in GetInstance: " + Platform.GetTypeName(obj), "obj"); + } + + 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()); + } + + private const long LongLimit = (Int64.MaxValue >> 7) - 0x7F; + + private readonly string identifier; + private byte[] contents; + + public Asn1RelativeOid(string identifier) + { + if (identifier == null) + throw new ArgumentNullException("identifier"); + if (!IsValidIdentifier(identifier, 0)) + throw new FormatException("string " + identifier + " not a relative OID"); + + this.identifier = identifier; + } + + private Asn1RelativeOid(Asn1RelativeOid oid, string branchID) + { + if (!IsValidIdentifier(branchID, 0)) + throw new FormatException("string " + branchID + " not a valid relative OID branch"); + + this.identifier = oid.Id + "." + branchID; + } + + private Asn1RelativeOid(byte[] contents, bool clone) + { + this.identifier = ParseContents(contents); + this.contents = clone ? Arrays.Clone(contents) : contents; + } + + public virtual Asn1RelativeOid Branch(string branchID) + { + return new Asn1RelativeOid(this, branchID); + } + + public string Id + { + get { return identifier; } + } + + public override string ToString() + { + return identifier; + } + + protected override bool Asn1Equals(Asn1Object asn1Object) + { + Asn1RelativeOid that = asn1Object as Asn1RelativeOid; + return null != that + && this.identifier == that.identifier; + } + + protected override int Asn1GetHashCode() + { + return identifier.GetHashCode(); + } + + internal override bool EncodeConstructed() + { + return false; + } + + internal override int EncodedLength(bool withID) + { + return Asn1OutputStream.GetLengthOfEncodingDL(withID, GetContents().Length); + } + + internal override void Encode(Asn1OutputStream asn1Out, bool withID) + { + asn1Out.WriteEncodingDL(withID, Asn1Tags.RelativeOid, GetContents()); + } + + private void DoOutput(MemoryStream bOut) + { + OidTokenizer tok = new OidTokenizer(identifier); + while (tok.HasMoreTokens) + { + string token = tok.NextToken(); + if (token.Length <= 18) + { + WriteField(bOut, Int64.Parse(token)); + } + else + { + WriteField(bOut, new BigInteger(token)); + } + } + } + + private byte[] GetContents() + { + lock (this) + { + if (contents == null) + { + MemoryStream bOut = new MemoryStream(); + DoOutput(bOut); + contents = bOut.ToArray(); + } + + return contents; + } + } + + internal static Asn1RelativeOid CreatePrimitive(byte[] contents, bool clone) + { + return new Asn1RelativeOid(contents, clone); + } + + internal static bool IsValidIdentifier(string identifier, int from) + { + int digitCount = 0; + + int pos = identifier.Length; + while (--pos >= from) + { + char ch = identifier[pos]; + + if (ch == '.') + { + if (0 == digitCount || (digitCount > 1 && identifier[pos + 1] == '0')) + return false; + + digitCount = 0; + } + else if ('0' <= ch && ch <= '9') + { + ++digitCount; + } + else + { + return false; + } + } + + if (0 == digitCount || (digitCount > 1 && identifier[pos + 1] == '0')) + return false; + + return true; + } + + internal static void WriteField(Stream outputStream, long fieldValue) + { + byte[] result = new byte[9]; + int pos = 8; + result[pos] = (byte)((int)fieldValue & 0x7F); + while (fieldValue >= (1L << 7)) + { + fieldValue >>= 7; + result[--pos] = (byte)((int)fieldValue | 0x80); + } + outputStream.Write(result, pos, 9 - pos); + } + + internal static void WriteField(Stream outputStream, BigInteger fieldValue) + { + int byteCount = (fieldValue.BitLength + 6) / 7; + if (byteCount == 0) + { + outputStream.WriteByte(0); + } + else + { + BigInteger tmpValue = fieldValue; + byte[] tmp = new byte[byteCount]; + for (int i = byteCount - 1; i >= 0; i--) + { + tmp[i] = (byte)(tmpValue.IntValue | 0x80); + tmpValue = tmpValue.ShiftRight(7); + } + tmp[byteCount - 1] &= 0x7F; + outputStream.Write(tmp, 0, tmp.Length); + } + } + + private static string ParseContents(byte[] contents) + { + StringBuilder objId = new StringBuilder(); + long value = 0; + BigInteger bigValue = null; + bool first = true; + + for (int i = 0; i != contents.Length; i++) + { + int b = contents[i]; + + if (value <= LongLimit) + { + value += b & 0x7F; + if ((b & 0x80) == 0) + { + if (first) + { + first = false; + } + else + { + objId.Append('.'); + } + + objId.Append(value); + value = 0; + } + else + { + value <<= 7; + } + } + else + { + if (bigValue == null) + { + bigValue = BigInteger.ValueOf(value); + } + bigValue = bigValue.Or(BigInteger.ValueOf(b & 0x7F)); + if ((b & 0x80) == 0) + { + if (first) + { + first = false; + } + else + { + objId.Append('.'); + } + + objId.Append(bigValue); + bigValue = null; + value = 0; + } + else + { + bigValue = bigValue.ShiftLeft(7); + } + } + } + + return objId.ToString(); + } + } +} -- cgit 1.4.1