Exemple #1
0
        private bool TryCopyCharacterString(
            Asn1Tag expectedTag,
            UniversalTagNumber universalTagNumber,
            Text.Encoding encoding,
            Span <char> destination,
            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(
                expectedTag,
                universalTagNumber,
                out int bytesRead,
                ref rented);

            try
            {
                bool copied = TryCopyCharacterString(
                    contents,
                    destination,
                    encoding,
                    out charsWritten);

                if (copied)
                {
                    _data = _data.Slice(bytesRead);
                }

                return(copied);
            }
            finally
            {
                if (rented != null)
                {
                    CryptoPool.Return(rented, contents.Length);
                }
            }
        }
Exemple #2
0
            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))
                        using (ICryptoTransform decryptor = alg.CreateDecryptor(cek, alg.IV))
                        {
                            // 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;
                }
            }
        /// <summary>
        ///   Reads the next value as a UTCTime with a specified tag.
        /// </summary>
        /// <param name="expectedTag">The tag to check for before reading.</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>
        /// <returns>
        ///   a DateTimeOffset representing the value encoded in the UTCTime.
        /// </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="System.Globalization.Calendar.TwoDigitYearMax"/>
        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;

            // The longest format is 17 bytes.
            Span <byte> tmpSpace = stackalloc byte[17];

            ReadOnlySpan <byte> contents = this.GetOctetStringContents(
                expectedTag,
                UniversalTagNumber.UtcTime,
                out int bytesRead,
                ref rented,
                tmpSpace);

            DateTimeOffset value = this.ParseUtcTime(contents, twoDigitYearMax);

            if (rented != null)
            {
                Debug.Fail($"UtcTime did not fit in tmpSpace ({contents.Length} total)");
                CryptoPool.Return(rented, contents.Length);
            }

            this._data = this._data.Slice(bytesRead);
            return(value);
        }
Exemple #4
0
        private static Memory <byte> PseudoRandomPlus(ReadOnlyMemory <byte> key, ReadOnlyMemory <byte> pepper, KerberosCryptoTransformer handler)
        {
            // PRF+(protocol key, octet string) -> (octet string)
            // PRF+(key, shared-info) := pseudo-random(key,  1 || shared-info ) ||
            //          pseudo-random(key, 2 || shared-info ) ||
            //          pseudo-random(key, 3 || shared-info ) || ...

            using (var pool = CryptoPool.Rent <byte>(pepper.Length + 1))
            {
                Memory <byte> input = pool.Memory.Slice(0, pepper.Length + 1);

                int prfSize = handler.BlockSize;

                int iterations = handler.KeySize / prfSize;

                if (handler.KeySize % prfSize > 0)
                {
                    iterations++;
                }

                input.Span[0] = 1;

                pepper.CopyTo(input.Slice(1));

                Memory <byte> result = new byte[prfSize * iterations];

                for (var i = 0; i < iterations; i++)
                {
                    handler.PseudoRandomFunction(key, input)
                    .Slice(0, prfSize)
                    .CopyTo(result.Slice(i * prfSize));

                    input.Span[0]++;
                }

                return(result);
            }
        }
Exemple #5
0
        public override ReadOnlyMemory <byte> Encrypt(ReadOnlyMemory <byte> data, KerberosKey kerberosKey, KeyUsage usage)
        {
            var Ke = GetOrDeriveKey(kerberosKey, usage);

            var confounder = GenerateRandomBytes(ConfounderSize);

            var concatLength = confounder.Length + data.Length;

            using (var cleartextPool = CryptoPool.Rent <byte>(concatLength))
            {
                var cleartext = Concat(confounder.Span, data.Span, cleartextPool.Memory.Slice(0, concatLength));

                var encrypted = AESCTS.Encrypt(
                    cleartext,
                    Ke,
                    AllZerosInitVector
                    );

                var checksum = MakeChecksum(cleartext, kerberosKey, usage, KeyDerivationMode.Ki, ChecksumSize);

                return(Concat(encrypted.Span, checksum.Span));
            }
        }
        private static ArraySegment <byte> RentDynamicBuffer <THandle>(NegativeSizeReadMethod <THandle> method, THandle handle)
        {
            int negativeSize = method(handle, null, 0);

            if (negativeSize > 0)
            {
                throw Interop.Crypto.CreateOpenSslCryptographicException();
            }

            int targetSize = -negativeSize;

            byte[] bytes = CryptoPool.Rent(targetSize);

            int ret = method(handle, bytes, targetSize);

            if (ret != 1)
            {
                CryptoPool.Return(bytes);
                throw Interop.Crypto.CreateOpenSslCryptographicException();
            }

            return(new ArraySegment <byte>(bytes, 0, targetSize));
        }
Exemple #7
0
        /// <summary>
        /// Purge the ticket cache of the provided Logon Id. Note that the value 0 zero is treated as the current users Logon Id.
        /// </summary>
        /// <param name="luid">The Logon Id of the cache to be purged.</param>
        public unsafe void PurgeTicketCache(long luid = 0)
        {
            var purgeRequest = new KERB_PURGE_TKT_CACHE_EX_REQUEST
            {
                MessageType = KERB_PROTOCOL_MESSAGE_TYPE.KerbPurgeTicketCacheExMessage,
                Flags       = 1,
                LogonId     = luid
            };

            var bufferSize = Marshal.SizeOf(typeof(KERB_PURGE_TKT_CACHE_EX_REQUEST));

            using (var pool = CryptoPool.Rent <byte>(bufferSize))
            {
                var buffer = pool.Memory.Slice(0, bufferSize);

                fixed(void *pBuffer = &MemoryMarshal.GetReference(buffer.Span))
                {
                    Marshal.StructureToPtr(purgeRequest, (IntPtr)pBuffer, false);

                    this.LsaCallAuthenticationPackage(pBuffer, bufferSize);
                }
            }
        }
Exemple #8
0
            private static AsymmetricAlgorithm 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());

                if (!writer.TryEncode(rented, out int written))
                {
                    Debug.Fail("TryEncode failed with a pre-allocated buffer");
                    throw new InvalidOperationException();
                }

                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);
                }
            }
Exemple #9
0
        /*
         * This is an implementation of SP800-108 KDF in Counter Mode
         * https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-108.pdf
         */

        public ReadOnlyMemory <byte> Derive(
            HashAlgorithmName algName,
            ReadOnlyMemory <byte> passwordBytes,
            ReadOnlyMemory <byte> salt,
            int k,
            int keySize
            )
        {
            // K1 = HMAC-SHA-256(key, 0x00000001 | label | 0x00 | k)
            // length(0x00000001 | 0x00 | k) = 9

            var inputLength = salt.Length + 9;

            using (var inputRented = CryptoPool.Rent <byte>(inputLength))
            {
                var input = inputRented.Memory.Slice(0, inputLength);

                input.Span[3] = 1;           // 0x00000001

                salt.CopyTo(input.Slice(4)); // label

                IHmacAlgorithm hmac;

                if (algName == HashAlgorithmName.SHA256)
                {
                    hmac = CryptoPal.Platform.HmacSha256(passwordBytes);
                }
                else
                {
                    hmac = CryptoPal.Platform.HmacSha384(passwordBytes);
                }

                BinaryPrimitives.WriteInt32BigEndian(input.Span.Slice(input.Length - 4), k);

                return(hmac.ComputeHash(input).Slice(0, keySize));
            }
        }
        internal static ArraySegment <byte> OpenSslRentEncode <THandle>(
            GetEncodedSizeFunc <THandle> getSize,
            EncodeFunc <THandle> encode,
            THandle handle)
            where THandle : SafeHandle
        {
            int size = getSize(handle);

            if (size < 1)
            {
                throw CreateOpenSslCryptographicException();
            }

            byte[] data = CryptoPool.Rent(size);

            int size2 = encode(handle, data);

            if (size2 < 1)
            {
                Debug.Fail(
                    $"{nameof(OpenSslEncode)}: {nameof(getSize)} succeeded ({size}) and {nameof(encode)} failed ({size2})");

                // Since we don't know what was written, assume it was secret and have the
                // CryptoPool.Return clear the whole array.
                // (It doesn't matter much, since we're behind Debug.Fail)
                CryptoPool.Return(data);

                // If it ever happens, ensure the error queue gets cleared.
                // And since it didn't write the data, reporting an exception is good too.
                throw CreateOpenSslCryptographicException();
            }

            Debug.Assert(size == size2);

            return(new ArraySegment <byte>(data, 0, size2));
        }
