mirror of
https://github.com/bisq-network/bisq.git
synced 2024-11-20 02:12:00 +01:00
Add AgeWittness domain (WIP)
This commit is contained in:
parent
fcd3106eb4
commit
007142ed9b
@ -20,6 +20,7 @@ package io.bisq.common.crypto;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.security.PublicKey;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.spec.X509EncodedKeySpec;
|
||||
import java.util.Base64;
|
||||
|
||||
@ -29,5 +30,11 @@ public class CryptoUtils {
|
||||
final X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(publicKey.getEncoded());
|
||||
return Base64.getEncoder().encodeToString(x509EncodedKeySpec.getEncoded());
|
||||
}
|
||||
|
||||
public static byte[] getSalt(int size) {
|
||||
byte[] salt = new byte[size];
|
||||
new SecureRandom().nextBytes(salt);
|
||||
return salt;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -17,9 +17,11 @@
|
||||
|
||||
package io.bisq.core.payment;
|
||||
|
||||
import io.bisq.common.crypto.CryptoUtils;
|
||||
import io.bisq.common.locale.TradeCurrency;
|
||||
import io.bisq.common.proto.ProtoUtil;
|
||||
import io.bisq.common.proto.persistable.PersistablePayload;
|
||||
import io.bisq.common.util.Utilities;
|
||||
import io.bisq.core.payment.payload.PaymentAccountPayload;
|
||||
import io.bisq.core.payment.payload.PaymentMethod;
|
||||
import io.bisq.core.proto.CoreProtoResolver;
|
||||
@ -57,6 +59,15 @@ public abstract class PaymentAccount implements PersistablePayload {
|
||||
@Nullable
|
||||
protected TradeCurrency selectedTradeCurrency;
|
||||
|
||||
@Setter
|
||||
@Nullable
|
||||
protected PaymentAccountAgeWitness paymentAccountAgeWitness;
|
||||
|
||||
// TODO add to PB!
|
||||
@Setter
|
||||
@Nullable
|
||||
protected byte[] salt;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Constructor
|
||||
@ -70,6 +81,9 @@ public abstract class PaymentAccount implements PersistablePayload {
|
||||
id = UUID.randomUUID().toString();
|
||||
creationDate = new Date().getTime();
|
||||
paymentAccountPayload = getPayload();
|
||||
|
||||
// We set by default a random salt. User can set salt as well by hex string
|
||||
salt = CryptoUtils.getSalt(32); // 256 bit
|
||||
}
|
||||
|
||||
|
||||
@ -142,4 +156,20 @@ public abstract class PaymentAccount implements PersistablePayload {
|
||||
}
|
||||
|
||||
protected abstract PaymentAccountPayload getPayload();
|
||||
|
||||
// TODO make abstract
|
||||
// Identifying data of payment account (e.g. IBAN).
|
||||
// This is critical code for verifying age of payment account.
|
||||
// Any change would break validation of historical data!
|
||||
public byte[] getAgeWitnessInputData() {
|
||||
return new byte[0];
|
||||
}
|
||||
|
||||
public void setSaltAsHex(String saltAsHex) {
|
||||
this.salt = Utilities.decodeFromHex(saltAsHex);
|
||||
}
|
||||
|
||||
public String getSaltAsHex() {
|
||||
return Utilities.bytesAsHexString(salt);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,135 @@
|
||||
/*
|
||||
* This file is part of bisq.
|
||||
*
|
||||
* bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bisq.core.payment;
|
||||
|
||||
import com.google.protobuf.Message;
|
||||
import io.bisq.common.crypto.Sig;
|
||||
import io.bisq.network.p2p.storage.payload.LazyProcessedStoragePayload;
|
||||
import io.bisq.network.p2p.storage.payload.PersistedStoragePayload;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Value;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.security.PublicKey;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
// Object has 118 raw bytes (not PB size)
|
||||
@Slf4j
|
||||
@EqualsAndHashCode(exclude = {"signaturePubKey"})
|
||||
@Value
|
||||
public class PaymentAccountAgeWitness implements LazyProcessedStoragePayload, PersistedStoragePayload {
|
||||
|
||||
private final byte[] hash; // 32 bytes
|
||||
private final byte[] hashOfPubKey; // 32 bytes
|
||||
private final byte[] signature; // 46 bytes
|
||||
private final long tradeDate; // 8 byte
|
||||
|
||||
|
||||
// Only used as cache for getOwnerPubKey
|
||||
transient private final PublicKey signaturePubKey;
|
||||
|
||||
// Should be only used in emergency case if we need to add data but do not want to break backward compatibility
|
||||
// at the P2P network storage checks. The hash of the object will be used to verify if the data is valid. Any new
|
||||
// field in a class would break that hash and therefore break the storage mechanism.
|
||||
@Nullable
|
||||
private Map<String, String> extraDataMap;
|
||||
|
||||
public PaymentAccountAgeWitness(byte[] hash,
|
||||
byte[] hashOfPubKey,
|
||||
byte[] signature,
|
||||
long tradeDate) {
|
||||
|
||||
this(hash,
|
||||
hashOfPubKey,
|
||||
signature,
|
||||
tradeDate,
|
||||
null);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// PROTO BUFFER
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
PaymentAccountAgeWitness(byte[] hash,
|
||||
byte[] hashOfPubKey,
|
||||
byte[] signature,
|
||||
long tradeDate,
|
||||
@Nullable Map<String, String> extraDataMap) {
|
||||
this.hash = hash;
|
||||
this.hashOfPubKey = hashOfPubKey;
|
||||
this.signature = signature;
|
||||
this.tradeDate = tradeDate;
|
||||
this.extraDataMap = extraDataMap;
|
||||
|
||||
signaturePubKey = Sig.getPublicKeyFromBytes(signature);
|
||||
}
|
||||
|
||||
|
||||
// TODO
|
||||
@Override
|
||||
public Message toProtoMessage() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/* @Override
|
||||
public PB.StoragePayload toProtoMessage() {
|
||||
final PB.PaymentAccountAgeWitness.Builder builder = PB.PaymentAccountAgeWitness.newBuilder()
|
||||
.setSignaturePubKeyBytes(ByteString.copyFrom(hash))
|
||||
.setSignaturePubKeyBytes(ByteString.copyFrom(pubKeyBytes))
|
||||
.setSignaturePubKeyBytes(ByteString.copyFrom(signaturePubKeyBytes));
|
||||
Optional.ofNullable(extraDataMap).ifPresent(builder::putAllExtraData);
|
||||
return PB.StoragePayload.newBuilder().setPaymentAccountAgeWitness(builder).build();
|
||||
}
|
||||
|
||||
public PB.PaymentAccountAgeWitness toProtoPaymentAccountAgeWitness() {
|
||||
return toProtoMessage().getPaymentAccountAgeWitness();
|
||||
}
|
||||
|
||||
public static PaymentAccountAgeWitness fromProto(PB.PaymentAccountAgeWitness proto) {
|
||||
return new PaymentAccountAgeWitness(
|
||||
OfferPayload.Direction.fromProto(proto.getDirection()),
|
||||
proto.getHash().toByteArray(),
|
||||
proto.getPubKeyBytes().toByteArray(),
|
||||
proto.getSignaturePubKeyBytes().toByteArray(),
|
||||
CollectionUtils.isEmpty(proto.getExtraDataMap()) ? null : proto.getExtraDataMap());
|
||||
}*/
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Getters
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public long getTTL() {
|
||||
return TimeUnit.DAYS.toMillis(30);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PublicKey getOwnerPubKey() {
|
||||
return signaturePubKey;
|
||||
}
|
||||
|
||||
//TODO impl. here or in caller?
|
||||
// We allow max 1 day time difference
|
||||
public boolean isTradeDateValid() {
|
||||
return new Date().getTime() - tradeDate < TimeUnit.DAYS.toMillis(1);
|
||||
}
|
||||
}
|
@ -0,0 +1,144 @@
|
||||
/*
|
||||
* This file is part of bisq.
|
||||
*
|
||||
* bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bisq.core.payment;
|
||||
|
||||
import io.bisq.common.crypto.CryptoException;
|
||||
import io.bisq.common.crypto.Hash;
|
||||
import io.bisq.common.crypto.KeyRing;
|
||||
import io.bisq.common.crypto.Sig;
|
||||
import io.bisq.common.util.Utilities;
|
||||
import io.bisq.core.trade.Trade;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.bitcoinj.core.Sha256Hash;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.math.BigInteger;
|
||||
import java.security.PublicKey;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Slf4j
|
||||
public class PaymentAccountAgeWitnessService {
|
||||
|
||||
private KeyRing keyRing;
|
||||
|
||||
@Inject
|
||||
public PaymentAccountAgeWitnessService(KeyRing keyRing) {
|
||||
this.keyRing = keyRing;
|
||||
}
|
||||
|
||||
public PaymentAccountAgeWitness getPaymentAccountWitness(PaymentAccount paymentAccount, Trade trade) throws CryptoException {
|
||||
byte[] ageWitnessInputData = paymentAccount.getAgeWitnessInputData();
|
||||
byte[] salt = paymentAccount.getSalt();
|
||||
final byte[] combined = ArrayUtils.addAll(ageWitnessInputData, salt);
|
||||
byte[] hash = Sha256Hash.hash(combined);
|
||||
byte[] signature = Sig.sign(keyRing.getSignatureKeyPair().getPrivate(), hash);
|
||||
long tradeDate = trade.getTakeOfferDate().getTime();
|
||||
byte[] hashOfPubKey = Sha256Hash.hash(keyRing.getPubKeyRing().getSignaturePubKeyBytes());
|
||||
return new PaymentAccountAgeWitness(hash,
|
||||
hashOfPubKey,
|
||||
signature,
|
||||
tradeDate);
|
||||
}
|
||||
|
||||
boolean verifyAgeWitness(byte[] peersAgeWitnessInputData,
|
||||
PaymentAccountAgeWitness witness,
|
||||
byte[] peersSalt,
|
||||
PublicKey peersPublicKey,
|
||||
int nonce,
|
||||
byte[] signatureOfNonce) throws CryptoException {
|
||||
|
||||
// Check if trade date in witness is not older than the release date of that feature (was added in v0.6)
|
||||
Date ageWitnessReleaseDate = new GregorianCalendar(2017, 9, 23).getTime();
|
||||
if (!isTradeDateAfterReleaseDate(witness.getTradeDate(), ageWitnessReleaseDate))
|
||||
return false;
|
||||
|
||||
|
||||
// Check if peer's pubkey is matching the hash in the witness data
|
||||
if (!verifyPubKeyHash(witness.getHashOfPubKey(), peersPublicKey))
|
||||
return false;
|
||||
|
||||
final byte[] combined = ArrayUtils.addAll(peersAgeWitnessInputData, peersSalt);
|
||||
byte[] hash = Sha256Hash.hash(combined);
|
||||
|
||||
// Check if the hash in the witness data matches the peer's payment account input data + salt
|
||||
if (!verifyWitnessHash(witness.getHash(), hash))
|
||||
return false;
|
||||
|
||||
// Check if the witness signature is correct
|
||||
if (!verifySignature(peersPublicKey, hash, witness.getSignature()))
|
||||
return false;
|
||||
|
||||
// Check if the signature of the nonce is correct
|
||||
return !verifySignatureOfNonce(peersPublicKey, nonce, signatureOfNonce);
|
||||
}
|
||||
|
||||
boolean isTradeDateAfterReleaseDate(long tradeDateAsLong, Date ageWitnessReleaseDate) {
|
||||
// Release date minus 1 day as tolerance for not synced clocks
|
||||
Date releaseDateWithTolerance = new Date(ageWitnessReleaseDate.getTime() - TimeUnit.DAYS.toMillis(1));
|
||||
final Date tradeDate = new Date(tradeDateAsLong);
|
||||
final boolean result = tradeDate.after(releaseDateWithTolerance);
|
||||
if (!result)
|
||||
log.warn("Trade date is earlier than release date of ageWitness minus 1 day. " +
|
||||
"ageWitnessReleaseDate={}, tradeDate={}", ageWitnessReleaseDate, tradeDate);
|
||||
return result;
|
||||
}
|
||||
|
||||
boolean verifyPubKeyHash(byte[] hashOfPubKey,
|
||||
PublicKey peersPublicKey) {
|
||||
final boolean result = Arrays.equals(Hash.getHash(Sig.getPublicKeyBytes(peersPublicKey)), hashOfPubKey);
|
||||
if (!result)
|
||||
log.warn("hashOfPubKey is not matching peers peersPublicKey. " +
|
||||
"hashOfPubKey={}, peersPublicKey={}", Utilities.bytesAsHexString(hashOfPubKey), peersPublicKey);
|
||||
return result;
|
||||
}
|
||||
|
||||
boolean verifyWitnessHash(byte[] witnessHash,
|
||||
byte[] hash) {
|
||||
final boolean result = Arrays.equals(witnessHash, hash);
|
||||
if (!result)
|
||||
log.warn("witnessHash is not matching peers hash. " +
|
||||
"witnessHash={}, hash={}", Utilities.bytesAsHexString(witnessHash), Utilities.bytesAsHexString(hash));
|
||||
return result;
|
||||
}
|
||||
|
||||
boolean verifySignature(PublicKey peersPublicKey, byte[] data, byte[] signature) {
|
||||
try {
|
||||
return Sig.verify(peersPublicKey, data, signature);
|
||||
} catch (CryptoException e) {
|
||||
log.warn("Signature of PaymentAccountAgeWitness is not correct. " +
|
||||
"peersPublicKey={}, data={}, signature={}",
|
||||
peersPublicKey, Utilities.bytesAsHexString(data), Utilities.bytesAsHexString(signature));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
boolean verifySignatureOfNonce(PublicKey peersPublicKey, int nonce, byte[] signature) {
|
||||
try {
|
||||
return Sig.verify(peersPublicKey, BigInteger.valueOf(nonce).toByteArray(), signature);
|
||||
} catch (CryptoException e) {
|
||||
log.warn("Signature of nonce is not correct. " +
|
||||
"peersPublicKey={}, nonce={}, signature={}",
|
||||
peersPublicKey, nonce, Utilities.bytesAsHexString(signature));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
@ -22,7 +22,9 @@ import io.bisq.core.payment.payload.PaymentAccountPayload;
|
||||
import io.bisq.core.payment.payload.PaymentMethod;
|
||||
import io.bisq.core.payment.payload.SepaAccountPayload;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.List;
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ -77,4 +79,11 @@ public final class SepaAccount extends CountryBasedPaymentAccount implements Ban
|
||||
public void removeAcceptedCountry(String countryCode) {
|
||||
((SepaAccountPayload) paymentAccountPayload).removeAcceptedCountry(countryCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getAgeWitnessInputData() {
|
||||
// We don't add holderName because we don't want to break age validation if the user recreates an account with
|
||||
// slight changes in holder name (e.g. add or remove middle name)
|
||||
return ArrayUtils.addAll(getIban().getBytes(Charset.forName("UTF-8")), getBic().getBytes(Charset.forName("UTF-8")));
|
||||
}
|
||||
}
|
||||
|
@ -31,6 +31,10 @@ import lombok.extern.slf4j.Slf4j;
|
||||
public abstract class PaymentAccountPayload implements NetworkPayload {
|
||||
protected final String paymentMethodId;
|
||||
protected final String id;
|
||||
|
||||
// That is problematic and should be removed in next hard fork.
|
||||
// Any change in maxTradePeriod would make existing payment accounts incompatible.
|
||||
// TODO prepare backward compatible change
|
||||
protected final long maxTradePeriod;
|
||||
|
||||
|
||||
|
@ -0,0 +1,109 @@
|
||||
package io.bisq.core.payment;
|
||||
|
||||
import io.bisq.common.crypto.CryptoException;
|
||||
import io.bisq.common.crypto.Hash;
|
||||
import io.bisq.common.crypto.Sig;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.bitcoinj.core.Sha256Hash;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.nio.charset.Charset;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PublicKey;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
@Slf4j
|
||||
public class PaymentAccountAgeWitnessServiceTest {
|
||||
|
||||
private PublicKey publicKey;
|
||||
private KeyPair keypair;
|
||||
private PaymentAccountAgeWitnessService service;
|
||||
|
||||
@Before
|
||||
public void setup() throws CertificateException, NoSuchAlgorithmException, KeyStoreException, IOException, CryptoException {
|
||||
service = new PaymentAccountAgeWitnessService(null);
|
||||
keypair = Sig.generateKeyPair();
|
||||
publicKey = keypair.getPublic();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws IOException {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsTradeDateAfterReleaseDate() throws CryptoException {
|
||||
Date ageWitnessReleaseDate = new GregorianCalendar(2017, 9, 23).getTime();
|
||||
Date tradeDate = new GregorianCalendar(2017, 10, 1).getTime();
|
||||
assertTrue(service.isTradeDateAfterReleaseDate(tradeDate.getTime(), ageWitnessReleaseDate));
|
||||
tradeDate = new GregorianCalendar(2017, 9, 23).getTime();
|
||||
assertTrue(service.isTradeDateAfterReleaseDate(tradeDate.getTime(), ageWitnessReleaseDate));
|
||||
tradeDate = new GregorianCalendar(2017, 9, 22, 0, 0, 1).getTime();
|
||||
assertTrue(service.isTradeDateAfterReleaseDate(tradeDate.getTime(), ageWitnessReleaseDate));
|
||||
tradeDate = new GregorianCalendar(2017, 9, 22).getTime();
|
||||
assertFalse(service.isTradeDateAfterReleaseDate(tradeDate.getTime(), ageWitnessReleaseDate));
|
||||
tradeDate = new GregorianCalendar(2017, 9, 21).getTime();
|
||||
assertFalse(service.isTradeDateAfterReleaseDate(tradeDate.getTime(), ageWitnessReleaseDate));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVerifyPubKeyHash() {
|
||||
byte[] hashOfPubKey = Hash.getHash(Sig.getPublicKeyBytes(publicKey));
|
||||
assertFalse(service.verifyPubKeyHash(new byte[0], publicKey));
|
||||
assertFalse(service.verifyPubKeyHash(new byte[1], publicKey));
|
||||
assertTrue(service.verifyPubKeyHash(hashOfPubKey, publicKey));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVerifySignature() throws CryptoException {
|
||||
byte[] ageWitnessInputData = "test".getBytes(Charset.forName("UTF-8"));
|
||||
byte[] salt = "salt".getBytes(Charset.forName("UTF-8"));
|
||||
final byte[] combined = ArrayUtils.addAll(ageWitnessInputData, salt);
|
||||
byte[] hash = Sha256Hash.hash(combined);
|
||||
byte[] signature = Sig.sign(keypair.getPrivate(), hash);
|
||||
assertTrue(service.verifySignature(publicKey, hash, signature));
|
||||
assertFalse(service.verifySignature(publicKey, new byte[0], new byte[0]));
|
||||
assertFalse(service.verifySignature(publicKey, hash, "sig2".getBytes(Charset.forName("UTF-8"))));
|
||||
assertFalse(service.verifySignature(publicKey, "hash2".getBytes(Charset.forName("UTF-8")), signature));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVerifySignatureOfNonce() throws CryptoException {
|
||||
int nonce = 1234;
|
||||
byte[] nonceAsBytes = BigInteger.valueOf(nonce).toByteArray();
|
||||
byte[] signature = Sig.sign(keypair.getPrivate(), nonceAsBytes);
|
||||
assertTrue(service.verifySignatureOfNonce(publicKey, nonce, signature));
|
||||
assertFalse(service.verifySignatureOfNonce(publicKey, nonce, "sig2".getBytes(Charset.forName("UTF-8"))));
|
||||
assertFalse(service.verifySignatureOfNonce(publicKey, 0, new byte[0]));
|
||||
assertFalse(service.verifySignatureOfNonce(publicKey, 9999, signature));
|
||||
}
|
||||
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user