/// <summary>
        ///   Reads a GeneralizedTime value from <paramref name="source"/> with a specified tag under
        ///   the specified encoding rules.
        /// </summary>
        /// <param name="source">The buffer containing encoded data.</param>
        /// <param name="ruleSet">The encoding constraints to use when interpreting the data.</param>
        /// <param name="bytesConsumed">
        ///   When this method returns, the total number of bytes for the encoded value.
        ///   This parameter is treated as uninitialized.
        /// </param>
        /// <param name="expectedTag">
        ///   The tag to check for before reading, or <see langword="null"/> for the default tag (Universal 24).
        /// </param>
        /// <returns>
        ///   The decoded value.
        /// </returns>
        /// <exception cref="ArgumentOutOfRangeException">
        ///   <paramref name="ruleSet"/> is not defined.
        /// </exception>
        /// <exception cref="AsnContentException">
        ///   the next value does not have the correct tag.
        ///
        ///   -or-
        ///
        ///   the length encoding is not valid under the current encoding rules.
        ///
        ///   -or-
        ///
        ///   the contents are not valid under the current encoding rules.
        /// </exception>
        /// <exception cref="ArgumentException">
        ///   <paramref name="expectedTag"/>.<see cref="Asn1Tag.TagClass"/> is
        ///   <see cref="TagClass.Universal"/>, but
        ///   <paramref name="expectedTag"/>.<see cref="Asn1Tag.TagValue"/> is not correct for
        ///   the method.
        /// </exception>
        public static DateTimeOffset ReadGeneralizedTime(
            ReadOnlySpan <byte> source,
            AsnEncodingRules ruleSet,
            out int bytesConsumed,
            Asn1Tag?expectedTag = null)
        {
            byte[]? rented = null;

            // An X.509 time is 15 characters (yyyyMMddHHmmssZ), beyond that is fractions (no limit) or
            // BER specified offset.
            const int   StackBufSize = 64;
            Span <byte> tmpSpace     = stackalloc byte[StackBufSize];

            ReadOnlySpan <byte> contents = GetOctetStringContents(
                source,
                ruleSet,
                expectedTag ?? Asn1Tag.GeneralizedTime,
                UniversalTagNumber.GeneralizedTime,
                out int bytesRead,
                ref rented,
                tmpSpace);

            DateTimeOffset value = ParseGeneralizedTime(ruleSet, contents);

            if (rented != null)
            {
                CryptoPool.Return(rented, contents.Length);
            }

            bytesConsumed = bytesRead;
            return(value);
        }
