Fix missing params in PrivateNotificationPayload constr. Use static fromProto methods. Removed readObject from SealedAndSigned and PrivateNotificationPayload. Add Sig.getSigPublicKeyFromBytes.

This commit is contained in:
Manfred Karrer 2017-05-12 18:46:42 +02:00
parent 98e702905d
commit b3d0d25d72
10 changed files with 119 additions and 134 deletions

View File

@ -18,10 +18,6 @@
package io.bisq.common.crypto;
public class CryptoException extends Exception {
private CryptoException() {
}
public CryptoException(String message) {
super(message);
}
@ -33,8 +29,4 @@ public class CryptoException extends Exception {
public CryptoException(Throwable cause) {
super(cause);
}
private CryptoException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}

View File

@ -0,0 +1,24 @@
/*
* 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.common.crypto;
public class KeyConversionException extends RuntimeException {
public KeyConversionException(Throwable cause) {
super(cause);
}
}

View File

@ -20,27 +20,20 @@ package io.bisq.common.crypto;
import com.google.protobuf.ByteString;
import io.bisq.common.network.NetworkPayload;
import io.bisq.generated.protobuffer.PB;
import lombok.EqualsAndHashCode;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
@Slf4j
@EqualsAndHashCode
public final class SealedAndSigned implements NetworkPayload {
// Payload
public final byte[] encryptedSecretKey;
public final byte[] encryptedPayloadWithHmac;
public final byte[] signature;
private final byte[] sigPublicKeyBytes;
// Domain
public transient PublicKey sigPublicKey;
public PublicKey sigPublicKey;
public SealedAndSigned(byte[] encryptedSecretKey, byte[] encryptedPayloadWithHmac, byte[] signature, PublicKey sigPublicKey) {
this.encryptedSecretKey = encryptedSecretKey;
@ -50,64 +43,18 @@ public final class SealedAndSigned implements NetworkPayload {
this.sigPublicKeyBytes = new X509EncodedKeySpec(this.sigPublicKey.getEncoded()).getEncoded();
}
///////////////////////////////////////////////////////////////////////////////////////////
// PROTO BUFFER
///////////////////////////////////////////////////////////////////////////////////////////
public SealedAndSigned(byte[] encryptedSecretKey, byte[] encryptedPayloadWithHmac, byte[] signature, byte[] sigPublicKeyBytes) {
this(encryptedSecretKey, encryptedPayloadWithHmac, signature, SealedAndSigned.init(sigPublicKeyBytes));
this(encryptedSecretKey, encryptedPayloadWithHmac, signature, Sig.getSigPublicKeyFromBytes(sigPublicKeyBytes));
}
private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
try {
in.defaultReadObject();
sigPublicKey = init(sigPublicKeyBytes);
} catch (Throwable t) {
log.warn("Exception at readObject: " + t.getMessage());
}
}
/**
* We have the bytes, now recreate the sigPublicKey. This happens when receiving this class over the wire,
* because the public key is transient.
*/
static PublicKey init(byte[] sigPublicKeyBytes) {
PublicKey publicKey = null;
try {
publicKey = KeyFactory.getInstance(Sig.KEY_ALGO, "BC")
.generatePublic(new X509EncodedKeySpec(sigPublicKeyBytes));
} catch (InvalidKeySpecException | NoSuchAlgorithmException | NoSuchProviderException e) {
log.error("Error creating sigPublicKey", e);
}
return publicKey;
}
public PB.SealedAndSigned toProtoMessage() {
return PB.SealedAndSigned.newBuilder().setEncryptedSecretKey(ByteString.copyFrom(encryptedSecretKey))
.setEncryptedPayloadWithHmac(ByteString.copyFrom(encryptedPayloadWithHmac))
.setSignature(ByteString.copyFrom(signature)).setSigPublicKeyBytes(ByteString.copyFrom(sigPublicKeyBytes))
.build();
}
@SuppressWarnings("SimplifiableIfStatement")
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof SealedAndSigned)) return false;
SealedAndSigned that = (SealedAndSigned) o;
if (!Arrays.equals(encryptedSecretKey, that.encryptedSecretKey)) return false;
if (!Arrays.equals(encryptedPayloadWithHmac, that.encryptedPayloadWithHmac)) return false;
if (!Arrays.equals(signature, that.signature)) return false;
return !(sigPublicKey != null ? !sigPublicKey.equals(that.sigPublicKey) : that.sigPublicKey != null);
}
@Override
public int hashCode() {
int result = encryptedSecretKey != null ? Arrays.hashCode(encryptedSecretKey) : 0;
result = 31 * result + (encryptedPayloadWithHmac != null ? Arrays.hashCode(encryptedPayloadWithHmac) : 0);
result = 31 * result + (signature != null ? Arrays.hashCode(signature) : 0);
result = 31 * result + (sigPublicKey != null ? sigPublicKey.hashCode() : 0);
return result;
}
}

