frostfs-sdk-java/client/src/main/java/info/frostfs/sdk/tools/Verifier.java
Ori Bruk 3861eb0dc2 [#41] Add APE rule serializer
Signed-off-by: Ori Bruk <o.bruk@yadro.com>
2025-02-13 20:57:31 +03:00

140 lines
5.9 KiB
Java

package info.frostfs.sdk.tools;
import com.google.protobuf.Message;
import frostfs.session.Types;
import info.frostfs.sdk.constants.CryptoConst;
import info.frostfs.sdk.exceptions.ProcessFrostFSException;
import info.frostfs.sdk.exceptions.ResponseFrostFSException;
import info.frostfs.sdk.exceptions.ValidationFrostFSException;
import info.frostfs.sdk.mappers.response.ResponseStatusMapper;
import info.frostfs.sdk.utils.MessageHelper;
import org.apache.commons.codec.digest.DigestUtils;
import org.bouncycastle.asn1.sec.SECNamedCurves;
import org.bouncycastle.asn1.sec.SECObjectIdentifiers;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.crypto.signers.ECDSASigner;
import org.bouncycastle.crypto.signers.HMacDSAKCalculator;
import java.math.BigInteger;
import java.security.PublicKey;
import java.security.Signature;
import java.util.Arrays;
import static info.frostfs.sdk.KeyExtension.getPublicKeyFromBytes;
import static info.frostfs.sdk.constants.CryptoConst.RFC6979_SIGNATURE_SIZE;
import static info.frostfs.sdk.constants.ErrorConst.INVALID_RESPONSE;
import static info.frostfs.sdk.constants.ErrorConst.WRONG_SIGNATURE_SIZE_TEMPLATE;
import static info.frostfs.sdk.constants.FieldConst.*;
import static java.util.Objects.isNull;
import static org.bouncycastle.crypto.util.DigestFactory.createSHA256;
import static org.bouncycastle.util.BigIntegers.fromUnsignedByteArray;
public class Verifier {
private Verifier() {
}
public static boolean verifyRFC6979(frostfs.refs.Types.SignatureRFC6979 signature, Message data) {
return verifyRFC6979(signature.getKey().toByteArray(), data.toByteArray(), signature.getSign().toByteArray());
}
public static boolean verifyRFC6979(byte[] publicKey, byte[] data, byte[] sig) {
if (isNull(publicKey) || isNull(data) || isNull(sig)) {
return false;
}
var rs = decodeSignature(sig);
var digest = createSHA256();
var signer = new ECDSASigner(new HMacDSAKCalculator(digest));
var secp256R1 = SECNamedCurves.getByOID(SECObjectIdentifiers.secp256r1);
var ecParameters = new ECDomainParameters(secp256R1.getCurve(), secp256R1.getG(), secp256R1.getN());
var ecPublicKey = new ECPublicKeyParameters(secp256R1.getCurve().decodePoint(publicKey), ecParameters);
var hash = new byte[digest.getDigestSize()];
digest.update(data, 0, data.length);
digest.doFinal(hash, 0);
signer.init(false, ecPublicKey);
return signer.verifySignature(hash, rs[0], rs[1]);
}
private static BigInteger[] decodeSignature(byte[] sig) {
if (sig.length != RFC6979_SIGNATURE_SIZE) {
throw new ValidationFrostFSException(
String.format(WRONG_SIGNATURE_SIZE_TEMPLATE, RFC6979_SIGNATURE_SIZE, sig.length)
);
}
var rs = new BigInteger[2];
rs[0] = fromUnsignedByteArray(Arrays.copyOfRange(sig, 0, (RFC6979_SIGNATURE_SIZE / 2) - 1));
rs[1] = fromUnsignedByteArray(
Arrays.copyOfRange(sig, RFC6979_SIGNATURE_SIZE / 2, RFC6979_SIGNATURE_SIZE - 1)
);
return rs;
}
public static void checkResponse(Message response) {
if (!verify(response)) {
throw new ResponseFrostFSException(INVALID_RESPONSE);
}
var metaHeader = (Types.ResponseMetaHeader) MessageHelper.getField(response, META_HEADER_FIELD_NAME);
if (isNull(metaHeader) || metaHeader.getSerializedSize() == 0) {
return;
}
var status = ResponseStatusMapper.toModel(metaHeader.getStatus());
if (!status.isSuccess()) {
throw new ResponseFrostFSException(status);
}
}
public static boolean verify(Message response) {
var body = (Message) MessageHelper.getField(response, BODY_FIELD_NAME);
var metaHeader = (Types.ResponseMetaHeader) MessageHelper.getField(response, META_HEADER_FIELD_NAME);
var verifyHeader = (Types.ResponseVerificationHeader)
MessageHelper.getField(response, VERIFY_HEADER_FIELD_NAME);
return verifyMatryoshkaLevel(body, metaHeader, verifyHeader);
}
public static boolean verifyMatryoshkaLevel(Message data,
frostfs.session.Types.ResponseMetaHeader meta,
frostfs.session.Types.ResponseVerificationHeader verification) {
if (!verifyMessagePart(verification.getMetaSignature(), meta)) {
return false;
}
var origin = verification.getOrigin();
if (!verifyMessagePart(verification.getOriginSignature(), origin)) {
return false;
}
if (origin.getSerializedSize() == 0) {
return verifyMessagePart(verification.getBodySignature(), data);
}
return verification.getBodySignature().getSerializedSize() == 0
&& verifyMatryoshkaLevel(data, meta.getOrigin(), origin);
}
public static boolean verifyMessagePart(frostfs.refs.Types.Signature sig, Message data) {
if (sig.getSerializedSize() == 0 || sig.getKey().isEmpty() || sig.getSign().isEmpty()) {
return false;
}
var publicKey = getPublicKeyFromBytes(sig.getKey().toByteArray());
var data2Verify = data.getSerializedSize() == 0 ? new byte[]{} : data.toByteArray();
return verifyData(publicKey, data2Verify, sig.getSign().toByteArray());
}
public static boolean verifyData(PublicKey publicKey, byte[] data, byte[] sig) {
try {
Signature signature = Signature.getInstance(CryptoConst.SIGNATURE_ALGORITHM);
signature.initVerify(publicKey);
signature.update(DigestUtils.sha512(data));
return signature.verify(Arrays.copyOfRange(sig, 1, sig.length));
} catch (Exception exp) {
throw new ProcessFrostFSException(exp);
}
}
}