diff options
Diffstat (limited to 'crypto/src/asn1/DerObjectIdentifier.cs')
-rw-r--r-- | crypto/src/asn1/DerObjectIdentifier.cs | 78 |
1 files changed, 53 insertions, 25 deletions
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') |