View File

@ -19,10 +19,13 @@ package io.bisq.common.crypto;
import com.google.common.base.Charsets;
import org.bouncycastle.util.encoders.Base64;
import org.bouncycastle.util.encoders.Hex;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
/**
* StorageSignatureKeyPair/STORAGE_SIGN_KEY_ALGO: That is used for signing the data to be stored to the P2P network (by flooding).
@ -122,5 +125,19 @@ public class Sig {
public static boolean verify(PublicKey publicKey, String message, String signature) throws CryptoException {
return verify(publicKey, message.getBytes(Charsets.UTF_8), Base64.decode(signature));
}
/**
* @param sigPublicKeyBytes
* @return
*/
public static PublicKey getSigPublicKeyFromBytes(byte[] sigPublicKeyBytes) {
try {
return KeyFactory.getInstance(Sig.KEY_ALGO, "BC").generatePublic(new X509EncodedKeySpec(sigPublicKeyBytes));
} catch (InvalidKeySpecException | NoSuchAlgorithmException | NoSuchProviderException e) {
log.error("Error creating sigPublicKey from bytes. sigPublicKeyBytes as hex={}, error={}", Hex.toHexString(sigPublicKeyBytes), e);
e.printStackTrace();
throw new KeyConversionException(e);
}
}
}

View File

