All checks were successful
DCO / DCO (pull_request) Successful in 35s
Signed-off-by: Pavel Gross <p.gross@yadro.com>
142 lines
No EOL
4.4 KiB
C#
142 lines
No EOL
4.4 KiB
C#
using System;
|
|
using System.Security.Cryptography;
|
|
|
|
using FrostFS.Refs;
|
|
using FrostFS.SDK.ClientV2.Mappers.GRPC;
|
|
using FrostFS.SDK.Cryptography;
|
|
using FrostFS.SDK.ProtosV2.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;
|
|
|
|
namespace FrostFS.SDK.ClientV2;
|
|
|
|
public static class Verifier
|
|
{
|
|
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[] publicKey, byte[] data, byte[] sig)
|
|
{
|
|
if (publicKey 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 ecParameters = new ECDomainParameters(secp256R1.Curve, secp256R1.G, secp256R1.N);
|
|
var bcPublicKey = new ECPublicKeyParameters(secp256R1.Curve.DecodePoint(publicKey), ecParameters);
|
|
var hash = new byte[digest.GetDigestSize()];
|
|
|
|
digest.BlockUpdate(data, 0, data.Length);
|
|
digest.DoFinal(hash, 0);
|
|
signer.Init(false, bcPublicKey);
|
|
|
|
return signer.VerifySignature(hash, rs[0], rs[1]);
|
|
}
|
|
|
|
public static bool VerifyRFC6979(this SignatureRFC6979 signature, IMessage message)
|
|
{
|
|
if (signature is null)
|
|
{
|
|
throw new ArgumentNullException(nameof(signature));
|
|
}
|
|
|
|
return signature.Key.ToByteArray().VerifyRFC6979(message.ToByteArray(), signature.Sign.ToByteArray());
|
|
}
|
|
|
|
public static bool VerifyData(this ECDsa key, byte[] data, byte[] 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..]);
|
|
}
|
|
|
|
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 ? [] : data.ToByteArray();
|
|
|
|
return key.VerifyData(data2Verify, sig.Sign.ToByteArray());
|
|
}
|
|
|
|
internal 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);
|
|
|
|
return verification.BodySignature is null && VerifyMatryoskaLevel(body, meta.GetOrigin(), origin);
|
|
}
|
|
|
|
public static bool Verify(this IVerifiableMessage message)
|
|
{
|
|
if (message is null)
|
|
{
|
|
throw new ArgumentNullException(nameof(message));
|
|
}
|
|
|
|
return VerifyMatryoskaLevel(message.GetBody(), message.GetMetaHeader(), message.GetVerificationHeader());
|
|
}
|
|
|
|
internal static void CheckResponse(IResponse resp)
|
|
{
|
|
if (!resp.Verify())
|
|
throw new FormatException($"invalid response, type={resp.GetType()}");
|
|
|
|
var status = resp.MetaHeader.Status.ToModel();
|
|
|
|
if (status != null && !status.IsSuccess)
|
|
throw new ResponseException(status);
|
|
}
|
|
|
|
/// <summary>
|
|
/// This method is intended for unit tests for request verification.
|
|
/// </summary>
|
|
/// <param name="request">Created by SDK request to gRpc proxy</param>
|
|
public static void CheckRequest(IRequest request)
|
|
{
|
|
if (request is null)
|
|
{
|
|
throw new ArgumentNullException(nameof(request));
|
|
}
|
|
|
|
if (!request.Verify())
|
|
throw new FormatException($"invalid response, type={request.GetType()}");
|
|
}
|
|
} |