From 46bb00c0aaf51aab593f3b053b0f8547b0b3ece6 Mon Sep 17 00:00:00 2001 From: Christian Basler Date: Tue, 12 May 2015 19:04:00 +0200 Subject: [PATCH] I don't believe it - encryption works!!! --- .../java/ch/dissem/bitmessage/demo/Main.java | 98 ++++++++++--------- .../bitmessage/entity/BitmessageAddress.java | 23 ++++- .../bitmessage/entity/payload/CryptoBox.java | 83 +++++++++------- .../entity/payload/GenericPayload.java | 19 ++++ .../entity/payload/ObjectPayload.java | 2 +- .../bitmessage/entity/payload/V4Pubkey.java | 12 ++- .../entity/valueobject/PrivateKey.java | 8 ++ .../ch/dissem/bitmessage/factory/Factory.java | 1 - .../ch/dissem/bitmessage/utils/Bytes.java | 39 ++++++-- .../ch/dissem/bitmessage/utils/Security.java | 76 ++++++-------- .../entity/BitmessageAddressTest.java | 3 +- .../bitmessage/utils/EncryptionTest.java | 45 +++++++++ 12 files changed, 264 insertions(+), 145 deletions(-) create mode 100644 domain/src/test/java/ch/dissem/bitmessage/utils/EncryptionTest.java diff --git a/demo/src/main/java/ch/dissem/bitmessage/demo/Main.java b/demo/src/main/java/ch/dissem/bitmessage/demo/Main.java index 5c5085d..f34771f 100644 --- a/demo/src/main/java/ch/dissem/bitmessage/demo/Main.java +++ b/demo/src/main/java/ch/dissem/bitmessage/demo/Main.java @@ -16,11 +16,15 @@ package ch.dissem.bitmessage.demo; +import ch.dissem.bitmessage.BitmessageContext; import ch.dissem.bitmessage.entity.BitmessageAddress; import ch.dissem.bitmessage.entity.ObjectMessage; -import ch.dissem.bitmessage.entity.payload.ObjectType; -import ch.dissem.bitmessage.entity.payload.Pubkey; +import ch.dissem.bitmessage.entity.payload.*; +import ch.dissem.bitmessage.inventory.JdbcAddressRepository; import ch.dissem.bitmessage.inventory.JdbcInventory; +import ch.dissem.bitmessage.inventory.JdbcNodeRegistry; +import ch.dissem.bitmessage.networking.NetworkNode; +import ch.dissem.bitmessage.ports.NetworkHandler; import ch.dissem.bitmessage.utils.Base58; import ch.dissem.bitmessage.utils.Encode; import ch.dissem.bitmessage.utils.Security; @@ -31,6 +35,7 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.Arrays; import java.util.List; +import java.util.Scanner; /** * Created by chris on 06.04.15. @@ -39,43 +44,45 @@ public class Main { private final static Logger LOG = LoggerFactory.getLogger(Main.class); public static void main(String[] args) throws IOException { - final BitmessageAddress address = new BitmessageAddress("BM-2D9Vc5rFxxR5vTi53T9gkLfemViHRMVLQZ"); + final BitmessageAddress address = new BitmessageAddress("BM-87hJ99tPAXxtetvnje7Z491YSvbEtBJVc5e"); -// BitmessageContext ctx = new BitmessageContext.Builder() -// .addressRepo(new JdbcAddressRepository()) -// .inventory(new JdbcInventory()) -// .nodeRegistry(new JdbcNodeRegistry()) -// .networkHandler(new NetworkNode()) -// .port(48444) -// .streams(1) -// .build(); -// -// ctx.getNetworkHandler().start(new NetworkHandler.MessageListener() { -// @Override -// public void receive(ObjectPayload payload) { -//// LOG.info("message received: " + payload); -//// System.out.print('.'); -// if (payload instanceof V3Pubkey) { -// V3Pubkey pubkey = (V3Pubkey) payload; -// try { -// address.setPubkey(pubkey); -// System.out.println(address); -// } catch (Exception ignore) { -// System.err.println("Received pubkey we didn't request."); -// } -// } -// } -// }); -// -// Scanner scanner = new Scanner(System.in); + BitmessageContext ctx = new BitmessageContext.Builder() + .addressRepo(new JdbcAddressRepository()) + .inventory(new JdbcInventory()) + .nodeRegistry(new JdbcNodeRegistry()) + .networkHandler(new NetworkNode()) + .port(48444) + .streams(1) + .build(); + + ctx.getNetworkHandler().start(new NetworkHandler.MessageListener() { + @Override + public void receive(ObjectPayload payload) { +// LOG.info("message received: " + payload); +// System.out.print('.'); + if (payload instanceof V4Pubkey) { + V4Pubkey pubkey = (V4Pubkey) payload; + if (Arrays.equals(address.getTag(), pubkey.getTag())) { + System.out.println("Pubkey found!"); + try { + address.setPubkey(pubkey); + } catch (Exception ignore) { + System.err.println("Received pubkey we didn't request."); + } + } + } + } + }); + + Scanner scanner = new Scanner(System.in); // System.out.println("Press Enter to request pubkey for address " + address); // scanner.nextLine(); // ctx.send(1, address.getVersion(), new GetPubkey(address), 3000, 1000, 1000); -// -// System.out.println("Press Enter to exit"); -// scanner.nextLine(); -// LOG.info("Shutting down client"); -// ctx.getNetworkHandler().stop(); + + System.out.println("Press Enter to exit"); + scanner.nextLine(); + LOG.info("Shutting down client"); + ctx.getNetworkHandler().stop(); List objects = new JdbcInventory().getObjects(address.getStream(), address.getVersion(), ObjectType.PUBKEY); @@ -84,17 +91,18 @@ public class Main { for (ObjectMessage o : objects) { // if (!o.isSignatureValid()) System.out.println("Invalid signature."); // System.out.println(o.getPayload().getSignature().length); - Pubkey pubkey = (Pubkey) o.getPayload(); - if (Arrays.equals(address.getRipe(), pubkey.getRipe())) + V4Pubkey pubkey = (V4Pubkey) o.getPayload(); + if (Arrays.equals(address.getTag(), pubkey.getTag())) { System.out.println("Pubkey found!"); - try { - address.setPubkey(pubkey); - System.out.println(address); - } catch (Exception ignore) { - System.out.println("But setPubkey failed? " + address.getRipe().length + "/" + pubkey.getRipe().length); - System.out.println("Failed address: " + generateAddress(address.getStream(), address.getVersion(), pubkey.getRipe())); - if (Arrays.equals(address.getRipe(), pubkey.getRipe())) { - ignore.printStackTrace(); + try { + System.out.println("IV: "+o.getInventoryVector()); + address.setPubkey(pubkey); + } catch (Exception ignore) { + System.out.println("But setPubkey failed? " + address.getRipe().length + "/" + pubkey.getRipe().length); + System.out.println("Failed address: " + generateAddress(address.getStream(), address.getVersion(), pubkey.getRipe())); + if (Arrays.equals(address.getRipe(), pubkey.getRipe())) { + ignore.printStackTrace(); + } } } } 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 a366a56..46e4984 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/entity/BitmessageAddress.java +++ b/domain/src/main/java/ch/dissem/bitmessage/entity/BitmessageAddress.java @@ -64,7 +64,8 @@ public class BitmessageAddress { this.tag = Arrays.copyOfRange(checksum, 32, 64); this.privateEncryptionKey = Arrays.copyOfRange(checksum, 0, 32); // but for the address and its checksum they need to be stripped - os.write(Bytes.stripLeadingZeros(ripe)); + int offset = Bytes.numberOfLeadingZeros(ripe); + os.write(ripe, offset, ripe.length - offset); checksum = Security.doubleSha512(os.toByteArray(), ripe); os.write(checksum, 0, 4); this.address = "BM-" + Base58.encode(os.toByteArray()); @@ -73,10 +74,14 @@ public class BitmessageAddress { } } + private BitmessageAddress(Pubkey publicKey) { + this(publicKey.getVersion(), publicKey.getStream(), publicKey.getRipe()); + this.pubkey = publicKey; + } + public BitmessageAddress(PrivateKey privateKey) { - this(privateKey.getPubkey().getVersion(), privateKey.getPubkey().getStream(), privateKey.getPubkey().getRipe()); + this(privateKey.getPubkey()); this.privateKey = privateKey; - this.pubkey = privateKey.getPubkey(); } public BitmessageAddress(String address) { @@ -104,6 +109,18 @@ public class BitmessageAddress { } } + public static byte[] calculateTag(long version, long stream, byte[] ripe) { + try { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + Encode.varInt(version, out); + Encode.varInt(stream, out); + out.write(ripe); + return Arrays.copyOfRange(Security.doubleSha512(out.toByteArray()), 32, 64); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + public long getStream() { return stream; } diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/payload/CryptoBox.java b/domain/src/main/java/ch/dissem/bitmessage/entity/payload/CryptoBox.java index 68a7cda..bb606cb 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/entity/payload/CryptoBox.java +++ b/domain/src/main/java/ch/dissem/bitmessage/entity/payload/CryptoBox.java @@ -27,7 +27,6 @@ import org.bouncycastle.crypto.paddings.PKCS7Padding; import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher; import org.bouncycastle.crypto.params.KeyParameter; import org.bouncycastle.crypto.params.ParametersWithIV; -import org.bouncycastle.jce.interfaces.ECPublicKey; import org.bouncycastle.math.ec.ECPoint; import java.io.*; @@ -41,36 +40,37 @@ import java.util.Arrays; public class CryptoBox implements Streamable { private final byte[] initializationVector; private final int curveType; - private final byte[] xComponent; - private final byte[] yComponent; + private final ECPoint R; private final byte[] mac; private byte[] encrypted; public CryptoBox(Streamable data, byte[] encryptionKey) { + this(data, Security.keyToPoint(encryptionKey)); + } + + public CryptoBox(Streamable data, ECPoint K) { curveType = 0x02CA; // 1. The destination public key is called K. - ECPublicKey K = Security.getPublicKey(encryptionKey); // 2. Generate 16 random bytes using a secure random number generator. Call them IV. initializationVector = Security.randomBytes(16); // 3. Generate a new random EC key pair with private key called r and public key called R. - // TODO - BigInteger r = null; + byte[] r = Security.randomBytes(64); + R = Security.createPublicKey(r); // 4. Do an EC point multiply with public key K and private key r. This gives you public key P. - ECPoint P = K.getQ().multiply(r).normalize(); - xComponent = Bytes.stripLeadingZeros(P.getXCoord().getEncoded()); - yComponent = Bytes.stripLeadingZeros(P.getYCoord().getEncoded()); + ECPoint P = K.multiply(Security.keyToBigInt(r)).normalize(); + byte[] X = P.getXCoord().getEncoded(); // 5. Use the X component of public key P and calculate the SHA512 hash H. - byte[] H = Security.sha512(xComponent); + byte[] H = Security.sha512(X); // 6. The first 32 bytes of H are called key_e and the last 32 bytes are called key_m. byte[] key_e = Arrays.copyOfRange(H, 0, 32); - byte[] key_m = Arrays.copyOfRange(H, H.length - 32, 32); + byte[] key_m = Arrays.copyOfRange(H, 32, 64); // 7. Pad the input text to a multiple of 16 bytes, in accordance to PKCS7. // 8. Encrypt the data with AES-256-CBC, using IV as initialization vector, key_e as encryption key and the padded input text as payload. Call the output cipher text. - encrypted = null; // TODO + encrypted = crypt(true, Bytes.from(data), key_e); // 9. Calculate a 32 byte MAC with HMACSHA256, using key_m as salt and IV + R + cipher text as data. Call the output MAC. - mac = null; // TODO + mac = calculateMac(key_m); // The resulting data is: IV + R + cipher text + MAC } @@ -78,8 +78,7 @@ public class CryptoBox implements Streamable { private CryptoBox(Builder builder) { initializationVector = builder.initializationVector; curveType = builder.curveType; - xComponent = builder.xComponent; - yComponent = builder.yComponent; + R = Security.createPoint(builder.xComponent, builder.yComponent); encrypted = builder.encrypted; mac = builder.mac; } @@ -101,53 +100,65 @@ public class CryptoBox implements Streamable { */ public InputStream decrypt(byte[] privateKey) { // 1. The private key used to decrypt is called k. - BigInteger K = Security.keyToBigInt(privateKey); + BigInteger k = Security.keyToBigInt(privateKey); // 2. Do an EC point multiply with private key k and public key R. This gives you public key P. - ECPublicKey R = Security.getPublicKey(xComponent, yComponent); - ECPoint P = R.getQ().multiply(K).normalize(); + ECPoint P = R.multiply(k).normalize(); // 3. Use the X component of public key P and calculate the SHA512 hash H. - byte[] sha512key = Security.sha512(Bytes.expand(P.getXCoord().toBigInteger().toByteArray(), 32)); + byte[] H = Security.sha512(P.getXCoord().getEncoded()); // 4. The first 32 bytes of H are called key_e and the last 32 bytes are called key_m. - byte[] key_e = Arrays.copyOfRange(sha512key, 0, 32); - byte[] key_m = Arrays.copyOfRange(sha512key, 32, 64); + byte[] key_e = Arrays.copyOfRange(H, 0, 32); + byte[] key_m = Arrays.copyOfRange(H, 32, 64); // 5. Calculate MAC' with HMACSHA256, using key_m as salt and IV + R + cipher text as data. - ByteArrayOutputStream macData = new ByteArrayOutputStream(); - try { - writeWithoutMAC(macData); - } catch (IOException e) { - throw new RuntimeException(e); - } // 6. Compare MAC with MAC'. If not equal, decryption will fail. - if (!Arrays.equals(mac, Security.mac(key_m, macData.toByteArray()))) { + if (!Arrays.equals(mac, calculateMac(key_m))) { throw new RuntimeException("Invalid MAC while decrypting"); } // 7. Decrypt the cipher text with AES-256-CBC, using IV as initialization vector, key_e as decryption key // and the cipher text as payload. The output is the padded input text. + return new ByteArrayInputStream(crypt(false, encrypted, key_e)); + } + + private byte[] calculateMac(byte[] key_m) { + try { + ByteArrayOutputStream macData = new ByteArrayOutputStream(); + writeWithoutMAC(macData); + return Security.mac(key_m, macData.toByteArray()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private byte[] crypt(boolean encrypt, byte[] data, byte[] key_e) { BufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(new AESEngine()), new PKCS7Padding()); CipherParameters params = new ParametersWithIV(new KeyParameter(key_e), initializationVector); - cipher.init(false, params); + cipher.init(encrypt, params); - byte[] buffer = new byte[cipher.getOutputSize(encrypted.length)]; - int length = cipher.processBytes(encrypted, 0, encrypted.length, buffer, 0); + byte[] buffer = new byte[cipher.getOutputSize(data.length)]; + int length = cipher.processBytes(data, 0, data.length, buffer, 0); try { length += cipher.doFinal(buffer, length); } catch (InvalidCipherTextException e) { throw new IllegalArgumentException(e); } - return new ByteArrayInputStream(buffer, 0, length); + if (length < buffer.length) { + return Arrays.copyOfRange(buffer, 0, length); + } + return buffer; } private void writeWithoutMAC(OutputStream stream) throws IOException { stream.write(initializationVector); Encode.int16(curveType, stream); - Encode.int16(xComponent.length, stream); - stream.write(xComponent); - Encode.int16(yComponent.length, stream); - stream.write(yComponent); + byte[] x = R.getXCoord().getEncoded(); + byte[] y = R.getYCoord().getEncoded(); + Encode.int16(x.length, stream); + stream.write(x); + Encode.int16(y.length, stream); + stream.write(y); stream.write(encrypted); } diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/payload/GenericPayload.java b/domain/src/main/java/ch/dissem/bitmessage/entity/payload/GenericPayload.java index 30ba2be..64151d0 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/entity/payload/GenericPayload.java +++ b/domain/src/main/java/ch/dissem/bitmessage/entity/payload/GenericPayload.java @@ -21,6 +21,7 @@ import ch.dissem.bitmessage.utils.Decode; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.util.Arrays; /** * In cases we don't know what to do with an object, we just store its bytes and send it again - we don't really @@ -53,4 +54,22 @@ public class GenericPayload extends ObjectPayload { public void write(OutputStream stream) throws IOException { stream.write(data); } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + GenericPayload that = (GenericPayload) o; + + if (stream != that.stream) return false; + return Arrays.equals(data, that.data); + } + + @Override + public int hashCode() { + int result = (int) (stream ^ (stream >>> 32)); + result = 31 * result + Arrays.hashCode(data); + return result; + } } diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/payload/ObjectPayload.java b/domain/src/main/java/ch/dissem/bitmessage/entity/payload/ObjectPayload.java index 690e0ed..1667fb7 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/entity/payload/ObjectPayload.java +++ b/domain/src/main/java/ch/dissem/bitmessage/entity/payload/ObjectPayload.java @@ -34,7 +34,7 @@ public abstract class ObjectPayload implements Streamable { return false; } - public void writeBytesToSign(OutputStream out) throws IOException{ + public void writeBytesToSign(OutputStream out) throws IOException { // nothing to do } 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 5921089..f4712d2 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 @@ -16,7 +16,9 @@ package ch.dissem.bitmessage.entity.payload; +import ch.dissem.bitmessage.entity.BitmessageAddress; import ch.dissem.bitmessage.utils.Decode; +import ch.dissem.bitmessage.utils.Security; import java.io.IOException; import java.io.InputStream; @@ -40,11 +42,10 @@ public class V4Pubkey extends Pubkey { this.encrypted = encrypted; } - public V4Pubkey(byte[] tag, V3Pubkey decrypted) { + public V4Pubkey(V3Pubkey decrypted) { this.stream = decrypted.stream; - this.tag = tag; + this.tag = BitmessageAddress.calculateTag(4, decrypted.getStream(), decrypted.getRipe()); this.decrypted = decrypted; - this.encrypted = new CryptoBox(decrypted, null); } public static V4Pubkey read(InputStream in, long stream, int length) throws IOException { @@ -53,6 +54,11 @@ public class V4Pubkey extends Pubkey { CryptoBox.read(in, length - 32)); } + public void encrypt(byte[] privateKey) throws IOException { + if (getSignature() == null) throw new IllegalStateException("Pubkey must be signed before encryption."); + this.encrypted = new CryptoBox(decrypted, Security.createPublicKey(privateKey)); + } + public void decrypt(byte[] privateKey) throws IOException { decrypted = V3Pubkey.read(encrypted.decrypt(privateKey), stream); } diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/valueobject/PrivateKey.java b/domain/src/main/java/ch/dissem/bitmessage/entity/valueobject/PrivateKey.java index 6776750..af97047 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/entity/valueobject/PrivateKey.java +++ b/domain/src/main/java/ch/dissem/bitmessage/entity/valueobject/PrivateKey.java @@ -60,6 +60,14 @@ public class PrivateKey implements Streamable { } } + public byte[] getPrivateSigningKey() { + return privateSigningKey; + } + + public byte[] getPrivateEncryptionKey() { + return privateEncryptionKey; + } + public static PrivateKey read(InputStream is) throws IOException { int version = (int) Decode.varInt(is); long stream = Decode.varInt(is); diff --git a/domain/src/main/java/ch/dissem/bitmessage/factory/Factory.java b/domain/src/main/java/ch/dissem/bitmessage/factory/Factory.java index abee08d..44f4b1a 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/factory/Factory.java +++ b/domain/src/main/java/ch/dissem/bitmessage/factory/Factory.java @@ -82,7 +82,6 @@ public class Factory { .build(); case 4: return new V4Pubkey( - null, // FIXME: calculate tag new V3Pubkey.Builder() .stream(stream) .publicSigningKey(publicSigningKey) diff --git a/domain/src/main/java/ch/dissem/bitmessage/utils/Bytes.java b/domain/src/main/java/ch/dissem/bitmessage/utils/Bytes.java index f82b2d2..cedc1c1 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/utils/Bytes.java +++ b/domain/src/main/java/ch/dissem/bitmessage/utils/Bytes.java @@ -16,6 +16,12 @@ package ch.dissem.bitmessage.utils; +import ch.dissem.bitmessage.entity.Streamable; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Arrays; + /** * A helper class for working with byte arrays interpreted as unsigned big endian integers. */ @@ -41,6 +47,9 @@ public class Bytes { } } + /** + * Returns true if a < b. + */ public static boolean lt(byte[] a, byte[] b) { byte[] max = (a.length > b.length ? a : b); byte[] min = (max == a ? b : a); @@ -57,6 +66,9 @@ public class Bytes { return false; } + /** + * Returns true if a < b, where the first [size] bytes are checked. + */ public static boolean lt(byte[] a, byte[] b, int size) { for (int i = 0; i < size; i++) { if (a[i] != b[i]) { @@ -123,14 +135,25 @@ public class Bytes { throw new IllegalArgumentException("'" + c + "' is not a valid hex value"); } - public static byte[] stripLeadingZeros(byte[] bytes) { - for (int i = 0; i < bytes.length; i++) { - if (bytes[i] != 0) { - byte[] result = new byte[bytes.length - i]; - System.arraycopy(bytes, i, result, 0, bytes.length - i); - return result; - } + public static int numberOfLeadingZeros(byte[] bytes) { + int i; + for (i = 0; i < bytes.length; i++) { + if (bytes[i] != 0) return i; + } + return i; + } + + public static byte[] stripLeadingZeros(byte[] bytes) { + return Arrays.copyOfRange(bytes, numberOfLeadingZeros(bytes), bytes.length); + } + + public static byte[] from(Streamable data) { + try { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + data.write(out); + return out.toByteArray(); + } catch (IOException e) { + throw new RuntimeException(e); } - return new byte[0]; } } 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 e00674a..4d3defa 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/utils/Security.java +++ b/domain/src/main/java/ch/dissem/bitmessage/utils/Security.java @@ -23,10 +23,9 @@ import ch.dissem.bitmessage.ports.ProofOfWorkEngine; import org.bouncycastle.asn1.x9.X9ECParameters; import org.bouncycastle.crypto.ec.CustomNamedCurves; import org.bouncycastle.crypto.params.ECDomainParameters; -import org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util; import org.bouncycastle.jce.ECNamedCurveTable; -import org.bouncycastle.jce.interfaces.ECPublicKey; import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.math.ec.ECPoint; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -35,7 +34,6 @@ import javax.crypto.spec.SecretKeySpec; import java.io.IOException; import java.math.BigInteger; import java.security.*; -import java.security.spec.*; import java.util.Arrays; /** @@ -162,62 +160,46 @@ public class Security { public static Pubkey createPubkey(long version, long stream, byte[] privateSigningKey, byte[] privateEncryptionKey, long nonceTrialsPerByte, long extraBytes, Pubkey.Feature... features) { return Factory.createPubkey(version, stream, - createPublicKey(privateSigningKey), - createPublicKey(privateEncryptionKey), + createPublicKey(privateSigningKey).getEncoded(false), + createPublicKey(privateEncryptionKey).getEncoded(false), nonceTrialsPerByte, extraBytes, features); } - private static byte[] createPublicKey(byte[] privateKey) { - return EC_DOMAIN_PARAMETERS.getG().multiply(keyToBigInt(privateKey)).getEncoded(false); + public static ECPoint createPublicKey(byte[] privateKey) { + return EC_DOMAIN_PARAMETERS.getG().multiply(keyToBigInt(privateKey)).normalize(); } public static BigInteger keyToBigInt(byte[] privateKey) { return new BigInteger(1, privateKey); } - private static ECPoint keyToPoint(byte[] publicKey) { - BigInteger x = new BigInteger(Arrays.copyOfRange(publicKey, 1, 33)); - BigInteger y = new BigInteger(Arrays.copyOfRange(publicKey, 33, 65)); - return new ECPoint(x, y); + public static ECPoint keyToPoint(byte[] publicKey) { + BigInteger x = new BigInteger(1, Arrays.copyOfRange(publicKey, 1, 33)); + BigInteger y = new BigInteger(1, Arrays.copyOfRange(publicKey, 33, 65)); + return EC_DOMAIN_PARAMETERS.getCurve().createPoint(x, y); + } + + public static ECPoint createPoint(byte[] x, byte[] y) { + return EC_DOMAIN_PARAMETERS.getCurve().createPoint( + new BigInteger(1, x), + new BigInteger(1, y) + ); } 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); - } - } - - public static ECPublicKey getPublicKey(byte[] publicKey) { - if (publicKey[0] != 0x04) throw new IllegalArgumentException("Public key starting with 0x04 expected"); - return getPublicKey( - Arrays.copyOfRange(publicKey, 1, 33), - Arrays.copyOfRange(publicKey, 33, 65) - ); - } - - public static ECPublicKey getPublicKey(byte[] X, byte[] Y) { - try { - ECPoint w = new ECPoint(keyToBigInt(X), keyToBigInt(Y)); - EllipticCurve curve = EC5Util.convertCurve(EC_DOMAIN_PARAMETERS.getCurve(), EC_CURVE_PARAMETERS.getSeed()); - ECParameterSpec params = EC5Util.convertSpec(curve, ECNamedCurveTable.getParameterSpec(EC_CURVE_NAME)); - ECPublicKeySpec keySpec = new ECPublicKeySpec(w, params); - return (ECPublicKey) getKeyFactory().generatePublic(keySpec); - } catch (GeneralSecurityException e) { - throw new RuntimeException(e); - } - } - - private static KeyFactory getKeyFactory() throws NoSuchProviderException, NoSuchAlgorithmException { - return KeyFactory.getInstance("EC", "BC"); +// 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 } } 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 243e613..6683e17 100644 --- a/domain/src/test/java/ch/dissem/bitmessage/entity/BitmessageAddressTest.java +++ b/domain/src/test/java/ch/dissem/bitmessage/entity/BitmessageAddressTest.java @@ -25,6 +25,7 @@ import org.junit.Test; import java.io.IOException; +import static ch.dissem.bitmessage.entity.payload.Pubkey.Feature.DOES_ACK; import static org.junit.Assert.*; public class BitmessageAddressTest { @@ -53,7 +54,7 @@ public class BitmessageAddressTest { @Test public void testCreateAddress() { - BitmessageAddress address = new BitmessageAddress(new PrivateKey(0, 0, 0)); + BitmessageAddress address = new BitmessageAddress(new PrivateKey(1, 1000, 1000, DOES_ACK)); assertNotNull(address.getPubkey()); } diff --git a/domain/src/test/java/ch/dissem/bitmessage/utils/EncryptionTest.java b/domain/src/test/java/ch/dissem/bitmessage/utils/EncryptionTest.java new file mode 100644 index 0000000..8f3396b --- /dev/null +++ b/domain/src/test/java/ch/dissem/bitmessage/utils/EncryptionTest.java @@ -0,0 +1,45 @@ +/* + * Copyright 2015 Christian Basler + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ch.dissem.bitmessage.utils; + +import ch.dissem.bitmessage.entity.payload.CryptoBox; +import ch.dissem.bitmessage.entity.payload.GenericPayload; +import ch.dissem.bitmessage.entity.valueobject.PrivateKey; +import org.junit.Ignore; +import org.junit.Test; + +import java.io.IOException; +import java.security.KeyPair; + +import static org.junit.Assert.assertEquals; + +/** + * Created by chris on 10.05.15. + */ +public class EncryptionTest { + @Test + public void ensureDecryptedDataIsSameAsBeforeEncryption() throws IOException { + GenericPayload before = new GenericPayload(1, Security.randomBytes(100)); + + PrivateKey privateKey = new PrivateKey(1, 1000, 1000); + CryptoBox cryptoBox = new CryptoBox(before, privateKey.getPubkey().getEncryptionKey()); + + GenericPayload after = GenericPayload.read(cryptoBox.decrypt(privateKey.getPrivateEncryptionKey()), 1, 100); + + assertEquals(before, after); + } +}