diff --git a/domain/src/main/java/ch/dissem/bitmessage/BitmessageContext.java b/domain/src/main/java/ch/dissem/bitmessage/BitmessageContext.java index a1aca88..4b1b0b6 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/BitmessageContext.java +++ b/domain/src/main/java/ch/dissem/bitmessage/BitmessageContext.java @@ -44,7 +44,7 @@ import static ch.dissem.bitmessage.utils.UnixTime.DAY; /** * Use this class if you want to create a Bitmessage client. - *

+ *

* You'll need the Builder to create a BitmessageContext, and set the following properties: *

* The default implementations in the different module builds can be used. - *

+ *

+ *

* The port defaults to 8444 (the default Bitmessage port) + *

*/ public class BitmessageContext { public static final int CURRENT_VERSION = 3; diff --git a/domain/src/main/java/ch/dissem/bitmessage/InternalContext.java b/domain/src/main/java/ch/dissem/bitmessage/InternalContext.java index 600ee4f..e7f56f5 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/InternalContext.java +++ b/domain/src/main/java/ch/dissem/bitmessage/InternalContext.java @@ -35,8 +35,9 @@ import static ch.dissem.bitmessage.utils.UnixTime.DAY; * The internal context should normally only be used for port implementations. If you need it in your client * implementation, you're either doing something wrong, something very weird, or the BitmessageContext should * get extended. - *

+ *

* On the other hand, if you need the BitmessageContext in a port implementation, the same thing might apply. + *

*/ public class InternalContext { private final static Logger LOG = LoggerFactory.getLogger(InternalContext.class); 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 923f101..eb348a2 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/entity/BitmessageAddress.java +++ b/domain/src/main/java/ch/dissem/bitmessage/entity/BitmessageAddress.java @@ -158,7 +158,7 @@ public class BitmessageAddress { } /** - * Returns the private key used to decrypt Pubkey objects (for v4 addresses) and broadcasts. + * @return the private key used to decrypt Pubkey objects (for v4 addresses) and broadcasts. */ public byte[] getPublicDecryptionKey() { return publicDecryptionKey; 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 8fe6ff8..5fc3990 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 @@ -47,11 +47,11 @@ public class CryptoBox implements Streamable { private final byte[] mac; private byte[] encrypted; - public CryptoBox(Streamable data, byte[] encryptionKey) { + public CryptoBox(Streamable data, byte[] encryptionKey) throws IOException { this(data, Security.keyToPoint(encryptionKey)); } - public CryptoBox(Streamable data, ECPoint K) { + public CryptoBox(Streamable data, ECPoint K) throws IOException { curveType = 0x02CA; // 1. The destination public key is called K. @@ -71,7 +71,7 @@ public class CryptoBox implements Streamable { 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 = crypt(true, Bytes.from(data), key_e); + encrypted = crypt(true, Encode.bytes(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 = calculateMac(key_m); @@ -99,6 +99,9 @@ public class CryptoBox implements Streamable { } /** + * @param privateKey a private key, typically should be 32 bytes long + * @return an InputStream yielding the decrypted data + * @throws DecryptionFailedException if the payload can't be decrypted using this private key * @see https://bitmessage.org/wiki/Encryption#Decryption */ public InputStream decrypt(byte[] privateKey) throws DecryptionFailedException { diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/payload/GetPubkey.java b/domain/src/main/java/ch/dissem/bitmessage/entity/payload/GetPubkey.java index e23ab11..e31dbe3 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/entity/payload/GetPubkey.java +++ b/domain/src/main/java/ch/dissem/bitmessage/entity/payload/GetPubkey.java @@ -50,7 +50,7 @@ public class GetPubkey extends ObjectPayload { } /** - * Returns an array of bytes that represent either the ripe, or the tag of an address, depending on the + * @return an array of bytes that represent either the ripe, or the tag of an address, depending on the * address version. */ public byte[] getRipeTag() { 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 39c28ff..ef42718 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 @@ -50,7 +50,7 @@ public abstract class ObjectPayload implements Streamable { } /** - * The ECDSA signature which, as of protocol v3, covers the object header starting with the time, + * @return the ECDSA signature which, as of protocol v3, covers the object header starting with the time, * appended with the data described in this table down to the extra_bytes. Therefore, this must * be checked and set in the {@link ObjectMessage} object. */ diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/valueobject/Label.java b/domain/src/main/java/ch/dissem/bitmessage/entity/valueobject/Label.java index b77220b..e1bd8f2 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/entity/valueobject/Label.java +++ b/domain/src/main/java/ch/dissem/bitmessage/entity/valueobject/Label.java @@ -31,12 +31,15 @@ public class Label { } /** - * RGBA representation for the color. + * @return RGBA representation for the color. */ public int getColor() { return color; } + /** + * @param color RGBA representation for the color. + */ public void setColor(int color) { this.color = color; } @@ -50,14 +53,14 @@ public class Label { return id; } - public Type getType() { - return type; - } - public void setId(Object id) { this.id = id; } + public Type getType() { + return type; + } + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/domain/src/main/java/ch/dissem/bitmessage/ports/AddressRepository.java b/domain/src/main/java/ch/dissem/bitmessage/ports/AddressRepository.java index 7cd6be8..2770997 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/ports/AddressRepository.java +++ b/domain/src/main/java/ch/dissem/bitmessage/ports/AddressRepository.java @@ -26,7 +26,7 @@ public interface AddressRepository { BitmessageAddress findIdentity(byte[] ripeOrTag); /** - * Returns all Bitmessage addresses that belong to this user, i.e. have a private key. + * @return all Bitmessage addresses that belong to this user, i.e. have a private key. */ List getIdentities(); @@ -35,7 +35,7 @@ public interface AddressRepository { List getSubscriptions(long broadcastVersion); /** - * Returns all Bitmessage addresses that have no private key. + * @return all Bitmessage addresses that have no private key. */ List getContacts(); diff --git a/domain/src/main/java/ch/dissem/bitmessage/utils/Base58.java b/domain/src/main/java/ch/dissem/bitmessage/utils/Base58.java index 26ed801..ec2476a 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/utils/Base58.java +++ b/domain/src/main/java/ch/dissem/bitmessage/utils/Base58.java @@ -29,9 +29,8 @@ import static java.util.Arrays.copyOfRange; * @author Christian Basler: I removed some dependencies to the BitcoinJ code so it can be used here more easily. */ public class Base58 { - private static char[] ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz".toCharArray(); - private static final int[] INDEXES = new int[128]; + private static char[] ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz".toCharArray(); static { for (int i = 0; i < INDEXES.length; i++) { @@ -44,6 +43,9 @@ public class Base58 { /** * Encodes the given bytes in base58. No checksum is appended. + * + * @param input to encode + * @return base58 encoded input */ public static String encode(byte[] input) { if (input.length == 0) { 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 411f062..31a3dcc 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/utils/Bytes.java +++ b/domain/src/main/java/ch/dissem/bitmessage/utils/Bytes.java @@ -38,6 +38,9 @@ public class Bytes { /** * Increases nonce by value, which is used as an unsigned byte value. + * + * @param nonce an unsigned number + * @param value to be added to nonce */ public static void inc(byte[] nonce, byte value) { int i = nonce.length - 1; @@ -54,7 +57,7 @@ public class Bytes { } /** - * Returns true if a < b. + * @return true if a < b. */ public static boolean lt(byte[] a, byte[] b) { byte[] max = (a.length > b.length ? a : b); @@ -73,7 +76,7 @@ public class Bytes { } /** - * Returns true if a < b, where the first [size] bytes are used as the numbers to check. + * @return true if a < b, where the first [size] bytes are used as the numbers to check. */ public static boolean lt(byte[] a, byte[] b, int size) { for (int i = 0; i < size; i++) { @@ -91,7 +94,7 @@ public class Bytes { } /** - * Returns a new byte array of length, left-padded with '0'. + * @return a new byte array of length, left-padded with '0'. */ public static byte[] expand(byte[] source, int size) { byte[] result = new byte[size]; @@ -100,7 +103,7 @@ public class Bytes { } /** - * Returns a new byte array containing the first size bytes of the given array. + * @return a new byte array containing the first size bytes of the given array. */ public static byte[] truncate(byte[] source, int size) { byte[] result = new byte[size]; @@ -109,7 +112,7 @@ public class Bytes { } /** - * Returns the byte array that hex represents. This is meant for test use and should be rewritten if used in + * @return the byte array that hex represents. This is meant for test use and should be rewritten if used in * production code. */ public static byte[] fromHex(String hex) { @@ -136,7 +139,7 @@ public class Bytes { } /** - * Returns the number of leading '0' of a byte array. + * @return the number of leading '0' of a byte array. */ public static int numberOfLeadingZeros(byte[] bytes) { int i; @@ -145,24 +148,4 @@ public class Bytes { } return i; } - - /** - * Returns a copy of bytes with leading zeroes stripped. - */ - public static byte[] stripLeadingZeros(byte[] bytes) { - return Arrays.copyOfRange(bytes, numberOfLeadingZeros(bytes), bytes.length); - } - - /** - * Returns the byte array of the serialized data. - */ - public static byte[] from(Streamable data) { - try { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - data.write(out); - return out.toByteArray(); - } catch (IOException e) { - throw new RuntimeException(e); - } - } } diff --git a/domain/src/main/java/ch/dissem/bitmessage/utils/Collections.java b/domain/src/main/java/ch/dissem/bitmessage/utils/Collections.java index 1feb8a4..32ef5eb 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/utils/Collections.java +++ b/domain/src/main/java/ch/dissem/bitmessage/utils/Collections.java @@ -25,8 +25,11 @@ public class Collections { private final static Random RANDOM = new Random(); /** - * Returns a random subset of the given collection, or a copy of the collection if it's not larger than count. - * The randomness + * @param count the number of elements to return (if possible) + * @param collection the collection to take samples from + * @return a random subset of the given collection, or a copy of the collection if it's not larger than count. The + * result is by no means securely random, but should be random enough so not the same objects get selected over + * and over again. */ public static List selectRandom(int count, Collection collection) { ArrayList result = new ArrayList<>(count); @@ -44,7 +47,7 @@ public class Collections { } else { result.add(item); resultRest--; - if (resultRest == 0){ + if (resultRest == 0) { break; } skipMax = (int) Math.ceil(collectionRest / resultRest); diff --git a/domain/src/main/java/ch/dissem/bitmessage/utils/Encode.java b/domain/src/main/java/ch/dissem/bitmessage/utils/Encode.java index 5ea2814..095fb78 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/utils/Encode.java +++ b/domain/src/main/java/ch/dissem/bitmessage/utils/Encode.java @@ -112,7 +112,9 @@ public class Encode { } /** - * Returns an array of bytes representing the given streamable object. + * @param streamable the object to be serialized + * @return an array of bytes representing the given streamable object. + * @throws IOException if an I/O error occurs. */ public static byte[] bytes(Streamable streamable) throws IOException { ByteArrayOutputStream stream = new ByteArrayOutputStream(); @@ -121,8 +123,10 @@ public class Encode { } /** - * Returns the bytes of the given streamable object, 0-padded such that the final - * length is x*padding. + * @param streamable the object to be serialized + * @param padding the result will be padded such that its length is a multiple of padding + * @return the bytes of the given {@link Streamable} object, 0-padded such that the final length is x*padding. + * @throws IOException if an I/O error occurs. */ public static byte[] bytes(Streamable streamable, int padding) throws IOException { ByteArrayOutputStream stream = new ByteArrayOutputStream(); 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 d3e5878..25e248e 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/utils/Security.java +++ b/domain/src/main/java/ch/dissem/bitmessage/utils/Security.java @@ -40,7 +40,8 @@ import java.security.spec.KeySpec; import java.util.Arrays; /** - * Provides some methods to help with hashing and encryption. + * Provides some methods to help with hashing and encryption. All randoms are created using {@link SecureRandom}, + * which should be secure enough. */ public class Security { public static final Logger LOG = LoggerFactory.getLogger(Security.class); @@ -52,10 +53,26 @@ public class Security { java.security.Security.addProvider(new BouncyCastleProvider()); } + /** + * A helper method to calculate SHA-512 hashes. Please note that a new {@link MessageDigest} object is created at + * each call (to ensure thread safety), so you shouldn't use this if you need to do many hash calculations in + * success on the same thread. + * + * @param data to get hashed + * @return SHA-512 hash of data + */ public static byte[] sha512(byte[]... data) { return hash("SHA-512", data); } + /** + * A helper method to calculate doubleSHA-512 hashes. Please note that a new {@link MessageDigest} object is created + * at each call (to ensure thread safety), so you shouldn't use this if you need to do many hash calculations in + * success on the same thread. + * + * @param data to get hashed + * @return SHA-512 hash of data + */ public static byte[] doubleSha512(byte[]... data) { MessageDigest mda = md("SHA-512"); for (byte[] d : data) { @@ -64,33 +81,97 @@ public class Security { return mda.digest(mda.digest()); } + /** + * A helper method to calculate double SHA-512 hashes. This method allows to only use a part of the available bytes + * to use for the hash calculation. + *

+ * Please note that a new {@link MessageDigest} object is created at each call (to ensure thread safety), so you + * shouldn't use this if you need to do many hash calculations in short order on the same thread. + *

+ * + * @param data to get hashed + * @param length number of bytes to be taken into account + * @return SHA-512 hash of data + */ public static byte[] doubleSha512(byte[] data, int length) { MessageDigest mda = md("SHA-512"); mda.update(data, 0, length); return mda.digest(mda.digest()); } + + /** + * A helper method to calculate RIPEMD-160 hashes. Supplying multiple byte arrays has the same result as a + * concatenation of all arrays, but might perform better. + *

+ * Please note that a new {@link MessageDigest} object is created at + * each call (to ensure thread safety), so you shouldn't use this if you need to do many hash calculations in short + * order on the same thread. + *

+ * + * @param data to get hashed + * @return RIPEMD-160 hash of data + */ public static byte[] ripemd160(byte[]... data) { return hash("RIPEMD160", data); } + /** + * A helper method to calculate double SHA-256 hashes. This method allows to only use a part of the available bytes + * to use for the hash calculation. + *

+ * Please note that a new {@link MessageDigest} object is created at + * each call (to ensure thread safety), so you shouldn't use this if you need to do many hash calculations in short + * order on the same thread. + *

+ * + * @param data to get hashed + * @param length number of bytes to be taken into account + * @return SHA-256 hash of data + */ public static byte[] doubleSha256(byte[] data, int length) { MessageDigest mda = md("SHA-256"); mda.update(data, 0, length); return mda.digest(mda.digest()); } + /** + * A helper method to calculate SHA-1 hashes. Supplying multiple byte arrays has the same result as a + * concatenation of all arrays, but might perform better. + *

+ * Please note that a new {@link MessageDigest} object is created at + * each call (to ensure thread safety), so you shouldn't use this if you need to do many hash calculations in short + * order on the same thread. + *

+ * + * @param data to get hashed + * @return SHA hash of data + */ public static byte[] sha1(byte[]... data) { return hash("SHA-1", data); } + /** + * @param length number of bytes to return + * @return an array of the given size containing random bytes + */ public static byte[] randomBytes(int length) { byte[] result = new byte[length]; RANDOM.nextBytes(result); return result; } - public static void doProofOfWork(ObjectMessage object, ProofOfWorkEngine worker, long nonceTrialsPerByte, long extraBytes) { + /** + * Calculates the proof of work. This might take a long time, depending on the hardware, message size and time to + * live. + * + * @param object to do the proof of work for + * @param worker doing the actual proof of work + * @param nonceTrialsPerByte difficulty + * @param extraBytes bytes to add to the object size (makes it more difficult to send small messages) + */ + public static void doProofOfWork(ObjectMessage object, ProofOfWorkEngine worker, long nonceTrialsPerByte, + long extraBytes) { try { if (nonceTrialsPerByte < 1000) nonceTrialsPerByte = 1000; if (extraBytes < 1000) extraBytes = 1000; @@ -107,9 +188,13 @@ public class Security { } /** - * @throws InsufficientProofOfWorkException if proof of work doesn't check out + * @param object to be checked + * @param nonceTrialsPerByte difficulty + * @param extraBytes bytes to add to the object size + * @throws InsufficientProofOfWorkException if proof of work doesn't check out (makes it more difficult to send small messages) */ - public static void checkProofOfWork(ObjectMessage object, long nonceTrialsPerByte, long extraBytes) throws IOException { + public static void checkProofOfWork(ObjectMessage object, long nonceTrialsPerByte, long extraBytes) + throws IOException { byte[] target = getProofOfWorkTarget(object, nonceTrialsPerByte, extraBytes); byte[] value = Security.doubleSha512(object.getNonce(), getInitialHash(object)); if (Bytes.lt(target, value, 8)) { @@ -146,6 +231,13 @@ public class Security { } } + /** + * Calculates the MAC for a message (data) + * + * @param key_m the symmetric key used + * @param data the message data to calculate the MAC for + * @return the MAC + */ public static byte[] mac(byte[] key_m, byte[] data) { try { Mac mac = Mac.getInstance("HmacSHA256", "BC"); @@ -156,6 +248,18 @@ public class Security { } } + /** + * Create a new public key fom given private keys. + * + * @param version of the public key / address + * @param stream of the address + * @param privateSigningKey private key used for signing + * @param privateEncryptionKey private key used for encryption + * @param nonceTrialsPerByte proof of work difficulty + * @param extraBytes bytes to add for the proof of work (make it harder for small messages) + * @param features of the address + * @return a public key object + */ public static Pubkey createPubkey(long version, long stream, byte[] privateSigningKey, byte[] privateEncryptionKey, long nonceTrialsPerByte, long extraBytes, Pubkey.Feature... features) { return Factory.createPubkey(version, stream, @@ -164,10 +268,18 @@ public class Security { nonceTrialsPerByte, extraBytes, features); } + /** + * @param privateKey private key as byte array + * @return a public key corresponding to the given private key + */ public static ECPoint createPublicKey(byte[] privateKey) { return EC_CURVE_PARAMETERS.getG().multiply(keyToBigInt(privateKey)).normalize(); } + /** + * @param privateKey private key as byte array + * @return a big integer representation (unsigned) of the given bytes + */ public static BigInteger keyToBigInt(byte[] privateKey) { return new BigInteger(1, privateKey); } @@ -185,7 +297,13 @@ public class Security { ); } - public static boolean isSignatureValid(byte[] bytesToSign, byte[] signature, Pubkey pubkey) { + /** + * @param data to check + * @param signature the signature of the message + * @param pubkey the sender's public key + * @return true if the signature is valid, false otherwise + */ + public static boolean isSignatureValid(byte[] data, byte[] signature, Pubkey pubkey) { try { ECParameterSpec spec = new ECParameterSpec( EC_CURVE_PARAMETERS.getCurve(), @@ -201,13 +319,20 @@ public class Security { Signature sig = Signature.getInstance("ECDSA", "BC"); sig.initVerify(publicKey); - sig.update(bytesToSign); + sig.update(data); return sig.verify(signature); } catch (Exception e) { throw new RuntimeException(e); } } + /** + * Calculate the signature of data, using the given private key. + * + * @param data to be signed + * @param privateKey to be used for signing + * @return the signature + */ public static byte[] getSignature(byte[] data, ch.dissem.bitmessage.entity.valueobject.PrivateKey privateKey) { try { ECParameterSpec spec = new ECParameterSpec( @@ -231,6 +356,9 @@ public class Security { } } + /** + * @return a random number of type long + */ public static long randomNonce() { return RANDOM.nextLong(); } diff --git a/domain/src/main/java/ch/dissem/bitmessage/utils/UnixTime.java b/domain/src/main/java/ch/dissem/bitmessage/utils/UnixTime.java index f5d8052..ffd4df8 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/utils/UnixTime.java +++ b/domain/src/main/java/ch/dissem/bitmessage/utils/UnixTime.java @@ -30,7 +30,7 @@ public class UnixTime { public static final long DAY = 24 * HOUR; /** - * Returns the time in second based Unix time ({@link System#currentTimeMillis()}/1000) + * @return the time in second based Unix time ({@link System#currentTimeMillis()}/1000) */ public static long now() { return System.currentTimeMillis() / 1000; @@ -38,6 +38,9 @@ public class UnixTime { /** * Same as {@link #now()} + shiftSeconds, but might be more readable. + * + * @param shiftSeconds number of seconds from now we're interested in + * @return the Unix time in shiftSeconds seconds / shiftSeconds seconds ago */ public static long now(long shiftSeconds) { return (System.currentTimeMillis() / 1000) + shiftSeconds;