Move ASN.1 decoding/encoding out of PkcsPal
authorFilip Navara <filip.navara@gmail.com>
Wed, 17 Jul 2019 18:05:00 +0000 (20:05 +0200)
committerJeremy Barton <jbarton@microsoft.com>
Wed, 17 Jul 2019 18:04:59 +0000 (11:04 -0700)
Both the Windows version and the AnyOS version will use the managed ASN.1/BER/DER code via PkcsHelpers.

Commit migrated from https://github.com/dotnet/corefx/commit/ccad56758140def8084943e5a8f2acabbf81d454

15 files changed:
src/libraries/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Pal/AnyOS/ManagedPal.Asn.cs
src/libraries/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Pal/AnyOS/ManagedPal.Encrypt.cs
src/libraries/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Pal/Windows/PkcsPalWindows.Encrypt.cs
src/libraries/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Pal/Windows/PkcsPalWindows.cs
src/libraries/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/PkcsHelpers.cs
src/libraries/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/PkcsPal.cs
src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Pkcs12CertBag.cs
src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Pkcs12Info.cs
src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Pkcs12SafeContents.cs
src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Pkcs9ContentType.cs
src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Pkcs9DocumentDescription.cs
src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Pkcs9DocumentName.cs
src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Pkcs9LocalKeyId.cs
src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Pkcs9MessageDigest.cs
src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/Pkcs9SigningTime.cs

index 4798ceb..778ff54 100644 (file)
@@ -12,127 +12,6 @@ namespace Internal.Cryptography.Pal.AnyOS
 {
     internal sealed partial class ManagedPkcsPal : PkcsPal
     {
-        private static readonly byte[] s_invalidEmptyOid = { 0x06, 0x00 };
-
-        public override byte[] EncodeOctetString(byte[] octets)
-        {
-            // Write using DER to support the most readers.
-            using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER))
-            {
-                writer.WriteOctetString(octets);
-                return writer.Encode();
-            }
-        }
-
-        public override byte[] DecodeOctetString(byte[] encodedOctets)
-        {
-            return DecodeOctetStringCore(encodedOctets);
-        }
-
-        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);
-                }
-            }
-        }
-
-        public override byte[] EncodeUtcTime(DateTime utcTime)
-        {
-            const int maxLegalYear = 2049;
-            // Write using DER to support the most readers.
-            using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER))
-            {
-                try 
-                {
-                    // Sending the DateTime through ToLocalTime here will cause the right normalization
-                    // of DateTimeKind.Unknown.
-                    //
-                    // Unknown => Local (adjust) => UTC (adjust "back", add Z marker; matches Windows)
-                    if (utcTime.Kind == DateTimeKind.Unspecified)
-                    {
-                        writer.WriteUtcTime(utcTime.ToLocalTime(), maxLegalYear);
-                    }
-                    else
-                    {
-                        writer.WriteUtcTime(utcTime, maxLegalYear);
-                    }
-
-                    return writer.Encode();
-                }
-                catch (ArgumentException ex)
-                {
-                    throw new CryptographicException(ex.Message, ex);
-                }
-            }
-        }
-
-        public override DateTime DecodeUtcTime(byte[] encodedUtcTime)
-        {
-            // Read using BER because the CMS specification says the encoding is BER.
-            AsnReader reader = new AsnReader(encodedUtcTime, AsnEncodingRules.BER);
-            DateTimeOffset value = reader.ReadUtcTime();
-            reader.ThrowIfNotEmpty();
-            return value.UtcDateTime;
-        }
-
-        public override string DecodeOid(byte[] encodedOid)
-        {
-            // Windows compat.
-            if (s_invalidEmptyOid.AsSpan().SequenceEqual(encodedOid))
-            {
-                return string.Empty;
-            }
-
-            // Read using BER because the CMS specification says the encoding is BER.
-            AsnReader reader = new AsnReader(encodedOid, AsnEncodingRules.BER);
-            string value = reader.ReadObjectIdentifierAsString();
-            reader.ThrowIfNotEmpty();
-            return value;
-        }
-
         public override Oid GetEncodedMessageType(byte[] encodedMessage)
         {
             AsnReader reader = new AsnReader(encodedMessage, AsnEncodingRules.BER);
index 21d35ce..5419e09 100644 (file)
@@ -175,7 +175,7 @@ namespace Internal.Cryptography.Pal.AnyOS
                 }
                 else
                 {
-                    parameterBytes = EncodeOctetString(alg.IV);
+                    parameterBytes = PkcsHelpers.EncodeOctetString(alg.IV);
                 }
 
                 byte[] toEncrypt = contentInfo.Content;
