/// <summary> /// Reads the next value as a NamedBitList with tag UNIVERSAL 3, converting it to the /// [<see cref="FlagsAttribute"/>] enum specfied by <typeparamref name="TFlagsEnum"/>. /// </summary> /// <param name="expectedTag">The tag to check for before reading.</param> /// <typeparam name="TFlagsEnum">Destination enum type</typeparam> /// <returns> /// the NamedBitList value converted to a <typeparamref name="TFlagsEnum"/>. /// </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 --OR-- /// the encoded value is too big to fit in a <typeparamref name="TFlagsEnum"/> value /// </exception> /// <exception cref="ArgumentException"> /// <typeparamref name="TFlagsEnum"/> is not an enum type --OR-- /// <typeparamref name="TFlagsEnum"/> was not declared with <see cref="FlagsAttribute"/> /// --OR-- /// <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> /// <remarks> /// The bit alignment performed by this method is to interpret the most significant bit /// in the first byte of the value as the least significant bit in <typeparamref name="TFlagsEnum"/>, /// with bits increasing in value until the least significant bit of the first byte, proceeding /// with the most significant bit of the second byte, and so on. Under this scheme, the following /// ASN.1 type declaration and C# enumeration can be used together: /// /// <code> /// KeyUsage ::= BIT STRING { /// digitalSignature (0), /// nonRepudiation (1), /// keyEncipherment (2), /// dataEncipherment (3), /// keyAgreement (4), /// keyCertSign (5), /// cRLSign (6), /// encipherOnly (7), /// decipherOnly (8) } /// </code> /// /// <code> /// [Flags] /// enum KeyUsage /// { /// None = 0, /// DigitalSignature = 1 << (0), /// NonRepudiation = 1 << (1), /// KeyEncipherment = 1 << (2), /// DataEncipherment = 1 << (3), /// KeyAgreement = 1 << (4), /// KeyCertSign = 1 << (5), /// CrlSign = 1 << (6), /// EncipherOnly = 1 << (7), /// DecipherOnly = 1 << (8), /// } /// </code> /// /// Note that while the example here uses the KeyUsage NamedBitList from /// <a href="https://tools.ietf.org/html/rfc3280#section-4.2.1.3">RFC 3280 (4.2.1.3)</a>, /// the example enum uses values thar are different from /// System.Security.Cryptography.X509Certificates.X509KeyUsageFlags. /// </remarks> public TFlagsEnum ReadNamedBitListValue <TFlagsEnum>(Asn1Tag expectedTag) where TFlagsEnum : struct { Type tFlagsEnum = typeof(TFlagsEnum); return((TFlagsEnum)Enum.ToObject(tFlagsEnum, ReadNamedBitListValue(expectedTag, tFlagsEnum))); }
private string ReadObjectIdentifierAsString(Asn1Tag expectedTag, out int totalBytesRead) { Asn1Tag tag = ReadTagAndLength(out int? length, out int headerLength); CheckExpectedTag(tag, expectedTag, UniversalTagNumber.ObjectIdentifier); // T-REC-X.690-201508 sec 8.19.1 // T-REC-X.690-201508 sec 8.19.2 says the minimum length is 1 if (tag.IsConstructed || length < 1) { throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); } ReadOnlySpan<byte> contents = Slice(_data, headerLength, length!.Value); // Each byte can contribute a 3 digit value and a '.' (e.g. "126."), but usually // they convey one digit and a separator. // // The OID with the most arcs which were found after a 30 minute search is // "1.3.6.1.4.1.311.60.2.1.1" (EV cert jurisdiction of incorporation - locality) // which has 11 arcs. // The longest "known" segment is 16 bytes, a UUID-as-an-arc value. // 16 * 11 = 176 bytes for an "extremely long" OID. // // So pre-allocate the StringBuilder with at most 1020 characters, an input longer than // 255 encoded bytes will just have to re-allocate. StringBuilder builder = new StringBuilder(((byte)contents.Length) * 4); ReadSubIdentifier(contents, out int bytesRead, out long? smallValue, out BigInteger? largeValue); // T-REC-X.690-201508 sec 8.19.4 // The first two subidentifiers (X.Y) are encoded as (X * 40) + Y, because Y is // bounded [0, 39] for X in {0, 1}, and only X in {0, 1, 2} are legal. // So: // * identifier < 40 => X = 0, Y = identifier. // * identifier < 80 => X = 1, Y = identifier - 40. // * else: X = 2, Y = identifier - 80. byte firstArc; if (smallValue != null) { long firstIdentifier = smallValue.Value; if (firstIdentifier < 40) { firstArc = 0; } else if (firstIdentifier < 80) { firstArc = 1; firstIdentifier -= 40; } else { firstArc = 2; firstIdentifier -= 80; } builder.Append(firstArc); builder.Append('.'); builder.Append(firstIdentifier); } else { Debug.Assert(largeValue != null); BigInteger firstIdentifier = largeValue.Value; // We're only here because we were bigger than long.MaxValue, so // we're definitely on arc 2. Debug.Assert(firstIdentifier > long.MaxValue); firstArc = 2; firstIdentifier -= 80; builder.Append(firstArc); builder.Append('.'); builder.Append(firstIdentifier.ToString()); } contents = contents.Slice(bytesRead); while (!contents.IsEmpty) { ReadSubIdentifier(contents, out bytesRead, out smallValue, out largeValue); // Exactly one should be non-null. Debug.Assert((smallValue == null) != (largeValue == null)); builder.Append('.'); if (smallValue != null) { builder.Append(smallValue.Value); } else { builder.Append(largeValue!.Value.ToString()); } contents = contents.Slice(bytesRead); } totalBytesRead = headerLength + length.Value; return builder.ToString(); }
internal void Encode(AsnWriter writer, Asn1Tag tag) { writer.PushSequence(tag); // DEFAULT value handler for HashAlgorithm. { using (AsnWriter tmp = new AsnWriter(AsnEncodingRules.DER)) { HashAlgorithm.Encode(tmp); ReadOnlySpan <byte> encoded = tmp.EncodeAsSpan(); if (!encoded.SequenceEqual(s_defaultHashAlgorithm)) { writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); writer.WriteEncodedValue(encoded); writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); } } } // DEFAULT value handler for MaskGenAlgorithm. { using (AsnWriter tmp = new AsnWriter(AsnEncodingRules.DER)) { MaskGenAlgorithm.Encode(tmp); ReadOnlySpan <byte> encoded = tmp.EncodeAsSpan(); if (!encoded.SequenceEqual(s_defaultMaskGenAlgorithm)) { writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); writer.WriteEncodedValue(encoded); writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); } } } // DEFAULT value handler for SaltLength. { using (AsnWriter tmp = new AsnWriter(AsnEncodingRules.DER)) { tmp.WriteInteger(SaltLength); ReadOnlySpan <byte> encoded = tmp.EncodeAsSpan(); if (!encoded.SequenceEqual(s_defaultSaltLength)) { writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); writer.WriteEncodedValue(encoded); writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); } } } // DEFAULT value handler for TrailerField. { using (AsnWriter tmp = new AsnWriter(AsnEncodingRules.DER)) { tmp.WriteInteger(TrailerField); ReadOnlySpan <byte> encoded = tmp.EncodeAsSpan(); if (!encoded.SequenceEqual(s_defaultTrailerField)) { writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); writer.WriteEncodedValue(encoded); writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); } } } writer.PopSequence(tag); }
/// <summary> /// Reads the next value as a NamedBitList with tag UNIVERSAL 3, converting it to the /// [<see cref="FlagsAttribute"/>] enum specfied by <paramref name="tFlagsEnum"/>. /// </summary> /// <param name="expectedTag">The tag to check for before reading.</param> /// <param name="tFlagsEnum">Type object representing the destination type.</param> /// <returns> /// the NamedBitList value converted to a <paramref name="tFlagsEnum"/>. /// </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 --OR--- /// the encoded value is too big to fit in a <paramref name="tFlagsEnum"/> value /// </exception> /// <exception cref="ArgumentException"> /// <paramref name="tFlagsEnum"/> is not an enum type --OR-- /// <paramref name="tFlagsEnum"/> was not declared with <see cref="FlagsAttribute"/> /// --OR-- /// <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="ReadNamedBitListValue{TFlagsEnum}(Asn1Tag)"/> public Enum ReadNamedBitListValue(Asn1Tag expectedTag, Type tFlagsEnum) { // This will throw an ArgumentException if TEnum isn't an enum type, // so we don't need to validate it. Type backingType = tFlagsEnum.GetEnumUnderlyingType(); if (!tFlagsEnum.IsDefined(typeof(FlagsAttribute), false)) { throw new ArgumentException( SR.Cryptography_Asn_NamedBitListRequiresFlagsEnum, nameof(tFlagsEnum)); } int sizeLimit = Marshal.SizeOf(backingType); Span <byte> stackSpan = stackalloc byte[sizeLimit]; ReadOnlyMemory <byte> saveData = _data; // If TryCopyBitStringBytes succeeds but anything else fails _data will have moved, // so if anything throws here just move _data back to what it was. try { if (!TryCopyBitStringBytes(expectedTag, stackSpan, out int unusedBitCount, out int bytesWritten)) { throw new CryptographicException( SR.Format(SR.Cryptography_Asn_NamedBitListValueTooBig, tFlagsEnum.Name)); } if (bytesWritten == 0) { // The mode isn't relevant, zero is always zero. return((Enum)Enum.ToObject(tFlagsEnum, 0)); } ReadOnlySpan <byte> valueSpan = stackSpan.Slice(0, bytesWritten); // Now that the 0-bounds check is out of the way: // // T-REC-X.690-201508 sec 11.2.2 if (RuleSet == AsnEncodingRules.DER || RuleSet == AsnEncodingRules.CER) { byte lastByte = valueSpan[bytesWritten - 1]; // No unused bits tests 0x01, 1 is 0x02, 2 is 0x04, etc. // We already know that TryCopyBitStringBytes checked that the // declared unused bits were 0, this checks that the last "used" bit // isn't also zero. byte testBit = (byte)(1 << unusedBitCount); if ((lastByte & testBit) == 0) { throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); } } // Consider a NamedBitList defined as // // SomeList ::= BIT STRING { // a(0), b(1), c(2), d(3), e(4), f(5), g(6), h(7), i(8), j(9), k(10) // } // // The BIT STRING encoding of (a | j) is // unusedBitCount = 6, // contents: 0x80 0x40 (0b10000000_01000000) // // A the C# exposure of this structure we adhere to is // // [Flags] // enum SomeList // { // A = 1, // B = 1 << 1, // C = 1 << 2, // ... // } // // Which happens to be exactly backwards from how the bits are encoded, but the complexity // only needs to live here. return((Enum)Enum.ToObject(tFlagsEnum, InterpretNamedBitListReversed(valueSpan))); } catch { _data = saveData; throw; } }
internal void Encode(AsnWriter writer) { bool wroteValue = false; if (TeletexString != null) { if (wroteValue) { throw new CryptographicException(); } writer.WriteCharacterString(UniversalTagNumber.T61String, TeletexString); wroteValue = true; } if (PrintableString != null) { if (wroteValue) { throw new CryptographicException(); } writer.WriteCharacterString(UniversalTagNumber.PrintableString, PrintableString); wroteValue = true; } if (UniversalString.HasValue) { if (wroteValue) { throw new CryptographicException(); } // Validator for tag constraint for UniversalString { if (!Asn1Tag.TryDecode(UniversalString.Value.Span, out Asn1Tag validateTag, out _) || !validateTag.HasSameClassAndValue(new Asn1Tag((UniversalTagNumber)28))) { throw new CryptographicException(); } } writer.WriteEncodedValue(UniversalString.Value.Span); wroteValue = true; } if (Utf8String != null) { if (wroteValue) { throw new CryptographicException(); } writer.WriteCharacterString(UniversalTagNumber.UTF8String, Utf8String); wroteValue = true; } if (BmpString != null) { if (wroteValue) { throw new CryptographicException(); } writer.WriteCharacterString(UniversalTagNumber.BMPString, BmpString); wroteValue = true; } if (!wroteValue) { throw new CryptographicException(); } }
internal static void Decode(ref AsnValueReader reader, Asn1Tag expectedTag, ReadOnlyMemory <byte> rebind, out PssParamsAsn decoded) { decoded = default; AsnValueReader sequenceReader = reader.ReadSequence(expectedTag); AsnValueReader explicitReader; AsnValueReader defaultReader; if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 0))) { explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 0)); System.Security.Cryptography.Asn1.AlgorithmIdentifierAsn.Decode(ref explicitReader, rebind, out decoded.HashAlgorithm); explicitReader.ThrowIfNotEmpty(); } else { defaultReader = new AsnValueReader(s_defaultHashAlgorithm, AsnEncodingRules.DER); System.Security.Cryptography.Asn1.AlgorithmIdentifierAsn.Decode(ref defaultReader, rebind, out decoded.HashAlgorithm); } if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 1))) { explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 1)); System.Security.Cryptography.Asn1.AlgorithmIdentifierAsn.Decode(ref explicitReader, rebind, out decoded.MaskGenAlgorithm); explicitReader.ThrowIfNotEmpty(); } else { defaultReader = new AsnValueReader(s_defaultMaskGenAlgorithm, AsnEncodingRules.DER); System.Security.Cryptography.Asn1.AlgorithmIdentifierAsn.Decode(ref defaultReader, rebind, out decoded.MaskGenAlgorithm); } if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 2))) { explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 2)); if (!explicitReader.TryReadInt32(out decoded.SaltLength)) { explicitReader.ThrowIfNotEmpty(); } explicitReader.ThrowIfNotEmpty(); } else { defaultReader = new AsnValueReader(s_defaultSaltLength, AsnEncodingRules.DER); if (!defaultReader.TryReadInt32(out decoded.SaltLength)) { defaultReader.ThrowIfNotEmpty(); } } if (sequenceReader.HasData && sequenceReader.PeekTag().HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 3))) { explicitReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 3)); if (!explicitReader.TryReadInt32(out decoded.TrailerField)) { explicitReader.ThrowIfNotEmpty(); } explicitReader.ThrowIfNotEmpty(); } else { defaultReader = new AsnValueReader(s_defaultTrailerField, AsnEncodingRules.DER); if (!defaultReader.TryReadInt32(out decoded.TrailerField)) { defaultReader.ThrowIfNotEmpty(); } } sequenceReader.ThrowIfNotEmpty(); }
/// <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); }
// T-REC-X.690-201508 sec 8.19 private void WriteObjectIdentifierCore(Asn1Tag tag, ReadOnlySpan <char> oidValue) { CheckDisposed(); // T-REC-X.690-201508 sec 8.19.4 // The first character is in { 0, 1, 2 }, the second will be a '.', and a third (digit) // will also exist. if (oidValue.Length < 3) { throw new CryptographicException(SR.Argument_InvalidOidValue); } if (oidValue[1] != '.') { throw new CryptographicException(SR.Argument_InvalidOidValue); } // The worst case is "1.1.1.1.1", which takes 4 bytes (5 components, with the first two condensed) // Longer numbers get smaller: "2.1.127" is only 2 bytes. (81d (0x51) and 127 (0x7F)) // So length / 2 should prevent any reallocations. byte[] tmp = CryptoPool.Rent(oidValue.Length / 2); int tmpOffset = 0; try { int firstComponent = oidValue[0] switch { '0' => 0, '1' => 1, '2' => 2, _ => throw new CryptographicException(SR.Argument_InvalidOidValue), }; // The first two components are special: // ITU X.690 8.19.4: // The numerical value of the first subidentifier is derived from the values of the first two // object identifier components in the object identifier value being encoded, using the formula: // (X*40) + Y // where X is the value of the first object identifier component and Y is the value of the // second object identifier component. // NOTE - This packing of the first two object identifier components recognizes that only // three values are allocated from the root node, and at most 39 subsequent values from // nodes reached by X = 0 and X = 1. // skip firstComponent and the trailing . ReadOnlySpan <char> remaining = oidValue.Slice(2); BigInteger subIdentifier = ParseSubIdentifier(ref remaining); subIdentifier += 40 * firstComponent; int localLen = EncodeSubIdentifier(tmp.AsSpan(tmpOffset), ref subIdentifier); tmpOffset += localLen; while (!remaining.IsEmpty) { subIdentifier = ParseSubIdentifier(ref remaining); localLen = EncodeSubIdentifier(tmp.AsSpan(tmpOffset), ref subIdentifier); tmpOffset += localLen; } Debug.Assert(!tag.IsConstructed); WriteTag(tag); WriteLength(tmpOffset); Buffer.BlockCopy(tmp, 0, _buffer, _offset, tmpOffset); _offset += tmpOffset; } finally { CryptoPool.Return(tmp, tmpOffset); } }
private int ProcessConstructedBitString( ReadOnlyMemory <byte> source, Span <byte> destination, BitStringCopyAction?copyAction, bool isIndefinite, out int lastUnusedBitCount, out int bytesRead) { lastUnusedBitCount = 0; bytesRead = 0; int lastSegmentLength = MaxCERSegmentSize; AsnReader?tmpReader = new AsnReader(source, RuleSet); Stack <(AsnReader, bool, int)>?readerStack = null; int totalLength = 0; Asn1Tag tag = Asn1Tag.ConstructedBitString; Span <byte> curDest = destination; do { while (tmpReader.HasData) { tag = tmpReader.ReadTagAndLength(out int?length, out int headerLength); if (tag == Asn1Tag.PrimitiveBitString) { if (lastUnusedBitCount != 0) { // T-REC-X.690-201508 sec 8.6.4, only the last segment may have // a number of bits not a multiple of 8. throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); } if (RuleSet == AsnEncodingRules.CER && lastSegmentLength != MaxCERSegmentSize) { // T-REC-X.690-201508 sec 9.2 throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); } Debug.Assert(length != null); ReadOnlyMemory <byte> encodedValue = Slice(tmpReader._data, headerLength, length.Value); ParsePrimitiveBitStringContents( encodedValue, out lastUnusedBitCount, out ReadOnlyMemory <byte> contents, out byte normalizedLastByte); int localLen = headerLength + encodedValue.Length; tmpReader._data = tmpReader._data.Slice(localLen); bytesRead += localLen; totalLength += contents.Length; lastSegmentLength = encodedValue.Length; if (copyAction != null) { copyAction(contents, normalizedLastByte, curDest); curDest = curDest.Slice(contents.Length); } } else if (tag == Asn1Tag.EndOfContents && isIndefinite) { ValidateEndOfContents(tag, length, headerLength); bytesRead += headerLength; if (readerStack?.Count > 0) { (AsnReader topReader, bool wasIndefinite, int pushedBytesRead) = readerStack.Pop(); topReader._data = topReader._data.Slice(bytesRead); bytesRead += pushedBytesRead; isIndefinite = wasIndefinite; tmpReader = topReader; } else { // We have matched the EndOfContents that brought us here. break; } } else if (tag == Asn1Tag.ConstructedBitString) { if (RuleSet == AsnEncodingRules.CER) { // T-REC-X.690-201508 sec 9.2 throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); } if (readerStack == null) { readerStack = new Stack <(AsnReader, bool, int)>(); } readerStack.Push((tmpReader, isIndefinite, bytesRead)); tmpReader = new AsnReader( Slice(tmpReader._data, headerLength, length), RuleSet); bytesRead = headerLength; isIndefinite = (length == null); } else { // T-REC-X.690-201508 sec 8.6.4.1 (in particular, Note 2) throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); } } if (isIndefinite && tag != Asn1Tag.EndOfContents) { throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); } if (readerStack?.Count > 0) { (AsnReader topReader, bool wasIndefinite, int pushedBytesRead) = readerStack.Pop(); tmpReader = topReader; tmpReader._data = tmpReader._data.Slice(bytesRead); isIndefinite = wasIndefinite; bytesRead += pushedBytesRead; } else { tmpReader = null; } } while (tmpReader != null); return(totalLength); }
// T-REC-X.690-201508 sec 9.2, 8.7 private void WriteConstructedCerOctetString(Asn1Tag tag, ReadOnlySpan <byte> payload) { const int MaxCERSegmentSize = AsnReader.MaxCERSegmentSize; Debug.Assert(payload.Length > MaxCERSegmentSize); WriteTag(tag.AsConstructed()); WriteLength(-1); int fullSegments = Math.DivRem(payload.Length, MaxCERSegmentSize, out int lastSegmentSize); // The tag size is 1 byte. // The length will always be encoded as 82 03 E8 (3 bytes) // And 1000 content octets (by T-REC-X.690-201508 sec 9.2) const int FullSegmentEncodedSize = 1004; Debug.Assert( FullSegmentEncodedSize == 1 + 1 + MaxCERSegmentSize + GetEncodedLengthSubsequentByteCount(MaxCERSegmentSize)); int remainingEncodedSize; if (lastSegmentSize == 0) { remainingEncodedSize = 0; } else { // One byte of tag, and minimum one byte of length. remainingEncodedSize = 2 + lastSegmentSize + GetEncodedLengthSubsequentByteCount(lastSegmentSize); } // Reduce the number of copies by pre-calculating the size. // +2 for End-Of-Contents int expectedSize = fullSegments * FullSegmentEncodedSize + remainingEncodedSize + 2; EnsureWriteCapacity(expectedSize); byte[] ensureNoExtraCopy = _buffer; int savedOffset = _offset; ReadOnlySpan <byte> remainingData = payload; Span <byte> dest; Asn1Tag primitiveOctetString = Asn1Tag.PrimitiveOctetString; while (remainingData.Length > MaxCERSegmentSize) { // T-REC-X.690-201508 sec 8.7.3.2-note2 WriteTag(primitiveOctetString); WriteLength(MaxCERSegmentSize); dest = _buffer.AsSpan(_offset); remainingData.Slice(0, MaxCERSegmentSize).CopyTo(dest); _offset += MaxCERSegmentSize; remainingData = remainingData.Slice(MaxCERSegmentSize); } WriteTag(primitiveOctetString); WriteLength(remainingData.Length); dest = _buffer.AsSpan(_offset); remainingData.CopyTo(dest); _offset += remainingData.Length; WriteEndOfContents(); Debug.Assert(_offset - savedOffset == expectedSize, $"expected size was {expectedSize}, actual was {_offset - savedOffset}"); Debug.Assert(_buffer == ensureNoExtraCopy, $"_buffer was replaced during {nameof(WriteConstructedCerOctetString)}"); }
public static ReadOnlyMemory <byte> GetOctetStringBytes(this AsnReader reader, Asn1Tag expectedTag) { if (reader.TryGetPrimitiveOctetStringBytes(expectedTag, out ReadOnlyMemory <byte> contents)) { return(contents); } // Guaranteed too big, because it has the tag and length. int length = reader.PeekEncodedValue().Length; byte[] rented = ArrayPool <byte> .Shared.Rent(length); try { if (reader.TryCopyOctetStringBytes(expectedTag, rented, out int bytesWritten)) { return(new ReadOnlyMemory <byte>(rented.AsSpan(0, bytesWritten).ToArray())); } Debug.Fail("TryCopyOctetStringBytes produced more data than the encoded size"); throw new CryptographicException(); } finally { Array.Clear(rented, 0, length); ArrayPool <byte> .Shared.Return(rented); } }
public string ReadCharacterString(Asn1Tag expectedTag, UniversalTagNumber encodingType) { Text.Encoding encoding = AsnCharacterStringEncodings.GetEncoding(encodingType); return(ReadCharacterString(expectedTag, encodingType, encoding)); }
/// <summary> /// Reads the next value as an Enumerated with tag UNIVERSAL 10, converting it to the /// non-[<see cref="FlagsAttribute"/>] enum specfied by <typeparamref name="TEnum"/>. /// </summary> /// <param name="expectedTag">The tag to check for before reading.</param> /// <typeparam name="TEnum">Destination enum type</typeparam> /// <returns> /// the Enumerated value converted to a <typeparamref name="TEnum"/>. /// </returns> /// <remarks> /// This method does not validate that the return value is defined within /// <typeparamref name="TEnum"/>. /// </remarks> /// <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 --OR-- /// the encoded value is too big to fit in a <typeparamref name="TEnum"/> value /// </exception> /// <exception cref="ArgumentException"> /// <typeparamref name="TEnum"/> is not an enum type --OR-- /// <typeparamref name="TEnum"/> was declared with <see cref="FlagsAttribute"/> /// --OR-- /// <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 TEnum ReadEnumeratedValue <TEnum>(Asn1Tag expectedTag) where TEnum : struct { Type tEnum = typeof(TEnum); return((TEnum)Enum.ToObject(tEnum, ReadEnumeratedValue(expectedTag, tEnum))); }
internal void Encode(AsnWriter writer) { bool wroteValue = false; if (OtherName.HasValue) { if (wroteValue) { throw new CryptographicException(); } OtherName.Value.Encode(writer, new Asn1Tag(TagClass.ContextSpecific, 0)); wroteValue = true; } if (Rfc822Name != null) { if (wroteValue) { throw new CryptographicException(); } writer.WriteCharacterString(new Asn1Tag(TagClass.ContextSpecific, 1), UniversalTagNumber.IA5String, Rfc822Name); wroteValue = true; } if (DnsName != null) { if (wroteValue) { throw new CryptographicException(); } writer.WriteCharacterString(new Asn1Tag(TagClass.ContextSpecific, 2), UniversalTagNumber.IA5String, DnsName); wroteValue = true; } if (X400Address.HasValue) { if (wroteValue) { throw new CryptographicException(); } // Validator for tag constraint for X400Address { if (!Asn1Tag.TryParse(X400Address.Value.Span, out Asn1Tag validateTag, out _) || !validateTag.HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 3))) { throw new CryptographicException(); } } writer.WriteEncodedValue(X400Address.Value); wroteValue = true; } if (DirectoryName.HasValue) { if (wroteValue) { throw new CryptographicException(); } writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 4)); writer.WriteEncodedValue(DirectoryName.Value); writer.PopSequence(new Asn1Tag(TagClass.ContextSpecific, 4)); wroteValue = true; } if (EdiPartyName.HasValue) { if (wroteValue) { throw new CryptographicException(); } EdiPartyName.Value.Encode(writer, new Asn1Tag(TagClass.ContextSpecific, 5)); wroteValue = true; } if (Uri != null) { if (wroteValue) { throw new CryptographicException(); } writer.WriteCharacterString(new Asn1Tag(TagClass.ContextSpecific, 6), UniversalTagNumber.IA5String, Uri); wroteValue = true; } if (IPAddress.HasValue) { if (wroteValue) { throw new CryptographicException(); } writer.WriteOctetString(new Asn1Tag(TagClass.ContextSpecific, 7), IPAddress.Value.Span); wroteValue = true; } if (RegisteredId != null) { if (wroteValue) { throw new CryptographicException(); } writer.WriteObjectIdentifier(new Asn1Tag(TagClass.ContextSpecific, 8), RegisteredId); wroteValue = true; } if (!wroteValue) { throw new CryptographicException(); } }
internal static void Decode(AsnReader reader, out GeneralNameAsn decoded) { if (reader == null) { throw new ArgumentNullException(nameof(reader)); } decoded = default; Asn1Tag tag = reader.PeekTag(); AsnReader explicitReader; if (tag.HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 0))) { System.Security.Cryptography.Asn1.OtherNameAsn tmpOtherName; System.Security.Cryptography.Asn1.OtherNameAsn.Decode(reader, new Asn1Tag(TagClass.ContextSpecific, 0), out tmpOtherName); decoded.OtherName = tmpOtherName; } else if (tag.HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 1))) { decoded.Rfc822Name = reader.GetCharacterString(new Asn1Tag(TagClass.ContextSpecific, 1), UniversalTagNumber.IA5String); } else if (tag.HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 2))) { decoded.DnsName = reader.GetCharacterString(new Asn1Tag(TagClass.ContextSpecific, 2), UniversalTagNumber.IA5String); } else if (tag.HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 3))) { decoded.X400Address = reader.GetEncodedValue(); } else if (tag.HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 4))) { explicitReader = reader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 4)); decoded.DirectoryName = explicitReader.GetEncodedValue(); explicitReader.ThrowIfNotEmpty(); } else if (tag.HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 5))) { System.Security.Cryptography.Asn1.EdiPartyNameAsn tmpEdiPartyName; System.Security.Cryptography.Asn1.EdiPartyNameAsn.Decode(reader, new Asn1Tag(TagClass.ContextSpecific, 5), out tmpEdiPartyName); decoded.EdiPartyName = tmpEdiPartyName; } else if (tag.HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 6))) { decoded.Uri = reader.GetCharacterString(new Asn1Tag(TagClass.ContextSpecific, 6), UniversalTagNumber.IA5String); } else if (tag.HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 7))) { if (reader.TryGetPrimitiveOctetStringBytes(new Asn1Tag(TagClass.ContextSpecific, 7), out ReadOnlyMemory <byte> tmpIPAddress)) { decoded.IPAddress = tmpIPAddress; } else { decoded.IPAddress = reader.ReadOctetString(new Asn1Tag(TagClass.ContextSpecific, 7)); } } else if (tag.HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 8))) { decoded.RegisteredId = reader.ReadObjectIdentifierAsString(new Asn1Tag(TagClass.ContextSpecific, 8)); } else { throw new CryptographicException(); } }