Example #2
0
            private static DSA DecodeDsaPublicKey(byte[] encodedKeyValue, byte[] encodedParameters)
            {
                SubjectPublicKeyInfoAsn spki = new SubjectPublicKeyInfoAsn
                {
                    Algorithm = new AlgorithmIdentifierAsn {
                        Algorithm = Oids.Dsa, Parameters = encodedParameters
                    },
                    SubjectPublicKey = encodedKeyValue,
                };

                AsnWriter writer = new AsnWriter(AsnEncodingRules.DER);

                spki.Encode(writer);

                byte[] rented = CryptoPool.Rent(writer.GetEncodedLength());

                int written = writer.Encode(rented);

                DSA         dsa       = DSA.Create();
                IDisposable?toDispose = dsa;

                try
                {
                    dsa.ImportSubjectPublicKeyInfo(rented.AsSpan(0, written), out _);
                    toDispose = null;
                    return(dsa);
                }
                finally
                {
                    toDispose?.Dispose();
                    CryptoPool.Return(rented, written);
                }
            }
        /// <summary>
        ///   Reads an Octet String value from <paramref name="source"/> with a specified tag under
        ///   the specified encoding rules, returning the contents in a new array.
        /// </summary>
        /// <param name="source">The buffer containing encoded data.</param>
        /// <param name="ruleSet">The encoding constraints to use when interpreting the data.</param>
        /// <param name="bytesConsumed">
        ///   When this method returns, the total number of bytes for the encoded value.
        ///   This parameter is treated as uninitialized.
        /// </param>
        /// <param name="expectedTag">
        ///   The tag to check for before reading, or <see langword="null"/> for the default tag (Universal 4).
        /// </param>
        /// <returns>
        ///   An array containing the contents of the Octet String value.
        /// </returns>
        /// <exception cref="ArgumentOutOfRangeException">
        ///   <paramref name="ruleSet"/> is not defined.
        /// </exception>
        /// <exception cref="AsnContentException">
        ///   the next value does not have the correct tag.
        ///
        ///   -or-
        ///
        ///   the length encoding is not valid under the current encoding rules.
        ///
        ///   -or-
        ///
        ///   the contents are not valid under the current encoding rules.
        /// </exception>
        /// <exception cref="ArgumentException">
        ///   <paramref name="expectedTag"/>.<see cref="Asn1Tag.TagClass"/> is
        ///   <see cref="TagClass.Universal"/>, but
        ///   <paramref name="expectedTag"/>.<see cref="Asn1Tag.TagValue"/> is not correct for
        ///   the method.
        /// </exception>
        /// <seealso cref="TryReadPrimitiveOctetString"/>
        /// <seealso cref="TryReadOctetString"/>
        public static byte[] ReadOctetString(
            ReadOnlySpan <byte> source,
            AsnEncodingRules ruleSet,
            out int bytesConsumed,
            Asn1Tag?expectedTag = null)
        {
            byte[]? rented = null;

            ReadOnlySpan <byte> contents = GetOctetStringContents(
                source,
                ruleSet,
                expectedTag ?? Asn1Tag.PrimitiveOctetString,
                UniversalTagNumber.OctetString,
                out int consumed,
                ref rented);

            byte[] ret = contents.ToArray();

            if (rented != null)
            {
                CryptoPool.Return(rented, contents.Length);
            }

            bytesConsumed = consumed;
            return(ret);
        }
        /// <summary>
        ///   Reads the next value as a GeneralizedTime with a specified tag.
        /// </summary>
        /// <param name="expectedTag">The tag to check for before reading.</param>
        /// <param name="disallowFractions">
        ///   <c>true</c> to cause a <see cref="CryptographicException"/> to be thrown if a
        ///   fractional second is encountered, such as the restriction on the PKCS#7 Signing
        ///   Time attribute.
        /// </param>
        /// <returns>
        ///   a DateTimeOffset representing the value encoded in the GeneralizedTime.
        /// </returns>
        /// <exception cref="CryptographicException">
        ///   the next value does not have the correct tag --OR--
        ///   the length encoding is not valid under the current encoding rules --OR--
        ///   the contents are not valid under the current encoding rules
        /// </exception>
        /// <exception cref="ArgumentException">
        ///   <paramref name="expectedTag"/>.<see cref="Asn1Tag.TagClass"/> is
        ///   <see cref="TagClass.Universal"/>, but
        ///   <paramref name="expectedTag"/>.<see cref="Asn1Tag.TagValue"/> is not correct for
        ///   the method
        /// </exception>
        public DateTimeOffset ReadGeneralizedTime(Asn1Tag expectedTag, bool disallowFractions = false)
        {
            byte[] rented = null;

            // An X.509 time is 15 characters (yyyyMMddHHmmssZ), beyond that is fractions (no limit) or
            // BER specified offset.
            Span <byte> tmpSpace = stackalloc byte[64];

            ReadOnlySpan <byte> contents = GetOctetStringContents(
                expectedTag,
                UniversalTagNumber.GeneralizedTime,
                out int bytesRead,
                ref rented,
                tmpSpace);

            DateTimeOffset value = ParseGeneralizedTime(RuleSet, contents, disallowFractions);

            if (rented != null)
            {
                CryptoPool.Return(rented, contents.Length);
            }

            _data = _data.Slice(bytesRead);
            return(value);
        }
Example #5
0
        private void WriteConstructedCerCharacterString(Asn1Tag tag, Text.Encoding encoding, ReadOnlySpan <char> str, int size)
        {
            Debug.Assert(size > AsnReader.MaxCERSegmentSize);

            byte[] tmp;

            unsafe
            {
                fixed(char *strPtr = &MemoryMarshal.GetReference(str))
                {
                    tmp = CryptoPool.Rent(size);

                    fixed(byte *destPtr = tmp)
                    {
                        int written = encoding.GetBytes(strPtr, str.Length, destPtr, tmp.Length);

                        if (written != size)
                        {
                            Debug.Fail(
                                $"Encoding produced different answer for GetByteCount ({size}) and GetBytes ({written})");
                            throw new InvalidOperationException();
                        }
                    }
                }
            }

            WriteConstructedCerOctetString(tag, tmp.AsSpan(0, size));
            CryptoPool.Return(tmp, size);
        }
Example #6
0
        private static void ReturnRentedContentInfos(ContentInfoAsn[] rentedContents)
        {
            for (int i = 0; i < rentedContents.Length; i++)
            {
                string contentType = rentedContents[i].ContentType;

                if (contentType == null)
                {
                    break;
                }

                if (contentType == DecryptedSentinel)
                {
                    ReadOnlyMemory <byte> content = rentedContents[i].Content;

                    if (!MemoryMarshal.TryGetArray(content, out ArraySegment <byte> segment))
                    {
                        Debug.Fail("Couldn't unpack decrypted buffer.");
                    }

                    CryptoPool.Return(segment);
                    rentedContents[0].Content = default;
                }
            }

            ArrayPool <ContentInfoAsn> .Shared.Return(rentedContents, clearArray : true);
        }
