Esempio n. 1
0
        /// <summary>
        ///   Write the provided <see cref="DateTimeOffset"/> as a GeneralizedTime with a specified
        ///   UNIVERSAL 24, optionally excluding the fractional seconds.
        /// </summary>
        /// <param name="tag">The tagto write.</param>
        /// <param name="value">The value to write.</param>
        /// <param name="omitFractionalSeconds">
        ///   <c>true</c> to treat the fractional seconds in <paramref name="value"/> as 0 even if
        ///   a non-zero value is present.
        /// </param>
        /// <exception cref="ArgumentException">
        ///   <paramref name="tag"/>.<see cref="Asn1Tag.TagClass"/> is
        ///   <see cref="TagClass.Universal"/>, but
        ///   <paramref name="tag"/>.<see cref="Asn1Tag.TagValue"/> is not correct for
        ///   the method
        /// </exception>
        /// <exception cref="ObjectDisposedException">The writer has been Disposed.</exception>
        /// <seealso cref="WriteGeneralizedTime(Medikit.Security.Cryptography.Asn1.Asn1Tag,System.DateTimeOffset,bool)"/>
        public void WriteGeneralizedTime(Asn1Tag tag, DateTimeOffset value, bool omitFractionalSeconds = false)
        {
            CheckUniversalTag(tag, UniversalTagNumber.GeneralizedTime);

            // Clear the constructed flag, if present.
            WriteGeneralizedTimeCore(tag.AsPrimitive(), value, omitFractionalSeconds);
        }
        /// <summary>
        ///   Write the provided <see cref="DateTimeOffset"/> as a UTCTime with a specified tag,
        ///   accepting the two-digit year as valid in context.
        /// </summary>
        /// <param name="tag">The tag to write.</param>
        /// <param name="value">The value to write.</param>
        /// <exception cref="ArgumentException">
        ///   <paramref name="tag"/>.<see cref="Asn1Tag.TagClass"/> is
        ///   <see cref="TagClass.Universal"/>, but
        ///   <paramref name="tag"/>.<see cref="Asn1Tag.TagValue"/> is not correct for
        ///   the method
        /// </exception>
        /// <exception cref="ObjectDisposedException">The writer has been Disposed.</exception>
        /// <seealso cref="WriteUtcTime(Asn1Tag,DateTimeOffset,int)"/>
        /// <seealso cref="System.Globalization.Calendar.TwoDigitYearMax"/>
        public void WriteUtcTime(Asn1Tag tag, DateTimeOffset value)
        {
            CheckUniversalTag(tag, UniversalTagNumber.UtcTime);

            // Clear the constructed flag, if present.
            WriteUtcTimeCore(tag.AsPrimitive(), value);
        }
        /// <summary>
        ///   Write a non-[<see cref="FlagsAttribute"/>] enum value as an Enumerated with
        ///   tag UNIVERSAL 10.
        /// </summary>
        /// <param name="tag">The tag to write.</param>
        /// <param name="enumValue">The boxed enumeration value to write.</param>
        /// <exception cref="ArgumentNullException">
        ///   <paramref name="enumValue"/> is <c>null</c>
        /// </exception>
        /// <exception cref="ArgumentException">
        ///   <paramref name="tag"/>.<see cref="Asn1Tag.TagClass"/> is
        ///   <see cref="TagClass.Universal"/>, but
        ///   <paramref name="tag"/>.<see cref="Asn1Tag.TagValue"/> is not correct for
        ///   the method --OR--
        ///   <paramref name="enumValue"/> is not a boxed enum value --OR--
        ///   the unboxed type of <paramref name="enumValue"/> is declared [<see cref="FlagsAttribute"/>]
        /// </exception>
        /// <exception cref="ObjectDisposedException">The writer has been Disposed.</exception>
        /// <seealso cref="WriteEnumeratedValue(Medikit.Security.Cryptography.Asn1.Asn1Tag,object)"/>
        /// <seealso cref="WriteEnumeratedValue{T}(T)"/>
        public void WriteEnumeratedValue(Asn1Tag tag, object enumValue)
        {
            if (enumValue == null)
            {
                throw new ArgumentNullException(nameof(enumValue));
            }

            WriteEnumeratedValue(tag.AsPrimitive(), enumValue.GetType(), enumValue);
        }
