diff options
Diffstat (limited to 'crypto/src/asn1')
-rw-r--r-- | crypto/src/asn1/Asn1InputStream.cs | 11 | ||||
-rw-r--r-- | crypto/src/asn1/Asn1RelativeOid.cs | 84 | ||||
-rw-r--r-- | crypto/src/asn1/DerObjectIdentifier.cs | 78 |
3 files changed, 116 insertions, 57 deletions
diff --git a/crypto/src/asn1/Asn1InputStream.cs b/crypto/src/asn1/Asn1InputStream.cs index 96b0a1c66..3b5eaaa95 100644 --- a/crypto/src/asn1/Asn1InputStream.cs +++ b/crypto/src/asn1/Asn1InputStream.cs @@ -377,7 +377,9 @@ namespace Org.BouncyCastle.Asn1 switch (tagNo) { case Asn1Tags.BmpString: + { return CreateDerBmpString(defIn); + } case Asn1Tags.Boolean: { GetBuffer(defIn, tmpBuffers, out var contents); @@ -390,9 +392,16 @@ namespace Org.BouncyCastle.Asn1 } case Asn1Tags.ObjectIdentifier: { + DerObjectIdentifier.CheckContentsLength(defIn.Remaining); bool usedBuffer = GetBuffer(defIn, tmpBuffers, out var contents); return DerObjectIdentifier.CreatePrimitive(contents, clone: usedBuffer); } + case Asn1Tags.RelativeOid: + { + Asn1RelativeOid.CheckContentsLength(defIn.Remaining); + bool usedBuffer = GetBuffer(defIn, tmpBuffers, out var contents); + return Asn1RelativeOid.CreatePrimitive(contents, clone: usedBuffer); + } } byte[] bytes = defIn.ToArray(); @@ -421,8 +430,6 @@ namespace Org.BouncyCastle.Asn1 return Asn1OctetString.CreatePrimitive(bytes); case Asn1Tags.PrintableString: return DerPrintableString.CreatePrimitive(bytes); - case Asn1Tags.RelativeOid: - return Asn1RelativeOid.CreatePrimitive(bytes, false); case Asn1Tags.T61String: return DerT61String.CreatePrimitive(bytes); case Asn1Tags.UniversalString: diff --git a/crypto/src/asn1/Asn1RelativeOid.cs b/crypto/src/asn1/Asn1RelativeOid.cs index f43a85479..1fbf83d5c 100644 --- a/crypto/src/asn1/Asn1RelativeOid.cs +++ b/crypto/src/asn1/Asn1RelativeOid.cs @@ -22,6 +22,16 @@ namespace Org.BouncyCastle.Asn1 } } + /// <summary>Implementation limit on the length of the contents octets for a Relative OID.</summary> + /// <remarks> + /// We adopt the same value used by OpenJDK for Object Identifier. In theory there is no limit on the length of + /// the contents, or the number of subidentifiers, or the length of individual subidentifiers. In practice, + /// supporting arbitrary lengths can lead to issues, e.g. denial-of-service attacks when attempting to convert a + /// parsed value to its (decimal) string form. + /// </remarks> + private const int MaxContentsLength = 4096; + private const int MaxIdentifierLength = MaxContentsLength * 4 - 1; + public static Asn1RelativeOid FromContents(byte[] contents) { if (contents == null) @@ -68,14 +78,18 @@ namespace Org.BouncyCastle.Asn1 { if (identifier == null) throw new ArgumentNullException(nameof(identifier)); - if (!IsValidIdentifier(identifier, 0)) + if (identifier.Length <= MaxIdentifierLength && IsValidIdentifier(identifier, from: 0)) { - oid = default; - return false; + byte[] contents = ParseIdentifier(identifier); + if (contents.Length <= MaxContentsLength) + { + oid = new Asn1RelativeOid(contents, identifier); + return true; + } } - oid = new Asn1RelativeOid(ParseIdentifier(identifier), identifier); - return true; + oid = default; + return false; } private const long LongLimit = (long.MaxValue >> 7) - 0x7F; @@ -85,31 +99,13 @@ namespace Org.BouncyCastle.Asn1 public Asn1RelativeOid(string identifier) { - if (identifier == null) - throw new ArgumentNullException("identifier"); - if (!IsValidIdentifier(identifier, 0)) - throw new FormatException("string " + identifier + " not a relative OID"); - - m_contents = ParseIdentifier(identifier); - m_identifier = identifier; - } - - private Asn1RelativeOid(Asn1RelativeOid oid, string branchID) - { - if (!IsValidIdentifier(branchID, 0)) - throw new FormatException("string " + branchID + " not a valid relative OID branch"); - - m_contents = Arrays.Concatenate(oid.m_contents, ParseIdentifier(branchID)); - m_identifier = oid.GetID() + "." + branchID; - } + CheckIdentifier(identifier); - private Asn1RelativeOid(byte[] contents, bool clone) - { - if (!IsValidContents(contents)) - throw new ArgumentException("invalid relative OID contents", nameof(contents)); + byte[] contents = ParseIdentifier(identifier); + CheckContentsLength(contents.Length); - m_contents = clone ? Arrays.Clone(contents) : contents; - m_identifier = null; + m_contents = contents; + m_identifier = identifier; } private Asn1RelativeOid(byte[] contents, string identifier) @@ -120,7 +116,14 @@ namespace Org.BouncyCastle.Asn1 public virtual Asn1RelativeOid Branch(string branchID) { - return new Asn1RelativeOid(this, branchID); + CheckIdentifier(branchID); + + byte[] branchContents = ParseIdentifier(branchID); + CheckContentsLength(m_contents.Length + branchContents.Length); + + return new Asn1RelativeOid( + contents: Arrays.Concatenate(m_contents, branchContents), + identifier: GetID() + "." + branchID); } public string GetID() @@ -165,9 +168,30 @@ namespace Org.BouncyCastle.Asn1 return new PrimitiveDerEncoding(tagClass, tagNo, m_contents); } + internal static void CheckContentsLength(int contentsLength) + { + if (contentsLength > MaxContentsLength) + throw new ArgumentException("exceeded relative OID contents length limit"); + } + + internal static void CheckIdentifier(string identifier) + { + if (identifier == null) + throw new ArgumentNullException(nameof(identifier)); + if (identifier.Length > MaxIdentifierLength) + throw new ArgumentException("exceeded relative OID contents length limit"); + if (!IsValidIdentifier(identifier, from: 0)) + throw new FormatException("string " + identifier + " not a valid relative OID"); + } + internal static Asn1RelativeOid CreatePrimitive(byte[] contents, bool clone) { - return new Asn1RelativeOid(contents, clone); + CheckContentsLength(contents.Length); + + if (!IsValidContents(contents)) + throw new ArgumentException("invalid relative OID contents", nameof(contents)); + + return new Asn1RelativeOid(clone ? Arrays.Clone(contents) : contents, identifier: null); } internal static bool IsValidContents(byte[] contents) diff --git a/crypto/src/asn1/DerObjectIdentifier.cs b/crypto/src/asn1/DerObjectIdentifier.cs index 04792cbdd..7e1d5c2ff 100644 --- a/crypto/src/asn1/DerObjectIdentifier.cs +++ b/crypto/src/asn1/DerObjectIdentifier.cs @@ -23,6 +23,16 @@ namespace Org.BouncyCastle.Asn1 } } + /// <summary>Implementation limit on the length of the contents octets for an Object Identifier.</summary> + /// <remarks> + /// We adopt the same value used by OpenJDK. In theory there is no limit on the length of the contents, or the + /// number of subidentifiers, or the length of individual subidentifiers. In practice, supporting arbitrary + /// lengths can lead to issues, e.g. denial-of-service attacks when attempting to convert a parsed value to its + /// (decimal) string form. + /// </remarks> + private const int MaxContentsLength = 4096; + private const int MaxIdentifierLength = MaxContentsLength * 4 + 1; + public static DerObjectIdentifier FromContents(byte[] contents) { if (contents == null) @@ -86,14 +96,18 @@ namespace Org.BouncyCastle.Asn1 { if (identifier == null) throw new ArgumentNullException(nameof(identifier)); - if (!IsValidIdentifier(identifier)) + if (identifier.Length <= MaxIdentifierLength && IsValidIdentifier(identifier)) { - oid = default; - return false; + byte[] contents = ParseIdentifier(identifier); + if (contents.Length <= MaxContentsLength) + { + oid = new DerObjectIdentifier(contents, identifier); + return true; + } } - oid = new DerObjectIdentifier(ParseIdentifier(identifier), identifier); - return true; + oid = default; + return false; } private const long LongLimit = (long.MaxValue >> 7) - 0x7F; @@ -105,22 +119,13 @@ namespace Org.BouncyCastle.Asn1 public DerObjectIdentifier(string identifier) { - if (identifier == null) - throw new ArgumentNullException("identifier"); - if (!IsValidIdentifier(identifier)) - throw new FormatException("string " + identifier + " not an OID"); + CheckIdentifier(identifier); - m_contents = ParseIdentifier(identifier); - m_identifier = identifier; - } + byte[] contents = ParseIdentifier(identifier); + CheckContentsLength(contents.Length); - private DerObjectIdentifier(byte[] contents, bool clone) - { - if (!Asn1RelativeOid.IsValidContents(contents)) - throw new ArgumentException("invalid OID contents", nameof(contents)); - - m_contents = clone ? Arrays.Clone(contents) : contents; - m_identifier = null; + m_contents = contents; + m_identifier = identifier; } private DerObjectIdentifier(byte[] contents, string identifier) @@ -131,11 +136,13 @@ namespace Org.BouncyCastle.Asn1 public virtual DerObjectIdentifier Branch(string branchID) { - if (!Asn1RelativeOid.IsValidIdentifier(branchID, 0)) - throw new FormatException("string " + branchID + " not a valid OID branch"); + Asn1RelativeOid.CheckIdentifier(branchID); + + byte[] branchContents = Asn1RelativeOid.ParseIdentifier(branchID); + CheckContentsLength(m_contents.Length + branchContents.Length); return new DerObjectIdentifier( - contents: Arrays.Concatenate(m_contents, Asn1RelativeOid.ParseIdentifier(branchID)), + contents: Arrays.Concatenate(m_contents, branchContents), identifier: GetID() + "." + branchID); } @@ -195,9 +202,27 @@ namespace Org.BouncyCastle.Asn1 return new PrimitiveDerEncoding(tagClass, tagNo, m_contents); } + internal static void CheckContentsLength(int contentsLength) + { + if (contentsLength > MaxContentsLength) + throw new ArgumentException("exceeded OID contents length limit"); + } + + internal static void CheckIdentifier(string identifier) + { + if (identifier == null) + throw new ArgumentNullException(nameof(identifier)); + if (identifier.Length > MaxIdentifierLength) + throw new ArgumentException("exceeded OID contents length limit"); + if (!IsValidIdentifier(identifier)) + throw new FormatException("string " + identifier + " not a valid OID"); + } + internal static DerObjectIdentifier CreatePrimitive(byte[] contents, bool clone) { - int index = Arrays.GetHashCode(contents); + CheckContentsLength(contents.Length); + + uint index = (uint)Arrays.GetHashCode(contents); index ^= index >> 20; index ^= index >> 10; @@ -207,7 +232,10 @@ namespace Org.BouncyCastle.Asn1 if (originalEntry != null && Arrays.AreEqual(contents, originalEntry.m_contents)) return originalEntry; - var newEntry = new DerObjectIdentifier(contents, clone); + if (!Asn1RelativeOid.IsValidContents(contents)) + throw new ArgumentException("invalid OID contents", nameof(contents)); + + var newEntry = new DerObjectIdentifier(clone ? Arrays.Clone(contents) : contents, identifier: null); var exchangedEntry = Interlocked.CompareExchange(ref Cache[index], newEntry, originalEntry); if (exchangedEntry != originalEntry) @@ -228,7 +256,7 @@ namespace Org.BouncyCastle.Asn1 if (first < '0' || first > '2') return false; - if (!Asn1RelativeOid.IsValidIdentifier(identifier, 2)) + if (!Asn1RelativeOid.IsValidIdentifier(identifier, from: 2)) return false; if (first == '2') |