Example #7
0
        /// <summary>
        ///   Reads the next value as a Integer with a specified tag, returning the contents
        ///   as a <see cref="BigInteger"/> over the original data.
        /// </summary>
        /// <param name="expectedTag">The tag to check for before reading.</param>
        /// <returns>
        ///   The bytes of the Integer value, in signed big-endian form.
        /// </returns>
        /// <exception cref="CryptographicException">
        ///   the next value does not have the correct tag --OR--
        ///   the length encoding is not valid under the current encoding rules --OR--
        ///   the contents are not valid under the current encoding rules
        /// </exception>
        /// <exception cref="ArgumentException">
        ///   <paramref name="expectedTag"/>.<see cref="Asn1Tag.TagClass"/> is
        ///   <see cref="TagClass.Universal"/>, but
        ///   <paramref name="expectedTag"/>.<see cref="Asn1Tag.TagValue"/> is not correct for
        ///   the method
        /// </exception>
        public BigInteger ReadInteger(Asn1Tag expectedTag)
        {
            ReadOnlyMemory <byte> contents =
                this.GetIntegerContents(expectedTag, UniversalTagNumber.Integer, out int headerLength);

            // TODO: Split this for netcoreapp/netstandard to use the Big-Endian BigInteger parsing
            byte[]     tmp = CryptoPool.Rent(contents.Length);
            BigInteger value;

            try
            {
                byte fill = (contents.Span[0] & 0x80) == 0 ? (byte)0 : (byte)0xFF;

                // Fill the unused portions of tmp with positive or negative padding.
                new Span <byte>(tmp, contents.Length, tmp.Length - contents.Length).Fill(fill);
                contents.CopyTo(tmp);

                // Convert to Little-Endian.
                AsnWriter.Reverse(new Span <byte>(tmp, 0, contents.Length));
                value = new BigInteger(tmp);
            }
            finally
            {
                // Let CryptoPool.Return clear the whole tmp so that not even the sign bit
                // is returned to the array pool.
                CryptoPool.Return(tmp);
            }

            this._data = this._data.Slice(headerLength + contents.Length);
            return(value);
        }
Example #8
0
        /// <summary>
        ///   Reads the next value as an OCTET STRING with tag UNIVERSAL 4, returning the value
        ///   in a byte array.
        /// </summary>
        /// <param name="expectedTag">The tag to check for before reading.</param>
        /// <returns>
        ///   a copy of the value in a newly allocated, precisely sized, array.
        /// </returns>
        /// <exception cref="CryptographicException">
        ///   the next value does not have the correct tag --OR--
        ///   the length encoding is not valid under the current encoding rules --OR--
        ///   the contents are not valid under the current encoding rules
        /// </exception>
        /// <exception cref="ArgumentException">
        ///   <paramref name="expectedTag"/>.<see cref="Asn1Tag.TagClass"/> is
        ///   <see cref="TagClass.Universal"/>, but
        ///   <paramref name="expectedTag"/>.<see cref="Asn1Tag.TagValue"/> is not correct for
        ///   the method
        /// </exception>
        /// <seealso cref="TryReadPrimitiveOctetStringBytes(Asn1Tag,out ReadOnlyMemory{byte})"/>
        /// <seealso cref="TryCopyOctetStringBytes(Asn1Tag,Span{byte},out int)"/>
        /// <seealso cref="ReadOctetString()"/>
        public byte[] ReadOctetString(Asn1Tag expectedTag)
        {
            ReadOnlyMemory <byte> memory;

            if (TryReadPrimitiveOctetStringBytes(expectedTag, out memory))
            {
                return(memory.ToArray());
            }

            memory = PeekEncodedValue();

            // Guaranteed long enough
            byte[] rented     = CryptoPool.Rent(memory.Length);
            int    dataLength = 0;

            try
            {
                if (!TryCopyOctetStringBytes(expectedTag, rented, out dataLength))
                {
                    Debug.Fail("TryCopyOctetStringBytes failed with a pre-allocated buffer");
                    throw new CryptographicException();
                }

                byte[] alloc = new byte[dataLength];
                rented.AsSpan(0, dataLength).CopyTo(alloc);
                return(alloc);
            }
            finally
            {
                CryptoPool.Return(rented, dataLength);
            }
        }