Esempio n. 4
0
        private void WriteBitStringCore(Asn1Tag tag, ReadOnlySpan <byte> bitString, int unusedBitCount)
        {
            // T-REC-X.690-201508 sec 8.6.2.2
            if (unusedBitCount < 0 || unusedBitCount > 7)
            {
                throw new ArgumentOutOfRangeException(
                          nameof(unusedBitCount),
                          unusedBitCount,
                          SR.Cryptography_Asn_UnusedBitCountRange);
            }

            CheckDisposed();

            // T-REC-X.690-201508 sec 8.6.2.3
            if (bitString.Length == 0 && unusedBitCount != 0)
            {
                throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
            }

            byte lastByte = bitString.IsEmpty ? (byte)0 : bitString[bitString.Length - 1];

            // T-REC-X.690-201508 sec 11.2
            //
            // This could be ignored for BER, but since DER is more common and
            // it likely suggests a program error on the caller, leave it enabled for
            // BER for now.
            if (!CheckValidLastByte(lastByte, unusedBitCount))
            {
                // TODO: Probably warrants a distinct message.
                throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
            }

            if (RuleSet == AsnEncodingRules.CER)
            {
                // T-REC-X.690-201508 sec 9.2
                //
                // If it's not within a primitive segment, use the constructed encoding.
                // (>= instead of > because of the unused bit count byte)
                if (bitString.Length >= AsnReader.MaxCERSegmentSize)
                {
                    WriteConstructedCerBitString(tag, bitString, unusedBitCount);
                    return;
                }
            }

            // Clear the constructed flag, if present.
            WriteTag(tag.AsPrimitive());
            // The unused bits byte requires +1.
            WriteLength(bitString.Length + 1);
            _buffer[_offset] = (byte)unusedBitCount;
            _offset++;
            bitString.CopyTo(_buffer.AsSpan(_offset));
            _offset += bitString.Length;
        }
