diff --git a/NBitcoin/BIP39/Mnemonic.cs b/NBitcoin/BIP39/Mnemonic.cs index f574fd9cd3..52a6e67fee 100644 --- a/NBitcoin/BIP39/Mnemonic.cs +++ b/NBitcoin/BIP39/Mnemonic.cs @@ -173,9 +173,19 @@ public byte[] DeriveSeed(string passphrase = null) var salt = Concat(NoBOMUTF8.GetBytes("mnemonic"), Normalize(passphrase)); var bytes = Normalize(_Mnemonic); #if NO_NATIVE_HMACSHA512 +#if NONATIVEHASH var mac = new NBitcoin.BouncyCastle.Crypto.Macs.HMac(new NBitcoin.BouncyCastle.Crypto.Digests.Sha512Digest()); mac.Init(new NBitcoin.BouncyCastle.Crypto.Parameters.KeyParameter(bytes)); return Pbkdf2.ComputeDerivedKey(mac, salt, 2048, 64); +#else + using (var sha512 = new Crypto.NativeDigests.ManagedSha512Digest()) + { + var mac = new NBitcoin.BouncyCastle.Crypto.Macs.HMac(sha512); + mac.Init(new NBitcoin.BouncyCastle.Crypto.Parameters.KeyParameter(bytes)); + return Pbkdf2.ComputeDerivedKey(mac, salt, 2048, 64); + } +#endif + #elif NO_NATIVE_RFC2898_HMACSHA512 return NBitcoin.Crypto.Pbkdf2.ComputeDerivedKey(new System.Security.Cryptography.HMACSHA512(bytes), salt, 2048, 64); #else diff --git a/NBitcoin/BouncyCastle/crypto/digests/GeneralDigest.cs b/NBitcoin/BouncyCastle/crypto/digests/GeneralDigest.cs index 98cf940a8d..20e5084318 100644 --- a/NBitcoin/BouncyCastle/crypto/digests/GeneralDigest.cs +++ b/NBitcoin/BouncyCastle/crypto/digests/GeneralDigest.cs @@ -8,10 +8,7 @@ namespace NBitcoin.BouncyCastle.Crypto.Digests * base implementation of MD4 family style digest as outlined in * "Handbook of Applied Cryptography", pages 344 - 347. */ - internal abstract class GeneralDigest -#if !NO_BC - : IDigest, IMemoable -#endif + internal abstract class GeneralDigest : IDigest, IMemoable { private const int BYTE_LENGTH = 64; @@ -133,9 +130,7 @@ public abstract string AlgorithmName } public abstract int GetDigestSize(); public abstract int DoFinal(byte[] output, int outOff); -#if !NO_BC public abstract IMemoable Copy(); public abstract void Reset(IMemoable t); -#endif } } diff --git a/NBitcoin/BouncyCastle/crypto/digests/RipeMD160Digest.cs b/NBitcoin/BouncyCastle/crypto/digests/RipeMD160Digest.cs index 3d152c1ce2..9732b75186 100644 --- a/NBitcoin/BouncyCastle/crypto/digests/RipeMD160Digest.cs +++ b/NBitcoin/BouncyCastle/crypto/digests/RipeMD160Digest.cs @@ -590,7 +590,6 @@ internal override void ProcessBlock() X[i] = 0; } } -#if !NO_BC public override IMemoable Copy() { return new RipeMD160Digest(this); @@ -602,7 +601,6 @@ public override void Reset(IMemoable other) CopyIn(d); } -#endif } } diff --git a/NBitcoin/BouncyCastle/crypto/digests/Sha1Digest.cs b/NBitcoin/BouncyCastle/crypto/digests/Sha1Digest.cs index c5b652acad..2acc52a7b3 100644 --- a/NBitcoin/BouncyCastle/crypto/digests/Sha1Digest.cs +++ b/NBitcoin/BouncyCastle/crypto/digests/Sha1Digest.cs @@ -270,7 +270,6 @@ internal override void ProcessBlock() xOff = 0; Array.Clear(X, 0, 16); } -#if !NO_BC public override IMemoable Copy() { return new Sha1Digest(this); @@ -282,7 +281,6 @@ public override void Reset(IMemoable other) CopyIn(d); } -#endif } } #endif diff --git a/NBitcoin/BouncyCastle/crypto/digests/Sha256Digest.cs b/NBitcoin/BouncyCastle/crypto/digests/Sha256Digest.cs index 34a57d762c..38b0373e75 100644 --- a/NBitcoin/BouncyCastle/crypto/digests/Sha256Digest.cs +++ b/NBitcoin/BouncyCastle/crypto/digests/Sha256Digest.cs @@ -336,7 +336,6 @@ private static uint Theta1( 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 }; -#if !NO_BC public override IMemoable Copy() { return new Sha256Digest(this); @@ -348,6 +347,5 @@ public override void Reset(IMemoable other) CopyIn(d); } -#endif } } diff --git a/NBitcoin/Crypto/Cryptsharp/SCrypt.cs b/NBitcoin/Crypto/Cryptsharp/SCrypt.cs index 06851da8db..bd2b691b12 100644 --- a/NBitcoin/Crypto/Cryptsharp/SCrypt.cs +++ b/NBitcoin/Crypto/Cryptsharp/SCrypt.cs @@ -18,10 +18,10 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #endregion #if !NO_BC -using NBitcoin.BouncyCastle.Crypto.Parameters; using NBitcoin.BouncyCastle.Security; #endif using NBitcoin.Crypto.Internal; +using NBitcoin.BouncyCastle.Crypto.Parameters; using System; #if !USEBC using System.Security.Cryptography; diff --git a/NBitcoin/Crypto/Hashes.cs b/NBitcoin/Crypto/Hashes.cs index a404937299..606433f656 100644 --- a/NBitcoin/Crypto/Hashes.cs +++ b/NBitcoin/Crypto/Hashes.cs @@ -1,8 +1,9 @@ #if !NO_BC -using NBitcoin.BouncyCastle.Crypto.Parameters; using NBitcoin.BouncyCastle.Security; #endif +using NBitcoin.BouncyCastle.Crypto.Parameters; using NBitcoin.BouncyCastle.Crypto.Digests; +using NBitcoin.Crypto.NativeDigests; // Managed digests, compatible with BC's HMac algorithm. using System; using System.Collections.Generic; using System.IO; @@ -806,6 +807,7 @@ public static byte[] HMACSHA512(byte[] key, byte[] data) mac.DoFinal(result, 0); return result; } + #if HAS_SPAN public static bool HMACSHA512(byte[] key, ReadOnlySpan data, Span output, out int outputLength) { @@ -823,6 +825,7 @@ public static bool HMACSHA512(byte[] key, ReadOnlySpan data, Span ou return true; } #endif + public static byte[] HMACSHA256(byte[] key, byte[] data) { var mac = new NBitcoin.BouncyCastle.Crypto.Macs.HMac(new Sha256Digest()); @@ -833,11 +836,55 @@ public static byte[] HMACSHA256(byte[] key, byte[] data) return result; } -#else +#elif NO_NATIVE_HMACSHA512 // There is a native hash, but no native HMAC. + public static byte[] HMACSHA512(byte[] key, byte[] data) + { + using (var sha512 = new ManagedSha512Digest()) + { + var mac = new NBitcoin.BouncyCastle.Crypto.Macs.HMac(sha512); + mac.Init(new KeyParameter(key)); + mac.BlockUpdate(data, 0, data.Length); + byte[] result = new byte[mac.GetMacSize()]; + mac.DoFinal(result, 0); + return result; + } + } + +#if HAS_SPAN + public static bool HMACSHA512(byte[] key, ReadOnlySpan data, Span output, out int outputLength) + { + outputLength = 0; + var mac = new NBitcoin.BouncyCastle.Crypto.Macs.HMac(new Sha512Digest()); + var macSize = mac.GetMacSize(); + if (output.Length < macSize) + return false; + mac.Init(new KeyParameter(key)); + mac.BlockUpdate(data.ToArray(), 0, data.Length); + byte[] result = new byte[macSize]; + mac.DoFinal(result, 0); + result.CopyTo(result); + outputLength = result.Length; + } +#endif + + public static byte[] HMACSHA256(byte[] key, byte[] data) + { + using (var sha256 = new ManagedSha256Digest()) + { + var mac = new NBitcoin.BouncyCastle.Crypto.Macs.HMac(sha256); + mac.Init(new KeyParameter(key)); + mac.BlockUpdate(data, 0, data.Length); + byte[] result = new byte[mac.GetMacSize()]; + mac.DoFinal(result, 0); + return result; + } + } +#else // There is no native hash and no native HMAC public static byte[] HMACSHA512(byte[] key, byte[] data) { return new HMACSHA512(key).ComputeHash(data); } + #if HAS_SPAN public static bool HMACSHA512(byte[] key, ReadOnlySpan data, Span output, out int outputLength) { @@ -845,11 +892,13 @@ public static bool HMACSHA512(byte[] key, ReadOnlySpan data, Span ou return hmac.TryComputeHash(data, output, out outputLength); } #endif + public static byte[] HMACSHA256(byte[] key, byte[] data) { return new HMACSHA256(key).ComputeHash(data); } #endif + #if HAS_SPAN public static void BIP32Hash(byte[] chainCode, uint nChild, byte header, Span data, Span output) { diff --git a/NBitcoin/Crypto/NativeDigests/ManagedSha256Digest.cs b/NBitcoin/Crypto/NativeDigests/ManagedSha256Digest.cs new file mode 100644 index 0000000000..446511d3c2 --- /dev/null +++ b/NBitcoin/Crypto/NativeDigests/ManagedSha256Digest.cs @@ -0,0 +1,63 @@ +using NBitcoin.BouncyCastle.Crypto; +using System; + +namespace NBitcoin.Crypto.NativeDigests +{ +#if !NETSTANDARD1X && !NONATIVEHASH + + using System.Security.Cryptography; + + /// + /// A wrapper around the native SHA256, implements BouncyCastle's IDigest interface in order + /// to be compatible with BouncyCastle's HMac implementation. + /// + internal class ManagedSha256Digest : IDigest, IDisposable + { + private const int DigestLength = 32; + SHA256Managed nativeSha256 = null; + byte[] input; + + public ManagedSha256Digest() + { + Reset(); + } + + public string AlgorithmName => "SHA-256"; + + public void BlockUpdate(byte[] input, int inOff, int length) + { + this.input = new byte[length]; + Array.Copy(input, inOff, this.input, 0, length); + } + + public int DoFinal(byte[] output, int outOff) + { + var hash = nativeSha256.ComputeHash(input, 0, input.Length); + Array.Copy(hash, 0, output, outOff, hash.Length); + Reset(); + return DigestLength; + } + + public int GetByteLength() => 64; + + public int GetDigestSize() => DigestLength; + + public void Reset() + { + nativeSha256?.Dispose(); + nativeSha256 = new SHA256Managed(); + input = null; + } + + public void Update(byte input) + { + throw new NotImplementedException("Code monkey didn't expect you would need this."); + } + + public void Dispose() + { + nativeSha256?.Dispose(); + } + } +#endif +} diff --git a/NBitcoin/Crypto/NativeDigests/ManagedSha512Digest.cs b/NBitcoin/Crypto/NativeDigests/ManagedSha512Digest.cs new file mode 100644 index 0000000000..458c7c154e --- /dev/null +++ b/NBitcoin/Crypto/NativeDigests/ManagedSha512Digest.cs @@ -0,0 +1,62 @@ +using NBitcoin.BouncyCastle.Crypto; +using System; + +namespace NBitcoin.Crypto.NativeDigests +{ +#if !NETSTANDARD1X && !NONATIVEHASH + using System.Security.Cryptography; + + /// + /// A wrapper around the native SHA512, implements BouncyCastle's IDigest interface in order + /// to be compatible with BouncyCastle's HMac implementation. + /// + internal class ManagedSha512Digest : IDigest, IDisposable + { + private const int DigestLength = 64; + SHA512Managed nativeSha512 = null; + byte[] input; + + public ManagedSha512Digest() + { + Reset(); + } + + public string AlgorithmName => "SHA-512"; + + public void BlockUpdate(byte[] input, int inOff, int length) + { + this.input = new byte[length]; + Array.Copy(input, inOff, this.input, 0, length); + } + + public int DoFinal(byte[] output, int outOff) + { + var hash = nativeSha512.ComputeHash(input, 0, input.Length); + Array.Copy(hash, 0, output, outOff, hash.Length); + Reset(); + return DigestLength; + } + + public int GetByteLength() => 64; + + public int GetDigestSize() => DigestLength; + + public void Reset() + { + nativeSha512?.Dispose(); + nativeSha512 = new SHA512Managed(); + input = null; + } + + public void Update(byte input) + { + throw new NotImplementedException("Code monkey didn't expect you would need this."); + } + + public void Dispose() + { + nativeSha512?.Dispose(); + } + } +#endif +} diff --git a/NBitcoin/NBitcoin.csproj b/NBitcoin/NBitcoin.csproj index 32f4c0656d..90d15f934e 100644 --- a/NBitcoin/NBitcoin.csproj +++ b/NBitcoin/NBitcoin.csproj @@ -87,6 +87,12 @@ + + + + + + diff --git a/NBitcoin/Secp256k1/Schnorr/ECPrivKey.cs b/NBitcoin/Secp256k1/Schnorr/ECPrivKey.cs index 887edf23bf..4b98f5dc3a 100644 --- a/NBitcoin/Secp256k1/Schnorr/ECPrivKey.cs +++ b/NBitcoin/Secp256k1/Schnorr/ECPrivKey.cs @@ -101,7 +101,7 @@ public bool TryGetNonce(Span nonce32, ReadOnlySpan msg32, ReadOnlySp #endif class SchnorrNonceFunction : INonceFunction { - byte[]? data = null; + readonly byte[]? data = null; public SchnorrNonceFunction(byte[]? nonceData = null) { this.data = nonceData;