public override byte[] DecodeOctetString(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. #if __MonoCS__ ReadOnlySpan <byte> data = new byte[0]; #else ReadOnlySpan <byte> data = stackalloc byte[0]; #endif byte[] poolBytes = null; try { if (!reader.TryGetPrimitiveOctetStringBytes(out var contents)) { if (reader.TryCopyOctetStringBytes(tmp, out int bytesWritten)) { data = tmp.Slice(0, bytesWritten); } else { poolBytes = ArrayPool <byte> .Shared.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; } if (reader.HasData) { throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); } return(data.ToArray()); } finally { if (poolBytes != null) { Array.Clear(poolBytes, 0, data.Length); ArrayPool <byte> .Shared.Return(poolBytes); } } }
public static byte[] DecodeOctetString(ReadOnlyMemory <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); } } }
public static void TryCopyOctetStringBytes_Success_CER_MaxPrimitiveLength() { // CER says that the maximum encoding length for an OctetString primitive // is 1000. // // So we need 04 [1000] { 1000 anythings } // 1000 => 0x3E8, so the length encoding is 82 03 E8. // 1000 + 3 + 1 == 1004 byte[] input = new byte[1004]; input[0] = 0x04; input[1] = 0x82; input[2] = 0x03; input[3] = 0xE8; // Content input[4] = 0x02; input[5] = 0xA0; input[1002] = 0xA5; input[1003] = 0xFC; byte[] output = new byte[1000]; AsnReader reader = new AsnReader(input, AsnEncodingRules.CER); bool success = reader.TryCopyOctetStringBytes( output, out int bytesWritten); Assert.True(success, "reader.TryCopyOctetStringBytes"); Assert.Equal(1000, bytesWritten); Assert.Equal( input.AsSpan(4).ByteArrayToHex(), output.ByteArrayToHex()); }
public static ReadOnlyMemory <byte> DecodeOctetStringAsMemory(ReadOnlyMemory <byte> encodedOctetString) { AsnReader reader = new AsnReader(encodedOctetString, AsnEncodingRules.BER); if (reader.PeekEncodedValue().Length != encodedOctetString.Length) { throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); } // Almost everything in X.509 is DER-encoded, which means Octet String values are // encoded as a primitive (non-segmented) // // Even in BER Octet Strings are usually encoded as a primitive. if (reader.TryReadPrimitiveOctetStringBytes(out ReadOnlyMemory <byte> primitiveContents)) { return(primitiveContents); } byte[] tooBig = new byte[encodedOctetString.Length]; if (reader.TryCopyOctetStringBytes(tooBig, out int bytesWritten)) { return(tooBig.AsMemory(0, bytesWritten)); } Debug.Fail("TryCopyOctetStringBytes failed with an over-allocated array"); throw new CryptographicException(); }
public static void TryCopyOctetStringBytes_ExtremelyNested() { byte[] dataBytes = new byte[4 * 16384]; // This will build 2^14 nested indefinite length values. // In the end, none of them contain any content. // // For what it's worth, the initial algorithm succeeded at 1061, and StackOverflowed with 1062. int end = dataBytes.Length / 2; // UNIVERSAL OCTET STRING [Constructed] const byte Tag = 0x20 | (byte)UniversalTagNumber.OctetString; for (int i = 0; i < end; i += 2) { dataBytes[i] = Tag; // Indefinite length dataBytes[i + 1] = 0x80; } AsnReader reader = new AsnReader(dataBytes, AsnEncodingRules.BER); int bytesWritten; Assert.True(reader.TryCopyOctetStringBytes(Span <byte> .Empty, out bytesWritten)); Assert.Equal(0, bytesWritten); }
internal static ReadOnlyMemory <byte> GetContent( ReadOnlyMemory <byte> wrappedContent, string contentType) { // Read the input. // // PKCS7's id-data is written in both PKCS#7 and CMS as an OCTET STRING wrapping // the arbitrary bytes, so the OCTET STRING must always be present. // // For other types, CMS says to always write an OCTET STRING, and to put the properly // encoded data within it. // PKCS#7 originally ommitted the OCTET STRING wrapper for this model, so this is the // dynamic adapter. // // See https://tools.ietf.org/html/rfc5652#section-5.2.1 byte[] rented = null; int bytesWritten = 0; try { AsnReader reader = new AsnReader(wrappedContent, AsnEncodingRules.BER); if (reader.TryGetPrimitiveOctetStringBytes(out ReadOnlyMemory <byte> inner)) { return(inner); } rented = ArrayPool <byte> .Shared.Rent(wrappedContent.Length); if (!reader.TryCopyOctetStringBytes(rented, out bytesWritten)) { Debug.Fail($"TryCopyOctetStringBytes failed with an array larger than the encoded value"); throw new CryptographicException(); } return(rented.AsSpan(0, bytesWritten).ToArray()); } catch (Exception) { if (contentType == Oids.Pkcs7Data) { throw; } } finally { if (rented != null) { rented.AsSpan(0, bytesWritten).Clear(); ArrayPool <byte> .Shared.Return(rented); } } // PKCS#7 encoding for something other than id-data. Debug.Assert(contentType != Oids.Pkcs7Data); return(wrappedContent); }
public static void TryCopyOctetStringBytes_Fails(PublicEncodingRules ruleSet, string inputHex) { byte[] inputData = inputHex.HexToByteArray(); AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); bool didRead = reader.TryCopyOctetStringBytes( Span <byte> .Empty, out int bytesWritten); Assert.False(didRead, "reader.TryCopyOctetStringBytes"); Assert.Equal(0, bytesWritten); }
private static void TryCopyOctetStringBytes_Throws( PublicEncodingRules ruleSet, byte[] input) { Assert.Throws <CryptographicException>( () => { AsnReader reader = new AsnReader(input, (AsnEncodingRules)ruleSet); reader.TryCopyOctetStringBytes( Span <byte> .Empty, out int bytesWritten); }); }
private static SymmetricAlgorithm OpenAlgorithm(AlgorithmIdentifierAsn contentEncryptionAlgorithm) { SymmetricAlgorithm alg = OpenAlgorithm(contentEncryptionAlgorithm.Algorithm); if (alg is RC2) { if (contentEncryptionAlgorithm.Parameters == null) { // Windows issues CRYPT_E_BAD_DECODE throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); } Rc2CbcParameters rc2Params = Rc2CbcParameters.Decode( contentEncryptionAlgorithm.Parameters.Value, AsnEncodingRules.BER); alg.KeySize = rc2Params.GetEffectiveKeyBits(); alg.IV = rc2Params.Iv.ToArray(); } else { if (contentEncryptionAlgorithm.Parameters == null) { // Windows issues CRYPT_E_BAD_DECODE throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); } AsnReader reader = new AsnReader(contentEncryptionAlgorithm.Parameters.Value, AsnEncodingRules.BER); if (reader.TryReadPrimitiveOctetStringBytes(out ReadOnlyMemory <byte> primitiveBytes)) { alg.IV = primitiveBytes.ToArray(); } else { byte[] iv = new byte[alg.BlockSize / 8]; if (!reader.TryCopyOctetStringBytes(iv, out int bytesWritten) || bytesWritten != iv.Length) { throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); } alg.IV = iv; } } return(alg); }
public static void TryCopyOctetStringBytes_Success( PublicEncodingRules ruleSet, string inputHex, string expectedHex) { byte[] inputData = inputHex.HexToByteArray(); byte[] output = new byte[expectedHex.Length / 2]; AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet); bool didRead = reader.TryCopyOctetStringBytes( output, out int bytesWritten); Assert.True(didRead, "reader.TryCopyOctetStringBytes"); Assert.Equal(expectedHex, output.AsSpan(0, bytesWritten).ByteArrayToHex()); }
public static ReadOnlyMemory <byte> DecodeOctetString(ReadOnlyMemory <byte> encodedOctetString) { AsnReader reader = new AsnReader(encodedOctetString, AsnEncodingRules.BER); if (reader.PeekEncodedValue().Length != encodedOctetString.Length) { throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding); } if (reader.TryGetPrimitiveOctetStringBytes(out ReadOnlyMemory <byte> primitiveContents)) { return(primitiveContents); } byte[] tooBig = new byte[encodedOctetString.Length]; if (reader.TryCopyOctetStringBytes(tooBig, out int bytesWritten)) { return(tooBig.AsMemory(0, bytesWritten)); } Debug.Fail("TryCopyOctetStringBytes failed with an over-allocated array"); throw new CryptographicException(); }
public KerberosError(ref AsnReader reader) { var tag = reader.ReadTagAndLength(out var contentLength, out var bytesRead); reader = reader.AdvanceReader(bytesRead); if (tag.TagClass != TagClass.Universal || tag.TagValue != (int)UniversalTagNumber.Sequence) { throw new InvalidOperationException("Not a sequence but " + tag); } while (reader.HasData) { tag = reader.ReadTagAndLength(out contentLength, out bytesRead); reader = reader.AdvanceReader(bytesRead); if (tag.TagClass == TagClass.ContextSpecific) { switch (tag.TagValue) { case 0: // pvno [0] INTEGER (5), ProtocolVersionNumber = (int)reader.GetInteger(); break; case 1: // msg-type [1] INTEGER (30), MessageType = (MessageType)(int)reader.GetInteger(); break; case 2: // ctime [2] KerberosTime OPTIONAL, CTime = reader.GetGeneralizedTime(disallowFractions: true); break; case 3: // cusec [3] Microseconds OPTIONAL, var cusec = reader.GetInteger(); CUsec = new Microseconds((int)cusec); break; case 4: // stime [4] KerberosTime, STime = reader.GetGeneralizedTime(disallowFractions: true); break; case 5: // susec [5] Microseconds, var susec = reader.GetInteger(); SUsec = new Microseconds((int)susec); break; case 6: // error-code [6] Int32, ErrorCode = (KrbErrorCode)(int)reader.GetInteger(); break; case 7: // crealm [7] Realm OPTIONAL, CRealm = reader.GetCharacterString(new Asn1Tag(UniversalTagNumber.GeneralString), KerberosTags.RealmTag); break; case 8: // cname [8] PrincipalName OPTIONAL, CName = new PrincipalName(ref reader); break; case 9: // realm [9] Realm -- service realm --, ServiceRealm = reader.GetCharacterString(new Asn1Tag(UniversalTagNumber.GeneralString), KerberosTags.RealmTag); break; case 10: // sname [10] PrincipalName -- service name --, SName = new PrincipalName(ref reader); break; case 11: // e-text [11] KerberosString OPTIONAL, EText = reader.GetCharacterString(new Asn1Tag(UniversalTagNumber.GeneralString), KerberosTags.KerberosStringTag); break; case 12: // e-data [12] OCTET STRING OPTIONAL EData = new byte[contentLength.Value]; reader.TryCopyOctetStringBytes(EData, out _); break; } } } }
public static unsafe ContentInfo TryDecryptCore( byte[] cek, string contentType, ReadOnlyMemory <byte>?content, AlgorithmIdentifierAsn contentEncryptionAlgorithm, out Exception exception) { if (content == null) { exception = null; return(new ContentInfo( new Oid(contentType), Array.Empty <byte>())); } byte[] decrypted = DecryptContent(content.Value, cek, contentEncryptionAlgorithm, out exception); if (exception != null) { return(null); } if (contentType == Oids.Pkcs7Data) { byte[] tmp = null; try { AsnReader reader = new AsnReader(decrypted, AsnEncodingRules.BER); if (reader.TryGetPrimitiveOctetStringBytes(out ReadOnlyMemory <byte> contents)) { decrypted = contents.ToArray(); } else { tmp = ArrayPool <byte> .Shared.Rent(decrypted.Length); if (reader.TryCopyOctetStringBytes(tmp, out int written)) { Span <byte> innerContents = new Span <byte>(tmp, 0, written); decrypted = innerContents.ToArray(); innerContents.Clear(); } else { Debug.Fail("Octet string grew during copy"); // If this happens (which requires decrypted was overwritten, which // shouldn't be possible), just leave decrypted alone. } } } catch (CryptographicException) { } finally { if (tmp != null) { // Already cleared ArrayPool <byte> .Shared.Return(tmp); } } } else { decrypted = GetAsnSequenceWithContentNoValidation(decrypted); } exception = null; return(new ContentInfo( new Oid(contentType), decrypted)); }
public static unsafe ContentInfo?TryDecryptCore( byte[] cek, string contentType, ReadOnlyMemory <byte>?content, AlgorithmIdentifierAsn contentEncryptionAlgorithm, out Exception?exception) { if (content == null) { exception = null; return(new ContentInfo( new Oid(contentType), Array.Empty <byte>())); } byte[]? decrypted = DecryptContent(content.Value, cek, contentEncryptionAlgorithm, out exception); if (exception != null) { return(null); } // Compat: Previous versions of the managed PAL encryptor would wrap the contents in an octet stream // which is not correct and is incompatible with other CMS readers. To maintain compatibility with // existing CMS that have the incorrect wrapping, we attempt to remove it. if (contentType == Oids.Pkcs7Data) { byte[]? tmp = null; try { AsnReader reader = new AsnReader(decrypted, AsnEncodingRules.BER); if (reader.TryReadPrimitiveOctetStringBytes(out ReadOnlyMemory <byte> contents)) { decrypted = contents.ToArray(); } else { tmp = CryptoPool.Rent(decrypted !.Length); if (reader.TryCopyOctetStringBytes(tmp, out int written)) { Span <byte> innerContents = new Span <byte>(tmp, 0, written); decrypted = innerContents.ToArray(); innerContents.Clear(); } else { Debug.Fail("Octet string grew during copy"); // If this happens (which requires decrypted was overwritten, which // shouldn't be possible), just leave decrypted alone. } } } catch (CryptographicException) { } finally { if (tmp != null) { // Already cleared CryptoPool.Return(tmp, clearSize: 0); } } } else { decrypted = GetAsnSequenceWithContentNoValidation(decrypted); } exception = null; return(new ContentInfo( new Oid(contentType), decrypted !)); }
public static void TryCopyOctetStringBytes_Success_CER_MinConstructedLength() { // CER says that the maximum encoding length for an OctetString primitive // is 1000, and that a constructed form must be used for values greater // than 1000 bytes, with segments dividing up for each thousand // [1000, 1000, ..., len%1000]. // // So our smallest constructed form is 1001 bytes, [1000, 1] // // 24 80 (indefinite constructed octet string) // 04 82 03 E9 (primitive octet string, 1000 bytes) // [1000 content bytes] // 04 01 (primitive octet string, 1 byte) // pp // 00 00 (end of contents, 0 bytes) // 1011 total. byte[] input = new byte[1011]; int offset = 0; // CONSTRUCTED OCTET STRING (Indefinite) input[offset++] = 0x24; input[offset++] = 0x80; // OCTET STRING (1000) input[offset++] = 0x04; input[offset++] = 0x82; input[offset++] = 0x03; input[offset++] = 0xE8; // Primitive 1: (55 A0 :: A5 FC) (1000) input[offset++] = 0x55; input[offset] = 0xA0; offset += 997; input[offset++] = 0xA5; input[offset++] = 0xFC; // OCTET STRING (1) input[offset++] = 0x04; input[offset++] = 0x01; // Primitive 2: One more byte input[offset] = 0xF7; byte[] expected = new byte[1001]; offset = 0; expected[offset++] = 0x55; expected[offset] = 0xA0; offset += 997; expected[offset++] = 0xA5; expected[offset++] = 0xFC; expected[offset] = 0xF7; byte[] output = new byte[1001]; AsnReader reader = new AsnReader(input, AsnEncodingRules.CER); bool success = reader.TryCopyOctetStringBytes( output, out int bytesWritten); Assert.True(success, "reader.TryCopyOctetStringBytes"); Assert.Equal(1001, bytesWritten); Assert.Equal( expected.ByteArrayToHex(), output.ByteArrayToHex()); }
internal static AlgorithmIdentifier ToPresentationObject(this AlgorithmIdentifierAsn asn) { int keyLength; switch (asn.Algorithm.Value) { case Oids.Rc2Cbc: { if (asn.Parameters == null) { keyLength = 0; break; } Rc2CbcParameters rc2Params = AsnSerializer.Deserialize <Rc2CbcParameters>( asn.Parameters.Value, AsnEncodingRules.BER); int keySize = rc2Params.GetEffectiveKeyBits(); // These are the only values .NET Framework would set. switch (keySize) { case 40: case 56: case 64: case 128: keyLength = keySize; break; default: keyLength = 0; break; } break; } case Oids.Rc4: { if (asn.Parameters == null) { keyLength = 0; break; } int saltLen = 0; AsnReader reader = new AsnReader(asn.Parameters.Value, AsnEncodingRules.BER); // DER NULL is considered the same as not present. // No call to ReadNull() is necessary because the serializer already verified that // there's no data after the [AnyValue] value. if (reader.PeekTag() != Asn1Tag.Null) { if (reader.TryGetPrimitiveOctetStringBytes(out ReadOnlyMemory <byte> contents)) { saltLen = contents.Length; } else { Span <byte> salt = stackalloc byte[KeyLengths.Rc4Max_128Bit / 8]; if (!reader.TryCopyOctetStringBytes(salt, out saltLen)) { throw new CryptographicException(); } } } keyLength = KeyLengths.Rc4Max_128Bit - 8 * saltLen; break; } case Oids.DesCbc: keyLength = KeyLengths.Des_64Bit; break; case Oids.TripleDesCbc: keyLength = KeyLengths.TripleDes_192Bit; break; default: // .NET Framework doesn't set a keylength for AES, or any other algorithm than the ones // listed here. keyLength = 0; break; } return(new AlgorithmIdentifier(new Oid(asn.Algorithm), keyLength)); }
public override unsafe ContentInfo TryDecrypt( RecipientInfo recipientInfo, X509Certificate2 cert, X509Certificate2Collection originatorCerts, X509Certificate2Collection extraStore, out Exception exception) { // When encryptedContent is null Windows seems to decrypt the CEK first, // then return a 0 byte answer. byte[] cek; if (recipientInfo.Pal is ManagedKeyTransPal ktri) { cek = ktri.DecryptCek(cert, out exception); } else { exception = new CryptographicException( SR.Cryptography_Cms_RecipientType_NotSupported, recipientInfo.Type.ToString()); return(null); } byte[] decrypted; // Pin CEK to prevent it from getting copied during heap compaction. fixed(byte *pinnedCek = cek) { try { if (exception != null) { return(null); } ReadOnlyMemory <byte>?encryptedContent = _envelopedData.EncryptedContentInfo.EncryptedContent; if (encryptedContent == null) { exception = null; return(new ContentInfo( new Oid(_envelopedData.EncryptedContentInfo.ContentType), Array.Empty <byte>())); } decrypted = DecryptContent(encryptedContent.Value, cek, out exception); } finally { if (cek != null) { Array.Clear(cek, 0, cek.Length); } } } if (exception != null) { return(null); } if (_envelopedData.EncryptedContentInfo.ContentType == Oids.Pkcs7Data) { byte[] tmp = null; try { AsnReader reader = new AsnReader(decrypted, AsnEncodingRules.BER); if (reader.TryGetPrimitiveOctetStringBytes(out ReadOnlyMemory <byte> contents)) { decrypted = contents.ToArray(); } else { tmp = ArrayPool <byte> .Shared.Rent(decrypted.Length); if (reader.TryCopyOctetStringBytes(tmp, out int written)) { Span <byte> innerContents = new Span <byte>(tmp, 0, written); decrypted = innerContents.ToArray(); innerContents.Clear(); } else { Debug.Fail("Octet string grew during copy"); // If this happens (which requires decrypted was overwritten, which // shouldn't be possible), just leave decrypted alone. } } } catch (CryptographicException) { } finally { if (tmp != null) { // Already cleared ArrayPool <byte> .Shared.Return(tmp); } } } else { decrypted = GetAsnSequenceWithContentNoValidation(decrypted); } exception = null; return(new ContentInfo( new Oid(_envelopedData.EncryptedContentInfo.ContentType), decrypted)); }