Example #9
0
        /// <summary>
        ///   Reads an Integer value from <paramref name="source"/> with a specified tag under
        ///   the specified encoding rules.
        /// </summary>
        /// <param name="source">The buffer containing encoded data.</param>
        /// <param name="ruleSet">The encoding constraints to use when interpreting the data.</param>
        /// <param name="bytesConsumed">
        ///   When this method returns, the total number of bytes for the encoded value.
        ///   This parameter is treated as uninitialized.
        /// </param>
        /// <param name="expectedTag">
        ///   The tag to check for before reading, or <see langword="null"/> for the default tag (Universal 2).
        /// </param>
        /// <returns>
        ///   The decoded numeric value.
        /// </returns>
        /// <exception cref="ArgumentOutOfRangeException">
        ///   <paramref name="ruleSet"/> is not defined.
        /// </exception>
        /// <exception cref="AsnContentException">
        ///   the next value does not have the correct tag.
        ///
        ///   -or-
        ///
        ///   the length encoding is not valid under the current encoding rules.
        ///
        ///   -or-
        ///
        ///   the contents are not valid under the current encoding rules.
        /// </exception>
        /// <exception cref="ArgumentException">
        ///   <paramref name="expectedTag"/>.<see cref="Asn1Tag.TagClass"/> is
        ///   <see cref="TagClass.Universal"/>, but
        ///   <paramref name="expectedTag"/>.<see cref="Asn1Tag.TagValue"/> is not correct for
        ///   the method.
        /// </exception>
        public static BigInteger ReadInteger(
            ReadOnlySpan <byte> source,
            AsnEncodingRules ruleSet,
            out int bytesConsumed,
            Asn1Tag?expectedTag = null)
        {
            ReadOnlySpan <byte> contents = ReadIntegerBytes(source, ruleSet, out int consumed, expectedTag);

            // TODO: Split this for netcoreapp/netstandard to use the Big-Endian BigInteger parsing
            byte[]     tmp = CryptoPool.Rent(contents.Length);
            BigInteger value;

            try
            {
                byte fill = (contents[0] & 0x80) == 0 ? (byte)0 : (byte)0xFF;
                // Fill the unused portions of tmp with positive or negative padding.
                new Span <byte>(tmp, contents.Length, tmp.Length - contents.Length).Fill(fill);
                contents.CopyTo(tmp);
                // Convert to Little-Endian.
                AsnWriter.Reverse(new Span <byte>(tmp, 0, contents.Length));
                value = new BigInteger(tmp);
            }
            finally
            {
                // Let CryptoPool.Return clear the whole tmp so that not even the sign bit
                // is returned to the array pool.
                CryptoPool.Return(tmp);
            }

            bytesConsumed = consumed;
            return(value);
        }
Example #10
0
        /// <summary>
        ///   Reads an Integer value from <paramref name="source"/> with a specified tag under
        ///   the specified encoding rules.
        /// </summary>
        /// <param name="source">The buffer containing encoded data.</param>
        /// <param name="ruleSet">The encoding constraints to use when interpreting the data.</param>
        /// <param name="bytesConsumed">
        ///   When this method returns, the total number of bytes for the encoded value.
        ///   This parameter is treated as uninitialized.
        /// </param>
        /// <param name="expectedTag">
        ///   The tag to check for before reading, or <see langword="null"/> for the default tag (Universal 2).
        /// </param>
        /// <returns>
        ///   The decoded numeric value.
        /// </returns>
        /// <exception cref="ArgumentOutOfRangeException">
        ///   <paramref name="ruleSet"/> is not defined.
        /// </exception>
        /// <exception cref="AsnContentException">
        ///   the next value does not have the correct tag.
        ///
        ///   -or-
        ///
        ///   the length encoding is not valid under the current encoding rules.
        ///
        ///   -or-
        ///
        ///   the contents are not valid under the current encoding rules.
        /// </exception>
        /// <exception cref="ArgumentException">
        ///   <paramref name="expectedTag"/>.<see cref="Asn1Tag.TagClass"/> is
        ///   <see cref="TagClass.Universal"/>, but
        ///   <paramref name="expectedTag"/>.<see cref="Asn1Tag.TagValue"/> is not correct for
        ///   the method.
        /// </exception>
        public static BigInteger ReadInteger(
            ReadOnlySpan <byte> source,
            AsnEncodingRules ruleSet,
            out int bytesConsumed,
            Asn1Tag?expectedTag = null)
        {
            ReadOnlySpan <byte> contents = ReadIntegerBytes(source, ruleSet, out int consumed, expectedTag);

#if NETCOREAPP2_1_OR_GREATER
            BigInteger value = new BigInteger(contents, isBigEndian: true);
#else
            byte[]     tmp = CryptoPool.Rent(contents.Length);
            BigInteger value;

            try
            {
                byte fill = (contents[0] & 0x80) == 0 ? (byte)0 : (byte)0xFF;
                // Fill the unused portions of tmp with positive or negative padding.
                tmp.AsSpan(contents.Length, tmp.Length - contents.Length).Fill(fill);
                contents.CopyTo(tmp);
                // Convert to Little-Endian.
                tmp.AsSpan(0, contents.Length).Reverse();
                value = new BigInteger(tmp);
            }
            finally
            {
                // Let CryptoPool.Return clear the whole tmp so that not even the sign bit
                // is returned to the array pool.
                CryptoPool.Return(tmp);
            }
#endif

            bytesConsumed = consumed;
            return(value);
        }
