using System; using System.IO; using System.Security.Cryptography; using FrostFS.Refs; using FrostFS.SDK.Cryptography; using FrostFS.SDK.Proto.Interfaces; using FrostFS.Session; using Google.Protobuf; using Org.BouncyCastle.Asn1.Sec; using Org.BouncyCastle.Crypto.Digests; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Crypto.Signers; using Org.BouncyCastle.Math; using Org.BouncyCastle.Utilities; using Signature = FrostFS.Refs.Signature; namespace FrostFS.SDK.Client; public static class RequestSigner { internal const int RFC6979SignatureSize = 64; internal static ByteString SignRFC6979(this ECDsa key, byte[] data) { if (key is null) { throw new ArgumentNullException(nameof(key)); } var digest = new Sha256Digest(); var secp256R1 = SecNamedCurves.GetByName("secp256r1"); var ecParameters = new ECDomainParameters(secp256R1.Curve, secp256R1.G, secp256R1.N); var privateKey = new ECPrivateKeyParameters(new BigInteger(1, key.PrivateKey()), ecParameters); var signer = new ECDsaSigner(new HMacDsaKCalculator(digest)); var hash = new byte[digest.GetDigestSize()]; digest.BlockUpdate(data, 0, data.Length); digest.DoFinal(hash, 0); signer.Init(true, privateKey); var rs = signer.GenerateSignature(hash); Span signature = stackalloc byte[RFC6979SignatureSize]; var rbytes = rs[0].ToByteArrayUnsigned(); var sbytes = rs[1].ToByteArrayUnsigned(); var index = RFC6979SignatureSize / 2 - rbytes.Length; rbytes.AsSpan().CopyTo(signature.Slice(index)); index = RFC6979SignatureSize - sbytes.Length; sbytes.AsSpan().CopyTo(signature.Slice(index)); return ByteString.CopyFrom(signature); } internal static SignatureRFC6979 SignRFC6979(this ECDsa key, IMessage message) { return new SignatureRFC6979 { Key = ByteString.CopyFrom(key.PublicKey()), Sign = key.SignRFC6979(message.ToByteArray()), }; } internal static SignatureRFC6979 SignRFC6979(this ECDsa key, ByteString data) { return new SignatureRFC6979 { Key = ByteString.CopyFrom(key.PublicKey()), Sign = key.SignRFC6979(data.ToByteArray()), }; } public static ByteString SignData(this ECDsa key, ReadOnlyMemory data) { if (key is null) { throw new ArgumentNullException(nameof(key)); } Span result = stackalloc byte[65]; result[0] = 0x04; key.SignHash(data.Sha512()).AsSpan().CopyTo(result.Slice(1)); return ByteString.CopyFrom(result); } public static ByteString SignDataByHash(this ECDsa key, byte[] hash) { if (key is null) { throw new ArgumentNullException(nameof(key)); } Span result = stackalloc byte[65]; result[0] = 0x04; key.SignHash(hash).AsSpan().CopyTo(result.Slice(1)); return ByteString.CopyFrom(result); } internal static Signature SignMessagePart(this ClientKey key, IMessage? data) { if (data is null) { return new Signature { Key = key.PublicKeyProto, Sign = key.ECDsaKey.SignData(ReadOnlyMemory.Empty), }; } var size = data.CalculateSize(); if (size == 0) { return new Signature { Key = key.PublicKeyProto, Sign = key.ECDsaKey.SignData(ReadOnlyMemory.Empty), }; } using HashStream stream = new(); data.WriteTo(stream); var sig = new Signature { Key = key.PublicKeyProto, Sign = key.ECDsaKey.SignDataByHash(stream.Hash()) }; return sig; } internal static void Sign(this IVerifiableMessage message, ClientKey key) { var meta = message.GetMetaHeader(); IVerificationHeader verify = message switch { IRequest => new RequestVerificationHeader(), IResponse => new ResponseVerificationHeader(), _ => throw new InvalidOperationException("Unsupported message type") }; var verifyOrigin = message.GetVerificationHeader(); if (verifyOrigin is null) verify.BodySignature = key.SignMessagePart(message.GetBody()); else verify.SetOrigin(verifyOrigin); verify.MetaSignature = key.SignMessagePart(meta); verify.OriginSignature = key.SignMessagePart(verifyOrigin); message.SetVerificationHeader(verify); } }