/// <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); }
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); }
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); }
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); }
/// <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); }
/// <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); } }
/// <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); }
/// <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); }
/// <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(); }
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; } }
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); }
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); }
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); }
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); }
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); }
/// <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); }
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); } } }