Exemple #11
0
        public byte[] ExportPrivateKey(
            ReadOnlySpan <byte> passwordBytes,
            S2kParameters s2kParameters)
        {
            ECParameters ecParameters = new ECParameters();

            byte[] secretPart = Array.Empty <byte>();

            try
            {
                ecParameters = ecdsa.ExportParameters(true);

                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 */ +
                    encryptedSecretLength;
                var destination = new byte[estimatedLength];
                WriteOpenPgpECParameters(ecParameters, destination, out int bytesWritten);

                S2kBasedEncryption.EncryptSecretKey(passwordBytes, s2kParameters, secretPart.AsSpan(0, secretSize), destination.AsSpan(bytesWritten));
                return(destination.AsSpan(0, bytesWritten + encryptedSecretLength).ToArray());
            }
            finally
            {
                CryptoPool.Return(secretPart);
                if (ecParameters.D != null)
                {
                    CryptographicOperations.ZeroMemory(ecParameters.D);
                }
            }
        }
Exemple #12
0
        private static void Derive(
            ReadOnlySpan <char> password,
            HashAlgorithmName hashAlgorithm,
            int iterationCount,
            byte id,
            ReadOnlySpan <byte> salt,
            Span <byte> destination)
        {
            // https://tools.ietf.org/html/rfc7292#appendix-B.2
            Debug.Assert(iterationCount >= 1);

            if (!s_uvLookup.TryGetValue(hashAlgorithm, out Tuple <int, int>?uv))
            {
                throw new CryptographicException(SR.Cryptography_UnknownHashAlgorithm, hashAlgorithm.Name);
            }

            (int u, int v) = uv;

            Debug.Assert(v <= 1024);

            //  1. Construct a string, D (the "diversifier"), by concatenating v/8 copies of ID.
            int         vBytes = v >> 3;
            Span <byte> D      = stackalloc byte[vBytes];

            D.Fill(id);

            // 2.  Concatenate copies of the salt together to create a string S of
            // length v(ceiling(s/ v)) bits(the final copy of the salt may be
            // truncated to create S). Note that if the salt is the empty
            // string, then so is S.
            int SLen = ((salt.Length - 1 + vBytes) / vBytes) * vBytes;

            // The password is a null-terminated UTF-16BE version of the input.
            int passLen = checked ((password.Length + 1) * 2);

            // If password == default then the span represents the null string (as opposed to
            // an empty string), and the P block should then have size 0 in the next step.
            if (password == default)
            {
                passLen = 0;
            }

            // 3.  Concatenate copies of the password together to create a string P
            // of length v(ceiling(p/v)) bits (the final copy of the password
            // may be truncated to create P).  Note that if the password is the
            // empty string, then so is P.
            //
            // (The RFC quote considers the trailing '\0' to be part of the string,
            // so "empty string" from this RFC means "null string" in C#, and C#'s
            // "empty string" is not 'empty' in this context.)
            int PLen = ((passLen - 1 + vBytes) / vBytes) * vBytes;

            // 4.  Set I=S||P to be the concatenation of S and P.
            int         ILen = SLen + PLen;
            Span <byte> I    = stackalloc byte[0];

            byte[]? IRented = null;

            if (ILen <= 1024)
            {
                I = stackalloc byte[ILen];
            }
            else
            {
                IRented = CryptoPool.Rent(ILen);
                I       = IRented.AsSpan(0, ILen);
            }

            IncrementalHash hash = IncrementalHash.CreateHash(hashAlgorithm);

            try
            {
                CircularCopy(salt, I.Slice(0, SLen));
                CircularCopyUtf16BE(password, I.Slice(SLen));

                int uBytes = u >> 3;

                Span <byte> hashBuf = stackalloc byte[uBytes];
                Span <byte> bBuf    = stackalloc byte[vBytes];

                // 5.  Set c=ceiling(n/u).
                // 6.  For i=1, 2, ..., c, do the following:
                // (later we're going to start writing A_i values as output,
                // they mean "while work remains").
                while (true)
                {
                    // A.  Set A_i=H^r(D||I). (i.e., the r-th hash of D||I,
                    // H(H(H(... H(D || I))))
                    hash.AppendData(D);
                    hash.AppendData(I);

                    for (int j = iterationCount; j > 0; j--)
                    {
                        if (!hash.TryGetHashAndReset(hashBuf, out int bytesWritten) || bytesWritten != hashBuf.Length)
                        {
                            Debug.Fail($"Hash output wrote {bytesWritten} bytes when {hashBuf.Length} was expected");
                            throw new CryptographicException();
                        }

                        if (j != 1)
                        {
                            hash.AppendData(hashBuf);
                        }
                    }

                    // 7.  Concatenate A_1, A_2, ..., A_c together to form a pseudorandom
                    // bit string, A.
                    //
                    // 8.  Use the first n bits of A as the output of this entire process.

                    if (hashBuf.Length >= destination.Length)
                    {
                        hashBuf.Slice(0, destination.Length).CopyTo(destination);
                        return;
                    }

                    hashBuf.CopyTo(destination);
                    destination = destination.Slice(hashBuf.Length);

                    // B.  Concatenate copies of A_i to create a string B of length v
                    // bits(the final copy of Ai may be truncated to create B).
                    CircularCopy(hashBuf, bBuf);

                    // C.  Treating I as a concatenation I_0, I_1, ..., I_(k-1) of v-bit
                    // blocks, where k = ceiling(s / v) + ceiling(p / v), modify I by
                    // setting I_j = (I_j + B + 1) mod 2 ^ v for each j.
                    for (int j = (I.Length / vBytes) - 1; j >= 0; j--)
                    {
                        Span <byte> I_j = I.Slice(j * vBytes, vBytes);
                        AddPlusOne(I_j, bBuf);
                    }
                }
            }
            finally
            {
                CryptographicOperations.ZeroMemory(I);

                if (IRented != null)
                {
                    CryptoPool.Return(IRented, clearSize: 0);
                }

                hash.Dispose();
            }
        }
