Updated and fixed JavaDoc

This commit is contained in:
Christian Basler 2015-06-18 18:25:47 +02:00
parent 6be8d51f6d
commit 1eb6851798
14 changed files with 189 additions and 57 deletions

View File

@ -44,7 +44,7 @@ import static ch.dissem.bitmessage.utils.UnixTime.DAY;
/** /**
* Use this class if you want to create a Bitmessage client. * Use this class if you want to create a Bitmessage client.
* <p/> * <p>
* You'll need the Builder to create a BitmessageContext, and set the following properties: * You'll need the Builder to create a BitmessageContext, and set the following properties:
* <ul> * <ul>
* <li>addressRepo</li> * <li>addressRepo</li>
@ -55,8 +55,10 @@ import static ch.dissem.bitmessage.utils.UnixTime.DAY;
* <li>streams</li> * <li>streams</li>
* </ul> * </ul>
* The default implementations in the different module builds can be used. * The default implementations in the different module builds can be used.
* <p/> * </p>
* <p>
* The port defaults to 8444 (the default Bitmessage port) * The port defaults to 8444 (the default Bitmessage port)
* </p>
*/ */
public class BitmessageContext { public class BitmessageContext {
public static final int CURRENT_VERSION = 3; public static final int CURRENT_VERSION = 3;

View File

@ -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 * 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 * implementation, you're either doing something wrong, something very weird, or the BitmessageContext should
* get extended. * get extended.
* <p/> * <p>
* On the other hand, if you need the BitmessageContext in a port implementation, the same thing might apply. * On the other hand, if you need the BitmessageContext in a port implementation, the same thing might apply.
* </p>
*/ */
public class InternalContext { public class InternalContext {
private final static Logger LOG = LoggerFactory.getLogger(InternalContext.class); private final static Logger LOG = LoggerFactory.getLogger(InternalContext.class);

View File

@ -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() { public byte[] getPublicDecryptionKey() {
return publicDecryptionKey; return publicDecryptionKey;

View File

@ -47,11 +47,11 @@ public class CryptoBox implements Streamable {
private final byte[] mac; private final byte[] mac;
private byte[] encrypted; private byte[] encrypted;
public CryptoBox(Streamable data, byte[] encryptionKey) { public CryptoBox(Streamable data, byte[] encryptionKey) throws IOException {
this(data, Security.keyToPoint(encryptionKey)); this(data, Security.keyToPoint(encryptionKey));
} }
public CryptoBox(Streamable data, ECPoint K) { public CryptoBox(Streamable data, ECPoint K) throws IOException {
curveType = 0x02CA; curveType = 0x02CA;
// 1. The destination public key is called K. // 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); byte[] key_m = Arrays.copyOfRange(H, 32, 64);
// 7. Pad the input text to a multiple of 16 bytes, in accordance to PKCS7. // 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. // 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. // 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); 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 <a href='https://bitmessage.org/wiki/Encryption#Decryption'>https://bitmessage.org/wiki/Encryption#Decryption</a> * @see <a href='https://bitmessage.org/wiki/Encryption#Decryption'>https://bitmessage.org/wiki/Encryption#Decryption</a>
*/ */
public InputStream decrypt(byte[] privateKey) throws DecryptionFailedException { public InputStream decrypt(byte[] privateKey) throws DecryptionFailedException {

View File

@ -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. * address version.
*/ */
public byte[] getRipeTag() { public byte[] getRipeTag() {

View File

@ -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 * 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. * be checked and set in the {@link ObjectMessage} object.
*/ */

View File

@ -31,12 +31,15 @@ public class Label {
} }
/** /**
* RGBA representation for the color. * @return RGBA representation for the color.
*/ */
public int getColor() { public int getColor() {
return color; return color;
} }
/**
* @param color RGBA representation for the color.
*/
public void setColor(int color) { public void setColor(int color) {
this.color = color; this.color = color;
} }
@ -50,14 +53,14 @@ public class Label {
return id; return id;
} }
public Type getType() {
return type;
}
public void setId(Object id) { public void setId(Object id) {
this.id = id; this.id = id;
} }
public Type getType() {
return type;
}
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if (this == o) return true; if (this == o) return true;

View File

@ -26,7 +26,7 @@ public interface AddressRepository {
BitmessageAddress findIdentity(byte[] ripeOrTag); 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<BitmessageAddress> getIdentities(); List<BitmessageAddress> getIdentities();
@ -35,7 +35,7 @@ public interface AddressRepository {
List<BitmessageAddress> getSubscriptions(long broadcastVersion); List<BitmessageAddress> getSubscriptions(long broadcastVersion);
/** /**
* Returns all Bitmessage addresses that have no private key. * @return all Bitmessage addresses that have no private key.
*/ */
List<BitmessageAddress> getContacts(); List<BitmessageAddress> getContacts();

View File

@ -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. * @author Christian Basler: I removed some dependencies to the BitcoinJ code so it can be used here more easily.
*/ */
public class Base58 { public class Base58 {
private static char[] ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz".toCharArray();
private static final int[] INDEXES = new int[128]; private static final int[] INDEXES = new int[128];
private static char[] ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz".toCharArray();
static { static {
for (int i = 0; i < INDEXES.length; i++) { 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. * Encodes the given bytes in base58. No checksum is appended.
*
* @param input to encode
* @return base58 encoded input
*/ */
public static String encode(byte[] input) { public static String encode(byte[] input) {
if (input.length == 0) { if (input.length == 0) {

View File

@ -38,6 +38,9 @@ public class Bytes {
/** /**
* Increases nonce by value, which is used as an unsigned byte value. * 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) { public static void inc(byte[] nonce, byte value) {
int i = nonce.length - 1; int i = nonce.length - 1;
@ -54,7 +57,7 @@ public class Bytes {
} }
/** /**
* Returns true if a < b. * @return true if a &lt; b.
*/ */
public static boolean lt(byte[] a, byte[] b) { public static boolean lt(byte[] a, byte[] b) {
byte[] max = (a.length > b.length ? a : 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 &lt; b, where the first [size] bytes are used as the numbers to check.
*/ */
public static boolean lt(byte[] a, byte[] b, int size) { public static boolean lt(byte[] a, byte[] b, int size) {
for (int i = 0; i < size; i++) { 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) { public static byte[] expand(byte[] source, int size) {
byte[] result = new byte[size]; byte[] result = new byte[size];
@ -100,7 +103,7 @@ public class Bytes {
} }
/** /**
* Returns a new byte array containing the first <em>size</em> bytes of the given array. * @return a new byte array containing the first <em>size</em> bytes of the given array.
*/ */
public static byte[] truncate(byte[] source, int size) { public static byte[] truncate(byte[] source, int size) {
byte[] result = new byte[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. * production code.
*/ */
public static byte[] fromHex(String hex) { 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) { public static int numberOfLeadingZeros(byte[] bytes) {
int i; int i;
@ -145,24 +148,4 @@ public class Bytes {
} }
return i; 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);
}
}
} }

View File

@ -25,8 +25,11 @@ public class Collections {
private final static Random RANDOM = new Random(); 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. * @param count the number of elements to return (if possible)
* The randomness * @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 <T> List<T> selectRandom(int count, Collection<T> collection) { public static <T> List<T> selectRandom(int count, Collection<T> collection) {
ArrayList<T> result = new ArrayList<>(count); ArrayList<T> result = new ArrayList<>(count);
@ -44,7 +47,7 @@ public class Collections {
} else { } else {
result.add(item); result.add(item);
resultRest--; resultRest--;
if (resultRest == 0){ if (resultRest == 0) {
break; break;
} }
skipMax = (int) Math.ceil(collectionRest / resultRest); skipMax = (int) Math.ceil(collectionRest / resultRest);

View File

@ -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 { public static byte[] bytes(Streamable streamable) throws IOException {
ByteArrayOutputStream stream = new ByteArrayOutputStream(); 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 * @param streamable the object to be serialized
* length is x*padding. * @param padding the result will be padded such that its length is a multiple of <em>padding</em>
* @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 { public static byte[] bytes(Streamable streamable, int padding) throws IOException {
ByteArrayOutputStream stream = new ByteArrayOutputStream(); ByteArrayOutputStream stream = new ByteArrayOutputStream();

View File

@ -40,7 +40,8 @@ import java.security.spec.KeySpec;
import java.util.Arrays; 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 class Security {
public static final Logger LOG = LoggerFactory.getLogger(Security.class); public static final Logger LOG = LoggerFactory.getLogger(Security.class);
@ -52,10 +53,26 @@ public class Security {
java.security.Security.addProvider(new BouncyCastleProvider()); 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) { public static byte[] sha512(byte[]... data) {
return hash("SHA-512", 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) { public static byte[] doubleSha512(byte[]... data) {
MessageDigest mda = md("SHA-512"); MessageDigest mda = md("SHA-512");
for (byte[] d : data) { for (byte[] d : data) {
@ -64,33 +81,97 @@ public class Security {
return mda.digest(mda.digest()); 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.
* <p>
* 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.
* </p>
*
* @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) { public static byte[] doubleSha512(byte[] data, int length) {
MessageDigest mda = md("SHA-512"); MessageDigest mda = md("SHA-512");
mda.update(data, 0, length); mda.update(data, 0, length);
return mda.digest(mda.digest()); 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.
* <p>
* 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.
* </p>
*
* @param data to get hashed
* @return RIPEMD-160 hash of data
*/
public static byte[] ripemd160(byte[]... data) { public static byte[] ripemd160(byte[]... data) {
return hash("RIPEMD160", 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.
* <p>
* 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.
* </p>
*
* @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) { public static byte[] doubleSha256(byte[] data, int length) {
MessageDigest mda = md("SHA-256"); MessageDigest mda = md("SHA-256");
mda.update(data, 0, length); mda.update(data, 0, length);
return mda.digest(mda.digest()); 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.
* <p>
* 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.
* </p>
*
* @param data to get hashed
* @return SHA hash of data
*/
public static byte[] sha1(byte[]... data) { public static byte[] sha1(byte[]... data) {
return hash("SHA-1", 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) { public static byte[] randomBytes(int length) {
byte[] result = new byte[length]; byte[] result = new byte[length];
RANDOM.nextBytes(result); RANDOM.nextBytes(result);
return 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 { try {
if (nonceTrialsPerByte < 1000) nonceTrialsPerByte = 1000; if (nonceTrialsPerByte < 1000) nonceTrialsPerByte = 1000;
if (extraBytes < 1000) extraBytes = 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[] target = getProofOfWorkTarget(object, nonceTrialsPerByte, extraBytes);
byte[] value = Security.doubleSha512(object.getNonce(), getInitialHash(object)); byte[] value = Security.doubleSha512(object.getNonce(), getInitialHash(object));
if (Bytes.lt(target, value, 8)) { 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) { public static byte[] mac(byte[] key_m, byte[] data) {
try { try {
Mac mac = Mac.getInstance("HmacSHA256", "BC"); 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, public static Pubkey createPubkey(long version, long stream, byte[] privateSigningKey, byte[] privateEncryptionKey,
long nonceTrialsPerByte, long extraBytes, Pubkey.Feature... features) { long nonceTrialsPerByte, long extraBytes, Pubkey.Feature... features) {
return Factory.createPubkey(version, stream, return Factory.createPubkey(version, stream,
@ -164,10 +268,18 @@ public class Security {
nonceTrialsPerByte, extraBytes, features); 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) { public static ECPoint createPublicKey(byte[] privateKey) {
return EC_CURVE_PARAMETERS.getG().multiply(keyToBigInt(privateKey)).normalize(); 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) { public static BigInteger keyToBigInt(byte[] privateKey) {
return new BigInteger(1, 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 { try {
ECParameterSpec spec = new ECParameterSpec( ECParameterSpec spec = new ECParameterSpec(
EC_CURVE_PARAMETERS.getCurve(), EC_CURVE_PARAMETERS.getCurve(),
@ -201,13 +319,20 @@ public class Security {
Signature sig = Signature.getInstance("ECDSA", "BC"); Signature sig = Signature.getInstance("ECDSA", "BC");
sig.initVerify(publicKey); sig.initVerify(publicKey);
sig.update(bytesToSign); sig.update(data);
return sig.verify(signature); return sig.verify(signature);
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException(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) { public static byte[] getSignature(byte[] data, ch.dissem.bitmessage.entity.valueobject.PrivateKey privateKey) {
try { try {
ECParameterSpec spec = new ECParameterSpec( ECParameterSpec spec = new ECParameterSpec(
@ -231,6 +356,9 @@ public class Security {
} }
} }
/**
* @return a random number of type long
*/
public static long randomNonce() { public static long randomNonce() {
return RANDOM.nextLong(); return RANDOM.nextLong();
} }

View File

@ -30,7 +30,7 @@ public class UnixTime {
public static final long DAY = 24 * HOUR; 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() { public static long now() {
return System.currentTimeMillis() / 1000; return System.currentTimeMillis() / 1000;
@ -38,6 +38,9 @@ public class UnixTime {
/** /**
* Same as {@link #now()} + shiftSeconds, but might be more readable. * 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) { public static long now(long shiftSeconds) {
return (System.currentTimeMillis() / 1000) + shiftSeconds; return (System.currentTimeMillis() / 1000) + shiftSeconds;