From ed14e95d704b7308004cbba5d31053bdabd7ce0e Mon Sep 17 00:00:00 2001 From: Christian Basler Date: Fri, 15 May 2015 12:36:57 +0200 Subject: [PATCH] Signature check works --- .../bitmessage/entity/BitmessageAddress.java | 20 ++++----- .../dissem/bitmessage/entity/Encrypted.java | 2 + .../bitmessage/entity/ObjectMessage.java | 21 ++++++++- .../bitmessage/entity/payload/V4Pubkey.java | 11 +++++ .../ch/dissem/bitmessage/utils/Security.java | 43 ++++++++++++------- .../entity/BitmessageAddressTest.java | 8 ++-- 6 files changed, 73 insertions(+), 32 deletions(-) diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/BitmessageAddress.java b/domain/src/main/java/ch/dissem/bitmessage/entity/BitmessageAddress.java index 46e4984..5e3fd8a 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/entity/BitmessageAddress.java +++ b/domain/src/main/java/ch/dissem/bitmessage/entity/BitmessageAddress.java @@ -41,7 +41,7 @@ public class BitmessageAddress { /** * Used for V4 address encryption. It's easier to just create it regardless of address version. */ - private final byte[] privateEncryptionKey; + private final byte[] pubkeyDecryptionKey; private String address; @@ -62,7 +62,7 @@ public class BitmessageAddress { // for the tag, the checksum has to be created with 0x00 padding byte[] checksum = Security.doubleSha512(os.toByteArray(), ripe); this.tag = Arrays.copyOfRange(checksum, 32, 64); - this.privateEncryptionKey = Arrays.copyOfRange(checksum, 0, 32); + this.pubkeyDecryptionKey = Arrays.copyOfRange(checksum, 0, 32); // but for the address and its checksum they need to be stripped int offset = Bytes.numberOfLeadingZeros(ripe); os.write(ripe, offset, ripe.length - offset); @@ -103,7 +103,7 @@ public class BitmessageAddress { } checksum = Security.doubleSha512(Arrays.copyOfRange(bytes, 0, counter.length()), ripe); this.tag = Arrays.copyOfRange(checksum, 32, 64); - this.privateEncryptionKey = Arrays.copyOfRange(checksum, 0, 32); + this.pubkeyDecryptionKey = Arrays.copyOfRange(checksum, 0, 32); } catch (IOException e) { throw new RuntimeException(e); } @@ -135,20 +135,18 @@ public class BitmessageAddress { public void setPubkey(Pubkey pubkey) { if (pubkey instanceof V4Pubkey) { - try { - V4Pubkey v4 = (V4Pubkey) pubkey; - if (!Arrays.equals(tag, v4.getTag())) - throw new IllegalArgumentException("Pubkey has incompatible tag"); - v4.decrypt(privateEncryptionKey); - } catch (IOException e) { - throw new RuntimeException(e); - } + if (!Arrays.equals(tag, ((V4Pubkey) pubkey).getTag())) + throw new IllegalArgumentException("Pubkey has incompatible tag"); } if (!Arrays.equals(ripe, pubkey.getRipe())) throw new IllegalArgumentException("Pubkey has incompatible ripe"); this.pubkey = pubkey; } + public byte[] getPubkeyDecryptionKey() { + return pubkeyDecryptionKey; + } + public PrivateKey getPrivateKey() { return privateKey; } diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/Encrypted.java b/domain/src/main/java/ch/dissem/bitmessage/entity/Encrypted.java index b0afcb6..a861bce 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/entity/Encrypted.java +++ b/domain/src/main/java/ch/dissem/bitmessage/entity/Encrypted.java @@ -25,4 +25,6 @@ public interface Encrypted { void encrypt(byte[] publicKey) throws IOException; void decrypt(byte[] privateKey) throws IOException; + + boolean isDecrypted(); } diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/ObjectMessage.java b/domain/src/main/java/ch/dissem/bitmessage/entity/ObjectMessage.java index 7259bad..45f43c9 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/entity/ObjectMessage.java +++ b/domain/src/main/java/ch/dissem/bitmessage/entity/ObjectMessage.java @@ -90,6 +90,10 @@ public class ObjectMessage implements MessagePayload { return new InventoryVector(Bytes.truncate(Security.doubleSha512(nonce, getPayloadBytesWithoutNonce()), 32)); } + private boolean isEncrypted() { + return payload instanceof Encrypted && !((Encrypted) payload).isDecrypted(); + } + public boolean isSigned() { return payload.isSigned(); } @@ -103,10 +107,23 @@ public class ObjectMessage implements MessagePayload { public void sign(PrivateKey key) { // TODO +// Security. } - public boolean isSignatureValid() throws IOException { - Pubkey pubkey = null; // TODO + public void decrypt(PrivateKey key) throws IOException { + if (payload instanceof Encrypted){ + ((Encrypted) payload).decrypt(key.getPrivateEncryptionKey()); + } + } + + public void decrypt(byte[] privateEncryptionKey) throws IOException { + if (payload instanceof Encrypted){ + ((Encrypted) payload).decrypt(privateEncryptionKey); + } + } + + public boolean isSignatureValid(Pubkey pubkey) throws IOException { + if (isEncrypted()) throw new IllegalStateException("Payload must be decrypted first"); return Security.isSignatureValid(getBytesToSign(), payload.getSignature(), pubkey); } diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/payload/V4Pubkey.java b/domain/src/main/java/ch/dissem/bitmessage/entity/payload/V4Pubkey.java index 2a173d0..b5d4c8a 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/entity/payload/V4Pubkey.java +++ b/domain/src/main/java/ch/dissem/bitmessage/entity/payload/V4Pubkey.java @@ -65,12 +65,23 @@ public class V4Pubkey extends Pubkey implements Encrypted { decrypted = V3Pubkey.read(encrypted.decrypt(privateKey), stream); } + @Override + public boolean isDecrypted() { + return decrypted != null; + } + @Override public void write(OutputStream stream) throws IOException { stream.write(tag); encrypted.write(stream); } + @Override + public void writeBytesToSign(OutputStream out) throws IOException { + out.write(tag); + decrypted.writeBytesToSign(out); + } + @Override public long getVersion() { return 4; diff --git a/domain/src/main/java/ch/dissem/bitmessage/utils/Security.java b/domain/src/main/java/ch/dissem/bitmessage/utils/Security.java index 376b99e..819387f 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/utils/Security.java +++ b/domain/src/main/java/ch/dissem/bitmessage/utils/Security.java @@ -22,8 +22,14 @@ import ch.dissem.bitmessage.factory.Factory; import ch.dissem.bitmessage.ports.ProofOfWorkEngine; import org.bouncycastle.asn1.x9.X9ECParameters; import org.bouncycastle.crypto.ec.CustomNamedCurves; +import org.bouncycastle.jce.ECNamedCurveTable; +import org.bouncycastle.jce.ECPointUtil; import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.jce.spec.ECNamedCurveSpec; +import org.bouncycastle.jce.spec.ECParameterSpec; +import org.bouncycastle.jce.spec.ECPublicKeySpec; import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.util.encoders.Hex; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -31,9 +37,8 @@ import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import java.io.IOException; import java.math.BigInteger; -import java.security.GeneralSecurityException; -import java.security.MessageDigest; -import java.security.SecureRandom; +import java.security.*; +import java.security.spec.KeySpec; import java.util.Arrays; /** @@ -181,18 +186,24 @@ public class Security { public static boolean isSignatureValid(byte[] bytesToSign, byte[] signature, Pubkey pubkey) { ECPoint W = keyToPoint(pubkey.getSigningKey()); -// try { -// ECParameterSpec param = null; -// KeySpec keySpec = new ECPublicKeySpec(W, param); -// PublicKey publicKey = KeyFactory.getInstance("ECDSA", "BC").generatePublic(keySpec); -// -// Signature sig = Signature.getInstance("ECDSA", "BC"); -// sig.initVerify(publicKey); -// sig.update(bytesToSign); -// return sig.verify(signature); -// } catch (Exception e) { -// throw new RuntimeException(e); -// } - return false; // TODO + try { + ECParameterSpec spec = new ECParameterSpec( + EC_CURVE_PARAMETERS.getCurve(), + EC_CURVE_PARAMETERS.getG(), + EC_CURVE_PARAMETERS.getN(), + EC_CURVE_PARAMETERS.getH(), + EC_CURVE_PARAMETERS.getSeed() + ); + + KeySpec keySpec = new ECPublicKeySpec(W, spec); + PublicKey publicKey = KeyFactory.getInstance("ECDSA", "BC").generatePublic(keySpec); + + Signature sig = Signature.getInstance("ECDSA", "BC"); + sig.initVerify(publicKey); + sig.update(bytesToSign); + return sig.verify(signature); + } catch (Exception e) { + throw new RuntimeException(e); + } } } diff --git a/domain/src/test/java/ch/dissem/bitmessage/entity/BitmessageAddressTest.java b/domain/src/test/java/ch/dissem/bitmessage/entity/BitmessageAddressTest.java index 6683e17..bdec638 100644 --- a/domain/src/test/java/ch/dissem/bitmessage/entity/BitmessageAddressTest.java +++ b/domain/src/test/java/ch/dissem/bitmessage/entity/BitmessageAddressTest.java @@ -73,6 +73,7 @@ public class BitmessageAddressTest { ObjectMessage object = TestUtils.loadObjectMessage(3, "V3Pubkey.payload"); Pubkey pubkey = (Pubkey) object.getPayload(); + assertTrue(object.isSignatureValid(pubkey)); address.setPubkey(pubkey); assertArrayEquals(Bytes.fromHex("007402be6e76c3cb87caa946d0c003a3d4d8e1d5"), pubkey.getRipe()); @@ -80,10 +81,11 @@ public class BitmessageAddressTest { @Test public void testV4PubkeyImport() throws IOException { - // TODO - ObjectMessage object = TestUtils.loadObjectMessage(4, "V4Pubkey.payload"); - V4Pubkey pubkey = (V4Pubkey) object.getPayload(); BitmessageAddress address = new BitmessageAddress("BM-2cXxfcSetKnbHJX2Y85rSkaVpsdNUZ5q9h"); + ObjectMessage object = TestUtils.loadObjectMessage(4, "V4Pubkey.payload"); + object.decrypt(address.getPubkeyDecryptionKey()); + V4Pubkey pubkey = (V4Pubkey) object.getPayload(); + assertTrue(object.isSignatureValid(pubkey)); address.setPubkey(pubkey); }