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 45f43c9..88c35bc 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/entity/ObjectMessage.java +++ b/domain/src/main/java/ch/dissem/bitmessage/entity/ObjectMessage.java @@ -17,6 +17,7 @@ package ch.dissem.bitmessage.entity; import ch.dissem.bitmessage.entity.payload.ObjectPayload; +import ch.dissem.bitmessage.entity.payload.ObjectType; import ch.dissem.bitmessage.entity.payload.Pubkey; import ch.dissem.bitmessage.entity.valueobject.InventoryVector; import ch.dissem.bitmessage.entity.valueobject.PrivateKey; @@ -98,30 +99,47 @@ public class ObjectMessage implements MessagePayload { return payload.isSigned(); } - private byte[] getBytesToSign() throws IOException { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - writeHeaderWithoutNonce(out); - payload.writeBytesToSign(out); - return out.toByteArray(); + private byte[] getBytesToSign() { + try { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + writeHeaderWithoutNonce(out); + payload.writeBytesToSign(out); + return out.toByteArray(); + } catch (IOException e) { + throw new RuntimeException(e); + } } public void sign(PrivateKey key) { - // TODO -// Security. + if (payload.isSigned()) { + payload.setSignature(Security.getSignature(getBytesToSign(), key)); + } } public void decrypt(PrivateKey key) throws IOException { - if (payload instanceof Encrypted){ + if (payload instanceof Encrypted) { ((Encrypted) payload).decrypt(key.getPrivateEncryptionKey()); } } public void decrypt(byte[] privateEncryptionKey) throws IOException { - if (payload instanceof Encrypted){ + if (payload instanceof Encrypted) { ((Encrypted) payload).decrypt(privateEncryptionKey); } } + public void encrypt(byte[] publicEncryptionKey) throws IOException{ + if (payload instanceof Encrypted){ + ((Encrypted) payload).encrypt(publicEncryptionKey); + } + } + + public void encrypt(Pubkey publicKey) throws IOException{ + if (payload instanceof Encrypted){ + ((Encrypted) payload).encrypt(publicKey.getEncryptionKey()); + } + } + public boolean isSignatureValid(Pubkey pubkey) throws IOException { if (isEncrypted()) throw new IllegalStateException("Payload must be decrypted first"); return Security.isSignatureValid(getBytesToSign(), payload.getSignature(), pubkey); @@ -176,6 +194,11 @@ public class ObjectMessage implements MessagePayload { return this; } + public Builder objectType(ObjectType objectType) { + this.objectType = objectType.getNumber(); + return this; + } + public Builder version(long version) { this.version = version; return this; 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 b5d4c8a..cf4a230 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 @@ -123,4 +123,9 @@ public class V4Pubkey extends Pubkey implements Encrypted { public void setSignature(byte[] signature) { decrypted.setSignature(signature); } + + @Override + public boolean isSigned() { + return true; + } } 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 819387f..d32f899 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/utils/Security.java +++ b/domain/src/main/java/ch/dissem/bitmessage/utils/Security.java @@ -22,14 +22,11 @@ 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.ECPrivateKeySpec; 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; @@ -185,7 +182,6 @@ public class Security { } public static boolean isSignatureValid(byte[] bytesToSign, byte[] signature, Pubkey pubkey) { - ECPoint W = keyToPoint(pubkey.getSigningKey()); try { ECParameterSpec spec = new ECParameterSpec( EC_CURVE_PARAMETERS.getCurve(), @@ -195,7 +191,8 @@ public class Security { EC_CURVE_PARAMETERS.getSeed() ); - KeySpec keySpec = new ECPublicKeySpec(W, spec); + ECPoint Q = keyToPoint(pubkey.getSigningKey()); + KeySpec keySpec = new ECPublicKeySpec(Q, spec); PublicKey publicKey = KeyFactory.getInstance("ECDSA", "BC").generatePublic(keySpec); Signature sig = Signature.getInstance("ECDSA", "BC"); @@ -206,4 +203,27 @@ public class Security { throw new RuntimeException(e); } } + + public static byte[] getSignature(byte[] data, ch.dissem.bitmessage.entity.valueobject.PrivateKey privateKey) { + 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() + ); + + BigInteger d = keyToBigInt(privateKey.getPrivateSigningKey()); + KeySpec keySpec = new ECPrivateKeySpec(d, spec); + PrivateKey privKey = KeyFactory.getInstance("ECDSA", "BC").generatePrivate(keySpec); + + Signature sig = Signature.getInstance("ECDSA", "BC"); + sig.initSign(privKey); + sig.update(data); + return sig.sign(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } } diff --git a/domain/src/test/java/ch/dissem/bitmessage/utils/EncryptionTest.java b/domain/src/test/java/ch/dissem/bitmessage/EncryptionTest.java similarity index 95% rename from domain/src/test/java/ch/dissem/bitmessage/utils/EncryptionTest.java rename to domain/src/test/java/ch/dissem/bitmessage/EncryptionTest.java index 8f3396b..8f7745b 100644 --- a/domain/src/test/java/ch/dissem/bitmessage/utils/EncryptionTest.java +++ b/domain/src/test/java/ch/dissem/bitmessage/EncryptionTest.java @@ -14,11 +14,12 @@ * limitations under the License. */ -package ch.dissem.bitmessage.utils; +package ch.dissem.bitmessage; import ch.dissem.bitmessage.entity.payload.CryptoBox; import ch.dissem.bitmessage.entity.payload.GenericPayload; import ch.dissem.bitmessage.entity.valueobject.PrivateKey; +import ch.dissem.bitmessage.utils.Security; import org.junit.Ignore; import org.junit.Test; diff --git a/domain/src/test/java/ch/dissem/bitmessage/SignatureTest.java b/domain/src/test/java/ch/dissem/bitmessage/SignatureTest.java new file mode 100644 index 0000000..9bffef1 --- /dev/null +++ b/domain/src/test/java/ch/dissem/bitmessage/SignatureTest.java @@ -0,0 +1,54 @@ +/* + * 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; + +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.valueobject.PrivateKey; +import ch.dissem.bitmessage.utils.TestUtils; +import org.junit.Test; + +import java.io.IOException; + +import static org.junit.Assert.assertTrue; + +public class SignatureTest { + @Test + public void ensureValidationWorks() throws IOException { + ObjectMessage object = TestUtils.loadObjectMessage(3, "V3Pubkey.payload"); + Pubkey pubkey = (Pubkey) object.getPayload(); + assertTrue(object.isSignatureValid(pubkey)); + } + + @Test + public void ensureSigningWorks() throws IOException { + PrivateKey privateKey = new PrivateKey(1, 1000, 1000); + BitmessageAddress address = new BitmessageAddress(privateKey); + + ObjectMessage objectMessage = new ObjectMessage.Builder() + .objectType(ObjectType.PUBKEY) + .stream(1) + .version(1) + .payload(privateKey.getPubkey()) + .build(); + objectMessage.sign(privateKey); + + assertTrue(objectMessage.isSignatureValid(privateKey.getPubkey())); + } +}