Example #11
0
        /// <summary>
        ///   Reads a Bit String value from <paramref name="source"/> with a specified tag under
        ///   the specified encoding rules, returning the contents in a new array.
        /// </summary>
        /// <param name="source">The buffer containing encoded data.</param>
        /// <param name="ruleSet">The encoding constraints to use when interpreting the data.</param>
        /// <param name="unusedBitCount">
        ///   On success, receives the number of bits in the last byte which were reported as
        ///   "unused" by the writer.
        ///   This parameter is treated as uninitialized.
        /// </param>
        /// <param name="bytesConsumed">
        ///   When this method returns, the total number of bytes for the encoded value.
        ///   This parameter is treated as uninitialized.
        /// </param>
        /// <param name="expectedTag">
        ///   The tag to check for before reading, or <see langword="null"/> for the default tag (Universal 3).
        /// </param>
        /// <returns>
        ///   An array containing the contents of the Bit String value.
        /// </returns>
        /// <remarks>
        ///   The least significant bits in the last byte which are reported as "unused" by the
        ///   <paramref name="unusedBitCount"/> value will be copied into the return value
        ///   as unset bits, irrespective of their value in the encoded representation.
        /// </remarks>
        /// <exception cref="ArgumentOutOfRangeException">
        ///   <paramref name="ruleSet"/> is not defined.
        /// </exception>
        /// <exception cref="AsnContentException">
        ///   the next value does not have the correct tag.
        ///
        ///   -or-
        ///
        ///   the length encoding is not valid under the current encoding rules.
        ///
        ///   -or-
        ///
        ///   the contents are not valid under the current encoding rules.
        /// </exception>
        /// <exception cref="ArgumentException">
        ///   <paramref name="expectedTag"/>.<see cref="Asn1Tag.TagClass"/> is
        ///   <see cref="TagClass.Universal"/>, but
        ///   <paramref name="expectedTag"/>.<see cref="Asn1Tag.TagValue"/> is not correct for
        ///   the method.
        /// </exception>
        /// <seealso cref="TryReadPrimitiveBitString"/>
        /// <seealso cref="TryReadBitString"/>
        public static byte[] ReadBitString(
            ReadOnlySpan <byte> source,
            AsnEncodingRules ruleSet,
            out int unusedBitCount,
            out int bytesConsumed,
            Asn1Tag?expectedTag = null)
        {
            if (TryReadPrimitiveBitStringCore(
                    source,
                    ruleSet,
                    expectedTag ?? Asn1Tag.PrimitiveBitString,
                    out int?contentsLength,
                    out int headerLength,
                    out int localUbc,
                    out ReadOnlySpan <byte> localValue,
                    out int consumed,
                    out byte normalizedLastByte))
            {
                byte[] ret = localValue.ToArray();

                // Update the last byte in case it's a non-canonical byte in a BER encoding.
                if (localValue.Length > 0)
                {
                    ret[ret.Length - 1] = normalizedLastByte;
                }

                unusedBitCount = localUbc;
                bytesConsumed  = consumed;
                return(ret);
            }

            // If we get here, the tag was appropriate, but the encoding was constructed.

            // Guaranteed long enough
            int tooBig = contentsLength ?? SeekEndOfContents(source.Slice(headerLength), ruleSet);

            byte[] rented = CryptoPool.Rent(tooBig);

            if (TryCopyConstructedBitStringValue(
                    Slice(source, headerLength, contentsLength),
                    ruleSet,
                    rented,
                    contentsLength == null,
                    out localUbc,
                    out int bytesRead,
                    out int written))
            {
                byte[] ret = rented.AsSpan(0, written).ToArray();
                CryptoPool.Return(rented, written);
                unusedBitCount = localUbc;
                bytesConsumed  = headerLength + bytesRead;
                return(ret);
            }

            Debug.Fail("TryCopyConstructedBitStringValue failed with a pre-allocated buffer");
            throw new AsnContentException();
        }
Example #12
0
            private static byte[]? DecryptContent(
                ReadOnlyMemory <byte> encryptedContent,
                byte[] cek,
                AlgorithmIdentifierAsn contentEncryptionAlgorithm,
                out Exception?exception)
            {
                exception = null;
                int encryptedContentLength = encryptedContent.Length;

                byte[]? encryptedContentArray = CryptoPool.Rent(encryptedContentLength);

                try
                {
                    encryptedContent.CopyTo(encryptedContentArray);

                    using (SymmetricAlgorithm alg = OpenAlgorithm(contentEncryptionAlgorithm))
                    {
                        ICryptoTransform decryptor;

                        try
                        {
                            decryptor = alg.CreateDecryptor(cek, alg.IV);
                        }
                        catch (ArgumentException ae)
                        {
                            // Decrypting or deriving the symmetric key with the wrong key may still succeed
                            // but produce a symmetric key that is not the correct length.
                            throw new CryptographicException(SR.Cryptography_Cms_InvalidSymmetricKey, ae);
                        }

                        using (decryptor)
                        {
                            // If we extend this library to accept additional algorithm providers
                            // then a different array pool needs to be used.
                            Debug.Assert(alg.GetType().Assembly == typeof(Aes).Assembly);

                            return(decryptor.OneShot(
                                       encryptedContentArray,
                                       0,
                                       encryptedContentLength));
                        }
                    }
                }
                catch (CryptographicException e)
                {
                    exception = e;
                    return(null);
                }
                finally
                {
                    CryptoPool.Return(encryptedContentArray, encryptedContentLength);
                    encryptedContentArray = null;
                }
            }
