public static void Decode(ref AsnValueReader reader, ReadOnlyMemory <byte> rebind, out GeneralNameAsn decoded) { decoded = default; Asn1Tag tag = reader.PeekTag(); AsnValueReader explicitReader; ReadOnlySpan <byte> rebindSpan = rebind.Span; int offset; ReadOnlySpan <byte> tmpSpan; if (tag.HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 0))) { Medikit.Security.Cryptography.Asn1.OtherNameAsn tmpOtherName; Medikit.Security.Cryptography.Asn1.OtherNameAsn.Decode(ref reader, new Asn1Tag(TagClass.ContextSpecific, 0), rebind, out tmpOtherName); decoded.OtherName = tmpOtherName; } else if (tag.HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 1))) { decoded.Rfc822Name = reader.ReadCharacterString(new Asn1Tag(TagClass.ContextSpecific, 1), UniversalTagNumber.IA5String); } else if (tag.HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 2))) { decoded.DnsName = reader.ReadCharacterString(new Asn1Tag(TagClass.ContextSpecific, 2), UniversalTagNumber.IA5String); } else if (tag.HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 3))) { tmpSpan = reader.ReadEncodedValue(); decoded.X400Address = rebindSpan.Overlaps(tmpSpan, out offset) ? rebind.Slice(offset, tmpSpan.Length) : tmpSpan.ToArray(); } else if (tag.HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 4))) { explicitReader = reader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 4)); tmpSpan = explicitReader.ReadEncodedValue(); decoded.DirectoryName = rebindSpan.Overlaps(tmpSpan, out offset) ? rebind.Slice(offset, tmpSpan.Length) : tmpSpan.ToArray(); explicitReader.ThrowIfNotEmpty(); } else if (tag.HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 5))) { Medikit.Security.Cryptography.Asn1.EdiPartyNameAsn tmpEdiPartyName; Medikit.Security.Cryptography.Asn1.EdiPartyNameAsn.Decode(ref reader, new Asn1Tag(TagClass.ContextSpecific, 5), rebind, out tmpEdiPartyName); decoded.EdiPartyName = tmpEdiPartyName; } else if (tag.HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 6))) { decoded.Uri = reader.ReadCharacterString(new Asn1Tag(TagClass.ContextSpecific, 6), UniversalTagNumber.IA5String); } else if (tag.HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 7))) { if (reader.TryReadPrimitiveOctetStringBytes(new Asn1Tag(TagClass.ContextSpecific, 7), out tmpSpan)) { decoded.IPAddress = rebindSpan.Overlaps(tmpSpan, out offset) ? rebind.Slice(offset, tmpSpan.Length) : tmpSpan.ToArray(); } 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(); } }
public 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.TryDecode(X400Address.Value.Span, out Asn1Tag validateTag, out _) || !validateTag.HasSameClassAndValue(new Asn1Tag(TagClass.ContextSpecific, 3))) { throw new CryptographicException(); } } writer.WriteEncodedValue(X400Address.Value.Span); wroteValue = true; } if (DirectoryName.HasValue) { if (wroteValue) { throw new CryptographicException(); } writer.PushSequence(new Asn1Tag(TagClass.ContextSpecific, 4)); writer.WriteEncodedValue(DirectoryName.Value.Span); 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(); } }
/// <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); } }
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(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(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(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(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 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))); }
/// <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); }
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)); Medikit.Security.Cryptography.Asn1.AlgorithmIdentifierAsn.Decode(ref explicitReader, rebind, out decoded.HashAlgorithm); explicitReader.ThrowIfNotEmpty(); } else { defaultReader = new AsnValueReader(DefaultHashAlgorithm, AsnEncodingRules.DER); Medikit.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)); Medikit.Security.Cryptography.Asn1.AlgorithmIdentifierAsn.Decode(ref explicitReader, rebind, out decoded.MaskGenAlgorithm); explicitReader.ThrowIfNotEmpty(); } else { defaultReader = new AsnValueReader(DefaultMaskGenAlgorithm, AsnEncodingRules.DER); Medikit.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(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(DefaultTrailerField, AsnEncodingRules.DER); if (!defaultReader.TryReadInt32(out decoded.TrailerField)) { defaultReader.ThrowIfNotEmpty(); } } sequenceReader.ThrowIfNotEmpty(); }
// T-REC-X.690-201508 sec 8.3 private void WriteNonNegativeIntegerCore(Asn1Tag tag, ulong value) { int valueLength; // 0x80 needs two bytes: 0x00 0x80 if (value < 0x80) { valueLength = 1; } else if (value < 0x8000) { valueLength = 2; } else if (value < 0x800000) { valueLength = 3; } else if (value < 0x80000000) { valueLength = 4; } else if (value < 0x80_00000000) { valueLength = 5; } else if (value < 0x8000_00000000) { valueLength = 6; } else if (value < 0x800000_00000000) { valueLength = 7; } else if (value < 0x80000000_00000000) { valueLength = 8; } else { valueLength = 9; } // Clear the constructed bit, if it was set. Debug.Assert(!tag.IsConstructed); WriteTag(tag); WriteLength(valueLength); ulong remaining = value; int idx = _offset + valueLength - 1; do { _buffer[idx] = (byte)remaining; remaining >>= 8; idx--; } while (idx >= _offset); #if DEBUG if (valueLength > 1) { // T-REC-X.690-201508 sec 8.3.2 // Cannot start with 9 bits of 0 (or 9 bits of 1, but that's not this method). Debug.Assert(_buffer[_offset] != 0 || _buffer[_offset + 1] > 0x7F); } #endif _offset += valueLength; }
// T-REC-X.690-201508 sec 8.3 private void WriteIntegerCore(Asn1Tag tag, long value) { if (value >= 0) { WriteNonNegativeIntegerCore(tag, (ulong)value); return; } int valueLength; if (value >= sbyte.MinValue) { valueLength = 1; } else if (value >= short.MinValue) { valueLength = 2; } else if (value >= unchecked ((long)0xFFFFFFFF_FF800000)) { valueLength = 3; } else if (value >= int.MinValue) { valueLength = 4; } else if (value >= unchecked ((long)0xFFFFFF80_00000000)) { valueLength = 5; } else if (value >= unchecked ((long)0xFFFF8000_00000000)) { valueLength = 6; } else if (value >= unchecked ((long)0xFF800000_00000000)) { valueLength = 7; } else { valueLength = 8; } Debug.Assert(!tag.IsConstructed); WriteTag(tag); WriteLength(valueLength); long remaining = value; int idx = _offset + valueLength - 1; do { _buffer[idx] = (byte)remaining; remaining >>= 8; idx--; } while (idx >= _offset); #if DEBUG if (valueLength > 1) { // T-REC-X.690-201508 sec 8.3.2 // Cannot start with 9 bits of 1 (or 9 bits of 0, but that's not this method). Debug.Assert(_buffer[_offset] != 0xFF || _buffer[_offset + 1] < 0x80); } #endif _offset += valueLength; }
/// <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); }
/// <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); }
/// <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); }