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