Example #13
0
        private static string?GetCdpUrl(SafeX509Handle cert)
        {
            ArraySegment <byte> crlDistributionPoints =
                OpenSslX509CertificateReader.FindFirstExtension(cert, Oids.CrlDistributionPoints);

            if (crlDistributionPoints.Array == null)
            {
                return(null);
            }

            try
            {
                AsnValueReader reader         = new AsnValueReader(crlDistributionPoints, AsnEncodingRules.DER);
                AsnValueReader sequenceReader = reader.ReadSequence();
                reader.ThrowIfNotEmpty();

                while (sequenceReader.HasData)
                {
                    DistributionPointAsn.Decode(ref sequenceReader, crlDistributionPoints, out DistributionPointAsn distributionPoint);

                    // Only distributionPoint is supported
                    // Only fullName is supported, nameRelativeToCRLIssuer is for LDAP-based lookup.
                    if (distributionPoint.DistributionPoint.HasValue &&
                        distributionPoint.DistributionPoint.Value.FullName != null)
                    {
                        foreach (GeneralNameAsn name in distributionPoint.DistributionPoint.Value.FullName)
                        {
                            if (name.Uri != null &&
                                Uri.TryCreate(name.Uri, UriKind.Absolute, out Uri? uri) &&
                                uri.Scheme == "http")
                            {
                                return(name.Uri);
                            }
                        }
                    }
                }
            }
            catch (CryptographicException)
            {
                // Treat any ASN errors as if the extension was missing.
            }
            catch (AsnContentException)
            {
                // Treat any ASN errors as if the extension was missing.
            }
            finally
            {
                // The data came from a certificate, so it's public.
                CryptoPool.Return(crlDistributionPoints.Array, clearSize: 0);
            }

            return(null);
        }
Example #14
0
 protected override void Dispose(bool disposing)
 {
     if (disposing)
     {
         CryptoPool.Return(FR);
         FR = Array.Empty <byte>();
         CryptoPool.Return(FRE);
         FRE = Array.Empty <byte>();
         blockTransform.Dispose();
     }
     base.Dispose(disposing);
 }
        /// <summary>
        ///   Reads a UtcTime value from <paramref name="source"/> with a specified tag under
        ///   the specified encoding rules.
        /// </summary>
        /// <param name="source">The buffer containing encoded data.</param>
        /// <param name="ruleSet">The encoding constraints to use when interpreting the data.</param>
        /// <param name="bytesConsumed">
        ///   When this method returns, the total number of bytes for the encoded value.
        ///   This parameter is treated as uninitialized.
        /// </param>
        /// <param name="twoDigitYearMax">
        ///   The largest year to represent with this value.
        ///   The default value, 2049, represents the 1950-2049 range for X.509 certificates.
        /// </param>
        /// <param name="expectedTag">
        ///   The tag to check for before reading, or <see langword="null"/> for the default tag (Universal 24).
        /// </param>
        /// <returns>
        ///   The decoded value.
        /// </returns>
        /// <exception cref="ArgumentOutOfRangeException">
        ///   <paramref name="ruleSet"/> is not defined.
        ///
        ///   -or-
        ///
        ///   <paramref name="twoDigitYearMax"/> is not in the range [99, 9999].
        /// </exception>
        /// <exception cref="AsnContentException">
        ///   the next value does not have the correct tag.
        ///
        ///   -or-
        ///
        ///   the length encoding is not valid under the current encoding rules.
        ///
        ///   -or-
        ///
        ///   the contents are not valid under the current encoding rules.
        /// </exception>
        /// <exception cref="ArgumentException">
        ///   <paramref name="expectedTag"/>.<see cref="Asn1Tag.TagClass"/> is
        ///   <see cref="TagClass.Universal"/>, but
        ///   <paramref name="expectedTag"/>.<see cref="Asn1Tag.TagValue"/> is not correct for
        ///   the method.
        /// </exception>
        /// <seealso cref="System.Globalization.Calendar.TwoDigitYearMax"/>
        public static DateTimeOffset ReadUtcTime(
            ReadOnlySpan <byte> source,
            AsnEncodingRules ruleSet,
            out int bytesConsumed,
            int twoDigitYearMax = 2049,
            Asn1Tag?expectedTag = null)
        {
            if (twoDigitYearMax < 1 || twoDigitYearMax > 9999)
            {
                throw new ArgumentOutOfRangeException(nameof(twoDigitYearMax));
            }

            // T-REC-X.680-201510 sec 47.3 says it is IMPLICIT VisibleString, which means
            // that BER is allowed to do complex constructed forms.

            // The full allowed formats (T-REC-X.680-201510 sec 47.3)
            // YYMMDDhhmmZ  (a, b1, c1)
            // YYMMDDhhmm+hhmm (a, b1, c2+)
            // YYMMDDhhmm-hhmm (a, b1, c2-)
            // YYMMDDhhmmssZ (a, b2, c1)
            // YYMMDDhhmmss+hhmm (a, b2, c2+)
            // YYMMDDhhmmss-hhmm (a, b2, c2-)

            // CER and DER are restricted to YYMMDDhhmmssZ
            // T-REC-X.690-201510 sec 11.8

            // The longest format is 17 bytes.
            Span <byte> tmpSpace = stackalloc byte[17];

            byte[]? rented = null;

            ReadOnlySpan <byte> contents = GetOctetStringContents(
                source,
                ruleSet,
                expectedTag ?? Asn1Tag.UtcTime,
                UniversalTagNumber.UtcTime,
                out int bytesRead,
                ref rented,
                tmpSpace);

            DateTimeOffset value = ParseUtcTime(contents, ruleSet, twoDigitYearMax);

            if (rented != null)
            {
                Debug.Fail($"UtcTime did not fit in tmpSpace ({contents.Length} total)");
                CryptoPool.Return(rented, contents.Length);
            }

            bytesConsumed = bytesRead;
            return(value);
        }
