internal static int ComputeTotp( #if NET6_0_OR_GREATER byte[] key, #else HashAlgorithm hashAlgorithm, #endif ulong timestepNumber, string?modifier) { // # of 0's = length of pin const int Mod = 1000000; // See https://tools.ietf.org/html/rfc4226 // We can add an optional modifier var timestepAsBytes = BitConverter.GetBytes(IPAddress.HostToNetworkOrder((long)timestepNumber)); #if NET6_0_OR_GREATER var hash = HMACSHA1.HashData(key, ApplyModifier(timestepAsBytes, modifier)); #else var hash = hashAlgorithm.ComputeHash(ApplyModifier(timestepAsBytes, modifier)); #endif // Generate DT string var offset = hash[hash.Length - 1] & 0xf; Debug.Assert(offset + 4 < hash.Length); var binaryCode = (hash[offset] & 0x7f) << 24 | (hash[offset + 1] & 0xff) << 16 | (hash[offset + 2] & 0xff) << 8 | (hash[offset + 3] & 0xff); return(binaryCode % Mod); }
protected override int HashDataOneShot(ReadOnlySpan <byte> key, Stream source, Span <byte> destination) => HMACSHA1.HashData(key, source, destination);
protected override byte[] HashDataOneShot(byte[] key, Stream source) => HMACSHA1.HashData(key, source);
protected override byte[] HashDataOneShot(ReadOnlySpan <byte> key, Stream source) => HMACSHA1.HashData(key, source);
private static unsafe byte[] MacAndEncode( AsnWriter tmpWriter, ReadOnlyMemory <byte> encodedAuthSafe, ReadOnlySpan <char> passwordSpan) { const int MacSize = 160 / 8; // HMAC-SHA1 is 160 bits. Span <byte> macKey = stackalloc byte[MacSize]; Span <byte> macSalt = stackalloc byte[MacSize]; Span <byte> macSpan = stackalloc byte[MacSize]; RandomNumberGenerator.Fill(macSalt); Pkcs12Kdf.DeriveMacKey( passwordSpan, HashAlgorithmName.SHA1, s_windowsPbe.IterationCount, macSalt, macKey); int bytesWritten = HMACSHA1.HashData(macKey, encodedAuthSafe.Span, macSpan); if (bytesWritten != MacSize) { Debug.Fail($"HMACSHA1.HashData wrote {bytesWritten} of {MacSize} bytes"); throw new CryptographicException(); } CryptographicOperations.ZeroMemory(macKey); // https://tools.ietf.org/html/rfc7292#section-4 // // PFX ::= SEQUENCE { // version INTEGER {v3(3)}(v3,...), // authSafe ContentInfo, // macData MacData OPTIONAL // } Debug.Assert(tmpWriter.GetEncodedLength() == 0); tmpWriter.PushSequence(); tmpWriter.WriteInteger(3); tmpWriter.PushSequence(); { tmpWriter.WriteObjectIdentifier(Oids.Pkcs7Data); tmpWriter.PushSequence(s_contextSpecific0); { tmpWriter.WriteOctetString(encodedAuthSafe.Span); tmpWriter.PopSequence(s_contextSpecific0); } tmpWriter.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. // } tmpWriter.PushSequence(); { tmpWriter.PushSequence(); { tmpWriter.PushSequence(); { tmpWriter.WriteObjectIdentifier(Oids.Sha1); tmpWriter.PopSequence(); } tmpWriter.WriteOctetString(macSpan); tmpWriter.PopSequence(); } tmpWriter.WriteOctetString(macSalt); tmpWriter.WriteInteger(s_windowsPbe.IterationCount); tmpWriter.PopSequence(); } tmpWriter.PopSequence(); return(tmpWriter.Encode()); }