using System; using System.Linq; using System.Security.Cryptography; using FrostFS.Object; using FrostFS.Refs; using FrostFS.SDK.Client.Mappers.GRPC; using FrostFS.SDK.Cryptography; using Google.Protobuf; namespace FrostFS.SDK.Client; public static class ObjectTools { public static FrostFsObjectId CalculateObjectId( FrostFsObjectHeader header, ReadOnlyMemory payloadHash, FrostFsOwner owner, FrostFsVersion version, ClientKey key) { if (header is null) { throw new ArgumentNullException(nameof(header)); } if (owner is null) { throw new ArgumentNullException(nameof(owner)); } if (version is null) { throw new ArgumentNullException(nameof(version)); } if (key is null) { throw new ArgumentNullException(nameof(key)); } var grpcHeader = CreateHeader(header, payloadHash, owner, version); if (header.Split != null) SetSplitValues(grpcHeader, header.Split, owner, version, key); using var sha256 = SHA256.Create(); using HashStream stream = new(sha256); grpcHeader.WriteTo(stream); return new FrostFsObjectId(Base58.Encode(stream.Hash())); } internal static Object.Object CreateSingleObject(FrostFsObject @object, ClientContext ctx) { @object.Header.OwnerId ??= ctx.Owner; @object.Header.Version ??= ctx.Version; var grpcHeader = @object.Header.GetHeader(); grpcHeader.PayloadLength = (ulong)@object.SingleObjectPayload.Length; if (@object.PayloadHash != null) { grpcHeader.PayloadHash = ChecksumFromSha256(@object.PayloadHash); } else { grpcHeader.PayloadHash = Sha256Checksum(@object.SingleObjectPayload); } var split = @object.Header.Split; if (split != null) { SetSplitValues(grpcHeader, split, ctx.Owner, ctx.Version, ctx.Key); } var obj = new Object.Object { Header = grpcHeader, ObjectId = new ObjectID { Value = UnsafeByteOperations.UnsafeWrap(grpcHeader.Sha256()) }, Payload = UnsafeByteOperations.UnsafeWrap(@object.SingleObjectPayload) }; obj.Signature = new Signature { Key = ctx.Key.PublicKeyProto, Sign = ctx.Key.ECDsaKey.SignData(obj.ObjectId.ToByteArray()), }; return obj; } internal static void SetSplitValues( Header grpcHeader, FrostFsSplit split, FrostFsOwner owner, FrostFsVersion version, ClientKey key) { if (split == null) { return; } if (key == null) { throw new FrostFsInvalidObjectException(nameof(key)); } grpcHeader.Split = new Header.Types.Split { SplitId = split.SplitId?.GetSplitId() }; if (split.Children != null && split.Children.Count != 0) { grpcHeader.Split.Children.AddRange(split.Children.Select(id => id.ToMessage())); } if (split.ParentHeader is not null) { var grpcParentHeader = CreateHeader(split.ParentHeader, DataHasher.Sha256([]), owner, version); grpcHeader.Split.Parent = new ObjectID { Value = UnsafeByteOperations.UnsafeWrap(grpcParentHeader.Sha256()) }; grpcHeader.Split.ParentHeader = grpcParentHeader; grpcHeader.Split.ParentSignature = new Signature { Key = key.PublicKeyProto, Sign = key.ECDsaKey.SignData(grpcHeader.Split.Parent.ToByteArray()), }; } grpcHeader.Split.Previous = split.Previous?.ToMessage(); } internal static Header CreateHeader( FrostFsObjectHeader header, ReadOnlyMemory payloadChecksum, FrostFsOwner owner, FrostFsVersion version) { header.OwnerId ??= owner; header.Version ??= version; var grpcHeader = header.GetHeader(); grpcHeader.PayloadHash = ChecksumFromSha256(payloadChecksum); return grpcHeader; } internal static Checksum Sha256Checksum(ReadOnlyMemory data) { return new Checksum { Type = ChecksumType.Sha256, Sum = UnsafeByteOperations.UnsafeWrap(DataHasher.Sha256(data)) }; } internal static Checksum ChecksumFromSha256(ReadOnlyMemory dataHash) { return new Checksum { Type = ChecksumType.Sha256, Sum = UnsafeByteOperations.UnsafeWrap(dataHash) }; } }