forked from TrueCloudLab/frostfs-sdk-java
116 lines
5.4 KiB
Java
116 lines
5.4 KiB
Java
package info.FrostFS.sdk;
|
|
|
|
import com.google.protobuf.AbstractMessage;
|
|
import frostFS.refs.Types;
|
|
import info.FrostFS.sdk.mappers.StatusMapper;
|
|
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.getPublicKeyByPublic;
|
|
import static java.util.Objects.isNull;
|
|
import static org.bouncycastle.crypto.util.DigestFactory.createSHA256;
|
|
import static org.bouncycastle.util.BigIntegers.fromUnsignedByteArray;
|
|
|
|
public class Verifier {
|
|
public static final int RFC6979_SIGNATURE_SIZE = 64;
|
|
|
|
public static boolean verifyRFC6979(Types.SignatureRFC6979 signature, AbstractMessage message) {
|
|
return verifyRFC6979(signature.getKey().toByteArray(), message.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 IllegalArgumentException(
|
|
String.format("Wrong signature size. Expected length=%s, actual=%s",
|
|
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(AbstractMessage response) {
|
|
if (!verify(response)) {
|
|
throw new IllegalArgumentException("Invalid response");
|
|
}
|
|
var metaHeader = (frostFS.session.Types.ResponseMetaHeader) response
|
|
.getField(response.getDescriptorForType().findFieldByName("meta_header"));
|
|
var status = StatusMapper.toModel(metaHeader.getStatus());
|
|
if (!status.isSuccess()) {
|
|
throw new IllegalArgumentException(status.toString());
|
|
}
|
|
}
|
|
|
|
public static boolean verify(AbstractMessage message) {
|
|
var body = (AbstractMessage) message.getField(message.getDescriptorForType().findFieldByName("body"));
|
|
var metaHeader = (frostFS.session.Types.ResponseMetaHeader)
|
|
message.getField(message.getDescriptorForType().findFieldByName("meta_header"));
|
|
var verifyHeader = (frostFS.session.Types.ResponseVerificationHeader)
|
|
message.getField(message.getDescriptorForType().findFieldByName("verify_header"));
|
|
|
|
return verifyMatryoshkaLevel(body, metaHeader, verifyHeader);
|
|
}
|
|
|
|
public static boolean verifyMatryoshkaLevel(AbstractMessage body,
|
|
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(), body);
|
|
}
|
|
return verification.getBodySignature().getSerializedSize() == 0
|
|
&& verifyMatryoshkaLevel(body, meta.getOrigin(), origin);
|
|
}
|
|
|
|
public static boolean verifyMessagePart(Types.Signature sig, AbstractMessage data) {
|
|
if (sig.getSerializedSize() == 0 || sig.getKey().isEmpty() || sig.getSign().isEmpty()) return false;
|
|
|
|
var publicKey = getPublicKeyByPublic(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("NONEwithECDSAinP1363Format");
|
|
signature.initVerify(publicKey);
|
|
signature.update(DigestUtils.sha512(data));
|
|
return signature.verify(Arrays.copyOfRange(sig, 1, sig.length));
|
|
} catch (Exception exp) {
|
|
throw new RuntimeException(exp);
|
|
}
|
|
}
|
|
}
|