Example #16
0
        private static string ReadCharacterStringCore(
            ReadOnlySpan <byte> source,
            AsnEncodingRules ruleSet,
            Asn1Tag expectedTag,
            UniversalTagNumber universalTagNumber,
            Text.Encoding encoding,
            out int bytesConsumed)
        {
            byte[]? rented = null;

            // T-REC-X.690-201508 sec 8.23.3, all character strings are encoded as octet strings.
            ReadOnlySpan <byte> contents = GetOctetStringContents(
                source,
                ruleSet,
                expectedTag,
                universalTagNumber,
                out int bytesRead,
                ref rented);

            string str;

            if (contents.Length == 0)
            {
                str = string.Empty;
            }
            else
            {
                unsafe
                {
                    fixed(byte *bytePtr = &MemoryMarshal.GetReference(contents))
                    {
                        try
                        {
                            str = encoding.GetString(bytePtr, contents.Length);
                        }
                        catch (DecoderFallbackException e)
                        {
                            throw new AsnContentException(SR.ContentException_DefaultMessage, e);
                        }
                    }
                }
            }

            if (rented != null)
            {
                CryptoPool.Return(rented, contents.Length);
            }

            bytesConsumed = bytesRead;
            return(str);
        }
Example #17
0
        public static byte[] DecodeOctetStringCore(byte[] encodedOctets)
        {
            // Read using BER because the CMS specification says the encoding is BER.
            AsnReader reader = new AsnReader(encodedOctets, AsnEncodingRules.BER);

            const int   ArbitraryStackLimit = 256;
            Span <byte> tmp = stackalloc byte[ArbitraryStackLimit];
            // Use stackalloc 0 so data can later hold a slice of tmp.
            ReadOnlySpan <byte> data = stackalloc byte[0];

            byte[] poolBytes = null;

            try
            {
                if (!reader.TryReadPrimitiveOctetStringBytes(out var contents))
                {
                    if (reader.TryCopyOctetStringBytes(tmp, out int bytesWritten))
                    {
                        data = tmp.Slice(0, bytesWritten);
                    }
                    else
                    {
                        poolBytes = CryptoPool.Rent(reader.PeekContentBytes().Length);

                        if (!reader.TryCopyOctetStringBytes(poolBytes, out bytesWritten))
                        {
                            Debug.Fail("TryCopyOctetStringBytes failed with a provably-large-enough buffer");
                            throw new CryptographicException();
                        }

                        data = new ReadOnlySpan <byte>(poolBytes, 0, bytesWritten);
                    }
                }
                else
                {
                    data = contents.Span;
                }

                reader.ThrowIfNotEmpty();

                return(data.ToArray());
            }
            finally
            {
                if (poolBytes != null)
                {
                    CryptoPool.Return(poolBytes, data.Length);
                }
            }
        }
        private void WriteConstructedCerCharacterString(Asn1Tag tag, Text.Encoding encoding, ReadOnlySpan <char> str, int size)
        {
            Debug.Assert(size > AsnReader.MaxCERSegmentSize);

            byte[] tmp     = CryptoPool.Rent(size);
            int    written = encoding.GetBytes(str, tmp);

            if (written != size)
            {
                Debug.Fail(
                    $"Encoding produced different answer for GetByteCount ({size}) and GetBytes ({written})");
                throw new InvalidOperationException();
            }

            WriteConstructedCerOctetString(tag, tmp.AsSpan(0, size));
            CryptoPool.Return(tmp, size);
        }
        public DateTimeOffset ReadUtcTime(Asn1Tag expectedTag, int twoDigitYearMax = 2049)
        {
            // T-REC-X.680-201510 sec 47.3 says it is IMPLICIT VisibleString, which means
            // that BER is allowed to do complex constructed forms.

            // The full allowed formats (T-REC-X.680-201510 sec 47.3)
            // YYMMDDhhmmZ  (a, b1, c1)
            // YYMMDDhhmm+hhmm (a, b1, c2+)
            // YYMMDDhhmm-hhmm (a, b1, c2-)
            // YYMMDDhhmmssZ (a, b2, c1)
            // YYMMDDhhmmss+hhmm (a, b2, c2+)
            // YYMMDDhhmmss-hhmm (a, b2, c2-)

            // CER and DER are restricted to YYMMDDhhmmssZ
            // T-REC-X.690-201510 sec 11.8

            byte[]? rented = null;
            Span <byte> tmpSpace;

            unsafe
            {
                // The longest format is 17 bytes.
                const int StackBufSize = 17;
                byte *    stackBuf     = stackalloc byte[StackBufSize];
                tmpSpace = new Span <byte>(stackBuf, StackBufSize);
            }

            ReadOnlySpan <byte> contents = GetOctetStringContents(
                expectedTag,
                UniversalTagNumber.UtcTime,
                out int bytesRead,
                ref rented,
                tmpSpace);

            DateTimeOffset value = ParseUtcTime(contents, twoDigitYearMax);

            if (rented != null)
            {
                Debug.Fail($"UtcTime did not fit in tmpSpace ({contents.Length} total)");
                CryptoPool.Return(rented, contents.Length);
            }

            _data = _data.Slice(bytesRead);
            return(value);
        }