Exemple #13
0
        internal byte[] Encrypt(
            ReadOnlySpan <char> password,
            ReadOnlySpan <byte> passwordBytes,
            PbeParameters pbeParameters)
        {
            Debug.Assert(pbeParameters != null);
            Debug.Assert(pbeParameters.IterationCount >= 1);

            AsnWriter writer = null;

            using (AsnWriter contentsWriter = Encode())
            {
                ReadOnlySpan <byte> contentsSpan = contentsWriter.EncodeAsSpan();

                PasswordBasedEncryption.InitiateEncryption(
                    pbeParameters,
                    out SymmetricAlgorithm cipher,
                    out string hmacOid,
                    out string encryptionAlgorithmOid,
                    out bool isPkcs12);

                int         cipherBlockBytes = cipher.BlockSize / 8;
                byte[]      encryptedRent    = CryptoPool.Rent(contentsSpan.Length + cipherBlockBytes);
                Span <byte> encryptedSpan    = Span <byte> .Empty;
                Span <byte> iv   = stackalloc byte[cipherBlockBytes];
                Span <byte> salt = stackalloc byte[16];
                RandomNumberGenerator.Fill(salt);

                try
                {
                    int written = PasswordBasedEncryption.Encrypt(
                        password,
                        passwordBytes,
                        cipher,
                        isPkcs12,
                        contentsSpan,
                        pbeParameters,
                        salt,
                        encryptedRent,
                        iv);

                    encryptedSpan = encryptedRent.AsSpan(0, written);

                    writer = new AsnWriter(AsnEncodingRules.DER);

                    // EncryptedData
                    writer.PushSequence();

                    // version
                    // Since we're not writing unprotected attributes, version=0
                    writer.WriteInteger(0);

                    // encryptedContentInfo
                    {
                        writer.PushSequence();
                        writer.WriteObjectIdentifier(Oids.Pkcs7Data);

                        PasswordBasedEncryption.WritePbeAlgorithmIdentifier(
                            writer,
                            isPkcs12,
                            encryptionAlgorithmOid,
                            salt,
                            pbeParameters.IterationCount,
                            hmacOid,
                            iv);

                        writer.WriteOctetString(
                            new Asn1Tag(TagClass.ContextSpecific, 0),
                            encryptedSpan);

                        writer.PopSequence();
                    }

                    writer.PopSequence();

                    return(writer.Encode());
                }
                finally
                {
                    CryptographicOperations.ZeroMemory(encryptedSpan);
                    CryptoPool.Return(encryptedRent, clearSize: 0);
                    writer?.Dispose();
                }
            }
        }
        private static unsafe ArraySegment <byte> EncodeAuthSafe(
            AsnWriter tmpWriter,
            SafeBagAsn[] keyBags,
            int keyCount,
            CertBagAsn[] certBags,
            AttributeAsn[] certAttrs,
            int certIdx,
            ReadOnlySpan <char> passwordSpan)
        {
            string?encryptionAlgorithmOid  = null;
            bool   certsIsPkcs12Encryption = false;
            string?certsHmacOid            = null;

            ArraySegment <byte> encodedKeyContents  = default;
            ArraySegment <byte> encodedCertContents = default;

            try
            {
                if (keyCount > 0)
                {
                    encodedKeyContents = EncodeKeys(tmpWriter, keyBags, keyCount);
                }

                Span <byte> salt = stackalloc byte[16];
                RandomNumberGenerator.Fill(salt);
                Span <byte> certContentsIv = stackalloc byte[8];

                if (certIdx > 0)
                {
                    encodedCertContents = EncodeCerts(
                        tmpWriter,
                        certBags,
                        certAttrs,
                        certIdx,
                        salt,
                        passwordSpan,
                        certContentsIv,
                        out certsHmacOid,
                        out encryptionAlgorithmOid,
                        out certsIsPkcs12Encryption);
                }

                return(EncodeAuthSafe(
                           tmpWriter,
                           encodedKeyContents,
                           encodedCertContents,
                           certsIsPkcs12Encryption,
                           certsHmacOid !,
                           encryptionAlgorithmOid !,
                           salt,
                           certContentsIv));
            }
            finally
            {
                if (encodedCertContents.Array != null)
                {
                    CryptoPool.Return(encodedCertContents);
                }

                if (encodedKeyContents.Array != null)
                {
                    CryptoPool.Return(encodedKeyContents);
                }
            }
        }
            private static byte[]? DecryptContent(
                ReadOnlyMemory <byte> encryptedContent,
                byte[] cek,
                AlgorithmIdentifierAsn contentEncryptionAlgorithm,
                out Exception?exception)
            {
                exception = null;

                // Windows compat: If the encrypted content is completely empty, even where it does not make sense for the
                // mode and padding (e.g. CBC + PKCS7), produce an empty plaintext.
                if (encryptedContent.IsEmpty)
                {
                    return(Array.Empty <byte>());
                }

#if NET
                try
                {
                    using (SymmetricAlgorithm alg = OpenAlgorithm(contentEncryptionAlgorithm))
                    {
                        try
                        {
                            alg.Key = cek;
                        }
                        catch (CryptographicException ce)
                        {
                            throw new CryptographicException(SR.Cryptography_Cms_InvalidSymmetricKey, ce);
                        }

                        return(alg.DecryptCbc(encryptedContent.Span, alg.IV));
                    }
                }
                catch (CryptographicException ce)
                {
                    exception = ce;
                    return(null);
                }
#else
                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);
                }
#endif
            }
Exemple #16
0
        /// <summary>
        /// Create a "NewCredentials" logon session for the current LSA Handle. This does not authenticate the user
        /// and only uses the credentials provided for outbound calls similar to the /netonly flag for runas.exe.
        ///
        /// Note: this will call <see cref="ImpersonateLoggedOnUser(LsaTokenSafeHandle)" /> and set the current
        /// thread's primary token to the generated NT Token.
        /// </summary>
        /// <param name="username">The username to be used. Note leaving this null will use the default value "user".
        /// Passing an empty string will cause LSA to treat this as an anonymous user.</param>
        /// <param name="password">The password to be used by LSA for any future outbound ticket requests not already cached.</param>
        /// <param name="realm">The default realm to be used by LSA for the any outbound ticket requests not already cached.</param>
        public unsafe void LogonUser(string username = null, string password = null, string realm = null)
        {
            if (username == null)
            {
                username = DefaultUserName;
            }

            if (password == null)
            {
                password = string.Empty;
            }

            if (realm == null)
            {
                realm = string.Empty;
            }

            var originName = new LSA_STRING
            {
                Buffer        = ProcessName,
                Length        = (ushort)(ProcessName.Length * 2),
                MaximumLength = (ushort)(ProcessName.Length * 2)
            };

            var bufferSize = Marshal.SizeOf(typeof(KERB_INTERACTIVE_LOGON)) +
                             (realm.Length * 2) +
                             (username.Length * 2) +
                             (password.Length * 2);

            if (this.impersonationContext != null)
            {
                this.impersonationContext.Dispose();
                this.impersonationContext = null;
            }

            LsaBufferSafeHandle profileBuffer = null;

            using (var pool = CryptoPool.Rent <byte>(bufferSize))
            {
                var buffer = pool.Memory.Slice(0, bufferSize);

                try
                {
                    fixed(byte *pBuffer = &MemoryMarshal.GetReference(buffer.Span))
                    {
                        KERB_INTERACTIVE_LOGON *pLogon = (KERB_INTERACTIVE_LOGON *)pBuffer;

                        pLogon->MessageType = KERB_LOGON_SUBMIT_TYPE.KerbInteractiveLogon;

                        int offset = Marshal.SizeOf(typeof(KERB_INTERACTIVE_LOGON));

                        SetString(realm, (IntPtr)pLogon, ref pLogon->LogonDomainName, ref offset);
                        SetString(username, (IntPtr)pLogon, ref pLogon->UserName, ref offset);
                        SetString(password, (IntPtr)pLogon, ref pLogon->Password, ref offset);

                        var tokenSource = new TOKEN_SOURCE()
                        {
                            SourceName = Encoding.UTF8.GetBytes("kerb.net")
                        };

                        int profileLength = 0;

                        int result = LsaLogonUser(
                            this.lsaHandle,
                            ref originName,
                            SECURITY_LOGON_TYPE.NewCredentials,
                            this.negotiateAuthPackage,
                            pLogon,
                            bufferSize,
                            IntPtr.Zero,
                            ref tokenSource,
                            out profileBuffer,
                            ref profileLength,
                            out this.luid,
                            out this.impersonationContext,
                            out IntPtr pQuotas,
                            out int subStatus
                            );

                        LsaThrowIfError(result);
                    }
                }
                finally
                {
                    profileBuffer?.Dispose();
                }
            }

            // this call to impersonate will set the current thread token to be the token out of LsaLogonUser
            // do we need to do anything special if this gets used within an async context?

            this.impersonationContext.Impersonate();
        }
