From cf1fae8d2c6b508909a554a1721bbc41c19d4d7a Mon Sep 17 00:00:00 2001 From: Pavel Gross Date: Tue, 12 Nov 2024 04:00:26 +0300 Subject: [PATCH] span and memory --- src/FrostFS.SDK.Client/FrostFSClient.cs | 2 +- .../Interfaces/IFrostFSClient.cs | 2 +- src/FrostFS.SDK.Client/Mappers/ContainerId.cs | 2 +- .../Mappers/Netmap/NodeInfo.cs | 2 +- .../Mappers/Object/Object.cs | 2 +- .../Mappers/Object/ObjectHeaderMapper.cs | 2 +- .../Mappers/Object/ObjectId.cs | 2 +- src/FrostFS.SDK.Client/Mappers/OwnerId.cs | 2 +- .../Models/Chain/ChainTarget.cs | 4 +- .../Models/Containers/FrostFsContainerId.cs | 2 +- .../Models/Object/FrostFsObjectId.cs | 7 +- src/FrostFS.SDK.Client/Pool/ClientWrapper.cs | 2 +- src/FrostFS.SDK.Client/Pool/Pool.cs | 2 +- .../Services/ApeManagerServiceProvider.cs | 4 +- .../Services/ContainerServiceProvider.cs | 2 +- .../Services/NetmapServiceProvider.cs | 68 ++++++--- .../Services/ObjectServiceProvider.cs | 6 +- src/FrostFS.SDK.Client/Tools/ObjectTools.cs | 4 +- src/FrostFS.SDK.Client/Tools/RequestSigner.cs | 40 +++--- src/FrostFS.SDK.Client/Tools/Verifier.cs | 22 +-- src/FrostFS.SDK.Cryptography/Extentions.cs | 5 +- .../FrostFS.SDK.Cryptography.csproj | 8 ++ src/FrostFS.SDK.Cryptography/UUID.cs | 6 +- .../Mocks/AsyncStreamRangeReaderMock.cs | 5 - .../Mocks/AsyncStreamReaderMock.cs | 2 +- .../ContainerServiceBase.cs | 6 +- src/FrostFS.SDK.Tests/Mocks/ObjectMock.cs | 9 +- src/FrostFS.SDK.Tests/ObjectTest.cs | 6 +- src/FrostFS.SDK.Tests/SmokeClientTests.cs | 132 +++++++++--------- 29 files changed, 193 insertions(+), 165 deletions(-) diff --git a/src/FrostFS.SDK.Client/FrostFSClient.cs b/src/FrostFS.SDK.Client/FrostFSClient.cs index 8dc4d3f..281e5f3 100644 --- a/src/FrostFS.SDK.Client/FrostFSClient.cs +++ b/src/FrostFS.SDK.Client/FrostFSClient.cs @@ -175,7 +175,7 @@ public class FrostFSClient : IFrostFSClient } #region ApeManagerImplementation - public Task AddChainAsync(PrmApeChainAdd args) + public Task> AddChainAsync(PrmApeChainAdd args) { if (args is null) { diff --git a/src/FrostFS.SDK.Client/Interfaces/IFrostFSClient.cs b/src/FrostFS.SDK.Client/Interfaces/IFrostFSClient.cs index 5749249..5aacc58 100644 --- a/src/FrostFS.SDK.Client/Interfaces/IFrostFSClient.cs +++ b/src/FrostFS.SDK.Client/Interfaces/IFrostFSClient.cs @@ -21,7 +21,7 @@ public interface IFrostFSClient : IDisposable #endregion #region ApeManager - Task AddChainAsync(PrmApeChainAdd args); + Task> AddChainAsync(PrmApeChainAdd args); Task RemoveChainAsync(PrmApeChainRemove args); diff --git a/src/FrostFS.SDK.Client/Mappers/ContainerId.cs b/src/FrostFS.SDK.Client/Mappers/ContainerId.cs index fe8632f..df27320 100644 --- a/src/FrostFS.SDK.Client/Mappers/ContainerId.cs +++ b/src/FrostFS.SDK.Client/Mappers/ContainerId.cs @@ -43,6 +43,6 @@ public static class ContainerIdMapper throw new ArgumentNullException(nameof(message)); } - return new FrostFsContainerId(Base58.Encode(message.Value.ToByteArray())); + return new FrostFsContainerId(Base58.Encode(message.Value.Span)); } } \ No newline at end of file diff --git a/src/FrostFS.SDK.Client/Mappers/Netmap/NodeInfo.cs b/src/FrostFS.SDK.Client/Mappers/Netmap/NodeInfo.cs index 747468b..d7340eb 100644 --- a/src/FrostFS.SDK.Client/Mappers/Netmap/NodeInfo.cs +++ b/src/FrostFS.SDK.Client/Mappers/Netmap/NodeInfo.cs @@ -39,7 +39,7 @@ public static class NodeInfoMapper state: state, addresses: [.. nodeInfo.Addresses], attributes: nodeInfo.Attributes.ToDictionary(n => n.Key, n => n.Value), - publicKey: nodeInfo.PublicKey.ToByteArray() + publicKey: nodeInfo.PublicKey.Memory ); } } \ No newline at end of file diff --git a/src/FrostFS.SDK.Client/Mappers/Object/Object.cs b/src/FrostFS.SDK.Client/Mappers/Object/Object.cs index b9d1520..5f85fc5 100644 --- a/src/FrostFS.SDK.Client/Mappers/Object/Object.cs +++ b/src/FrostFS.SDK.Client/Mappers/Object/Object.cs @@ -6,7 +6,7 @@ internal static class ObjectMapper { return new FrostFsObject(obj.Header.ToModel()) { - ObjectId = FrostFsObjectId.FromHash(obj.ObjectId.Value.ToByteArray()) + ObjectId = FrostFsObjectId.FromHash(obj.ObjectId.Value.Span) }; } } diff --git a/src/FrostFS.SDK.Client/Mappers/Object/ObjectHeaderMapper.cs b/src/FrostFS.SDK.Client/Mappers/Object/ObjectHeaderMapper.cs index 1ee29a6..317b31c 100644 --- a/src/FrostFS.SDK.Client/Mappers/Object/ObjectHeaderMapper.cs +++ b/src/FrostFS.SDK.Client/Mappers/Object/ObjectHeaderMapper.cs @@ -40,7 +40,7 @@ public static class ObjectHeaderMapper } var model = new FrostFsObjectHeader( - new FrostFsContainerId(Base58.Encode(header.ContainerId.Value.ToByteArray())), + new FrostFsContainerId(Base58.Encode(header.ContainerId.Value.Span)), objTypeName, header.Attributes.Select(attribute => attribute.ToModel()).ToArray(), split, diff --git a/src/FrostFS.SDK.Client/Mappers/Object/ObjectId.cs b/src/FrostFS.SDK.Client/Mappers/Object/ObjectId.cs index 373edfb..343e3de 100644 --- a/src/FrostFS.SDK.Client/Mappers/Object/ObjectId.cs +++ b/src/FrostFS.SDK.Client/Mappers/Object/ObjectId.cs @@ -28,6 +28,6 @@ public static class ObjectIdMapper throw new ArgumentNullException(nameof(objectId)); } - return FrostFsObjectId.FromHash(objectId.Value.ToByteArray()); + return FrostFsObjectId.FromHash(objectId.Value.Span); } } \ No newline at end of file diff --git a/src/FrostFS.SDK.Client/Mappers/OwnerId.cs b/src/FrostFS.SDK.Client/Mappers/OwnerId.cs index 297178a..6739a0b 100644 --- a/src/FrostFS.SDK.Client/Mappers/OwnerId.cs +++ b/src/FrostFS.SDK.Client/Mappers/OwnerId.cs @@ -44,7 +44,7 @@ public static class OwnerIdMapper if (!Caches.Owners.TryGetValue(message, out FrostFsOwner? model)) { - model = new FrostFsOwner(Base58.Encode(message.Value.ToByteArray())); + model = new FrostFsOwner(Base58.Encode(message.Value.Span)); Caches.Owners.Set(message, model, _oneHourExpiration); } diff --git a/src/FrostFS.SDK.Client/Models/Chain/ChainTarget.cs b/src/FrostFS.SDK.Client/Models/Chain/ChainTarget.cs index dd52835..8a17363 100644 --- a/src/FrostFS.SDK.Client/Models/Chain/ChainTarget.cs +++ b/src/FrostFS.SDK.Client/Models/Chain/ChainTarget.cs @@ -45,12 +45,12 @@ public struct FrostFsChainTarget(FrostFsTargetType type, string name) : IEquatab return $"{Name}{Type}".GetHashCode(StringComparison.InvariantCulture); } - public static bool operator == (FrostFsChainTarget left, FrostFsChainTarget right) + public static bool operator ==(FrostFsChainTarget left, FrostFsChainTarget right) { return left.Equals(right); } - public static bool operator != (FrostFsChainTarget left, FrostFsChainTarget right) + public static bool operator !=(FrostFsChainTarget left, FrostFsChainTarget right) { return !(left == right); } diff --git a/src/FrostFS.SDK.Client/Models/Containers/FrostFsContainerId.cs b/src/FrostFS.SDK.Client/Models/Containers/FrostFsContainerId.cs index f0b3fdf..9f081cd 100644 --- a/src/FrostFS.SDK.Client/Models/Containers/FrostFsContainerId.cs +++ b/src/FrostFS.SDK.Client/Models/Containers/FrostFsContainerId.cs @@ -27,7 +27,7 @@ public class FrostFsContainerId if (containerID != null) { - this.modelId = Base58.Encode(containerID.Value.ToByteArray()); + this.modelId = Base58.Encode(containerID.Value.Span); return this.modelId; } diff --git a/src/FrostFS.SDK.Client/Models/Object/FrostFsObjectId.cs b/src/FrostFS.SDK.Client/Models/Object/FrostFsObjectId.cs index 49fde5a..57300a3 100644 --- a/src/FrostFS.SDK.Client/Models/Object/FrostFsObjectId.cs +++ b/src/FrostFS.SDK.Client/Models/Object/FrostFsObjectId.cs @@ -8,13 +8,8 @@ public class FrostFsObjectId(string id) { public string Value { get; } = id; - public static FrostFsObjectId FromHash(byte[] hash) + public static FrostFsObjectId FromHash(ReadOnlySpan hash) { - if (hash is null) - { - throw new ArgumentNullException(nameof(hash)); - } - if (hash.Length != Constants.Sha256HashLength) throw new FormatException("ObjectID must be a sha256 hash."); diff --git a/src/FrostFS.SDK.Client/Pool/ClientWrapper.cs b/src/FrostFS.SDK.Client/Pool/ClientWrapper.cs index 87a80b0..547577b 100644 --- a/src/FrostFS.SDK.Client/Pool/ClientWrapper.cs +++ b/src/FrostFS.SDK.Client/Pool/ClientWrapper.cs @@ -106,7 +106,7 @@ public class ClientWrapper : ClientStatusMonitor try { client = new(WrapperPrm, sessionCache); - + //TODO: set additioanl params var error = await client.Dial(ctx).ConfigureAwait(false); if (!string.IsNullOrEmpty(error)) diff --git a/src/FrostFS.SDK.Client/Pool/Pool.cs b/src/FrostFS.SDK.Client/Pool/Pool.cs index 44ae06a..bd43d1e 100644 --- a/src/FrostFS.SDK.Client/Pool/Pool.cs +++ b/src/FrostFS.SDK.Client/Pool/Pool.cs @@ -560,7 +560,7 @@ public partial class Pool : IFrostFSClient return await client.Client!.CreateSessionAsync(args).ConfigureAwait(false); } - public async Task AddChainAsync(PrmApeChainAdd args) + public async Task> AddChainAsync(PrmApeChainAdd args) { if (args is null) { diff --git a/src/FrostFS.SDK.Client/Services/ApeManagerServiceProvider.cs b/src/FrostFS.SDK.Client/Services/ApeManagerServiceProvider.cs index 840223d..84cc60a 100644 --- a/src/FrostFS.SDK.Client/Services/ApeManagerServiceProvider.cs +++ b/src/FrostFS.SDK.Client/Services/ApeManagerServiceProvider.cs @@ -16,7 +16,7 @@ internal sealed class ApeManagerServiceProvider : ContextAccessor _apeManagerServiceClient = apeManagerServiceClient; } - internal async Task AddChainAsync(PrmApeChainAdd args) + internal async Task> AddChainAsync(PrmApeChainAdd args) { var ctx = args.Context!; ctx.Key ??= ClientContext.Key?.ECDsaKey; @@ -40,7 +40,7 @@ internal sealed class ApeManagerServiceProvider : ContextAccessor Verifier.CheckResponse(response); - return response.Body.ChainId.ToByteArray(); + return response.Body.ChainId.Memory; } internal async Task RemoveChainAsync(PrmApeChainRemove args) diff --git a/src/FrostFS.SDK.Client/Services/ContainerServiceProvider.cs b/src/FrostFS.SDK.Client/Services/ContainerServiceProvider.cs index 3a0379e..57ee865 100644 --- a/src/FrostFS.SDK.Client/Services/ContainerServiceProvider.cs +++ b/src/FrostFS.SDK.Client/Services/ContainerServiceProvider.cs @@ -68,7 +68,7 @@ internal sealed class ContainerServiceProvider(ContainerService.ContainerService foreach (var cid in response.Body.ContainerIds) { - yield return new FrostFsContainerId(Base58.Encode(cid.Value.ToByteArray())); + yield return new FrostFsContainerId(Base58.Encode(cid.Value.Span)); } } diff --git a/src/FrostFS.SDK.Client/Services/NetmapServiceProvider.cs b/src/FrostFS.SDK.Client/Services/NetmapServiceProvider.cs index 18649d8..8df02c9 100644 --- a/src/FrostFS.SDK.Client/Services/NetmapServiceProvider.cs +++ b/src/FrostFS.SDK.Client/Services/NetmapServiceProvider.cs @@ -1,6 +1,4 @@ using System; -using System.Linq; -using System.Text; using System.Threading.Tasks; using FrostFS.Netmap; @@ -107,12 +105,16 @@ internal sealed class NetmapServiceProvider : ContextAccessor return response.ToModel(); } - private static bool GetBoolValue(byte[] bytes) + private static bool GetBoolValue(ReadOnlySpan bytes) { - return bytes.Any(b => b != 0); + for (int i = bytes.Length - 1; i >= 0; i--) + if (bytes[i] != 0) + return true; + + return false; } - private static ulong GetLongValue(byte[] bytes) + private static ulong GetLongValue(ReadOnlySpan bytes) { ulong val = 0; for (var i = bytes.Length - 1; i >= 0; i--) @@ -123,24 +125,50 @@ internal sealed class NetmapServiceProvider : ContextAccessor private static void SetNetworksParam(Parameter param, NetworkSettings settings) { - var key = Encoding.UTF8.GetString(param.Key.ToByteArray()); + var key = param.Key.ToStringUtf8(); - var valueBytes = param.Value.ToByteArray(); + var valueBytes = param.Value.Span; switch (key) { - case "AuditFee": settings.AuditFee = GetLongValue(valueBytes); break; - case "BasicIncomeRate": settings.BasicIncomeRate = GetLongValue(valueBytes); break; - case "ContainerFee": settings.ContainerFee = GetLongValue(valueBytes); break; - case "ContainerAliasFee": settings.ContainerAliasFee = GetLongValue(valueBytes); break; - case "EpochDuration": settings.EpochDuration = GetLongValue(valueBytes); break; - case "InnerRingCandidateFee": settings.InnerRingCandidateFee = GetLongValue(valueBytes); break; - case "MaxECDataCount": settings.MaxECDataCount = GetLongValue(valueBytes); break; - case "MaxECParityCount": settings.MaxECParityCount = GetLongValue(valueBytes); break; - case "MaxObjectSize": settings.MaxObjectSize = GetLongValue(valueBytes); break; - case "WithdrawFee": settings.WithdrawFee = GetLongValue(valueBytes); break; - case "HomomorphicHashingDisabled": settings.HomomorphicHashingDisabled = GetBoolValue(valueBytes); break; - case "MaintenanceModeAllowed": settings.MaintenanceModeAllowed = GetBoolValue(valueBytes); break; - default: settings.UnnamedSettings.Add(key, valueBytes); break; + case "AuditFee": + settings.AuditFee = GetLongValue(valueBytes); + break; + case "BasicIncomeRate": + settings.BasicIncomeRate = GetLongValue(valueBytes); + break; + case "ContainerFee": + settings.ContainerFee = GetLongValue(valueBytes); + break; + case "ContainerAliasFee": + settings.ContainerAliasFee = GetLongValue(valueBytes); + break; + case "EpochDuration": + settings.EpochDuration = GetLongValue(valueBytes); + break; + case "InnerRingCandidateFee": + settings.InnerRingCandidateFee = GetLongValue(valueBytes); + break; + case "MaxECDataCount": + settings.MaxECDataCount = GetLongValue(valueBytes); + break; + case "MaxECParityCount": + settings.MaxECParityCount = GetLongValue(valueBytes); + break; + case "MaxObjectSize": + settings.MaxObjectSize = GetLongValue(valueBytes); + break; + case "WithdrawFee": + settings.WithdrawFee = GetLongValue(valueBytes); + break; + case "HomomorphicHashingDisabled": + settings.HomomorphicHashingDisabled = GetBoolValue(valueBytes); + break; + case "MaintenanceModeAllowed": + settings.MaintenanceModeAllowed = GetBoolValue(valueBytes); + break; + default: + settings.UnnamedSettings.Add(key, valueBytes.ToArray()); + break; } } } diff --git a/src/FrostFS.SDK.Client/Services/ObjectServiceProvider.cs b/src/FrostFS.SDK.Client/Services/ObjectServiceProvider.cs index cc1440a..8c42b43 100644 --- a/src/FrostFS.SDK.Client/Services/ObjectServiceProvider.cs +++ b/src/FrostFS.SDK.Client/Services/ObjectServiceProvider.cs @@ -271,7 +271,7 @@ internal sealed class ObjectServiceProvider(ObjectService.ObjectServiceClient cl await foreach (var oid in objectsIds) { - yield return FrostFsObjectId.FromHash(oid.Value.ToByteArray()); + yield return FrostFsObjectId.FromHash(oid.Value.Span); } } @@ -332,7 +332,7 @@ internal sealed class ObjectServiceProvider(ObjectService.ObjectServiceClient cl Verifier.CheckResponse(response); - return FrostFsObjectId.FromHash(grpcObject.ObjectId.Value.ToByteArray()); + return FrostFsObjectId.FromHash(grpcObject.ObjectId.Value.Span); } internal async Task PatchObjectAsync(PrmObjectPatch args) @@ -589,7 +589,7 @@ internal sealed class ObjectServiceProvider(ObjectService.ObjectServiceClient cl var response = await stream.Close().ConfigureAwait(false); Verifier.CheckResponse(response); - return new PutObjectResult(FrostFsObjectId.FromHash(response.Body.ObjectId.Value.ToByteArray()), sentBytes); + return new PutObjectResult(FrostFsObjectId.FromHash(response.Body.ObjectId.Value.Span), sentBytes); } finally { diff --git a/src/FrostFS.SDK.Client/Tools/ObjectTools.cs b/src/FrostFS.SDK.Client/Tools/ObjectTools.cs index f20cacd..621069b 100644 --- a/src/FrostFS.SDK.Client/Tools/ObjectTools.cs +++ b/src/FrostFS.SDK.Client/Tools/ObjectTools.cs @@ -47,7 +47,7 @@ internal static class ObjectTools obj.Signature = new Signature { Key = ctx.GetPublicKeyCache(), - Sign = ByteString.CopyFrom(ctx.Key!.SignData(obj.ObjectId.ToByteArray())), + Sign = ctx.Key!.SignData(obj.ObjectId.ToByteArray()), }; return obj; @@ -78,7 +78,7 @@ internal static class ObjectTools grpcHeader.Split.ParentSignature = new Signature { Key = ctx.GetPublicKeyCache(), - Sign = ByteString.CopyFrom(ctx.Key.SignData(grpcHeader.Split.Parent.ToByteArray())), + Sign = ctx.Key.SignData(grpcHeader.Split.Parent.ToByteArray()), }; } diff --git a/src/FrostFS.SDK.Client/Tools/RequestSigner.cs b/src/FrostFS.SDK.Client/Tools/RequestSigner.cs index a45c569..9222e11 100644 --- a/src/FrostFS.SDK.Client/Tools/RequestSigner.cs +++ b/src/FrostFS.SDK.Client/Tools/RequestSigner.cs @@ -20,18 +20,13 @@ public static class RequestSigner { internal const int RFC6979SignatureSize = 64; - internal static byte[] SignRFC6979(this ECDsa key, byte[] data) + internal static ByteString SignRFC6979(this ECDsa key, byte[] data) { if (key is null) { throw new ArgumentNullException(nameof(key)); } - if (data is null) - { - throw new ArgumentNullException(nameof(data)); - } - var digest = new Sha256Digest(); var secp256R1 = SecNamedCurves.GetByName("secp256r1"); var ecParameters = new ECDomainParameters(secp256R1.Curve, secp256R1.G, secp256R1.N); @@ -44,16 +39,18 @@ public static class RequestSigner signer.Init(true, privateKey); var rs = signer.GenerateSignature(hash); - var signature = new byte[RFC6979SignatureSize]; + + Span signature = stackalloc byte[RFC6979SignatureSize]; + var rbytes = rs[0].ToByteArrayUnsigned(); var sbytes = rs[1].ToByteArrayUnsigned(); var index = RFC6979SignatureSize / 2 - rbytes.Length; - rbytes.CopyTo(signature, index); + rbytes.AsSpan().CopyTo(signature[index..]); index = RFC6979SignatureSize - sbytes.Length; - sbytes.CopyTo(signature, index); + sbytes.AsSpan().CopyTo(signature[index..]); - return signature; + return ByteString.CopyFrom(signature); } internal static SignatureRFC6979 SignRFC6979(this ECDsa key, IMessage message) @@ -61,7 +58,7 @@ public static class RequestSigner return new SignatureRFC6979 { Key = ByteString.CopyFrom(key.PublicKey()), - Sign = ByteString.CopyFrom(key.SignRFC6979(message.ToByteArray())), + Sign = key.SignRFC6979(message.ToByteArray()) }; } @@ -70,23 +67,29 @@ public static class RequestSigner return new SignatureRFC6979 { Key = ByteString.CopyFrom(key.PublicKey()), - Sign = ByteString.CopyFrom(key.SignRFC6979(data.ToByteArray())), + Sign = key.SignRFC6979(data.ToByteArray()), }; } - public static byte[] SignData(this ECDsa key, byte[] data) + public static ByteString SignData(this ECDsa key, ReadOnlySpan data) { if (key is null) { throw new ArgumentNullException(nameof(key)); } - var hash = new byte[65]; - hash[0] = 0x04; + Span dataHash = stackalloc byte[64]; + data.Sha512(ref dataHash); - key.SignHash(data.Sha512()).CopyTo(hash, 1); + Span hash = stackalloc byte[64]; + key.TrySignHash(dataHash, hash, out _); - return hash; + Span result = stackalloc byte[65]; + result[0] = 0x04; + + hash.CopyTo(result[1..]); + + return ByteString.CopyFrom(result); } internal static Signature SignMessagePart(this ECDsa key, IMessage? data) @@ -95,12 +98,11 @@ public static class RequestSigner var sig = new Signature { Key = ByteString.CopyFrom(key.PublicKey()), - Sign = ByteString.CopyFrom(key.SignData(data2Sign)), + Sign = key.SignData(data2Sign), }; return sig; } - internal static void Sign(this IVerifiableMessage message, ECDsa key) { var meta = message.GetMetaHeader(); diff --git a/src/FrostFS.SDK.Client/Tools/Verifier.cs b/src/FrostFS.SDK.Client/Tools/Verifier.cs index bc8dd31..c4df11a 100644 --- a/src/FrostFS.SDK.Client/Tools/Verifier.cs +++ b/src/FrostFS.SDK.Client/Tools/Verifier.cs @@ -33,11 +33,14 @@ public static class Verifier return rs; } - public static bool VerifyRFC6979(this byte[] publicKey, byte[] data, byte[] sig) + public static bool VerifyRFC6979(this byte[] publicKey, IMessage message, ByteString signature) { - if (publicKey is null || data is null || sig is null) + if (publicKey is null || message is null || signature is null) return false; + byte[] sig = signature.ToByteArray(); + byte[] data = message.ToByteArray(); + var rs = DecodeSignature(sig); var digest = new Sha256Digest(); var signer = new ECDsaSigner(new HMacDsaKCalculator(digest)); @@ -60,21 +63,22 @@ public static class Verifier throw new ArgumentNullException(nameof(signature)); } - return signature.Key.ToByteArray().VerifyRFC6979(message.ToByteArray(), signature.Sign.ToByteArray()); + return signature.Key.ToByteArray().VerifyRFC6979(message, signature.Sign); } - public static bool VerifyData(this ECDsa key, byte[] data, byte[] sig) + public static bool VerifyData(this ECDsa key, ReadOnlySpan data, ByteString sig) { if (key is null) throw new ArgumentNullException(nameof(key)); - if (data is null) - throw new ArgumentNullException(nameof(data)); - if (sig is null) throw new ArgumentNullException(nameof(sig)); - return key.VerifyHash(data.Sha512(), sig[1..]); + Span hash = stackalloc byte[64]; + + data.Sha512(ref hash); + + return key.VerifyHash(hash, sig.Span[1..]); } public static bool VerifyMessagePart(this Signature sig, IMessage data) @@ -85,7 +89,7 @@ public static class Verifier using var key = sig.Key.ToByteArray().LoadPublicKey(); var data2Verify = data is null ? [] : data.ToByteArray(); - return key.VerifyData(data2Verify, sig.Sign.ToByteArray()); + return key.VerifyData(data2Verify, sig.Sign); } internal static bool VerifyMatryoskaLevel(IMessage body, IMetaHeader meta, IVerificationHeader verification) diff --git a/src/FrostFS.SDK.Cryptography/Extentions.cs b/src/FrostFS.SDK.Cryptography/Extentions.cs index 9d61972..cb68153 100644 --- a/src/FrostFS.SDK.Cryptography/Extentions.cs +++ b/src/FrostFS.SDK.Cryptography/Extentions.cs @@ -1,3 +1,4 @@ +using System; using System.Security.Cryptography; using System.Threading; @@ -46,14 +47,14 @@ public static class Extentions } } - public static byte[] Sha512(this byte[] value) + public static void Sha512(this ReadOnlySpan value, ref Span hash) { bool lockTaken = false; try { _spinlockSha512.Enter(ref lockTaken); - return _sha512.ComputeHash(value); + _sha512.TryComputeHash(value, hash, out _); } finally { diff --git a/src/FrostFS.SDK.Cryptography/FrostFS.SDK.Cryptography.csproj b/src/FrostFS.SDK.Cryptography/FrostFS.SDK.Cryptography.csproj index f33a107..5cbd8d5 100644 --- a/src/FrostFS.SDK.Cryptography/FrostFS.SDK.Cryptography.csproj +++ b/src/FrostFS.SDK.Cryptography/FrostFS.SDK.Cryptography.csproj @@ -10,6 +10,14 @@ true + + $(DefineConstants);NETSTANDARD2_1_OR_GREATER + + + + $(DefineConstants);NETSTANDARD2_1_OR_GREATER + + diff --git a/src/FrostFS.SDK.Cryptography/UUID.cs b/src/FrostFS.SDK.Cryptography/UUID.cs index c92a874..94fba3f 100644 --- a/src/FrostFS.SDK.Cryptography/UUID.cs +++ b/src/FrostFS.SDK.Cryptography/UUID.cs @@ -11,9 +11,7 @@ public static class UUIDExtension if (id == null) throw new ArgumentNullException(nameof(id)); - var bytes = id.ToByteArray(); - - var orderedBytes = GetGuidBytesDirectOrder(bytes); + var orderedBytes = GetGuidBytesDirectOrder(id.Span); return new Guid(orderedBytes); } @@ -32,7 +30,7 @@ public static class UUIDExtension return orderedBytes; } - private static byte[] GetGuidBytesDirectOrder(byte[] source) + private static byte[] GetGuidBytesDirectOrder(ReadOnlySpan source) { if (source.Length != 16) throw new ArgumentException("Wrong uuid binary format"); diff --git a/src/FrostFS.SDK.Tests/Mocks/AsyncStreamRangeReaderMock.cs b/src/FrostFS.SDK.Tests/Mocks/AsyncStreamRangeReaderMock.cs index d1366c7..bd9d858 100644 --- a/src/FrostFS.SDK.Tests/Mocks/AsyncStreamRangeReaderMock.cs +++ b/src/FrostFS.SDK.Tests/Mocks/AsyncStreamRangeReaderMock.cs @@ -1,9 +1,4 @@ -using System.Security.Cryptography; - using FrostFS.Object; -using FrostFS.SDK.Client; -using FrostFS.SDK.Client.Mappers.GRPC; -using FrostFS.SDK.Cryptography; using FrostFS.Session; using Google.Protobuf; diff --git a/src/FrostFS.SDK.Tests/Mocks/AsyncStreamReaderMock.cs b/src/FrostFS.SDK.Tests/Mocks/AsyncStreamReaderMock.cs index c09df83..c2e30a4 100644 --- a/src/FrostFS.SDK.Tests/Mocks/AsyncStreamReaderMock.cs +++ b/src/FrostFS.SDK.Tests/Mocks/AsyncStreamReaderMock.cs @@ -43,7 +43,7 @@ public class AsyncStreamReaderMock(string key, FrostFsObjectHeader objectHeader) Signature = new Refs.Signature { Key = ByteString.CopyFrom(Key.PublicKey()), - Sign = ByteString.CopyFrom(Key.SignData(header.ToByteArray())), + Sign = Key.SignData(header.ToByteArray()) } } }, diff --git a/src/FrostFS.SDK.Tests/Mocks/ContainerServiceMocks/ContainerServiceBase.cs b/src/FrostFS.SDK.Tests/Mocks/ContainerServiceMocks/ContainerServiceBase.cs index ca1c670..8d4230c 100644 --- a/src/FrostFS.SDK.Tests/Mocks/ContainerServiceMocks/ContainerServiceBase.cs +++ b/src/FrostFS.SDK.Tests/Mocks/ContainerServiceMocks/ContainerServiceBase.cs @@ -46,19 +46,19 @@ public abstract class ServiceBase(string key) { Key = ByteString.CopyFrom(Key.PublicKey()), Scheme = Refs.SignatureScheme.EcdsaRfc6979Sha256, - Sign = ByteString.CopyFrom(Key.SignData(response.MetaHeader.ToByteArray())) + Sign = Key.SignData(response.MetaHeader.ToByteArray()) }, BodySignature = new Refs.Signature { Key = ByteString.CopyFrom(Key.PublicKey()), Scheme = Refs.SignatureScheme.EcdsaRfc6979Sha256, - Sign = ByteString.CopyFrom(Key.SignData(response.GetBody().ToByteArray())) + Sign = Key.SignData(response.GetBody().ToByteArray()) }, OriginSignature = new Refs.Signature { Key = ByteString.CopyFrom(Key.PublicKey()), Scheme = Refs.SignatureScheme.EcdsaRfc6979Sha256, - Sign = ByteString.CopyFrom(Key.SignData([])) + Sign = Key.SignData([]) } }; diff --git a/src/FrostFS.SDK.Tests/Mocks/ObjectMock.cs b/src/FrostFS.SDK.Tests/Mocks/ObjectMock.cs index 4b2b5c1..4f07e41 100644 --- a/src/FrostFS.SDK.Tests/Mocks/ObjectMock.cs +++ b/src/FrostFS.SDK.Tests/Mocks/ObjectMock.cs @@ -5,7 +5,6 @@ using FrostFS.Object; using FrostFS.SDK.Client; using FrostFS.SDK.Client.Mappers.GRPC; using FrostFS.SDK.Cryptography; -using FrostFS.Session; using Google.Protobuf; @@ -41,7 +40,7 @@ public class ObjectMocker(string key) : ObjectServiceBase(key) public GetRangeHashRequest? GetRangeHashRequest { get; set; } - public Collection RangeHashResponses { get; } = []; + public Collection RangeHashResponses { get; } = []; public override Mock GetMock() { @@ -94,7 +93,7 @@ public class ObjectMocker(string key) : ObjectServiceBase(key) headResponse.Body.Header.Signature = new Refs.Signature { Key = ByteString.CopyFrom(Key.PublicKey()), - Sign = ByteString.CopyFrom(Key.SignData(headResponse.Body.Header.ToByteArray())), + Sign = Key.SignData(headResponse.Body.Header.ToByteArray()), }; headResponse.VerifyHeader = GetResponseVerificationHeader(headResponse); @@ -259,7 +258,7 @@ public class ObjectMocker(string key) : ObjectServiceBase(key) response.Body.HashList.Add(hash); } } - + response.VerifyHeader = GetResponseVerificationHeader(response); return new AsyncUnaryCall( @@ -281,7 +280,7 @@ public class ObjectMocker(string key) : ObjectServiceBase(key) { Body = new PatchResponse.Types.Body { - ObjectId = new Refs.ObjectID { Value = ByteString.CopyFrom(SHA256.HashData([1,2,3])) }, + ObjectId = new Refs.ObjectID { Value = ByteString.CopyFrom(SHA256.HashData([1, 2, 3])) }, }, MetaHeader = ResponseMetaHeader }; diff --git a/src/FrostFS.SDK.Tests/ObjectTest.cs b/src/FrostFS.SDK.Tests/ObjectTest.cs index a5f9f01..d11dc60 100644 --- a/src/FrostFS.SDK.Tests/ObjectTest.cs +++ b/src/FrostFS.SDK.Tests/ObjectTest.cs @@ -10,8 +10,6 @@ using FrostFS.SDK.Cryptography; using Google.Protobuf; -using static FrostFS.Object.ECInfo.Types; - namespace FrostFS.SDK.Tests; [SuppressMessage("Reliability", "CA2007:Consider calling ConfigureAwait on the awaited task", Justification = "Default Value is correct for tests")] @@ -339,9 +337,9 @@ public class ObjectTest : ObjectTestsBase Assert.Equal(address.ContainerId, body.Address.ContainerId); Assert.Equal(address.ObjectId, body.Address.ObjectId); - + Assert.Equal(32, body.Patch.Chunk.Length); - + Assert.Equal(SHA256.HashData(patch), SHA256.HashData(body.Patch.Chunk.ToArray())); } } diff --git a/src/FrostFS.SDK.Tests/SmokeClientTests.cs b/src/FrostFS.SDK.Tests/SmokeClientTests.cs index ba822c7..1909526 100644 --- a/src/FrostFS.SDK.Tests/SmokeClientTests.cs +++ b/src/FrostFS.SDK.Tests/SmokeClientTests.cs @@ -248,83 +248,83 @@ public class SmokeClientTests : SmokeTestsBase [InlineData(6 * 1024 * 1024 + 100)] public async void SimpleScenarioTest(int objectSize) { - using var client = FrostFSClient.GetSingleOwnerInstance(GetSingleOwnerOptions(this.keyString, this.url)); + using var client = FrostFSClient.GetSingleOwnerInstance(GetSingleOwnerOptions(this.keyString, this.url)); - await Cleanup(client); + await Cleanup(client); - bool callbackInvoked = false; - var ctx = new CallContext + bool callbackInvoked = false; + var ctx = new CallContext + { + // Timeout = TimeSpan.FromSeconds(20), + Callback = new((CallStatistics cs) => { - // Timeout = TimeSpan.FromSeconds(20), - Callback = new((CallStatistics cs) => - { - callbackInvoked = true; - Assert.True(cs.ElapsedMicroSeconds > 0); - }) - }; - - var createContainerParam = new PrmContainerCreate( - new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, new FrostFsReplica(1)), [new("testKey", "testValue")]), ctx); - - var createdContainer = await client.CreateContainerAsync(createContainerParam); - - var container = await client.GetContainerAsync(new PrmContainerGet(createdContainer, ctx)); - Assert.NotNull(container); - Assert.True(callbackInvoked); - - var bytes = GetRandomBytes(objectSize); - - var param = new PrmObjectPut(new CallContext - { - Callback = new((CallStatistics cs) => Assert.True(cs.ElapsedMicroSeconds > 0)) + callbackInvoked = true; + Assert.True(cs.ElapsedMicroSeconds > 0); }) - { - Header = new FrostFsObjectHeader( - containerId: createdContainer, - type: FrostFsObjectType.Regular, - [new FrostFsAttributePair("fileName", "test")]), - Payload = new MemoryStream(bytes), - ClientCut = false - }; + }; - var objectId = await client.PutObjectAsync(param); + var createContainerParam = new PrmContainerCreate( + new FrostFsContainerInfo(new FrostFsPlacementPolicy(true, new FrostFsReplica(1)), [new("testKey", "testValue")]), ctx); - var filter = new FilterByAttributePair(FrostFsMatchType.Equals, "fileName", "test"); + var createdContainer = await client.CreateContainerAsync(createContainerParam); - bool hasObject = false; - await foreach (var objId in client.SearchObjectsAsync(new PrmObjectSearch(createdContainer) { Filters = [filter] })) - { - hasObject = true; + var container = await client.GetContainerAsync(new PrmContainerGet(createdContainer, ctx)); + Assert.NotNull(container); + Assert.True(callbackInvoked); - var objHeader = await client.GetObjectHeadAsync(new PrmObjectHeadGet(createdContainer, objectId)); - Assert.Equal((ulong)bytes.Length, objHeader.PayloadLength); - Assert.NotNull(objHeader.Attributes); - Assert.Single(objHeader.Attributes); - Assert.Equal("fileName", objHeader.Attributes.First().Key); - Assert.Equal("test", objHeader.Attributes.First().Value); - } + var bytes = GetRandomBytes(objectSize); - Assert.True(hasObject); + var param = new PrmObjectPut(new CallContext + { + Callback = new((CallStatistics cs) => Assert.True(cs.ElapsedMicroSeconds > 0)) + }) + { + Header = new FrostFsObjectHeader( + containerId: createdContainer, + type: FrostFsObjectType.Regular, + [new FrostFsAttributePair("fileName", "test")]), + Payload = new MemoryStream(bytes), + ClientCut = false + }; - var @object = await client.GetObjectAsync(new PrmObjectGet(createdContainer, objectId)); + var objectId = await client.PutObjectAsync(param); - var downloadedBytes = new byte[@object.Header.PayloadLength]; - MemoryStream ms = new(downloadedBytes); + var filter = new FilterByAttributePair(FrostFsMatchType.Equals, "fileName", "test"); - ReadOnlyMemory? chunk = null; - while ((chunk = await @object.ObjectReader!.ReadChunk()) != null) - { - ms.Write(chunk.Value.Span); - } + bool hasObject = false; + await foreach (var objId in client.SearchObjectsAsync(new PrmObjectSearch(createdContainer) { Filters = [filter] })) + { + hasObject = true; - Assert.Equal(SHA256.HashData(bytes), SHA256.HashData(downloadedBytes)); + var objHeader = await client.GetObjectHeadAsync(new PrmObjectHeadGet(createdContainer, objectId)); + Assert.Equal((ulong)bytes.Length, objHeader.PayloadLength); + Assert.NotNull(objHeader.Attributes); + Assert.Single(objHeader.Attributes); + Assert.Equal("fileName", objHeader.Attributes.First().Key); + Assert.Equal("test", objHeader.Attributes.First().Value); + } - await Cleanup(client); + Assert.True(hasObject); - await foreach (var _ in client.ListContainersAsync()) - { - Assert.Fail("Containers exist"); - } + var @object = await client.GetObjectAsync(new PrmObjectGet(createdContainer, objectId)); + + var downloadedBytes = new byte[@object.Header.PayloadLength]; + MemoryStream ms = new(downloadedBytes); + + ReadOnlyMemory? chunk = null; + while ((chunk = await @object.ObjectReader!.ReadChunk()) != null) + { + ms.Write(chunk.Value.Span); + } + + Assert.Equal(SHA256.HashData(bytes), SHA256.HashData(downloadedBytes)); + + await Cleanup(client); + + await foreach (var _ in client.ListContainersAsync()) + { + Assert.Fail("Containers exist"); + } } [Fact] @@ -388,7 +388,7 @@ public class SmokeClientTests : SmokeTestsBase ms.Write(chunk.Value.Span); } - for(int i = 0; i < (int)range.Offset; i++) + for (int i = 0; i < (int)range.Offset; i++) Assert.Equal(downloadedBytes[i], bytes[i]); var rangeEnd = range.Offset + range.Length; @@ -494,15 +494,15 @@ public class SmokeClientTests : SmokeTestsBase var objectId = await client.PutObjectAsync(param); - var rangeParam = new PrmRangeHashGet(createdContainer, objectId, [ new FrostFsRange(100, 64)], bytes); + var rangeParam = new PrmRangeHashGet(createdContainer, objectId, [new FrostFsRange(100, 64)], bytes); var hashes = await client.GetRangeHashAsync(rangeParam); foreach (var hash in hashes) { - var x = hash.Slice(0, 32).ToArray(); + var x = hash[..32].ToArray(); } - + await Cleanup(client); await foreach (var _ in client.ListContainersAsync())