Some extensions for server POW
This commit is contained in:
		| @@ -0,0 +1,139 @@ | ||||
| /* | ||||
|  * 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.extensions; | ||||
|  | ||||
| import ch.dissem.bitmessage.entity.BitmessageAddress; | ||||
| import ch.dissem.bitmessage.entity.CustomMessage; | ||||
| import ch.dissem.bitmessage.entity.Streamable; | ||||
| import ch.dissem.bitmessage.entity.payload.CryptoBox; | ||||
| import ch.dissem.bitmessage.entity.payload.Pubkey; | ||||
| import ch.dissem.bitmessage.exception.DecryptionFailedException; | ||||
| import ch.dissem.bitmessage.factory.Factory; | ||||
| import ch.dissem.bitmessage.utils.Encode; | ||||
|  | ||||
| import java.io.*; | ||||
|  | ||||
| import static ch.dissem.bitmessage.utils.Decode.*; | ||||
| import static ch.dissem.bitmessage.utils.Singleton.security; | ||||
|  | ||||
| /** | ||||
|  * A {@link CustomMessage} implementation that contains signed and encrypted data. | ||||
|  * | ||||
|  * @author Christian Basler | ||||
|  */ | ||||
| public class CryptoCustomMessage<T extends Streamable> extends CustomMessage { | ||||
|     private final Reader<T> dataReader; | ||||
|     private CryptoBox container; | ||||
|     private BitmessageAddress sender; | ||||
|     private T data; | ||||
|  | ||||
|     public CryptoCustomMessage(T data) throws IOException { | ||||
|         this.data = data; | ||||
|         this.dataReader = null; | ||||
|     } | ||||
|  | ||||
|     private CryptoCustomMessage(CryptoBox container, Reader<T> dataReader) { | ||||
|         this.container = container; | ||||
|         this.dataReader = dataReader; | ||||
|     } | ||||
|  | ||||
|     public static <T extends Streamable> CryptoCustomMessage<T> read(byte[] data, Reader<T> dataReader) throws IOException { | ||||
|         CryptoBox cryptoBox = CryptoBox.read(new ByteArrayInputStream(data), data.length); | ||||
|         return new CryptoCustomMessage<>(cryptoBox, dataReader); | ||||
|     } | ||||
|  | ||||
|     public BitmessageAddress getSender() { | ||||
|         return sender; | ||||
|     } | ||||
|  | ||||
|     public void signAndEncrypt(BitmessageAddress identity, byte[] publicKey) throws IOException { | ||||
|         ByteArrayOutputStream out = new ByteArrayOutputStream(); | ||||
|  | ||||
|         Encode.varInt(identity.getVersion(), out); | ||||
|         Encode.varInt(identity.getStream(), out); | ||||
|         Encode.int32(identity.getPubkey().getBehaviorBitfield(), out); | ||||
|         out.write(identity.getPubkey().getSigningKey(), 1, 64); | ||||
|         out.write(identity.getPubkey().getEncryptionKey(), 1, 64); | ||||
|         if (identity.getVersion() >= 3) { | ||||
|             Encode.varInt(identity.getPubkey().getNonceTrialsPerByte(), out); | ||||
|             Encode.varInt(identity.getPubkey().getExtraBytes(), out); | ||||
|         } | ||||
|  | ||||
|         data.write(out); | ||||
|         Encode.varBytes(security().getSignature(out.toByteArray(), identity.getPrivateKey()), out); | ||||
|         container = new CryptoBox(out.toByteArray(), publicKey); | ||||
|     } | ||||
|  | ||||
|     public T decrypt(byte[] privateKey) throws IOException, DecryptionFailedException { | ||||
|         SignatureCheckingInputStream in = new SignatureCheckingInputStream(container.decrypt(privateKey)); | ||||
|  | ||||
|         long addressVersion = varInt(in); | ||||
|         long stream = varInt(in); | ||||
|         int behaviorBitfield = int32(in); | ||||
|         byte[] publicSigningKey = bytes(in, 64); | ||||
|         byte[] publicEncryptionKey = bytes(in, 64); | ||||
|         long nonceTrialsPerByte = addressVersion >= 3 ? varInt(in) : 0; | ||||
|         long extraBytes = addressVersion >= 3 ? varInt(in) : 0; | ||||
|  | ||||
|         sender = new BitmessageAddress(Factory.createPubkey( | ||||
|                 addressVersion, | ||||
|                 stream, | ||||
|                 publicSigningKey, | ||||
|                 publicEncryptionKey, | ||||
|                 nonceTrialsPerByte, | ||||
|                 extraBytes, | ||||
|                 behaviorBitfield | ||||
|         )); | ||||
|  | ||||
|         data = dataReader.read(sender, in); | ||||
|  | ||||
|         in.checkSignature(sender.getPubkey()); | ||||
|  | ||||
|         return data; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void write(OutputStream out) throws IOException { | ||||
|         container.write(out); | ||||
|     } | ||||
|  | ||||
|     public interface Reader<T> { | ||||
|         T read(BitmessageAddress sender, InputStream in) throws IOException; | ||||
|     } | ||||
|  | ||||
|     private class SignatureCheckingInputStream extends InputStream { | ||||
|         private final ByteArrayOutputStream out = new ByteArrayOutputStream(); | ||||
|         private final InputStream wrapped; | ||||
|  | ||||
|         private SignatureCheckingInputStream(InputStream wrapped) { | ||||
|             this.wrapped = wrapped; | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         public int read() throws IOException { | ||||
|             int read = wrapped.read(); | ||||
|             if (read >= 0) out.write(read); | ||||
|             return read; | ||||
|         } | ||||
|  | ||||
|         public void checkSignature(Pubkey pubkey) throws IOException, RuntimeException { | ||||
|             if (!security().isSignatureValid(out.toByteArray(), varBytes(wrapped), pubkey)) { | ||||
|                 throw new RuntimeException("Signature check failed"); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,86 @@ | ||||
| /* | ||||
|  * 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.extensions.pow; | ||||
|  | ||||
| import ch.dissem.bitmessage.entity.BitmessageAddress; | ||||
| import ch.dissem.bitmessage.entity.Streamable; | ||||
| import ch.dissem.bitmessage.utils.Encode; | ||||
|  | ||||
| import java.io.IOException; | ||||
| import java.io.InputStream; | ||||
| import java.io.OutputStream; | ||||
|  | ||||
| import static ch.dissem.bitmessage.utils.Decode.*; | ||||
|  | ||||
| /** | ||||
|  * @author Christian Basler | ||||
|  */ | ||||
| public class ProofOfWorkRequest implements Streamable { | ||||
|     private final BitmessageAddress sender; | ||||
|     private final byte[] initialHash; | ||||
|     private final Request request; | ||||
|     private final byte[] data; | ||||
|  | ||||
|     private ProofOfWorkRequest(BitmessageAddress sender, byte[] initialHash, Request request, byte[] data) { | ||||
|         this.sender = sender; | ||||
|         this.initialHash = initialHash; | ||||
|         this.request = request; | ||||
|         this.data = data; | ||||
|     } | ||||
|  | ||||
|     public static ProofOfWorkRequest read(BitmessageAddress client, InputStream in) throws IOException { | ||||
|         return new ProofOfWorkRequest( | ||||
|                 client, | ||||
|                 bytes(in, 64), | ||||
|                 Request.valueOf(varString(in)), | ||||
|                 varBytes(in) | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public BitmessageAddress getSender() { | ||||
|         return sender; | ||||
|     } | ||||
|  | ||||
|     public byte[] getInitialHash() { | ||||
|         return initialHash; | ||||
|     } | ||||
|  | ||||
|     public Request getRequest() { | ||||
|         return request; | ||||
|     } | ||||
|  | ||||
|     public byte[] getData() { | ||||
|         return data; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void write(OutputStream out) throws IOException { | ||||
|         out.write(initialHash); | ||||
|         Encode.varString(request.name(), out); | ||||
|         Encode.varBytes(data, out); | ||||
|     } | ||||
|  | ||||
|     public enum Request { | ||||
|         CALCULATE, | ||||
|         QUERY, | ||||
|         ERROR, | ||||
|         OK, | ||||
|         QUEUED, | ||||
|         CALCULATING, | ||||
|         COMPLETE | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,58 @@ | ||||
| /* | ||||
|  * 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.extensions; | ||||
|  | ||||
| import ch.dissem.bitmessage.entity.BitmessageAddress; | ||||
| import ch.dissem.bitmessage.entity.payload.GenericPayload; | ||||
| import ch.dissem.bitmessage.entity.valueobject.PrivateKey; | ||||
| import ch.dissem.bitmessage.utils.TestBase; | ||||
| import ch.dissem.bitmessage.utils.TestUtils; | ||||
| import org.junit.Test; | ||||
|  | ||||
| import java.io.ByteArrayInputStream; | ||||
| import java.io.ByteArrayOutputStream; | ||||
| import java.io.IOException; | ||||
| import java.io.InputStream; | ||||
|  | ||||
| import static ch.dissem.bitmessage.utils.Singleton.security; | ||||
| import static org.junit.Assert.assertEquals; | ||||
|  | ||||
| public class CryptoCustomMessageTest extends TestBase { | ||||
|     @Test | ||||
|     public void testEncryptThenDecrypt() throws Exception { | ||||
|         PrivateKey privateKey = PrivateKey.read(TestUtils.getResource("BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8.privkey")); | ||||
|         BitmessageAddress sendingIdentity = new BitmessageAddress(privateKey); | ||||
|  | ||||
|         GenericPayload payloadBefore = new GenericPayload(0, 1, security().randomBytes(100)); | ||||
|         CryptoCustomMessage<GenericPayload> messageBefore = new CryptoCustomMessage<>(payloadBefore); | ||||
|         messageBefore.signAndEncrypt(sendingIdentity, security().createPublicKey(sendingIdentity.getPublicDecryptionKey())); | ||||
|  | ||||
|         ByteArrayOutputStream out = new ByteArrayOutputStream(); | ||||
|         messageBefore.write(out); | ||||
|         ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); | ||||
|  | ||||
|         CryptoCustomMessage<GenericPayload> messageAfter = CryptoCustomMessage.read(out.toByteArray(), new CryptoCustomMessage.Reader<GenericPayload>() { | ||||
|             @Override | ||||
|             public GenericPayload read(BitmessageAddress ignore, InputStream in) throws IOException { | ||||
|                 return GenericPayload.read(0, in, 1, 100); | ||||
|             } | ||||
|         }); | ||||
|         GenericPayload payloadAfter = messageAfter.decrypt(sendingIdentity.getPublicDecryptionKey()); | ||||
|  | ||||
|         assertEquals(payloadBefore, payloadAfter); | ||||
|     } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user