Exemple #17
0
        private static void ReadSubIdentifier(
            ReadOnlySpan <byte> source,
            out int bytesRead,
            out long?smallValue,
            out BigInteger?largeValue)
        {
            Debug.Assert(source.Length > 0);

            // T-REC-X.690-201508 sec 8.19.2 (last sentence)
            if (source[0] == 0x80)
            {
                throw new CryptographicException(SR.Resource("Cryptography_Der_Invalid_Encoding"));
            }

            // First, see how long the segment is
            int end = -1;
            int idx;

            for (idx = 0; idx < source.Length; idx++)
            {
                // If the high bit isn't set this marks the end of the sub-identifier.
                bool endOfIdentifier = (source[idx] & 0x80) == 0;

                if (endOfIdentifier)
                {
                    end = idx;
                    break;
                }
            }

            if (end < 0)
            {
                throw new CryptographicException(SR.Resource("Cryptography_Der_Invalid_Encoding"));
            }

            bytesRead = end + 1;
            long accum = 0;

            // Fast path, 9 or fewer bytes => fits in a signed long.
            // (7 semantic bits per byte * 9 bytes = 63 bits, which leaves the sign bit alone)
            if (bytesRead <= 9)
            {
                for (idx = 0; idx < bytesRead; idx++)
                {
                    byte cur = source[idx];
                    accum <<= 7;
                    accum  |= (byte)(cur & 0x7F);
                }

                largeValue = null;
                smallValue = accum;
                return;
            }

            // Slow path, needs temporary storage.

            const int SemanticByteCount = 7;
            const int ContentByteCount  = 8;

            // Every 8 content bytes turns into 7 integer bytes, so scale the count appropriately.
            // Add one while we're shrunk to account for the needed padding byte or the len%8 discarded bytes.
            int bytesRequired = ((bytesRead / ContentByteCount) + 1) * SemanticByteCount;

            byte[] tmpBytes = CryptoPool.Rent(bytesRequired);
            // Ensure all the bytes are zeroed out for BigInteger's parsing.
            Array.Clear(tmpBytes, 0, tmpBytes.Length);

            Span <byte> writeSpan       = tmpBytes;
            Span <byte> accumValueBytes = stackalloc byte[sizeof(long)];
            int         nextStop        = bytesRead;

            idx = bytesRead - ContentByteCount;

            while (nextStop > 0)
            {
                byte cur = source[idx];

                accum <<= 7;
                accum  |= (byte)(cur & 0x7F);

                idx++;

                if (idx >= nextStop)
                {
                    Debug.Assert(idx == nextStop);
                    Debug.Assert(writeSpan.Length >= SemanticByteCount);

                    BinaryPrimitives.WriteInt64LittleEndian(accumValueBytes, accum);
                    Debug.Assert(accumValueBytes[7] == 0);
                    accumValueBytes.Slice(0, SemanticByteCount).CopyTo(writeSpan);
                    writeSpan = writeSpan.Slice(SemanticByteCount);

                    accum     = 0;
                    nextStop -= ContentByteCount;
                    idx       = Math.Max(0, nextStop - ContentByteCount);
                }
            }

            int bytesWritten = tmpBytes.Length - writeSpan.Length;

            // Verify our bytesRequired calculation. There should be at most 7 padding bytes.
            // If the length % 8 is 7 we'll have 0 padding bytes, but the sign bit is still clear.
            //
            // 8 content bytes had a sign bit problem, so we gave it a second 7-byte block, 7 remain.
            // 7 content bytes got a single block but used and wrote 7 bytes, but only 49 of the 56 bits.
            // 6 content bytes have a padding count of 1.
            // 1 content byte has a padding count of 6.
            // 0 content bytes is illegal, but see 8 for the cycle.
            int paddingByteCount = bytesRequired - bytesWritten;

            Debug.Assert(paddingByteCount >= 0 && paddingByteCount < sizeof(long));

            largeValue = new BigInteger(tmpBytes);
            smallValue = null;

            CryptoPool.Return(tmpBytes, bytesWritten);
        }
        private static ArraySegment <byte> EncodeCerts(
            AsnWriter tmpWriter,
            CertBagAsn[] certBags,
            AttributeAsn[] certAttrs,
            int certCount,
            Span <byte> salt,
            ReadOnlySpan <char> passwordSpan,
            Span <byte> certContentsIv,
            out string hmacOid,
            out string encryptionAlgorithmOid,
            out bool isPkcs12)
        {
            Debug.Assert(tmpWriter.GetEncodedLength() == 0);
            tmpWriter.PushSequence();

            PasswordBasedEncryption.InitiateEncryption(
                s_windowsPbe,
                out SymmetricAlgorithm cipher,
                out hmacOid,
                out encryptionAlgorithmOid,
                out isPkcs12);

            using (cipher)
            {
                Debug.Assert(certContentsIv.Length * 8 == cipher.BlockSize);

                for (int i = certCount - 1; i >= 0; --i)
                {
                    // Manually write the SafeBagAsn
                    // https://tools.ietf.org/html/rfc7292#section-4.2
                    //
                    // SafeBag ::= SEQUENCE {
                    //   bagId          BAG-TYPE.&id ({PKCS12BagSet})
                    //   bagValue       [0] EXPLICIT BAG-TYPE.&Type({PKCS12BagSet}{@bagId}),
                    //   bagAttributes  SET OF PKCS12Attribute OPTIONAL
                    // }
                    tmpWriter.PushSequence();

                    tmpWriter.WriteObjectIdentifierForCrypto(Oids.Pkcs12CertBag);

                    tmpWriter.PushSequence(s_contextSpecific0);
                    certBags[i].Encode(tmpWriter);
                    tmpWriter.PopSequence(s_contextSpecific0);

                    if (certAttrs[i].AttrType != null)
                    {
                        tmpWriter.PushSetOf();
                        certAttrs[i].Encode(tmpWriter);
                        tmpWriter.PopSetOf();
                    }

                    tmpWriter.PopSequence();
                }

                tmpWriter.PopSequence();

                // The padding applied will add at most a block to the output,
                // so ask for contentsSpan.Length + the number of bytes in a cipher block.
                int    cipherBlockBytes = cipher.BlockSize >> 3;
                int    requestedSize    = checked (tmpWriter.GetEncodedLength() + cipherBlockBytes);
                byte[] certContents     = CryptoPool.Rent(requestedSize);

                int encryptedLength = PasswordBasedEncryption.Encrypt(
                    passwordSpan,
                    ReadOnlySpan <byte> .Empty,
                    cipher,
                    isPkcs12,
                    tmpWriter,
                    s_windowsPbe,
                    salt,
                    certContents,
                    certContentsIv);

                Debug.Assert(encryptedLength <= requestedSize);
                tmpWriter.Reset();

                return(new ArraySegment <byte>(certContents, 0, encryptedLength));
            }
        }
        private unsafe void ImportKey(DiffieHellmanKey incoming, ref IntPtr hKey)
        {
            DiffieHellmanKey key;

            string keyType;
            int    dwMagic;
            int    structSize;

            if (incoming.Type == AsymmetricKeyType.Private)
            {
                key = incoming;

                keyType    = BCRYPT_DH_PRIVATE_BLOB;
                dwMagic    = BCRYPT_DH_PRIVATE_MAGIC;
                structSize = sizeof(BCRYPT_DH_KEY_BLOB_HEADER) + (key.KeyLength * 4);
            }
            else
            {
                key = (DiffieHellmanKey)PublicKey;

                keyType    = BCRYPT_DH_PUBLIC_BLOB;
                dwMagic    = BCRYPT_DH_PUBLIC_MAGIC;
                structSize = sizeof(BCRYPT_DH_KEY_BLOB_HEADER) + (key.KeyLength * 3);
            }

            Factor = incoming.Factor.ToArray();

            using (var rented = CryptoPool.Rent <byte>(structSize))
            {
                rented.Memory.Span.Fill(0);

                fixed(byte *pbInput = &MemoryMarshal.GetReference(rented.Memory.Span))
                {
                    BCRYPT_DH_KEY_BLOB *param = (BCRYPT_DH_KEY_BLOB *)pbInput;

                    param->header.dwMagic = dwMagic;
                    param->header.cbKey   = key.KeyLength;

                    key.Modulus.CopyTo(
                        rented.Memory.Slice(sizeof(BCRYPT_DH_KEY_BLOB_HEADER))
                        );

                    key.Generator.CopyTo(
                        rented.Memory.Slice(sizeof(BCRYPT_DH_KEY_BLOB_HEADER) + key.Modulus.Length)
                        );

                    incoming.Public.CopyTo(
                        rented.Memory.Slice(sizeof(BCRYPT_DH_KEY_BLOB_HEADER) + key.Modulus.Length + key.Generator.Length)
                        );

                    if (incoming.Type == AsymmetricKeyType.Private && incoming.Private.Length > 0)
                    {
                        incoming.Private.CopyTo(
                            rented.Memory.Slice(sizeof(BCRYPT_DH_KEY_BLOB_HEADER) + key.Modulus.Length + key.Generator.Length + key.Public.Length)
                            );
                    }

                    var status = BCryptImportKeyPair(
                        hAlgorithm,
                        IntPtr.Zero,
                        keyType,
                        ref hKey,
                        pbInput,
                        structSize,
                        0
                        );

                    ThrowIfNotNtSuccess(status);
                }
            }
        }
        internal static bool TryDecodePem(ReadOnlySpan <byte> rawData, DerCallback derCallback)
        {
            // If the character is a control character that isn't whitespace, then we're probably using a DER encoding
            // and not using a PEM encoding in ASCII.
            if (char.IsControl((char)rawData[0]) && !char.IsWhiteSpace((char)rawData[0]))
            {
                return(false);
            }

            // Look for the PEM marker. This doesn't guarantee it will be a valid PEM since we don't check whether
            // the marker is at the beginning of line or whether the line is a complete marker. It's just a quick
            // check to avoid conversion from bytes to characters if the content is DER encoded.
            if (rawData.IndexOf(pemBegin) < 0)
            {
                return(false);
            }

            char[] certPem = ArrayPool <char> .Shared.Rent(rawData.Length);

            byte[]? certBytes = null;

            try
            {
                Encoding.ASCII.GetChars(rawData, certPem);

                foreach ((ReadOnlySpan <char> contents, PemFields fields) in new PemEnumerator(certPem.AsSpan(0, rawData.Length)))
                {
                    ReadOnlySpan <char> label = contents[fields.Label];

                    if (label.SequenceEqual(PemLabels.X509Certificate) || label.SequenceEqual(PemLabels.Pkcs7Certificate))
                    {
                        certBytes = CryptoPool.Rent(fields.DecodedDataLength);

                        if (!Convert.TryFromBase64Chars(contents[fields.Base64Data], certBytes, out int bytesWritten) ||
                            bytesWritten != fields.DecodedDataLength)
                        {
                            Debug.Fail("The contents should have already been validated by the PEM reader.");
                            throw new CryptographicException(SR.Cryptography_X509_NoPemCertificate);
                        }

                        X509ContentType contentType =
                            label.SequenceEqual(PemLabels.X509Certificate) ?
                            X509ContentType.Cert :
                            X509ContentType.Pkcs7;
                        bool cont = derCallback(certBytes.AsSpan(0, bytesWritten), contentType);

                        CryptoPool.Return(certBytes, clearSize: 0);
                        certBytes = null;

                        if (!cont)
                        {
                            return(true);
                        }
                    }
                }
            }
            finally
            {
                ArrayPool <char> .Shared.Return(certPem, clearArray : true);

                if (certBytes != null)
                {
                    CryptoPool.Return(certBytes, clearSize: 0);
                }
            }

            return(true);
        }
        private static ArraySegment <byte> EncodeAuthSafe(
            AsnWriter tmpWriter,
            ReadOnlyMemory <byte> encodedKeyContents,
            ReadOnlyMemory <byte> encodedCertContents,
            bool isPkcs12,
            string hmacOid,
            string encryptionAlgorithmOid,
            Span <byte> salt,
            Span <byte> certContentsIv)
        {
            Debug.Assert(tmpWriter.GetEncodedLength() == 0);

            tmpWriter.PushSequence();

            if (!encodedKeyContents.IsEmpty)
            {
                tmpWriter.PushSequence();
                tmpWriter.WriteObjectIdentifier(Oids.Pkcs7Data);
                tmpWriter.PushSequence(s_contextSpecific0);

                ReadOnlySpan <byte> keyContents = encodedKeyContents.Span;
                tmpWriter.WriteOctetString(keyContents);

                tmpWriter.PopSequence(s_contextSpecific0);
                tmpWriter.PopSequence();
            }

            if (!encodedCertContents.IsEmpty)
            {
                tmpWriter.PushSequence();

                {
                    tmpWriter.WriteObjectIdentifier(Oids.Pkcs7Encrypted);

                    tmpWriter.PushSequence(s_contextSpecific0);
                    tmpWriter.PushSequence();

                    {
                        // No unprotected attributes: version 0 data
                        tmpWriter.WriteInteger(0);

                        tmpWriter.PushSequence();

                        {
                            tmpWriter.WriteObjectIdentifier(Oids.Pkcs7Data);

                            PasswordBasedEncryption.WritePbeAlgorithmIdentifier(
                                tmpWriter,
                                isPkcs12,
                                encryptionAlgorithmOid,
                                salt,
                                s_windowsPbe.IterationCount,
                                hmacOid,
                                certContentsIv);

                            tmpWriter.WriteOctetString(encodedCertContents.Span, s_contextSpecific0);
                            tmpWriter.PopSequence();
                        }

                        tmpWriter.PopSequence();
                        tmpWriter.PopSequence(s_contextSpecific0);
                    }

                    tmpWriter.PopSequence();
                }
            }

            tmpWriter.PopSequence();

            int authSafeLength = tmpWriter.GetEncodedLength();

            byte[] authSafe = CryptoPool.Rent(authSafeLength);

            if (!tmpWriter.TryEncode(authSafe, out authSafeLength))
            {
                Debug.Fail("TryEncode failed with a pre-allocated buffer");
                throw new InvalidOperationException();
            }

            tmpWriter.Reset();

            return(new ArraySegment <byte>(authSafe, 0, authSafeLength));
        }
        private byte[] ExportPfx(SafePasswordHandle password)
        {
            int certCount = 1;

            if (_singleCertPal == null)
            {
                Debug.Assert(_certs != null);
                certCount = _certs.Count;
            }

            CertBagAsn[] certBags = ArrayPool <CertBagAsn> .Shared.Rent(certCount);

            SafeBagAsn[] keyBags = ArrayPool <SafeBagAsn> .Shared.Rent(certCount);

            AttributeAsn[] certAttrs = ArrayPool <AttributeAsn> .Shared.Rent(certCount);

            certAttrs.AsSpan(0, certCount).Clear();

            AsnWriter           tmpWriter       = new AsnWriter(AsnEncodingRules.DER);
            ArraySegment <byte> encodedAuthSafe = default;

            bool gotRef = false;

            password.DangerousAddRef(ref gotRef);

            try
            {
                ReadOnlySpan <char> passwordSpan = password.DangerousGetSpan();

                int keyIdx  = 0;
                int certIdx = 0;

                if (_singleCertPal != null)
                {
                    BuildBags(
                        _singleCertPal,
                        passwordSpan,
                        tmpWriter,
                        certBags,
                        certAttrs,
                        keyBags,
                        ref certIdx,
                        ref keyIdx);
                }
                else
                {
                    foreach (X509Certificate2 cert in _certs !)
                    {
                        BuildBags(
                            cert.Pal,
                            passwordSpan,
                            tmpWriter,
                            certBags,
                            certAttrs,
                            keyBags,
                            ref certIdx,
                            ref keyIdx);
                    }
                }

                encodedAuthSafe = EncodeAuthSafe(
                    tmpWriter,
                    keyBags,
                    keyIdx,
                    certBags,
                    certAttrs,
                    certIdx,
                    passwordSpan);

                return(MacAndEncode(tmpWriter, encodedAuthSafe, passwordSpan));
            }
            finally
            {
                password.DangerousRelease();
                certAttrs.AsSpan(0, certCount).Clear();
                certBags.AsSpan(0, certCount).Clear();
                keyBags.AsSpan(0, certCount).Clear();
                ArrayPool <AttributeAsn> .Shared.Return(certAttrs);

                ArrayPool <CertBagAsn> .Shared.Return(certBags);

                ArrayPool <SafeBagAsn> .Shared.Return(keyBags);

                if (encodedAuthSafe.Array != null)
                {
                    CryptoPool.Return(encodedAuthSafe);
                }
            }
        }
        private static string?GetCdpUrl(SafeX509Handle cert)
        {
            ArraySegment <byte> crlDistributionPoints =
                OpenSslX509CertificateReader.FindFirstExtension(cert, Oids.CrlDistributionPoints);

            if (crlDistributionPoints.Array == null)
            {
                if (OpenSslX509ChainEventSource.Log.IsEnabled())
                {
                    OpenSslX509ChainEventSource.Log.NoCdpFound(cert);
                }

                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)
                            {
                                if (Uri.TryCreate(name.Uri, UriKind.Absolute, out Uri? uri) &&
                                    uri.Scheme == "http")
                                {
                                    return(name.Uri);
                                }
                                else
                                {
                                    if (OpenSslX509ChainEventSource.Log.IsEnabled())
                                    {
                                        OpenSslX509ChainEventSource.Log.NonHttpCdpEntry(name.Uri);
                                    }
                                }
                            }
                        }

                        if (OpenSslX509ChainEventSource.Log.IsEnabled())
                        {
                            OpenSslX509ChainEventSource.Log.NoMatchingCdpEntry();
                        }
                    }
                }
            }
            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);
        }