@ -785,7 +785,7 @@ message USPostalMoneyOrderAccountPayload {
message PrivateNotificationPayload {
string message = 1;
string signature_as_base64 = 2;
bytes public_key_bytes = 3;
bytes sig_public_key_bytes = 3;
}

View File

@ -80,7 +80,7 @@ public class PrivateNotificationManager {
PrivateNotificationMessage privateNotificationMessage = (PrivateNotificationMessage) wireEnvelope;
log.trace("Received privateNotificationMessage: " + privateNotificationMessage);
if (privateNotificationMessage.getSenderNodeAddress().equals(senderNodeAddress)) {
final PrivateNotificationPayload privateNotification = privateNotificationMessage.privateNotificationPayload;
final PrivateNotificationPayload privateNotification = privateNotificationMessage.getPrivateNotificationPayload();
if (verifySignature(privateNotification))
privateNotificationMessageProperty.set(privateNotification);
} else {
@ -128,13 +128,13 @@ public class PrivateNotificationManager {
}
private void signAndAddSignatureToPrivateNotificationMessage(PrivateNotificationPayload privateNotification) {
String privateNotificationMessageAsHex = Utils.HEX.encode(privateNotification.message.getBytes());
String privateNotificationMessageAsHex = Utils.HEX.encode(privateNotification.getMessage().getBytes());
String signatureAsBase64 = privateNotificationSigningKey.signMessage(privateNotificationMessageAsHex);
privateNotification.setSigAndPubKey(signatureAsBase64, keyRing.getSignatureKeyPair().getPublic());
}
private boolean verifySignature(PrivateNotificationPayload privateNotification) {
String privateNotificationMessageAsHex = Utils.HEX.encode(privateNotification.message.getBytes());
String privateNotificationMessageAsHex = Utils.HEX.encode(privateNotification.getMessage().getBytes());
try {
ECKey.fromPublicOnly(HEX.decode(pubKeyAsHex)).verifyMessage(privateNotificationMessageAsHex, privateNotification.getSignatureAsBase64());
return true;

View File

@ -5,16 +5,14 @@ import io.bisq.common.network.NetworkEnvelope;
import io.bisq.generated.protobuffer.PB;
import io.bisq.network.p2p.MailboxMessage;
import io.bisq.network.p2p.NodeAddress;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import lombok.Value;
import lombok.extern.slf4j.Slf4j;
@EqualsAndHashCode
@ToString
@Value
@Slf4j
public class PrivateNotificationMessage implements MailboxMessage {
private final NodeAddress myNodeAddress;
public final PrivateNotificationPayload privateNotificationPayload;
private final PrivateNotificationPayload privateNotificationPayload;
private final String uid;
private final int messageVersion = Version.getP2PMessageVersion();
@ -26,6 +24,27 @@ public class PrivateNotificationMessage implements MailboxMessage {
this.uid = uid;
}
///////////////////////////////////////////////////////////////////////////////////////////
// PROTO BUFFER
///////////////////////////////////////////////////////////////////////////////////////////
public static NetworkEnvelope fromProto(PB.PrivateNotificationMessage proto) {
return new PrivateNotificationMessage(PrivateNotificationPayload.fromProto(proto.getPrivateNotificationPayload()),
NodeAddress.fromProto(proto.getMyNodeAddress()),
proto.getUid());
}
@Override
public PB.NetworkEnvelope toProtoNetworkEnvelope() {
PB.NetworkEnvelope.Builder msgBuilder = NetworkEnvelope.getDefaultBuilder();
return msgBuilder.setPrivateNotificationMessage(msgBuilder.getPrivateNotificationMessageBuilder()
.setMessageVersion(messageVersion)
.setUid(uid)
.setMyNodeAddress(myNodeAddress.toProtoMessage())
.setPrivateNotificationPayload(privateNotificationPayload.toProtoMessage())).build();
}
@Override
public NodeAddress getSenderNodeAddress() {
return myNodeAddress;
@ -41,13 +60,7 @@ public class PrivateNotificationMessage implements MailboxMessage {
return messageVersion;
}
@Override
public PB.NetworkEnvelope toProtoNetworkEnvelope() {
PB.NetworkEnvelope.Builder msgBuilder = NetworkEnvelope.getDefaultBuilder();
return msgBuilder.setPrivateNotificationMessage(msgBuilder.getPrivateNotificationMessageBuilder()
.setMessageVersion(messageVersion)
.setUid(uid)
.setMyNodeAddress(myNodeAddress.toProtoMessage())
.setPrivateNotificationPayload(privateNotificationPayload.toProtoMessage())).build();
public PrivateNotificationPayload getPrivateNotificationPayload() {
return privateNotificationPayload;
}
}

View File

@ -22,72 +22,69 @@ import io.bisq.common.crypto.Sig;
import io.bisq.common.network.NetworkPayload;
import io.bisq.generated.protobuffer.PB;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.util.encoders.Hex;
import java.io.IOException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import javax.annotation.Nullable;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import static com.google.common.base.Preconditions.checkNotNull;
@EqualsAndHashCode
@Slf4j
@Getter
public final class PrivateNotificationPayload implements NetworkPayload {
// Payload
public final String message;
private final String message;
@Nullable
private String signatureAsBase64;
private byte[] publicKeyBytes;
// Domain
private transient PublicKey publicKey;
@Nullable
private byte[] sigPublicKeyBytes;
@Nullable
private PublicKey publicKey;
public PrivateNotificationPayload(String message) {
this.message = message;
}
public PrivateNotificationPayload(String message, String signatureAsBase64, byte[] publicKeyBytes) {
///////////////////////////////////////////////////////////////////////////////////////////
// PROTO BUFFER
///////////////////////////////////////////////////////////////////////////////////////////
private PrivateNotificationPayload(String message, String signatureAsBase64, byte[] sigPublicKeyBytes) {
this(message);
this.signatureAsBase64 = signatureAsBase64;
this.publicKeyBytes = publicKeyBytes;
init();
this.sigPublicKeyBytes = sigPublicKeyBytes;
publicKey = Sig.getSigPublicKeyFromBytes(sigPublicKeyBytes);
}
private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
try {
in.defaultReadObject();
init();
} catch (Throwable t) {
log.warn("Exception at readObject: " + t.getMessage());
}
}
private void init() {
try {
publicKey = KeyFactory.getInstance(Sig.KEY_ALGO, "BC").generatePublic(new X509EncodedKeySpec(publicKeyBytes));
} catch (InvalidKeySpecException | NoSuchAlgorithmException | NoSuchProviderException e) {
log.error("Could not create public key from bytes", e);
}
}
public void setSigAndPubKey(String signatureAsBase64, PublicKey storagePublicKey) {
this.signatureAsBase64 = signatureAsBase64;
this.publicKey = storagePublicKey;
this.publicKeyBytes = new X509EncodedKeySpec(this.publicKey.getEncoded()).getEncoded();
}
public String getSignatureAsBase64() {
return signatureAsBase64;
public static PrivateNotificationPayload fromProto(PB.PrivateNotificationPayload proto) {
return new PrivateNotificationPayload(proto.getMessage(),
proto.getSignatureAsBase64(),
proto.getSignatureAsBase64Bytes().toByteArray());
}
@Override
public PB.PrivateNotificationPayload toProtoMessage() {
checkNotNull(sigPublicKeyBytes, "sigPublicKeyBytes must nto be null");
return PB.PrivateNotificationPayload.newBuilder()
.setMessage(message)
.setSignatureAsBase64(signatureAsBase64)
.setPublicKeyBytes(ByteString.copyFrom(publicKeyBytes)).build();
.setSigPublicKeyBytes(ByteString.copyFrom(sigPublicKeyBytes)).build();
}
///////////////////////////////////////////////////////////////////////////////////////////
// API
///////////////////////////////////////////////////////////////////////////////////////////
public void setSigAndPubKey(String signatureAsBase64, PublicKey storagePublicKey) {
this.signatureAsBase64 = signatureAsBase64;
this.publicKey = storagePublicKey;
this.sigPublicKeyBytes = new X509EncodedKeySpec(this.publicKey.getEncoded()).getEncoded();
}
// Hex
@ -96,7 +93,7 @@ public final class PrivateNotificationPayload implements NetworkPayload {
return "PrivateNotification{" +
"message='" + message + '\'' +
", signatureAsBase64='" + signatureAsBase64 + '\'' +
", publicKeyBytes=" + Hex.toHexString(publicKeyBytes) +
", publicKeyBytes=" + Hex.toHexString(sigPublicKeyBytes) +
'}';
}
}

View File

@ -183,7 +183,7 @@ public class CoreNetworkProtoResolver implements NetworkProtoResolver {
result = getPayoutTxPublishedMessage(msg.getPayoutTxPublishedMessage());
break;
case PRIVATE_NOTIFICATION_MESSAGE:
result = getPrivateNotificationMessage(msg.getPrivateNotificationMessage());
result = PrivateNotificationMessage.fromProto(msg.getPrivateNotificationMessage());
break;
default:
log.warn("Unknown message case:{}:{}", msg.getMessageCase());
@ -200,11 +200,6 @@ public class CoreNetworkProtoResolver implements NetworkProtoResolver {
return new OfferAvailabilityRequest(msg.getOfferId(), PubKeyRing.fromProto(msg.getPubKeyRing()), msg.getTakersTradePrice());
}
private static NetworkEnvelope getPrivateNotificationMessage(PB.PrivateNotificationMessage privateNotificationMessage) {
return new PrivateNotificationMessage(getPrivateNotification(privateNotificationMessage.getPrivateNotificationPayload()),
NodeAddress.fromProto(privateNotificationMessage.getMyNodeAddress()),
privateNotificationMessage.getUid());
}
private static NetworkEnvelope getPayoutTxPublishedMessage(PB.PayoutTxPublishedMessage payoutTxPublishedMessage) {
return new PayoutTxPublishedMessage(payoutTxPublishedMessage.getTradeId(),

View File

@ -918,7 +918,7 @@ public class MainViewModel implements ViewModel {
private void displayPrivateNotification(PrivateNotificationPayload privateNotification) {
new Popup<>().headLine(Res.get("popup.privateNotification.headline"))
.attention(privateNotification.message)
.attention(privateNotification.getMessage())
.setHeadlineStyle("-fx-text-fill: -bs-error-red; -fx-font-weight: bold; -fx-font-size: 16;")
.onClose(privateNotificationManager::removePrivateNotification)
.useIUnderstandButton()