index ac32160..f2e4555 100644 (file)
@@ -28,15 +28,7 @@ namespace Internal.Cryptography.Pal.Windows
                 byte[] encodedContent;
                 if (contentInfo.ContentType.Value.Equals(Oids.Pkcs7Data, StringComparison.OrdinalIgnoreCase))
                 {
-                    unsafe
-                    {
-                        byte[] content = contentInfo.Content;
-                        fixed (byte* pContent = content)
-                        {
-                            DATA_BLOB blob = new DATA_BLOB((IntPtr)pContent, (uint)(content.Length));
-                            encodedContent = Interop.Crypt32.CryptEncodeObjectToByteArray(CryptDecodeObjectStructType.X509_OCTET_STRING, &blob);
-                        }
-                    }
+                    encodedContent = PkcsHelpers.EncodeOctetString(contentInfo.Content);
                 }
                 else
                 {
index fb6a64f..41eb8e9 100644 (file)
@@ -28,76 +28,6 @@ namespace Internal.Cryptography.Pal.Windows
             return DecryptorPalWindows.Decode(encodedMessage, out version, out contentInfo, out contentEncryptionAlgorithm, out originatorCerts, out unprotectedAttributes);
         }
 
-        public sealed override byte[] EncodeOctetString(byte[] octets)
-        {
-            unsafe
-            {
-                fixed (byte* pOctets = octets)
-                {
-                    DATA_BLOB blob = new DATA_BLOB((IntPtr)pOctets, (uint)(octets.Length));
-                    return Interop.Crypt32.CryptEncodeObjectToByteArray(CryptDecodeObjectStructType.X509_OCTET_STRING, &blob);
-                }
-            }
-        }
-
-        public sealed override byte[] DecodeOctetString(byte[] encodedOctets)
-        {
-            using (SafeHandle sh = Interop.Crypt32.CryptDecodeObjectToMemory(CryptDecodeObjectStructType.X509_OCTET_STRING, encodedOctets))
-            {
-                unsafe
-                {
-                    DATA_BLOB blob = *(DATA_BLOB*)(sh.DangerousGetHandle());
-                    return blob.ToByteArray();
-                }
-            }
-        }
-
-        public sealed override byte[] EncodeUtcTime(DateTime utcTime)
-        {
-            long ft;
-            try 
-            {
-                ft = utcTime.ToFileTimeUtc();
-            }
-            catch (ArgumentException ex)
-            {
-                throw new CryptographicException(ex.Message, ex);
-            }
-
-            unsafe
-            {
-                return Interop.Crypt32.CryptEncodeObjectToByteArray(CryptDecodeObjectStructType.PKCS_UTC_TIME, &ft);
-            }
-        }
-
-        public sealed override DateTime DecodeUtcTime(byte[] encodedUtcTime)
-        {
-            long signingTime = 0;
-            unsafe
-            {
-                fixed (byte* pEncodedUtcTime = encodedUtcTime)
-                {
-                    int cbSize = sizeof(long);
-                    if (!Interop.Crypt32.CryptDecodeObject(CryptDecodeObjectStructType.PKCS_UTC_TIME, (IntPtr)pEncodedUtcTime, encodedUtcTime.Length, &signingTime, ref cbSize))
-                        throw Marshal.GetLastWin32Error().ToCryptographicException();
-                }
-            }
-            return DateTime.FromFileTimeUtc(signingTime);
-        }
-
-        public sealed override string DecodeOid(byte[] encodedOid)
-        {
-            using (SafeHandle sh = Interop.Crypt32.CryptDecodeObjectToMemory(CryptDecodeObjectStructType.X509_OBJECT_IDENTIFIER, encodedOid))
-            {
-                unsafe
-                {
-                    IntPtr pOidValue = *(IntPtr*)(sh.DangerousGetHandle());
-                    string contentType = pOidValue.ToStringAnsi();
-                    return contentType;
-                }
-            }
-        }
-
         public sealed override Oid GetEncodedMessageType(byte[] encodedMessage)
         {
             using (SafeCryptMsgHandle hCryptMsg = Interop.Crypt32.CryptMsgOpenToDecode(MsgEncodingType.All, 0, 0, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero))
index 5bb35bc..bfdf112 100644 (file)
@@ -458,7 +458,7 @@ namespace Internal.Cryptography
             }
         }
 
-        public static ReadOnlyMemory<byte> DecodeOctetString(ReadOnlyMemory<byte> encodedOctetString)
+        public static ReadOnlyMemory<byte> DecodeOctetStringAsMemory(ReadOnlyMemory<byte> encodedOctetString)
         {
             AsnReader reader = new AsnReader(encodedOctetString, AsnEncodingRules.BER);
 
@@ -483,6 +483,122 @@ namespace Internal.Cryptography
             throw new CryptographicException();
         }
 
