Example #1
0
        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);
                }
            }
        }
Example #2
0
        public bool VerifyMac(ReadOnlySpan <char> password)
        {
            if (IntegrityMode != Pkcs12IntegrityMode.Password)
            {
                throw new InvalidOperationException(
                          SR.Format(
                              SR.Cryptography_Pkcs12_WrongModeForVerify,
                              Pkcs12IntegrityMode.Password,
                              IntegrityMode));
            }

            Debug.Assert(_decoded.MacData.HasValue);

            HashAlgorithmName hashAlgorithm;
            int expectedOutputSize;

            string algorithmValue = _decoded.MacData.Value.Mac.DigestAlgorithm.Algorithm.Value;

            switch (algorithmValue)
            {
            case Oids.Md5:
                expectedOutputSize = 128 >> 3;
                hashAlgorithm      = HashAlgorithmName.MD5;
                break;

            case Oids.Sha1:
                expectedOutputSize = 160 >> 3;
                hashAlgorithm      = HashAlgorithmName.SHA1;
                break;

            case Oids.Sha256:
                expectedOutputSize = 256 >> 3;
                hashAlgorithm      = HashAlgorithmName.SHA256;
                break;

            case Oids.Sha384:
                expectedOutputSize = 384 >> 3;
                hashAlgorithm      = HashAlgorithmName.SHA384;
                break;

            case Oids.Sha512:
                expectedOutputSize = 512 >> 3;
                hashAlgorithm      = HashAlgorithmName.SHA512;
                break;

            default:
                throw new CryptographicException(
                          SR.Format(SR.Cryptography_UnknownHashAlgorithm, algorithmValue));
            }

            if (_decoded.MacData.Value.Mac.Digest.Length != expectedOutputSize)
            {
                throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
            }

            // Cannot use the ArrayPool or stackalloc here because CreateHMAC needs a properly bounded array.
            byte[] derived = new byte[expectedOutputSize];

            int iterationCount =
                PasswordBasedEncryption.NormalizeIterationCount(_decoded.MacData.Value.IterationCount);

            Pkcs12Kdf.DeriveMacKey(
                password,
                hashAlgorithm,
                iterationCount,
                _decoded.MacData.Value.MacSalt.Span,
                derived);

            using (IncrementalHash hmac = IncrementalHash.CreateHMAC(hashAlgorithm, derived))
            {
                hmac.AppendData(_authSafeContents.Span);

                if (!hmac.TryGetHashAndReset(derived, out int bytesWritten) || bytesWritten != expectedOutputSize)
                {
                    Debug.Fail($"TryGetHashAndReset wrote {bytesWritten} bytes when {expectedOutputSize} was expected");
                    throw new CryptographicException();
                }

                return(CryptographicOperations.FixedTimeEquals(
                           derived,
                           _decoded.MacData.Value.Mac.Digest.Span));
            }
        }