From 8996c0d97c96ed46d438bf8a0245a89b5ed9459d Mon Sep 17 00:00:00 2001 From: Oliver Weichhold Date: Fri, 26 Jan 2018 20:17:29 +0100 Subject: [PATCH] Dev (#190) - Made RPC polling optional - Update configuration examples to be closer to real world usage - Added no RPC polling config example - Don't expose payment processing config extension data via API. Fixes #103 - Keep extra data in api result but sanitize it first - Increase PG command timeout - Add support for ZEN and XVG - Return Coin POW Algorithm in API results - Expose pool total miner earnings via API - Improved LTC block explorer links - Neoscrypt update - Neoscrypt tests - Support GBX & CRC - Don't tie ZMQ block notify to a single address. Fixes #183 - X17 hash integration - Drop paged retries from Monero Payments as those won't work anyway when transfer_split fails. - Support Verge X17 variant - Added support for MaxActiveJobs Bitcoin Pool extra config --- src/MiningCore.Tests/Crypto/HashingTests.cs | 36 + src/MiningCore/Api/ApiServer.cs | 12 +- .../Api/Extensions/MiningPoolExtensions.cs | 30 + .../Api/Responses/GetPoolsResponse.cs | 2 + .../Blockchain/Bitcoin/BitcoinConstants.cs | 9 +- .../Blockchain/Bitcoin/BitcoinJob.cs | 18 +- .../Blockchain/Bitcoin/BitcoinJobManager.cs | 84 +- .../Bitcoin/BitcoinPayoutHandler.cs | 10 +- .../Blockchain/Bitcoin/BitcoinPool.cs | 2 +- .../Blockchain/Bitcoin/BitcoinProperties.cs | 72 +- .../BitcoinDaemonEndpointConfigExtra.cs | 33 + .../Configuration/BitcoinPoolConfigExtra.cs | 8 +- src/MiningCore/Blockchain/CoinMetaData.cs | 59 +- src/MiningCore/Blockchain/JobManagerBase.cs | 28 +- .../Blockchain/Monero/MoneroPayoutHandler.cs | 57 +- .../Blockchain/ZCash/ZCashConstants.cs | 113 +- .../Blockchain/ZCash/ZCashPayoutHandler.cs | 2 +- src/MiningCore/Blockchain/ZCash/ZCashPool.cs | 2 +- src/MiningCore/Configuration/ClusterConfig.cs | 8 +- .../Crypto/Hashing/Algorithms/NeoScrypt.cs | 54 + .../Crypto/Hashing/Algorithms/Scrypt.cs | 26 +- .../Crypto/Hashing/Algorithms/X17.cs | 45 + .../DaemonInterface/DaemonClient.cs | 73 + .../Extensions/DictionaryExtensions.cs | 45 + .../Extensions/PoolingExtensions.cs | 16 + src/MiningCore/Extensions/StringExtensions.cs | 6 +- src/MiningCore/Native/LibMultihash.cs | 25 +- .../Notifications/NotificationService.cs | 2 +- src/MiningCore/Payments/PayoutHandlerBase.cs | 2 +- .../Postgres/Repositories/StatsRepository.cs | 10 + .../Repositories/IStatsRepository.cs | 1 + src/MiningCore/Program.cs | 2 +- .../runtimes/win-x64/native/libmultihash.dll | Bin 870912 -> 908800 bytes .../runtimes/win-x86/native/libmultihash.dll | Bin 870912 -> 907776 bytes src/Native/libmultihash/Makefile | 3 +- src/Native/libmultihash/exports.cpp | 8 +- src/Native/libmultihash/libmultihash.vcxproj | 8 +- .../libmultihash/libmultihash.vcxproj.filters | 24 +- src/Native/libmultihash/neoscrypt.c | 2884 ++++++++++++++--- src/Native/libmultihash/neoscrypt.h | 70 +- src/Native/libmultihash/sha3/hamsi_helper.c | 6 +- src/Native/libmultihash/sha3/haval_helper.c | 195 ++ src/Native/libmultihash/sha3/sph_haval.c | 975 ++++++ src/Native/libmultihash/sha3/sph_haval.h | 969 ++++++ src/Native/libmultihash/sha3/sph_sha2.c | 691 ++++ src/Native/libmultihash/sha3/sph_sha2.h | 371 +++ src/Native/libmultihash/sha3/sph_sha2big.c | 248 ++ src/Native/libmultihash/x17.c | 115 + src/Native/libmultihash/x17.h | 16 + 49 files changed, 6744 insertions(+), 731 deletions(-) create mode 100644 src/MiningCore/Blockchain/Bitcoin/Configuration/BitcoinDaemonEndpointConfigExtra.cs create mode 100644 src/MiningCore/Crypto/Hashing/Algorithms/NeoScrypt.cs create mode 100644 src/MiningCore/Crypto/Hashing/Algorithms/X17.cs create mode 100644 src/MiningCore/Extensions/DictionaryExtensions.cs create mode 100644 src/MiningCore/Extensions/PoolingExtensions.cs create mode 100644 src/Native/libmultihash/sha3/haval_helper.c create mode 100644 src/Native/libmultihash/sha3/sph_haval.c create mode 100644 src/Native/libmultihash/sha3/sph_haval.h create mode 100644 src/Native/libmultihash/sha3/sph_sha2.c create mode 100644 src/Native/libmultihash/sha3/sph_sha2.h create mode 100644 src/Native/libmultihash/sha3/sph_sha2big.c create mode 100644 src/Native/libmultihash/x17.c create mode 100644 src/Native/libmultihash/x17.h diff --git a/src/MiningCore.Tests/Crypto/HashingTests.cs b/src/MiningCore.Tests/Crypto/HashingTests.cs index 064624311..0f6d3d634 100644 --- a/src/MiningCore.Tests/Crypto/HashingTests.cs +++ b/src/MiningCore.Tests/Crypto/HashingTests.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics; using System.Linq; using MiningCore.Crypto.Hashing.Algorithms; using MiningCore.Crypto.Hashing.Equihash; @@ -13,6 +14,9 @@ public class HashingTests : TestBase { private static readonly byte[] testValue = Enumerable.Repeat((byte) 0x80, 32).ToArray(); + // some algos need 80 byte input buffers + private static readonly byte[] testValue2 = Enumerable.Repeat((byte)0x80, 80).ToArray(); + [Fact] public void Blake_Hash_Should_Match() { @@ -77,6 +81,22 @@ public void Scrypt_Hash_Should_Throw_On_Null_Input() Assert.Throws(() => hasher.Digest(null)); } + [Fact] + public void NeoScrypt_Hash_Should_Match() + { + var hasher = new NeoScrypt(0); + var result = hasher.Digest(testValue2).ToHexString(); + + Assert.Equal("7915d56de262bf23b1fb9104cf5d2a13fcbed2f6b4b9b657309c222b09f54bc0", result); + } + + [Fact] + public void NeoScrypt_Hash_Should_Throw_On_Null_Input() + { + var hasher = new NeoScrypt(0); + Assert.Throws(() => hasher.Digest(null)); + } + [Fact] public void ScryptN_Hash_Should_Match() { @@ -166,6 +186,22 @@ public void X11_Hash_Should_Throw_On_Null_Input() Assert.Throws(() => hasher.Digest(null)); } + [Fact] + public void X17_Hash_Should_Match() + { + var hasher = new X17(); + var result = hasher.Digest(testValue).ToHexString(); + + Assert.Equal("6a9a4f558168e60241e46fe44365021c4d7e7344144ab1739d6fb0125ac4c592", result); + } + + [Fact] + public void X17_Hash_Should_Throw_On_Null_Input() + { + var hasher = new Sha256S(); + Assert.Throws(() => hasher.Digest(null)); + } + [Fact] public void Skein_Hash_Should_Match() { diff --git a/src/MiningCore/Api/ApiServer.cs b/src/MiningCore/Api/ApiServer.cs index a0efde744..bc347181d 100644 --- a/src/MiningCore/Api/ApiServer.cs +++ b/src/MiningCore/Api/ApiServer.cs @@ -227,6 +227,7 @@ private async Task GetPoolInfosAsync(HttpContext context, Match m) var result = config.ToPoolInfo(mapper, stats); // enrich + result.TotalPaid = cf.Run(con => statsRepo.GetTotalPoolPayments(con, config.Id)); #if DEBUG var from = new DateTime(2018, 1, 6, 16, 0, 0); #else @@ -256,9 +257,10 @@ private async Task GetPoolInfoAsync(HttpContext context, Match m) var response = new GetPoolResponse { Pool = pool.ToPoolInfo(mapper, stats) - }; - + }; + // enrich + response.Pool.TotalPaid = cf.Run(con => statsRepo.GetTotalPoolPayments(con, pool.Id)); #if DEBUG var from = new DateTime(2018, 1, 7, 16, 0, 0); #else @@ -380,7 +382,7 @@ private async Task PagePoolPaymentsAsync(HttpContext context, Match m) .ToArray(); // enrich payments - CoinMetaData.PaymentInfoLinks.TryGetValue(pool.Coin.Type, out var txInfobaseUrl); + CoinMetaData.TxInfoLinks.TryGetValue(pool.Coin.Type, out var txInfobaseUrl); CoinMetaData.AddressInfoLinks.TryGetValue(pool.Coin.Type, out var addressInfobaseUrl); foreach (var payment in payments) @@ -426,7 +428,7 @@ private async Task GetMinerInfoAsync(HttpContext context, Match m) stats.LastPayment = statsResult.LastPayment.Created; // Compute info link - if (CoinMetaData.PaymentInfoLinks.TryGetValue(pool.Coin.Type, out var baseUrl)) + if (CoinMetaData.TxInfoLinks.TryGetValue(pool.Coin.Type, out var baseUrl)) stats.LastPaymentLink = string.Format(baseUrl, statsResult.LastPayment.TransactionConfirmationData); } @@ -464,7 +466,7 @@ private async Task PageMinerPaymentsAsync(HttpContext context, Match m) .ToArray(); // enrich payments - CoinMetaData.PaymentInfoLinks.TryGetValue(pool.Coin.Type, out var txInfobaseUrl); + CoinMetaData.TxInfoLinks.TryGetValue(pool.Coin.Type, out var txInfobaseUrl); CoinMetaData.AddressInfoLinks.TryGetValue(pool.Coin.Type, out var addressInfobaseUrl); foreach (var payment in payments) diff --git a/src/MiningCore/Api/Extensions/MiningPoolExtensions.cs b/src/MiningCore/Api/Extensions/MiningPoolExtensions.cs index 9bb5ca2cd..96dacf9ad 100644 --- a/src/MiningCore/Api/Extensions/MiningPoolExtensions.cs +++ b/src/MiningCore/Api/Extensions/MiningPoolExtensions.cs @@ -2,7 +2,9 @@ using AutoMapper; using MiningCore.Api.Responses; using MiningCore.Blockchain; +using MiningCore.Blockchain.Ethereum.Configuration; using MiningCore.Configuration; +using MiningCore.Extensions; using MiningCore.Mining; namespace MiningCore.Api.Extensions @@ -13,6 +15,9 @@ public static PoolInfo ToPoolInfo(this PoolConfig pool, IMapper mapper, Persiste { var poolInfo = mapper.Map(pool); + // enrich with basic information + poolInfo.Coin.Algorithm = GetPoolAlgorithm(pool); + poolInfo.PoolStats = mapper.Map(stats); poolInfo.NetworkStats = mapper.Map(stats); @@ -24,7 +29,32 @@ public static PoolInfo ToPoolInfo(this PoolConfig pool, IMapper mapper, Persiste // pool fees poolInfo.PoolFeePercent = (float)pool.RewardRecipients.Sum(x => x.Percentage); + // strip security critical stuff + if (poolInfo.PaymentProcessing.Extra != null) + { + var extra = poolInfo.PaymentProcessing.Extra; + + extra.StripValue(nameof(EthereumPoolPaymentProcessingConfigExtra.CoinbasePassword)); + } + return poolInfo; } + + private static string GetPoolAlgorithm(PoolConfig pool) + { + var result = pool.Coin.Algorithm; + + if (string.IsNullOrEmpty(result)) + { + if (CoinMetaData.CoinAlgorithm.TryGetValue(pool.Coin.Type, out var getter)) + result = getter(pool.Coin.Type); + } + + // Capitalize + if (!string.IsNullOrEmpty(result) && result.Length > 1) + result = result.Substring(0, 1).ToUpper() + result.Substring(1); + + return result; + } } } \ No newline at end of file diff --git a/src/MiningCore/Api/Responses/GetPoolsResponse.cs b/src/MiningCore/Api/Responses/GetPoolsResponse.cs index 992e20813..6642e30ba 100644 --- a/src/MiningCore/Api/Responses/GetPoolsResponse.cs +++ b/src/MiningCore/Api/Responses/GetPoolsResponse.cs @@ -30,6 +30,7 @@ namespace MiningCore.Api.Responses public class ApiCoinConfig { public string Type { get; set; } + public string Algorithm { get; set; } } public class ApiPoolPaymentProcessingConfig @@ -63,6 +64,7 @@ public partial class PoolInfo public PoolStats PoolStats { get; set; } public BlockchainStats NetworkStats { get; set; } public MinerPerformanceStats[] TopMiners { get; set; } + public decimal TotalPaid { get; set; } } public class GetPoolsResponse diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinConstants.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinConstants.cs index 13fe0b9c1..ea30b2ab5 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinConstants.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinConstants.cs @@ -80,6 +80,7 @@ public class KnownAddresses public static readonly Dictionary DevFeeAddresses = new Dictionary { {CoinType.BTC, "17QnVor1B6oK1rWnVVBrdX9gFzVkZZbhDm"}, + {CoinType.BCH, "1LJGTzNDTuTvkHpTxNSdmAEBAXAnEHDVqQ"}, {CoinType.LTC, "LTK6CWastkmBzGxgQhTTtCUjkjDA14kxzC"}, {CoinType.DOGE, "DGDuKRhBewGP1kbUz4hszNd2p6dDzWYy9Q"}, {CoinType.NMC, "NDSLDpFEcTbuRVcWHdJyiRZThVAcb5Z79o"}, @@ -92,9 +93,13 @@ public class KnownAddresses {CoinType.MONA, "MBbkeAM3VQKg474bgxJEXrtcnMg8cjHY3S"}, {CoinType.VTC, "VfCAvPVrksYvwcpU7E44e51HxfvVhcxMXf"}, {CoinType.ZEC, "t1YHZHz2DGVMJiggD2P4fBQ2TAPgtLSUwZ7"}, - {CoinType.ZCL, "t1Ysa2CHdpMu8T2zjYXQNQxPgGh8ehe9QYo"}, + {CoinType.ZCL, "t1MFU1vD3YKgsK6Uh8hW7UTY8mKAV2xVqBr"}, + {CoinType.ZEN, "znigQacfTvRiwD2TRhwkBHLNchQ2AZisD94"}, {CoinType.BTG, "GQb77ZuMCyJGZFyxpzqNfm7GB1rQreP4n6"}, - {CoinType.XMR, "475YVJbPHPedudkhrcNp1wDcLMTGYusGPF5fqE7XjnragVLPdqbCHBdZg3dF4dN9hXMjjvGbykS6a77dTAQvGrpiQqHp2eH"} + {CoinType.MOON, "2QvpGimMYLyqKsczQXZjv56h6me3M8orwj" }, + {CoinType.XVG, "D5xPoHLM6HPkwWSqAweECTSQirJBmRjS8i" }, + {CoinType.XMR, "475YVJbPHPedudkhrcNp1wDcLMTGYusGPF5fqE7XjnragVLPdqbCHBdZg3dF4dN9hXMjjvGbykS6a77dTAQvGrpiQqHp2eH"}, + {CoinType.ETN, "etnkQJwBCjmR1MfkU8D355ZWxxLMhs8miPrtKHWN4U3uUowq9ugeKccVBoEG3n13n74us5AkT8tEoTog46w4HBgn8sMuBRhm9h"}, }; } diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinJob.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinJob.cs index 72da03f57..2d555fc80 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinJob.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinJob.cs @@ -23,7 +23,6 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using System.Globalization; using System.IO; using System.Linq; -using System.Numerics; using System.Text; using MiningCore.Blockchain.Bitcoin.DaemonResponses; using MiningCore.Configuration; @@ -333,16 +332,19 @@ protected virtual BitcoinShare ProcessShareInternal(StratumClient worker, string var result = new BitcoinShare { BlockHeight = BlockTemplate.Height, - IsBlockCandidate = isBlockCandidate + BlockReward = rewardToPool.ToDecimal(MoneyUnit.BTC), + NetworkDifficulty = Difficulty * shareMultiplier, + Difficulty = stratumDifficulty, }; var blockBytes = SerializeBlock(headerBytes, coinbase); - result.BlockHex = blockBytes.ToHexString(); - result.BlockHash = blockHasher.Digest(headerBytes, nTime).ToHexString(); - result.BlockHeight = BlockTemplate.Height; - result.BlockReward = rewardToPool.ToDecimal(MoneyUnit.BTC); - result.NetworkDifficulty = Difficulty * shareMultiplier; - result.Difficulty = stratumDifficulty; + + if (isBlockCandidate) + { + result.IsBlockCandidate = true; + result.BlockHex = blockBytes.ToHexString(); + result.BlockHash = blockHasher.Digest(headerBytes, nTime).ToHexString(); + } return result; } diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs index f13e945df..9c3a84d2b 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs @@ -20,13 +20,9 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using System; using System.Collections.Generic; -using System.IO; using System.Linq; using System.Net; -using System.Reactive.Disposables; using System.Reactive.Linq; -using System.Text; -using System.Threading; using System.Threading.Tasks; using Autofac; using MiningCore.Blockchain.Bitcoin.Configuration; @@ -38,14 +34,11 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using MiningCore.Crypto.Hashing.Special; using MiningCore.DaemonInterface; using MiningCore.Extensions; -using MiningCore.Mining; using MiningCore.Notifications; using MiningCore.Stratum; using MiningCore.Time; using MiningCore.Util; using NBitcoin; -using NetMQ; -using NetMQ.Sockets; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using NLog; @@ -76,12 +69,12 @@ public BitcoinJobManager( protected readonly NotificationService notificationService; protected readonly IMasterClock clock; protected DaemonClient daemon; - protected BitcoinPoolConfigExtra extraPoolConfig; protected readonly IExtraNonceProvider extraNonceProvider; protected const int ExtranonceBytes = 4; protected readonly IHashAlgorithm sha256d = new Sha256D(); protected readonly IHashAlgorithm sha256dReverse = new DigestReverser(new Sha256D()); - protected const int MaxActiveJobs = 4; + protected int maxActiveJobs = 4; + protected BitcoinPoolConfigExtra extraPoolConfig; protected readonly IHashAlgorithm sha256s = new Sha256S(); protected readonly List validJobs = new List(); protected IHashAlgorithm blockHasher; @@ -112,60 +105,21 @@ protected virtual void SetupJobUpdates() var sources = new List>(); var cancelTimeout = new List>(); - // block updates via ZMQ pub/sub - var zmqPublisherSocket = extraPoolConfig?.ZmqBlockNotifySocket?.Trim(); + // collect ports + var zmq = poolConfig.Daemons + .Where(x => !string.IsNullOrEmpty(x.Extra.SafeExtensionDataAs()?.ZmqBlockNotifySocket)) + .ToDictionary(x => x, x => x.Extra.SafeExtensionDataAs().ZmqBlockNotifySocket); - if (!string.IsNullOrEmpty(zmqPublisherSocket)) + if (zmq.Count > 0) { - var newJobsPubSub = Observable.Defer(()=> Observable.Create(obs => - { - var tcs = new CancellationTokenSource(); - - Task.Factory.StartNew(() => - { - while (!tcs.IsCancellationRequested) - { - try - { - using (var subSocket = new SubscriberSocket()) - { - //subSocket.Options.ReceiveHighWatermark = 1000; - subSocket.Connect(zmqPublisherSocket); - subSocket.Subscribe(BitcoinConstants.ZmqPublisherTopicBlockHash); - - logger.Info($"Subscribed to {zmqPublisherSocket}/{BitcoinConstants.ZmqPublisherTopicBlockHash} for ZMQ pub/sub block updates"); - - while (true) - { - subSocket.ReceiveMultipartMessage(2); - //var msg = subSocket.ReceiveMultipartMessage(2); - //var topic = msg.First().ConvertToString(Encoding.UTF8); - //var body = msg.Last().ConvertToString(Encoding.UTF8); - - obs.OnNext(true); - } - } - } - - catch (Exception ex) - { - logger.Error(ex); - } - - // do not consume all CPU cycles in case of a long lasting error condition - Thread.Sleep(1000); - } - }, tcs.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default); + logger.Info(() => $"[{LogCat}] Subscribing to ZMQ push-updates from {string.Join(", ", zmq.Values)}"); - return Disposable.Create(() => - { - tcs.Cancel(); - }); - })) - .Select(_ => Observable.FromAsync(() => UpdateJob(false, "ZMQ pub/sub"))) - .Concat() - .Publish() - .RefCount(); + var newJobsPubSub = daemon.ZmqSubscribe(zmq, BitcoinConstants.ZmqPublisherTopicBlockHash, 2) + .Do(x=> x.Dispose()) // we don't care about the contents + .Select(_ => Observable.FromAsync(() => UpdateJob(false, "ZMQ pub/sub"))) + .Concat() + .Publish() + .RefCount(); sources.Add(newJobsPubSub); cancelTimeout.Add(newJobsPubSub); @@ -475,6 +429,9 @@ public override void Configure(PoolConfig poolConfig, ClusterConfig clusterConfi { extraPoolConfig = poolConfig.Extra.SafeExtensionDataAs(); + if (extraPoolConfig?.MaxActiveJobs.HasValue == true) + maxActiveJobs = extraPoolConfig.MaxActiveJobs.Value; + base.Configure(poolConfig, clusterConfig); } @@ -657,8 +614,9 @@ protected virtual async Task UpdateJob(bool forceUpdate, string via = null var job = currentJob; var isNew = job == null || - job.BlockTemplate?.PreviousBlockhash != blockTemplate.PreviousBlockhash || - job.BlockTemplate?.Height < blockTemplate.Height; + (blockTemplate != null && + job.BlockTemplate?.PreviousBlockhash != blockTemplate.PreviousBlockhash && + blockTemplate.Height > job.BlockTemplate?.Height); if (isNew || forceUpdate) { @@ -685,7 +643,7 @@ protected virtual async Task UpdateJob(bool forceUpdate, string via = null validJobs.Add(job); // trim active jobs - while (validJobs.Count > MaxActiveJobs) + while (validJobs.Count > maxActiveJobs) validJobs.RemoveAt(0); } diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinPayoutHandler.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinPayoutHandler.cs index af0fa4a7e..8c7b6e88b 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinPayoutHandler.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinPayoutHandler.cs @@ -46,9 +46,9 @@ namespace MiningCore.Blockchain.Bitcoin [CoinMetadata( CoinType.BTC, CoinType.BCH, CoinType.NMC, CoinType.PPC, CoinType.LTC, CoinType.DOGE, CoinType.DGB, CoinType.VIA, - CoinType.GRS, CoinType.MONA, CoinType.VTC, - CoinType.BTG, CoinType.GLT, CoinType.STAK, - CoinType.MOON)] + CoinType.GRS, CoinType.MONA, CoinType.VTC, CoinType.BTG, + CoinType.GLT, CoinType.STAK, CoinType.MOON, CoinType.XVG, + CoinType.GBX, CoinType.CRC)] public class BitcoinPayoutHandler : PayoutHandlerBase, IPayoutHandler { @@ -74,7 +74,7 @@ public BitcoinPayoutHandler( protected readonly IComponentContext ctx; protected DaemonClient daemon; protected BitcoinCoinProperties coinProperties; - protected BitcoinPoolConfigExtra extraPoolConfig; + protected BitcoinDaemonEndpointConfigExtra extraPoolConfig; protected BitcoinPoolPaymentProcessingConfigExtra extraPoolPaymentProcessingConfig; protected override string LogCategory => "Bitcoin Payout Handler"; @@ -88,7 +88,7 @@ public virtual Task ConfigureAsync(ClusterConfig clusterConfig, PoolConfig poolC this.poolConfig = poolConfig; this.clusterConfig = clusterConfig; - extraPoolConfig = poolConfig.Extra.SafeExtensionDataAs(); + extraPoolConfig = poolConfig.Extra.SafeExtensionDataAs(); extraPoolPaymentProcessingConfig = poolConfig.PaymentProcessing.Extra.SafeExtensionDataAs(); coinProperties = BitcoinProperties.GetCoinProperties(poolConfig.Coin.Type, poolConfig.Coin.Algorithm); diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinPool.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinPool.cs index 29438ebb1..a9525f54c 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinPool.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinPool.cs @@ -34,7 +34,7 @@ namespace MiningCore.Blockchain.Bitcoin CoinType.BTC, CoinType.BCH, CoinType.NMC, CoinType.PPC, CoinType.LTC, CoinType.DOGE, CoinType.DGB, CoinType.VIA, CoinType.GRS, CoinType.MONA, CoinType.VTC, CoinType.GLT, - CoinType.MOON)] + CoinType.MOON, CoinType.XVG, CoinType.GBX, CoinType.CRC)] public class BitcoinPool : BitcoinPoolBase, BlockTemplate> { public BitcoinPool(IComponentContext ctx, diff --git a/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs b/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs index 701b1b3d3..80036d849 100644 --- a/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs +++ b/src/MiningCore/Blockchain/Bitcoin/BitcoinProperties.cs @@ -11,13 +11,14 @@ namespace MiningCore.Blockchain.Bitcoin public class BitcoinCoinProperties { public BitcoinCoinProperties(double shareMultiplier, IHashAlgorithm coinbaseHasher, IHashAlgorithm headerHasher, - IHashAlgorithm blockHasher, IHashAlgorithm posblockHasher = null) + IHashAlgorithm blockHasher, string algorithm, IHashAlgorithm posblockHasher = null) { ShareMultiplier = shareMultiplier; CoinbaseHasher = coinbaseHasher; HeaderHasher = headerHasher; BlockHasher = blockHasher; PoSBlockHasher = posblockHasher; + Algorithm = algorithm; } public double ShareMultiplier { get; } @@ -25,6 +26,7 @@ public BitcoinCoinProperties(double shareMultiplier, IHashAlgorithm coinbaseHash public IHashAlgorithm HeaderHasher { get; } public IHashAlgorithm BlockHasher { get; } public IHashAlgorithm PoSBlockHasher { get; } + public string Algorithm { get; } } public class BitcoinProperties @@ -33,42 +35,50 @@ public class BitcoinProperties private static readonly IHashAlgorithm sha256D = new Sha256D(); private static readonly IHashAlgorithm sha256DReverse = new DigestReverser(sha256D); private static readonly IHashAlgorithm x11 = new X11(); + private static readonly IHashAlgorithm x17 = new X17(); private static readonly IHashAlgorithm groestl = new Groestl(); private static readonly IHashAlgorithm lyra2Rev2 = new Lyra2Rev2(); private static readonly IHashAlgorithm scrypt_1024_1 = new Scrypt(1024, 1); private static readonly IHashAlgorithm skein = new Skein(); private static readonly IHashAlgorithm qubit = new Qubit(); private static readonly IHashAlgorithm groestlMyriad = new GroestlMyriad(); + private static readonly IHashAlgorithm neoScryptProfile1 = new NeoScrypt(0x80000620); private static readonly BitcoinCoinProperties sha256Coin = - new BitcoinCoinProperties(1, sha256D, sha256D, sha256DReverse); + new BitcoinCoinProperties(1, sha256D, sha256D, sha256DReverse, "Sha256"); private static readonly BitcoinCoinProperties scryptCoin = - new BitcoinCoinProperties(Math.Pow(2, 16), sha256D, scrypt_1024_1, sha256DReverse, new DigestReverser(scrypt_1024_1)); + new BitcoinCoinProperties(Math.Pow(2, 16), sha256D, scrypt_1024_1, sha256DReverse, "Scrypt", new DigestReverser(scrypt_1024_1)); private static readonly BitcoinCoinProperties groestlCoin = - new BitcoinCoinProperties(Math.Pow(2, 8), sha256S, groestl, new DigestReverser(groestl)); + new BitcoinCoinProperties(Math.Pow(2, 8), sha256S, groestl, new DigestReverser(groestl), "Groestl"); private static readonly BitcoinCoinProperties lyra2Rev2CoinVariantA = - new BitcoinCoinProperties(Math.Pow(2, 8), sha256D, lyra2Rev2, sha256DReverse); + new BitcoinCoinProperties(Math.Pow(2, 8), sha256D, lyra2Rev2, sha256DReverse, "Lyra2re2"); private static readonly BitcoinCoinProperties lyra2Rev2CoinVariantB = - new BitcoinCoinProperties(Math.Pow(2, 8), sha256D, lyra2Rev2, new DigestReverser(lyra2Rev2)); + new BitcoinCoinProperties(Math.Pow(2, 8), sha256D, lyra2Rev2, new DigestReverser(lyra2Rev2), "Lyra2re2"); private static readonly BitcoinCoinProperties x11Coin = - new BitcoinCoinProperties(1, sha256D, x11, new DigestReverser(x11)); + new BitcoinCoinProperties(1, sha256D, x11, new DigestReverser(x11), "X11"); private static readonly BitcoinCoinProperties skeinCoin = - new BitcoinCoinProperties(1, sha256D, skein, sha256DReverse); + new BitcoinCoinProperties(1, sha256D, skein, sha256DReverse, "Skein"); private static readonly BitcoinCoinProperties qubitCoin = - new BitcoinCoinProperties(1, sha256D, qubit, sha256DReverse); + new BitcoinCoinProperties(1, sha256D, qubit, sha256DReverse, "Qubit"); private static readonly BitcoinCoinProperties groestlMyriadCoin = - new BitcoinCoinProperties(Math.Pow(2, 8), sha256S, groestlMyriad, sha256DReverse); + new BitcoinCoinProperties(Math.Pow(2, 8), sha256S, groestlMyriad, sha256DReverse, "Groestl-Myriad"); private static readonly BitcoinCoinProperties equihashCoin = - new BitcoinCoinProperties(1, new DummyHasher(), sha256D, sha256DReverse); + new BitcoinCoinProperties(1, new DummyHasher(), sha256D, sha256DReverse, "Equihash"); + + private static readonly BitcoinCoinProperties neoScryptCoin = + new BitcoinCoinProperties(Math.Pow(2, 16), sha256D, neoScryptProfile1, sha256DReverse, "Neoscrypt"); + + private static readonly BitcoinCoinProperties x17Coin = + new BitcoinCoinProperties(1, sha256D, x17, new DigestReverser(x17), "X17"); private static readonly Dictionary coinProperties = new Dictionary { @@ -102,12 +112,19 @@ public class BitcoinProperties { CoinType.ZEC, equihashCoin }, { CoinType.BTG, equihashCoin }, { CoinType.ZCL, equihashCoin }, + { CoinType.ZEN, equihashCoin }, + + // Neoscrypt + { CoinType.GBX, neoScryptCoin }, + { CoinType.CRC, neoScryptCoin }, }; public static BitcoinCoinProperties GetCoinProperties(CoinType coin, string algorithm = null) { if (coin == CoinType.DGB) return GetDigiByteProperties(algorithm); + else if (coin == CoinType.XVG) + return GetVergeProperties(algorithm); coinProperties.TryGetValue(coin, out var props); return props; @@ -117,7 +134,7 @@ private static BitcoinCoinProperties GetDigiByteProperties(string algorithm) { Contract.Requires(!string.IsNullOrEmpty(algorithm), $"{nameof(algorithm)} must not be empty"); - switch(algorithm) + switch(algorithm.ToLower()) { case "sha256d": return sha256Coin; @@ -136,5 +153,36 @@ private static BitcoinCoinProperties GetDigiByteProperties(string algorithm) return scryptCoin; } } + + private static BitcoinCoinProperties GetVergeProperties(string algorithm) + { + Contract.Requires(!string.IsNullOrEmpty(algorithm), $"{nameof(algorithm)} must not be empty"); + + switch (algorithm.ToLower()) + { + case "lyra2rev2": + return lyra2Rev2CoinVariantA; + + case "groestl-myriad": + return groestlMyriadCoin; + + case "x17": + return x17Coin; + + case "blake2s": + throw new NotSupportedException($"algorithm {algorithm} not yet supported"); + + default: // scrypt + return scryptCoin; + } + } + + public static string GetAlgorithm(CoinType coin) + { + if (coinProperties.TryGetValue(coin, out var props)) + return props.Algorithm; + + return string.Empty; + } } } diff --git a/src/MiningCore/Blockchain/Bitcoin/Configuration/BitcoinDaemonEndpointConfigExtra.cs b/src/MiningCore/Blockchain/Bitcoin/Configuration/BitcoinDaemonEndpointConfigExtra.cs new file mode 100644 index 000000000..394fee48f --- /dev/null +++ b/src/MiningCore/Blockchain/Bitcoin/Configuration/BitcoinDaemonEndpointConfigExtra.cs @@ -0,0 +1,33 @@ +/* +Copyright 2017 Coin Foundry (coinfoundry.org) +Authors: Oliver Weichhold (oliver@weichhold.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial +portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT +LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +namespace MiningCore.Blockchain.Bitcoin.Configuration +{ + public class BitcoinDaemonEndpointConfigExtra + { + public int? MinimumConfirmations { get; set; } + + /// + /// Address of ZeroMQ block notify socket + /// Should match the value of -zmqpubhashblock daemon start parameter + /// + public string ZmqBlockNotifySocket { get; set; } + } +} diff --git a/src/MiningCore/Blockchain/Bitcoin/Configuration/BitcoinPoolConfigExtra.cs b/src/MiningCore/Blockchain/Bitcoin/Configuration/BitcoinPoolConfigExtra.cs index 7f5acf8b6..56607d93c 100644 --- a/src/MiningCore/Blockchain/Bitcoin/Configuration/BitcoinPoolConfigExtra.cs +++ b/src/MiningCore/Blockchain/Bitcoin/Configuration/BitcoinPoolConfigExtra.cs @@ -22,12 +22,6 @@ namespace MiningCore.Blockchain.Bitcoin.Configuration { public class BitcoinPoolConfigExtra { - public int? MinimumConfirmations { get; set; } - - /// - /// Address of ZeroMQ block notify socket - /// Should match the value of -zmqpubhashblock daemon start parameter - /// - public string ZmqBlockNotifySocket { get; set; } + public int? MaxActiveJobs { get; set; } } } diff --git a/src/MiningCore/Blockchain/CoinMetaData.cs b/src/MiningCore/Blockchain/CoinMetaData.cs index 264d83eaf..85892af61 100644 --- a/src/MiningCore/Blockchain/CoinMetaData.cs +++ b/src/MiningCore/Blockchain/CoinMetaData.cs @@ -1,4 +1,6 @@ +using System; using System.Collections.Generic; +using MiningCore.Blockchain.Bitcoin; using MiningCore.Blockchain.Ethereum; using MiningCore.Configuration; @@ -22,13 +24,14 @@ public static class CoinMetaData { CoinType.XMR, new Dictionary { { string.Empty, "https://chainradar.com/xmr/block/{0}" }}}, { CoinType.ETN, new Dictionary { { string.Empty, "https://blockexplorer.electroneum.com/block/{0}" } }}, - { CoinType.LTC, new Dictionary { { string.Empty, "http://explorer.litecoin.net/block/{0}" }}}, + { CoinType.LTC, new Dictionary { { string.Empty, "https://chainz.cryptoid.info/ltc/block.dws?{0}.htm" } }}, { CoinType.BCH, new Dictionary { { string.Empty, "https://www.blocktrail.com/BCC/block/{0}" }}}, { CoinType.DASH, new Dictionary { { string.Empty, "https://chainz.cryptoid.info/dash/block.dws?{0}.htm" }}}, { CoinType.BTC, new Dictionary { { string.Empty, "https://blockchain.info/block/{0}" }}}, { CoinType.DOGE, new Dictionary { { string.Empty, "https://dogechain.info/block/{0}" }}}, { CoinType.ZEC, new Dictionary { { string.Empty, "https://explorer.zcha.in/blocks/{0}" }}}, { CoinType.ZCL, new Dictionary { { string.Empty, "http://explorer.zclmine.pro/blocks/{0}" }}}, + { CoinType.ZEN, new Dictionary { { string.Empty, "http://explorer.zensystem.io/blocks/{0}" } }}, { CoinType.DGB, new Dictionary { { string.Empty, "https://digiexplorer.info/block/{0}" }}}, { CoinType.NMC, new Dictionary { { string.Empty, "https://explorer.namecoin.info/b/{0}" }}}, { CoinType.GRS, new Dictionary { { string.Empty, "https://groestlsight.groestlcoin.org/block/{0}" }}}, @@ -40,22 +43,24 @@ public static class CoinMetaData { CoinType.EXP, new Dictionary { { string.Empty, "http://www.gander.tech/blocks/{0}" }}}, { CoinType.AEON, new Dictionary { { string.Empty, "https://chainradar.com/aeon/block/{0}" }}}, { CoinType.STAK, new Dictionary { { string.Empty, "https://straks.info/block/{0}" }}}, - { CoinType.MOON, new Dictionary { { string.Empty, " https://chainz.cryptoid.info/moon/block.dws?{0}.htm" }}}, + { CoinType.MOON, new Dictionary { { string.Empty, "https://chainz.cryptoid.info/moon/block.dws?{0}.htm" }}}, + { CoinType.XVG, new Dictionary { { string.Empty, "https://verge-blockchain.info/block/{0}" } }}, }; - public static readonly Dictionary PaymentInfoLinks = new Dictionary + public static readonly Dictionary TxInfoLinks = new Dictionary { { CoinType.XMR, "https://chainradar.com/xmr/transaction/{0}" }, { CoinType.ETN, "https://blockexplorer.electroneum.com/tx/{0}" }, { CoinType.ETH, "https://etherscan.io/tx/{0}" }, { CoinType.ETC, "https://gastracker.io/tx/{0}" }, - { CoinType.LTC, "http://explorer.litecoin.net/tx/{0}" }, + { CoinType.LTC, "https://chainz.cryptoid.info/ltc/tx.dws?{0}.htm" }, { CoinType.BCH, "https://www.blocktrail.com/BCC/tx/{0}" }, { CoinType.DASH, "https://chainz.cryptoid.info/dash/tx.dws?{0}.htm" }, { CoinType.BTC, "https://blockchain.info/tx/{0}" }, { CoinType.DOGE, "https://dogechain.info/tx/{0}" }, { CoinType.ZEC, "https://explorer.zcha.in/transactions/{0}" }, { CoinType.ZCL, "http://explorer.zclmine.pro/transactions/{0}" }, + { CoinType.ZEN, "http://explorer.zensystem.io/transactions/{0}" }, { CoinType.DGB, "https://digiexplorer.info/tx/{0}" }, { CoinType.NMC, "https://explorer.namecoin.info/tx/{0}" }, { CoinType.GRS, "https://groestlsight.groestlcoin.org/tx/{0}" }, @@ -68,19 +73,23 @@ public static class CoinMetaData { CoinType.EXP, "http://www.gander.tech/tx/{0}" }, { CoinType.AEON, "https://chainradar.com/aeon/transaction/{0}" }, { CoinType.MOON, "https://chainz.cryptoid.info/moon/tx.dws?{0}.htm" }, + { CoinType.XVG, "https://verge-blockchain.info/tx/{0}" }, + { CoinType.GBX, "http://gobyte.ezmine.io/tx/{0}" }, + { CoinType.CRC, "http://explorer.cryptopros.us/tx/{0}" }, }; - + public static readonly Dictionary AddressInfoLinks = new Dictionary { { CoinType.ETH, "https://etherscan.io/address/{0}" }, { CoinType.ETC, "https://gastracker.io/addr/{0}" }, - { CoinType.LTC, "http://explorer.litecoin.net/address/{0}" }, + { CoinType.LTC, "https://chainz.cryptoid.info/ltc/address.dws?{0}.htm" }, { CoinType.BCH, "https://www.blocktrail.com/BCC/address/{0}" }, { CoinType.DASH, "https://chainz.cryptoid.info/dash/address.dws?{0}.htm" }, { CoinType.BTC, "https://blockchain.info/address/{0}" }, { CoinType.DOGE, "https://dogechain.info/address/{0}" }, { CoinType.ZEC, "https://explorer.zcha.in/accounts/{0}" }, { CoinType.ZCL, "http://explorer.zclmine.pro/accounts/{0}" }, + { CoinType.ZEN, "http://explorer.zensystem.io/accounts/{0}" }, { CoinType.DGB, "https://digiexplorer.info/address/{0}" }, { CoinType.NMC, "https://explorer.namecoin.info/a/{0}" }, { CoinType.GRS, "https://groestlsight.groestlcoin.org/address/{0}" }, @@ -92,6 +101,44 @@ public static class CoinMetaData { CoinType.ELLA, "https://explorer.ellaism.org/addr/{0}" }, { CoinType.EXP, "http://www.gander.tech/address/{0}" }, { CoinType.MOON, "https://chainz.cryptoid.info/moon/address.dws?{0}.htm" }, + { CoinType.XVG, "https://verge-blockchain.info/address/{0}" }, + { CoinType.GBX, "http://gobyte.ezmine.io/address/{0}" }, + { CoinType.CRC, "http://explorer.cryptopros.us/address/{0}" }, + }; + + private const string Ethash = "Dagger-Hashimoto"; + private const string Cryptonight = "Cryptonight"; + private const string CryptonightLight = "Cryptonight-Light"; + + public static readonly Dictionary> CoinAlgorithm = new Dictionary> + { + { CoinType.ETH, (coin)=> Ethash }, + { CoinType.ETC, (coin)=> Ethash }, + { CoinType.LTC, BitcoinProperties.GetAlgorithm }, + { CoinType.BCH, BitcoinProperties.GetAlgorithm }, + { CoinType.DASH, BitcoinProperties.GetAlgorithm }, + { CoinType.BTC, BitcoinProperties.GetAlgorithm }, + { CoinType.DOGE, BitcoinProperties.GetAlgorithm }, + { CoinType.ZEC, BitcoinProperties.GetAlgorithm }, + { CoinType.ZCL, BitcoinProperties.GetAlgorithm }, + { CoinType.ZEN, BitcoinProperties.GetAlgorithm }, + { CoinType.DGB, BitcoinProperties.GetAlgorithm }, + { CoinType.NMC, BitcoinProperties.GetAlgorithm }, + { CoinType.GRS, BitcoinProperties.GetAlgorithm }, + { CoinType.MONA, BitcoinProperties.GetAlgorithm }, + { CoinType.STAK, BitcoinProperties.GetAlgorithm }, + { CoinType.GLT, BitcoinProperties.GetAlgorithm }, + { CoinType.VTC, BitcoinProperties.GetAlgorithm }, + { CoinType.BTG, BitcoinProperties.GetAlgorithm }, + { CoinType.ELLA, (coin)=> Ethash }, + { CoinType.EXP, (coin)=> Ethash }, + { CoinType.MOON, BitcoinProperties.GetAlgorithm }, + { CoinType.XVG, BitcoinProperties.GetAlgorithm }, + { CoinType.XMR, (coin)=> Cryptonight }, + { CoinType.ETN, (coin)=> Cryptonight }, + { CoinType.AEON, (coin)=> CryptonightLight }, + { CoinType.GBX, BitcoinProperties.GetAlgorithm }, + { CoinType.CRC, BitcoinProperties.GetAlgorithm }, }; } } diff --git a/src/MiningCore/Blockchain/JobManagerBase.cs b/src/MiningCore/Blockchain/JobManagerBase.cs index 7535c2ec0..fa683e422 100644 --- a/src/MiningCore/Blockchain/JobManagerBase.cs +++ b/src/MiningCore/Blockchain/JobManagerBase.cs @@ -1,20 +1,20 @@ -/* +/* Copyright 2017 Coin Foundry (coinfoundry.org) Authors: Oliver Weichhold (oliver@weichhold.com) -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and -associated documentation files (the "Software"), to deal in the Software without restriction, -including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, -and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all copies or substantial +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT -LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT +LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ @@ -24,6 +24,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using System.Threading.Tasks; using Autofac; using MiningCore.Configuration; +using MiningCore.Extensions; using MiningCore.Util; using NLog; using Contract = MiningCore.Contracts.Contract; @@ -43,7 +44,7 @@ protected JobManagerBase(IComponentContext ctx) protected ClusterConfig clusterConfig; protected TJob currentJob; - private long jobId; + private int jobId; protected object jobLock = new object(); protected ILogger logger; protected PoolConfig poolConfig; @@ -73,12 +74,13 @@ protected virtual async Task StartDaemonAsync() protected string NextJobId(string format = null) { - var value = Interlocked.Increment(ref jobId); + Interlocked.Increment(ref jobId); + var value = Interlocked.CompareExchange(ref jobId, 0, Int32.MinValue); if (format != null) return value.ToString(format); - return value.ToString(CultureInfo.InvariantCulture); + return value.ToStringHex8(); } protected abstract Task AreDaemonsHealthy(); diff --git a/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs b/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs index a7fa0d590..6ad8b2efc 100644 --- a/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs +++ b/src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs @@ -163,62 +163,21 @@ private async Task PayoutBatch(Balance[] balances) var transferResponse = await walletDaemon.ExecuteCmdSingleAsync(MWC.Transfer, request); // gracefully handle error -4 (transaction would be too large. try /transfer_split) - if (transferResponse.Error?.Code == -4) + if (transferResponse.Error?.Code == -4 && walletSupportsTransferSplit) { - if (walletSupportsTransferSplit) - { - logger.Info(() => $"[{LogCategory}] Retrying transfer using {MWC.TransferSplit}"); - - var transferSplitResponse = await walletDaemon.ExecuteCmdSingleAsync(MWC.TransferSplit, request); - - // gracefully handle error -4 (transaction would be too large. try /transfer_split) - if (transferResponse.Error?.Code != -4) - { - HandleTransferResponse(transferSplitResponse, balances); - return; - } - } - - // retry paged - logger.Info(() => $"[{LogCategory}] Retrying paged"); + logger.Info(() => $"[{LogCategory}] Retrying transfer using {MWC.TransferSplit}"); - var validBalances = balances.Where(x => x.Amount > 0).ToArray(); - var pageSize = 10; - var pageCount = (int)Math.Ceiling((double)validBalances.Length / pageSize); + var transferSplitResponse = await walletDaemon.ExecuteCmdSingleAsync(MWC.TransferSplit, request); - for (var i = 0; i < pageCount; i++) + // gracefully handle error -4 (transaction would be too large. try /transfer_split) + if (transferResponse.Error?.Code != -4) { - var page = validBalances - .Skip(i * pageSize) - .Take(pageSize) - .ToArray(); - - // update request - request.Destinations = page - .Where(x => x.Amount > 0) - .Select(x => - { - ExtractAddressAndPaymentId(x.Address, out var address, out var paymentId); - - return new TransferDestination - { - Address = address, - Amount = (ulong) Math.Floor(x.Amount * MoneroConstants.SmallestUnit[poolConfig.Coin.Type]) - }; - }).ToArray(); - - logger.Info(() => $"[{LogCategory}] Page {i + 1}: Paying out {FormatAmount(page.Sum(x => x.Amount))} to {page.Length} addresses"); - - transferResponse = await walletDaemon.ExecuteCmdSingleAsync(MWC.Transfer, request); - HandleTransferResponse(transferResponse, page); - - if (transferResponse.Error != null) - break; + HandleTransferResponse(transferSplitResponse, balances); + return; } } - else - HandleTransferResponse(transferResponse, balances); + HandleTransferResponse(transferResponse, balances); } private void ExtractAddressAndPaymentId(string input, out string address, out string paymentId) diff --git a/src/MiningCore/Blockchain/ZCash/ZCashConstants.cs b/src/MiningCore/Blockchain/ZCash/ZCashConstants.cs index fd94ae50f..e8ac8fe22 100644 --- a/src/MiningCore/Blockchain/ZCash/ZCashConstants.cs +++ b/src/MiningCore/Blockchain/ZCash/ZCashConstants.cs @@ -29,14 +29,14 @@ namespace MiningCore.Blockchain.ZCash public class ZCashCoinbaseTxConfig { public bool PayFoundersReward { get; set; } - public int PercentFoundersReward { get; set; } + public decimal PercentFoundersReward { get; set; } public string[] FoundersRewardAddresses { get; set; } public ulong FoundersRewardSubsidySlowStartInterval { get; set; } public ulong FoundersRewardSubsidyHalvingInterval { get; set; } public ulong FoundersRewardSubsidySlowStartShift => FoundersRewardSubsidySlowStartInterval / 2; public ulong LastFoundersRewardBlockHeight => FoundersRewardSubsidyHalvingInterval + FoundersRewardSubsidySlowStartShift - 1; - public int PercentTreasuryReward { get; set; } + public decimal PercentTreasuryReward { get; set; } public ulong TreasuryRewardStartBlockHeight { get; set; } public string[] TreasuryRewardAddresses { get; set; } public double TreasuryRewardAddressChangeInterval { get; set; } @@ -119,7 +119,7 @@ public class ZCashConstants } }, }; - + private static readonly Dictionary ZCLCoinbaseTxConfig = new Dictionary { { @@ -166,11 +166,116 @@ public class ZCashConstants }, }; + private static readonly Dictionary ZencashCoinbaseTxConfig = new Dictionary + { + { + BitcoinNetworkType.Main, new ZCashCoinbaseTxConfig + { + PayFoundersReward = true, + PercentFoundersReward = 8.5m, + FoundersRewardSubsidyHalvingInterval = 840000, + FoundersRewardSubsidySlowStartInterval = 2, + + FoundersRewardAddresses = new[] + { + "zssEdGnZCQ9G86LZFtbynMn1hYTVhn6eYCL", "zsrCsXXmUf8k59NLasEKfxA7us3iNvaPATz", "zsnLPsWMXW2s4w9EmFSwtSLRxL2LhPcfdby", + "zshdovbcPfUAfkPeEE2qLbKnoue9RsbVokU", "zsqmq97JAKCCBFRGvxgv6FiJgQLCZBDp62S", "zskyFVFA7VRYX8EGdXmYN75WaBB25FmiL3g", + "zsmncLmwEUdVmAGPUrUnNKmPGXyej7mbmdM", "zsfa9VVJCEdjfPbku4XrFcRR8kTDm2T64rz", "zsjdMnfWuFi46VeN2HSXVQWEGsnGHgVxayY", + "zseb8wRQ8rZ722oLX5B8rx7qwZiBRb9mdig", "zsjxkovhqiMVggoW7jvSRi3NTSD3a6b6qfd", "zsokCCSU3wvZrS2G6mEDpJ5cH49E7sDyNr1", + "zt12EsFgkABHLMRXA7JNnpMqLrxsgCLnVEV", "zt39mvuG9gDTHX8A8Qk45rbk3dSdQoJ8ZAv", "zssTQZs5YxDGijKC86dvcDxzWogWcK7n5AK", + "zsywuMoQK7Bved2nrXs56AEtWBhpb88rMzS", "zsxVS2w7h1fHFX2nQtGm4372pd4DSHzq9ee", "zsupGi7ro3uC8CEVwm9r7vrdVUZaXQnHF6T", + "zshVZvW47dA5AB3Sqk1h7ytwWJeUJUJxxaE", "zsubBCjvDx252MKFsL4Dcf5rJU9Z9Upqr1N", "zsweaST3NcU4hfgkVULfCsfEq41pjgMDgcW", + "zswz6Rxb1S33fUpftETZwtGiVSeYxNKq2xc", "zswnpHtiBbrvYDzbhPQshkgvLSfYhDMRJ4S", "zsjSYAWaEYj35Ht7aXrRJUGY6Dc8qCmgYqu", + "zsvMv8fGroWR8epbSiGDCJHmfe6ec2uFQrt", "zsujxCT56BExQDAwKwktBjtnopYnw8BiKbg", "zsxeXc2FTAzmUmeZmqVsKVdwTMSvzyns4rT", + "zsuLqgABNudD8bVPbVGeUjGqapuoXp68i7F", "zsoc39J1dCFK1U8kckZznvQkv8As7sajYLz", "zt21NFdu1KRPJ7VRKtrWugM2Jqe5ePNmU4T", + "zsp15qbVcbx9ifcjKe6XZEJTvzsFUZ2BHLT", "zso2KvqH6yxLQEYggHdmfL3Tcd5V6E9tqhp", "zsnFG2W5ZHRYh3QucNze4mp31tBkemtfxdj", + "zsex2CGJtxHyHbpLXm7kESBmp3vWRqUkJMy", "zsvtFv96nrgrXKUbtNe2BpCt8aQEp5oJ7F8", "zsk5KitThmhK9KBa1KDybPgEmGSFTHzhMVA", + "zsuy4n48c4NsJyaCZEzwdAKULM1FqbB6Y4z", "zsgtQVMpX2zNMLvHHG2NDwfqKoaebvVectJ", "zszQqXRSPGdqsWw4iaMTNN6aJz4JjEzSdCF", + "zst6dBLrTtaMQBX7BLMNjKLTGcP11PBmgTV", "zshD9r6Eb6dZGdzYW2HCb9CzkMokCT1NGJR", "zswUaj1TboEGmvSfF7fdoxWyH3RMx7MBHHo", + "zsv8s4Poi5GxCsbBrRJ97Vsvazp84nrz5AN", "zsmmxrKU6dqWFwUKow1iyovg3gxrgXpEivr", "zskh1221aRC9WEfb5a59WxffeW34McmZZsw", + "zssAhuj57NnVm4yNFT6o8muRctABkUaBu3L", "zsi5Yr4Z8HwBvdBqQE8gk7ahExDu95J4oqZ", "zsy6ryEaxfk8emJ8bGVB7tmwRwBL8cfSqBW", + }, + + PercentTreasuryReward = 12, + TreasuryRewardAddressChangeInterval = 50000, + TreasuryRewardStartBlockHeight = 139200, + + TreasuryRewardAddresses = new[] + { + "zsyF68hcYYNLPj5i4PfQJ1kUY6nsFnZkc82", "zsfULrmbX7xbhqhAFRffVqCw9RyGv2hqNNG", + "zsoemTfqjicem2QVU8cgBHquKb1o9JR5p4Z", "zt339oiGL6tTgc9Q71f5g1sFTZf6QiXrRUr" + } + } + }, + { + BitcoinNetworkType.Test, new ZCashCoinbaseTxConfig + { + PayFoundersReward = true, + PercentFoundersReward = 8.5m, + FoundersRewardSubsidyHalvingInterval = 840000, + FoundersRewardSubsidySlowStartInterval = 2, + + FoundersRewardAddresses = new[] + { + "zrH8KT8KUcpKKNBu3fjH4hA84jZBCawErqn", "zrGsMC4ou1r5Vxy7Dnxg4PfKpansx83BM8g", "zr6sB2Az36D8CqzeZNavB11WbovsGtJSAZG", + "zrBAG3pXCTDq14nivNK9mW8SfwMNcdmMQpb", "zrRLwpYRYky4wsvwLVrDp8fs89EBTRhNMB1", "zrLozMfptTmE3zLP5SrTLyB8TXqH84Agjrr", + "zrMckkaLtVTEUvxj4ouU7BPDGa8xmdTZSVE", "zrFc897wJXmF7BcEdbvi2mS1bLrcuWYK6hm", "zrHEnni486u9SNcLWacroSgcdyMA33tfM92", + "zrJ3ymPV3R8Xk4N3BdNb898xvZvARm5K7mq", "zrDj3P6trx73291krxU51U9QnfkbGxkyJ6E", "zrJs3vMGFJi9pQCireeSLckJonamBnwTSrY", + "zrKFdXQoAkkycy52EFoWARyzZWx6Kat2Som", "zrEXbSe79FXA9KRMnJTZUWZdZhNnjcnAdrq", "zr7iAwfNgJsMpSCmijK3TuVDuNvSmLr1rUz", + "zrDEK7K6cftqSjeyVUH1WqJtBUkXN7GidxH", "zrRennuq75hyBVU4JymtZk8UcQ1vRPKpmpj", "zr9HRTL79pKmn5R8fvkC9kucZ4u1bQruLTD", + "zrML8KXpJsa1NVnbJuawX86ZvAn543tWdTT", "zrLBAkQoxpEtnztSUEcdxnEvuwtgxyAMGX7", "zr6kPnVzFBYmcBDsWoTrPHRuBxLq21o4zvT", + "zrMY3vdvqs9KSvx9TawvcyuVurt1Jj6GPVo", "zr9WB1qBpM4nwi1mudUFfjtMNmqzaBQDsXn", "zrAHbtHDPAqmzWJMQqSYzGyFnDWN3oELZRs", + "zrH1f5K3z7EQ6RWWZ7StCDWHTZwFChBVA2W", "zrNTacAid9LS4kAqzM4sw1YcF7gLFrzVM7U", "zrFyZpMVKMeDqbn6A2uUiL9mZmgxuR1pUBg", + "zrD1cqGFGzBcPogFHJvnN4XegvvmbTjA43t", "zr5A1D7czWkB4pAWfGC5Pux5Ek7anYybdPK", "zr8yTAxCy6jAdsc6qPvmVEQHbYo25AJKhy9", + "zrFW2YjQw4cABim5kEDwapbSqTz3wW7cWkk", "zr9nJvNbsrvUTZD41fhqAQeUcgMfqZmAweN", "zrCx4dXZd5b2tD483Ds4diHpo1QxBMJ76Jr", + "zr6eVeRwU6Puob3K1RfWtva1R458oj8pzkL", "zr7B92iHtQcobZjGCXo3DAqMQjsn7ka31wE", "zr8bcemLWAjYuphXSVqtqZWEnJipCB9F5oC", + "zrFzsuPXb7HsFd3srBqtVqnC9GQ94DQubV2", "zr4yiBobiHjHnCYi75NmYtyoqCV4A3kpHDL", "zrGVdR4K4F8MfmWxhUiTypK7PTsvHi8uTAh", + "zr7WiCDqCMvUdH1xmMu8YrBMFb2x2E6BX3z", "zrEFrGWLX4hPHuHRUD3TPbMAJyeSpMSctUc", "zr5c3f8PTnW8qBFX1GvK2LhyLBBCb1WDdGG", + "zrGkAZkZLqC9QKJR3XomgxNizCpNuAupTeg", "zrM7muDowiun9tCHhu5K9vcDGfUptuYorfZ", "zrCsWfwKotWnQmFviqAHAPAJ2jXqZYW966P", + "zrLLB3JB3jozUoMGFEGhjqyVXTpngVQ8c4T", "zrAEa8YjJ2f3m2VsM1Xa9EwibZxEnRoSLUx", "zrAdJgp7Cx35xTvB7ABWP8YLTNDArMjP1s3" + }, + + PercentTreasuryReward = 12, + TreasuryRewardAddressChangeInterval = 10000, + TreasuryRewardStartBlockHeight = 85500, + + TreasuryRewardAddresses = new[] + { + "zrRBQ5heytPMN5nY3ssPf3cG4jocXeD8fm1" + } + } + }, + { + BitcoinNetworkType.RegTest, new ZCashCoinbaseTxConfig + { + PayFoundersReward = true, + PercentFoundersReward = 8.5m, + FoundersRewardSubsidyHalvingInterval = 2000, + FoundersRewardSubsidySlowStartInterval = 0, + + FoundersRewardAddresses = new[] + { + "zrKmSdqZKZjnARd5e8FfRg4v1m74X7twxGa", + }, + + PercentTreasuryReward = 12, + TreasuryRewardAddressChangeInterval = 100, + TreasuryRewardStartBlockHeight = 139200, + + TreasuryRewardAddresses = new[] + { + "zrKmSdqZKZjnARd5e8FfRg4v1m74X7twxGa" + } + } + }, + }; + public static Dictionary> CoinbaseTxConfig = new Dictionary> { { CoinType.ZEC, ZCashCoinbaseTxConfig }, - { CoinType.ZCL, ZCLCoinbaseTxConfig } + { CoinType.ZCL, ZCLCoinbaseTxConfig }, + { CoinType.ZEN, ZencashCoinbaseTxConfig } }; } diff --git a/src/MiningCore/Blockchain/ZCash/ZCashPayoutHandler.cs b/src/MiningCore/Blockchain/ZCash/ZCashPayoutHandler.cs index b707341f7..40543b7a8 100644 --- a/src/MiningCore/Blockchain/ZCash/ZCashPayoutHandler.cs +++ b/src/MiningCore/Blockchain/ZCash/ZCashPayoutHandler.cs @@ -39,7 +39,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. namespace MiningCore.Blockchain.ZCash { - [CoinMetadata(CoinType.ZEC, CoinType.ZCL)] + [CoinMetadata(CoinType.ZEC, CoinType.ZCL, CoinType.ZEN)] public class ZCashPayoutHandler : BitcoinPayoutHandler { public ZCashPayoutHandler( diff --git a/src/MiningCore/Blockchain/ZCash/ZCashPool.cs b/src/MiningCore/Blockchain/ZCash/ZCashPool.cs index 1cf2ca629..571078eb9 100644 --- a/src/MiningCore/Blockchain/ZCash/ZCashPool.cs +++ b/src/MiningCore/Blockchain/ZCash/ZCashPool.cs @@ -32,7 +32,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. namespace MiningCore.Blockchain.ZCash { - [CoinMetadata(CoinType.ZEC, CoinType.ZCL)] + [CoinMetadata(CoinType.ZEC, CoinType.ZCL, CoinType.ZEN)] public class ZCashPool : ZCashPoolBase { public ZCashPool(IComponentContext ctx, diff --git a/src/MiningCore/Configuration/ClusterConfig.cs b/src/MiningCore/Configuration/ClusterConfig.cs index 359245535..69c15ed49 100644 --- a/src/MiningCore/Configuration/ClusterConfig.cs +++ b/src/MiningCore/Configuration/ClusterConfig.cs @@ -38,7 +38,8 @@ public enum CoinType VIA, // Viacoin PPC, // Peercoin ZEC, // ZCash - ZCL, // Zcassic + ZCL, // ZClassic + ZEN, // Zencash ETH, // Ethereum ETC, // Ethereum Classic EXP, // Expanse @@ -51,7 +52,10 @@ public enum CoinType AEON, // AEON STAK, // Straks ETN, // Electroneum - MOON //MoonCoin + MOON, // MoonCoin + XVG, // Verge + GBX, // GoByte + CRC, // CrowdCoin } public class CoinConfig diff --git a/src/MiningCore/Crypto/Hashing/Algorithms/NeoScrypt.cs b/src/MiningCore/Crypto/Hashing/Algorithms/NeoScrypt.cs new file mode 100644 index 000000000..39e207f0e --- /dev/null +++ b/src/MiningCore/Crypto/Hashing/Algorithms/NeoScrypt.cs @@ -0,0 +1,54 @@ +/* +Copyright 2017 Coin Foundry (coinfoundry.org) +Authors: Oliver Weichhold (oliver@weichhold.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial +portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT +LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +using System; +using MiningCore.Contracts; +using MiningCore.Native; + +namespace MiningCore.Crypto.Hashing.Algorithms +{ + public unsafe class NeoScrypt : IHashAlgorithm + { + public NeoScrypt(uint profile) + { + this.profile = profile; + } + + private readonly uint profile; + + public byte[] Digest(byte[] data, params object[] extra) + { + Contract.RequiresNonNull(data, nameof(data)); + Contract.Requires(data.Length == 80, $"{nameof(data)} length must be exactly 80 bytes"); + + var result = new byte[32]; + + fixed (byte* input = data) + { + fixed (byte* output = result) + { + LibMultihash.neoscrypt(input, output, (uint)data.Length, profile); + } + } + + return result; + } + } +} diff --git a/src/MiningCore/Crypto/Hashing/Algorithms/Scrypt.cs b/src/MiningCore/Crypto/Hashing/Algorithms/Scrypt.cs index 096fed4be..5af65b48f 100644 --- a/src/MiningCore/Crypto/Hashing/Algorithms/Scrypt.cs +++ b/src/MiningCore/Crypto/Hashing/Algorithms/Scrypt.cs @@ -1,20 +1,20 @@ -/* +/* Copyright 2017 Coin Foundry (coinfoundry.org) Authors: Oliver Weichhold (oliver@weichhold.com) -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and -associated documentation files (the "Software"), to deal in the Software without restriction, -including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, -and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all copies or substantial +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT -LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT +LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ @@ -40,11 +40,11 @@ public byte[] Digest(byte[] data, params object[] extra) var result = new byte[32]; - fixed(byte* input = data) + fixed (byte* input = data) { - fixed(byte* output = result) + fixed (byte* output = result) { - LibMultihash.scrypt(input, output, n, r, (uint) data.Length); + LibMultihash.scrypt(input, output, n, r, (uint)data.Length); } } diff --git a/src/MiningCore/Crypto/Hashing/Algorithms/X17.cs b/src/MiningCore/Crypto/Hashing/Algorithms/X17.cs new file mode 100644 index 000000000..114179d8f --- /dev/null +++ b/src/MiningCore/Crypto/Hashing/Algorithms/X17.cs @@ -0,0 +1,45 @@ +/* +Copyright 2017 Coin Foundry (coinfoundry.org) +Authors: Oliver Weichhold (oliver@weichhold.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial +portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT +LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +using MiningCore.Contracts; +using MiningCore.Native; + +namespace MiningCore.Crypto.Hashing.Algorithms +{ + public unsafe class X17 : IHashAlgorithm + { + public byte[] Digest(byte[] data, params object[] extra) + { + Contract.RequiresNonNull(data, nameof(data)); + + var result = new byte[32]; + + fixed(byte* input = data) + { + fixed(byte* output = result) + { + LibMultihash.x17(input, output, (uint) data.Length); + } + } + + return result; + } + } +} diff --git a/src/MiningCore/DaemonInterface/DaemonClient.cs b/src/MiningCore/DaemonInterface/DaemonClient.cs index b1d1ec00d..5f3a7b902 100644 --- a/src/MiningCore/DaemonInterface/DaemonClient.cs +++ b/src/MiningCore/DaemonInterface/DaemonClient.cs @@ -28,17 +28,21 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using System.Net.Http; using System.Net.Http.Headers; using System.Net.WebSockets; +using System.Reactive; using System.Reactive.Disposables; using System.Reactive.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; +using MiningCore.Blockchain.Bitcoin; using MiningCore.Buffers; using MiningCore.Configuration; using MiningCore.Extensions; using MiningCore.JsonRpc; using MiningCore.Stratum; using MiningCore.Util; +using NetMQ; +using NetMQ.Sockets; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using NLog; @@ -229,6 +233,18 @@ public IObservable> WebsocketSubscribe(Dictionary[]> ZmqSubscribe(Dictionary portMap, string topic, int numMsgSegments = 2) + { + Contract.Requires(!string.IsNullOrEmpty(topic), $"{nameof(topic)} must not be empty"); + + logger.LogInvoke(new[] { topic }); + + return Observable.Merge(portMap.Keys + .Select(endPoint => ZmqSubscribeEndpoint(endPoint, portMap[endPoint], topic, numMsgSegments))) + .Publish() + .RefCount(); + } + #endregion // API-Surface private async Task BuildRequestTask(DaemonEndpointConfig endPoint, string method, object payload, @@ -471,5 +487,62 @@ private IObservable> WebsocketSubscribeEndpoint(DaemonE }); })); } + + private IObservable[]> ZmqSubscribeEndpoint(DaemonEndpointConfig endPoint, string url, string topic, int numMsgSegments = 2) + { + return Observable.Defer(() => Observable.Create[]>(obs => + { + var tcs = new CancellationTokenSource(); + + Task.Factory.StartNew(() => + { + using (tcs) + { + while (!tcs.IsCancellationRequested) + { + try + { + using (var subSocket = new SubscriberSocket()) + { + //subSocket.Options.ReceiveHighWatermark = 1000; + subSocket.Connect(url); + subSocket.Subscribe(topic); + + logger.Debug($"Subscribed to {url}/{BitcoinConstants.ZmqPublisherTopicBlockHash}"); + + while (!tcs.IsCancellationRequested) + { + var msg = subSocket.ReceiveMultipartMessage(numMsgSegments); + + // Export all frame data as array of PooledArraySegments + var result = msg.Select(x => + { + var buf = ArrayPool.Shared.Rent(x.BufferSize); + Array.Copy(x.ToByteArray(), buf, x.BufferSize); + return new PooledArraySegment(buf, 0, x.BufferSize); + }).ToArray(); + + obs.OnNext(result); + } + } + } + + catch (Exception ex) + { + logger.Error(ex); + } + + // do not consume all CPU cycles in case of a long lasting error condition + Thread.Sleep(1000); + } + } + }, tcs.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default); + + return Disposable.Create(() => + { + tcs.Cancel(); + }); + })); + } } } diff --git a/src/MiningCore/Extensions/DictionaryExtensions.cs b/src/MiningCore/Extensions/DictionaryExtensions.cs new file mode 100644 index 000000000..e938e19e5 --- /dev/null +++ b/src/MiningCore/Extensions/DictionaryExtensions.cs @@ -0,0 +1,45 @@ +/* +Copyright 2017 Coin Foundry (coinfoundry.org) +Authors: Oliver Weichhold (oliver@weichhold.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial +portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT +LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.Linq; + +namespace MiningCore.Extensions +{ + public static class DictionaryExtensions + { + public static void StripValue(this IDictionary dict, string key) + { + key = key.ToLower(CultureInfo.InvariantCulture); + + var keyActual = dict.Keys.FirstOrDefault(x => x.ToLower(CultureInfo.InvariantCulture) == key); + + if (keyActual != null) + { + var result = dict.Remove(keyActual); + Debug.Assert(result); + } + } + } +} diff --git a/src/MiningCore/Extensions/PoolingExtensions.cs b/src/MiningCore/Extensions/PoolingExtensions.cs new file mode 100644 index 000000000..ad384063a --- /dev/null +++ b/src/MiningCore/Extensions/PoolingExtensions.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Text; +using MiningCore.Buffers; + +namespace MiningCore.Extensions +{ + public static class PoolingExtensions + { + public static void Dispose(this IEnumerable> col) + { + foreach(var seg in col) + seg.Dispose(); + } + } +} diff --git a/src/MiningCore/Extensions/StringExtensions.cs b/src/MiningCore/Extensions/StringExtensions.cs index 89d996c2a..d15735cbe 100644 --- a/src/MiningCore/Extensions/StringExtensions.cs +++ b/src/MiningCore/Extensions/StringExtensions.cs @@ -25,7 +25,6 @@ namespace MiningCore.Extensions { public static class StringExtensions { - /// /// Converts a str string to byte array. /// @@ -55,6 +54,11 @@ public static string ToStringHex8(this uint value) return value.ToString("x8", CultureInfo.InvariantCulture); } + public static string ToStringHex8(this int value) + { + return value.ToString("x8", CultureInfo.InvariantCulture); + } + public static string ToStringHexWithPrefix(this ulong value) { if (value == 0) diff --git a/src/MiningCore/Native/LibMultihash.cs b/src/MiningCore/Native/LibMultihash.cs index c53ca8c7f..2a44cf53c 100644 --- a/src/MiningCore/Native/LibMultihash.cs +++ b/src/MiningCore/Native/LibMultihash.cs @@ -1,20 +1,20 @@ -/* +/* Copyright 2017 Coin Foundry (coinfoundry.org) Authors: Oliver Weichhold (oliver@weichhold.com) -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and -associated documentation files (the "Software"), to deal in the Software without restriction, -including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, -and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all copies or substantial +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT -LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT +LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ @@ -37,8 +37,11 @@ public static unsafe class LibMultihash [DllImport("libmultihash", EntryPoint = "x15_export", CallingConvention = CallingConvention.Cdecl)] public static extern int x15(byte* input, byte* output, uint inputLength); + [DllImport("libmultihash", EntryPoint = "x17_export", CallingConvention = CallingConvention.Cdecl)] + public static extern int x17(byte* input, byte* output, uint inputLength); + [DllImport("libmultihash", EntryPoint = "neoscrypt_export", CallingConvention = CallingConvention.Cdecl)] - public static extern int neoscrypt(byte* input, byte* output, uint inputLength, int profile); + public static extern int neoscrypt(byte* input, byte* output, uint inputLength, uint profile); [DllImport("libmultihash", EntryPoint = "scryptn_export", CallingConvention = CallingConvention.Cdecl)] public static extern int scryptn(byte* input, byte* output, uint nFactor, uint inputLength); diff --git a/src/MiningCore/Notifications/NotificationService.cs b/src/MiningCore/Notifications/NotificationService.cs index 6bf241f8e..32f605453 100644 --- a/src/MiningCore/Notifications/NotificationService.cs +++ b/src/MiningCore/Notifications/NotificationService.cs @@ -99,7 +99,7 @@ public void NotifyPaymentSuccess(string poolId, decimal amount, int recpientsCou Category = NotificationCategory.PaymentSuccess, PoolId = poolId, Subject = "Payout Success Notification", - Msg = $"Paid out {FormatAmount(amount, poolId)} from pool {poolId} to {recpientsCount} recipients in Transaction(s) {txInfo}." + Msg = $"Paid {FormatAmount(amount, poolId)} from pool {poolId} to {recpientsCount} recipients in Transaction(s) {txInfo}." }); } diff --git a/src/MiningCore/Payments/PayoutHandlerBase.cs b/src/MiningCore/Payments/PayoutHandlerBase.cs index 2d758c985..0fc2d0062 100644 --- a/src/MiningCore/Payments/PayoutHandlerBase.cs +++ b/src/MiningCore/Payments/PayoutHandlerBase.cs @@ -157,7 +157,7 @@ protected virtual void NotifyPayoutSuccess(string poolId, Balance[] balances, st // prepare tx link var txInfo = string.Join(", ", txHashes); - if (CoinMetaData.PaymentInfoLinks.TryGetValue(poolConfig.Coin.Type, out var baseUrl)) + if (CoinMetaData.TxInfoLinks.TryGetValue(poolConfig.Coin.Type, out var baseUrl)) txInfo = string.Join(", ", txHashes.Select(txHash => $"{txHash}")); notificationService.NotifyPaymentSuccess(poolId, balances.Sum(x => x.Amount), balances.Length, txInfo, txFee); diff --git a/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs b/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs index 0b0cb87d2..c7de52c9d 100644 --- a/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs +++ b/src/MiningCore/Persistence/Postgres/Repositories/StatsRepository.cs @@ -84,6 +84,16 @@ public PoolStats GetLastPoolStats(IDbConnection con, string poolId) return mapper.Map(entity); } + public decimal GetTotalPoolPayments(IDbConnection con, string poolId) + { + logger.LogInvoke(); + + var query = "SELECT sum(amount) FROM payments WHERE poolid = @poolId"; + + var result = con.ExecuteScalar(query, new { poolId }); + return result; + } + public PoolStats[] GetPoolPerformanceBetweenHourly(IDbConnection con, string poolId, DateTime start, DateTime end) { logger.LogInvoke(new []{ poolId }); diff --git a/src/MiningCore/Persistence/Repositories/IStatsRepository.cs b/src/MiningCore/Persistence/Repositories/IStatsRepository.cs index 2bc58a450..e6e4c8829 100644 --- a/src/MiningCore/Persistence/Repositories/IStatsRepository.cs +++ b/src/MiningCore/Persistence/Repositories/IStatsRepository.cs @@ -31,6 +31,7 @@ public interface IStatsRepository void InsertPoolStats(IDbConnection con, IDbTransaction tx, PoolStats stats); void InsertMinerWorkerPerformanceStats(IDbConnection con, IDbTransaction tx, MinerWorkerPerformanceStats stats); PoolStats GetLastPoolStats(IDbConnection con, string poolId); + decimal GetTotalPoolPayments(IDbConnection con, string poolId); PoolStats[] GetPoolPerformanceBetweenHourly(IDbConnection con, string poolId, DateTime start, DateTime end); MinerStats GetMinerStats(IDbConnection con, IDbTransaction tx, string poolId, string miner); MinerWorkerPerformanceStats[] PagePoolMinersByHashrate(IDbConnection con, string poolId, DateTime from, int page, int pageSize); diff --git a/src/MiningCore/Program.cs b/src/MiningCore/Program.cs index f18e61fdb..eb438a599 100644 --- a/src/MiningCore/Program.cs +++ b/src/MiningCore/Program.cs @@ -505,7 +505,7 @@ private static void ConfigurePostgres(DatabaseConfig pgConfig, ContainerBuilder logger.ThrowLogPoolStartupException("Postgres configuration: invalid or missing 'user'"); // build connection string - var connectionString = $"Server={pgConfig.Host};Port={pgConfig.Port};Database={pgConfig.Database};User Id={pgConfig.User};Password={pgConfig.Password};CommandTimeout=90;"; + var connectionString = $"Server={pgConfig.Host};Port={pgConfig.Port};Database={pgConfig.Database};User Id={pgConfig.User};Password={pgConfig.Password};CommandTimeout=300;"; // register connection factory builder.RegisterInstance(new ConnectionFactory(connectionString)) diff --git a/src/MiningCore/runtimes/win-x64/native/libmultihash.dll b/src/MiningCore/runtimes/win-x64/native/libmultihash.dll index d5b8a193b05bb02efbd31318d4d50d2550ef0bc5..ee0b76888a624edef135a13b22427b37e4f0ba64 100644 GIT binary patch delta 160161 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 W;+}IqSNL4XbHY}A?6}(9_`d+UYxD#F diff --git a/src/MiningCore/runtimes/win-x86/native/libmultihash.dll b/src/MiningCore/runtimes/win-x86/native/libmultihash.dll index 9caa71ac6bbc1228e2ae6e216adc57ed508646ce..db45fc4d2dbcdb907ef227e70bf67cc33cfc42cf 100644 GIT binary patch delta 158199 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
    LdX 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 @17 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%0AW$zwR=3-*S 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 Q*qrieUu6HrZ?)$CUpy>pj{pDw diff --git a/src/Native/libmultihash/Makefile b/src/Native/libmultihash/Makefile index f74270fe1..8bd23a1bd 100644 --- a/src/Native/libmultihash/Makefile +++ b/src/Native/libmultihash/Makefile @@ -10,7 +10,8 @@ OBJECTS = bcrypt.o blake.o c11.o dcrypt.o fresh.o \ sha3/aes_helper.o sha3/hamsi.o sha3/hamsi_helper.o sha3/sph_blake.o sha3/sph_bmw.o sha3/sph_cubehash.o \ sha3/sph_echo.o sha3/sph_fugue.o sha3/sph_groestl.o sha3/sph_hefty1.o sha3/sph_jh.o sha3/sph_keccak.o \ sha3/sph_luffa.o sha3/sph_shabal.o sha3/sph_shavite.o sha3/sph_simd.o sha3/sph_skein.o sha3/sph_whirlpool.o \ - shavite3.o skein.o x11.o x15.o \ + sha3/sph_haval.o sha3/sph_sha2.o sha3/sph_sha2big.o \ + shavite3.o skein.o x11.o x15.o x17.o \ Lyra2.o Lyra2RE.o Sponge.o \ equi/endian.o equi/equi.o \ libethash/internal.o libethash/io.o libethash/io_posix.o libethash/sha3.o diff --git a/src/Native/libmultihash/exports.cpp b/src/Native/libmultihash/exports.cpp index ade36801b..4c2be0c56 100644 --- a/src/Native/libmultihash/exports.cpp +++ b/src/Native/libmultihash/exports.cpp @@ -34,6 +34,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include "x14.h" #include "nist5.h" #include "x15.h" +#include "x17.h" #include "fresh.h" #include "dcrypt.h" #include "jh.h" @@ -68,12 +69,17 @@ extern "C" MODULE_API void x11_export(const char* input, char* output, uint32_t x11_hash(input, output, input_len); } +extern "C" MODULE_API void x17_export(const char* input, char* output, uint32_t input_len) +{ + x17_hash(input, output, input_len); +} + extern "C" MODULE_API void x15_export(const char* input, char* output, uint32_t input_len) { x15_hash(input, output, input_len); } -extern "C" MODULE_API void neoscrypt_export(const char* input, char* output, uint32_t profile) +extern "C" MODULE_API void neoscrypt_export(const unsigned char* input, unsigned char* output, uint32_t profile) { neoscrypt(input, output, profile); } diff --git a/src/Native/libmultihash/libmultihash.vcxproj b/src/Native/libmultihash/libmultihash.vcxproj index 347583c2d..a12686db6 100644 --- a/src/Native/libmultihash/libmultihash.vcxproj +++ b/src/Native/libmultihash/libmultihash.vcxproj @@ -202,10 +202,12 @@ + + @@ -222,6 +224,7 @@ + @@ -254,17 +257,19 @@ - + + + @@ -278,6 +283,7 @@ + diff --git a/src/Native/libmultihash/libmultihash.vcxproj.filters b/src/Native/libmultihash/libmultihash.vcxproj.filters index 5dcfc1bf9..96053bcf3 100644 --- a/src/Native/libmultihash/libmultihash.vcxproj.filters +++ b/src/Native/libmultihash/libmultihash.vcxproj.filters @@ -191,6 +191,15 @@ Header Files + + Header Files + + + Header Files + + + Header Files + @@ -271,9 +280,6 @@ Source Files - - Source Files - Source Files @@ -355,6 +361,18 @@ Source Files + + Source Files + + + Source Files + + + Source Files + + + Source Files + diff --git a/src/Native/libmultihash/neoscrypt.c b/src/Native/libmultihash/neoscrypt.c index 0811cee81..228c57bb5 100644 --- a/src/Native/libmultihash/neoscrypt.c +++ b/src/Native/libmultihash/neoscrypt.c @@ -2,7 +2,7 @@ * Copyright (c) 2009 Colin Percival, 2011 ArtForz * Copyright (c) 2012 Andrew Moon (floodyberry) * Copyright (c) 2012 Samuel Neves - * Copyright (c) 2014 John Doering + * Copyright (c) 2014-2016 John Doering * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -34,38 +34,24 @@ #include "neoscrypt.h" -#if (_MSC_VER) -#include -#endif - -#if (WINDOWS) -/* sizeof(unsigned long) = 4 for MinGW64 */ -typedef unsigned long long ulong; -#else -typedef unsigned long ulong; -#endif -typedef unsigned int uint; -typedef unsigned char uchar; -typedef unsigned int bool; +#ifdef _WIN32 +#include +#define alloca(x) _alloca(x) +#endif - -#define MIN(a, b) ((a) < (b) ? a : b) -#define MAX(a, b) ((a) > (b) ? a : b) - - -#if (NEOSCRYPT_SHA256) +#ifdef SHA256 /* SHA-256 */ -static const uint32_t sha256_constants[64] = { - 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, - 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, - 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, - 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, - 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, - 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, - 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, - 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 +static const uint sha256_constants[64] = { + 0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5, 0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5, + 0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3, 0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174, + 0xE49B69C1, 0xEFBE4786, 0x0FC19DC6, 0x240CA1CC, 0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA, + 0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7, 0xC6E00BF3, 0xD5A79147, 0x06CA6351, 0x14292967, + 0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13, 0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85, + 0xA2BFE8A1, 0xA81A664B, 0xC24B8B70, 0xC76C51A3, 0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070, + 0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5, 0x391C0CB3, 0x4ED8AA4A, 0x5B9CCA4F, 0x682E6FF3, + 0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208, 0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2 }; #define Ch(x,y,z) (z ^ (x & (y ^ z))) @@ -88,18 +74,15 @@ static const uint32_t sha256_constants[64] = { r[1] = r[0]; \ r[0] = t0 + t1; - typedef struct sha256_hash_state_t { - uint32_t H[8]; - uint64_t T; - uint32_t leftover; - uint8_t buffer[SCRYPT_HASH_BLOCK_SIZE]; + uint H[8]; + ullong T; + uint leftover; + uchar buffer[BLOCK_SIZE]; } sha256_hash_state; - -static void sha256_blocks(sha256_hash_state *S, const uint8_t *in, size_t blocks) { - uint32_t r[8], w[64], t0, t1; - size_t i; +static void sha256_blocks(sha256_hash_state *S, const uchar *in, uint blocks) { + uint r[8], w[64], t0, t1, i; for(i = 0; i < 8; i++) r[i] = S->H[i]; @@ -118,34 +101,34 @@ static void sha256_blocks(sha256_hash_state *S, const uint8_t *in, size_t blocks r[i] += S->H[i]; S->H[i] = r[i]; } - S->T += SCRYPT_HASH_BLOCK_SIZE * 8; - in += SCRYPT_HASH_BLOCK_SIZE; + S->T += BLOCK_SIZE * 8; + in += BLOCK_SIZE; } } static void neoscrypt_hash_init_sha256(sha256_hash_state *S) { - S->H[0] = 0x6a09e667; - S->H[1] = 0xbb67ae85; - S->H[2] = 0x3c6ef372; - S->H[3] = 0xa54ff53a; - S->H[4] = 0x510e527f; - S->H[5] = 0x9b05688c; - S->H[6] = 0x1f83d9ab; - S->H[7] = 0x5be0cd19; + S->H[0] = 0x6A09E667; + S->H[1] = 0xBB67AE85; + S->H[2] = 0x3C6EF372; + S->H[3] = 0xA54FF53A; + S->H[4] = 0x510E527F; + S->H[5] = 0x9B05688C; + S->H[6] = 0x1F83D9AB; + S->H[7] = 0x5BE0CD19; S->T = 0; S->leftover = 0; } -static void neoscrypt_hash_update_sha256(sha256_hash_state *S, const uint8_t *in, size_t inlen) { - size_t blocks, want; +static void neoscrypt_hash_update_sha256(sha256_hash_state *S, const uchar *in, uint inlen) { + uint blocks, want; /* handle the previous data */ if(S->leftover) { - want = (SCRYPT_HASH_BLOCK_SIZE - S->leftover); + want = (BLOCK_SIZE - S->leftover); want = (want < inlen) ? want : inlen; - memcpy(S->buffer + S->leftover, in, want); - S->leftover += (uint32_t)want; - if(S->leftover < SCRYPT_HASH_BLOCK_SIZE) + neoscrypt_copy(S->buffer + S->leftover, in, want); + S->leftover += (uint)want; + if(S->leftover < BLOCK_SIZE) return; in += want; inlen -= want; @@ -153,28 +136,28 @@ static void neoscrypt_hash_update_sha256(sha256_hash_state *S, const uint8_t *in } /* handle the current data */ - blocks = (inlen & ~(SCRYPT_HASH_BLOCK_SIZE - 1)); - S->leftover = (uint32_t)(inlen - blocks); + blocks = (inlen & ~(BLOCK_SIZE - 1)); + S->leftover = (uint)(inlen - blocks); if(blocks) { - sha256_blocks(S, in, blocks / SCRYPT_HASH_BLOCK_SIZE); + sha256_blocks(S, in, blocks / BLOCK_SIZE); in += blocks; } /* handle leftover data */ if(S->leftover) - memcpy(S->buffer, in, S->leftover); + neoscrypt_copy(S->buffer, in, S->leftover); } -static void neoscrypt_hash_finish_sha256(sha256_hash_state *S, uint8_t *hash) { - uint64_t t = S->T + (S->leftover * 8); +static void neoscrypt_hash_finish_sha256(sha256_hash_state *S, uchar *hash) { + ullong t = S->T + (S->leftover * 8); S->buffer[S->leftover] = 0x80; if(S->leftover <= 55) { - memset(S->buffer + S->leftover + 1, 0, 55 - S->leftover); + neoscrypt_erase(S->buffer + S->leftover + 1, 55 - S->leftover); } else { - memset(S->buffer + S->leftover + 1, 0, 63 - S->leftover); + neoscrypt_erase(S->buffer + S->leftover + 1, 63 - S->leftover); sha256_blocks(S, S->buffer, 1); - memset(S->buffer, 0, 56); + neoscrypt_erase(S->buffer, 56); } U64TO8_BE(S->buffer + 56, t); @@ -190,13 +173,6 @@ static void neoscrypt_hash_finish_sha256(sha256_hash_state *S, uint8_t *hash) { U32TO8_BE(&hash[28], S->H[7]); } -static void neoscrypt_hash_sha256(hash_digest hash, const uint8_t *m, size_t mlen) { - sha256_hash_state st; - neoscrypt_hash_init_sha256(&st); - neoscrypt_hash_update_sha256(&st, m, mlen); - neoscrypt_hash_finish_sha256(&st, hash); -} - /* HMAC for SHA-256 */ @@ -204,40 +180,49 @@ typedef struct sha256_hmac_state_t { sha256_hash_state inner, outer; } sha256_hmac_state; -static void neoscrypt_hmac_init_sha256(sha256_hmac_state *st, const uint8_t *key, size_t keylen) { - uint8_t pad[SCRYPT_HASH_BLOCK_SIZE] = {0}; - size_t i; +static inline void neoscrypt_hmac_init_sha256(sha256_hmac_state *st, + const uchar *key, uint keylen) { + uchar pad[BLOCK_SIZE + DIGEST_SIZE]; + uint *P = (uint *) pad; + uint i; - neoscrypt_hash_init_sha256(&st->inner); - neoscrypt_hash_init_sha256(&st->outer); + /* The pad initialisation for the inner loop */ + for(i = 0; i < (BLOCK_SIZE >> 2); i++) + P[i] = 0x36363636; - if(keylen <= SCRYPT_HASH_BLOCK_SIZE) { - /* use the key directly if it's <= blocksize bytes */ - memcpy(pad, key, keylen); + if(keylen <= BLOCK_SIZE) { + /* XOR the key into the pad */ + neoscrypt_xor(pad, key, keylen); } else { - /* if it's > blocksize bytes, hash it */ - neoscrypt_hash_sha256(pad, key, keylen); + /* Hash the key and XOR into the pad */ + sha256_hash_state st0; + neoscrypt_hash_init_sha256(&st0); + neoscrypt_hash_update_sha256(&st0, key, keylen); + neoscrypt_hash_finish_sha256(&st0, &pad[BLOCK_SIZE]); + neoscrypt_xor(&pad[0], &pad[BLOCK_SIZE], DIGEST_SIZE); } - /* inner = (key ^ 0x36) */ - /* h(inner || ...) */ - for(i = 0; i < SCRYPT_HASH_BLOCK_SIZE; i++) - pad[i] ^= 0x36; - neoscrypt_hash_update_sha256(&st->inner, pad, SCRYPT_HASH_BLOCK_SIZE); - - /* outer = (key ^ 0x5c) */ - /* h(outer || ...) */ - for(i = 0; i < SCRYPT_HASH_BLOCK_SIZE; i++) - pad[i] ^= (0x5c ^ 0x36); - neoscrypt_hash_update_sha256(&st->outer, pad, SCRYPT_HASH_BLOCK_SIZE); + neoscrypt_hash_init_sha256(&st->inner); + /* h(inner || pad) */ + neoscrypt_hash_update_sha256(&st->inner, pad, BLOCK_SIZE); + + /* The pad re-initialisation for the outer loop */ + for(i = 0; i < (BLOCK_SIZE >> 2); i++) + P[i] ^= (0x36363636 ^ 0x5C5C5C5C); + + neoscrypt_hash_init_sha256(&st->outer); + /* h(outer || pad) */ + neoscrypt_hash_update_sha256(&st->outer, pad, BLOCK_SIZE); } -static void neoscrypt_hmac_update_sha256(sha256_hmac_state *st, const uint8_t *m, size_t mlen) { +static inline void neoscrypt_hmac_update_sha256(sha256_hmac_state *st, + const uchar *m, uint mlen) { /* h(inner || m...) */ neoscrypt_hash_update_sha256(&st->inner, m, mlen); } -static void neoscrypt_hmac_finish_sha256(sha256_hmac_state *st, hash_digest mac) { +static inline void neoscrypt_hmac_finish_sha256(sha256_hmac_state *st, + hash_digest mac) { /* h(inner || m) */ hash_digest innerhash; neoscrypt_hash_finish_sha256(&st->inner, innerhash); @@ -250,14 +235,14 @@ static void neoscrypt_hmac_finish_sha256(sha256_hmac_state *st, hash_digest mac) /* PBKDF2 for SHA-256 */ -static void neoscrypt_pbkdf2_sha256(const uint8_t *password, size_t password_len, - const uint8_t *salt, size_t salt_len, uint64_t N, uint8_t *output, size_t output_len) { +void neoscrypt_pbkdf2_sha256(const uchar *password, uint password_len, + const uchar *salt, uint salt_len, uint N, uchar *output, uint output_len) { sha256_hmac_state hmac_pw, hmac_pw_salt, work; hash_digest ti, u; - uint8_t be[4]; - uint32_t i, j, k, blocks; + uchar be[4]; + uint i, j, k, blocks; - /* bytes must be <= (0xffffffff - (SCRYPT_HASH_DIGEST_SIZE - 1)), which they will always be under scrypt */ + /* bytes must be <= (0xffffffff - (DIGEST_SIZE - 1)), which they will always be under scrypt */ /* hmac(password, ...) */ neoscrypt_hmac_init_sha256(&hmac_pw, password, password_len); @@ -266,20 +251,20 @@ static void neoscrypt_pbkdf2_sha256(const uint8_t *password, size_t password_len hmac_pw_salt = hmac_pw; neoscrypt_hmac_update_sha256(&hmac_pw_salt, salt, salt_len); - blocks = ((uint32_t)output_len + (SCRYPT_HASH_DIGEST_SIZE - 1)) / SCRYPT_HASH_DIGEST_SIZE; + blocks = ((uint)output_len + (DIGEST_SIZE - 1)) / DIGEST_SIZE; for(i = 1; i <= blocks; i++) { /* U1 = hmac(password, salt || be(i)) */ U32TO8_BE(be, i); work = hmac_pw_salt; neoscrypt_hmac_update_sha256(&work, be, 4); neoscrypt_hmac_finish_sha256(&work, ti); - memcpy(u, ti, sizeof(u)); + neoscrypt_copy(u, ti, sizeof(u)); /* T[i] = U1 ^ U2 ^ U3... */ for(j = 0; j < N - 1; j++) { /* UX = hmac(password, U{X-1}) */ work = hmac_pw; - neoscrypt_hmac_update_sha256(&work, u, SCRYPT_HASH_DIGEST_SIZE); + neoscrypt_hmac_update_sha256(&work, u, DIGEST_SIZE); neoscrypt_hmac_finish_sha256(&work, u); /* T[i] ^= UX */ @@ -287,47 +272,50 @@ static void neoscrypt_pbkdf2_sha256(const uint8_t *password, size_t password_len ti[k] ^= u[k]; } - memcpy(output, ti, (output_len > SCRYPT_HASH_DIGEST_SIZE) ? SCRYPT_HASH_DIGEST_SIZE : output_len); - output += SCRYPT_HASH_DIGEST_SIZE; - output_len -= SCRYPT_HASH_DIGEST_SIZE; + neoscrypt_copy(output, ti, (output_len > DIGEST_SIZE) ? DIGEST_SIZE : output_len); + output += DIGEST_SIZE; + output_len -= DIGEST_SIZE; } } -#endif +#endif /* SHA256 */ -#if (NEOSCRYPT_BLAKE256) +#ifdef BLAKE256 /* BLAKE-256 */ -const uint8_t blake256_sigma[] = { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15, - 14,10, 4, 8, 9,15,13, 6, 1,12, 0, 2,11, 7, 5, 3, - 11, 8,12, 0, 5, 2,15,13,10,14, 3, 6, 7, 1, 9, 4, - 7, 9, 3, 1,13,12,11,14, 2, 6, 5,10, 4, 0,15, 8, - 9, 0, 5, 7, 2, 4,10,15,14, 1,11,12, 6, 8, 3,13, - 2,12, 6,10, 0,11, 8, 3, 4,13, 7, 5,15,14, 1, 9, - 12, 5, 1,15,14,13, 4,10, 0, 7, 6, 3, 9, 2, 8,11, - 13,11, 7,14,12, 1, 3, 9, 5, 0,15, 4, 8, 6, 2,10, - 6,15,14, 9,11, 3, 0, 8,12, 2,13, 7, 1, 4,10, 5, - 10, 2, 8, 4, 7, 6, 1, 5,15,11, 9,14, 3,12,13 ,0, +const uchar blake256_sigma[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3, + 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4, + 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8, + 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13, + 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9, + 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11, + 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10, + 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5, + 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0 }; -const uint32_t blake256_constants[16] = { - 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344,0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89, - 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c,0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917 +const uint blake256_constants[16] = { + 0x243F6A88, 0x85A308D3, 0x13198A2E, 0x03707344, + 0xA4093822, 0x299F31D0, 0x082EFA98, 0xEC4E6C89, + 0x452821E6, 0x38D01377, 0xBE5466CF, 0x34E90C6C, + 0xC0AC29B7, 0xC97C50DD, 0x3F84D5B5, 0xB5470917 }; typedef struct blake256_hash_state_t { - uint32_t H[8], T[2]; - uint32_t leftover; - uint8_t buffer[SCRYPT_HASH_BLOCK_SIZE]; + uint H[8]; + uint T[2]; + uint leftover; + uchar buffer[BLOCK_SIZE]; } blake256_hash_state; -static void blake256_blocks(blake256_hash_state *S, const uint8_t *in, size_t blocks) { - const uint8_t *sigma, *sigma_end = blake256_sigma + (10 * 16); - uint32_t m[16], v[16], h[8], t[2]; - uint32_t i; +static void blake256_blocks(blake256_hash_state *S, const uchar *in, uint blocks) { + const uchar *sigma, *sigma_end = blake256_sigma + (10 * 16); + uint m[16], v[16], h[8], t[2]; + uint i; for(i = 0; i < 8; i++) h[i] = S->H[i]; @@ -343,35 +331,35 @@ static void blake256_blocks(blake256_hash_state *S, const uint8_t *in, size_t bl for(i = 0; i < 4; i++) v[i + 8] = blake256_constants[i]; for(i = 0; i < 2; i++) - v[i + 12] = blake256_constants[i+4] ^ t[0]; + v[i + 12] = blake256_constants[i + 4] ^ t[0]; for(i = 0; i < 2; i++) - v[i + 14] = blake256_constants[i+6] ^ t[1]; + v[i + 14] = blake256_constants[i + 6] ^ t[1]; for(i = 0; i < 16; i++) m[i] = U8TO32_BE(&in[i * 4]); in += 64; -#define G(a,b,c,d,e) \ - v[a] += (m[sigma[e+0]] ^ blake256_constants[sigma[e+1]]) + v[b]; \ - v[d] = ROTR32(v[d] ^ v[a],16); \ +#define G(a, b, c, d, e) \ + v[a] += (m[sigma[e + 0]] ^ blake256_constants[sigma[e + 1]]) + v[b]; \ + v[d] = ROTR32(v[d] ^ v[a], 16); \ v[c] += v[d]; \ - v[b] = ROTR32(v[b] ^ v[c],12); \ - v[a] += (m[sigma[e+1]] ^ blake256_constants[sigma[e+0]]) + v[b]; \ + v[b] = ROTR32(v[b] ^ v[c], 12); \ + v[a] += (m[sigma[e + 1]] ^ blake256_constants[sigma[e + 0]]) + v[b]; \ v[d] = ROTR32(v[d] ^ v[a], 8); \ v[c] += v[d]; \ v[b] = ROTR32(v[b] ^ v[c], 7); for(i = 0, sigma = blake256_sigma; i < 14; i++) { - G(0, 4, 8,12, 0); - G(1, 5, 9,13, 2); - G(2, 6,10,14, 4); - G(3, 7,11,15, 6); + G( 0, 4, 8, 12, 0); + G( 1, 5, 9, 13, 2); + G( 2, 6, 10, 14, 4); + G( 3, 7, 11, 15, 6); - G(0, 5,10,15, 8); - G(1, 6,11,12,10); - G(2, 7, 8,13,12); - G(3, 4, 9,14,14); + G( 0, 5, 10, 15, 8); + G( 1, 6, 11, 12, 10); + G( 2, 7, 8, 13, 12); + G( 3, 4, 9, 14, 14); sigma += 16; if(sigma == sigma_end) @@ -404,16 +392,17 @@ static void neoscrypt_hash_init_blake256(blake256_hash_state *S) { S->leftover = 0; } -static void neoscrypt_hash_update_blake256(blake256_hash_state *S, const uint8_t *in, size_t inlen) { - size_t blocks, want; +static void neoscrypt_hash_update_blake256(blake256_hash_state *S, + const uchar *in, uint inlen) { + uint blocks, want; /* handle the previous data */ if(S->leftover) { - want = (SCRYPT_HASH_BLOCK_SIZE - S->leftover); + want = (BLOCK_SIZE - S->leftover); want = (want < inlen) ? want : inlen; - memcpy(S->buffer + S->leftover, in, want); - S->leftover += (uint32_t)want; - if(S->leftover < SCRYPT_HASH_BLOCK_SIZE) + neoscrypt_copy(S->buffer + S->leftover, in, want); + S->leftover += (uint)want; + if(S->leftover < BLOCK_SIZE) return; in += want; inlen -= want; @@ -421,29 +410,29 @@ static void neoscrypt_hash_update_blake256(blake256_hash_state *S, const uint8_t } /* handle the current data */ - blocks = (inlen & ~(SCRYPT_HASH_BLOCK_SIZE - 1)); - S->leftover = (uint32_t)(inlen - blocks); + blocks = (inlen & ~(BLOCK_SIZE - 1)); + S->leftover = (uint)(inlen - blocks); if(blocks) { - blake256_blocks(S, in, blocks / SCRYPT_HASH_BLOCK_SIZE); + blake256_blocks(S, in, blocks / BLOCK_SIZE); in += blocks; } /* handle leftover data */ if(S->leftover) - memcpy(S->buffer, in, S->leftover); + neoscrypt_copy(S->buffer, in, S->leftover); } -static void neoscrypt_hash_finish_blake256(blake256_hash_state *S, uint8_t *hash) { - uint32_t th, tl, bits; +static void neoscrypt_hash_finish_blake256(blake256_hash_state *S, uchar *hash) { + uint th, tl, bits; bits = (S->leftover << 3); tl = S->T[0] + bits; th = S->T[1]; if(S->leftover == 0) { - S->T[0] = (uint32_t)0 - (uint32_t)512; - S->T[1] = (uint32_t)0 - (uint32_t)1; + S->T[0] = (uint)0 - (uint)512; + S->T[1] = (uint)0 - (uint)1; } else if(S->T[0] == 0) { - S->T[0] = ((uint32_t)0 - (uint32_t)512) + bits; + S->T[0] = ((uint)0 - (uint)512) + bits; S->T[1] = S->T[1] - 1; } else { S->T[0] -= (512 - bits); @@ -451,13 +440,13 @@ static void neoscrypt_hash_finish_blake256(blake256_hash_state *S, uint8_t *hash S->buffer[S->leftover] = 0x80; if(S->leftover <= 55) { - memset(S->buffer + S->leftover + 1, 0, 55 - S->leftover); + neoscrypt_erase(S->buffer + S->leftover + 1, 55 - S->leftover); } else { - memset(S->buffer + S->leftover + 1, 0, 63 - S->leftover); + neoscrypt_erase(S->buffer + S->leftover + 1, 63 - S->leftover); blake256_blocks(S, S->buffer, 1); - S->T[0] = (uint32_t)0 - (uint32_t)512; - S->T[1] = (uint32_t)0 - (uint32_t)1; - memset(S->buffer, 0, 56); + S->T[0] = (uint)0 - (uint)512; + S->T[1] = (uint)0 - (uint)1; + neoscrypt_erase(S->buffer, 56); } S->buffer[55] |= 1; U32TO8_BE(S->buffer + 56, th); @@ -474,54 +463,57 @@ static void neoscrypt_hash_finish_blake256(blake256_hash_state *S, uint8_t *hash U32TO8_BE(&hash[28], S->H[7]); } -static void neoscrypt_hash_blake256(hash_digest hash, const uint8_t *m, size_t mlen) { - blake256_hash_state st; - neoscrypt_hash_init_blake256(&st); - neoscrypt_hash_update_blake256(&st, m, mlen); - neoscrypt_hash_finish_blake256(&st, hash); -} - /* HMAC for BLAKE-256 */ typedef struct blake256_hmac_state_t { - blake256_hash_state inner, outer; + blake256_hash_state inner; + blake256_hash_state outer; } blake256_hmac_state; -static void neoscrypt_hmac_init_blake256(blake256_hmac_state *st, const uint8_t *key, size_t keylen) { - uint8_t pad[SCRYPT_HASH_BLOCK_SIZE] = {0}; - size_t i; +static inline void neoscrypt_hmac_init_blake256(blake256_hmac_state *st, + const uchar *key, uint keylen) { + uchar pad[BLOCK_SIZE + DIGEST_SIZE]; + uint *P = (uint *) pad; + uint i; - neoscrypt_hash_init_blake256(&st->inner); - neoscrypt_hash_init_blake256(&st->outer); + /* The pad initialisation for the inner loop */ + for(i = 0; i < (BLOCK_SIZE >> 2); i++) + P[i] = 0x36363636; - if(keylen <= SCRYPT_HASH_BLOCK_SIZE) { - /* use the key directly if it's <= blocksize bytes */ - memcpy(pad, key, keylen); + if(keylen <= BLOCK_SIZE) { + /* XOR the key into the pad */ + neoscrypt_xor(pad, key, keylen); } else { - /* if it's > blocksize bytes, hash it */ - neoscrypt_hash_blake256(pad, key, keylen); + /* Hash the key and XOR into the pad */ + blake256_hash_state st0; + neoscrypt_hash_init_blake256(&st0); + neoscrypt_hash_update_blake256(&st0, key, keylen); + neoscrypt_hash_finish_blake256(&st0, &pad[BLOCK_SIZE]); + neoscrypt_xor(&pad[0], &pad[BLOCK_SIZE], DIGEST_SIZE); } - /* inner = (key ^ 0x36) */ - /* h(inner || ...) */ - for(i = 0; i < SCRYPT_HASH_BLOCK_SIZE; i++) - pad[i] ^= 0x36; - neoscrypt_hash_update_blake256(&st->inner, pad, SCRYPT_HASH_BLOCK_SIZE); - - /* outer = (key ^ 0x5c) */ - /* h(outer || ...) */ - for(i = 0; i < SCRYPT_HASH_BLOCK_SIZE; i++) - pad[i] ^= (0x5c ^ 0x36); - neoscrypt_hash_update_blake256(&st->outer, pad, SCRYPT_HASH_BLOCK_SIZE); + neoscrypt_hash_init_blake256(&st->inner); + /* h(inner || pad) */ + neoscrypt_hash_update_blake256(&st->inner, pad, BLOCK_SIZE); + + /* The pad re-initialisation for the outer loop */ + for(i = 0; i < (BLOCK_SIZE >> 2); i++) + P[i] ^= (0x36363636 ^ 0x5C5C5C5C); + + neoscrypt_hash_init_blake256(&st->outer); + /* h(outer || pad) */ + neoscrypt_hash_update_blake256(&st->outer, pad, BLOCK_SIZE); } -static void neoscrypt_hmac_update_blake256(blake256_hmac_state *st, const uint8_t *m, size_t mlen) { +static inline void neoscrypt_hmac_update_blake256(blake256_hmac_state *st, + const uchar *m, uint mlen) { /* h(inner || m...) */ neoscrypt_hash_update_blake256(&st->inner, m, mlen); } -static void neoscrypt_hmac_finish_blake256(blake256_hmac_state *st, hash_digest mac) { +static inline void neoscrypt_hmac_finish_blake256(blake256_hmac_state *st, + hash_digest mac) { /* h(inner || m) */ hash_digest innerhash; neoscrypt_hash_finish_blake256(&st->inner, innerhash); @@ -534,14 +526,15 @@ static void neoscrypt_hmac_finish_blake256(blake256_hmac_state *st, hash_digest /* PBKDF2 for BLAKE-256 */ -static void neoscrypt_pbkdf2_blake256(const uint8_t *password, size_t password_len, - const uint8_t *salt, size_t salt_len, uint64_t N, uint8_t *output, size_t output_len) { +static void neoscrypt_pbkdf2_blake256(const uchar *password, + uint password_len, const uchar *salt, uint salt_len, uint N, + uchar *output, uint output_len) { blake256_hmac_state hmac_pw, hmac_pw_salt, work; hash_digest ti, u; - uint8_t be[4]; - uint32_t i, j, k, blocks; + uchar be[4]; + uint i, j, k, blocks; - /* bytes must be <= (0xffffffff - (SCRYPT_HASH_DIGEST_SIZE - 1)), which they will always be under scrypt */ + /* bytes must be <= (0xffffffff - (DIGEST_SIZE - 1)), which they will always be under scrypt */ /* hmac(password, ...) */ neoscrypt_hmac_init_blake256(&hmac_pw, password, password_len); @@ -550,20 +543,20 @@ static void neoscrypt_pbkdf2_blake256(const uint8_t *password, size_t password_l hmac_pw_salt = hmac_pw; neoscrypt_hmac_update_blake256(&hmac_pw_salt, salt, salt_len); - blocks = ((uint32_t)output_len + (SCRYPT_HASH_DIGEST_SIZE - 1)) / SCRYPT_HASH_DIGEST_SIZE; + blocks = ((uint)output_len + (DIGEST_SIZE - 1)) / DIGEST_SIZE; for(i = 1; i <= blocks; i++) { /* U1 = hmac(password, salt || be(i)) */ U32TO8_BE(be, i); work = hmac_pw_salt; neoscrypt_hmac_update_blake256(&work, be, 4); neoscrypt_hmac_finish_blake256(&work, ti); - memcpy(u, ti, sizeof(u)); + neoscrypt_copy(u, ti, sizeof(u)); /* T[i] = U1 ^ U2 ^ U3... */ for(j = 0; j < N - 1; j++) { /* UX = hmac(password, U{X-1}) */ work = hmac_pw; - neoscrypt_hmac_update_blake256(&work, u, SCRYPT_HASH_DIGEST_SIZE); + neoscrypt_hmac_update_blake256(&work, u, DIGEST_SIZE); neoscrypt_hmac_finish_blake256(&work, u); /* T[i] ^= UX */ @@ -571,26 +564,22 @@ static void neoscrypt_pbkdf2_blake256(const uint8_t *password, size_t password_l ti[k] ^= u[k]; } - memcpy(output, ti, (output_len > SCRYPT_HASH_DIGEST_SIZE) ? SCRYPT_HASH_DIGEST_SIZE : output_len); - output += SCRYPT_HASH_DIGEST_SIZE; - output_len -= SCRYPT_HASH_DIGEST_SIZE; + neoscrypt_copy(output, ti, (output_len > DIGEST_SIZE) ? DIGEST_SIZE : output_len); + output += DIGEST_SIZE; + output_len -= DIGEST_SIZE; } } -#endif +#endif /* BLAKE256 */ /* NeoScrypt */ -#if defined(ASM) +#ifdef ASM -extern void neoscrypt_salsa(uint *X, uint rounds); -extern void neoscrypt_salsa_tangle(uint *X, uint count); -extern void neoscrypt_chacha(uint *X, uint rounds); - -extern void neoscrypt_blkcpy(void *dstp, const void *srcp, uint len); -extern void neoscrypt_blkswp(void *blkAp, void *blkBp, uint len); -extern void neoscrypt_blkxor(void *dstp, const void *srcp, uint len); +extern void neoscrypt_copy(void *dstp, const void *srcp, uint len); +extern void neoscrypt_erase(void *dstp, uint len); +extern void neoscrypt_xor(void *dstp, const void *srcp, uint len); #else @@ -662,15 +651,14 @@ static void neoscrypt_chacha(uint *X, uint rounds) { #undef quarter } - /* Fast 32-bit / 64-bit memcpy(); * len must be a multiple of 32 bytes */ static void neoscrypt_blkcpy(void *dstp, const void *srcp, uint len) { - ulong *dst = (ulong *) dstp; - ulong *src = (ulong *) srcp; + size_t *dst = (size_t *) dstp; + size_t *src = (size_t *) srcp; uint i; - for(i = 0; i < (len / sizeof(ulong)); i += 4) { + for(i = 0; i < (len / sizeof(size_t)); i += 4) { dst[i] = src[i]; dst[i + 1] = src[i + 1]; dst[i + 2] = src[i + 2]; @@ -681,12 +669,12 @@ static void neoscrypt_blkcpy(void *dstp, const void *srcp, uint len) { /* Fast 32-bit / 64-bit block swapper; * len must be a multiple of 32 bytes */ static void neoscrypt_blkswp(void *blkAp, void *blkBp, uint len) { - ulong *blkA = (ulong *) blkAp; - ulong *blkB = (ulong *) blkBp; - register ulong t0, t1, t2, t3; + size_t *blkA = (size_t *) blkAp; + size_t *blkB = (size_t *) blkBp; + register size_t t0, t1, t2, t3; uint i; - for(i = 0; i < (len / sizeof(ulong)); i += 4) { + for(i = 0; i < (len / sizeof(size_t)); i += 4) { t0 = blkA[i]; t1 = blkA[i + 1]; t2 = blkA[i + 2]; @@ -705,11 +693,11 @@ static void neoscrypt_blkswp(void *blkAp, void *blkBp, uint len) { /* Fast 32-bit / 64-bit block XOR engine; * len must be a multiple of 32 bytes */ static void neoscrypt_blkxor(void *dstp, const void *srcp, uint len) { - ulong *dst = (ulong *) dstp; - ulong *src = (ulong *) srcp; + size_t *dst = (size_t *) dstp; + size_t *src = (size_t *) srcp; uint i; - for(i = 0; i < (len / sizeof(ulong)); i += 4) { + for(i = 0; i < (len / sizeof(size_t)); i += 4) { dst[i] ^= src[i]; dst[i + 1] ^= src[i + 1]; dst[i + 2] ^= src[i + 2]; @@ -717,18 +705,16 @@ static void neoscrypt_blkxor(void *dstp, const void *srcp, uint len) { } } -#endif - /* 32-bit / 64-bit optimised memcpy() */ -static void neoscrypt_copy(void *dstp, const void *srcp, uint len) { - ulong *dst = (ulong *) dstp; - ulong *src = (ulong *) srcp; +void neoscrypt_copy(void *dstp, const void *srcp, uint len) { + size_t *dst = (size_t *) dstp; + size_t *src = (size_t *) srcp; uint i, tail; - for(i = 0; i < (len / sizeof(ulong)); i++) + for(i = 0; i < (len / sizeof(size_t)); i++) dst[i] = src[i]; - tail = len & (sizeof(ulong) - 1); + tail = len & (sizeof(size_t) - 1); if(tail) { uchar *dstb = (uchar *) dstp; uchar *srcb = (uchar *) srcp; @@ -739,15 +725,15 @@ static void neoscrypt_copy(void *dstp, const void *srcp, uint len) { } /* 32-bit / 64-bit optimised memory erase aka memset() to zero */ -static void neoscrypt_erase(void *dstp, uint len) { - const ulong null = 0; - ulong *dst = (ulong *) dstp; +void neoscrypt_erase(void *dstp, uint len) { + const size_t null = 0; + size_t *dst = (size_t *) dstp; uint i, tail; - for(i = 0; i < (len / sizeof(ulong)); i++) + for(i = 0; i < (len / sizeof(size_t)); i++) dst[i] = null; - tail = len & (sizeof(ulong) - 1); + tail = len & (sizeof(size_t) - 1); if(tail) { uchar *dstb = (uchar *) dstp; @@ -757,15 +743,15 @@ static void neoscrypt_erase(void *dstp, uint len) { } /* 32-bit / 64-bit optimised XOR engine */ -static void neoscrypt_xor(void *dstp, const void *srcp, uint len) { - ulong *dst = (ulong *) dstp; - ulong *src = (ulong *) srcp; +void neoscrypt_xor(void *dstp, const void *srcp, uint len) { + size_t *dst = (size_t *) dstp; + size_t *src = (size_t *) srcp; uint i, tail; - for(i = 0; i < (len / sizeof(ulong)); i++) + for(i = 0; i < (len / sizeof(size_t)); i++) dst[i] ^= src[i]; - tail = len & (sizeof(ulong) - 1); + tail = len & (sizeof(size_t) - 1); if(tail) { uchar *dstb = (uchar *) dstp; uchar *srcb = (uchar *) srcp; @@ -775,12 +761,10 @@ static void neoscrypt_xor(void *dstp, const void *srcp, uint len) { } } +#endif /* ASM */ -/* BLAKE2s */ -#define BLAKE2S_BLOCK_SIZE 64U -#define BLAKE2S_OUT_SIZE 32U -#define BLAKE2S_KEY_SIZE 32U +/* BLAKE2s */ /* Parameter block of 32 bytes */ typedef struct blake2s_param_t { @@ -796,13 +780,15 @@ typedef struct blake2s_param_t { uchar personal[8]; } blake2s_param; -/* State block of 180 bytes */ +/* State block of 256 bytes */ typedef struct blake2s_state_t { uint h[8]; uint t[2]; uint f[2]; - uchar buf[2 * BLAKE2S_BLOCK_SIZE]; + uchar buf[2 * BLOCK_SIZE]; uint buflen; + uint padding[3]; + uchar tempbuf[BLOCK_SIZE]; } blake2s_state; static const uint blake2s_IV[8] = { @@ -810,97 +796,1440 @@ static const uint blake2s_IV[8] = { 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19 }; -static const uint8_t blake2s_sigma[10][16] = { - { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } , - { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } , - { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 } , - { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 } , - { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 } , - { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 } , - { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 } , - { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 } , - { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 } , - { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13 , 0 } , -}; +#ifdef ASM -static void blake2s_compress(blake2s_state *S, const uint *buf) { - uint i; - uint m[16]; - uint v[16]; +extern void blake2s_compress(blake2s_state *S); - neoscrypt_copy(m, buf, 64); - neoscrypt_copy(v, S, 32); +#else - v[ 8] = blake2s_IV[0]; - v[ 9] = blake2s_IV[1]; +/* Buffer mixer (compressor) */ +static void blake2s_compress(blake2s_state *S) { + uint *v = (uint *) S->tempbuf; + uint *m = (uint *) S->buf; + register uint t0, t1, t2, t3; + + v[0] = S->h[0]; + v[1] = S->h[1]; + v[2] = S->h[2]; + v[3] = S->h[3]; + v[4] = S->h[4]; + v[5] = S->h[5]; + v[6] = S->h[6]; + v[7] = S->h[7]; + v[8] = blake2s_IV[0]; + v[9] = blake2s_IV[1]; v[10] = blake2s_IV[2]; v[11] = blake2s_IV[3]; v[12] = S->t[0] ^ blake2s_IV[4]; v[13] = S->t[1] ^ blake2s_IV[5]; v[14] = S->f[0] ^ blake2s_IV[6]; v[15] = S->f[1] ^ blake2s_IV[7]; -#define G(r,i,a,b,c,d) \ - do { \ - a = a + b + m[blake2s_sigma[r][2*i+0]]; \ - d = ROTR32(d ^ a, 16); \ - c = c + d; \ - b = ROTR32(b ^ c, 12); \ - a = a + b + m[blake2s_sigma[r][2*i+1]]; \ - d = ROTR32(d ^ a, 8); \ - c = c + d; \ - b = ROTR32(b ^ c, 7); \ - } while(0) -#define ROUND(r) \ - do { \ - G(r, 0, v[ 0], v[ 4], v[ 8], v[12]); \ - G(r, 1, v[ 1], v[ 5], v[ 9], v[13]); \ - G(r, 2, v[ 2], v[ 6], v[10], v[14]); \ - G(r, 3, v[ 3], v[ 7], v[11], v[15]); \ - G(r, 4, v[ 0], v[ 5], v[10], v[15]); \ - G(r, 5, v[ 1], v[ 6], v[11], v[12]); \ - G(r, 6, v[ 2], v[ 7], v[ 8], v[13]); \ - G(r, 7, v[ 3], v[ 4], v[ 9], v[14]); \ - } while(0) - ROUND(0); - ROUND(1); - ROUND(2); - ROUND(3); - ROUND(4); - ROUND(5); - ROUND(6); - ROUND(7); - ROUND(8); - ROUND(9); - - for(i = 0; i < 8; i++) - S->h[i] = S->h[i] ^ v[i] ^ v[i + 8]; -#undef G -#undef ROUND +/* Round 0 */ + t0 = v[0]; + t1 = v[4]; + t0 = t0 + t1 + m[0]; + t3 = v[12]; + t2 = v[8]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[1]; + v[0] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[12] = t3; + t2 = t2 + t3; + v[8] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[4] = t1; + + t0 = v[1]; + t1 = v[5]; + t0 = t0 + t1 + m[2]; + t3 = v[13]; + t2 = v[9]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[3]; + v[1] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[13] = t3; + t2 = t2 + t3; + v[9] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[5] = t1; + + t0 = v[2]; + t1 = v[6]; + t0 = t0 + t1 + m[4]; + t3 = v[14]; + t2 = v[10]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[5]; + v[2] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[14] = t3; + t2 = t2 + t3; + v[10] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[6] = t1; + + t0 = v[3]; + t1 = v[7]; + t0 = t0 + t1 + m[6]; + t3 = v[15]; + t2 = v[11]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[7]; + v[3] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[15] = t3; + t2 = t2 + t3; + v[11] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[7] = t1; + + t0 = v[0]; + t1 = v[5]; + t0 = t0 + t1 + m[8]; + t3 = v[15]; + t2 = v[10]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[9]; + v[0] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[15] = t3; + t2 = t2 + t3; + v[10] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[5] = t1; + + t0 = v[1]; + t1 = v[6]; + t0 = t0 + t1 + m[10]; + t3 = v[12]; + t2 = v[11]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[11]; + v[1] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[12] = t3; + t2 = t2 + t3; + v[11] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[6] = t1; + + t0 = v[2]; + t1 = v[7]; + t0 = t0 + t1 + m[12]; + t3 = v[13]; + t2 = v[8]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[13]; + v[2] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[13] = t3; + t2 = t2 + t3; + v[8] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[7] = t1; + + t0 = v[3]; + t1 = v[4]; + t0 = t0 + t1 + m[14]; + t3 = v[14]; + t2 = v[9]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[15]; + v[3] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[14] = t3; + t2 = t2 + t3; + v[9] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[4] = t1; + +/* Round 1 */ + t0 = v[0]; + t1 = v[4]; + t0 = t0 + t1 + m[14]; + t3 = v[12]; + t2 = v[8]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[10]; + v[0] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[12] = t3; + t2 = t2 + t3; + v[8] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[4] = t1; + + t0 = v[1]; + t1 = v[5]; + t0 = t0 + t1 + m[4]; + t3 = v[13]; + t2 = v[9]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[8]; + v[1] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[13] = t3; + t2 = t2 + t3; + v[9] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[5] = t1; + + t0 = v[2]; + t1 = v[6]; + t0 = t0 + t1 + m[9]; + t3 = v[14]; + t2 = v[10]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[15]; + v[2] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[14] = t3; + t2 = t2 + t3; + v[10] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[6] = t1; + + t0 = v[3]; + t1 = v[7]; + t0 = t0 + t1 + m[13]; + t3 = v[15]; + t2 = v[11]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[6]; + v[3] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[15] = t3; + t2 = t2 + t3; + v[11] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[7] = t1; + + t0 = v[0]; + t1 = v[5]; + t0 = t0 + t1 + m[1]; + t3 = v[15]; + t2 = v[10]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[12]; + v[0] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[15] = t3; + t2 = t2 + t3; + v[10] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[5] = t1; + + t0 = v[1]; + t1 = v[6]; + t0 = t0 + t1 + m[0]; + t3 = v[12]; + t2 = v[11]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[2]; + v[1] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[12] = t3; + t2 = t2 + t3; + v[11] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[6] = t1; + + t0 = v[2]; + t1 = v[7]; + t0 = t0 + t1 + m[11]; + t3 = v[13]; + t2 = v[8]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[7]; + v[2] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[13] = t3; + t2 = t2 + t3; + v[8] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[7] = t1; + + t0 = v[3]; + t1 = v[4]; + t0 = t0 + t1 + m[5]; + t3 = v[14]; + t2 = v[9]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[3]; + v[3] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[14] = t3; + t2 = t2 + t3; + v[9] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[4] = t1; + +/* Round 2 */ + t0 = v[0]; + t1 = v[4]; + t0 = t0 + t1 + m[11]; + t3 = v[12]; + t2 = v[8]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[8]; + v[0] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[12] = t3; + t2 = t2 + t3; + v[8] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[4] = t1; + + t0 = v[1]; + t1 = v[5]; + t0 = t0 + t1 + m[12]; + t3 = v[13]; + t2 = v[9]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[0]; + v[1] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[13] = t3; + t2 = t2 + t3; + v[9] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[5] = t1; + + t0 = v[2]; + t1 = v[6]; + t0 = t0 + t1 + m[5]; + t3 = v[14]; + t2 = v[10]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[2]; + v[2] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[14] = t3; + t2 = t2 + t3; + v[10] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[6] = t1; + + t0 = v[3]; + t1 = v[7]; + t0 = t0 + t1 + m[15]; + t3 = v[15]; + t2 = v[11]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[13]; + v[3] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[15] = t3; + t2 = t2 + t3; + v[11] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[7] = t1; + + t0 = v[0]; + t1 = v[5]; + t0 = t0 + t1 + m[10]; + t3 = v[15]; + t2 = v[10]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[14]; + v[0] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[15] = t3; + t2 = t2 + t3; + v[10] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[5] = t1; + + t0 = v[1]; + t1 = v[6]; + t0 = t0 + t1 + m[3]; + t3 = v[12]; + t2 = v[11]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[6]; + v[1] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[12] = t3; + t2 = t2 + t3; + v[11] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[6] = t1; + + t0 = v[2]; + t1 = v[7]; + t0 = t0 + t1 + m[7]; + t3 = v[13]; + t2 = v[8]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[1]; + v[2] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[13] = t3; + t2 = t2 + t3; + v[8] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[7] = t1; + + t0 = v[3]; + t1 = v[4]; + t0 = t0 + t1 + m[9]; + t3 = v[14]; + t2 = v[9]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[4]; + v[3] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[14] = t3; + t2 = t2 + t3; + v[9] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[4] = t1; + +/* Round 3 */ + t0 = v[0]; + t1 = v[4]; + t0 = t0 + t1 + m[7]; + t3 = v[12]; + t2 = v[8]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[9]; + v[0] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[12] = t3; + t2 = t2 + t3; + v[8] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[4] = t1; + + t0 = v[1]; + t1 = v[5]; + t0 = t0 + t1 + m[3]; + t3 = v[13]; + t2 = v[9]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[1]; + v[1] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[13] = t3; + t2 = t2 + t3; + v[9] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[5] = t1; + + t0 = v[2]; + t1 = v[6]; + t0 = t0 + t1 + m[13]; + t3 = v[14]; + t2 = v[10]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[12]; + v[2] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[14] = t3; + t2 = t2 + t3; + v[10] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[6] = t1; + + t0 = v[3]; + t1 = v[7]; + t0 = t0 + t1 + m[11]; + t3 = v[15]; + t2 = v[11]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[14]; + v[3] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[15] = t3; + t2 = t2 + t3; + v[11] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[7] = t1; + + t0 = v[0]; + t1 = v[5]; + t0 = t0 + t1 + m[2]; + t3 = v[15]; + t2 = v[10]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[6]; + v[0] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[15] = t3; + t2 = t2 + t3; + v[10] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[5] = t1; + + t0 = v[1]; + t1 = v[6]; + t0 = t0 + t1 + m[5]; + t3 = v[12]; + t2 = v[11]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[10]; + v[1] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[12] = t3; + t2 = t2 + t3; + v[11] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[6] = t1; + + t0 = v[2]; + t1 = v[7]; + t0 = t0 + t1 + m[4]; + t3 = v[13]; + t2 = v[8]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[0]; + v[2] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[13] = t3; + t2 = t2 + t3; + v[8] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[7] = t1; + + t0 = v[3]; + t1 = v[4]; + t0 = t0 + t1 + m[15]; + t3 = v[14]; + t2 = v[9]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[8]; + v[3] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[14] = t3; + t2 = t2 + t3; + v[9] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[4] = t1; + +/* Round 4 */ + t0 = v[0]; + t1 = v[4]; + t0 = t0 + t1 + m[9]; + t3 = v[12]; + t2 = v[8]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[0]; + v[0] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[12] = t3; + t2 = t2 + t3; + v[8] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[4] = t1; + + t0 = v[1]; + t1 = v[5]; + t0 = t0 + t1 + m[5]; + t3 = v[13]; + t2 = v[9]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[7]; + v[1] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[13] = t3; + t2 = t2 + t3; + v[9] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[5] = t1; + + t0 = v[2]; + t1 = v[6]; + t0 = t0 + t1 + m[2]; + t3 = v[14]; + t2 = v[10]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[4]; + v[2] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[14] = t3; + t2 = t2 + t3; + v[10] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[6] = t1; + + t0 = v[3]; + t1 = v[7]; + t0 = t0 + t1 + m[10]; + t3 = v[15]; + t2 = v[11]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[15]; + v[3] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[15] = t3; + t2 = t2 + t3; + v[11] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[7] = t1; + + t0 = v[0]; + t1 = v[5]; + t0 = t0 + t1 + m[14]; + t3 = v[15]; + t2 = v[10]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[1]; + v[0] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[15] = t3; + t2 = t2 + t3; + v[10] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[5] = t1; + + t0 = v[1]; + t1 = v[6]; + t0 = t0 + t1 + m[11]; + t3 = v[12]; + t2 = v[11]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[12]; + v[1] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[12] = t3; + t2 = t2 + t3; + v[11] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[6] = t1; + + t0 = v[2]; + t1 = v[7]; + t0 = t0 + t1 + m[6]; + t3 = v[13]; + t2 = v[8]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[8]; + v[2] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[13] = t3; + t2 = t2 + t3; + v[8] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[7] = t1; + + t0 = v[3]; + t1 = v[4]; + t0 = t0 + t1 + m[3]; + t3 = v[14]; + t2 = v[9]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[13]; + v[3] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[14] = t3; + t2 = t2 + t3; + v[9] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[4] = t1; + +/* Round 5 */ + t0 = v[0]; + t1 = v[4]; + t0 = t0 + t1 + m[2]; + t3 = v[12]; + t2 = v[8]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[12]; + v[0] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[12] = t3; + t2 = t2 + t3; + v[8] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[4] = t1; + + t0 = v[1]; + t1 = v[5]; + t0 = t0 + t1 + m[6]; + t3 = v[13]; + t2 = v[9]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[10]; + v[1] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[13] = t3; + t2 = t2 + t3; + v[9] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[5] = t1; + + t0 = v[2]; + t1 = v[6]; + t0 = t0 + t1 + m[0]; + t3 = v[14]; + t2 = v[10]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[11]; + v[2] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[14] = t3; + t2 = t2 + t3; + v[10] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[6] = t1; + + t0 = v[3]; + t1 = v[7]; + t0 = t0 + t1 + m[8]; + t3 = v[15]; + t2 = v[11]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[3]; + v[3] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[15] = t3; + t2 = t2 + t3; + v[11] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[7] = t1; + + t0 = v[0]; + t1 = v[5]; + t0 = t0 + t1 + m[4]; + t3 = v[15]; + t2 = v[10]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[13]; + v[0] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[15] = t3; + t2 = t2 + t3; + v[10] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[5] = t1; + + t0 = v[1]; + t1 = v[6]; + t0 = t0 + t1 + m[7]; + t3 = v[12]; + t2 = v[11]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[5]; + v[1] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[12] = t3; + t2 = t2 + t3; + v[11] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[6] = t1; + + t0 = v[2]; + t1 = v[7]; + t0 = t0 + t1 + m[15]; + t3 = v[13]; + t2 = v[8]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[14]; + v[2] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[13] = t3; + t2 = t2 + t3; + v[8] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[7] = t1; + + t0 = v[3]; + t1 = v[4]; + t0 = t0 + t1 + m[1]; + t3 = v[14]; + t2 = v[9]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[9]; + v[3] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[14] = t3; + t2 = t2 + t3; + v[9] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[4] = t1; + +/* Round 6 */ + t0 = v[0]; + t1 = v[4]; + t0 = t0 + t1 + m[12]; + t3 = v[12]; + t2 = v[8]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[5]; + v[0] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[12] = t3; + t2 = t2 + t3; + v[8] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[4] = t1; + + t0 = v[1]; + t1 = v[5]; + t0 = t0 + t1 + m[1]; + t3 = v[13]; + t2 = v[9]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[15]; + v[1] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[13] = t3; + t2 = t2 + t3; + v[9] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[5] = t1; + + t0 = v[2]; + t1 = v[6]; + t0 = t0 + t1 + m[14]; + t3 = v[14]; + t2 = v[10]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[13]; + v[2] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[14] = t3; + t2 = t2 + t3; + v[10] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[6] = t1; + + t0 = v[3]; + t1 = v[7]; + t0 = t0 + t1 + m[4]; + t3 = v[15]; + t2 = v[11]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[10]; + v[3] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[15] = t3; + t2 = t2 + t3; + v[11] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[7] = t1; + + t0 = v[0]; + t1 = v[5]; + t0 = t0 + t1 + m[0]; + t3 = v[15]; + t2 = v[10]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[7]; + v[0] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[15] = t3; + t2 = t2 + t3; + v[10] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[5] = t1; + + t0 = v[1]; + t1 = v[6]; + t0 = t0 + t1 + m[6]; + t3 = v[12]; + t2 = v[11]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[3]; + v[1] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[12] = t3; + t2 = t2 + t3; + v[11] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[6] = t1; + + t0 = v[2]; + t1 = v[7]; + t0 = t0 + t1 + m[9]; + t3 = v[13]; + t2 = v[8]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[2]; + v[2] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[13] = t3; + t2 = t2 + t3; + v[8] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[7] = t1; + + t0 = v[3]; + t1 = v[4]; + t0 = t0 + t1 + m[8]; + t3 = v[14]; + t2 = v[9]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[11]; + v[3] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[14] = t3; + t2 = t2 + t3; + v[9] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[4] = t1; + +/* Round 7 */ + t0 = v[0]; + t1 = v[4]; + t0 = t0 + t1 + m[13]; + t3 = v[12]; + t2 = v[8]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[11]; + v[0] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[12] = t3; + t2 = t2 + t3; + v[8] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[4] = t1; + + t0 = v[1]; + t1 = v[5]; + t0 = t0 + t1 + m[7]; + t3 = v[13]; + t2 = v[9]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[14]; + v[1] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[13] = t3; + t2 = t2 + t3; + v[9] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[5] = t1; + + t0 = v[2]; + t1 = v[6]; + t0 = t0 + t1 + m[12]; + t3 = v[14]; + t2 = v[10]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[1]; + v[2] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[14] = t3; + t2 = t2 + t3; + v[10] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[6] = t1; + + t0 = v[3]; + t1 = v[7]; + t0 = t0 + t1 + m[3]; + t3 = v[15]; + t2 = v[11]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[9]; + v[3] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[15] = t3; + t2 = t2 + t3; + v[11] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[7] = t1; + + t0 = v[0]; + t1 = v[5]; + t0 = t0 + t1 + m[5]; + t3 = v[15]; + t2 = v[10]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[0]; + v[0] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[15] = t3; + t2 = t2 + t3; + v[10] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[5] = t1; + + t0 = v[1]; + t1 = v[6]; + t0 = t0 + t1 + m[15]; + t3 = v[12]; + t2 = v[11]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[4]; + v[1] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[12] = t3; + t2 = t2 + t3; + v[11] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[6] = t1; + + t0 = v[2]; + t1 = v[7]; + t0 = t0 + t1 + m[8]; + t3 = v[13]; + t2 = v[8]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[6]; + v[2] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[13] = t3; + t2 = t2 + t3; + v[8] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[7] = t1; + + t0 = v[3]; + t1 = v[4]; + t0 = t0 + t1 + m[2]; + t3 = v[14]; + t2 = v[9]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[10]; + v[3] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[14] = t3; + t2 = t2 + t3; + v[9] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[4] = t1; + +/* Round 8 */ + t0 = v[0]; + t1 = v[4]; + t0 = t0 + t1 + m[6]; + t3 = v[12]; + t2 = v[8]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[15]; + v[0] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[12] = t3; + t2 = t2 + t3; + v[8] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[4] = t1; + + t0 = v[1]; + t1 = v[5]; + t0 = t0 + t1 + m[14]; + t3 = v[13]; + t2 = v[9]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[9]; + v[1] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[13] = t3; + t2 = t2 + t3; + v[9] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[5] = t1; + + t0 = v[2]; + t1 = v[6]; + t0 = t0 + t1 + m[11]; + t3 = v[14]; + t2 = v[10]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[3]; + v[2] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[14] = t3; + t2 = t2 + t3; + v[10] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[6] = t1; + + t0 = v[3]; + t1 = v[7]; + t0 = t0 + t1 + m[0]; + t3 = v[15]; + t2 = v[11]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[8]; + v[3] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[15] = t3; + t2 = t2 + t3; + v[11] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[7] = t1; + + t0 = v[0]; + t1 = v[5]; + t0 = t0 + t1 + m[12]; + t3 = v[15]; + t2 = v[10]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[2]; + v[0] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[15] = t3; + t2 = t2 + t3; + v[10] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[5] = t1; + + t0 = v[1]; + t1 = v[6]; + t0 = t0 + t1 + m[13]; + t3 = v[12]; + t2 = v[11]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[7]; + v[1] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[12] = t3; + t2 = t2 + t3; + v[11] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[6] = t1; + + t0 = v[2]; + t1 = v[7]; + t0 = t0 + t1 + m[1]; + t3 = v[13]; + t2 = v[8]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[4]; + v[2] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[13] = t3; + t2 = t2 + t3; + v[8] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[7] = t1; + + t0 = v[3]; + t1 = v[4]; + t0 = t0 + t1 + m[10]; + t3 = v[14]; + t2 = v[9]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[5]; + v[3] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[14] = t3; + t2 = t2 + t3; + v[9] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[4] = t1; + +/* Round 9 */ + t0 = v[0]; + t1 = v[4]; + t0 = t0 + t1 + m[10]; + t3 = v[12]; + t2 = v[8]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[2]; + v[0] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[12] = t3; + t2 = t2 + t3; + v[8] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[4] = t1; + + t0 = v[1]; + t1 = v[5]; + t0 = t0 + t1 + m[8]; + t3 = v[13]; + t2 = v[9]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[4]; + v[1] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[13] = t3; + t2 = t2 + t3; + v[9] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[5] = t1; + + t0 = v[2]; + t1 = v[6]; + t0 = t0 + t1 + m[7]; + t3 = v[14]; + t2 = v[10]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[6]; + v[2] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[14] = t3; + t2 = t2 + t3; + v[10] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[6] = t1; + + t0 = v[3]; + t1 = v[7]; + t0 = t0 + t1 + m[1]; + t3 = v[15]; + t2 = v[11]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[5]; + v[3] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[15] = t3; + t2 = t2 + t3; + v[11] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[7] = t1; + + t0 = v[0]; + t1 = v[5]; + t0 = t0 + t1 + m[15]; + t3 = v[15]; + t2 = v[10]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[11]; + v[0] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[15] = t3; + t2 = t2 + t3; + v[10] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[5] = t1; + + t0 = v[1]; + t1 = v[6]; + t0 = t0 + t1 + m[9]; + t3 = v[12]; + t2 = v[11]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[14]; + v[1] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[12] = t3; + t2 = t2 + t3; + v[11] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[6] = t1; + + t0 = v[2]; + t1 = v[7]; + t0 = t0 + t1 + m[3]; + t3 = v[13]; + t2 = v[8]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[12]; + v[2] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[13] = t3; + t2 = t2 + t3; + v[8] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[7] = t1; + + t0 = v[3]; + t1 = v[4]; + t0 = t0 + t1 + m[13]; + t3 = v[14]; + t2 = v[9]; + t3 = ROTR32(t3 ^ t0, 16); + t2 = t2 + t3; + t1 = ROTR32(t1 ^ t2, 12); + t0 = t0 + t1 + m[0]; + v[3] = t0; + t3 = ROTR32(t3 ^ t0, 8); + v[14] = t3; + t2 = t2 + t3; + v[9] = t2; + t1 = ROTR32(t1 ^ t2, 7); + v[4] = t1; + + S->h[0] ^= v[0] ^ v[8]; + S->h[1] ^= v[1] ^ v[9]; + S->h[2] ^= v[2] ^ v[10]; + S->h[3] ^= v[3] ^ v[11]; + S->h[4] ^= v[4] ^ v[12]; + S->h[5] ^= v[5] ^ v[13]; + S->h[6] ^= v[6] ^ v[14]; + S->h[7] ^= v[7] ^ v[15]; } -static void blake2s_update(blake2s_state *S, const uchar *input, uint input_size) { +#endif /* ASM */ + +static void blake2s_update(blake2s_state *S, const uchar *input, + uint input_size) { uint left, fill; while(input_size > 0) { left = S->buflen; - fill = 2 * BLAKE2S_BLOCK_SIZE - left; + fill = 2 * BLOCK_SIZE - left; if(input_size > fill) { /* Buffer fill */ neoscrypt_copy(S->buf + left, input, fill); S->buflen += fill; /* Counter increment */ - S->t[0] += BLAKE2S_BLOCK_SIZE; + S->t[0] += BLOCK_SIZE; /* Compress */ - blake2s_compress(S, (uint *) S->buf); + blake2s_compress(S); /* Shift buffer left */ - neoscrypt_copy(S->buf, S->buf + BLAKE2S_BLOCK_SIZE, BLAKE2S_BLOCK_SIZE); - S->buflen -= BLAKE2S_BLOCK_SIZE; + neoscrypt_copy(S->buf, S->buf + BLOCK_SIZE, BLOCK_SIZE); + S->buflen -= BLOCK_SIZE; input += fill; input_size -= fill; } else { neoscrypt_copy(S->buf + left, input, input_size); - S->buflen += input_size; + S->buflen += input_size; /* Do not compress */ input += input_size; input_size = 0; @@ -908,9 +2237,9 @@ static void blake2s_update(blake2s_state *S, const uchar *input, uint input_size } } -static void neoscrypt_blake2s(const void *input, const uint input_size, const void *key, const uchar key_size, - void *output, const uchar output_size) { - uchar block[BLAKE2S_BLOCK_SIZE]; +void neoscrypt_blake2s(const void *input, const uint input_size, + const void *key, const uchar key_size, void *output, const uchar output_size) { + uchar block[BLOCK_SIZE]; blake2s_param P[1]; blake2s_state S[1]; @@ -921,52 +2250,60 @@ static void neoscrypt_blake2s(const void *input, const uint input_size, const vo P->fanout = 1; P->depth = 1; - neoscrypt_erase(S, 180); + neoscrypt_erase(S, 256); neoscrypt_copy(S, blake2s_IV, 32); neoscrypt_xor(S, P, 32); - neoscrypt_erase(block, BLAKE2S_BLOCK_SIZE); + neoscrypt_erase(block, BLOCK_SIZE); neoscrypt_copy(block, key, key_size); - blake2s_update(S, (uchar *) block, BLAKE2S_BLOCK_SIZE); + blake2s_update(S, (uchar *) block, BLOCK_SIZE); /* Update */ blake2s_update(S, (uchar *) input, input_size); /* Finish */ - if(S->buflen > BLAKE2S_BLOCK_SIZE) { - S->t[0] += BLAKE2S_BLOCK_SIZE; - blake2s_compress(S, (uint *) S->buf); - S->buflen -= BLAKE2S_BLOCK_SIZE; - neoscrypt_copy(S->buf, S->buf + BLAKE2S_BLOCK_SIZE, S->buflen); + if(S->buflen > BLOCK_SIZE) { + S->t[0] += BLOCK_SIZE; + blake2s_compress(S); + S->buflen -= BLOCK_SIZE; + neoscrypt_copy(S->buf, S->buf + BLOCK_SIZE, S->buflen); } S->t[0] += S->buflen; S->f[0] = ~0U; - neoscrypt_erase(S->buf + S->buflen, 2 * BLAKE2S_BLOCK_SIZE - S->buflen); - blake2s_compress(S, (uint *) S->buf); + neoscrypt_erase(S->buf + S->buflen, 2 * BLOCK_SIZE - S->buflen); + blake2s_compress(S); /* Write back */ neoscrypt_copy(output, S, output_size); } +#ifndef OPT #define FASTKDF_BUFFER_SIZE 256U -#define NEOSCRYPT_FASTKDF_STACK_SIZE 2 * FASTKDF_BUFFER_SIZE + BLAKE2S_BLOCK_SIZE + BLAKE2S_KEY_SIZE + BLAKE2S_OUT_SIZE + 0x40 - /* FastKDF, a fast buffered key derivation function: * FASTKDF_BUFFER_SIZE must be a power of 2; * password_len, salt_len and output_len should not exceed FASTKDF_BUFFER_SIZE; * prf_output_size must be <= prf_key_size; */ -static void neoscrypt_fastkdf(const uchar *password, uint password_len, const uchar *salt, uint salt_len, - uint N, uchar *output, uint output_len) { - const uint stack_align = 0x40, kdf_buf_size = FASTKDF_BUFFER_SIZE, - prf_input_size = BLAKE2S_BLOCK_SIZE, prf_key_size = BLAKE2S_KEY_SIZE, prf_output_size = BLAKE2S_OUT_SIZE; +void neoscrypt_fastkdf(const uchar *password, uint password_len, + const uchar *salt, uint salt_len, uint N, uchar *output, uint output_len) { + const size_t stack_align = 0x40; + const uint kdf_buf_size = FASTKDF_BUFFER_SIZE, + prf_input_size = 64, prf_key_size = 32, prf_output_size = 32; uint bufptr, a, b, i, j; uchar *A, *B, *prf_input, *prf_key, *prf_output; /* Align and set up the buffers in stack */ - uchar stack[NEOSCRYPT_FASTKDF_STACK_SIZE]; - A = &stack[stack_align & ~(stack_align - 1)]; + // No VLAs with VC ;-( +#ifndef _MSC_VER + uchar stack[2 * kdf_buf_size + prf_input_size + prf_key_size + + prf_output_size + stack_align]; +#else + uchar* stack = alloca(2 * kdf_buf_size + prf_input_size + prf_key_size + + prf_output_size + stack_align); +#endif + + A = (uchar *) (((size_t)stack & ~(stack_align - 1)) + stack_align); B = &A[kdf_buf_size + prf_input_size]; prf_output = &A[2 * kdf_buf_size + prf_input_size + prf_key_size]; @@ -1004,7 +2341,8 @@ static void neoscrypt_fastkdf(const uchar *password, uint password_len, const uc prf_key = &B[bufptr]; /* PRF */ - neoscrypt_blake2s(prf_input, prf_input_size, prf_key, prf_key_size, prf_output, prf_output_size); + neoscrypt_blake2s(prf_input, prf_input_size, prf_key, prf_key_size, + prf_output, prf_output_size); /* Calculate the next buffer pointer */ for(j = 0, bufptr = 0; j < prf_output_size; j++) @@ -1016,17 +2354,18 @@ static void neoscrypt_fastkdf(const uchar *password, uint password_len, const uc /* Head modified, tail updated */ if(bufptr < prf_key_size) - neoscrypt_copy(&B[kdf_buf_size + bufptr], &B[bufptr], MIN(prf_output_size, prf_key_size - bufptr)); - + neoscrypt_copy(&B[kdf_buf_size + bufptr], &B[bufptr], + MIN(prf_output_size, prf_key_size - bufptr)); /* Tail modified, head updated */ - if((kdf_buf_size - bufptr) < prf_output_size) - neoscrypt_copy(&B[0], &B[kdf_buf_size], prf_output_size - (kdf_buf_size - bufptr)); + else if((kdf_buf_size - bufptr) < prf_output_size) + neoscrypt_copy(&B[0], &B[kdf_buf_size], + prf_output_size - (kdf_buf_size - bufptr)); } /* Modify and copy into the output buffer */ if(output_len > kdf_buf_size) - output_len = kdf_buf_size; + output_len = kdf_buf_size; a = kdf_buf_size - bufptr; if(a >= output_len) { @@ -1041,6 +2380,111 @@ static void neoscrypt_fastkdf(const uchar *password, uint password_len, const uc } +#else + +#ifdef ASM + +extern void neoscrypt_fastkdf_opt(const uchar *password, const uchar *salt, + uchar *output, uint mode); + +#else + +/* Initialisation vector with a parameter block XOR'ed in */ +static const uint blake2s_IV_P_XOR[8] = { + 0x6B08C647, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, + 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19 +}; + +/* Performance optimised FastKDF with BLAKE2s integrated */ +void neoscrypt_fastkdf_opt(const uchar *password, const uchar *salt, + uchar *output, uint mode) { + const size_t stack_align = 0x40; + uint bufptr, output_len, i, j; + uchar *A, *B; + uint *S; + + /* Align and set up the buffers in stack */ + uchar stack[864 + stack_align]; + A = (uchar *) (((size_t)stack & ~(stack_align - 1)) + stack_align); + B = &A[320]; + S = (uint *) &A[608]; + + neoscrypt_copy(&A[0], &password[0], 80); + neoscrypt_copy(&A[80], &password[0], 80); + neoscrypt_copy(&A[160], &password[0], 80); + neoscrypt_copy(&A[240], &password[0], 16); + neoscrypt_copy(&A[256], &password[0], 64); + + if(!mode) { + output_len = 256; + neoscrypt_copy(&B[0], &salt[0], 80); + neoscrypt_copy(&B[80], &salt[0], 80); + neoscrypt_copy(&B[160], &salt[0], 80); + neoscrypt_copy(&B[240], &salt[0], 16); + neoscrypt_copy(&B[256], &salt[0], 32); + } else { + output_len = 32; + neoscrypt_copy(&B[0], &salt[0], 256); + neoscrypt_copy(&B[256], &salt[0], 32); + } + + for(i = 0, bufptr = 0; i < 32; i++) { + + /* BLAKE2s: initialise */ + neoscrypt_copy(&S[0], blake2s_IV_P_XOR, 32); + neoscrypt_erase(&S[8], 16); + + /* BLAKE2s: update key */ + neoscrypt_copy(&S[12], &B[bufptr], 32); + neoscrypt_erase(&S[20], 32); + + /* BLAKE2s: compress IV using key */ + S[8] = 64; + blake2s_compress((blake2s_state *) S); + + /* BLAKE2s: update input */ + neoscrypt_copy(&S[12], &A[bufptr], 64); + + /* BLAKE2s: compress again using input */ + S[8] = 128; + S[10] = ~0U; + blake2s_compress((blake2s_state *) S); + + for(j = 0, bufptr = 0; j < 8; j++) { + bufptr += S[j]; + bufptr += (S[j] >> 8); + bufptr += (S[j] >> 16); + bufptr += (S[j] >> 24); + } + bufptr &= 0xFF; + + neoscrypt_xor(&B[bufptr], &S[0], 32); + + if(bufptr < 32) + neoscrypt_copy(&B[256 + bufptr], &B[bufptr], 32 - bufptr); + else if(bufptr > 224) + neoscrypt_copy(&B[0], &B[256], bufptr - 224); + + } + + i = 256 - bufptr; + if(i >= output_len) { + neoscrypt_xor(&B[bufptr], &A[0], output_len); + neoscrypt_copy(&output[0], &B[bufptr], output_len); + } else { + neoscrypt_xor(&B[bufptr], &A[0], i); + neoscrypt_xor(&B[0], &A[i], output_len - i); + neoscrypt_copy(&output[0], &B[bufptr], i); + neoscrypt_copy(&output[i], &B[0], output_len - i); + } +} + +#endif /* ASM */ + +#endif /* !(OPT) */ + + +#ifndef ASM /* Configurable optimised block mixer */ static void neoscrypt_blkmix(uint *X, uint *Y, uint r, uint mixmode) { @@ -1058,60 +2502,62 @@ static void neoscrypt_blkmix(uint *X, uint *Y, uint r, uint mixmode) { Xc" = Yb; Xd" = Yd; */ if(r == 1) { - neoscrypt_blkxor(&X[0], &X[16], SCRYPT_BLOCK_SIZE); - if(mixer) - neoscrypt_chacha(&X[0], rounds); - else - neoscrypt_salsa(&X[0], rounds); - neoscrypt_blkxor(&X[16], &X[0], SCRYPT_BLOCK_SIZE); - if(mixer) - neoscrypt_chacha(&X[16], rounds); - else - neoscrypt_salsa(&X[16], rounds); + if(mixer) { + neoscrypt_blkxor(&X[0], &X[16], BLOCK_SIZE); + neoscrypt_chacha(&X[0], rounds); + neoscrypt_blkxor(&X[16], &X[0], BLOCK_SIZE); + neoscrypt_chacha(&X[16], rounds); + } else { + neoscrypt_blkxor(&X[0], &X[16], BLOCK_SIZE); + neoscrypt_salsa(&X[0], rounds); + neoscrypt_blkxor(&X[16], &X[0], BLOCK_SIZE); + neoscrypt_salsa(&X[16], rounds); + } return; } if(r == 2) { - neoscrypt_blkxor(&X[0], &X[48], SCRYPT_BLOCK_SIZE); - if(mixer) - neoscrypt_chacha(&X[0], rounds); - else - neoscrypt_salsa(&X[0], rounds); - neoscrypt_blkxor(&X[16], &X[0], SCRYPT_BLOCK_SIZE); - if(mixer) - neoscrypt_chacha(&X[16], rounds); - else - neoscrypt_salsa(&X[16], rounds); - neoscrypt_blkxor(&X[32], &X[16], SCRYPT_BLOCK_SIZE); - if(mixer) - neoscrypt_chacha(&X[32], rounds); - else - neoscrypt_salsa(&X[32], rounds); - neoscrypt_blkxor(&X[48], &X[32], SCRYPT_BLOCK_SIZE); - if(mixer) - neoscrypt_chacha(&X[48], rounds); - else - neoscrypt_salsa(&X[48], rounds); - neoscrypt_blkswp(&X[16], &X[32], SCRYPT_BLOCK_SIZE); + if(mixer) { + neoscrypt_blkxor(&X[0], &X[48], BLOCK_SIZE); + neoscrypt_chacha(&X[0], rounds); + neoscrypt_blkxor(&X[16], &X[0], BLOCK_SIZE); + neoscrypt_chacha(&X[16], rounds); + neoscrypt_blkxor(&X[32], &X[16], BLOCK_SIZE); + neoscrypt_chacha(&X[32], rounds); + neoscrypt_blkxor(&X[48], &X[32], BLOCK_SIZE); + neoscrypt_chacha(&X[48], rounds); + neoscrypt_blkswp(&X[16], &X[32], BLOCK_SIZE); + } else { + neoscrypt_blkxor(&X[0], &X[48], BLOCK_SIZE); + neoscrypt_salsa(&X[0], rounds); + neoscrypt_blkxor(&X[16], &X[0], BLOCK_SIZE); + neoscrypt_salsa(&X[16], rounds); + neoscrypt_blkxor(&X[32], &X[16], BLOCK_SIZE); + neoscrypt_salsa(&X[32], rounds); + neoscrypt_blkxor(&X[48], &X[32], BLOCK_SIZE); + neoscrypt_salsa(&X[48], rounds); + neoscrypt_blkswp(&X[16], &X[32], BLOCK_SIZE); + } return; } /* Reference code for any reasonable r */ for(i = 0; i < 2 * r; i++) { - if(i) neoscrypt_blkxor(&X[16 * i], &X[16 * (i - 1)], SCRYPT_BLOCK_SIZE); - else neoscrypt_blkxor(&X[0], &X[16 * (2 * r - 1)], SCRYPT_BLOCK_SIZE); + if(i) neoscrypt_blkxor(&X[16 * i], &X[16 * (i - 1)], BLOCK_SIZE); + else neoscrypt_blkxor(&X[0], &X[16 * (2 * r - 1)], BLOCK_SIZE); if(mixer) neoscrypt_chacha(&X[16 * i], rounds); else neoscrypt_salsa(&X[16 * i], rounds); - neoscrypt_blkcpy(&Y[16 * i], &X[16 * i], SCRYPT_BLOCK_SIZE); + neoscrypt_blkcpy(&Y[16 * i], &X[16 * i], BLOCK_SIZE); } for(i = 0; i < r; i++) - neoscrypt_blkcpy(&X[16 * i], &Y[16 * 2 * i], SCRYPT_BLOCK_SIZE); + neoscrypt_blkcpy(&X[16 * i], &Y[16 * 2 * i], BLOCK_SIZE); for(i = 0; i < r; i++) - neoscrypt_blkcpy(&X[16 * (i + r)], &Y[16 * (2 * i + 1)], SCRYPT_BLOCK_SIZE); + neoscrypt_blkcpy(&X[16 * (i + r)], &Y[16 * (2 * i + 1)], BLOCK_SIZE); } + /* NeoScrypt core engine: * p = 1, salt = password; * Basic customisation (required): @@ -1143,11 +2589,9 @@ static void neoscrypt_blkmix(uint *X, uint *Y, uint r, uint mixmode) { * ..... * 11110 = N of 2147483648; * profile bits 30 to 13 are reserved */ -void neoscrypt(const char *input, char *uoutput, int profile) { - const unsigned char *password = input; - const unsigned char *output = uoutput; - - uint N = 128, r = 2, dblmix = 1, mixmode = 0x14, stack_align = 0x40; +void neoscrypt(const uchar *password, uchar *output, uint profile) { + const size_t stack_align = 0x40; + uint N = 128, r = 2, dblmix = 1, mixmode = 0x14; uint kdf, i, j; uint *X, *Y, *Z, *V; @@ -1163,20 +2607,19 @@ void neoscrypt(const char *input, char *uoutput, int profile) { r = (1 << ((profile >> 5) & 0x7)); } - const int stack_size = (N + 3) * r * 2 * SCRYPT_BLOCK_SIZE + stack_align; - -#if !defined(_MSC_VER) - uchar stack[stack_size]; + // No VLAs with VC ;-( +#ifndef _MSC_VER + uchar stack[(N + 3) * r * 2 * BLOCK_SIZE + stack_align]; #else - uchar *stack = _alloca(stack_size); + uchar* stack = alloca((N + 3) * r * 2 * BLOCK_SIZE + stack_align); #endif - /* X = r * 2 * SCRYPT_BLOCK_SIZE */ - X = (uint *) &stack[stack_align & ~(stack_align - 1)]; + /* X = r * 2 * BLOCK_SIZE */ + X = (uint *) (((size_t)stack & ~(stack_align - 1)) + stack_align); /* Z is a copy of X for ChaCha */ Z = &X[32 * r]; /* Y is an X sized temporal space */ Y = &X[64 * r]; - /* V = N * r * 2 * SCRYPT_BLOCK_SIZE */ + /* V = N * r * 2 * BLOCK_SIZE */ V = &X[96 * r]; /* X = KDF(password, salt) */ @@ -1186,55 +2629,58 @@ void neoscrypt(const char *input, char *uoutput, int profile) { default: case(0x0): - neoscrypt_fastkdf(password, 80, password, 80, 32, (uchar *) X, r * 2 * SCRYPT_BLOCK_SIZE); +#ifdef OPT + neoscrypt_fastkdf_opt(password, password, (uchar *) X, 0); +#else + neoscrypt_fastkdf(password, 80, password, 80, 32, + (uchar *) X, r * 2 * BLOCK_SIZE); +#endif break; -#if (NEOSCRYPT_SHA256) +#ifdef SHA256 case(0x1): - neoscrypt_pbkdf2_sha256(password, 80, password, 80, 1, (uchar *) X, r * 2 * SCRYPT_BLOCK_SIZE); + neoscrypt_pbkdf2_sha256(password, 80, password, 80, 1, + (uchar *) X, r * 2 * BLOCK_SIZE); break; #endif -#if (NEOSCRYPT_BLAKE256) +#ifdef BLAKE256 case(0x2): - neoscrypt_pbkdf2_blake256(password, 80, password, 80, 1, (uchar *) X, r * 2 * SCRYPT_BLOCK_SIZE); + neoscrypt_pbkdf2_blake256(password, 80, password, 80, 1, + (uchar *) X, r * 2 * BLOCK_SIZE); break; #endif } - /* Process ChaCha 1st, Salsa 2nd and XOR them into PBKDF2; otherwise Salsa only */ + /* Process ChaCha 1st, Salsa 2nd and XOR them into FastKDF; otherwise Salsa only */ if(dblmix) { /* blkcpy(Z, X) */ - neoscrypt_blkcpy(&Z[0], &X[0], r * 2 * SCRYPT_BLOCK_SIZE); + neoscrypt_blkcpy(&Z[0], &X[0], r * 2 * BLOCK_SIZE); /* Z = SMix(Z) */ for(i = 0; i < N; i++) { /* blkcpy(V, Z) */ - neoscrypt_blkcpy(&V[i * (32 * r)], &Z[0], r * 2 * SCRYPT_BLOCK_SIZE); + neoscrypt_blkcpy(&V[i * (32 * r)], &Z[0], r * 2 * BLOCK_SIZE); /* blkmix(Z, Y) */ neoscrypt_blkmix(&Z[0], &Y[0], r, (mixmode | 0x0100)); } + for(i = 0; i < N; i++) { /* integerify(Z) mod N */ j = (32 * r) * (Z[16 * (2 * r - 1)] & (N - 1)); /* blkxor(Z, V) */ - neoscrypt_blkxor(&Z[0], &V[j], r * 2 * SCRYPT_BLOCK_SIZE); + neoscrypt_blkxor(&Z[0], &V[j], r * 2 * BLOCK_SIZE); /* blkmix(Z, Y) */ neoscrypt_blkmix(&Z[0], &Y[0], r, (mixmode | 0x0100)); } } -#if (ASM) - /* Must be called before and after SSE2 Salsa */ - neoscrypt_salsa_tangle(&X[0], r * 2); -#endif - /* X = SMix(X) */ for(i = 0; i < N; i++) { /* blkcpy(V, X) */ - neoscrypt_blkcpy(&V[i * (32 * r)], &X[0], r * 2 * SCRYPT_BLOCK_SIZE); + neoscrypt_blkcpy(&V[i * (32 * r)], &X[0], r * 2 * BLOCK_SIZE); /* blkmix(X, Y) */ neoscrypt_blkmix(&X[0], &Y[0], r, mixmode); } @@ -1242,36 +2688,39 @@ void neoscrypt(const char *input, char *uoutput, int profile) { /* integerify(X) mod N */ j = (32 * r) * (X[16 * (2 * r - 1)] & (N - 1)); /* blkxor(X, V) */ - neoscrypt_blkxor(&X[0], &V[j], r * 2 * SCRYPT_BLOCK_SIZE); + neoscrypt_blkxor(&X[0], &V[j], r * 2 * BLOCK_SIZE); /* blkmix(X, Y) */ neoscrypt_blkmix(&X[0], &Y[0], r, mixmode); } -#if (ASM) - neoscrypt_salsa_tangle(&X[0], r * 2); -#endif - if(dblmix) /* blkxor(X, Z) */ - neoscrypt_blkxor(&X[0], &Z[0], r * 2 * SCRYPT_BLOCK_SIZE); + neoscrypt_blkxor(&X[0], &Z[0], r * 2 * BLOCK_SIZE); /* output = KDF(password, X) */ switch(kdf) { default: case(0x0): - neoscrypt_fastkdf(password, 80, (uchar *) X, r * 2 * SCRYPT_BLOCK_SIZE, 32, output, 32); +#ifdef OPT + neoscrypt_fastkdf_opt(password, (uchar *) X, output, 1); +#else + neoscrypt_fastkdf(password, 80, (uchar *) X, + r * 2 * BLOCK_SIZE, 32, output, 32); +#endif break; -#if (NEOSCRYPT_SHA256) +#ifdef SHA256 case(0x1): - neoscrypt_pbkdf2_sha256(password, 80, (uchar *) X, r * 2 * SCRYPT_BLOCK_SIZE, 1, output, 32); + neoscrypt_pbkdf2_sha256(password, 80, (uchar *) X, + r * 2 * BLOCK_SIZE, 1, output, 32); break; #endif -#if (NEOSCRYPT_BLAKE256) +#ifdef BLAKE256 case(0x2): - neoscrypt_pbkdf2_blake256(password, 80, (uchar *) X, r * 2 * SCRYPT_BLOCK_SIZE, 1, output, 32); + neoscrypt_pbkdf2_blake256(password, 80, (uchar *) X, + r * 2 * BLOCK_SIZE, 1, output, 32); break; #endif @@ -1279,119 +2728,552 @@ void neoscrypt(const char *input, char *uoutput, int profile) { } +#endif /* !(ASM) */ + + +#if defined(ASM) && defined(MINER_4WAY) + +extern void neoscrypt_xor_salsa_4way(uint *X, uint *X0, uint *Y, uint double_rounds); +extern void neoscrypt_xor_chacha_4way(uint *Z, uint *Z0, uint *Y, uint double_rounds); + +#if (1) + +extern void neoscrypt_blkcpy(void *dstp, const void *srcp, uint len); +extern void neoscrypt_blkswp(void *blkAp, void *blkBp, uint len); +extern void neoscrypt_blkxor(void *dstp, const void *srcp, uint len); + +extern void neoscrypt_pack_4way(void *dstp, const void *srcp, uint len); +extern void neoscrypt_unpack_4way(void *dstp, const void *srcp, uint len); +extern void neoscrypt_xor_4way(void *dstp, const void *srcAp, + const void *srcBp, const void *srcCp, const void *srcDp, uint len); + +#else + +/* The following code is for reference only */ + +static void neoscrypt_blkcpy(void *dstp, const void *srcp, uint len) { + size_t *dst = (size_t *) dstp; + size_t *src = (size_t *) srcp; + uint i; + + for(i = 0; i < (len / sizeof(size_t)); i += 4) { + dst[i] = src[i]; + dst[i + 1] = src[i + 1]; + dst[i + 2] = src[i + 2]; + dst[i + 3] = src[i + 3]; + } +} + +static void neoscrypt_blkswp(void *blkAp, void *blkBp, uint len) { + size_t *blkA = (size_t *) blkAp; + size_t *blkB = (size_t *) blkBp; + register size_t t0, t1, t2, t3; + uint i; + + for(i = 0; i < (len / sizeof(size_t)); i += 4) { + t0 = blkA[i]; + t1 = blkA[i + 1]; + t2 = blkA[i + 2]; + t3 = blkA[i + 3]; + blkA[i] = blkB[i]; + blkA[i + 1] = blkB[i + 1]; + blkA[i + 2] = blkB[i + 2]; + blkA[i + 3] = blkB[i + 3]; + blkB[i] = t0; + blkB[i + 1] = t1; + blkB[i + 2] = t2; + blkB[i + 3] = t3; + } +} + +static void neoscrypt_blkxor(void *dstp, const void *srcp, uint len) { + size_t *dst = (size_t *) dstp; + size_t *src = (size_t *) srcp; + uint i; + + for(i = 0; i < (len / sizeof(size_t)); i += 4) { + dst[i] ^= src[i]; + dst[i + 1] ^= src[i + 1]; + dst[i + 2] ^= src[i + 2]; + dst[i + 3] ^= src[i + 3]; + } +} + + +static void neoscrypt_pack_4way(void *dstp, const void *srcp, uint len) { + uint *dst = (uint *) dstp; + uint *src = (uint *) srcp; + uint i, j; + + len >>= 4; + + for(i = 0, j = 0; j < len; i += 4, j++) { + dst[i] = src[j]; + dst[i + 1] = src[j + len]; + dst[i + 2] = src[j + 2 * len]; + dst[i + 3] = src[j + 3 * len]; + } +} -#if (NEOSCRYPT_TEST) +static void neoscrypt_unpack_4way(void *dstp, const void *srcp, uint len) { + uint *dst = (uint *) dstp; + uint *src = (uint *) srcp; + uint i, j; -#include + len >>= 4; -int main() { - uint prf_input_len = 64, prf_key_len = 32, prf_output_len = 32; - uint kdf_input_len = 80, kdf_output_len = 256, N = 32; - uint neoscrypt_output_len = 32; - uchar input[kdf_input_len], output[kdf_output_len]; + for(i = 0, j = 0; j < len; i += 4, j++) { + dst[j] = src[i]; + dst[j + len] = src[i + 1]; + dst[j + 2 * len] = src[i + 2]; + dst[j + 3 * len] = src[i + 3]; + } +} + +static void neoscrypt_xor_4way(void *dstp, const void *srcAp, + const void *srcBp, const void *srcCp, const void *srcDp, uint len) { + uint *dst = (uint *) dstp; + uint *srcA = (uint *) srcAp; + uint *srcB = (uint *) srcBp; + uint *srcC = (uint *) srcCp; + uint *srcD = (uint *) srcDp; uint i; - bool fail; - for(i = 0; i < kdf_input_len; i++) { - input[i] = i; + for(i = 0; i < (len >> 2); i += 4) { + dst[i] ^= srcA[i]; + dst[i + 1] ^= srcB[i + 1]; + dst[i + 2] ^= srcC[i + 2]; + dst[i + 3] ^= srcD[i + 3]; } +} - neoscrypt_blake2s(input, prf_input_len, input, prf_key_len, output, prf_output_len); +#endif - uchar blake2s_ref[32] = { - 0x89, 0x75, 0xB0, 0x57, 0x7F, 0xD3, 0x55, 0x66, - 0xD7, 0x50, 0xB3, 0x62, 0xB0, 0x89, 0x7A, 0x26, - 0xC3, 0x99, 0x13, 0x6D, 0xF0, 0x7B, 0xAB, 0xAB, - 0xBD, 0xE6, 0x20, 0x3F, 0xF2, 0x95, 0x4E, 0xD4 }; - for(i = 0, fail = 0; i < prf_output_len; i++) { - if(output[i] != blake2s_ref[i]) { - fail = 1; - break; - } +/* 4-way NeoScrypt(128, 2, 1) with Salsa20/20 and ChaCha20/20 */ +void neoscrypt_4way(const uchar *password, uchar *output, uchar *scratchpad) { + const uint N = 128, r = 2, double_rounds = 10; + uint *X, *Z, *V, *Y, *P; + uint i, j0, j1, j2, j3, k; + + /* 2 * BLOCK_SIZE compacted to 128 below */; + + /* Scratchpad size is 4 * ((N + 3) * r * 128 + 80) bytes */ + + X = (uint *) &scratchpad[0]; + Z = &X[4 * 32 * r]; + V = &X[4 * 64 * r]; + /* Y is a temporary work space */ + Y = &X[4 * (N + 2) * 32 * r]; + /* P is a set of passwords 80 bytes each */ + P = &X[4 * (N + 3) * 32 * r]; + + /* Load the password and increment nonces */ + for(k = 0; k < 4; k++) { + neoscrypt_copy(&P[k * 20], password, 80); + P[(k + 1) * 20 - 1] += k; } - if(fail) { - printf("BLAKE2s integrity test failed!\n"); - return(1); - } else { - printf("BLAKE2s integrity test passed.\n"); + neoscrypt_fastkdf_4way((uchar *) &P[0], (uchar *) &P[0], (uchar *) &Y[0], + (uchar *) &scratchpad[0], 0); + + neoscrypt_pack_4way(&X[0], &Y[0], 4 * r * 128); + + neoscrypt_blkcpy(&Z[0], &X[0], 4 * r * 128); + + for(i = 0; i < N; i++) { + neoscrypt_blkcpy(&V[i * r * 128], &Z[0], 4 * r * 128); + neoscrypt_xor_chacha_4way(&Z[0], &Z[192], &Y[0], double_rounds); + neoscrypt_xor_chacha_4way(&Z[64], &Z[0], &Y[0], double_rounds); + neoscrypt_xor_chacha_4way(&Z[128], &Z[64], &Y[0], double_rounds); + neoscrypt_xor_chacha_4way(&Z[192], &Z[128], &Y[0], double_rounds); + neoscrypt_blkswp(&Z[64], &Z[128], r * 128); } - neoscrypt_fastkdf(input, kdf_input_len, input, kdf_input_len, N, output, kdf_output_len); - - uchar fastkdf_ref[256] = { - 0xCC, 0xBC, 0x19, 0x71, 0xEC, 0x44, 0xE3, 0x17, - 0xB3, 0xC9, 0xDE, 0x16, 0x76, 0x02, 0x60, 0xB8, - 0xE2, 0xD4, 0x79, 0xB6, 0x88, 0xCA, 0xB5, 0x4A, - 0xCF, 0x6E, 0x0E, 0x9A, 0xAE, 0x48, 0x78, 0x12, - 0xA1, 0x95, 0x1E, 0xE1, 0xD1, 0x0A, 0xC2, 0x94, - 0x1F, 0x0A, 0x39, 0x73, 0xFE, 0xA4, 0xCD, 0x87, - 0x4B, 0x38, 0x54, 0x72, 0xB5, 0x53, 0xC3, 0xEA, - 0xC1, 0x26, 0x8D, 0xA7, 0xFF, 0x3F, 0xC1, 0x79, - 0xA6, 0xFF, 0x96, 0x54, 0x29, 0x05, 0xC0, 0x22, - 0x90, 0xDB, 0x53, 0x87, 0x2D, 0x29, 0x00, 0xA6, - 0x14, 0x16, 0x38, 0x63, 0xDA, 0xBC, 0x0E, 0x99, - 0x68, 0xB3, 0x98, 0x92, 0x42, 0xE3, 0xF6, 0xB4, - 0x19, 0xE3, 0xE3, 0xF6, 0x8E, 0x67, 0x47, 0x7B, - 0xB6, 0xFB, 0xEA, 0xCE, 0x6D, 0x0F, 0xAF, 0xF6, - 0x19, 0x43, 0x8D, 0xF7, 0x3E, 0xB5, 0xFB, 0xA3, - 0x64, 0x5E, 0xD2, 0x72, 0x80, 0x6B, 0x39, 0x93, - 0xB7, 0x80, 0x04, 0xCB, 0xF5, 0xC2, 0x61, 0xB1, - 0x90, 0x4E, 0x2B, 0x02, 0x57, 0x53, 0x77, 0x16, - 0x6A, 0x52, 0xBD, 0xD1, 0x62, 0xEC, 0xA1, 0xCB, - 0x89, 0x03, 0x29, 0xA2, 0x02, 0x5C, 0x9A, 0x62, - 0x99, 0x44, 0x54, 0xEA, 0x44, 0x91, 0x27, 0x3A, - 0x50, 0x82, 0x62, 0x03, 0x99, 0xB3, 0xFA, 0xF7, - 0xD4, 0x13, 0x47, 0x61, 0xFB, 0x0A, 0xE7, 0x81, - 0x61, 0x57, 0x58, 0x4C, 0x69, 0x4E, 0x67, 0x0A, - 0xC1, 0x21, 0xA7, 0xD2, 0xF6, 0x6D, 0x2F, 0x10, - 0x01, 0xFB, 0xA5, 0x47, 0x2C, 0xE5, 0x15, 0xD7, - 0x6A, 0xEF, 0xC9, 0xE2, 0xC2, 0x88, 0xA2, 0x3B, - 0x6C, 0x8D, 0xBB, 0x26, 0xE7, 0xC4, 0x15, 0xEC, - 0x5E, 0x5D, 0x74, 0x79, 0xBD, 0x81, 0x35, 0xA1, - 0x42, 0x27, 0xEB, 0x57, 0xCF, 0xF6, 0x2E, 0x51, - 0x90, 0xFD, 0xD9, 0xE4, 0x53, 0x6E, 0x12, 0xA1, - 0x99, 0x79, 0x4D, 0x29, 0x6F, 0x5B, 0x4D, 0x9A }; - - for(i = 0, fail = 0; i < kdf_output_len; i++) { - if(output[i] != fastkdf_ref[i]) { - fail = 1; - break; - } + for(i = 0; i < N; i++) { + j0 = (4 * r * 32) * (Z[64 * (2 * r - 1)] & (N - 1)); + j1 = (4 * r * 32) * (Z[1 + (64 * (2 * r - 1))] & (N - 1)); + j2 = (4 * r * 32) * (Z[2 + (64 * (2 * r - 1))] & (N - 1)); + j3 = (4 * r * 32) * (Z[3 + (64 * (2 * r - 1))] & (N - 1)); + neoscrypt_xor_4way(&Z[0], + &V[j0], &V[j1], &V[j2], &V[j3], 4 * r * 128); + neoscrypt_xor_chacha_4way(&Z[0], &Z[192], &Y[0], double_rounds); + neoscrypt_xor_chacha_4way(&Z[64], &Z[0], &Y[0], double_rounds); + neoscrypt_xor_chacha_4way(&Z[128], &Z[64], &Y[0], double_rounds); + neoscrypt_xor_chacha_4way(&Z[192], &Z[128], &Y[0], double_rounds); + neoscrypt_blkswp(&Z[64], &Z[128], 256); } - if(fail) { - printf("FastKDF integrity test failed!\n"); - return(1); - } else { - printf("FastKDF integrity test passed.\n"); + for(i = 0; i < N; i++) { + neoscrypt_blkcpy(&V[i * r * 128], &X[0], 4 * r * 128); + neoscrypt_xor_salsa_4way(&X[0], &X[192], &Y[0], double_rounds); + neoscrypt_xor_salsa_4way(&X[64], &X[0], &Y[0], double_rounds); + neoscrypt_xor_salsa_4way(&X[128], &X[64], &Y[0], double_rounds); + neoscrypt_xor_salsa_4way(&X[192], &X[128], &Y[0], double_rounds); + neoscrypt_blkswp(&X[64], &X[128], r * 128); } - neoscrypt(input, output, 0x80000620); + for(i = 0; i < N; i++) { + j0 = (4 * r * 32) * (X[64 * (2 * r - 1)] & (N - 1)); + j1 = (4 * r * 32) * (X[1 + (64 * (2 * r - 1))] & (N - 1)); + j2 = (4 * r * 32) * (X[2 + (64 * (2 * r - 1))] & (N - 1)); + j3 = (4 * r * 32) * (X[3 + (64 * (2 * r - 1))] & (N - 1)); + neoscrypt_xor_4way(&X[0], + &V[j0], &V[j1], &V[j2], &V[j3], 4 * r * 128); + neoscrypt_xor_salsa_4way(&X[0], &X[192], &Y[0], double_rounds); + neoscrypt_xor_salsa_4way(&X[64], &X[0], &Y[0], double_rounds); + neoscrypt_xor_salsa_4way(&X[128], &X[64], &Y[0], double_rounds); + neoscrypt_xor_salsa_4way(&X[192], &X[128], &Y[0], double_rounds); + neoscrypt_blkswp(&X[64], &X[128], r * 128); + } - uchar neoscrypt_ref[32] = { - 0x72, 0x58, 0x96, 0x1A, 0xFB, 0x33, 0xFD, 0x12, - 0xD0, 0x0C, 0xAC, 0xB8, 0xD6, 0x3F, 0x4F, 0x4F, - 0x52, 0xBB, 0x69, 0x17, 0x04, 0x38, 0x65, 0xDD, - 0x24, 0xA0, 0x8F, 0x57, 0x88, 0x53, 0x12, 0x2D }; + neoscrypt_blkxor(&X[0], &Z[0], 4 * r * 128); - for(i = 0, fail = 0; i < neoscrypt_output_len; i++) { - if(output[i] != neoscrypt_ref[i]) { - fail = 1; - break; + neoscrypt_unpack_4way(&Y[0], &X[0], 4 * r * 128); + + neoscrypt_fastkdf_4way((uchar *) &P[0], (uchar *) &Y[0], (uchar *) &output[0], + (uchar *) &scratchpad[0], 1); +} + +#ifdef SHA256 +/* 4-way Scrypt(1024, 1, 1) with Salsa20/8 */ +void scrypt_4way(const uchar *password, uchar *output, uchar *scratchpad) { + const uint N = 1024, r = 1, double_rounds = 4; + uint *X, *V, *Y, *P; + uint i, j0, j1, j2, j3, k; + + /* Scratchpad size is 4 * ((N + 2) * r * 128 + 80) bytes */ + + X = (uint *) &scratchpad[0]; + V = &X[4 * 32 * r]; + Y = &X[4 * (N + 1) * 32 * r]; + P = &X[4 * (N + 2) * 32 * r]; + + for(k = 0; k < 4; k++) { + neoscrypt_copy(&P[k * 20], password, 80); + P[(k + 1) * 20 - 1] += k; + } + + for(k = 0; k < 4; k++) + neoscrypt_pbkdf2_sha256((uchar *) &P[k * 20], 80, + (uchar *) &P[k * 20], 80, 1, + (uchar *) &Y[k * r * 32], r * 128); + + neoscrypt_pack_4way(&X[0], &Y[0], 4 * r * 128); + + for(i = 0; i < N; i++) { + neoscrypt_blkcpy(&V[i * r * 128], &X[0], 4 * r * 128); + neoscrypt_xor_salsa_4way(&X[0], &X[64], &Y[0], double_rounds); + neoscrypt_xor_salsa_4way(&X[64], &X[0], &Y[0], double_rounds); + } + + for(i = 0; i < N; i++) { + j0 = (4 * r * 32) * (X[64 * (2 * r - 1)] & (N - 1)); + j1 = (4 * r * 32) * (X[1 + (64 * (2 * r - 1))] & (N - 1)); + j2 = (4 * r * 32) * (X[2 + (64 * (2 * r - 1))] & (N - 1)); + j3 = (4 * r * 32) * (X[3 + (64 * (2 * r - 1))] & (N - 1)); + neoscrypt_xor_4way(&X[0], + &V[j0], &V[j1], &V[j2], &V[j3], 4 * r * 128); + neoscrypt_xor_salsa_4way(&X[0], &X[64], &Y[0], double_rounds); + neoscrypt_xor_salsa_4way(&X[64], &X[0], &Y[0], double_rounds); + } + + neoscrypt_unpack_4way(&Y[0], &X[0], 4 * r * 128); + + for(k = 0; k < 4; k++) + neoscrypt_pbkdf2_sha256((uchar *) &P[k * 20], 80, + (uchar *) &Y[k * r * 32], r * 128, 1, + (uchar *) &output[k * 32], 32); +} +#endif /* SHA256 */ + + +extern void blake2s_compress_4way(void *T); + +/* 4-way initialisation vector with a parameter block XOR'ed in */ +static const uint blake2s_IV_P_XOR_4way[32] = { + 0x6B08C647, 0x6B08C647, 0x6B08C647, 0x6B08C647, + 0xBB67AE85, 0xBB67AE85, 0xBB67AE85, 0xBB67AE85, + 0x3C6EF372, 0x3C6EF372, 0x3C6EF372, 0x3C6EF372, + 0xA54FF53A, 0xA54FF53A, 0xA54FF53A, 0xA54FF53A, + 0x510E527F, 0x510E527F, 0x510E527F, 0x510E527F, + 0x9B05688C, 0x9B05688C, 0x9B05688C, 0x9B05688C, + 0x1F83D9AB, 0x1F83D9AB, 0x1F83D9AB, 0x1F83D9AB, + 0x5BE0CD19, 0x5BE0CD19, 0x5BE0CD19, 0x5BE0CD19 +}; + +/* 4-way BLAKE2s implementation */ +void neoscrypt_blake2s_4way(const uchar *input, const uchar *key, uchar *output) { + const size_t stack_align = 0x40; + uint *T; + + /* Align and set up the buffer in stack */ + uchar stack[704 + stack_align]; + T = (uint *) (((size_t)stack & ~(stack_align - 1)) + stack_align); + + /* Initialise */ + neoscrypt_copy(&T[0], blake2s_IV_P_XOR_4way, 128); + neoscrypt_erase(&T[32], 64); + + /* Update keys */ + neoscrypt_pack_4way(&T[48], &key[0], 128); + neoscrypt_erase(&T[80], 128); + + /* Compress IVs using keys */ + T[32] = 64; + T[33] = 64; + T[34] = 64; + T[35] = 64; + blake2s_compress_4way(&T[0]); + + /* Update inputs */ + neoscrypt_pack_4way(&T[48], &input[0], 256); + + /* Compress using inputs */ + T[32] = 128; + T[33] = 128; + T[34] = 128; + T[35] = 128; + T[40] = ~0U; + T[41] = ~0U; + T[42] = ~0U; + T[43] = ~0U; + blake2s_compress_4way(&T[0]); + + neoscrypt_unpack_4way(&output[0], &T[0], 128); +} + + +/* 4-way FastKDF with BLAKE2s integrated */ +void neoscrypt_fastkdf_4way(const uchar *password, const uchar *salt, + uchar *output, uchar *scratchpad, uint mode) { + uint bufptr_a = 0, bufptr_b = 0, bufptr_c = 0, bufptr_d = 0; + uint output_len, i, j; + uint *T; + uchar *Aa, *Ab, *Ac, *Ad; + uchar *Ba, *Bb, *Bc, *Bd; + + T = (uint *) &scratchpad[0]; + Aa = (uchar *) &T[176]; + Ab = (uchar *) &T[256]; + Ac = (uchar *) &T[336]; + Ad = (uchar *) &T[416]; + Ba = (uchar *) &T[496]; + Bb = (uchar *) &T[568]; + Bc = (uchar *) &T[640]; + Bd = (uchar *) &T[712]; + + neoscrypt_copy(&Aa[0], &password[0], 80); + neoscrypt_copy(&Aa[80], &password[0], 80); + neoscrypt_copy(&Aa[160], &password[0], 80); + neoscrypt_copy(&Aa[240], &password[0], 16); + neoscrypt_copy(&Aa[256], &password[0], 64); + neoscrypt_copy(&Ab[0], &password[80], 80); + neoscrypt_copy(&Ab[80], &password[80], 80); + neoscrypt_copy(&Ab[160], &password[80], 80); + neoscrypt_copy(&Ab[240], &password[80], 16); + neoscrypt_copy(&Ab[256], &password[80], 64); + neoscrypt_copy(&Ac[0], &password[160], 80); + neoscrypt_copy(&Ac[80], &password[160], 80); + neoscrypt_copy(&Ac[160], &password[160], 80); + neoscrypt_copy(&Ac[240], &password[160], 16); + neoscrypt_copy(&Ac[256], &password[160], 64); + neoscrypt_copy(&Ad[0], &password[240], 80); + neoscrypt_copy(&Ad[80], &password[240], 80); + neoscrypt_copy(&Ad[160], &password[240], 80); + neoscrypt_copy(&Ad[240], &password[240], 16); + neoscrypt_copy(&Ad[256], &password[240], 64); + + if(!mode) { + output_len = 256; + neoscrypt_copy(&Ba[0], &salt[0], 80); + neoscrypt_copy(&Ba[80], &salt[0], 80); + neoscrypt_copy(&Ba[160], &salt[0], 80); + neoscrypt_copy(&Ba[240], &salt[0], 16); + neoscrypt_copy(&Ba[256], &salt[0], 32); + neoscrypt_copy(&Bb[0], &salt[80], 80); + neoscrypt_copy(&Bb[80], &salt[80], 80); + neoscrypt_copy(&Bb[160], &salt[80], 80); + neoscrypt_copy(&Bb[240], &salt[80], 16); + neoscrypt_copy(&Bb[256], &salt[80], 32); + neoscrypt_copy(&Bc[0], &salt[160], 80); + neoscrypt_copy(&Bc[80], &salt[160], 80); + neoscrypt_copy(&Bc[160], &salt[160], 80); + neoscrypt_copy(&Bc[240], &salt[160], 16); + neoscrypt_copy(&Bc[256], &salt[160], 32); + neoscrypt_copy(&Bd[0], &salt[240], 80); + neoscrypt_copy(&Bd[80], &salt[240], 80); + neoscrypt_copy(&Bd[160], &salt[240], 80); + neoscrypt_copy(&Bd[240], &salt[240], 16); + neoscrypt_copy(&Bd[256], &salt[240], 32); + } else { + output_len = 32; + neoscrypt_copy(&Ba[0], &salt[0], 256); + neoscrypt_copy(&Ba[256], &salt[0], 32); + neoscrypt_copy(&Bb[0], &salt[256], 256); + neoscrypt_copy(&Bb[256], &salt[256], 32); + neoscrypt_copy(&Bc[0], &salt[512], 256); + neoscrypt_copy(&Bc[256], &salt[512], 32); + neoscrypt_copy(&Bd[0], &salt[768], 256); + neoscrypt_copy(&Bd[256], &salt[768], 32); + } + + for(i = 0; i < 32; i++) { + + /* BLAKE2s: initialise */ + neoscrypt_copy(&T[0], blake2s_IV_P_XOR_4way, 128); + neoscrypt_erase(&T[32], 64); + + /* BLAKE2s: update keys */ + for(j = 0; j < 32; j += 8) { + T[j + 48] = *((uint *) &Ba[bufptr_a + j]); + T[j + 49] = *((uint *) &Bb[bufptr_b + j]); + T[j + 50] = *((uint *) &Bc[bufptr_c + j]); + T[j + 51] = *((uint *) &Bd[bufptr_d + j]); + T[j + 52] = *((uint *) &Ba[bufptr_a + j + 4]); + T[j + 53] = *((uint *) &Bb[bufptr_b + j + 4]); + T[j + 54] = *((uint *) &Bc[bufptr_c + j + 4]); + T[j + 55] = *((uint *) &Bd[bufptr_d + j + 4]); + } + neoscrypt_erase(&T[80], 128); + + /* BLAKE2s: compress IVs using keys */ + T[32] = 64; + T[33] = 64; + T[34] = 64; + T[35] = 64; + blake2s_compress_4way(&T[0]); + + /* BLAKE2s: update inputs */ + for(j = 0; j < 64; j += 8) { + T[j + 48] = *((uint *) &Aa[bufptr_a + j]); + T[j + 49] = *((uint *) &Ab[bufptr_b + j]); + T[j + 50] = *((uint *) &Ac[bufptr_c + j]); + T[j + 51] = *((uint *) &Ad[bufptr_d + j]); + T[j + 52] = *((uint *) &Aa[bufptr_a + j + 4]); + T[j + 53] = *((uint *) &Ab[bufptr_b + j + 4]); + T[j + 54] = *((uint *) &Ac[bufptr_c + j + 4]); + T[j + 55] = *((uint *) &Ad[bufptr_d + j + 4]); + } + + /* BLAKE2s: compress using inputs */ + T[32] = 128; + T[33] = 128; + T[34] = 128; + T[35] = 128; + T[40] = ~0U; + T[41] = ~0U; + T[42] = ~0U; + T[43] = ~0U; + blake2s_compress_4way(&T[0]); + + bufptr_a = 0; + bufptr_b = 0; + bufptr_c = 0; + bufptr_d = 0; + for(j = 0; j < 32; j += 4) { + bufptr_a += T[j]; + bufptr_a += (T[j] >> 8); + bufptr_a += (T[j] >> 16); + bufptr_a += (T[j] >> 24); + bufptr_b += T[j + 1]; + bufptr_b += (T[j + 1] >> 8); + bufptr_b += (T[j + 1] >> 16); + bufptr_b += (T[j + 1] >> 24); + bufptr_c += T[j + 2]; + bufptr_c += (T[j + 2] >> 8); + bufptr_c += (T[j + 2] >> 16); + bufptr_c += (T[j + 2] >> 24); + bufptr_d += T[j + 3]; + bufptr_d += (T[j + 3] >> 8); + bufptr_d += (T[j + 3] >> 16); + bufptr_d += (T[j + 3] >> 24); + } + bufptr_a &= 0xFF; + bufptr_b &= 0xFF; + bufptr_c &= 0xFF; + bufptr_d &= 0xFF; + + for(j = 0; j < 32; j += 8) { + *((uint *) &Ba[bufptr_a + j]) ^= T[j]; + *((uint *) &Bb[bufptr_b + j]) ^= T[j + 1]; + *((uint *) &Bc[bufptr_c + j]) ^= T[j + 2]; + *((uint *) &Bd[bufptr_d + j]) ^= T[j + 3]; + *((uint *) &Ba[bufptr_a + j + 4]) ^= T[j + 4]; + *((uint *) &Bb[bufptr_b + j + 4]) ^= T[j + 5]; + *((uint *) &Bc[bufptr_c + j + 4]) ^= T[j + 6]; + *((uint *) &Bd[bufptr_d + j + 4]) ^= T[j + 7]; } + + if(bufptr_a < 32) + neoscrypt_copy(&Ba[256 + bufptr_a], &Ba[bufptr_a], 32 - bufptr_a); + else if(bufptr_a > 224) + neoscrypt_copy(&Ba[0], &Ba[256], bufptr_a - 224); + if(bufptr_b < 32) + neoscrypt_copy(&Bb[256 + bufptr_b], &Bb[bufptr_b], 32 - bufptr_b); + else if(bufptr_b > 224) + neoscrypt_copy(&Bb[0], &Bb[256], bufptr_b - 224); + if(bufptr_c < 32) + neoscrypt_copy(&Bc[256 + bufptr_c], &Bc[bufptr_c], 32 - bufptr_c); + else if(bufptr_c > 224) + neoscrypt_copy(&Bc[0], &Bc[256], bufptr_c - 224); + if(bufptr_d < 32) + neoscrypt_copy(&Bd[256 + bufptr_d], &Bd[bufptr_d], 32 - bufptr_d); + else if(bufptr_d > 224) + neoscrypt_copy(&Bd[0], &Bd[256], bufptr_d - 224); + } - if(fail) { - printf("NeoScrypt integrity test failed!\n"); - return(1); + i = 256 - bufptr_a; + if(i >= output_len) { + neoscrypt_xor(&Ba[bufptr_a], &Aa[0], output_len); + neoscrypt_copy(&output[0], &Ba[bufptr_a], output_len); + } else { + neoscrypt_xor(&Ba[bufptr_a], &Aa[0], i); + neoscrypt_xor(&Ba[0], &Aa[i], output_len - i); + neoscrypt_copy(&output[0], &Ba[bufptr_a], i); + neoscrypt_copy(&output[i], &Ba[0], output_len - i); + } + i = 256 - bufptr_b; + if(i >= output_len) { + neoscrypt_xor(&Bb[bufptr_b], &Ab[0], output_len); + neoscrypt_copy(&output[output_len], &Bb[bufptr_b], output_len); + } else { + neoscrypt_xor(&Bb[bufptr_b], &Ab[0], i); + neoscrypt_xor(&Bb[0], &Ab[i], output_len - i); + neoscrypt_copy(&output[output_len], &Bb[bufptr_b], i); + neoscrypt_copy(&output[output_len + i], &Bb[0], output_len - i); + } + i = 256 - bufptr_c; + if(i >= output_len) { + neoscrypt_xor(&Bc[bufptr_c], &Ac[0], output_len); + neoscrypt_copy(&output[2 * output_len], &Bc[bufptr_c], output_len); + } else { + neoscrypt_xor(&Bc[bufptr_c], &Ac[0], i); + neoscrypt_xor(&Bc[0], &Ac[i], output_len - i); + neoscrypt_copy(&output[2 * output_len], &Bc[bufptr_c], i); + neoscrypt_copy(&output[2 * output_len + i], &Bc[0], output_len - i); + } + i = 256 - bufptr_d; + if(i >= output_len) { + neoscrypt_xor(&Bd[bufptr_d], &Ad[0], output_len); + neoscrypt_copy(&output[3 * output_len], &Bd[bufptr_d], output_len); } else { - printf("NeoScrypt integrity test passed.\n"); + neoscrypt_xor(&Bd[bufptr_d], &Ad[0], i); + neoscrypt_xor(&Bd[0], &Ad[i], output_len - i); + neoscrypt_copy(&output[3 * output_len], &Bd[bufptr_d], i); + neoscrypt_copy(&output[3 * output_len + i], &Bd[0], output_len - i); } - return(0); } -#endif +#endif /* (ASM) && (MINER_4WAY) */ + +#ifndef ASM +uint cpu_vec_exts() { + + /* No assembly, no extensions */ + return(0); +} +#endif diff --git a/src/Native/libmultihash/neoscrypt.h b/src/Native/libmultihash/neoscrypt.h index cfbcfc7e2..f385b4368 100644 --- a/src/Native/libmultihash/neoscrypt.h +++ b/src/Native/libmultihash/neoscrypt.h @@ -1,30 +1,72 @@ -#ifdef __cplusplus +#if (__cplusplus) extern "C" { #endif -void neoscrypt(const char *input, char *output, int profile); -#ifdef __cplusplus + +void neoscrypt(const unsigned char *password, unsigned char *output, + unsigned int profile); + +void neoscrypt_blake2s(const void *input, const unsigned int input_size, + const void *key, const unsigned char key_size, + void *output, const unsigned char output_size); + +void neoscrypt_copy(void *dstp, const void *srcp, unsigned int len); +void neoscrypt_erase(void *dstp, unsigned int len); +void neoscrypt_xor(void *dstp, const void *srcp, unsigned int len); + +#if defined(ASM) && defined(MINER_4WAY) +void neoscrypt_4way(const unsigned char *password, unsigned char *output, + unsigned char *scratchpad); + +#ifdef SHA256 +void scrypt_4way(const unsigned char *password, unsigned char *output, + unsigned char *scratchpad); +#endif + +void neoscrypt_blake2s_4way(const unsigned char *input, + const unsigned char *key, unsigned char *output); + +void neoscrypt_fastkdf_4way(const unsigned char *password, + const unsigned char *salt, unsigned char *output, unsigned char *scratchpad, + const unsigned int mode); +#endif + +unsigned int cpu_vec_exts(void); + +#if (__cplusplus) } #else -#define SCRYPT_BLOCK_SIZE 64 -#define SCRYPT_HASH_BLOCK_SIZE 64 -#define SCRYPT_HASH_DIGEST_SIZE 32 -typedef uint8_t hash_digest[SCRYPT_HASH_DIGEST_SIZE]; +typedef unsigned long long ullong; +typedef signed long long llong; +typedef unsigned int uint; +typedef unsigned char uchar; + +#ifndef MIN +#define MIN(a, b) ((a) < (b) ? a : b) +#endif + +#ifndef MAX +#define MAX(a, b) ((a) > (b) ? a : b) +#endif + +#define BLOCK_SIZE 64 +#define DIGEST_SIZE 32 + +typedef uchar hash_digest[DIGEST_SIZE]; #define ROTL32(a,b) (((a) << (b)) | ((a) >> (32 - b))) #define ROTR32(a,b) (((a) >> (b)) | ((a) << (32 - b))) #define U8TO32_BE(p) \ - (((uint32_t)((p)[0]) << 24) | ((uint32_t)((p)[1]) << 16) | \ - ((uint32_t)((p)[2]) << 8) | ((uint32_t)((p)[3]))) + (((uint)((p)[0]) << 24) | ((uint)((p)[1]) << 16) | \ + ((uint)((p)[2]) << 8) | ((uint)((p)[3]))) #define U32TO8_BE(p, v) \ - (p)[0] = (uint8_t)((v) >> 24); (p)[1] = (uint8_t)((v) >> 16); \ - (p)[2] = (uint8_t)((v) >> 8); (p)[3] = (uint8_t)((v) ); + (p)[0] = (uchar)((v) >> 24); (p)[1] = (uchar)((v) >> 16); \ + (p)[2] = (uchar)((v) >> 8); (p)[3] = (uchar)((v) ); #define U64TO8_BE(p, v) \ - U32TO8_BE((p), (uint32_t)((v) >> 32)); \ - U32TO8_BE((p) + 4, (uint32_t)((v) )); + U32TO8_BE((p), (uint)((v) >> 32)); \ + U32TO8_BE((p) + 4, (uint)((v) )); #endif - diff --git a/src/Native/libmultihash/sha3/hamsi_helper.c b/src/Native/libmultihash/sha3/hamsi_helper.c index cdf2fc9fe..807085295 100644 --- a/src/Native/libmultihash/sha3/hamsi_helper.c +++ b/src/Native/libmultihash/sha3/hamsi_helper.c @@ -10,7 +10,7 @@ * ==========================(LICENSE BEGIN)============================ * * Copyright (c) 2007-2010 Projet RNRT SAPHIR - * + * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including @@ -18,10 +18,10 @@ * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: - * + * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. diff --git a/src/Native/libmultihash/sha3/haval_helper.c b/src/Native/libmultihash/sha3/haval_helper.c new file mode 100644 index 000000000..c402fc699 --- /dev/null +++ b/src/Native/libmultihash/sha3/haval_helper.c @@ -0,0 +1,195 @@ +/* $Id: haval_helper.c 218 2010-06-08 17:06:34Z tp $ */ +/* + * Helper code, included (three times !) by HAVAL implementation. + * + * TODO: try to merge this with md_helper.c. + * + * ==========================(LICENSE BEGIN)============================ + * + * Copyright (c) 2007-2010 Projet RNRT SAPHIR + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * ===========================(LICENSE END)============================= + * + * @author Thomas Pornin + */ + +#undef SPH_XCAT +#define SPH_XCAT(a, b) SPH_XCAT_(a, b) +#undef SPH_XCAT_ +#define SPH_XCAT_(a, b) a ## b + +static void +#ifdef SPH_UPTR +SPH_XCAT(SPH_XCAT(haval, PASSES), _short) +#else +SPH_XCAT(haval, PASSES) +#endif +(sph_haval_context *sc, const void *data, size_t len) +{ + unsigned current; + +#if SPH_64 + current = (unsigned)sc->count & 127U; +#else + current = (unsigned)sc->count_low & 127U; +#endif + while (len > 0) { + unsigned clen; +#if !SPH_64 + sph_u32 clow, clow2; +#endif + + clen = 128U - current; + if (clen > len) + clen = len; + memcpy(sc->buf + current, data, clen); + data = (const unsigned char *)data + clen; + current += clen; + len -= clen; + if (current == 128U) { + DSTATE; + IN_PREPARE(sc->buf); + + RSTATE; + SPH_XCAT(CORE, PASSES)(INW); + WSTATE; + current = 0; + } +#if SPH_64 + sc->count += clen; +#else + clow = sc->count_low; + clow2 = SPH_T32(clow + clen); + sc->count_low = clow2; + if (clow2 < clow) + sc->count_high ++; +#endif + } +} + +#ifdef SPH_UPTR +static void +SPH_XCAT(haval, PASSES)(sph_haval_context *sc, const void *data, size_t len) +{ + unsigned current; + size_t orig_len; +#if !SPH_64 + sph_u32 clow, clow2; +#endif + DSTATE; + + if (len < 256U) { + SPH_XCAT(SPH_XCAT(haval, PASSES), _short)(sc, data, len); + return; + } +#if SPH_64 + current = (unsigned)sc->count & 127U; +#else + current = (unsigned)sc->count_low & 127U; +#endif + if (current > 0) { + unsigned clen; + + clen = 128U - current; + SPH_XCAT(SPH_XCAT(haval, PASSES), _short)(sc, data, clen); + data = (const unsigned char *)data + clen; + len -= clen; + } +#if !SPH_UNALIGNED + if (((SPH_UPTR)data & 3U) != 0) { + SPH_XCAT(SPH_XCAT(haval, PASSES), _short)(sc, data, len); + return; + } +#endif + orig_len = len; + RSTATE; + while (len >= 128U) { + IN_PREPARE(data); + + SPH_XCAT(CORE, PASSES)(INW); + data = (const unsigned char *)data + 128U; + len -= 128U; + } + WSTATE; + if (len > 0) + memcpy(sc->buf, data, len); +#if SPH_64 + sc->count += (sph_u64)orig_len; +#else + clow = sc->count_low; + clow2 = SPH_T32(clow + orig_len); + sc->count_low = clow2; + if (clow2 < clow) + sc->count_high ++; + orig_len >>= 12; + orig_len >>= 10; + orig_len >>= 10; + sc->count_high += orig_len; +#endif +} +#endif + +static void +SPH_XCAT(SPH_XCAT(haval, PASSES), _close)(sph_haval_context *sc, + unsigned ub, unsigned n, void *dst) +{ + unsigned current; + DSTATE; + +#if SPH_64 + current = (unsigned)sc->count & 127U; +#else + current = (unsigned)sc->count_low & 127U; +#endif + sc->buf[current ++] = (0x01 << n) | ((ub & 0xFF) >> (8 - n)); + RSTATE; + if (current > 118U) { + memset(sc->buf + current, 0, 128U - current); + + do { + IN_PREPARE(sc->buf); + + SPH_XCAT(CORE, PASSES)(INW); + } while (0); + current = 0; + } + memset(sc->buf + current, 0, 118U - current); + sc->buf[118] = 0x01 | (PASSES << 3); + sc->buf[119] = sc->olen << 3; +#if SPH_64 + sph_enc64le_aligned(sc->buf + 120, SPH_T64(sc->count << 3)); +#else + sph_enc32le_aligned(sc->buf + 120, SPH_T32(sc->count_low << 3)); + sph_enc32le_aligned(sc->buf + 124, + SPH_T32((sc->count_high << 3) | (sc->count_low >> 29))); +#endif + do { + IN_PREPARE(sc->buf); + + SPH_XCAT(CORE, PASSES)(INW); + } while (0); + + WSTATE; + haval_out(sc, dst); + haval_init(sc, sc->olen, sc->passes); +} + diff --git a/src/Native/libmultihash/sha3/sph_haval.c b/src/Native/libmultihash/sha3/sph_haval.c new file mode 100644 index 000000000..90922b638 --- /dev/null +++ b/src/Native/libmultihash/sha3/sph_haval.c @@ -0,0 +1,975 @@ +/* $Id: haval.c 227 2010-06-16 17:28:38Z tp $ */ +/* + * HAVAL implementation. + * + * The HAVAL reference paper is of questionable clarity with regards to + * some details such as endianness of bits within a byte, bytes within + * a 32-bit word, or the actual ordering of words within a stream of + * words. This implementation has been made compatible with the reference + * implementation available on: http://labs.calyptix.com/haval.php + * + * ==========================(LICENSE BEGIN)============================ + * + * Copyright (c) 2007-2010 Projet RNRT SAPHIR + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * ===========================(LICENSE END)============================= + * + * @author Thomas Pornin + */ + +#include +#include + +#include "sph_haval.h" + +#if SPH_SMALL_FOOTPRINT && !defined SPH_SMALL_FOOTPRINT_HAVAL +#define SPH_SMALL_FOOTPRINT_HAVAL 1 +#endif + +/* + * Basic definition from the reference paper. + * +#define F1(x6, x5, x4, x3, x2, x1, x0) \ + (((x1) & (x4)) ^ ((x2) & (x5)) ^ ((x3) & (x6)) ^ ((x0) & (x1)) ^ (x0)) + * + */ + +#define F1(x6, x5, x4, x3, x2, x1, x0) \ + (((x1) & ((x0) ^ (x4))) ^ ((x2) & (x5)) ^ ((x3) & (x6)) ^ (x0)) + +/* + * Basic definition from the reference paper. + * +#define F2(x6, x5, x4, x3, x2, x1, x0) \ + (((x1) & (x2) & (x3)) ^ ((x2) & (x4) & (x5)) ^ ((x1) & (x2)) \ + ^ ((x1) & (x4)) ^ ((x2) & (x6)) ^ ((x3) & (x5)) \ + ^ ((x4) & (x5)) ^ ((x0) & (x2)) ^ (x0)) + * + */ + +#define F2(x6, x5, x4, x3, x2, x1, x0) \ + (((x2) & (((x1) & ~(x3)) ^ ((x4) & (x5)) ^ (x6) ^ (x0))) \ + ^ ((x4) & ((x1) ^ (x5))) ^ ((x3 & (x5)) ^ (x0))) + +/* + * Basic definition from the reference paper. + * +#define F3(x6, x5, x4, x3, x2, x1, x0) \ + (((x1) & (x2) & (x3)) ^ ((x1) & (x4)) ^ ((x2) & (x5)) \ + ^ ((x3) & (x6)) ^ ((x0) & (x3)) ^ (x0)) + * + */ + +#define F3(x6, x5, x4, x3, x2, x1, x0) \ + (((x3) & (((x1) & (x2)) ^ (x6) ^ (x0))) \ + ^ ((x1) & (x4)) ^ ((x2) & (x5)) ^ (x0)) + +/* + * Basic definition from the reference paper. + * +#define F4(x6, x5, x4, x3, x2, x1, x0) \ + (((x1) & (x2) & (x3)) ^ ((x2) & (x4) & (x5)) ^ ((x3) & (x4) & (x6)) \ + ^ ((x1) & (x4)) ^ ((x2) & (x6)) ^ ((x3) & (x4)) ^ ((x3) & (x5)) \ + ^ ((x3) & (x6)) ^ ((x4) & (x5)) ^ ((x4) & (x6)) ^ ((x0) & (x4)) ^ (x0)) + * + */ + +#define F4(x6, x5, x4, x3, x2, x1, x0) \ + (((x3) & (((x1) & (x2)) ^ ((x4) | (x6)) ^ (x5))) \ + ^ ((x4) & ((~(x2) & (x5)) ^ (x1) ^ (x6) ^ (x0))) \ + ^ ((x2) & (x6)) ^ (x0)) + +/* + * Basic definition from the reference paper. + * +#define F5(x6, x5, x4, x3, x2, x1, x0) \ + (((x1) & (x4)) ^ ((x2) & (x5)) ^ ((x3) & (x6)) \ + ^ ((x0) & (x1) & (x2) & (x3)) ^ ((x0) & (x5)) ^ (x0)) + * + */ + +#define F5(x6, x5, x4, x3, x2, x1, x0) \ + (((x0) & ~(((x1) & (x2) & (x3)) ^ (x5))) \ + ^ ((x1) & (x4)) ^ ((x2) & (x5)) ^ ((x3) & (x6))) + +/* + * The macros below integrate the phi() permutations, depending on the + * pass and the total number of passes. + */ + +#define FP3_1(x6, x5, x4, x3, x2, x1, x0) \ + F1(x1, x0, x3, x5, x6, x2, x4) +#define FP3_2(x6, x5, x4, x3, x2, x1, x0) \ + F2(x4, x2, x1, x0, x5, x3, x6) +#define FP3_3(x6, x5, x4, x3, x2, x1, x0) \ + F3(x6, x1, x2, x3, x4, x5, x0) + +#define FP4_1(x6, x5, x4, x3, x2, x1, x0) \ + F1(x2, x6, x1, x4, x5, x3, x0) +#define FP4_2(x6, x5, x4, x3, x2, x1, x0) \ + F2(x3, x5, x2, x0, x1, x6, x4) +#define FP4_3(x6, x5, x4, x3, x2, x1, x0) \ + F3(x1, x4, x3, x6, x0, x2, x5) +#define FP4_4(x6, x5, x4, x3, x2, x1, x0) \ + F4(x6, x4, x0, x5, x2, x1, x3) + +#define FP5_1(x6, x5, x4, x3, x2, x1, x0) \ + F1(x3, x4, x1, x0, x5, x2, x6) +#define FP5_2(x6, x5, x4, x3, x2, x1, x0) \ + F2(x6, x2, x1, x0, x3, x4, x5) +#define FP5_3(x6, x5, x4, x3, x2, x1, x0) \ + F3(x2, x6, x0, x4, x3, x1, x5) +#define FP5_4(x6, x5, x4, x3, x2, x1, x0) \ + F4(x1, x5, x3, x2, x0, x4, x6) +#define FP5_5(x6, x5, x4, x3, x2, x1, x0) \ + F5(x2, x5, x0, x6, x4, x3, x1) + +/* + * One step, for "n" passes, pass number "p" (1 <= p <= n), using + * input word number "w" and step constant "c". + */ +#define STEP(n, p, x7, x6, x5, x4, x3, x2, x1, x0, w, c) do { \ + sph_u32 t = FP ## n ## _ ## p(x6, x5, x4, x3, x2, x1, x0); \ + (x7) = SPH_T32(SPH_ROTR32(t, 7) + SPH_ROTR32((x7), 11) \ + + (w) + (c)); \ + } while (0) + +/* + * PASSy(n, in) computes pass number "y", for a total of "n", using the + * one-argument macro "in" to access input words. Current state is assumed + * to be held in variables "s0" to "s7". + */ + +#if SPH_SMALL_FOOTPRINT_HAVAL + +#define PASS1(n, in) do { \ + unsigned pass_count; \ + for (pass_count = 0; pass_count < 32; pass_count += 8) { \ + STEP(n, 1, s7, s6, s5, s4, s3, s2, s1, s0, \ + in(pass_count + 0), SPH_C32(0x00000000)); \ + STEP(n, 1, s6, s5, s4, s3, s2, s1, s0, s7, \ + in(pass_count + 1), SPH_C32(0x00000000)); \ + STEP(n, 1, s5, s4, s3, s2, s1, s0, s7, s6, \ + in(pass_count + 2), SPH_C32(0x00000000)); \ + STEP(n, 1, s4, s3, s2, s1, s0, s7, s6, s5, \ + in(pass_count + 3), SPH_C32(0x00000000)); \ + STEP(n, 1, s3, s2, s1, s0, s7, s6, s5, s4, \ + in(pass_count + 4), SPH_C32(0x00000000)); \ + STEP(n, 1, s2, s1, s0, s7, s6, s5, s4, s3, \ + in(pass_count + 5), SPH_C32(0x00000000)); \ + STEP(n, 1, s1, s0, s7, s6, s5, s4, s3, s2, \ + in(pass_count + 6), SPH_C32(0x00000000)); \ + STEP(n, 1, s0, s7, s6, s5, s4, s3, s2, s1, \ + in(pass_count + 7), SPH_C32(0x00000000)); \ + } \ + } while (0) + +#define PASSG(p, n, in) do { \ + unsigned pass_count; \ + for (pass_count = 0; pass_count < 32; pass_count += 8) { \ + STEP(n, p, s7, s6, s5, s4, s3, s2, s1, s0, \ + in(MP ## p[pass_count + 0]), \ + RK ## p[pass_count + 0]); \ + STEP(n, p, s6, s5, s4, s3, s2, s1, s0, s7, \ + in(MP ## p[pass_count + 1]), \ + RK ## p[pass_count + 1]); \ + STEP(n, p, s5, s4, s3, s2, s1, s0, s7, s6, \ + in(MP ## p[pass_count + 2]), \ + RK ## p[pass_count + 2]); \ + STEP(n, p, s4, s3, s2, s1, s0, s7, s6, s5, \ + in(MP ## p[pass_count + 3]), \ + RK ## p[pass_count + 3]); \ + STEP(n, p, s3, s2, s1, s0, s7, s6, s5, s4, \ + in(MP ## p[pass_count + 4]), \ + RK ## p[pass_count + 4]); \ + STEP(n, p, s2, s1, s0, s7, s6, s5, s4, s3, \ + in(MP ## p[pass_count + 5]), \ + RK ## p[pass_count + 5]); \ + STEP(n, p, s1, s0, s7, s6, s5, s4, s3, s2, \ + in(MP ## p[pass_count + 6]), \ + RK ## p[pass_count + 6]); \ + STEP(n, p, s0, s7, s6, s5, s4, s3, s2, s1, \ + in(MP ## p[pass_count + 7]), \ + RK ## p[pass_count + 7]); \ + } \ + } while (0) + +#define PASS2(n, in) PASSG(2, n, in) +#define PASS3(n, in) PASSG(3, n, in) +#define PASS4(n, in) PASSG(4, n, in) +#define PASS5(n, in) PASSG(5, n, in) + +static const unsigned MP2[32] = { + 5, 14, 26, 18, 11, 28, 7, 16, + 0, 23, 20, 22, 1, 10, 4, 8, + 30, 3, 21, 9, 17, 24, 29, 6, + 19, 12, 15, 13, 2, 25, 31, 27 +}; + +static const unsigned MP3[32] = { + 19, 9, 4, 20, 28, 17, 8, 22, + 29, 14, 25, 12, 24, 30, 16, 26, + 31, 15, 7, 3, 1, 0, 18, 27, + 13, 6, 21, 10, 23, 11, 5, 2 +}; + +static const unsigned MP4[32] = { + 24, 4, 0, 14, 2, 7, 28, 23, + 26, 6, 30, 20, 18, 25, 19, 3, + 22, 11, 31, 21, 8, 27, 12, 9, + 1, 29, 5, 15, 17, 10, 16, 13 +}; + +static const unsigned MP5[32] = { + 27, 3, 21, 26, 17, 11, 20, 29, + 19, 0, 12, 7, 13, 8, 31, 10, + 5, 9, 14, 30, 18, 6, 28, 24, + 2, 23, 16, 22, 4, 1, 25, 15 +}; + +static const sph_u32 RK2[32] = { + SPH_C32(0x452821E6), SPH_C32(0x38D01377), + SPH_C32(0xBE5466CF), SPH_C32(0x34E90C6C), + SPH_C32(0xC0AC29B7), SPH_C32(0xC97C50DD), + SPH_C32(0x3F84D5B5), SPH_C32(0xB5470917), + SPH_C32(0x9216D5D9), SPH_C32(0x8979FB1B), + SPH_C32(0xD1310BA6), SPH_C32(0x98DFB5AC), + SPH_C32(0x2FFD72DB), SPH_C32(0xD01ADFB7), + SPH_C32(0xB8E1AFED), SPH_C32(0x6A267E96), + SPH_C32(0xBA7C9045), SPH_C32(0xF12C7F99), + SPH_C32(0x24A19947), SPH_C32(0xB3916CF7), + SPH_C32(0x0801F2E2), SPH_C32(0x858EFC16), + SPH_C32(0x636920D8), SPH_C32(0x71574E69), + SPH_C32(0xA458FEA3), SPH_C32(0xF4933D7E), + SPH_C32(0x0D95748F), SPH_C32(0x728EB658), + SPH_C32(0x718BCD58), SPH_C32(0x82154AEE), + SPH_C32(0x7B54A41D), SPH_C32(0xC25A59B5) +}; + +static const sph_u32 RK3[32] = { + SPH_C32(0x9C30D539), SPH_C32(0x2AF26013), + SPH_C32(0xC5D1B023), SPH_C32(0x286085F0), + SPH_C32(0xCA417918), SPH_C32(0xB8DB38EF), + SPH_C32(0x8E79DCB0), SPH_C32(0x603A180E), + SPH_C32(0x6C9E0E8B), SPH_C32(0xB01E8A3E), + SPH_C32(0xD71577C1), SPH_C32(0xBD314B27), + SPH_C32(0x78AF2FDA), SPH_C32(0x55605C60), + SPH_C32(0xE65525F3), SPH_C32(0xAA55AB94), + SPH_C32(0x57489862), SPH_C32(0x63E81440), + SPH_C32(0x55CA396A), SPH_C32(0x2AAB10B6), + SPH_C32(0xB4CC5C34), SPH_C32(0x1141E8CE), + SPH_C32(0xA15486AF), SPH_C32(0x7C72E993), + SPH_C32(0xB3EE1411), SPH_C32(0x636FBC2A), + SPH_C32(0x2BA9C55D), SPH_C32(0x741831F6), + SPH_C32(0xCE5C3E16), SPH_C32(0x9B87931E), + SPH_C32(0xAFD6BA33), SPH_C32(0x6C24CF5C) +}; + +static const sph_u32 RK4[32] = { + SPH_C32(0x7A325381), SPH_C32(0x28958677), + SPH_C32(0x3B8F4898), SPH_C32(0x6B4BB9AF), + SPH_C32(0xC4BFE81B), SPH_C32(0x66282193), + SPH_C32(0x61D809CC), SPH_C32(0xFB21A991), + SPH_C32(0x487CAC60), SPH_C32(0x5DEC8032), + SPH_C32(0xEF845D5D), SPH_C32(0xE98575B1), + SPH_C32(0xDC262302), SPH_C32(0xEB651B88), + SPH_C32(0x23893E81), SPH_C32(0xD396ACC5), + SPH_C32(0x0F6D6FF3), SPH_C32(0x83F44239), + SPH_C32(0x2E0B4482), SPH_C32(0xA4842004), + SPH_C32(0x69C8F04A), SPH_C32(0x9E1F9B5E), + SPH_C32(0x21C66842), SPH_C32(0xF6E96C9A), + SPH_C32(0x670C9C61), SPH_C32(0xABD388F0), + SPH_C32(0x6A51A0D2), SPH_C32(0xD8542F68), + SPH_C32(0x960FA728), SPH_C32(0xAB5133A3), + SPH_C32(0x6EEF0B6C), SPH_C32(0x137A3BE4) +}; + +static const sph_u32 RK5[32] = { + SPH_C32(0xBA3BF050), SPH_C32(0x7EFB2A98), + SPH_C32(0xA1F1651D), SPH_C32(0x39AF0176), + SPH_C32(0x66CA593E), SPH_C32(0x82430E88), + SPH_C32(0x8CEE8619), SPH_C32(0x456F9FB4), + SPH_C32(0x7D84A5C3), SPH_C32(0x3B8B5EBE), + SPH_C32(0xE06F75D8), SPH_C32(0x85C12073), + SPH_C32(0x401A449F), SPH_C32(0x56C16AA6), + SPH_C32(0x4ED3AA62), SPH_C32(0x363F7706), + SPH_C32(0x1BFEDF72), SPH_C32(0x429B023D), + SPH_C32(0x37D0D724), SPH_C32(0xD00A1248), + SPH_C32(0xDB0FEAD3), SPH_C32(0x49F1C09B), + SPH_C32(0x075372C9), SPH_C32(0x80991B7B), + SPH_C32(0x25D479D8), SPH_C32(0xF6E8DEF7), + SPH_C32(0xE3FE501A), SPH_C32(0xB6794C3B), + SPH_C32(0x976CE0BD), SPH_C32(0x04C006BA), + SPH_C32(0xC1A94FB6), SPH_C32(0x409F60C4) +}; + +#else + +#define PASS1(n, in) do { \ + STEP(n, 1, s7, s6, s5, s4, s3, s2, s1, s0, in( 0), SPH_C32(0x00000000)); \ + STEP(n, 1, s6, s5, s4, s3, s2, s1, s0, s7, in( 1), SPH_C32(0x00000000)); \ + STEP(n, 1, s5, s4, s3, s2, s1, s0, s7, s6, in( 2), SPH_C32(0x00000000)); \ + STEP(n, 1, s4, s3, s2, s1, s0, s7, s6, s5, in( 3), SPH_C32(0x00000000)); \ + STEP(n, 1, s3, s2, s1, s0, s7, s6, s5, s4, in( 4), SPH_C32(0x00000000)); \ + STEP(n, 1, s2, s1, s0, s7, s6, s5, s4, s3, in( 5), SPH_C32(0x00000000)); \ + STEP(n, 1, s1, s0, s7, s6, s5, s4, s3, s2, in( 6), SPH_C32(0x00000000)); \ + STEP(n, 1, s0, s7, s6, s5, s4, s3, s2, s1, in( 7), SPH_C32(0x00000000)); \ + \ + STEP(n, 1, s7, s6, s5, s4, s3, s2, s1, s0, in( 8), SPH_C32(0x00000000)); \ + STEP(n, 1, s6, s5, s4, s3, s2, s1, s0, s7, in( 9), SPH_C32(0x00000000)); \ + STEP(n, 1, s5, s4, s3, s2, s1, s0, s7, s6, in(10), SPH_C32(0x00000000)); \ + STEP(n, 1, s4, s3, s2, s1, s0, s7, s6, s5, in(11), SPH_C32(0x00000000)); \ + STEP(n, 1, s3, s2, s1, s0, s7, s6, s5, s4, in(12), SPH_C32(0x00000000)); \ + STEP(n, 1, s2, s1, s0, s7, s6, s5, s4, s3, in(13), SPH_C32(0x00000000)); \ + STEP(n, 1, s1, s0, s7, s6, s5, s4, s3, s2, in(14), SPH_C32(0x00000000)); \ + STEP(n, 1, s0, s7, s6, s5, s4, s3, s2, s1, in(15), SPH_C32(0x00000000)); \ + \ + STEP(n, 1, s7, s6, s5, s4, s3, s2, s1, s0, in(16), SPH_C32(0x00000000)); \ + STEP(n, 1, s6, s5, s4, s3, s2, s1, s0, s7, in(17), SPH_C32(0x00000000)); \ + STEP(n, 1, s5, s4, s3, s2, s1, s0, s7, s6, in(18), SPH_C32(0x00000000)); \ + STEP(n, 1, s4, s3, s2, s1, s0, s7, s6, s5, in(19), SPH_C32(0x00000000)); \ + STEP(n, 1, s3, s2, s1, s0, s7, s6, s5, s4, in(20), SPH_C32(0x00000000)); \ + STEP(n, 1, s2, s1, s0, s7, s6, s5, s4, s3, in(21), SPH_C32(0x00000000)); \ + STEP(n, 1, s1, s0, s7, s6, s5, s4, s3, s2, in(22), SPH_C32(0x00000000)); \ + STEP(n, 1, s0, s7, s6, s5, s4, s3, s2, s1, in(23), SPH_C32(0x00000000)); \ + \ + STEP(n, 1, s7, s6, s5, s4, s3, s2, s1, s0, in(24), SPH_C32(0x00000000)); \ + STEP(n, 1, s6, s5, s4, s3, s2, s1, s0, s7, in(25), SPH_C32(0x00000000)); \ + STEP(n, 1, s5, s4, s3, s2, s1, s0, s7, s6, in(26), SPH_C32(0x00000000)); \ + STEP(n, 1, s4, s3, s2, s1, s0, s7, s6, s5, in(27), SPH_C32(0x00000000)); \ + STEP(n, 1, s3, s2, s1, s0, s7, s6, s5, s4, in(28), SPH_C32(0x00000000)); \ + STEP(n, 1, s2, s1, s0, s7, s6, s5, s4, s3, in(29), SPH_C32(0x00000000)); \ + STEP(n, 1, s1, s0, s7, s6, s5, s4, s3, s2, in(30), SPH_C32(0x00000000)); \ + STEP(n, 1, s0, s7, s6, s5, s4, s3, s2, s1, in(31), SPH_C32(0x00000000)); \ + } while (0) + +#define PASS2(n, in) do { \ + STEP(n, 2, s7, s6, s5, s4, s3, s2, s1, s0, in( 5), SPH_C32(0x452821E6)); \ + STEP(n, 2, s6, s5, s4, s3, s2, s1, s0, s7, in(14), SPH_C32(0x38D01377)); \ + STEP(n, 2, s5, s4, s3, s2, s1, s0, s7, s6, in(26), SPH_C32(0xBE5466CF)); \ + STEP(n, 2, s4, s3, s2, s1, s0, s7, s6, s5, in(18), SPH_C32(0x34E90C6C)); \ + STEP(n, 2, s3, s2, s1, s0, s7, s6, s5, s4, in(11), SPH_C32(0xC0AC29B7)); \ + STEP(n, 2, s2, s1, s0, s7, s6, s5, s4, s3, in(28), SPH_C32(0xC97C50DD)); \ + STEP(n, 2, s1, s0, s7, s6, s5, s4, s3, s2, in( 7), SPH_C32(0x3F84D5B5)); \ + STEP(n, 2, s0, s7, s6, s5, s4, s3, s2, s1, in(16), SPH_C32(0xB5470917)); \ + \ + STEP(n, 2, s7, s6, s5, s4, s3, s2, s1, s0, in( 0), SPH_C32(0x9216D5D9)); \ + STEP(n, 2, s6, s5, s4, s3, s2, s1, s0, s7, in(23), SPH_C32(0x8979FB1B)); \ + STEP(n, 2, s5, s4, s3, s2, s1, s0, s7, s6, in(20), SPH_C32(0xD1310BA6)); \ + STEP(n, 2, s4, s3, s2, s1, s0, s7, s6, s5, in(22), SPH_C32(0x98DFB5AC)); \ + STEP(n, 2, s3, s2, s1, s0, s7, s6, s5, s4, in( 1), SPH_C32(0x2FFD72DB)); \ + STEP(n, 2, s2, s1, s0, s7, s6, s5, s4, s3, in(10), SPH_C32(0xD01ADFB7)); \ + STEP(n, 2, s1, s0, s7, s6, s5, s4, s3, s2, in( 4), SPH_C32(0xB8E1AFED)); \ + STEP(n, 2, s0, s7, s6, s5, s4, s3, s2, s1, in( 8), SPH_C32(0x6A267E96)); \ + \ + STEP(n, 2, s7, s6, s5, s4, s3, s2, s1, s0, in(30), SPH_C32(0xBA7C9045)); \ + STEP(n, 2, s6, s5, s4, s3, s2, s1, s0, s7, in( 3), SPH_C32(0xF12C7F99)); \ + STEP(n, 2, s5, s4, s3, s2, s1, s0, s7, s6, in(21), SPH_C32(0x24A19947)); \ + STEP(n, 2, s4, s3, s2, s1, s0, s7, s6, s5, in( 9), SPH_C32(0xB3916CF7)); \ + STEP(n, 2, s3, s2, s1, s0, s7, s6, s5, s4, in(17), SPH_C32(0x0801F2E2)); \ + STEP(n, 2, s2, s1, s0, s7, s6, s5, s4, s3, in(24), SPH_C32(0x858EFC16)); \ + STEP(n, 2, s1, s0, s7, s6, s5, s4, s3, s2, in(29), SPH_C32(0x636920D8)); \ + STEP(n, 2, s0, s7, s6, s5, s4, s3, s2, s1, in( 6), SPH_C32(0x71574E69)); \ + \ + STEP(n, 2, s7, s6, s5, s4, s3, s2, s1, s0, in(19), SPH_C32(0xA458FEA3)); \ + STEP(n, 2, s6, s5, s4, s3, s2, s1, s0, s7, in(12), SPH_C32(0xF4933D7E)); \ + STEP(n, 2, s5, s4, s3, s2, s1, s0, s7, s6, in(15), SPH_C32(0x0D95748F)); \ + STEP(n, 2, s4, s3, s2, s1, s0, s7, s6, s5, in(13), SPH_C32(0x728EB658)); \ + STEP(n, 2, s3, s2, s1, s0, s7, s6, s5, s4, in( 2), SPH_C32(0x718BCD58)); \ + STEP(n, 2, s2, s1, s0, s7, s6, s5, s4, s3, in(25), SPH_C32(0x82154AEE)); \ + STEP(n, 2, s1, s0, s7, s6, s5, s4, s3, s2, in(31), SPH_C32(0x7B54A41D)); \ + STEP(n, 2, s0, s7, s6, s5, s4, s3, s2, s1, in(27), SPH_C32(0xC25A59B5)); \ + } while (0) + +#define PASS3(n, in) do { \ + STEP(n, 3, s7, s6, s5, s4, s3, s2, s1, s0, in(19), SPH_C32(0x9C30D539)); \ + STEP(n, 3, s6, s5, s4, s3, s2, s1, s0, s7, in( 9), SPH_C32(0x2AF26013)); \ + STEP(n, 3, s5, s4, s3, s2, s1, s0, s7, s6, in( 4), SPH_C32(0xC5D1B023)); \ + STEP(n, 3, s4, s3, s2, s1, s0, s7, s6, s5, in(20), SPH_C32(0x286085F0)); \ + STEP(n, 3, s3, s2, s1, s0, s7, s6, s5, s4, in(28), SPH_C32(0xCA417918)); \ + STEP(n, 3, s2, s1, s0, s7, s6, s5, s4, s3, in(17), SPH_C32(0xB8DB38EF)); \ + STEP(n, 3, s1, s0, s7, s6, s5, s4, s3, s2, in( 8), SPH_C32(0x8E79DCB0)); \ + STEP(n, 3, s0, s7, s6, s5, s4, s3, s2, s1, in(22), SPH_C32(0x603A180E)); \ + \ + STEP(n, 3, s7, s6, s5, s4, s3, s2, s1, s0, in(29), SPH_C32(0x6C9E0E8B)); \ + STEP(n, 3, s6, s5, s4, s3, s2, s1, s0, s7, in(14), SPH_C32(0xB01E8A3E)); \ + STEP(n, 3, s5, s4, s3, s2, s1, s0, s7, s6, in(25), SPH_C32(0xD71577C1)); \ + STEP(n, 3, s4, s3, s2, s1, s0, s7, s6, s5, in(12), SPH_C32(0xBD314B27)); \ + STEP(n, 3, s3, s2, s1, s0, s7, s6, s5, s4, in(24), SPH_C32(0x78AF2FDA)); \ + STEP(n, 3, s2, s1, s0, s7, s6, s5, s4, s3, in(30), SPH_C32(0x55605C60)); \ + STEP(n, 3, s1, s0, s7, s6, s5, s4, s3, s2, in(16), SPH_C32(0xE65525F3)); \ + STEP(n, 3, s0, s7, s6, s5, s4, s3, s2, s1, in(26), SPH_C32(0xAA55AB94)); \ + \ + STEP(n, 3, s7, s6, s5, s4, s3, s2, s1, s0, in(31), SPH_C32(0x57489862)); \ + STEP(n, 3, s6, s5, s4, s3, s2, s1, s0, s7, in(15), SPH_C32(0x63E81440)); \ + STEP(n, 3, s5, s4, s3, s2, s1, s0, s7, s6, in( 7), SPH_C32(0x55CA396A)); \ + STEP(n, 3, s4, s3, s2, s1, s0, s7, s6, s5, in( 3), SPH_C32(0x2AAB10B6)); \ + STEP(n, 3, s3, s2, s1, s0, s7, s6, s5, s4, in( 1), SPH_C32(0xB4CC5C34)); \ + STEP(n, 3, s2, s1, s0, s7, s6, s5, s4, s3, in( 0), SPH_C32(0x1141E8CE)); \ + STEP(n, 3, s1, s0, s7, s6, s5, s4, s3, s2, in(18), SPH_C32(0xA15486AF)); \ + STEP(n, 3, s0, s7, s6, s5, s4, s3, s2, s1, in(27), SPH_C32(0x7C72E993)); \ + \ + STEP(n, 3, s7, s6, s5, s4, s3, s2, s1, s0, in(13), SPH_C32(0xB3EE1411)); \ + STEP(n, 3, s6, s5, s4, s3, s2, s1, s0, s7, in( 6), SPH_C32(0x636FBC2A)); \ + STEP(n, 3, s5, s4, s3, s2, s1, s0, s7, s6, in(21), SPH_C32(0x2BA9C55D)); \ + STEP(n, 3, s4, s3, s2, s1, s0, s7, s6, s5, in(10), SPH_C32(0x741831F6)); \ + STEP(n, 3, s3, s2, s1, s0, s7, s6, s5, s4, in(23), SPH_C32(0xCE5C3E16)); \ + STEP(n, 3, s2, s1, s0, s7, s6, s5, s4, s3, in(11), SPH_C32(0x9B87931E)); \ + STEP(n, 3, s1, s0, s7, s6, s5, s4, s3, s2, in( 5), SPH_C32(0xAFD6BA33)); \ + STEP(n, 3, s0, s7, s6, s5, s4, s3, s2, s1, in( 2), SPH_C32(0x6C24CF5C)); \ + } while (0) + +#define PASS4(n, in) do { \ + STEP(n, 4, s7, s6, s5, s4, s3, s2, s1, s0, in(24), SPH_C32(0x7A325381)); \ + STEP(n, 4, s6, s5, s4, s3, s2, s1, s0, s7, in( 4), SPH_C32(0x28958677)); \ + STEP(n, 4, s5, s4, s3, s2, s1, s0, s7, s6, in( 0), SPH_C32(0x3B8F4898)); \ + STEP(n, 4, s4, s3, s2, s1, s0, s7, s6, s5, in(14), SPH_C32(0x6B4BB9AF)); \ + STEP(n, 4, s3, s2, s1, s0, s7, s6, s5, s4, in( 2), SPH_C32(0xC4BFE81B)); \ + STEP(n, 4, s2, s1, s0, s7, s6, s5, s4, s3, in( 7), SPH_C32(0x66282193)); \ + STEP(n, 4, s1, s0, s7, s6, s5, s4, s3, s2, in(28), SPH_C32(0x61D809CC)); \ + STEP(n, 4, s0, s7, s6, s5, s4, s3, s2, s1, in(23), SPH_C32(0xFB21A991)); \ + \ + STEP(n, 4, s7, s6, s5, s4, s3, s2, s1, s0, in(26), SPH_C32(0x487CAC60)); \ + STEP(n, 4, s6, s5, s4, s3, s2, s1, s0, s7, in( 6), SPH_C32(0x5DEC8032)); \ + STEP(n, 4, s5, s4, s3, s2, s1, s0, s7, s6, in(30), SPH_C32(0xEF845D5D)); \ + STEP(n, 4, s4, s3, s2, s1, s0, s7, s6, s5, in(20), SPH_C32(0xE98575B1)); \ + STEP(n, 4, s3, s2, s1, s0, s7, s6, s5, s4, in(18), SPH_C32(0xDC262302)); \ + STEP(n, 4, s2, s1, s0, s7, s6, s5, s4, s3, in(25), SPH_C32(0xEB651B88)); \ + STEP(n, 4, s1, s0, s7, s6, s5, s4, s3, s2, in(19), SPH_C32(0x23893E81)); \ + STEP(n, 4, s0, s7, s6, s5, s4, s3, s2, s1, in( 3), SPH_C32(0xD396ACC5)); \ + \ + STEP(n, 4, s7, s6, s5, s4, s3, s2, s1, s0, in(22), SPH_C32(0x0F6D6FF3)); \ + STEP(n, 4, s6, s5, s4, s3, s2, s1, s0, s7, in(11), SPH_C32(0x83F44239)); \ + STEP(n, 4, s5, s4, s3, s2, s1, s0, s7, s6, in(31), SPH_C32(0x2E0B4482)); \ + STEP(n, 4, s4, s3, s2, s1, s0, s7, s6, s5, in(21), SPH_C32(0xA4842004)); \ + STEP(n, 4, s3, s2, s1, s0, s7, s6, s5, s4, in( 8), SPH_C32(0x69C8F04A)); \ + STEP(n, 4, s2, s1, s0, s7, s6, s5, s4, s3, in(27), SPH_C32(0x9E1F9B5E)); \ + STEP(n, 4, s1, s0, s7, s6, s5, s4, s3, s2, in(12), SPH_C32(0x21C66842)); \ + STEP(n, 4, s0, s7, s6, s5, s4, s3, s2, s1, in( 9), SPH_C32(0xF6E96C9A)); \ + \ + STEP(n, 4, s7, s6, s5, s4, s3, s2, s1, s0, in( 1), SPH_C32(0x670C9C61)); \ + STEP(n, 4, s6, s5, s4, s3, s2, s1, s0, s7, in(29), SPH_C32(0xABD388F0)); \ + STEP(n, 4, s5, s4, s3, s2, s1, s0, s7, s6, in( 5), SPH_C32(0x6A51A0D2)); \ + STEP(n, 4, s4, s3, s2, s1, s0, s7, s6, s5, in(15), SPH_C32(0xD8542F68)); \ + STEP(n, 4, s3, s2, s1, s0, s7, s6, s5, s4, in(17), SPH_C32(0x960FA728)); \ + STEP(n, 4, s2, s1, s0, s7, s6, s5, s4, s3, in(10), SPH_C32(0xAB5133A3)); \ + STEP(n, 4, s1, s0, s7, s6, s5, s4, s3, s2, in(16), SPH_C32(0x6EEF0B6C)); \ + STEP(n, 4, s0, s7, s6, s5, s4, s3, s2, s1, in(13), SPH_C32(0x137A3BE4)); \ + } while (0) + +#define PASS5(n, in) do { \ + STEP(n, 5, s7, s6, s5, s4, s3, s2, s1, s0, in(27), SPH_C32(0xBA3BF050)); \ + STEP(n, 5, s6, s5, s4, s3, s2, s1, s0, s7, in( 3), SPH_C32(0x7EFB2A98)); \ + STEP(n, 5, s5, s4, s3, s2, s1, s0, s7, s6, in(21), SPH_C32(0xA1F1651D)); \ + STEP(n, 5, s4, s3, s2, s1, s0, s7, s6, s5, in(26), SPH_C32(0x39AF0176)); \ + STEP(n, 5, s3, s2, s1, s0, s7, s6, s5, s4, in(17), SPH_C32(0x66CA593E)); \ + STEP(n, 5, s2, s1, s0, s7, s6, s5, s4, s3, in(11), SPH_C32(0x82430E88)); \ + STEP(n, 5, s1, s0, s7, s6, s5, s4, s3, s2, in(20), SPH_C32(0x8CEE8619)); \ + STEP(n, 5, s0, s7, s6, s5, s4, s3, s2, s1, in(29), SPH_C32(0x456F9FB4)); \ + \ + STEP(n, 5, s7, s6, s5, s4, s3, s2, s1, s0, in(19), SPH_C32(0x7D84A5C3)); \ + STEP(n, 5, s6, s5, s4, s3, s2, s1, s0, s7, in( 0), SPH_C32(0x3B8B5EBE)); \ + STEP(n, 5, s5, s4, s3, s2, s1, s0, s7, s6, in(12), SPH_C32(0xE06F75D8)); \ + STEP(n, 5, s4, s3, s2, s1, s0, s7, s6, s5, in( 7), SPH_C32(0x85C12073)); \ + STEP(n, 5, s3, s2, s1, s0, s7, s6, s5, s4, in(13), SPH_C32(0x401A449F)); \ + STEP(n, 5, s2, s1, s0, s7, s6, s5, s4, s3, in( 8), SPH_C32(0x56C16AA6)); \ + STEP(n, 5, s1, s0, s7, s6, s5, s4, s3, s2, in(31), SPH_C32(0x4ED3AA62)); \ + STEP(n, 5, s0, s7, s6, s5, s4, s3, s2, s1, in(10), SPH_C32(0x363F7706)); \ + \ + STEP(n, 5, s7, s6, s5, s4, s3, s2, s1, s0, in( 5), SPH_C32(0x1BFEDF72)); \ + STEP(n, 5, s6, s5, s4, s3, s2, s1, s0, s7, in( 9), SPH_C32(0x429B023D)); \ + STEP(n, 5, s5, s4, s3, s2, s1, s0, s7, s6, in(14), SPH_C32(0x37D0D724)); \ + STEP(n, 5, s4, s3, s2, s1, s0, s7, s6, s5, in(30), SPH_C32(0xD00A1248)); \ + STEP(n, 5, s3, s2, s1, s0, s7, s6, s5, s4, in(18), SPH_C32(0xDB0FEAD3)); \ + STEP(n, 5, s2, s1, s0, s7, s6, s5, s4, s3, in( 6), SPH_C32(0x49F1C09B)); \ + STEP(n, 5, s1, s0, s7, s6, s5, s4, s3, s2, in(28), SPH_C32(0x075372C9)); \ + STEP(n, 5, s0, s7, s6, s5, s4, s3, s2, s1, in(24), SPH_C32(0x80991B7B)); \ + \ + STEP(n, 5, s7, s6, s5, s4, s3, s2, s1, s0, in( 2), SPH_C32(0x25D479D8)); \ + STEP(n, 5, s6, s5, s4, s3, s2, s1, s0, s7, in(23), SPH_C32(0xF6E8DEF7)); \ + STEP(n, 5, s5, s4, s3, s2, s1, s0, s7, s6, in(16), SPH_C32(0xE3FE501A)); \ + STEP(n, 5, s4, s3, s2, s1, s0, s7, s6, s5, in(22), SPH_C32(0xB6794C3B)); \ + STEP(n, 5, s3, s2, s1, s0, s7, s6, s5, s4, in( 4), SPH_C32(0x976CE0BD)); \ + STEP(n, 5, s2, s1, s0, s7, s6, s5, s4, s3, in( 1), SPH_C32(0x04C006BA)); \ + STEP(n, 5, s1, s0, s7, s6, s5, s4, s3, s2, in(25), SPH_C32(0xC1A94FB6)); \ + STEP(n, 5, s0, s7, s6, s5, s4, s3, s2, s1, in(15), SPH_C32(0x409F60C4)); \ + } while (0) + +#endif + +#define SAVE_STATE \ + sph_u32 u0, u1, u2, u3, u4, u5, u6, u7; \ + do { \ + u0 = s0; \ + u1 = s1; \ + u2 = s2; \ + u3 = s3; \ + u4 = s4; \ + u5 = s5; \ + u6 = s6; \ + u7 = s7; \ + } while (0) + +#define UPDATE_STATE do { \ + s0 = SPH_T32(s0 + u0); \ + s1 = SPH_T32(s1 + u1); \ + s2 = SPH_T32(s2 + u2); \ + s3 = SPH_T32(s3 + u3); \ + s4 = SPH_T32(s4 + u4); \ + s5 = SPH_T32(s5 + u5); \ + s6 = SPH_T32(s6 + u6); \ + s7 = SPH_T32(s7 + u7); \ + } while (0) + +/* + * COREn(in) performs the core HAVAL computation for "n" passes, using + * the one-argument macro "in" to access the input words. Running state + * is held in variable "s0" to "s7". + */ + +#define CORE3(in) do { \ + SAVE_STATE; \ + PASS1(3, in); \ + PASS2(3, in); \ + PASS3(3, in); \ + UPDATE_STATE; \ + } while (0) + +#define CORE4(in) do { \ + SAVE_STATE; \ + PASS1(4, in); \ + PASS2(4, in); \ + PASS3(4, in); \ + PASS4(4, in); \ + UPDATE_STATE; \ + } while (0) + +#define CORE5(in) do { \ + SAVE_STATE; \ + PASS1(5, in); \ + PASS2(5, in); \ + PASS3(5, in); \ + PASS4(5, in); \ + PASS5(5, in); \ + UPDATE_STATE; \ + } while (0) + +/* + * DSTATE declares the state variables "s0" to "s7". + */ +#define DSTATE sph_u32 s0, s1, s2, s3, s4, s5, s6, s7 + +/* + * RSTATE fills the state variables from the context "sc". + */ +#define RSTATE do { \ + s0 = sc->s0; \ + s1 = sc->s1; \ + s2 = sc->s2; \ + s3 = sc->s3; \ + s4 = sc->s4; \ + s5 = sc->s5; \ + s6 = sc->s6; \ + s7 = sc->s7; \ + } while (0) + +/* + * WSTATE updates the context "sc" from the state variables. + */ +#define WSTATE do { \ + sc->s0 = s0; \ + sc->s1 = s1; \ + sc->s2 = s2; \ + sc->s3 = s3; \ + sc->s4 = s4; \ + sc->s5 = s5; \ + sc->s6 = s6; \ + sc->s7 = s7; \ + } while (0) + +/* + * Initialize a context. "olen" is the output length, in 32-bit words + * (between 4 and 8, inclusive). "passes" is the number of passes + * (3, 4 or 5). + */ +static void +haval_init(sph_haval_context *sc, unsigned olen, unsigned passes) +{ + sc->s0 = SPH_C32(0x243F6A88); + sc->s1 = SPH_C32(0x85A308D3); + sc->s2 = SPH_C32(0x13198A2E); + sc->s3 = SPH_C32(0x03707344); + sc->s4 = SPH_C32(0xA4093822); + sc->s5 = SPH_C32(0x299F31D0); + sc->s6 = SPH_C32(0x082EFA98); + sc->s7 = SPH_C32(0xEC4E6C89); + sc->olen = olen; + sc->passes = passes; +#if SPH_64 + sc->count = 0; +#else + sc->count_high = 0; + sc->count_low = 0; +#endif +} + +/* + * IN_PREPARE(data) contains declarations and code to prepare for + * reading input words pointed to by "data". + * INW(i) reads the word number "i" (from 0 to 31). + */ +#if SPH_LITTLE_FAST +#define IN_PREPARE(indata) const unsigned char *const load_ptr = \ + (const unsigned char *)(indata) +#define INW(i) sph_dec32le_aligned(load_ptr + 4 * (i)) +#else +#define IN_PREPARE(indata) \ + sph_u32 X_var[32]; \ + int load_index; \ + \ + for (load_index = 0; load_index < 32; load_index ++) \ + X_var[load_index] = sph_dec32le_aligned( \ + (const unsigned char *)(indata) + 4 * load_index) +#define INW(i) X_var[i] +#endif + +/* + * Mixing operation used for 128-bit output tailoring. This function + * takes the byte 0 from a0, byte 1 from a1, byte 2 from a2 and byte 3 + * from a3, and combines them into a 32-bit word, which is then rotated + * to the left by n bits. + */ +static SPH_INLINE sph_u32 +mix128(sph_u32 a0, sph_u32 a1, sph_u32 a2, sph_u32 a3, int n) +{ + sph_u32 tmp; + + tmp = (a0 & SPH_C32(0x000000FF)) + | (a1 & SPH_C32(0x0000FF00)) + | (a2 & SPH_C32(0x00FF0000)) + | (a3 & SPH_C32(0xFF000000)); + if (n > 0) + tmp = SPH_ROTL32(tmp, n); + return tmp; +} + +/* + * Mixing operation used to compute output word 0 for 160-bit output. + */ +static SPH_INLINE sph_u32 +mix160_0(sph_u32 x5, sph_u32 x6, sph_u32 x7) +{ + sph_u32 tmp; + + tmp = (x5 & SPH_C32(0x01F80000)) + | (x6 & SPH_C32(0xFE000000)) + | (x7 & SPH_C32(0x0000003F)); + return SPH_ROTL32(tmp, 13); +} + +/* + * Mixing operation used to compute output word 1 for 160-bit output. + */ +static SPH_INLINE sph_u32 +mix160_1(sph_u32 x5, sph_u32 x6, sph_u32 x7) +{ + sph_u32 tmp; + + tmp = (x5 & SPH_C32(0xFE000000)) + | (x6 & SPH_C32(0x0000003F)) + | (x7 & SPH_C32(0x00000FC0)); + return SPH_ROTL32(tmp, 7); +} + +/* + * Mixing operation used to compute output word 2 for 160-bit output. + */ +static SPH_INLINE sph_u32 +mix160_2(sph_u32 x5, sph_u32 x6, sph_u32 x7) +{ + sph_u32 tmp; + + tmp = (x5 & SPH_C32(0x0000003F)) + | (x6 & SPH_C32(0x00000FC0)) + | (x7 & SPH_C32(0x0007F000)); + return tmp; +} + +/* + * Mixing operation used to compute output word 3 for 160-bit output. + */ +static SPH_INLINE sph_u32 +mix160_3(sph_u32 x5, sph_u32 x6, sph_u32 x7) +{ + sph_u32 tmp; + + tmp = (x5 & SPH_C32(0x00000FC0)) + | (x6 & SPH_C32(0x0007F000)) + | (x7 & SPH_C32(0x01F80000)); + return tmp >> 6; +} + +/* + * Mixing operation used to compute output word 4 for 160-bit output. + */ +static SPH_INLINE sph_u32 +mix160_4(sph_u32 x5, sph_u32 x6, sph_u32 x7) +{ + sph_u32 tmp; + + tmp = (x5 & SPH_C32(0x0007F000)) + | (x6 & SPH_C32(0x01F80000)) + | (x7 & SPH_C32(0xFE000000)); + return tmp >> 12; +} + +/* + * Mixing operation used to compute output word 0 for 192-bit output. + */ +static SPH_INLINE sph_u32 +mix192_0(sph_u32 x6, sph_u32 x7) +{ + sph_u32 tmp; + + tmp = (x6 & SPH_C32(0xFC000000)) | (x7 & SPH_C32(0x0000001F)); + return SPH_ROTL32(tmp, 6); +} + +/* + * Mixing operation used to compute output word 1 for 192-bit output. + */ +static SPH_INLINE sph_u32 +mix192_1(sph_u32 x6, sph_u32 x7) +{ + return (x6 & SPH_C32(0x0000001F)) | (x7 & SPH_C32(0x000003E0)); +} + +/* + * Mixing operation used to compute output word 2 for 192-bit output. + */ +static SPH_INLINE sph_u32 +mix192_2(sph_u32 x6, sph_u32 x7) +{ + return ((x6 & SPH_C32(0x000003E0)) | (x7 & SPH_C32(0x0000FC00))) >> 5; +} + +/* + * Mixing operation used to compute output word 3 for 192-bit output. + */ +static SPH_INLINE sph_u32 +mix192_3(sph_u32 x6, sph_u32 x7) +{ + return ((x6 & SPH_C32(0x0000FC00)) | (x7 & SPH_C32(0x001F0000))) >> 10; +} + +/* + * Mixing operation used to compute output word 4 for 192-bit output. + */ +static SPH_INLINE sph_u32 +mix192_4(sph_u32 x6, sph_u32 x7) +{ + return ((x6 & SPH_C32(0x001F0000)) | (x7 & SPH_C32(0x03E00000))) >> 16; +} + +/* + * Mixing operation used to compute output word 5 for 192-bit output. + */ +static SPH_INLINE sph_u32 +mix192_5(sph_u32 x6, sph_u32 x7) +{ + return ((x6 & SPH_C32(0x03E00000)) | (x7 & SPH_C32(0xFC000000))) >> 21; +} + +/* + * Write out HAVAL output. The output length is tailored to the requested + * length. + */ +static void +haval_out(sph_haval_context *sc, void *dst) +{ + DSTATE; + unsigned char *buf; + + buf = (unsigned char*)dst; + RSTATE; + switch (sc->olen) { + case 4: + sph_enc32le(buf, SPH_T32(s0 + mix128(s7, s4, s5, s6, 24))); + sph_enc32le(buf + 4, SPH_T32(s1 + mix128(s6, s7, s4, s5, 16))); + sph_enc32le(buf + 8, SPH_T32(s2 + mix128(s5, s6, s7, s4, 8))); + sph_enc32le(buf + 12, SPH_T32(s3 + mix128(s4, s5, s6, s7, 0))); + break; + case 5: + sph_enc32le(buf, SPH_T32(s0 + mix160_0(s5, s6, s7))); + sph_enc32le(buf + 4, SPH_T32(s1 + mix160_1(s5, s6, s7))); + sph_enc32le(buf + 8, SPH_T32(s2 + mix160_2(s5, s6, s7))); + sph_enc32le(buf + 12, SPH_T32(s3 + mix160_3(s5, s6, s7))); + sph_enc32le(buf + 16, SPH_T32(s4 + mix160_4(s5, s6, s7))); + break; + case 6: + sph_enc32le(buf, SPH_T32(s0 + mix192_0(s6, s7))); + sph_enc32le(buf + 4, SPH_T32(s1 + mix192_1(s6, s7))); + sph_enc32le(buf + 8, SPH_T32(s2 + mix192_2(s6, s7))); + sph_enc32le(buf + 12, SPH_T32(s3 + mix192_3(s6, s7))); + sph_enc32le(buf + 16, SPH_T32(s4 + mix192_4(s6, s7))); + sph_enc32le(buf + 20, SPH_T32(s5 + mix192_5(s6, s7))); + break; + case 7: + sph_enc32le(buf, SPH_T32(s0 + ((s7 >> 27) & 0x1F))); + sph_enc32le(buf + 4, SPH_T32(s1 + ((s7 >> 22) & 0x1F))); + sph_enc32le(buf + 8, SPH_T32(s2 + ((s7 >> 18) & 0x0F))); + sph_enc32le(buf + 12, SPH_T32(s3 + ((s7 >> 13) & 0x1F))); + sph_enc32le(buf + 16, SPH_T32(s4 + ((s7 >> 9) & 0x0F))); + sph_enc32le(buf + 20, SPH_T32(s5 + ((s7 >> 4) & 0x1F))); + sph_enc32le(buf + 24, SPH_T32(s6 + ((s7 ) & 0x0F))); + break; + case 8: + sph_enc32le(buf, s0); + sph_enc32le(buf + 4, s1); + sph_enc32le(buf + 8, s2); + sph_enc32le(buf + 12, s3); + sph_enc32le(buf + 16, s4); + sph_enc32le(buf + 20, s5); + sph_enc32le(buf + 24, s6); + sph_enc32le(buf + 28, s7); + break; + } +} + +/* + * The main core functions inline the code with the COREx() macros. We + * use a helper file, included three times, which avoids code copying. + */ + +#undef PASSES +#define PASSES 3 +#include "haval_helper.c" + +#undef PASSES +#define PASSES 4 +#include "haval_helper.c" + +#undef PASSES +#define PASSES 5 +#include "haval_helper.c" + +/* ====================================================================== */ + +#define API(xxx, y) \ +void \ +sph_haval ## xxx ## _ ## y ## _init(void *cc) \ +{ \ + haval_init((sph_haval_context*)cc, xxx >> 5, y); \ +} \ + \ +void \ +sph_haval ## xxx ## _ ## y (void *cc, const void *data, size_t len) \ +{ \ + haval ## y((sph_haval_context*)cc, data, len); \ +} \ + \ +void \ +sph_haval ## xxx ## _ ## y ## _close(void *cc, void *dst) \ +{ \ + haval ## y ## _close((sph_haval_context*)cc, 0, 0, dst); \ +} \ + \ +void \ +sph_haval ## xxx ## _ ## y ## addbits_and_close( \ + void *cc, unsigned ub, unsigned n, void *dst) \ +{ \ + haval ## y ## _close((sph_haval_context*)cc, ub, n, dst); \ +} + +API(128, 3) +API(128, 4) +API(128, 5) +API(160, 3) +API(160, 4) +API(160, 5) +API(192, 3) +API(192, 4) +API(192, 5) +API(224, 3) +API(224, 4) +API(224, 5) +API(256, 3) +API(256, 4) +API(256, 5) + +#define RVAL do { \ + s0 = val[0]; \ + s1 = val[1]; \ + s2 = val[2]; \ + s3 = val[3]; \ + s4 = val[4]; \ + s5 = val[5]; \ + s6 = val[6]; \ + s7 = val[7]; \ + } while (0) + +#define WVAL do { \ + val[0] = s0; \ + val[1] = s1; \ + val[2] = s2; \ + val[3] = s3; \ + val[4] = s4; \ + val[5] = s5; \ + val[6] = s6; \ + val[7] = s7; \ + } while (0) + +#define INMSG(i) msg[i] + +/* see sph_haval.h */ +void +sph_haval_3_comp(const sph_u32 msg[32], sph_u32 val[8]) +{ + DSTATE; + + RVAL; + CORE3(INMSG); + WVAL; +} + +/* see sph_haval.h */ +void +sph_haval_4_comp(const sph_u32 msg[32], sph_u32 val[8]) +{ + DSTATE; + + RVAL; + CORE4(INMSG); + WVAL; +} + +/* see sph_haval.h */ +void +sph_haval_5_comp(const sph_u32 msg[32], sph_u32 val[8]) +{ + DSTATE; + + RVAL; + CORE5(INMSG); + WVAL; +} + diff --git a/src/Native/libmultihash/sha3/sph_haval.h b/src/Native/libmultihash/sha3/sph_haval.h new file mode 100644 index 000000000..6334a9226 --- /dev/null +++ b/src/Native/libmultihash/sha3/sph_haval.h @@ -0,0 +1,969 @@ +/* $Id: sph_haval.h 218 2010-06-08 17:06:34Z tp $ */ +/** +* HAVAL interface. +* +* HAVAL is actually a family of 15 hash functions, depending on whether +* the internal computation uses 3, 4 or 5 passes, and on the output +* length, which is 128, 160, 192, 224 or 256 bits. This implementation +* provides interface functions for all 15, which internally map to +* three cores (depending on the number of passes). Note that output +* lengths other than 256 bits are not obtained by a simple truncation +* of a longer result; the requested length is encoded within the +* padding data. +* +* HAVAL was published in: Yuliang Zheng, Josef Pieprzyk and Jennifer +* Seberry: "HAVAL -- a one-way hashing algorithm with variable length +* of output", Advances in Cryptology -- AUSCRYPT'92, Lecture Notes in +* Computer Science, Vol.718, pp.83-104, Springer-Verlag, 1993. +* +* This paper, and a reference implementation, are available on the +* Calyptix web site: http://labs.calyptix.com/haval.php +* +* The HAVAL reference paper is quite unclear on the data encoding +* details, i.e. endianness (both byte order within a 32-bit word, and +* word order within a message block). This implementation has been +* made compatible with the reference implementation referenced above. +* +* @warning A collision for HAVAL-128/3 (HAVAL with three passes and +* 128-bit output) has been published; this function is thus considered +* as cryptographically broken. The status for other variants is unclear; +* use only with care. +* +* ==========================(LICENSE BEGIN)============================ +* +* Copyright (c) 2007-2010 Projet RNRT SAPHIR +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be +* included in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +* ===========================(LICENSE END)============================= +* +* @file sph_haval.h +* @author Thomas Pornin +*/ + +#ifndef SPH_HAVAL_H__ +#define SPH_HAVAL_H__ + +#include +#include "sph_types.h" + +/** +* Output size (in bits) for HAVAL-128/3. +*/ +#define SPH_SIZE_haval128_3 128 + +/** +* Output size (in bits) for HAVAL-128/4. +*/ +#define SPH_SIZE_haval128_4 128 + +/** +* Output size (in bits) for HAVAL-128/5. +*/ +#define SPH_SIZE_haval128_5 128 + +/** +* Output size (in bits) for HAVAL-160/3. +*/ +#define SPH_SIZE_haval160_3 160 + +/** +* Output size (in bits) for HAVAL-160/4. +*/ +#define SPH_SIZE_haval160_4 160 + +/** +* Output size (in bits) for HAVAL-160/5. +*/ +#define SPH_SIZE_haval160_5 160 + +/** +* Output size (in bits) for HAVAL-192/3. +*/ +#define SPH_SIZE_haval192_3 192 + +/** +* Output size (in bits) for HAVAL-192/4. +*/ +#define SPH_SIZE_haval192_4 192 + +/** +* Output size (in bits) for HAVAL-192/5. +*/ +#define SPH_SIZE_haval192_5 192 + +/** +* Output size (in bits) for HAVAL-224/3. +*/ +#define SPH_SIZE_haval224_3 224 + +/** +* Output size (in bits) for HAVAL-224/4. +*/ +#define SPH_SIZE_haval224_4 224 + +/** +* Output size (in bits) for HAVAL-224/5. +*/ +#define SPH_SIZE_haval224_5 224 + +/** +* Output size (in bits) for HAVAL-256/3. +*/ +#define SPH_SIZE_haval256_3 256 + +/** +* Output size (in bits) for HAVAL-256/4. +*/ +#define SPH_SIZE_haval256_4 256 + +/** +* Output size (in bits) for HAVAL-256/5. +*/ +#define SPH_SIZE_haval256_5 256 + +/** +* This structure is a context for HAVAL computations: it contains the +* intermediate values and some data from the last entered block. Once +* a HAVAL computation has been performed, the context can be reused for +* another computation. +* +* The contents of this structure are private. A running HAVAL computation +* can be cloned by copying the context (e.g. with a simple +* memcpy()). +*/ +typedef struct { +#ifndef DOXYGEN_IGNORE +unsigned char buf[128]; /* first field, for alignment */ +sph_u32 s0, s1, s2, s3, s4, s5, s6, s7; +unsigned olen, passes; +#if SPH_64 +sph_u64 count; +#else +sph_u32 count_high, count_low; +#endif +#endif +} sph_haval_context; + +/** +* Type for a HAVAL-128/3 context (identical to the common context). +*/ +typedef sph_haval_context sph_haval128_3_context; + +/** +* Type for a HAVAL-128/4 context (identical to the common context). +*/ +typedef sph_haval_context sph_haval128_4_context; + +/** +* Type for a HAVAL-128/5 context (identical to the common context). +*/ +typedef sph_haval_context sph_haval128_5_context; + +/** +* Type for a HAVAL-160/3 context (identical to the common context). +*/ +typedef sph_haval_context sph_haval160_3_context; + +/** +* Type for a HAVAL-160/4 context (identical to the common context). +*/ +typedef sph_haval_context sph_haval160_4_context; + +/** +* Type for a HAVAL-160/5 context (identical to the common context). +*/ +typedef sph_haval_context sph_haval160_5_context; + +/** +* Type for a HAVAL-192/3 context (identical to the common context). +*/ +typedef sph_haval_context sph_haval192_3_context; + +/** +* Type for a HAVAL-192/4 context (identical to the common context). +*/ +typedef sph_haval_context sph_haval192_4_context; + +/** +* Type for a HAVAL-192/5 context (identical to the common context). +*/ +typedef sph_haval_context sph_haval192_5_context; + +/** +* Type for a HAVAL-224/3 context (identical to the common context). +*/ +typedef sph_haval_context sph_haval224_3_context; + +/** +* Type for a HAVAL-224/4 context (identical to the common context). +*/ +typedef sph_haval_context sph_haval224_4_context; + +/** +* Type for a HAVAL-224/5 context (identical to the common context). +*/ +typedef sph_haval_context sph_haval224_5_context; + +/** +* Type for a HAVAL-256/3 context (identical to the common context). +*/ +typedef sph_haval_context sph_haval256_3_context; + +/** +* Type for a HAVAL-256/4 context (identical to the common context). +*/ +typedef sph_haval_context sph_haval256_4_context; + +/** +* Type for a HAVAL-256/5 context (identical to the common context). +*/ +typedef sph_haval_context sph_haval256_5_context; + +/** +* Initialize the context for HAVAL-128/3. +* +* @param cc context to initialize (pointer to a +* sph_haval128_3_context structure) +*/ +void sph_haval128_3_init(void *cc); + +/** +* Process some data bytes for HAVAL-128/3. If len is 0, +* then this function does nothing. +* +* @param cc the HAVAL-128/3 context +* @param data the input data +* @param len the input data length (in bytes) +*/ +void sph_haval128_3(void *cc, const void *data, size_t len); + +/** +* Close a HAVAL-128/3 computation. The output buffer must be wide +* enough to accomodate the result (16 bytes). The context is automatically +* reinitialized. +* +* @param cc the HAVAL-128/3 context +* @param dst the output buffer +*/ +void sph_haval128_3_close(void *cc, void *dst); + +/** +* Close a HAVAL-128/3 computation. Up to 7 extra input bits may be added +* to the input message; these are the n upper bits of +* the ub byte (i.e. the first extra bit has value 128 in +* ub, the second extra bit has value 64, and so on). Other +* bits in ub are ignored. +* +* The output buffer must be wide enough to accomodate the result (16 +* bytes). The context is automatically reinitialized. +* +* @param cc the HAVAL-128/3 context +* @param ub the extra bits +* @param n the number of extra bits (0 to 7) +* @param dst the output buffer +*/ +void sph_haval128_3_addbits_and_close(void *cc, +unsigned ub, unsigned n, void *dst); + +/** +* Initialize the context for HAVAL-128/4. +* +* @param cc context to initialize (pointer to a +* sph_haval128_4_context structure) +*/ +void sph_haval128_4_init(void *cc); + +/** +* Process some data bytes for HAVAL-128/4. If len is 0, +* then this function does nothing. +* +* @param cc the HAVAL-128/4 context +* @param data the input data +* @param len the input data length (in bytes) +*/ +void sph_haval128_4(void *cc, const void *data, size_t len); + +/** +* Close a HAVAL-128/4 computation. The output buffer must be wide +* enough to accomodate the result (16 bytes). The context is automatically +* reinitialized. +* +* @param cc the HAVAL-128/4 context +* @param dst the output buffer +*/ +void sph_haval128_4_close(void *cc, void *dst); + +/** +* Close a HAVAL-128/4 computation. Up to 7 extra input bits may be added +* to the input message; these are the n upper bits of +* the ub byte (i.e. the first extra bit has value 128 in +* ub, the second extra bit has value 64, and so on). Other +* bits in ub are ignored. +* +* The output buffer must be wide enough to accomodate the result (16 +* bytes). The context is automatically reinitialized. +* +* @param cc the HAVAL-128/4 context +* @param ub the extra bits +* @param n the number of extra bits (0 to 7) +* @param dst the output buffer +*/ +void sph_haval128_4_addbits_and_close(void *cc, +unsigned ub, unsigned n, void *dst); + +/** +* Initialize the context for HAVAL-128/5. +* +* @param cc context to initialize (pointer to a +* sph_haval128_5_context structure) +*/ +void sph_haval128_5_init(void *cc); + +/** +* Process some data bytes for HAVAL-128/5. If len is 0, +* then this function does nothing. +* +* @param cc the HAVAL-128/5 context +* @param data the input data +* @param len the input data length (in bytes) +*/ +void sph_haval128_5(void *cc, const void *data, size_t len); + +/** +* Close a HAVAL-128/5 computation. The output buffer must be wide +* enough to accomodate the result (16 bytes). The context is automatically +* reinitialized. +* +* @param cc the HAVAL-128/5 context +* @param dst the output buffer +*/ +void sph_haval128_5_close(void *cc, void *dst); + +/** +* Close a HAVAL-128/5 computation. Up to 7 extra input bits may be added +* to the input message; these are the n upper bits of +* the ub byte (i.e. the first extra bit has value 128 in +* ub, the second extra bit has value 64, and so on). Other +* bits in ub are ignored. +* +* The output buffer must be wide enough to accomodate the result (16 +* bytes). The context is automatically reinitialized. +* +* @param cc the HAVAL-128/5 context +* @param ub the extra bits +* @param n the number of extra bits (0 to 7) +* @param dst the output buffer +*/ +void sph_haval128_5_addbits_and_close(void *cc, +unsigned ub, unsigned n, void *dst); + +/** +* Initialize the context for HAVAL-160/3. +* +* @param cc context to initialize (pointer to a +* sph_haval160_3_context structure) +*/ +void sph_haval160_3_init(void *cc); + +/** +* Process some data bytes for HAVAL-160/3. If len is 0, +* then this function does nothing. +* +* @param cc the HAVAL-160/3 context +* @param data the input data +* @param len the input data length (in bytes) +*/ +void sph_haval160_3(void *cc, const void *data, size_t len); + +/** +* Close a HAVAL-160/3 computation. The output buffer must be wide +* enough to accomodate the result (20 bytes). The context is automatically +* reinitialized. +* +* @param cc the HAVAL-160/3 context +* @param dst the output buffer +*/ +void sph_haval160_3_close(void *cc, void *dst); + +/** +* Close a HAVAL-160/3 computation. Up to 7 extra input bits may be added +* to the input message; these are the n upper bits of +* the ub byte (i.e. the first extra bit has value 128 in +* ub, the second extra bit has value 64, and so on). Other +* bits in ub are ignored. +* +* The output buffer must be wide enough to accomodate the result (20 +* bytes). The context is automatically reinitialized. +* +* @param cc the HAVAL-160/3 context +* @param ub the extra bits +* @param n the number of extra bits (0 to 7) +* @param dst the output buffer +*/ +void sph_haval160_3_addbits_and_close(void *cc, +unsigned ub, unsigned n, void *dst); + +/** +* Initialize the context for HAVAL-160/4. +* +* @param cc context to initialize (pointer to a +* sph_haval160_4_context structure) +*/ +void sph_haval160_4_init(void *cc); + +/** +* Process some data bytes for HAVAL-160/4. If len is 0, +* then this function does nothing. +* +* @param cc the HAVAL-160/4 context +* @param data the input data +* @param len the input data length (in bytes) +*/ +void sph_haval160_4(void *cc, const void *data, size_t len); + +/** +* Close a HAVAL-160/4 computation. The output buffer must be wide +* enough to accomodate the result (20 bytes). The context is automatically +* reinitialized. +* +* @param cc the HAVAL-160/4 context +* @param dst the output buffer +*/ +void sph_haval160_4_close(void *cc, void *dst); + +/** +* Close a HAVAL-160/4 computation. Up to 7 extra input bits may be added +* to the input message; these are the n upper bits of +* the ub byte (i.e. the first extra bit has value 128 in +* ub, the second extra bit has value 64, and so on). Other +* bits in ub are ignored. +* +* The output buffer must be wide enough to accomodate the result (20 +* bytes). The context is automatically reinitialized. +* +* @param cc the HAVAL-160/4 context +* @param ub the extra bits +* @param n the number of extra bits (0 to 7) +* @param dst the output buffer +*/ +void sph_haval160_3_addbits_and_close(void *cc, +unsigned ub, unsigned n, void *dst); + +/** +* Initialize the context for HAVAL-160/5. +* +* @param cc context to initialize (pointer to a +* sph_haval160_5_context structure) +*/ +void sph_haval160_5_init(void *cc); + +/** +* Process some data bytes for HAVAL-160/5. If len is 0, +* then this function does nothing. +* +* @param cc the HAVAL-160/5 context +* @param data the input data +* @param len the input data length (in bytes) +*/ +void sph_haval160_5(void *cc, const void *data, size_t len); + +/** +* Close a HAVAL-160/5 computation. The output buffer must be wide +* enough to accomodate the result (20 bytes). The context is automatically +* reinitialized. +* +* @param cc the HAVAL-160/5 context +* @param dst the output buffer +*/ +void sph_haval160_5_close(void *cc, void *dst); + +/** +* Close a HAVAL-160/5 computation. Up to 7 extra input bits may be added +* to the input message; these are the n upper bits of +* the ub byte (i.e. the first extra bit has value 128 in +* ub, the second extra bit has value 64, and so on). Other +* bits in ub are ignored. +* +* The output buffer must be wide enough to accomodate the result (20 +* bytes). The context is automatically reinitialized. +* +* @param cc the HAVAL-160/5 context +* @param ub the extra bits +* @param n the number of extra bits (0 to 7) +* @param dst the output buffer +*/ +void sph_haval160_5_addbits_and_close(void *cc, +unsigned ub, unsigned n, void *dst); + +/** +* Initialize the context for HAVAL-192/3. +* +* @param cc context to initialize (pointer to a +* sph_haval192_3_context structure) +*/ +void sph_haval192_3_init(void *cc); + +/** +* Process some data bytes for HAVAL-192/3. If len is 0, +* then this function does nothing. +* +* @param cc the HAVAL-192/3 context +* @param data the input data +* @param len the input data length (in bytes) +*/ +void sph_haval192_3(void *cc, const void *data, size_t len); + +/** +* Close a HAVAL-192/3 computation. The output buffer must be wide +* enough to accomodate the result (24 bytes). The context is automatically +* reinitialized. +* +* @param cc the HAVAL-192/3 context +* @param dst the output buffer +*/ +void sph_haval192_3_close(void *cc, void *dst); + +/** +* Close a HAVAL-192/3 computation. Up to 7 extra input bits may be added +* to the input message; these are the n upper bits of +* the ub byte (i.e. the first extra bit has value 128 in +* ub, the second extra bit has value 64, and so on). Other +* bits in ub are ignored. +* +* The output buffer must be wide enough to accomodate the result (24 +* bytes). The context is automatically reinitialized. +* +* @param cc the HAVAL-192/3 context +* @param ub the extra bits +* @param n the number of extra bits (0 to 7) +* @param dst the output buffer +*/ +void sph_haval192_3_addbits_and_close(void *cc, +unsigned ub, unsigned n, void *dst); + +/** +* Initialize the context for HAVAL-192/4. +* +* @param cc context to initialize (pointer to a +* sph_haval192_4_context structure) +*/ +void sph_haval192_4_init(void *cc); + +/** +* Process some data bytes for HAVAL-192/4. If len is 0, +* then this function does nothing. +* +* @param cc the HAVAL-192/4 context +* @param data the input data +* @param len the input data length (in bytes) +*/ +void sph_haval192_4(void *cc, const void *data, size_t len); + +/** +* Close a HAVAL-192/4 computation. The output buffer must be wide +* enough to accomodate the result (24 bytes). The context is automatically +* reinitialized. +* +* @param cc the HAVAL-192/4 context +* @param dst the output buffer +*/ +void sph_haval192_4_close(void *cc, void *dst); + +/** +* Close a HAVAL-192/4 computation. Up to 7 extra input bits may be added +* to the input message; these are the n upper bits of +* the ub byte (i.e. the first extra bit has value 128 in +* ub, the second extra bit has value 64, and so on). Other +* bits in ub are ignored. +* +* The output buffer must be wide enough to accomodate the result (24 +* bytes). The context is automatically reinitialized. +* +* @param cc the HAVAL-192/4 context +* @param ub the extra bits +* @param n the number of extra bits (0 to 7) +* @param dst the output buffer +*/ +void sph_haval192_4_addbits_and_close(void *cc, +unsigned ub, unsigned n, void *dst); + +/** +* Initialize the context for HAVAL-192/5. +* +* @param cc context to initialize (pointer to a +* sph_haval192_5_context structure) +*/ +void sph_haval192_5_init(void *cc); + +/** +* Process some data bytes for HAVAL-192/5. If len is 0, +* then this function does nothing. +* +* @param cc the HAVAL-192/5 context +* @param data the input data +* @param len the input data length (in bytes) +*/ +void sph_haval192_5(void *cc, const void *data, size_t len); + +/** +* Close a HAVAL-192/5 computation. The output buffer must be wide +* enough to accomodate the result (24 bytes). The context is automatically +* reinitialized. +* +* @param cc the HAVAL-192/5 context +* @param dst the output buffer +*/ +void sph_haval192_5_close(void *cc, void *dst); + +/** +* Close a HAVAL-192/5 computation. Up to 7 extra input bits may be added +* to the input message; these are the n upper bits of +* the ub byte (i.e. the first extra bit has value 128 in +* ub, the second extra bit has value 64, and so on). Other +* bits in ub are ignored. +* +* The output buffer must be wide enough to accomodate the result (24 +* bytes). The context is automatically reinitialized. +* +* @param cc the HAVAL-192/5 context +* @param ub the extra bits +* @param n the number of extra bits (0 to 7) +* @param dst the output buffer +*/ +void sph_haval192_5_addbits_and_close(void *cc, +unsigned ub, unsigned n, void *dst); + +/** +* Initialize the context for HAVAL-224/3. +* +* @param cc context to initialize (pointer to a +* sph_haval224_3_context structure) +*/ +void sph_haval224_3_init(void *cc); + +/** +* Process some data bytes for HAVAL-224/3. If len is 0, +* then this function does nothing. +* +* @param cc the HAVAL-224/3 context +* @param data the input data +* @param len the input data length (in bytes) +*/ +void sph_haval224_3(void *cc, const void *data, size_t len); + +/** +* Close a HAVAL-224/3 computation. The output buffer must be wide +* enough to accomodate the result (28 bytes). The context is automatically +* reinitialized. +* +* @param cc the HAVAL-224/3 context +* @param dst the output buffer +*/ +void sph_haval224_3_close(void *cc, void *dst); + +/** +* Close a HAVAL-224/3 computation. Up to 7 extra input bits may be added +* to the input message; these are the n upper bits of +* the ub byte (i.e. the first extra bit has value 128 in +* ub, the second extra bit has value 64, and so on). Other +* bits in ub are ignored. +* +* The output buffer must be wide enough to accomodate the result (28 +* bytes). The context is automatically reinitialized. +* +* @param cc the HAVAL-224/3 context +* @param ub the extra bits +* @param n the number of extra bits (0 to 7) +* @param dst the output buffer +*/ +void sph_haval224_3_addbits_and_close(void *cc, +unsigned ub, unsigned n, void *dst); + +/** +* Initialize the context for HAVAL-224/4. +* +* @param cc context to initialize (pointer to a +* sph_haval224_4_context structure) +*/ +void sph_haval224_4_init(void *cc); + +/** +* Process some data bytes for HAVAL-224/4. If len is 0, +* then this function does nothing. +* +* @param cc the HAVAL-224/4 context +* @param data the input data +* @param len the input data length (in bytes) +*/ +void sph_haval224_4(void *cc, const void *data, size_t len); + +/** +* Close a HAVAL-224/4 computation. The output buffer must be wide +* enough to accomodate the result (28 bytes). The context is automatically +* reinitialized. +* +* @param cc the HAVAL-224/4 context +* @param dst the output buffer +*/ +void sph_haval224_4_close(void *cc, void *dst); + +/** +* Close a HAVAL-224/4 computation. Up to 7 extra input bits may be added +* to the input message; these are the n upper bits of +* the ub byte (i.e. the first extra bit has value 128 in +* ub, the second extra bit has value 64, and so on). Other +* bits in ub are ignored. +* +* The output buffer must be wide enough to accomodate the result (28 +* bytes). The context is automatically reinitialized. +* +* @param cc the HAVAL-224/4 context +* @param ub the extra bits +* @param n the number of extra bits (0 to 7) +* @param dst the output buffer +*/ +void sph_haval224_4_addbits_and_close(void *cc, +unsigned ub, unsigned n, void *dst); + +/** +* Initialize the context for HAVAL-224/5. +* +* @param cc context to initialize (pointer to a +* sph_haval224_5_context structure) +*/ +void sph_haval224_5_init(void *cc); + +/** +* Process some data bytes for HAVAL-224/5. If len is 0, +* then this function does nothing. +* +* @param cc the HAVAL-224/5 context +* @param data the input data +* @param len the input data length (in bytes) +*/ +void sph_haval224_5(void *cc, const void *data, size_t len); + +/** +* Close a HAVAL-224/5 computation. The output buffer must be wide +* enough to accomodate the result (28 bytes). The context is automatically +* reinitialized. +* +* @param cc the HAVAL-224/5 context +* @param dst the output buffer +*/ +void sph_haval224_5_close(void *cc, void *dst); + +/** +* Close a HAVAL-224/5 computation. Up to 7 extra input bits may be added +* to the input message; these are the n upper bits of +* the ub byte (i.e. the first extra bit has value 128 in +* ub, the second extra bit has value 64, and so on). Other +* bits in ub are ignored. +* +* The output buffer must be wide enough to accomodate the result (28 +* bytes). The context is automatically reinitialized. +* +* @param cc the HAVAL-224/5 context +* @param ub the extra bits +* @param n the number of extra bits (0 to 7) +* @param dst the output buffer +*/ +void sph_haval224_5_addbits_and_close(void *cc, +unsigned ub, unsigned n, void *dst); + +/** +* Initialize the context for HAVAL-256/3. +* +* @param cc context to initialize (pointer to a +* sph_haval256_3_context structure) +*/ +void sph_haval256_3_init(void *cc); + +/** +* Process some data bytes for HAVAL-256/3. If len is 0, +* then this function does nothing. +* +* @param cc the HAVAL-256/3 context +* @param data the input data +* @param len the input data length (in bytes) +*/ +void sph_haval256_3(void *cc, const void *data, size_t len); + +/** +* Close a HAVAL-256/3 computation. The output buffer must be wide +* enough to accomodate the result (32 bytes). The context is automatically +* reinitialized. +* +* @param cc the HAVAL-256/3 context +* @param dst the output buffer +*/ +void sph_haval256_3_close(void *cc, void *dst); + +/** +* Close a HAVAL-256/3 computation. Up to 7 extra input bits may be added +* to the input message; these are the n upper bits of +* the ub byte (i.e. the first extra bit has value 128 in +* ub, the second extra bit has value 64, and so on). Other +* bits in ub are ignored. +* +* The output buffer must be wide enough to accomodate the result (32 +* bytes). The context is automatically reinitialized. +* +* @param cc the HAVAL-256/3 context +* @param ub the extra bits +* @param n the number of extra bits (0 to 7) +* @param dst the output buffer +*/ +void sph_haval256_3_addbits_and_close(void *cc, +unsigned ub, unsigned n, void *dst); + +/** +* Initialize the context for HAVAL-256/4. +* +* @param cc context to initialize (pointer to a +* sph_haval256_4_context structure) +*/ +void sph_haval256_4_init(void *cc); + +/** +* Process some data bytes for HAVAL-256/4. If len is 0, +* then this function does nothing. +* +* @param cc the HAVAL-256/4 context +* @param data the input data +* @param len the input data length (in bytes) +*/ +void sph_haval256_4(void *cc, const void *data, size_t len); + +/** +* Close a HAVAL-256/4 computation. The output buffer must be wide +* enough to accomodate the result (32 bytes). The context is automatically +* reinitialized. +* +* @param cc the HAVAL-256/4 context +* @param dst the output buffer +*/ +void sph_haval256_4_close(void *cc, void *dst); + +/** +* Close a HAVAL-256/4 computation. Up to 7 extra input bits may be added +* to the input message; these are the n upper bits of +* the ub byte (i.e. the first extra bit has value 128 in +* ub, the second extra bit has value 64, and so on). Other +* bits in ub are ignored. +* +* The output buffer must be wide enough to accomodate the result (32 +* bytes). The context is automatically reinitialized. +* +* @param cc the HAVAL-256/4 context +* @param ub the extra bits +* @param n the number of extra bits (0 to 7) +* @param dst the output buffer +*/ +void sph_haval256_4_addbits_and_close(void *cc, +unsigned ub, unsigned n, void *dst); + +/** +* Initialize the context for HAVAL-256/5. +* +* @param cc context to initialize (pointer to a +* sph_haval256_5_context structure) +*/ +void sph_haval256_5_init(void *cc); + +/** +* Process some data bytes for HAVAL-256/5. If len is 0, +* then this function does nothing. +* +* @param cc the HAVAL-256/5 context +* @param data the input data +* @param len the input data length (in bytes) +*/ +void sph_haval256_5(void *cc, const void *data, size_t len); + +/** +* Close a HAVAL-256/5 computation. The output buffer must be wide +* enough to accomodate the result (32 bytes). The context is automatically +* reinitialized. +* +* @param cc the HAVAL-256/5 context +* @param dst the output buffer +*/ +void sph_haval256_5_close(void *cc, void *dst); + +/** +* Close a HAVAL-256/5 computation. Up to 7 extra input bits may be added +* to the input message; these are the n upper bits of +* the ub byte (i.e. the first extra bit has value 128 in +* ub, the second extra bit has value 64, and so on). Other +* bits in ub are ignored. +* +* The output buffer must be wide enough to accomodate the result (32 +* bytes). The context is automatically reinitialized. +* +* @param cc the HAVAL-256/5 context +* @param ub the extra bits +* @param n the number of extra bits (0 to 7) +* @param dst the output buffer +*/ +void sph_haval256_5_addbits_and_close(void *cc, +unsigned ub, unsigned n, void *dst); + +/** +* Apply the HAVAL compression function on the provided data. The +* msg parameter contains the 32 32-bit input blocks, +* as numerical values (hence after the little-endian decoding). The +* val parameter contains the 8 32-bit input blocks for +* the compression function; the output is written in place in this +* array. This function uses three internal passes. +* +* @param msg the message block (32 values) +* @param val the function 256-bit input and output +*/ +void sph_haval_3_comp(const sph_u32 msg[32], sph_u32 val[8]); + +/** +* Apply the HAVAL compression function on the provided data. The +* msg parameter contains the 32 32-bit input blocks, +* as numerical values (hence after the little-endian decoding). The +* val parameter contains the 8 32-bit input blocks for +* the compression function; the output is written in place in this +* array. This function uses four internal passes. +* +* @param msg the message block (32 values) +* @param val the function 256-bit input and output +*/ +void sph_haval_4_comp(const sph_u32 msg[32], sph_u32 val[8]); + +/** +* Apply the HAVAL compression function on the provided data. The +* msg parameter contains the 32 32-bit input blocks, +* as numerical values (hence after the little-endian decoding). The +* val parameter contains the 8 32-bit input blocks for +* the compression function; the output is written in place in this +* array. This function uses five internal passes. +* +* @param msg the message block (32 values) +* @param val the function 256-bit input and output +*/ +void sph_haval_5_comp(const sph_u32 msg[32], sph_u32 val[8]); + +#endif diff --git a/src/Native/libmultihash/sha3/sph_sha2.c b/src/Native/libmultihash/sha3/sph_sha2.c new file mode 100644 index 000000000..2c8de457a --- /dev/null +++ b/src/Native/libmultihash/sha3/sph_sha2.c @@ -0,0 +1,691 @@ +/* $Id: sha2.c 227 2010-06-16 17:28:38Z tp $ */ +/* + * SHA-224 / SHA-256 implementation. + * + * ==========================(LICENSE BEGIN)============================ + * + * Copyright (c) 2007-2010 Projet RNRT SAPHIR + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * ===========================(LICENSE END)============================= + * + * @author Thomas Pornin + */ + +#include +#include + +#include "sph_sha2.h" + +#if SPH_SMALL_FOOTPRINT && !defined SPH_SMALL_FOOTPRINT_SHA2 +#define SPH_SMALL_FOOTPRINT_SHA2 1 +#endif + +#define CH(X, Y, Z) ((((Y) ^ (Z)) & (X)) ^ (Z)) +#define MAJ(X, Y, Z) (((Y) & (Z)) | (((Y) | (Z)) & (X))) + +#define ROTR SPH_ROTR32 + +#define BSG2_0(x) (ROTR(x, 2) ^ ROTR(x, 13) ^ ROTR(x, 22)) +#define BSG2_1(x) (ROTR(x, 6) ^ ROTR(x, 11) ^ ROTR(x, 25)) +#define SSG2_0(x) (ROTR(x, 7) ^ ROTR(x, 18) ^ SPH_T32((x) >> 3)) +#define SSG2_1(x) (ROTR(x, 17) ^ ROTR(x, 19) ^ SPH_T32((x) >> 10)) + +static const sph_u32 H224[8] = { + SPH_C32(0xC1059ED8), SPH_C32(0x367CD507), SPH_C32(0x3070DD17), + SPH_C32(0xF70E5939), SPH_C32(0xFFC00B31), SPH_C32(0x68581511), + SPH_C32(0x64F98FA7), SPH_C32(0xBEFA4FA4) +}; + +static const sph_u32 H256[8] = { + SPH_C32(0x6A09E667), SPH_C32(0xBB67AE85), SPH_C32(0x3C6EF372), + SPH_C32(0xA54FF53A), SPH_C32(0x510E527F), SPH_C32(0x9B05688C), + SPH_C32(0x1F83D9AB), SPH_C32(0x5BE0CD19) +}; + +/* + * The SHA2_ROUND_BODY defines the body for a SHA-224 / SHA-256 + * compression function implementation. The "in" parameter should + * evaluate, when applied to a numerical input parameter from 0 to 15, + * to an expression which yields the corresponding input block. The "r" + * parameter should evaluate to an array or pointer expression + * designating the array of 8 words which contains the input and output + * of the compression function. + */ + +#if SPH_SMALL_FOOTPRINT_SHA2 + +static const sph_u32 K[64] = { + SPH_C32(0x428A2F98), SPH_C32(0x71374491), + SPH_C32(0xB5C0FBCF), SPH_C32(0xE9B5DBA5), + SPH_C32(0x3956C25B), SPH_C32(0x59F111F1), + SPH_C32(0x923F82A4), SPH_C32(0xAB1C5ED5), + SPH_C32(0xD807AA98), SPH_C32(0x12835B01), + SPH_C32(0x243185BE), SPH_C32(0x550C7DC3), + SPH_C32(0x72BE5D74), SPH_C32(0x80DEB1FE), + SPH_C32(0x9BDC06A7), SPH_C32(0xC19BF174), + SPH_C32(0xE49B69C1), SPH_C32(0xEFBE4786), + SPH_C32(0x0FC19DC6), SPH_C32(0x240CA1CC), + SPH_C32(0x2DE92C6F), SPH_C32(0x4A7484AA), + SPH_C32(0x5CB0A9DC), SPH_C32(0x76F988DA), + SPH_C32(0x983E5152), SPH_C32(0xA831C66D), + SPH_C32(0xB00327C8), SPH_C32(0xBF597FC7), + SPH_C32(0xC6E00BF3), SPH_C32(0xD5A79147), + SPH_C32(0x06CA6351), SPH_C32(0x14292967), + SPH_C32(0x27B70A85), SPH_C32(0x2E1B2138), + SPH_C32(0x4D2C6DFC), SPH_C32(0x53380D13), + SPH_C32(0x650A7354), SPH_C32(0x766A0ABB), + SPH_C32(0x81C2C92E), SPH_C32(0x92722C85), + SPH_C32(0xA2BFE8A1), SPH_C32(0xA81A664B), + SPH_C32(0xC24B8B70), SPH_C32(0xC76C51A3), + SPH_C32(0xD192E819), SPH_C32(0xD6990624), + SPH_C32(0xF40E3585), SPH_C32(0x106AA070), + SPH_C32(0x19A4C116), SPH_C32(0x1E376C08), + SPH_C32(0x2748774C), SPH_C32(0x34B0BCB5), + SPH_C32(0x391C0CB3), SPH_C32(0x4ED8AA4A), + SPH_C32(0x5B9CCA4F), SPH_C32(0x682E6FF3), + SPH_C32(0x748F82EE), SPH_C32(0x78A5636F), + SPH_C32(0x84C87814), SPH_C32(0x8CC70208), + SPH_C32(0x90BEFFFA), SPH_C32(0xA4506CEB), + SPH_C32(0xBEF9A3F7), SPH_C32(0xC67178F2) +}; + +#define SHA2_MEXP1(in, pc) do { \ + W[pc] = in(pc); \ + } while (0) + +#define SHA2_MEXP2(in, pc) do { \ + W[(pc) & 0x0F] = SPH_T32(SSG2_1(W[((pc) - 2) & 0x0F]) \ + + W[((pc) - 7) & 0x0F] \ + + SSG2_0(W[((pc) - 15) & 0x0F]) + W[(pc) & 0x0F]); \ + } while (0) + +#define SHA2_STEPn(n, a, b, c, d, e, f, g, h, in, pc) do { \ + sph_u32 t1, t2; \ + SHA2_MEXP ## n(in, pc); \ + t1 = SPH_T32(h + BSG2_1(e) + CH(e, f, g) \ + + K[pcount + (pc)] + W[(pc) & 0x0F]); \ + t2 = SPH_T32(BSG2_0(a) + MAJ(a, b, c)); \ + d = SPH_T32(d + t1); \ + h = SPH_T32(t1 + t2); \ + } while (0) + +#define SHA2_STEP1(a, b, c, d, e, f, g, h, in, pc) \ + SHA2_STEPn(1, a, b, c, d, e, f, g, h, in, pc) +#define SHA2_STEP2(a, b, c, d, e, f, g, h, in, pc) \ + SHA2_STEPn(2, a, b, c, d, e, f, g, h, in, pc) + +#define SHA2_ROUND_BODY(in, r) do { \ + sph_u32 A, B, C, D, E, F, G, H; \ + sph_u32 W[16]; \ + unsigned pcount; \ + \ + A = (r)[0]; \ + B = (r)[1]; \ + C = (r)[2]; \ + D = (r)[3]; \ + E = (r)[4]; \ + F = (r)[5]; \ + G = (r)[6]; \ + H = (r)[7]; \ + pcount = 0; \ + SHA2_STEP1(A, B, C, D, E, F, G, H, in, 0); \ + SHA2_STEP1(H, A, B, C, D, E, F, G, in, 1); \ + SHA2_STEP1(G, H, A, B, C, D, E, F, in, 2); \ + SHA2_STEP1(F, G, H, A, B, C, D, E, in, 3); \ + SHA2_STEP1(E, F, G, H, A, B, C, D, in, 4); \ + SHA2_STEP1(D, E, F, G, H, A, B, C, in, 5); \ + SHA2_STEP1(C, D, E, F, G, H, A, B, in, 6); \ + SHA2_STEP1(B, C, D, E, F, G, H, A, in, 7); \ + SHA2_STEP1(A, B, C, D, E, F, G, H, in, 8); \ + SHA2_STEP1(H, A, B, C, D, E, F, G, in, 9); \ + SHA2_STEP1(G, H, A, B, C, D, E, F, in, 10); \ + SHA2_STEP1(F, G, H, A, B, C, D, E, in, 11); \ + SHA2_STEP1(E, F, G, H, A, B, C, D, in, 12); \ + SHA2_STEP1(D, E, F, G, H, A, B, C, in, 13); \ + SHA2_STEP1(C, D, E, F, G, H, A, B, in, 14); \ + SHA2_STEP1(B, C, D, E, F, G, H, A, in, 15); \ + for (pcount = 16; pcount < 64; pcount += 16) { \ + SHA2_STEP2(A, B, C, D, E, F, G, H, in, 0); \ + SHA2_STEP2(H, A, B, C, D, E, F, G, in, 1); \ + SHA2_STEP2(G, H, A, B, C, D, E, F, in, 2); \ + SHA2_STEP2(F, G, H, A, B, C, D, E, in, 3); \ + SHA2_STEP2(E, F, G, H, A, B, C, D, in, 4); \ + SHA2_STEP2(D, E, F, G, H, A, B, C, in, 5); \ + SHA2_STEP2(C, D, E, F, G, H, A, B, in, 6); \ + SHA2_STEP2(B, C, D, E, F, G, H, A, in, 7); \ + SHA2_STEP2(A, B, C, D, E, F, G, H, in, 8); \ + SHA2_STEP2(H, A, B, C, D, E, F, G, in, 9); \ + SHA2_STEP2(G, H, A, B, C, D, E, F, in, 10); \ + SHA2_STEP2(F, G, H, A, B, C, D, E, in, 11); \ + SHA2_STEP2(E, F, G, H, A, B, C, D, in, 12); \ + SHA2_STEP2(D, E, F, G, H, A, B, C, in, 13); \ + SHA2_STEP2(C, D, E, F, G, H, A, B, in, 14); \ + SHA2_STEP2(B, C, D, E, F, G, H, A, in, 15); \ + } \ + (r)[0] = SPH_T32((r)[0] + A); \ + (r)[1] = SPH_T32((r)[1] + B); \ + (r)[2] = SPH_T32((r)[2] + C); \ + (r)[3] = SPH_T32((r)[3] + D); \ + (r)[4] = SPH_T32((r)[4] + E); \ + (r)[5] = SPH_T32((r)[5] + F); \ + (r)[6] = SPH_T32((r)[6] + G); \ + (r)[7] = SPH_T32((r)[7] + H); \ + } while (0) + +#else + +#define SHA2_ROUND_BODY(in, r) do { \ + sph_u32 A, B, C, D, E, F, G, H, T1, T2; \ + sph_u32 W00, W01, W02, W03, W04, W05, W06, W07; \ + sph_u32 W08, W09, W10, W11, W12, W13, W14, W15; \ + \ + A = (r)[0]; \ + B = (r)[1]; \ + C = (r)[2]; \ + D = (r)[3]; \ + E = (r)[4]; \ + F = (r)[5]; \ + G = (r)[6]; \ + H = (r)[7]; \ + W00 = in(0); \ + T1 = SPH_T32(H + BSG2_1(E) + CH(E, F, G) \ + + SPH_C32(0x428A2F98) + W00); \ + T2 = SPH_T32(BSG2_0(A) + MAJ(A, B, C)); \ + D = SPH_T32(D + T1); \ + H = SPH_T32(T1 + T2); \ + W01 = in(1); \ + T1 = SPH_T32(G + BSG2_1(D) + CH(D, E, F) \ + + SPH_C32(0x71374491) + W01); \ + T2 = SPH_T32(BSG2_0(H) + MAJ(H, A, B)); \ + C = SPH_T32(C + T1); \ + G = SPH_T32(T1 + T2); \ + W02 = in(2); \ + T1 = SPH_T32(F + BSG2_1(C) + CH(C, D, E) \ + + SPH_C32(0xB5C0FBCF) + W02); \ + T2 = SPH_T32(BSG2_0(G) + MAJ(G, H, A)); \ + B = SPH_T32(B + T1); \ + F = SPH_T32(T1 + T2); \ + W03 = in(3); \ + T1 = SPH_T32(E + BSG2_1(B) + CH(B, C, D) \ + + SPH_C32(0xE9B5DBA5) + W03); \ + T2 = SPH_T32(BSG2_0(F) + MAJ(F, G, H)); \ + A = SPH_T32(A + T1); \ + E = SPH_T32(T1 + T2); \ + W04 = in(4); \ + T1 = SPH_T32(D + BSG2_1(A) + CH(A, B, C) \ + + SPH_C32(0x3956C25B) + W04); \ + T2 = SPH_T32(BSG2_0(E) + MAJ(E, F, G)); \ + H = SPH_T32(H + T1); \ + D = SPH_T32(T1 + T2); \ + W05 = in(5); \ + T1 = SPH_T32(C + BSG2_1(H) + CH(H, A, B) \ + + SPH_C32(0x59F111F1) + W05); \ + T2 = SPH_T32(BSG2_0(D) + MAJ(D, E, F)); \ + G = SPH_T32(G + T1); \ + C = SPH_T32(T1 + T2); \ + W06 = in(6); \ + T1 = SPH_T32(B + BSG2_1(G) + CH(G, H, A) \ + + SPH_C32(0x923F82A4) + W06); \ + T2 = SPH_T32(BSG2_0(C) + MAJ(C, D, E)); \ + F = SPH_T32(F + T1); \ + B = SPH_T32(T1 + T2); \ + W07 = in(7); \ + T1 = SPH_T32(A + BSG2_1(F) + CH(F, G, H) \ + + SPH_C32(0xAB1C5ED5) + W07); \ + T2 = SPH_T32(BSG2_0(B) + MAJ(B, C, D)); \ + E = SPH_T32(E + T1); \ + A = SPH_T32(T1 + T2); \ + W08 = in(8); \ + T1 = SPH_T32(H + BSG2_1(E) + CH(E, F, G) \ + + SPH_C32(0xD807AA98) + W08); \ + T2 = SPH_T32(BSG2_0(A) + MAJ(A, B, C)); \ + D = SPH_T32(D + T1); \ + H = SPH_T32(T1 + T2); \ + W09 = in(9); \ + T1 = SPH_T32(G + BSG2_1(D) + CH(D, E, F) \ + + SPH_C32(0x12835B01) + W09); \ + T2 = SPH_T32(BSG2_0(H) + MAJ(H, A, B)); \ + C = SPH_T32(C + T1); \ + G = SPH_T32(T1 + T2); \ + W10 = in(10); \ + T1 = SPH_T32(F + BSG2_1(C) + CH(C, D, E) \ + + SPH_C32(0x243185BE) + W10); \ + T2 = SPH_T32(BSG2_0(G) + MAJ(G, H, A)); \ + B = SPH_T32(B + T1); \ + F = SPH_T32(T1 + T2); \ + W11 = in(11); \ + T1 = SPH_T32(E + BSG2_1(B) + CH(B, C, D) \ + + SPH_C32(0x550C7DC3) + W11); \ + T2 = SPH_T32(BSG2_0(F) + MAJ(F, G, H)); \ + A = SPH_T32(A + T1); \ + E = SPH_T32(T1 + T2); \ + W12 = in(12); \ + T1 = SPH_T32(D + BSG2_1(A) + CH(A, B, C) \ + + SPH_C32(0x72BE5D74) + W12); \ + T2 = SPH_T32(BSG2_0(E) + MAJ(E, F, G)); \ + H = SPH_T32(H + T1); \ + D = SPH_T32(T1 + T2); \ + W13 = in(13); \ + T1 = SPH_T32(C + BSG2_1(H) + CH(H, A, B) \ + + SPH_C32(0x80DEB1FE) + W13); \ + T2 = SPH_T32(BSG2_0(D) + MAJ(D, E, F)); \ + G = SPH_T32(G + T1); \ + C = SPH_T32(T1 + T2); \ + W14 = in(14); \ + T1 = SPH_T32(B + BSG2_1(G) + CH(G, H, A) \ + + SPH_C32(0x9BDC06A7) + W14); \ + T2 = SPH_T32(BSG2_0(C) + MAJ(C, D, E)); \ + F = SPH_T32(F + T1); \ + B = SPH_T32(T1 + T2); \ + W15 = in(15); \ + T1 = SPH_T32(A + BSG2_1(F) + CH(F, G, H) \ + + SPH_C32(0xC19BF174) + W15); \ + T2 = SPH_T32(BSG2_0(B) + MAJ(B, C, D)); \ + E = SPH_T32(E + T1); \ + A = SPH_T32(T1 + T2); \ + W00 = SPH_T32(SSG2_1(W14) + W09 + SSG2_0(W01) + W00); \ + T1 = SPH_T32(H + BSG2_1(E) + CH(E, F, G) \ + + SPH_C32(0xE49B69C1) + W00); \ + T2 = SPH_T32(BSG2_0(A) + MAJ(A, B, C)); \ + D = SPH_T32(D + T1); \ + H = SPH_T32(T1 + T2); \ + W01 = SPH_T32(SSG2_1(W15) + W10 + SSG2_0(W02) + W01); \ + T1 = SPH_T32(G + BSG2_1(D) + CH(D, E, F) \ + + SPH_C32(0xEFBE4786) + W01); \ + T2 = SPH_T32(BSG2_0(H) + MAJ(H, A, B)); \ + C = SPH_T32(C + T1); \ + G = SPH_T32(T1 + T2); \ + W02 = SPH_T32(SSG2_1(W00) + W11 + SSG2_0(W03) + W02); \ + T1 = SPH_T32(F + BSG2_1(C) + CH(C, D, E) \ + + SPH_C32(0x0FC19DC6) + W02); \ + T2 = SPH_T32(BSG2_0(G) + MAJ(G, H, A)); \ + B = SPH_T32(B + T1); \ + F = SPH_T32(T1 + T2); \ + W03 = SPH_T32(SSG2_1(W01) + W12 + SSG2_0(W04) + W03); \ + T1 = SPH_T32(E + BSG2_1(B) + CH(B, C, D) \ + + SPH_C32(0x240CA1CC) + W03); \ + T2 = SPH_T32(BSG2_0(F) + MAJ(F, G, H)); \ + A = SPH_T32(A + T1); \ + E = SPH_T32(T1 + T2); \ + W04 = SPH_T32(SSG2_1(W02) + W13 + SSG2_0(W05) + W04); \ + T1 = SPH_T32(D + BSG2_1(A) + CH(A, B, C) \ + + SPH_C32(0x2DE92C6F) + W04); \ + T2 = SPH_T32(BSG2_0(E) + MAJ(E, F, G)); \ + H = SPH_T32(H + T1); \ + D = SPH_T32(T1 + T2); \ + W05 = SPH_T32(SSG2_1(W03) + W14 + SSG2_0(W06) + W05); \ + T1 = SPH_T32(C + BSG2_1(H) + CH(H, A, B) \ + + SPH_C32(0x4A7484AA) + W05); \ + T2 = SPH_T32(BSG2_0(D) + MAJ(D, E, F)); \ + G = SPH_T32(G + T1); \ + C = SPH_T32(T1 + T2); \ + W06 = SPH_T32(SSG2_1(W04) + W15 + SSG2_0(W07) + W06); \ + T1 = SPH_T32(B + BSG2_1(G) + CH(G, H, A) \ + + SPH_C32(0x5CB0A9DC) + W06); \ + T2 = SPH_T32(BSG2_0(C) + MAJ(C, D, E)); \ + F = SPH_T32(F + T1); \ + B = SPH_T32(T1 + T2); \ + W07 = SPH_T32(SSG2_1(W05) + W00 + SSG2_0(W08) + W07); \ + T1 = SPH_T32(A + BSG2_1(F) + CH(F, G, H) \ + + SPH_C32(0x76F988DA) + W07); \ + T2 = SPH_T32(BSG2_0(B) + MAJ(B, C, D)); \ + E = SPH_T32(E + T1); \ + A = SPH_T32(T1 + T2); \ + W08 = SPH_T32(SSG2_1(W06) + W01 + SSG2_0(W09) + W08); \ + T1 = SPH_T32(H + BSG2_1(E) + CH(E, F, G) \ + + SPH_C32(0x983E5152) + W08); \ + T2 = SPH_T32(BSG2_0(A) + MAJ(A, B, C)); \ + D = SPH_T32(D + T1); \ + H = SPH_T32(T1 + T2); \ + W09 = SPH_T32(SSG2_1(W07) + W02 + SSG2_0(W10) + W09); \ + T1 = SPH_T32(G + BSG2_1(D) + CH(D, E, F) \ + + SPH_C32(0xA831C66D) + W09); \ + T2 = SPH_T32(BSG2_0(H) + MAJ(H, A, B)); \ + C = SPH_T32(C + T1); \ + G = SPH_T32(T1 + T2); \ + W10 = SPH_T32(SSG2_1(W08) + W03 + SSG2_0(W11) + W10); \ + T1 = SPH_T32(F + BSG2_1(C) + CH(C, D, E) \ + + SPH_C32(0xB00327C8) + W10); \ + T2 = SPH_T32(BSG2_0(G) + MAJ(G, H, A)); \ + B = SPH_T32(B + T1); \ + F = SPH_T32(T1 + T2); \ + W11 = SPH_T32(SSG2_1(W09) + W04 + SSG2_0(W12) + W11); \ + T1 = SPH_T32(E + BSG2_1(B) + CH(B, C, D) \ + + SPH_C32(0xBF597FC7) + W11); \ + T2 = SPH_T32(BSG2_0(F) + MAJ(F, G, H)); \ + A = SPH_T32(A + T1); \ + E = SPH_T32(T1 + T2); \ + W12 = SPH_T32(SSG2_1(W10) + W05 + SSG2_0(W13) + W12); \ + T1 = SPH_T32(D + BSG2_1(A) + CH(A, B, C) \ + + SPH_C32(0xC6E00BF3) + W12); \ + T2 = SPH_T32(BSG2_0(E) + MAJ(E, F, G)); \ + H = SPH_T32(H + T1); \ + D = SPH_T32(T1 + T2); \ + W13 = SPH_T32(SSG2_1(W11) + W06 + SSG2_0(W14) + W13); \ + T1 = SPH_T32(C + BSG2_1(H) + CH(H, A, B) \ + + SPH_C32(0xD5A79147) + W13); \ + T2 = SPH_T32(BSG2_0(D) + MAJ(D, E, F)); \ + G = SPH_T32(G + T1); \ + C = SPH_T32(T1 + T2); \ + W14 = SPH_T32(SSG2_1(W12) + W07 + SSG2_0(W15) + W14); \ + T1 = SPH_T32(B + BSG2_1(G) + CH(G, H, A) \ + + SPH_C32(0x06CA6351) + W14); \ + T2 = SPH_T32(BSG2_0(C) + MAJ(C, D, E)); \ + F = SPH_T32(F + T1); \ + B = SPH_T32(T1 + T2); \ + W15 = SPH_T32(SSG2_1(W13) + W08 + SSG2_0(W00) + W15); \ + T1 = SPH_T32(A + BSG2_1(F) + CH(F, G, H) \ + + SPH_C32(0x14292967) + W15); \ + T2 = SPH_T32(BSG2_0(B) + MAJ(B, C, D)); \ + E = SPH_T32(E + T1); \ + A = SPH_T32(T1 + T2); \ + W00 = SPH_T32(SSG2_1(W14) + W09 + SSG2_0(W01) + W00); \ + T1 = SPH_T32(H + BSG2_1(E) + CH(E, F, G) \ + + SPH_C32(0x27B70A85) + W00); \ + T2 = SPH_T32(BSG2_0(A) + MAJ(A, B, C)); \ + D = SPH_T32(D + T1); \ + H = SPH_T32(T1 + T2); \ + W01 = SPH_T32(SSG2_1(W15) + W10 + SSG2_0(W02) + W01); \ + T1 = SPH_T32(G + BSG2_1(D) + CH(D, E, F) \ + + SPH_C32(0x2E1B2138) + W01); \ + T2 = SPH_T32(BSG2_0(H) + MAJ(H, A, B)); \ + C = SPH_T32(C + T1); \ + G = SPH_T32(T1 + T2); \ + W02 = SPH_T32(SSG2_1(W00) + W11 + SSG2_0(W03) + W02); \ + T1 = SPH_T32(F + BSG2_1(C) + CH(C, D, E) \ + + SPH_C32(0x4D2C6DFC) + W02); \ + T2 = SPH_T32(BSG2_0(G) + MAJ(G, H, A)); \ + B = SPH_T32(B + T1); \ + F = SPH_T32(T1 + T2); \ + W03 = SPH_T32(SSG2_1(W01) + W12 + SSG2_0(W04) + W03); \ + T1 = SPH_T32(E + BSG2_1(B) + CH(B, C, D) \ + + SPH_C32(0x53380D13) + W03); \ + T2 = SPH_T32(BSG2_0(F) + MAJ(F, G, H)); \ + A = SPH_T32(A + T1); \ + E = SPH_T32(T1 + T2); \ + W04 = SPH_T32(SSG2_1(W02) + W13 + SSG2_0(W05) + W04); \ + T1 = SPH_T32(D + BSG2_1(A) + CH(A, B, C) \ + + SPH_C32(0x650A7354) + W04); \ + T2 = SPH_T32(BSG2_0(E) + MAJ(E, F, G)); \ + H = SPH_T32(H + T1); \ + D = SPH_T32(T1 + T2); \ + W05 = SPH_T32(SSG2_1(W03) + W14 + SSG2_0(W06) + W05); \ + T1 = SPH_T32(C + BSG2_1(H) + CH(H, A, B) \ + + SPH_C32(0x766A0ABB) + W05); \ + T2 = SPH_T32(BSG2_0(D) + MAJ(D, E, F)); \ + G = SPH_T32(G + T1); \ + C = SPH_T32(T1 + T2); \ + W06 = SPH_T32(SSG2_1(W04) + W15 + SSG2_0(W07) + W06); \ + T1 = SPH_T32(B + BSG2_1(G) + CH(G, H, A) \ + + SPH_C32(0x81C2C92E) + W06); \ + T2 = SPH_T32(BSG2_0(C) + MAJ(C, D, E)); \ + F = SPH_T32(F + T1); \ + B = SPH_T32(T1 + T2); \ + W07 = SPH_T32(SSG2_1(W05) + W00 + SSG2_0(W08) + W07); \ + T1 = SPH_T32(A + BSG2_1(F) + CH(F, G, H) \ + + SPH_C32(0x92722C85) + W07); \ + T2 = SPH_T32(BSG2_0(B) + MAJ(B, C, D)); \ + E = SPH_T32(E + T1); \ + A = SPH_T32(T1 + T2); \ + W08 = SPH_T32(SSG2_1(W06) + W01 + SSG2_0(W09) + W08); \ + T1 = SPH_T32(H + BSG2_1(E) + CH(E, F, G) \ + + SPH_C32(0xA2BFE8A1) + W08); \ + T2 = SPH_T32(BSG2_0(A) + MAJ(A, B, C)); \ + D = SPH_T32(D + T1); \ + H = SPH_T32(T1 + T2); \ + W09 = SPH_T32(SSG2_1(W07) + W02 + SSG2_0(W10) + W09); \ + T1 = SPH_T32(G + BSG2_1(D) + CH(D, E, F) \ + + SPH_C32(0xA81A664B) + W09); \ + T2 = SPH_T32(BSG2_0(H) + MAJ(H, A, B)); \ + C = SPH_T32(C + T1); \ + G = SPH_T32(T1 + T2); \ + W10 = SPH_T32(SSG2_1(W08) + W03 + SSG2_0(W11) + W10); \ + T1 = SPH_T32(F + BSG2_1(C) + CH(C, D, E) \ + + SPH_C32(0xC24B8B70) + W10); \ + T2 = SPH_T32(BSG2_0(G) + MAJ(G, H, A)); \ + B = SPH_T32(B + T1); \ + F = SPH_T32(T1 + T2); \ + W11 = SPH_T32(SSG2_1(W09) + W04 + SSG2_0(W12) + W11); \ + T1 = SPH_T32(E + BSG2_1(B) + CH(B, C, D) \ + + SPH_C32(0xC76C51A3) + W11); \ + T2 = SPH_T32(BSG2_0(F) + MAJ(F, G, H)); \ + A = SPH_T32(A + T1); \ + E = SPH_T32(T1 + T2); \ + W12 = SPH_T32(SSG2_1(W10) + W05 + SSG2_0(W13) + W12); \ + T1 = SPH_T32(D + BSG2_1(A) + CH(A, B, C) \ + + SPH_C32(0xD192E819) + W12); \ + T2 = SPH_T32(BSG2_0(E) + MAJ(E, F, G)); \ + H = SPH_T32(H + T1); \ + D = SPH_T32(T1 + T2); \ + W13 = SPH_T32(SSG2_1(W11) + W06 + SSG2_0(W14) + W13); \ + T1 = SPH_T32(C + BSG2_1(H) + CH(H, A, B) \ + + SPH_C32(0xD6990624) + W13); \ + T2 = SPH_T32(BSG2_0(D) + MAJ(D, E, F)); \ + G = SPH_T32(G + T1); \ + C = SPH_T32(T1 + T2); \ + W14 = SPH_T32(SSG2_1(W12) + W07 + SSG2_0(W15) + W14); \ + T1 = SPH_T32(B + BSG2_1(G) + CH(G, H, A) \ + + SPH_C32(0xF40E3585) + W14); \ + T2 = SPH_T32(BSG2_0(C) + MAJ(C, D, E)); \ + F = SPH_T32(F + T1); \ + B = SPH_T32(T1 + T2); \ + W15 = SPH_T32(SSG2_1(W13) + W08 + SSG2_0(W00) + W15); \ + T1 = SPH_T32(A + BSG2_1(F) + CH(F, G, H) \ + + SPH_C32(0x106AA070) + W15); \ + T2 = SPH_T32(BSG2_0(B) + MAJ(B, C, D)); \ + E = SPH_T32(E + T1); \ + A = SPH_T32(T1 + T2); \ + W00 = SPH_T32(SSG2_1(W14) + W09 + SSG2_0(W01) + W00); \ + T1 = SPH_T32(H + BSG2_1(E) + CH(E, F, G) \ + + SPH_C32(0x19A4C116) + W00); \ + T2 = SPH_T32(BSG2_0(A) + MAJ(A, B, C)); \ + D = SPH_T32(D + T1); \ + H = SPH_T32(T1 + T2); \ + W01 = SPH_T32(SSG2_1(W15) + W10 + SSG2_0(W02) + W01); \ + T1 = SPH_T32(G + BSG2_1(D) + CH(D, E, F) \ + + SPH_C32(0x1E376C08) + W01); \ + T2 = SPH_T32(BSG2_0(H) + MAJ(H, A, B)); \ + C = SPH_T32(C + T1); \ + G = SPH_T32(T1 + T2); \ + W02 = SPH_T32(SSG2_1(W00) + W11 + SSG2_0(W03) + W02); \ + T1 = SPH_T32(F + BSG2_1(C) + CH(C, D, E) \ + + SPH_C32(0x2748774C) + W02); \ + T2 = SPH_T32(BSG2_0(G) + MAJ(G, H, A)); \ + B = SPH_T32(B + T1); \ + F = SPH_T32(T1 + T2); \ + W03 = SPH_T32(SSG2_1(W01) + W12 + SSG2_0(W04) + W03); \ + T1 = SPH_T32(E + BSG2_1(B) + CH(B, C, D) \ + + SPH_C32(0x34B0BCB5) + W03); \ + T2 = SPH_T32(BSG2_0(F) + MAJ(F, G, H)); \ + A = SPH_T32(A + T1); \ + E = SPH_T32(T1 + T2); \ + W04 = SPH_T32(SSG2_1(W02) + W13 + SSG2_0(W05) + W04); \ + T1 = SPH_T32(D + BSG2_1(A) + CH(A, B, C) \ + + SPH_C32(0x391C0CB3) + W04); \ + T2 = SPH_T32(BSG2_0(E) + MAJ(E, F, G)); \ + H = SPH_T32(H + T1); \ + D = SPH_T32(T1 + T2); \ + W05 = SPH_T32(SSG2_1(W03) + W14 + SSG2_0(W06) + W05); \ + T1 = SPH_T32(C + BSG2_1(H) + CH(H, A, B) \ + + SPH_C32(0x4ED8AA4A) + W05); \ + T2 = SPH_T32(BSG2_0(D) + MAJ(D, E, F)); \ + G = SPH_T32(G + T1); \ + C = SPH_T32(T1 + T2); \ + W06 = SPH_T32(SSG2_1(W04) + W15 + SSG2_0(W07) + W06); \ + T1 = SPH_T32(B + BSG2_1(G) + CH(G, H, A) \ + + SPH_C32(0x5B9CCA4F) + W06); \ + T2 = SPH_T32(BSG2_0(C) + MAJ(C, D, E)); \ + F = SPH_T32(F + T1); \ + B = SPH_T32(T1 + T2); \ + W07 = SPH_T32(SSG2_1(W05) + W00 + SSG2_0(W08) + W07); \ + T1 = SPH_T32(A + BSG2_1(F) + CH(F, G, H) \ + + SPH_C32(0x682E6FF3) + W07); \ + T2 = SPH_T32(BSG2_0(B) + MAJ(B, C, D)); \ + E = SPH_T32(E + T1); \ + A = SPH_T32(T1 + T2); \ + W08 = SPH_T32(SSG2_1(W06) + W01 + SSG2_0(W09) + W08); \ + T1 = SPH_T32(H + BSG2_1(E) + CH(E, F, G) \ + + SPH_C32(0x748F82EE) + W08); \ + T2 = SPH_T32(BSG2_0(A) + MAJ(A, B, C)); \ + D = SPH_T32(D + T1); \ + H = SPH_T32(T1 + T2); \ + W09 = SPH_T32(SSG2_1(W07) + W02 + SSG2_0(W10) + W09); \ + T1 = SPH_T32(G + BSG2_1(D) + CH(D, E, F) \ + + SPH_C32(0x78A5636F) + W09); \ + T2 = SPH_T32(BSG2_0(H) + MAJ(H, A, B)); \ + C = SPH_T32(C + T1); \ + G = SPH_T32(T1 + T2); \ + W10 = SPH_T32(SSG2_1(W08) + W03 + SSG2_0(W11) + W10); \ + T1 = SPH_T32(F + BSG2_1(C) + CH(C, D, E) \ + + SPH_C32(0x84C87814) + W10); \ + T2 = SPH_T32(BSG2_0(G) + MAJ(G, H, A)); \ + B = SPH_T32(B + T1); \ + F = SPH_T32(T1 + T2); \ + W11 = SPH_T32(SSG2_1(W09) + W04 + SSG2_0(W12) + W11); \ + T1 = SPH_T32(E + BSG2_1(B) + CH(B, C, D) \ + + SPH_C32(0x8CC70208) + W11); \ + T2 = SPH_T32(BSG2_0(F) + MAJ(F, G, H)); \ + A = SPH_T32(A + T1); \ + E = SPH_T32(T1 + T2); \ + W12 = SPH_T32(SSG2_1(W10) + W05 + SSG2_0(W13) + W12); \ + T1 = SPH_T32(D + BSG2_1(A) + CH(A, B, C) \ + + SPH_C32(0x90BEFFFA) + W12); \ + T2 = SPH_T32(BSG2_0(E) + MAJ(E, F, G)); \ + H = SPH_T32(H + T1); \ + D = SPH_T32(T1 + T2); \ + W13 = SPH_T32(SSG2_1(W11) + W06 + SSG2_0(W14) + W13); \ + T1 = SPH_T32(C + BSG2_1(H) + CH(H, A, B) \ + + SPH_C32(0xA4506CEB) + W13); \ + T2 = SPH_T32(BSG2_0(D) + MAJ(D, E, F)); \ + G = SPH_T32(G + T1); \ + C = SPH_T32(T1 + T2); \ + W14 = SPH_T32(SSG2_1(W12) + W07 + SSG2_0(W15) + W14); \ + T1 = SPH_T32(B + BSG2_1(G) + CH(G, H, A) \ + + SPH_C32(0xBEF9A3F7) + W14); \ + T2 = SPH_T32(BSG2_0(C) + MAJ(C, D, E)); \ + F = SPH_T32(F + T1); \ + B = SPH_T32(T1 + T2); \ + W15 = SPH_T32(SSG2_1(W13) + W08 + SSG2_0(W00) + W15); \ + T1 = SPH_T32(A + BSG2_1(F) + CH(F, G, H) \ + + SPH_C32(0xC67178F2) + W15); \ + T2 = SPH_T32(BSG2_0(B) + MAJ(B, C, D)); \ + E = SPH_T32(E + T1); \ + A = SPH_T32(T1 + T2); \ + (r)[0] = SPH_T32((r)[0] + A); \ + (r)[1] = SPH_T32((r)[1] + B); \ + (r)[2] = SPH_T32((r)[2] + C); \ + (r)[3] = SPH_T32((r)[3] + D); \ + (r)[4] = SPH_T32((r)[4] + E); \ + (r)[5] = SPH_T32((r)[5] + F); \ + (r)[6] = SPH_T32((r)[6] + G); \ + (r)[7] = SPH_T32((r)[7] + H); \ + } while (0) + +#endif + +/* + * One round of SHA-224 / SHA-256. The data must be aligned for 32-bit access. + */ +static void +sha2_round(const unsigned char *data, sph_u32 r[8]) +{ +#define SHA2_IN(x) sph_dec32be_aligned(data + (4 * (x))) + SHA2_ROUND_BODY(SHA2_IN, r); +#undef SHA2_IN +} + +/* see sph_sha2.h */ +void +sph_sha224_init(void *cc) +{ + sph_sha224_context *sc; + + sc = (sph_sha224_context*)cc; + memcpy(sc->val, H224, sizeof H224); +#if SPH_64 + sc->count = 0; +#else + sc->count_high = sc->count_low = 0; +#endif +} + +/* see sph_sha2.h */ +void +sph_sha256_init(void *cc) +{ + sph_sha256_context *sc; + + sc = (sph_sha224_context*)cc; + memcpy(sc->val, H256, sizeof H256); +#if SPH_64 + sc->count = 0; +#else + sc->count_high = sc->count_low = 0; +#endif +} + +#define RFUN sha2_round +#define HASH sha224 +#define BE32 1 +#include "md_helper.c" + +/* see sph_sha2.h */ +void +sph_sha224_close(void *cc, void *dst) +{ + sha224_close(cc, dst, 7); + sph_sha224_init(cc); +} + +/* see sph_sha2.h */ +void +sph_sha224_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst) +{ + sha224_addbits_and_close(cc, ub, n, dst, 7); + sph_sha224_init(cc); +} + +/* see sph_sha2.h */ +void +sph_sha256_close(void *cc, void *dst) +{ + sha224_close(cc, dst, 8); + sph_sha256_init(cc); +} + +/* see sph_sha2.h */ +void +sph_sha256_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst) +{ + sha224_addbits_and_close(cc, ub, n, dst, 8); + sph_sha256_init(cc); +} + +/* see sph_sha2.h */ +void +sph_sha224_comp(const sph_u32 msg[16], sph_u32 val[8]) +{ +#define SHA2_IN(x) msg[x] + SHA2_ROUND_BODY(SHA2_IN, val); +#undef SHA2_IN +} + diff --git a/src/Native/libmultihash/sha3/sph_sha2.h b/src/Native/libmultihash/sha3/sph_sha2.h new file mode 100644 index 000000000..4b957c2ae --- /dev/null +++ b/src/Native/libmultihash/sha3/sph_sha2.h @@ -0,0 +1,371 @@ +/* $Id: sph_sha2.h 216 2010-06-08 09:46:57Z tp $ */ +/** + * SHA-224, SHA-256, SHA-384 and SHA-512 interface. + * + * SHA-256 has been published in FIPS 180-2, now amended with a change + * notice to include SHA-224 as well (which is a simple variation on + * SHA-256). SHA-384 and SHA-512 are also defined in FIPS 180-2. FIPS + * standards can be found at: + * http://csrc.nist.gov/publications/fips/ + * + * ==========================(LICENSE BEGIN)============================ + * + * Copyright (c) 2007-2010 Projet RNRT SAPHIR + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * ===========================(LICENSE END)============================= + * + * @file sph_sha2.h + * @author Thomas Pornin + */ + +#ifndef SPH_SHA2_H__ +#define SPH_SHA2_H__ + +#include +#include "sph_types.h" + +/** + * Output size (in bits) for SHA-224. + */ +#define SPH_SIZE_sha224 224 + +/** + * Output size (in bits) for SHA-256. + */ +#define SPH_SIZE_sha256 256 + +/** + * This structure is a context for SHA-224 computations: it contains the + * intermediate values and some data from the last entered block. Once + * a SHA-224 computation has been performed, the context can be reused for + * another computation. + * + * The contents of this structure are private. A running SHA-224 computation + * can be cloned by copying the context (e.g. with a simple + * memcpy()). + */ +typedef struct { +#ifndef DOXYGEN_IGNORE + unsigned char buf[64]; /* first field, for alignment */ + sph_u32 val[8]; +#if SPH_64 + sph_u64 count; +#else + sph_u32 count_high, count_low; +#endif +#endif +} sph_sha224_context; + +/** + * This structure is a context for SHA-256 computations. It is identical + * to the SHA-224 context. However, a context is initialized for SHA-224 + * or SHA-256, but not both (the internal IV is not the + * same). + */ +typedef sph_sha224_context sph_sha256_context; + +/** + * Initialize a SHA-224 context. This process performs no memory allocation. + * + * @param cc the SHA-224 context (pointer to + * a sph_sha224_context) + */ +void sph_sha224_init(void *cc); + +/** + * Process some data bytes. It is acceptable that len is zero + * (in which case this function does nothing). + * + * @param cc the SHA-224 context + * @param data the input data + * @param len the input data length (in bytes) + */ +void sph_sha224(void *cc, const void *data, size_t len); + +/** + * Terminate the current SHA-224 computation and output the result into the + * provided buffer. The destination buffer must be wide enough to + * accomodate the result (28 bytes). The context is automatically + * reinitialized. + * + * @param cc the SHA-224 context + * @param dst the destination buffer + */ +void sph_sha224_close(void *cc, void *dst); + +/** + * Add a few additional bits (0 to 7) to the current computation, then + * terminate it and output the result in the provided buffer, which must + * be wide enough to accomodate the result (28 bytes). If bit number i + * in ub has value 2^i, then the extra bits are those + * numbered 7 downto 8-n (this is the big-endian convention at the byte + * level). The context is automatically reinitialized. + * + * @param cc the SHA-224 context + * @param ub the extra bits + * @param n the number of extra bits (0 to 7) + * @param dst the destination buffer + */ +void sph_sha224_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst); + +/** + * Apply the SHA-224 compression function on the provided data. The + * msg parameter contains the 16 32-bit input blocks, + * as numerical values (hence after the big-endian decoding). The + * val parameter contains the 8 32-bit input blocks for + * the compression function; the output is written in place in this + * array. + * + * @param msg the message block (16 values) + * @param val the function 256-bit input and output + */ +void sph_sha224_comp(const sph_u32 msg[16], sph_u32 val[8]); + +/** + * Initialize a SHA-256 context. This process performs no memory allocation. + * + * @param cc the SHA-256 context (pointer to + * a sph_sha256_context) + */ +void sph_sha256_init(void *cc); + +#ifdef DOXYGEN_IGNORE +/** + * Process some data bytes, for SHA-256. This function is identical to + * sha_224() + * + * @param cc the SHA-224 context + * @param data the input data + * @param len the input data length (in bytes) + */ +void sph_sha256(void *cc, const void *data, size_t len); +#endif + +#ifndef DOXYGEN_IGNORE +#define sph_sha256 sph_sha224 +#endif + +/** + * Terminate the current SHA-256 computation and output the result into the + * provided buffer. The destination buffer must be wide enough to + * accomodate the result (32 bytes). The context is automatically + * reinitialized. + * + * @param cc the SHA-256 context + * @param dst the destination buffer + */ +void sph_sha256_close(void *cc, void *dst); + +/** + * Add a few additional bits (0 to 7) to the current computation, then + * terminate it and output the result in the provided buffer, which must + * be wide enough to accomodate the result (32 bytes). If bit number i + * in ub has value 2^i, then the extra bits are those + * numbered 7 downto 8-n (this is the big-endian convention at the byte + * level). The context is automatically reinitialized. + * + * @param cc the SHA-256 context + * @param ub the extra bits + * @param n the number of extra bits (0 to 7) + * @param dst the destination buffer + */ +void sph_sha256_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst); + +#ifdef DOXYGEN_IGNORE +/** + * Apply the SHA-256 compression function on the provided data. This + * function is identical to sha224_comp(). + * + * @param msg the message block (16 values) + * @param val the function 256-bit input and output + */ +void sph_sha256_comp(const sph_u32 msg[16], sph_u32 val[8]); +#endif + +#ifndef DOXYGEN_IGNORE +#define sph_sha256_comp sph_sha224_comp +#endif + +#if SPH_64 + +/** + * Output size (in bits) for SHA-384. + */ +#define SPH_SIZE_sha384 384 + +/** + * Output size (in bits) for SHA-512. + */ +#define SPH_SIZE_sha512 512 + +/** + * This structure is a context for SHA-384 computations: it contains the + * intermediate values and some data from the last entered block. Once + * a SHA-384 computation has been performed, the context can be reused for + * another computation. + * + * The contents of this structure are private. A running SHA-384 computation + * can be cloned by copying the context (e.g. with a simple + * memcpy()). + */ +typedef struct { +#ifndef DOXYGEN_IGNORE + unsigned char buf[128]; /* first field, for alignment */ + sph_u64 val[8]; + sph_u64 count; +#endif +} sph_sha384_context; + +/** + * Initialize a SHA-384 context. This process performs no memory allocation. + * + * @param cc the SHA-384 context (pointer to + * a sph_sha384_context) + */ +void sph_sha384_init(void *cc); + +/** + * Process some data bytes. It is acceptable that len is zero + * (in which case this function does nothing). + * + * @param cc the SHA-384 context + * @param data the input data + * @param len the input data length (in bytes) + */ +void sph_sha384(void *cc, const void *data, size_t len); + +/** + * Terminate the current SHA-384 computation and output the result into the + * provided buffer. The destination buffer must be wide enough to + * accomodate the result (48 bytes). The context is automatically + * reinitialized. + * + * @param cc the SHA-384 context + * @param dst the destination buffer + */ +void sph_sha384_close(void *cc, void *dst); + +/** + * Add a few additional bits (0 to 7) to the current computation, then + * terminate it and output the result in the provided buffer, which must + * be wide enough to accomodate the result (48 bytes). If bit number i + * in ub has value 2^i, then the extra bits are those + * numbered 7 downto 8-n (this is the big-endian convention at the byte + * level). The context is automatically reinitialized. + * + * @param cc the SHA-384 context + * @param ub the extra bits + * @param n the number of extra bits (0 to 7) + * @param dst the destination buffer + */ +void sph_sha384_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst); + +/** + * Apply the SHA-384 compression function on the provided data. The + * msg parameter contains the 16 64-bit input blocks, + * as numerical values (hence after the big-endian decoding). The + * val parameter contains the 8 64-bit input blocks for + * the compression function; the output is written in place in this + * array. + * + * @param msg the message block (16 values) + * @param val the function 512-bit input and output + */ +void sph_sha384_comp(const sph_u64 msg[16], sph_u64 val[8]); + +/** + * This structure is a context for SHA-512 computations. It is identical + * to the SHA-384 context. However, a context is initialized for SHA-384 + * or SHA-512, but not both (the internal IV is not the + * same). + */ +typedef sph_sha384_context sph_sha512_context; + +/** + * Initialize a SHA-512 context. This process performs no memory allocation. + * + * @param cc the SHA-512 context (pointer to + * a sph_sha512_context) + */ +void sph_sha512_init(void *cc); + +#ifdef DOXYGEN_IGNORE +/** + * Process some data bytes, for SHA-512. This function is identical to + * sph_sha384(). + * + * @param cc the SHA-384 context + * @param data the input data + * @param len the input data length (in bytes) + */ +void sph_sha512(void *cc, const void *data, size_t len); +#endif + +#ifndef DOXYGEN_IGNORE +#define sph_sha512 sph_sha384 +#endif + +/** + * Terminate the current SHA-512 computation and output the result into the + * provided buffer. The destination buffer must be wide enough to + * accomodate the result (64 bytes). The context is automatically + * reinitialized. + * + * @param cc the SHA-512 context + * @param dst the destination buffer + */ +void sph_sha512_close(void *cc, void *dst); + +/** + * Add a few additional bits (0 to 7) to the current computation, then + * terminate it and output the result in the provided buffer, which must + * be wide enough to accomodate the result (64 bytes). If bit number i + * in ub has value 2^i, then the extra bits are those + * numbered 7 downto 8-n (this is the big-endian convention at the byte + * level). The context is automatically reinitialized. + * + * @param cc the SHA-512 context + * @param ub the extra bits + * @param n the number of extra bits (0 to 7) + * @param dst the destination buffer + */ +void sph_sha512_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst); + +#ifdef DOXYGEN_IGNORE +/** + * Apply the SHA-512 compression function. This function is identical to + * sph_sha384_comp(). + * + * @param msg the message block (16 values) + * @param val the function 512-bit input and output + */ +void sph_sha512_comp(const sph_u64 msg[16], sph_u64 val[8]); +#endif + +#ifndef DOXYGEN_IGNORE +#define sph_sha512_comp sph_sha384_comp +#endif + +#endif + +#endif + diff --git a/src/Native/libmultihash/sha3/sph_sha2big.c b/src/Native/libmultihash/sha3/sph_sha2big.c new file mode 100644 index 000000000..be97eb986 --- /dev/null +++ b/src/Native/libmultihash/sha3/sph_sha2big.c @@ -0,0 +1,248 @@ +/* $Id: sha2big.c 216 2010-06-08 09:46:57Z tp $ */ +/* + * SHA-384 / SHA-512 implementation. + * + * ==========================(LICENSE BEGIN)============================ + * + * Copyright (c) 2007-2010 Projet RNRT SAPHIR + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * ===========================(LICENSE END)============================= + * + * @author Thomas Pornin + */ + +#include +#include + +#include "sph_sha2.h" + +#if SPH_64 + +#define CH(X, Y, Z) ((((Y) ^ (Z)) & (X)) ^ (Z)) +#define MAJ(X, Y, Z) (((X) & (Y)) | (((X) | (Y)) & (Z))) + +#define ROTR64 SPH_ROTR64 + +#define BSG5_0(x) (ROTR64(x, 28) ^ ROTR64(x, 34) ^ ROTR64(x, 39)) +#define BSG5_1(x) (ROTR64(x, 14) ^ ROTR64(x, 18) ^ ROTR64(x, 41)) +#define SSG5_0(x) (ROTR64(x, 1) ^ ROTR64(x, 8) ^ SPH_T64((x) >> 7)) +#define SSG5_1(x) (ROTR64(x, 19) ^ ROTR64(x, 61) ^ SPH_T64((x) >> 6)) + +static const sph_u64 K512[80] = { + SPH_C64(0x428A2F98D728AE22), SPH_C64(0x7137449123EF65CD), + SPH_C64(0xB5C0FBCFEC4D3B2F), SPH_C64(0xE9B5DBA58189DBBC), + SPH_C64(0x3956C25BF348B538), SPH_C64(0x59F111F1B605D019), + SPH_C64(0x923F82A4AF194F9B), SPH_C64(0xAB1C5ED5DA6D8118), + SPH_C64(0xD807AA98A3030242), SPH_C64(0x12835B0145706FBE), + SPH_C64(0x243185BE4EE4B28C), SPH_C64(0x550C7DC3D5FFB4E2), + SPH_C64(0x72BE5D74F27B896F), SPH_C64(0x80DEB1FE3B1696B1), + SPH_C64(0x9BDC06A725C71235), SPH_C64(0xC19BF174CF692694), + SPH_C64(0xE49B69C19EF14AD2), SPH_C64(0xEFBE4786384F25E3), + SPH_C64(0x0FC19DC68B8CD5B5), SPH_C64(0x240CA1CC77AC9C65), + SPH_C64(0x2DE92C6F592B0275), SPH_C64(0x4A7484AA6EA6E483), + SPH_C64(0x5CB0A9DCBD41FBD4), SPH_C64(0x76F988DA831153B5), + SPH_C64(0x983E5152EE66DFAB), SPH_C64(0xA831C66D2DB43210), + SPH_C64(0xB00327C898FB213F), SPH_C64(0xBF597FC7BEEF0EE4), + SPH_C64(0xC6E00BF33DA88FC2), SPH_C64(0xD5A79147930AA725), + SPH_C64(0x06CA6351E003826F), SPH_C64(0x142929670A0E6E70), + SPH_C64(0x27B70A8546D22FFC), SPH_C64(0x2E1B21385C26C926), + SPH_C64(0x4D2C6DFC5AC42AED), SPH_C64(0x53380D139D95B3DF), + SPH_C64(0x650A73548BAF63DE), SPH_C64(0x766A0ABB3C77B2A8), + SPH_C64(0x81C2C92E47EDAEE6), SPH_C64(0x92722C851482353B), + SPH_C64(0xA2BFE8A14CF10364), SPH_C64(0xA81A664BBC423001), + SPH_C64(0xC24B8B70D0F89791), SPH_C64(0xC76C51A30654BE30), + SPH_C64(0xD192E819D6EF5218), SPH_C64(0xD69906245565A910), + SPH_C64(0xF40E35855771202A), SPH_C64(0x106AA07032BBD1B8), + SPH_C64(0x19A4C116B8D2D0C8), SPH_C64(0x1E376C085141AB53), + SPH_C64(0x2748774CDF8EEB99), SPH_C64(0x34B0BCB5E19B48A8), + SPH_C64(0x391C0CB3C5C95A63), SPH_C64(0x4ED8AA4AE3418ACB), + SPH_C64(0x5B9CCA4F7763E373), SPH_C64(0x682E6FF3D6B2B8A3), + SPH_C64(0x748F82EE5DEFB2FC), SPH_C64(0x78A5636F43172F60), + SPH_C64(0x84C87814A1F0AB72), SPH_C64(0x8CC702081A6439EC), + SPH_C64(0x90BEFFFA23631E28), SPH_C64(0xA4506CEBDE82BDE9), + SPH_C64(0xBEF9A3F7B2C67915), SPH_C64(0xC67178F2E372532B), + SPH_C64(0xCA273ECEEA26619C), SPH_C64(0xD186B8C721C0C207), + SPH_C64(0xEADA7DD6CDE0EB1E), SPH_C64(0xF57D4F7FEE6ED178), + SPH_C64(0x06F067AA72176FBA), SPH_C64(0x0A637DC5A2C898A6), + SPH_C64(0x113F9804BEF90DAE), SPH_C64(0x1B710B35131C471B), + SPH_C64(0x28DB77F523047D84), SPH_C64(0x32CAAB7B40C72493), + SPH_C64(0x3C9EBE0A15C9BEBC), SPH_C64(0x431D67C49C100D4C), + SPH_C64(0x4CC5D4BECB3E42B6), SPH_C64(0x597F299CFC657E2A), + SPH_C64(0x5FCB6FAB3AD6FAEC), SPH_C64(0x6C44198C4A475817) +}; + +static const sph_u64 H384[8] = { + SPH_C64(0xCBBB9D5DC1059ED8), SPH_C64(0x629A292A367CD507), + SPH_C64(0x9159015A3070DD17), SPH_C64(0x152FECD8F70E5939), + SPH_C64(0x67332667FFC00B31), SPH_C64(0x8EB44A8768581511), + SPH_C64(0xDB0C2E0D64F98FA7), SPH_C64(0x47B5481DBEFA4FA4) +}; + +static const sph_u64 H512[8] = { + SPH_C64(0x6A09E667F3BCC908), SPH_C64(0xBB67AE8584CAA73B), + SPH_C64(0x3C6EF372FE94F82B), SPH_C64(0xA54FF53A5F1D36F1), + SPH_C64(0x510E527FADE682D1), SPH_C64(0x9B05688C2B3E6C1F), + SPH_C64(0x1F83D9ABFB41BD6B), SPH_C64(0x5BE0CD19137E2179) +}; + +/* + * This macro defines the body for a SHA-384 / SHA-512 compression function + * implementation. The "in" parameter should evaluate, when applied to a + * numerical input parameter from 0 to 15, to an expression which yields + * the corresponding input block. The "r" parameter should evaluate to + * an array or pointer expression designating the array of 8 words which + * contains the input and output of the compression function. + * + * SHA-512 is hard for the compiler. If the loop is completely unrolled, + * then the code will be quite huge (possibly more than 100 kB), and the + * performance will be degraded due to cache misses on the code. We + * unroll only eight steps, which avoids all needless copies when + * 64-bit registers are swapped. + */ + +#define SHA3_STEP(A, B, C, D, E, F, G, H, i) do { \ + sph_u64 T1, T2; \ + T1 = SPH_T64(H + BSG5_1(E) + CH(E, F, G) + K512[i] + W[i]); \ + T2 = SPH_T64(BSG5_0(A) + MAJ(A, B, C)); \ + D = SPH_T64(D + T1); \ + H = SPH_T64(T1 + T2); \ + } while (0) + +#define SHA3_ROUND_BODY(in, r) do { \ + int i; \ + sph_u64 A, B, C, D, E, F, G, H; \ + sph_u64 W[80]; \ + \ + for (i = 0; i < 16; i ++) \ + W[i] = in(i); \ + for (i = 16; i < 80; i ++) \ + W[i] = SPH_T64(SSG5_1(W[i - 2]) + W[i - 7] \ + + SSG5_0(W[i - 15]) + W[i - 16]); \ + A = (r)[0]; \ + B = (r)[1]; \ + C = (r)[2]; \ + D = (r)[3]; \ + E = (r)[4]; \ + F = (r)[5]; \ + G = (r)[6]; \ + H = (r)[7]; \ + for (i = 0; i < 80; i += 8) { \ + SHA3_STEP(A, B, C, D, E, F, G, H, i + 0); \ + SHA3_STEP(H, A, B, C, D, E, F, G, i + 1); \ + SHA3_STEP(G, H, A, B, C, D, E, F, i + 2); \ + SHA3_STEP(F, G, H, A, B, C, D, E, i + 3); \ + SHA3_STEP(E, F, G, H, A, B, C, D, i + 4); \ + SHA3_STEP(D, E, F, G, H, A, B, C, i + 5); \ + SHA3_STEP(C, D, E, F, G, H, A, B, i + 6); \ + SHA3_STEP(B, C, D, E, F, G, H, A, i + 7); \ + } \ + (r)[0] = SPH_T64((r)[0] + A); \ + (r)[1] = SPH_T64((r)[1] + B); \ + (r)[2] = SPH_T64((r)[2] + C); \ + (r)[3] = SPH_T64((r)[3] + D); \ + (r)[4] = SPH_T64((r)[4] + E); \ + (r)[5] = SPH_T64((r)[5] + F); \ + (r)[6] = SPH_T64((r)[6] + G); \ + (r)[7] = SPH_T64((r)[7] + H); \ + } while (0) + +/* + * One round of SHA-384 / SHA-512. The data must be aligned for 64-bit access. + */ +static void +sha3_round(const unsigned char *data, sph_u64 r[8]) +{ +#define SHA3_IN(x) sph_dec64be_aligned(data + (8 * (x))) + SHA3_ROUND_BODY(SHA3_IN, r); +#undef SHA3_IN +} + +/* see sph_sha3.h */ +void +sph_sha384_init(void *cc) +{ + sph_sha384_context *sc; + + sc = (sph_sha384_context*)cc; + memcpy(sc->val, H384, sizeof H384); + sc->count = 0; +} + +/* see sph_sha3.h */ +void +sph_sha512_init(void *cc) +{ + sph_sha512_context *sc; + + sc = (sph_sha512_context*)cc; + memcpy(sc->val, H512, sizeof H512); + sc->count = 0; +} + +#define RFUN sha3_round +#define HASH sha384 +#define BE64 1 +#include "md_helper.c" + +/* see sph_sha3.h */ +void +sph_sha384_close(void *cc, void *dst) +{ + sha384_close(cc, dst, 6); + sph_sha384_init(cc); +} + +/* see sph_sha3.h */ +void +sph_sha384_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst) +{ + sha384_addbits_and_close(cc, ub, n, dst, 6); + sph_sha384_init(cc); +} + +/* see sph_sha3.h */ +void +sph_sha512_close(void *cc, void *dst) +{ + sha384_close(cc, dst, 8); + sph_sha512_init(cc); +} + +/* see sph_sha3.h */ +void +sph_sha512_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst) +{ + sha384_addbits_and_close(cc, ub, n, dst, 8); + sph_sha512_init(cc); +} + +/* see sph_sha3.h */ +void +sph_sha384_comp(const sph_u64 msg[16], sph_u64 val[8]) +{ +#define SHA3_IN(x) msg[x] + SHA3_ROUND_BODY(SHA3_IN, val); +#undef SHA3_IN +} + +#endif + diff --git a/src/Native/libmultihash/x17.c b/src/Native/libmultihash/x17.c new file mode 100644 index 000000000..9defffb57 --- /dev/null +++ b/src/Native/libmultihash/x17.c @@ -0,0 +1,115 @@ +#include +#include +#include +#include + +#include "sha3/sph_blake.h" +#include "sha3/sph_bmw.h" +#include "sha3/sph_groestl.h" +#include "sha3/sph_jh.h" +#include "sha3/sph_keccak.h" +#include "sha3/sph_skein.h" +#include "sha3/sph_luffa.h" +#include "sha3/sph_cubehash.h" +#include "sha3/sph_shavite.h" +#include "sha3/sph_simd.h" +#include "sha3/sph_echo.h" +#include "sha3/sph_hamsi.h" +#include "sha3/sph_fugue.h" +#include "sha3/sph_shabal.h" +#include "sha3/sph_whirlpool.h" +#include "sha3/sph_sha2.h" +#include "sha3/sph_haval.h" + +void x17_hash(const char* input, char* output, uint32_t len) +{ + sph_blake512_context ctx_blake; + sph_bmw512_context ctx_bmw; + sph_groestl512_context ctx_groestl; + sph_skein512_context ctx_skein; + sph_jh512_context ctx_jh; + sph_keccak512_context ctx_keccak; + sph_luffa512_context ctx_luffa1; + sph_cubehash512_context ctx_cubehash1; + sph_shavite512_context ctx_shavite1; + sph_simd512_context ctx_simd1; + sph_echo512_context ctx_echo1; + sph_hamsi512_context ctx_hamsi1; + sph_fugue512_context ctx_fugue1; + sph_shabal512_context ctx_shabal1; + sph_whirlpool_context ctx_whirlpool1; + sph_sha512_context ctx_sha512; + sph_haval256_5_context ctx_haval; + + uint32_t hash[16]; + + sph_blake512_init(&ctx_blake); + sph_blake512 (&ctx_blake, input, len); + sph_blake512_close (&ctx_blake, hash); + + sph_bmw512_init(&ctx_bmw); + sph_bmw512 (&ctx_bmw, hash, 64); + sph_bmw512_close(&ctx_bmw, hash); + + sph_groestl512_init(&ctx_groestl); + sph_groestl512 (&ctx_groestl, hash, 64); + sph_groestl512_close(&ctx_groestl, hash); + + sph_skein512_init(&ctx_skein); + sph_skein512 (&ctx_skein, hash, 64); + sph_skein512_close (&ctx_skein, hash); + + sph_jh512_init(&ctx_jh); + sph_jh512 (&ctx_jh, hash, 64); + sph_jh512_close(&ctx_jh, hash); + + sph_keccak512_init(&ctx_keccak); + sph_keccak512 (&ctx_keccak, hash, 64); + sph_keccak512_close(&ctx_keccak, hash); + + sph_luffa512_init (&ctx_luffa1); + sph_luffa512 (&ctx_luffa1, hash, 64); + sph_luffa512_close (&ctx_luffa1, hash); + + sph_cubehash512_init (&ctx_cubehash1); + sph_cubehash512 (&ctx_cubehash1, hash, 64); + sph_cubehash512_close(&ctx_cubehash1, hash); + + sph_shavite512_init (&ctx_shavite1); + sph_shavite512 (&ctx_shavite1, hash, 64); + sph_shavite512_close(&ctx_shavite1, hash); + + sph_simd512_init (&ctx_simd1); + sph_simd512 (&ctx_simd1, hash, 64); + sph_simd512_close(&ctx_simd1, hash); + + sph_echo512_init (&ctx_echo1); + sph_echo512 (&ctx_echo1, hash, 64); + sph_echo512_close(&ctx_echo1, hash); + + sph_hamsi512_init (&ctx_hamsi1); + sph_hamsi512 (&ctx_hamsi1, hash, 64); + sph_hamsi512_close(&ctx_hamsi1, hash); + + sph_fugue512_init (&ctx_fugue1); + sph_fugue512 (&ctx_fugue1, hash, 64); + sph_fugue512_close(&ctx_fugue1, hash); + + sph_shabal512_init (&ctx_shabal1); + sph_shabal512 (&ctx_shabal1, hash, 64); + sph_shabal512_close(&ctx_shabal1, hash); + + sph_whirlpool_init (&ctx_whirlpool1); + sph_whirlpool (&ctx_whirlpool1, hash, 64); + sph_whirlpool_close(&ctx_whirlpool1, hash); + + sph_sha512_init(&ctx_sha512); + sph_sha512(&ctx_sha512,(const void*) hash, 64); + sph_sha512_close(&ctx_sha512,(void*) hash); + + sph_haval256_5_init(&ctx_haval); + sph_haval256_5(&ctx_haval,(const void*) hash, 64); + sph_haval256_5_close(&ctx_haval, hash); + + memcpy(output, hash, 32); +} diff --git a/src/Native/libmultihash/x17.h b/src/Native/libmultihash/x17.h new file mode 100644 index 000000000..11c29b889 --- /dev/null +++ b/src/Native/libmultihash/x17.h @@ -0,0 +1,16 @@ +#ifndef X17_H +#define X17_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +void x17_hash(const char* input, char* output, uint32_t len); + +#ifdef __cplusplus +} +#endif + +#endif