+        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 byte[] EncodeOctetString(byte[] octets)
+        {
+            // Write using DER to support the most readers.
+            using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER))
+            {
+                writer.WriteOctetString(octets);
+                return writer.Encode();
+            }
+        }
+
+        private static readonly byte[] s_invalidEmptyOid = { 0x06, 0x00 };
+
+        public static byte[] EncodeUtcTime(DateTime utcTime)
+        {
+            const int maxLegalYear = 2049;
+            // Write using DER to support the most readers.
+            using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER))
+            {
+                try
+                {
+                    // Sending the DateTime through ToLocalTime here will cause the right normalization
+                    // of DateTimeKind.Unknown.
+                    //
+                    // Unknown => Local (adjust) => UTC (adjust "back", add Z marker; matches Windows)
+                    if (utcTime.Kind == DateTimeKind.Unspecified)
+                    {
+                        writer.WriteUtcTime(utcTime.ToLocalTime(), maxLegalYear);
+                    }
+                    else
+                    {
+                        writer.WriteUtcTime(utcTime, maxLegalYear);
+                    }
+
+                    return writer.Encode();
+                }
+                catch (ArgumentException ex)
+                {
+                    throw new CryptographicException(ex.Message, ex);
+                }
+            }
+        }
+
+        public static DateTime DecodeUtcTime(byte[] encodedUtcTime)
+        {
+            // Read using BER because the CMS specification says the encoding is BER.
+            AsnReader reader = new AsnReader(encodedUtcTime, AsnEncodingRules.BER);
+            DateTimeOffset value = reader.ReadUtcTime();
+            reader.ThrowIfNotEmpty();
+            return value.UtcDateTime;
+        }
+
+        public static string DecodeOid(byte[] encodedOid)
+        {
+            // Windows compat.
+            if (s_invalidEmptyOid.AsSpan().SequenceEqual(encodedOid))
+            {
+                return string.Empty;
+            }
+
+            // Read using BER because the CMS specification says the encoding is BER.
+            AsnReader reader = new AsnReader(encodedOid, AsnEncodingRules.BER);
+            string value = reader.ReadObjectIdentifierAsString();
+            reader.ThrowIfNotEmpty();
+            return value;
+        }
+
         public static bool TryGetRsaOaepEncryptionPadding(
             ReadOnlyMemory<byte>? parameters,
             out RSAEncryptionPadding rsaEncryptionPadding,
index 19e3d15..375ab4e 100644 (file)
@@ -33,17 +33,6 @@ namespace Internal.Cryptography
         /// </summary>
         public abstract DecryptorPal Decode(byte[] encodedMessage, out int version, out ContentInfo contentInfo, out AlgorithmIdentifier contentEncryptionAlgorithm, out X509Certificate2Collection originatorCerts, out CryptographicAttributeObjectCollection unprotectedAttributes);
 
-        // 
-        // Encoders and decoders. These should be moved out of the Pal once we have a managed DER encoder/decoder api.
-        //
-        public abstract byte[] EncodeOctetString(byte[] octets);
-        public abstract byte[] DecodeOctetString(byte[] encodedOctets);
-
-        public abstract byte[] EncodeUtcTime(DateTime utcTime);
-        public abstract DateTime DecodeUtcTime(byte[] encodedUtcTime);
-
-        public abstract string DecodeOid(byte[] encodedOid);
-
         /// <summary>
         /// Implements the ContentInfo.GetContentType() behavior.
         /// </summary>
index e315b8e..b0d287c 100644 (file)
@@ -53,7 +53,7 @@ namespace System.Security.Cryptography.Pkcs
                 Oids.Pkcs12CertBag,
                 EncodeBagValue(
                     Oids.Pkcs12X509CertBagType,
-                    PkcsPal.Instance.EncodeOctetString(cert.RawData)),
+                    PkcsHelpers.EncodeOctetString(cert.RawData)),
                 skipCopy: true)
         {
             _decoded = CertBagAsn.Decode(EncodedBagValue, AsnEncodingRules.BER);
@@ -80,7 +80,7 @@ namespace System.Security.Cryptography.Pkcs
                 throw new InvalidOperationException(SR.Cryptography_Pkcs12_CertBagNotX509);
             }
 
-            return new X509Certificate2(PkcsHelpers.DecodeOctetString(_decoded.CertValue).ToArray());
+            return new X509Certificate2(PkcsHelpers.DecodeOctetString(_decoded.CertValue));
         }
 
         private static byte[] EncodeBagValue(Oid certificateType, ReadOnlyMemory<byte> encodedCertificate)
