163 lines
4.6 KiB
C#
163 lines
4.6 KiB
C#
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<byte> 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<byte> data)
|
|
{
|
|
if (key is null)
|
|
{
|
|
throw new ArgumentNullException(nameof(key));
|
|
}
|
|
|
|
Span<byte> 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<byte> 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<byte>.Empty),
|
|
};
|
|
}
|
|
|
|
var size = data.CalculateSize();
|
|
|
|
if (size == 0)
|
|
{
|
|
return new Signature
|
|
{
|
|
Key = key.PublicKeyProto,
|
|
Sign = key.ECDsaKey.SignData(ReadOnlyMemory<byte>.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);
|
|
}
|
|
}
|