Exemple #24
0
        private static ReadOnlyMemory <byte> DeriveManually(ReadOnlyMemory <byte> salt, int iterations, int keySize, IHmacAlgorithm hmac)
        {
            if (RequireNativeImplementation)
            {
                throw new CryptographicException("The caller requires the use of the native implementation but the platform doesn't support it.");
            }

            // This is here because .NET Standard doesn't include the built-in
            // ctor so it's possible the particular framework doesn't support it.
            // It's mostly based off the original Rfc2898DeriveBytes implementation.
            //
            // https://github.com/aspnet/DataProtection/blob/9941fb825fcbeefec898093755553679410d8a6b/src/Microsoft.AspNetCore.Cryptography.KeyDerivation/PBKDF2/ManagedPbkdf2Provider.cs

            // PBKDF2 is defined in NIST SP800-132, Sec. 5.3.
            // http://csrc.nist.gov/publications/nistpubs/800-132/nist-sp800-132.pdf

            byte[] retVal = new byte[keySize];

            int numBytesWritten   = 0;
            int numBytesRemaining = keySize;

            // For each block index, U_0 := Salt || block_index

            var saltLength = salt.Length + sizeof(uint);

            using (var saltWithBlockIndexRented = CryptoPool.Rent <byte>(checked (saltLength)))
            {
                var saltWithBlockIndex = saltWithBlockIndexRented.Memory.Slice(0, saltLength);

                salt.CopyTo(saltWithBlockIndex);

                for (uint blockIndex = 1; numBytesRemaining > 0; blockIndex++)
                {
                    // write the block index out as big-endian

                    saltWithBlockIndex.Span[saltWithBlockIndex.Length - 4] = (byte)(blockIndex >> 24);
                    saltWithBlockIndex.Span[saltWithBlockIndex.Length - 3] = (byte)(blockIndex >> 16);
                    saltWithBlockIndex.Span[saltWithBlockIndex.Length - 2] = (byte)(blockIndex >> 8);
                    saltWithBlockIndex.Span[saltWithBlockIndex.Length - 1] = (byte)blockIndex;

                    // U_1 = PRF(U_0) = PRF(Salt || block_index)
                    // T_blockIndex = U_1

                    byte[] u_iter       = hmac.ComputeHashArray(saltWithBlockIndex); // this is U_1
                    byte[] t_blockIndex = u_iter;

                    for (int iter = 1; iter < iterations; iter++)
                    {
                        u_iter = hmac.ComputeHashArray(u_iter);

                        for (int i = 0; i < u_iter.Length; i++)
                        {
                            t_blockIndex[i] ^= u_iter[i];
                        }

                        // At this point, the 'U_iter' variable actually contains U_{iter+1} (due to indexing differences).
                    }

                    // At this point, we're done iterating on this block, so copy the transformed block into retVal.

                    int numBytesToCopy = Math.Min(numBytesRemaining, t_blockIndex.Length);

                    Buffer.BlockCopy(t_blockIndex, 0, retVal, numBytesWritten, numBytesToCopy);

                    numBytesWritten   += numBytesToCopy;
                    numBytesRemaining -= numBytesToCopy;
                }

                // retVal := T_1 || T_2 || ... || T_n, where T_n may be truncated to meet the desired output length

                return(retVal);
            }
        }