Esempio n. 5
0
        // T-REC-X.690-201508 sec 8.23
        private void WriteCharacterStringCore(Asn1Tag tag, Encoding encoding, ReadOnlySpan <char> str)
        {
            int size = -1;

            // T-REC-X.690-201508 sec 9.2
            if (RuleSet == AsnEncodingRules.CER)
            {
                // TODO: Split this for netstandard vs netcoreapp for span?.
                unsafe
                {
                    fixed(char *strPtr = &MemoryMarshal.GetReference(str))
                    {
                        size = encoding.GetByteCount(strPtr, str.Length);

                        // If it exceeds the primitive segment size, use the constructed encoding.
                        if (size > AsnReader.MaxCERSegmentSize)
                        {
                            WriteConstructedCerCharacterString(tag, encoding, str, size);
                            return;
                        }
                    }
                }
            }

            // TODO: Split this for netstandard vs netcoreapp for span?.
            unsafe
            {
                fixed(char *strPtr = &MemoryMarshal.GetReference(str))
                {
                    if (size < 0)
                    {
                        size = encoding.GetByteCount(strPtr, str.Length);
                    }

                    // Clear the constructed tag, if present.
                    WriteTag(tag.AsPrimitive());
                    WriteLength(size);
                    Span <byte> dest = _buffer.AsSpan(_offset, size);

                    fixed(byte *destPtr = &MemoryMarshal.GetReference(dest))
                    {
                        int written = encoding.GetBytes(strPtr, str.Length, destPtr, dest.Length);

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

                    _offset += size;
                }
            }
        }
        /// <summary>
        ///   Write the provided <see cref="DateTimeOffset"/> as a UTCTime with a specified tag,
        ///   provided the year is in the allowed range.
        /// </summary>
        /// <param name="tag">The tag to write.</param>
        /// <param name="value">The value to write.</param>
        /// <param name="twoDigitYearMax">
        ///   The maximum valid year for <paramref name="value"/>, after conversion to UTC.
        ///   For the X.509 Time.utcTime range of 1950-2049, pass <c>2049</c>.
        /// </param>
        /// <exception cref="ArgumentException">
        ///   <paramref name="tag"/>.<see cref="Asn1Tag.TagClass"/> is
        ///   <see cref="TagClass.Universal"/>, but
        ///   <paramref name="tag"/>.<see cref="Asn1Tag.TagValue"/> is not correct for
        ///   the method
        /// </exception>
        /// <exception cref="ArgumentOutOfRangeException">
        ///   <paramref name="value"/>.<see cref="DateTimeOffset.Year"/> (after conversion to UTC)
        ///   is not in the range
        ///   (<paramref name="twoDigitYearMax"/> - 100, <paramref name="twoDigitYearMax"/>]
        /// </exception>
        /// <exception cref="ObjectDisposedException">The writer has been Disposed.</exception>
        /// <seealso cref="WriteUtcTime(Medikit.Security.Cryptography.Asn1.Asn1Tag,System.DateTimeOffset,int)"/>
        /// <seealso cref="System.Globalization.Calendar.TwoDigitYearMax"/>
        public void WriteUtcTime(Asn1Tag tag, DateTimeOffset value, int twoDigitYearMax)
        {
            CheckUniversalTag(tag, UniversalTagNumber.UtcTime);

            value = value.ToUniversalTime();

            if (value.Year > twoDigitYearMax || value.Year <= twoDigitYearMax - 100)
            {
                throw new ArgumentOutOfRangeException(nameof(value));
            }

            WriteUtcTimeCore(tag.AsPrimitive(), value);
        }
Esempio n. 7
0
        // T-REC-X.690-201508 sec 8.7
        private void WriteOctetStringCore(Asn1Tag tag, ReadOnlySpan <byte> octetString)
        {
            if (RuleSet == AsnEncodingRules.CER)
            {
                // If it's bigger than a primitive segment, use the constructed encoding
                // T-REC-X.690-201508 sec 9.2
                if (octetString.Length > AsnReader.MaxCERSegmentSize)
                {
                    WriteConstructedCerOctetString(tag, octetString);
                    return;
                }
            }

            // Clear the constructed flag, if present.
            WriteTag(tag.AsPrimitive());
            WriteLength(octetString.Length);
            octetString.CopyTo(_buffer.AsSpan(_offset));
            _offset += octetString.Length;
        }
 /// <summary>
 ///   Write a non-[<see cref="FlagsAttribute"/>] enum value as an Enumerated with
 ///   tag UNIVERSAL 10.
 /// </summary>
 /// <param name="tag">The tag to write.</param>
 /// <param name="enumValue">The boxed enumeration value to write.</param>
 /// <exception cref="ArgumentNullException">
 ///   <paramref name="enumValue"/> is <c>null</c>
 /// </exception>
 /// <exception cref="ArgumentException">
 ///   <paramref name="tag"/>.<see cref="Asn1Tag.TagClass"/> is
 ///   <see cref="TagClass.Universal"/>, but
 ///   <paramref name="tag"/>.<see cref="Asn1Tag.TagValue"/> is not correct for
 ///   the method --OR--
 ///   <typeparamref name="TEnum"/> is not an enum --OR--
 ///   <typeparamref name="TEnum"/> is declared [<see cref="FlagsAttribute"/>]
 /// </exception>
 /// <exception cref="ObjectDisposedException">The writer has been Disposed.</exception>
 /// <seealso cref="WriteEnumeratedValue(Asn1Tag,object)"/>
 /// <seealso cref="WriteEnumeratedValue{T}(T)"/>
 public void WriteEnumeratedValue <TEnum>(Asn1Tag tag, TEnum enumValue) where TEnum : struct
 {
     WriteEnumeratedValue(tag.AsPrimitive(), typeof(TEnum), enumValue);
 }
Esempio n. 9
0
        private void WriteBitStringCore <TState>(
            Asn1Tag tag,
            int byteLength,
            TState state,
            SpanAction <byte, TState> action,
            int unusedBitCount = 0)
        {
            if (byteLength == 0)
            {
                WriteBitStringCore(tag, ReadOnlySpan <byte> .Empty, unusedBitCount);
                return;
            }

            // T-REC-X.690-201508 sec 8.6.2.2
            if (unusedBitCount < 0 || unusedBitCount > 7)
            {
                throw new ArgumentOutOfRangeException(
                          nameof(unusedBitCount),
                          unusedBitCount,
                          SR.Cryptography_Asn_UnusedBitCountRange);
            }

            CheckDisposed();

            int         savedOffset = _offset;
            Span <byte> scratchSpace;

            byte[]? ensureNoExtraCopy = null;
            int expectedSize = 0;

            // T-REC-X.690-201508 sec 9.2
            //
            // If it's not within a primitive segment, use the constructed encoding.
            // (>= instead of > because of the unused bit count byte)
            bool segmentedWrite =
                RuleSet == AsnEncodingRules.CER && byteLength >= AsnReader.MaxCERSegmentSize;

            if (segmentedWrite)
            {
                // Rather than call the callback multiple times, grow the buffer to allow
                // for enough space for the final output, then return a buffer where the last segment
                // is in the correct place. (Data will shift backwards to the right spot while writing
                // other segments).
                expectedSize = DetermineCerBitStringTotalLength(tag, byteLength);
                EnsureWriteCapacity(expectedSize);
                int overhead = expectedSize - byteLength;

                // Start writing where the last content byte is in the correct place, which is
                // after all of the overhead, but ending before the two byte end-of-contents marker.
                int scratchStart = overhead - 2;
                ensureNoExtraCopy = _buffer;
                scratchSpace      = _buffer.AsSpan(scratchStart, byteLength);

                // Don't let gapped-writes be unpredictable.
                scratchSpace.Clear();
            }
            else
            {
                WriteTag(tag.AsPrimitive());
                // The unused bits byte requires +1.
                WriteLength(byteLength + 1);

                _buffer[_offset] = (byte)unusedBitCount;
                _offset++;

                scratchSpace = _buffer.AsSpan(_offset, byteLength);
            }

            action(scratchSpace, state);

            // T-REC-X.690-201508 sec 11.2
            //
            // This could be ignored for BER, but since DER is more common and
            // it likely suggests a program error on the caller, leave it enabled for
            // BER for now.
            if (!CheckValidLastByte(scratchSpace[byteLength - 1], unusedBitCount))
            {
                // Since we are restoring _offset we won't clear this on a grow or Dispose,
                // so clear it now.
                _offset = savedOffset;
                scratchSpace.Clear();

                // TODO: Probably warrants a distinct message.
                throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
            }

            if (segmentedWrite)
            {
                WriteConstructedCerBitString(tag, scratchSpace, unusedBitCount);
                Debug.Assert(_offset - savedOffset == expectedSize, $"expected size was {expectedSize}, actual was {_offset - savedOffset}");
                Debug.Assert(_buffer == ensureNoExtraCopy, $"_buffer was replaced during while writing a bit string via callback");
            }
            else
            {
                _offset += byteLength;
            }
        }
Esempio n. 10
0
        /// <summary>
        ///   Write NULL with a specified tag.
        /// </summary>
        /// <param name="tag">The tag to write.</param>
        /// <exception cref="ArgumentException">
        ///   <paramref name="tag"/>.<see cref="Asn1Tag.TagClass"/> is
        ///   <see cref="TagClass.Universal"/>, but
        ///   <paramref name="tag"/>.<see cref="Asn1Tag.TagValue"/> is not correct for
        ///   the method
        /// </exception>
        /// <exception cref="ObjectDisposedException">The writer has been Disposed.</exception>
        public void WriteNull(Asn1Tag tag)
        {
            CheckUniversalTag(tag, UniversalTagNumber.Null);

            WriteNullCore(tag.AsPrimitive());
        }
Esempio n. 11
0
        /// <summary>
        ///   Write an Object Identifier with a specified tag.
        /// </summary>
        /// <param name="tag">The tag to write.</param>
        /// <param name="oidValue">The object identifier to write.</param>
        /// <exception cref="ArgumentException">
        ///   <paramref name="tag"/>.<see cref="Asn1Tag.TagClass"/> is
        ///   <see cref="TagClass.Universal"/>, but
        ///   <paramref name="tag"/>.<see cref="Asn1Tag.TagValue"/> is not correct for
        ///   the method
        /// </exception>
        /// <exception cref="CryptographicException">
        ///   <paramref name="oidValue"/> is not a valid dotted decimal
        ///   object identifier
        /// </exception>
        /// <exception cref="ObjectDisposedException">The writer has been Disposed.</exception>
        public void WriteObjectIdentifier(Asn1Tag tag, ReadOnlySpan <char> oidValue)
        {
            CheckUniversalTag(tag, UniversalTagNumber.ObjectIdentifier);

            WriteObjectIdentifierCore(tag.AsPrimitive(), oidValue);
        }
Esempio n. 12
0
        /// <summary>
        ///   Write an Integer value with a specified tag.
        /// </summary>
        /// <param name="tag">The tag to write.</param>
        /// <param name="value">The value to write.</param>
        /// <exception cref="ArgumentException">
        ///   <paramref name="tag"/>.<see cref="Asn1Tag.TagClass"/> is
        ///   <see cref="TagClass.Universal"/>, but
        ///   <paramref name="tag"/>.<see cref="Asn1Tag.TagValue"/> is not correct for
        ///   the method
        /// </exception>
        /// <exception cref="ObjectDisposedException">The writer has been Disposed.</exception>
        public void WriteInteger(Asn1Tag tag, ulong value)
        {
            CheckUniversalTag(tag, UniversalTagNumber.Integer);

            WriteNonNegativeIntegerCore(tag.AsPrimitive(), value);
        }
Esempio n. 13
0
        /// <summary>
        ///   Write an Integer value with a specified tag.
        /// </summary>
        /// <param name="tag">The tag to write.</param>
        /// <param name="value">The integer value to write, in unsigned big-endian byte order.</param>
        /// <exception cref="ArgumentException">
        ///   <paramref name="tag"/>.<see cref="Asn1Tag.TagClass"/> is
        ///   <see cref="TagClass.Universal"/>, but
        ///   <paramref name="tag"/>.<see cref="Asn1Tag.TagValue"/> is not correct for
        ///   the method
        /// </exception>
        /// <exception cref="CryptographicException">
        ///   the 9 most sigificant bits are all unset
        /// </exception>
        /// <exception cref="ObjectDisposedException">The writer has been Disposed.</exception>
        public void WriteIntegerUnsigned(Asn1Tag tag, ReadOnlySpan <byte> value)
        {
            CheckUniversalTag(tag, UniversalTagNumber.Integer);

            WriteIntegerUnsignedCore(tag.AsPrimitive(), value);
        }
Esempio n. 14
0
        /// <summary>
        ///   Write an Integer value with a specified tag.
        /// </summary>
        /// <param name="tag">The tag to write.</param>
        /// <param name="value">The value to write.</param>
        /// <exception cref="ArgumentException">
        ///   <paramref name="tag"/>.<see cref="Asn1Tag.TagClass"/> is
        ///   <see cref="TagClass.Universal"/>, but
        ///   <paramref name="tag"/>.<see cref="Asn1Tag.TagValue"/> is not correct for
        ///   the method
        /// </exception>
        /// <exception cref="ObjectDisposedException">The writer has been Disposed.</exception>
        public void WriteInteger(Asn1Tag tag, BigInteger value)
        {
            CheckUniversalTag(tag, UniversalTagNumber.Integer);

            WriteIntegerCore(tag.AsPrimitive(), value);
        }
Esempio n. 15
0
        /// <summary>
        ///   Write a Boolean value with a specified tag.
        /// </summary>
        /// <param name="tag">The tag to write.</param>
        /// <param name="value">The value to write.</param>
        /// <exception cref="ArgumentException">
        ///   <paramref name="tag"/>.<see cref="Asn1Tag.TagClass"/> is
        ///   <see cref="TagClass.Universal"/>, but
        ///   <paramref name="tag"/>.<see cref="Asn1Tag.TagValue"/> is not correct for
        ///   the method
        /// </exception>
        public void WriteBoolean(Asn1Tag tag, bool value)
        {
            CheckUniversalTag(tag, UniversalTagNumber.Boolean);

            WriteBooleanCore(tag.AsPrimitive(), value);
        }