Example #20
0
        private static bool TryReadCharacterStringCore(
            ReadOnlySpan <byte> source,
            AsnEncodingRules ruleSet,
            Asn1Tag expectedTag,
            UniversalTagNumber universalTagNumber,
            Text.Encoding encoding,
            Span <char> destination,
            out int bytesConsumed,
            out int charsWritten)
        {
            byte[]? rented = null;

            // T-REC-X.690-201508 sec 8.23.3, all character strings are encoded as octet strings.
            ReadOnlySpan <byte> contents = GetOctetStringContents(
                source,
                ruleSet,
                expectedTag,
                universalTagNumber,
                out int bytesRead,
                ref rented);

            bool copied = TryReadCharacterStringCore(
                contents,
                destination,
                encoding,
                out charsWritten);

            if (rented != null)
            {
                CryptoPool.Return(rented, contents.Length);
            }

            if (copied)
            {
                bytesConsumed = bytesRead;
            }
            else
            {
                bytesConsumed = 0;
            }

            return(copied);
        }
Example #21
0
        /// <summary>
        /// Builds an <see cref="X500DistinguishedName" /> that represents the encoded attributes.
        /// </summary>
        /// <returns>
        /// An <see cref="X500DistinguishedName" /> that represents the encoded attributes.
        /// </returns>
        public X500DistinguishedName Build()
        {
            _writer.Reset();

            using (_writer.PushSequence())
            {
                for (int i = _encodedComponents.Count - 1; i >= 0; i--)
                {
                    _writer.WriteEncodedValue(_encodedComponents[i]);
                }
            }

            byte[] rented              = CryptoPool.Rent(_writer.GetEncodedLength());
            int    encoded             = _writer.Encode(rented);
            X500DistinguishedName name = new X500DistinguishedName(rented.AsSpan(0, encoded));

            CryptoPool.Return(rented, clearSize: 0); // Distinguished Names do not contain sensitive information.
            return(name);
        }
Example #22
0
        public byte[] ExportPrivateKey(
            ReadOnlySpan <byte> passwordBytes,
            S2kParameters s2kParameters)
        {
            ECParameters ecParameters = new ECParameters();

            byte[] secretPart = Array.Empty <byte>();

            try
            {
                ecParameters = ecdh.ExportParameters(true);
                if (ecdh is X25519)
                {
                    Array.Reverse(ecParameters.D !);
                }

                int secretSize = MPInteger.GetMPEncodedLength(ecParameters.D !);
                secretPart = CryptoPool.Rent(secretSize);
                MPInteger.TryWriteInteger(ecParameters.D, secretPart, out var _);

                int encryptedSecretLength = S2kBasedEncryption.GetEncryptedLength(s2kParameters, secretSize);
                int estimatedLength       =
                    32 /* OID */ +
                    MPInteger.GetMPEncodedLength(ecParameters.Q.X !, ecParameters.Q.Y !) + 1 /* EC Point type */ +
                    4 /* KDF Parameters */ +
                    encryptedSecretLength;
                var destination = new byte[estimatedLength];
                WriteOpenPgpECParameters(ecParameters, destination, out int bytesWritten);
                WriteKDFParameters(destination.AsSpan(bytesWritten));

                S2kBasedEncryption.EncryptSecretKey(passwordBytes, s2kParameters, secretPart.AsSpan(0, secretSize), destination.AsSpan(bytesWritten + 4));
                return(destination.AsSpan(0, bytesWritten + 4 + encryptedSecretLength).ToArray());
            }
            finally
            {
                CryptoPool.Return(secretPart);
                if (ecParameters.D != null)
                {
                    CryptographicOperations.ZeroMemory(ecParameters.D);
                }
            }
        }