Exemple #25
0
        // T-REC-X.690-201508 sec 8.19
        private void WriteObjectIdentifierCore(Asn1Tag tag, ReadOnlySpan <char> oidValue)
        {
            // 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 ArgumentException(SR.Argument_InvalidOidValue, nameof(oidValue));
            }
            if (oidValue[1] != '.')
            {
                throw new ArgumentException(SR.Argument_InvalidOidValue, nameof(oidValue));
            }

            // 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 ArgumentException(SR.Argument_InvalidOidValue, nameof(oidValue)),
                };

                // 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);
            }
        }
Exemple #26
0
            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 !));
            }
Exemple #27
0
        public static CspParameters GetProvParameters(this SafeProvOrNCryptKeyHandle handle)
        {
            // A normal key container name is a GUID (~34 bytes ASCII)
            // The longest standard provider name is 64 bytes (including the \0),
            // but we shouldn't have a CAPI call with a software CSP.
            //
            // In debug builds use a buffer which will need to be resized, but is big
            // enough to hold the DWORD "can't fail" values.
            Span <byte> stackSpan = stackalloc byte[
#if DEBUG
                sizeof(int)
#else
                64
#endif
                                    ];

            stackSpan.Clear();
            int size = stackSpan.Length;

            if (!Interop.Advapi32.CryptGetProvParam(handle, CryptProvParam.PP_PROVTYPE, stackSpan, ref size))
            {
                throw Interop.CPError.GetLastWin32Error().ToCryptographicException();
            }

            if (size != sizeof(int))
            {
                Debug.Fail("PP_PROVTYPE writes a DWORD - enum misalignment?");
                throw new CryptographicException();
            }

            int provType = MemoryMarshal.Read <int>(stackSpan.Slice(0, size));

            size = stackSpan.Length;
            if (!Interop.Advapi32.CryptGetProvParam(handle, CryptProvParam.PP_KEYSET_TYPE, stackSpan, ref size))
            {
                throw Interop.CPError.GetLastWin32Error().ToCryptographicException();
            }

            if (size != sizeof(int))
            {
                Debug.Fail("PP_KEYSET_TYPE writes a DWORD - enum misalignment?");
                throw new CryptographicException();
            }

            int keysetType = MemoryMarshal.Read <int>(stackSpan.Slice(0, size));

            // Only CRYPT_MACHINE_KEYSET is described as coming back, but be defensive.
            CspProviderFlags provFlags =
                ((CspProviderFlags)keysetType & CspProviderFlags.UseMachineKeyStore) |
                CspProviderFlags.UseExistingKey;

            byte[]      rented         = null;
            Span <byte> asciiStringBuf = stackSpan;

            string provName = GetStringProvParam(handle, CryptProvParam.PP_NAME, ref asciiStringBuf, ref rented, 0);
            int    maxClear = provName.Length;
            string keyName  = GetStringProvParam(handle, CryptProvParam.PP_CONTAINER, ref asciiStringBuf, ref rented, maxClear);

            maxClear = Math.Max(maxClear, keyName.Length);

            if (rented != null)
            {
                CryptoPool.Return(rented, maxClear);
            }

            return(new CspParameters(provType)
            {
                Flags = provFlags,
                KeyContainerName = keyName,
                ProviderName = provName,
            });
        }
        internal Interop.Crypto.X509VerifyStatusCode FindChainViaAia(
            ref List <X509Certificate2> downloadedCerts)
        {
            IntPtr lastCert = IntPtr.Zero;
            SafeX509StoreCtxHandle storeCtx = _storeCtx;

            Interop.Crypto.X509VerifyStatusCode statusCode =
                Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT;

            while (!IsCompleteChain(statusCode))
            {
                using (SafeX509Handle currentCert = Interop.Crypto.X509StoreCtxGetCurrentCert(storeCtx))
                {
                    IntPtr currentHandle = currentCert.DangerousGetHandle();

                    // No progress was made, give up.
                    if (currentHandle == lastCert)
                    {
                        break;
                    }

                    lastCert = currentHandle;

                    ArraySegment <byte> authorityInformationAccess =
                        OpenSslX509CertificateReader.FindFirstExtension(
                            currentCert,
                            Oids.AuthorityInformationAccess);

                    if (authorityInformationAccess.Count == 0)
                    {
                        break;
                    }

                    X509Certificate2 downloaded = DownloadCertificate(
                        authorityInformationAccess,
                        ref _remainingDownloadTime);

                    // The AIA record is contained in a public structure, so no need to clear it.
                    CryptoPool.Return(authorityInformationAccess.Array, clearSize: 0);

                    if (downloaded == null)
                    {
                        break;
                    }

                    if (downloadedCerts == null)
                    {
                        downloadedCerts = new List <X509Certificate2>();
                    }

                    AddToStackAndUpRef(downloaded.Handle, _untrustedLookup);
                    downloadedCerts.Add(downloaded);

                    Interop.Crypto.X509StoreCtxRebuildChain(storeCtx);
                    statusCode = Interop.Crypto.X509StoreCtxGetError(storeCtx);
                }
            }

            if (statusCode == Interop.Crypto.X509VerifyStatusCode.X509_V_OK && downloadedCerts != null)
            {
                using (SafeX509StackHandle chainStack = Interop.Crypto.X509StoreCtxGetChain(_storeCtx))
                {
                    int           chainSize     = Interop.Crypto.GetX509StackFieldCount(chainStack);
                    Span <IntPtr> tempChain     = stackalloc IntPtr[DefaultChainCapacity];
                    byte[]        tempChainRent = null;

                    if (chainSize <= tempChain.Length)
                    {
                        tempChain = tempChain.Slice(0, chainSize);
                    }
                    else
                    {
                        int targetSize = checked (chainSize * IntPtr.Size);
                        tempChainRent = CryptoPool.Rent(targetSize);
                        tempChain     = MemoryMarshal.Cast <byte, IntPtr>(tempChainRent.AsSpan(0, targetSize));
                    }

                    for (int i = 0; i < chainSize; i++)
                    {
                        tempChain[i] = Interop.Crypto.GetX509StackField(chainStack, i);
                    }

                    // In the average case we never made it here.
                    //
                    // Given that we made it here, in the average remaining case
                    // we are doing a one item for which will match in the second position
                    // of an (on-average) 3 item collection.
                    //
                    // The only case where this loop really matters is if downloading the
                    // certificate made an alternate chain better, which may have resulted in
                    // an extra download and made the first one not be involved any longer. In
                    // that case, it's a 2 item for loop matching against a three item set.
                    //
                    // So N*M is well contained.
                    for (int i = downloadedCerts.Count - 1; i >= 0; i--)
                    {
                        X509Certificate2 downloadedCert = downloadedCerts[i];

                        if (!tempChain.Contains(downloadedCert.Handle))
                        {
                            downloadedCert.Dispose();
                            downloadedCerts.RemoveAt(i);
                        }
                    }

                    if (downloadedCerts.Count == 0)
                    {
                        downloadedCerts = null;
                    }

                    if (tempChainRent != null)
                    {
                        CryptoPool.Return(tempChainRent);
                    }
                }
            }

            return(statusCode);
        }
        public void SealWithMac(
            ReadOnlySpan <char> password,
            HashAlgorithmName hashAlgorithm,
            int iterationCount)
        {
            if (iterationCount < 1)
            {
                throw new ArgumentOutOfRangeException(nameof(iterationCount));
            }
            if (IsSealed)
            {
                throw new InvalidOperationException(SR.Cryptography_Pkcs12_PfxIsSealed);
            }

            byte[]? rentedAuthSafe = null;
            Span <byte> authSafeSpan = default;

            byte[]? rentedMac = null;
            Span <byte> macSpan = default;
            Span <byte> salt    = stackalloc byte[0];

            try
            {
                AsnWriter contentsWriter = new AsnWriter(AsnEncodingRules.BER);

                using (IncrementalHash hasher = IncrementalHash.CreateHash(hashAlgorithm))
                {
                    contentsWriter.PushSequence();
                    if (_contents != null)
                    {
                        foreach (ContentInfoAsn contentInfo in _contents)
                        {
                            contentInfo.Encode(contentsWriter);
                        }
                    }
                    contentsWriter.PopSequence();

                    rentedAuthSafe = CryptoPool.Rent(contentsWriter.GetEncodedLength());

                    if (!contentsWriter.TryEncode(rentedAuthSafe, out int written))
                    {
                        Debug.Fail("TryEncode failed with a pre-allocated buffer");
                        throw new InvalidOperationException();
                    }

                    authSafeSpan = rentedAuthSafe.AsSpan(0, written);

                    // Get an array of the proper size for the hash.
                    byte[] macKey = hasher.GetHashAndReset();
                    rentedMac = CryptoPool.Rent(macKey.Length);
                    macSpan   = rentedMac.AsSpan(0, macKey.Length);

                    // Since the biggest supported hash is SHA-2-512 (64 bytes), the
                    // 128-byte cap here shouldn't ever come into play.
                    Debug.Assert(macKey.Length <= 128);
                    salt = stackalloc byte[Math.Min(macKey.Length, 128)];
                    RandomNumberGenerator.Fill(salt);

                    Pkcs12Kdf.DeriveMacKey(
                        password,
                        hashAlgorithm,
                        iterationCount,
                        salt,
                        macKey);

                    using (IncrementalHash mac = IncrementalHash.CreateHMAC(hashAlgorithm, macKey))
                    {
                        mac.AppendData(authSafeSpan);

                        if (!mac.TryGetHashAndReset(macSpan, out int bytesWritten) || bytesWritten != macSpan.Length)
                        {
                            Debug.Fail($"TryGetHashAndReset wrote {bytesWritten} of {macSpan.Length} bytes");
                            throw new CryptographicException();
                        }
                    }
                }

                // https://tools.ietf.org/html/rfc7292#section-4
                //
                // PFX ::= SEQUENCE {
                //   version    INTEGER {v3(3)}(v3,...),
                //   authSafe   ContentInfo,
                //   macData    MacData OPTIONAL
                // }
                AsnWriter writer = new AsnWriter(AsnEncodingRules.BER);
                {
                    writer.PushSequence();

                    writer.WriteInteger(3);

                    writer.PushSequence();
                    {
                        writer.WriteObjectIdentifierForCrypto(Oids.Pkcs7Data);

                        Asn1Tag contextSpecific0 = new Asn1Tag(TagClass.ContextSpecific, 0);

                        writer.PushSequence(contextSpecific0);
                        {
                            writer.WriteOctetString(authSafeSpan);
                            writer.PopSequence(contextSpecific0);
                        }

                        writer.PopSequence();
                    }

                    // https://tools.ietf.org/html/rfc7292#section-4
                    //
                    // MacData ::= SEQUENCE {
                    //   mac        DigestInfo,
                    //   macSalt    OCTET STRING,
                    //   iterations INTEGER DEFAULT 1
                    //   -- Note: The default is for historical reasons and its use is
                    //   -- deprecated.
                    // }
                    writer.PushSequence();
                    {
                        writer.PushSequence();
                        {
                            writer.PushSequence();
                            {
                                writer.WriteObjectIdentifierForCrypto(PkcsHelpers.GetOidFromHashAlgorithm(hashAlgorithm));
                                writer.PopSequence();
                            }

                            writer.WriteOctetString(macSpan);
                            writer.PopSequence();
                        }

                        writer.WriteOctetString(salt);

                        if (iterationCount > 1)
                        {
                            writer.WriteInteger(iterationCount);
                        }

                        writer.PopSequence();
                    }

                    writer.PopSequence();
                    _sealedData = writer.Encode();
                }
            }
            finally
            {
                CryptographicOperations.ZeroMemory(macSpan);
                CryptographicOperations.ZeroMemory(authSafeSpan);

                if (rentedMac != null)
                {
                    // Already cleared
                    CryptoPool.Return(rentedMac, clearSize: 0);
                }

                if (rentedAuthSafe != null)
                {
                    // Already cleared
                    CryptoPool.Return(rentedAuthSafe, clearSize: 0);
                }
            }
        }
