using System.Security.Cryptography; using FrostFS.Refs; using FrostFS.SDK.Cryptography; 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; namespace FrostFS.SDK.ClientV2 { public static class RequestVerifier { public const int RFC6979SignatureSize = 64; private static BigInteger[] DecodeSignature(byte[] sig) { if (sig.Length != RFC6979SignatureSize) throw new FormatException($"Wrong signature size, expect={RFC6979SignatureSize}, actual={sig.Length}"); var rs = new BigInteger[2]; rs[0] = new BigInteger(1, sig[..32]); rs[1] = new BigInteger(1, sig[32..]); return rs; } public static bool VerifyRFC6979(this byte[] public_key, byte[] data, byte[] sig) { if (public_key is null || data is null || sig is null) return false; var rs = DecodeSignature(sig); var digest = new Sha256Digest(); var signer = new ECDsaSigner(new HMacDsaKCalculator(digest)); var secp256r1 = SecNamedCurves.GetByName("secp256r1"); var ec_parameters = new ECDomainParameters(secp256r1.Curve, secp256r1.G, secp256r1.N); var bc_public_key = new ECPublicKeyParameters(secp256r1.Curve.DecodePoint(public_key), ec_parameters); var hash = new byte[digest.GetDigestSize()]; digest.BlockUpdate(data, 0, data.Length); digest.DoFinal(hash, 0); signer.Init(false, bc_public_key); return signer.VerifySignature(hash, rs[0], rs[1]); } public static bool VerifyRFC6979(this SignatureRFC6979 signature, IMessage message) { return signature.Key.ToByteArray().VerifyRFC6979(message.ToByteArray(), signature.Sign.ToByteArray()); } public static bool VerifyData(this ECDsa key, byte[] data, byte[] sig) { return key.VerifyHash(SHA512.Create().ComputeHash(data), sig[1..]); } public static bool VerifyMessagePart(this Signature sig, IMessage data) { if (sig is null || sig.Key is null || sig.Sign is null) return false; using var key = sig.Key.ToByteArray().LoadPublicKey(); var data2verify = data is null ? Array.Empty() : data.ToByteArray(); return key.VerifyData(data2verify, sig.Sign.ToByteArray()); } public static bool VerifyMatryoskaLevel(IMessage body, IMetaHeader meta, IVerificationHeader verification) { if (!verification.MetaSignature.VerifyMessagePart(meta)) return false; var origin = verification.GetOrigin(); if (!verification.OriginSignature.VerifyMessagePart(origin)) return false; if (origin is null) return verification.BodySignature.VerifyMessagePart(body); if (verification.BodySignature is not null) return false; return VerifyMatryoskaLevel(body, meta.GetOrigin(), origin); } public static bool Verify(this IVerificableMessage message) { return VerifyMatryoskaLevel(message.GetBody(), message.GetMetaHeader(), message.GetVerificationHeader()); } public static void ProcessResponse(IResponse resp) { Console.WriteLine(resp.MetaHeader.Status); if (!resp.Verify()) throw new FormatException($"invalid response, type={resp.GetType()}"); } } }