index f0a27f0..1455b52 100644 (file)
@@ -131,7 +131,7 @@ namespace System.Security.Cryptography.Pkcs
 
             if (pfx.AuthSafe.ContentType == Oids.Pkcs7Data)
             {
-                authSafeBytes = PkcsHelpers.DecodeOctetString(pfx.AuthSafe.Content);
+                authSafeBytes = PkcsHelpers.DecodeOctetStringAsMemory(pfx.AuthSafe.Content);
 
                 if (pfx.MacData.HasValue)
                 {
index 895dd66..a93361e 100644 (file)
@@ -49,7 +49,7 @@ namespace System.Security.Cryptography.Pkcs
                     break;
                 case Oids.Pkcs7Data:
                     ConfidentialityMode = Pkcs12ConfidentialityMode.None;
-                    _bags = ReadBags(PkcsHelpers.DecodeOctetString(contentInfoAsn.Content));
+                    _bags = ReadBags(PkcsHelpers.DecodeOctetStringAsMemory(contentInfoAsn.Content));
                     break;
                 default:
                     throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
index 940f095..93daef0 100644 (file)
@@ -47,7 +47,7 @@ namespace System.Security.Cryptography.Pkcs
             if (rawData == null)
                 return null;
 
-            string contentTypeValue = PkcsPal.Instance.DecodeOid(rawData);
+            string contentTypeValue = PkcsHelpers.DecodeOid(rawData);
             return new Oid(contentTypeValue);
         }
 
index fb84d4b..93c838f 100644 (file)
@@ -60,7 +60,7 @@ namespace System.Security.Cryptography.Pkcs
             if (rawData == null)
                 return null;
 
-            byte[] octets = PkcsPal.Instance.DecodeOctetString(rawData);
+            byte[] octets = PkcsHelpers.DecodeOctetString(rawData);
             return octets.OctetStringToUnicode();
         }
 
@@ -70,7 +70,7 @@ namespace System.Security.Cryptography.Pkcs
                 throw new ArgumentNullException(nameof(documentDescription));
 
             byte[] octets = documentDescription.UnicodeToOctetString();
-            return PkcsPal.Instance.EncodeOctetString(octets);
+            return PkcsHelpers.EncodeOctetString(octets);
         }
 
         private volatile string _lazyDocumentDescription = null;
index 6cf8c74..13e72a4 100644 (file)
@@ -60,7 +60,7 @@ namespace System.Security.Cryptography.Pkcs
             if (rawData == null)
                 return null;
 
-            byte[] octets = PkcsPal.Instance.DecodeOctetString(rawData);
+            byte[] octets = PkcsHelpers.DecodeOctetString(rawData);
             return octets.OctetStringToUnicode();
         }
 
@@ -70,7 +70,7 @@ namespace System.Security.Cryptography.Pkcs
                 throw new ArgumentNullException(nameof(documentName));
 
             byte[] octets = documentName.UnicodeToOctetString();
-            return PkcsPal.Instance.EncodeOctetString(octets);
+            return PkcsHelpers.EncodeOctetString(octets);
         }
 
         private volatile string _lazyDocumentName = null;
index 1f40720..64228cd 100644 (file)
@@ -3,7 +3,7 @@
 // See the LICENSE file in the project root for more information.
 
 using System.Security.Cryptography.Asn1;
-using Internal.Cryptography.Pal.AnyOS;
+using Internal.Cryptography;
 
 namespace System.Security.Cryptography.Pkcs
 {
@@ -48,7 +48,7 @@ namespace System.Security.Cryptography.Pkcs
                 return null;
             }
 
-            return ManagedPkcsPal.DecodeOctetStringCore(rawData);
+            return PkcsHelpers.DecodeOctetString(rawData);
         }
     }
 }
index 7349d6d..e97897b 100644 (file)
@@ -56,7 +56,7 @@ namespace System.Security.Cryptography.Pkcs
             if (rawData == null)
                 return null;
 
-            return PkcsPal.Instance.DecodeOctetString(rawData);
+            return PkcsHelpers.DecodeOctetString(rawData);
         }
 
         private volatile byte[] _lazyMessageDigest = null;
index d87e824..630c529 100644 (file)
@@ -64,12 +64,12 @@ namespace System.Security.Cryptography.Pkcs
             if (rawData == null)
                 return default(DateTime);
 
-            return PkcsPal.Instance.DecodeUtcTime(rawData);
+            return PkcsHelpers.DecodeUtcTime(rawData);
         }
 
         private static byte[] Encode(DateTime signingTime)
         {
-            return PkcsPal.Instance.EncodeUtcTime(signingTime);
+            return PkcsHelpers.EncodeUtcTime(signingTime);
         }
 
         private DateTime? _lazySigningTime = default(DateTime?);