Exemple #30
0
        private void Process(ReadOnlySpan <byte> nonce, ReadOnlySpan <byte> input, Span <byte> output, Span <byte> tag, ReadOnlySpan <byte> associatedData, bool outputIsCiphertext)
        {
            using var encryptor = aes.CreateEncryptor();
            var tmp               = CryptoPool.Rent(16);
            var counter           = CryptoPool.Rent(16);
            var counterEnc        = CryptoPool.Rent(16);
            var nonceMac          = CryptoPool.Rent(16);
            var associatedDataMac = CryptoPool.Rent(16);
            var ciphertextMac     = CryptoPool.Rent(16);

            try
            {
                CryptographicOperations.ZeroMemory(tmp.AsSpan(0, 15));
                tmp[15] = 0; // N tag
                cmac.TransformBlock(tmp, 0, 16, null, 0);
                cmac.TryComputeHash(nonce, nonceMac, out var _);

                tmp[15] = 1; // H tag
                cmac.TransformBlock(tmp, 0, 16, null, 0);
                cmac.TryComputeHash(associatedData, associatedDataMac, out var _);

                cmac.Initialize();
                tmp[15] = 2; // C tag
                cmac.TransformBlock(tmp, 0, 16, null, 0);

                nonceMac.AsSpan().CopyTo(counter);
                while (input.Length >= 16)
                {
                    encryptor.TransformBlock(counter, 0, 16, counterEnc, 0);
                    if (outputIsCiphertext)
                    {
                        for (int i = 0; i < 16; i++)
                        {
                            tmp[i] = (byte)(input[i] ^ counterEnc[i]);
                        }
                        cmac.TransformBlock(tmp, 0, 16, null, 0);
                        tmp.AsSpan(0, 16).CopyTo(output);
                    }
                    else
                    {
                        input.Slice(0, 16).CopyTo(tmp);
                        cmac.TransformBlock(tmp, 0, 16, null, 0);
                        for (int i = 0; i < 16; i++)
                        {
                            output[i] = (byte)(input[i] ^ counterEnc[i]);
                        }
                    }
                    byte add = 1;
                    for (int i = 15; i >= 0; i--)
                    {
                        counter[i] += add;
                        add         = counter[i] == 0 ? 1 : 0;
                    }
                    input  = input.Slice(16);
                    output = output.Slice(16);
                }

                if (input.Length > 0)
                {
                    encryptor.TransformBlock(counter, 0, 16, counterEnc, 0);
                    if (outputIsCiphertext)
                    {
                        for (int i = 0; i < input.Length; i++)
                        {
                            tmp[i] = (byte)(input[i] ^ counterEnc[i]);
                        }
                        cmac.TransformBlock(tmp, 0, input.Length, null, 0);
                        tmp.AsSpan(0, input.Length).CopyTo(output);
                    }
                    else
                    {
                        input.CopyTo(tmp);
                        cmac.TransformBlock(tmp, 0, input.Length, null, 0);
                        for (int i = 0; i < input.Length; i++)
                        {
                            output[i] = (byte)(input[i] ^ counterEnc[i]);
                        }
                    }
                }

                cmac.TryComputeHash(Array.Empty <byte>(), ciphertextMac, out var _);

                for (int i = 0; i < tag.Length; i++)
                {
                    tag[i] = (byte)(nonceMac[i] ^ associatedDataMac[i] ^ ciphertextMac[i]);
                }
            }
            finally
            {
                cmac.Initialize();
                CryptoPool.Return(tmp, 16);
                CryptoPool.Return(counter, 16);
                CryptoPool.Return(counterEnc, 16);
                CryptoPool.Return(nonceMac, 16);
                CryptoPool.Return(associatedDataMac, 16);
                CryptoPool.Return(ciphertextMac, 16);
            }
        }