184 lines
6.4 KiB
Java
184 lines
6.4 KiB
Java
/*
|
|
* 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.ports;
|
|
|
|
import ch.dissem.bitmessage.InternalContext;
|
|
import ch.dissem.bitmessage.entity.ObjectMessage;
|
|
import ch.dissem.bitmessage.entity.payload.Pubkey;
|
|
import ch.dissem.bitmessage.exception.InsufficientProofOfWorkException;
|
|
import ch.dissem.bitmessage.factory.Factory;
|
|
import ch.dissem.bitmessage.utils.Bytes;
|
|
import ch.dissem.bitmessage.utils.UnixTime;
|
|
import org.slf4j.Logger;
|
|
import org.slf4j.LoggerFactory;
|
|
|
|
import javax.crypto.Mac;
|
|
import javax.crypto.spec.SecretKeySpec;
|
|
import java.io.IOException;
|
|
import java.math.BigInteger;
|
|
import java.security.GeneralSecurityException;
|
|
import java.security.MessageDigest;
|
|
import java.security.SecureRandom;
|
|
|
|
import static ch.dissem.bitmessage.utils.Numbers.max;
|
|
|
|
/**
|
|
* Implements everything that isn't directly dependent on either Spongy- or Bouncycastle.
|
|
*/
|
|
public abstract class AbstractCryptography implements Cryptography, InternalContext.ContextHolder {
|
|
public static final Logger LOG = LoggerFactory.getLogger(Cryptography.class);
|
|
private static final SecureRandom RANDOM = new SecureRandom();
|
|
private static final BigInteger TWO = BigInteger.valueOf(2);
|
|
private static final BigInteger TWO_POW_64 = TWO.pow(64);
|
|
private static final BigInteger TWO_POW_16 = TWO.pow(16);
|
|
|
|
private final String provider;
|
|
private InternalContext context;
|
|
|
|
protected AbstractCryptography(String provider) {
|
|
this.provider = provider;
|
|
}
|
|
|
|
@Override
|
|
public void setContext(InternalContext context) {
|
|
this.context = context;
|
|
}
|
|
|
|
public byte[] sha512(byte[]... data) {
|
|
return hash("SHA-512", data);
|
|
}
|
|
|
|
public byte[] doubleSha512(byte[]... data) {
|
|
MessageDigest mda = md("SHA-512");
|
|
for (byte[] d : data) {
|
|
mda.update(d);
|
|
}
|
|
return mda.digest(mda.digest());
|
|
}
|
|
|
|
public byte[] doubleSha512(byte[] data, int length) {
|
|
MessageDigest mda = md("SHA-512");
|
|
mda.update(data, 0, length);
|
|
return mda.digest(mda.digest());
|
|
}
|
|
|
|
public byte[] ripemd160(byte[]... data) {
|
|
return hash("RIPEMD160", data);
|
|
}
|
|
|
|
public byte[] doubleSha256(byte[] data, int length) {
|
|
MessageDigest mda = md("SHA-256");
|
|
mda.update(data, 0, length);
|
|
return mda.digest(mda.digest());
|
|
}
|
|
|
|
public byte[] sha1(byte[]... data) {
|
|
return hash("SHA-1", data);
|
|
}
|
|
|
|
public byte[] randomBytes(int length) {
|
|
byte[] result = new byte[length];
|
|
RANDOM.nextBytes(result);
|
|
return result;
|
|
}
|
|
|
|
public void doProofOfWork(ObjectMessage object, long nonceTrialsPerByte,
|
|
long extraBytes, ProofOfWorkEngine.Callback callback) {
|
|
nonceTrialsPerByte = max(nonceTrialsPerByte, context.getNetworkNonceTrialsPerByte());
|
|
extraBytes = max(extraBytes, context.getNetworkExtraBytes());
|
|
|
|
byte[] initialHash = getInitialHash(object);
|
|
|
|
byte[] target = getProofOfWorkTarget(object, nonceTrialsPerByte, extraBytes);
|
|
|
|
context.getProofOfWorkEngine().calculateNonce(initialHash, target, callback);
|
|
}
|
|
|
|
public void checkProofOfWork(ObjectMessage object, long nonceTrialsPerByte, long extraBytes)
|
|
throws IOException {
|
|
byte[] target = getProofOfWorkTarget(object, nonceTrialsPerByte, extraBytes);
|
|
byte[] value = doubleSha512(object.getNonce(), getInitialHash(object));
|
|
if (Bytes.lt(target, value, 8)) {
|
|
throw new InsufficientProofOfWorkException(target, value);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public byte[] getInitialHash(ObjectMessage object) {
|
|
return sha512(object.getPayloadBytesWithoutNonce());
|
|
}
|
|
|
|
@Override
|
|
public byte[] getProofOfWorkTarget(ObjectMessage object, long nonceTrialsPerByte, long extraBytes) {
|
|
if (nonceTrialsPerByte == 0) nonceTrialsPerByte = context.getNetworkNonceTrialsPerByte();
|
|
if (extraBytes == 0) extraBytes = context.getNetworkExtraBytes();
|
|
|
|
BigInteger TTL = BigInteger.valueOf(object.getExpiresTime() - UnixTime.now());
|
|
BigInteger numerator = TWO_POW_64;
|
|
BigInteger powLength = BigInteger.valueOf(object.getPayloadBytesWithoutNonce().length + extraBytes);
|
|
BigInteger denominator = BigInteger.valueOf(nonceTrialsPerByte)
|
|
.multiply(
|
|
powLength.add(
|
|
powLength.multiply(TTL).divide(TWO_POW_16)
|
|
)
|
|
);
|
|
return Bytes.expand(numerator.divide(denominator).toByteArray(), 8);
|
|
}
|
|
|
|
private byte[] hash(String algorithm, byte[]... data) {
|
|
MessageDigest mda = md(algorithm);
|
|
for (byte[] d : data) {
|
|
mda.update(d);
|
|
}
|
|
return mda.digest();
|
|
}
|
|
|
|
private MessageDigest md(String algorithm) {
|
|
try {
|
|
return MessageDigest.getInstance(algorithm, provider);
|
|
} catch (GeneralSecurityException e) {
|
|
throw new RuntimeException(e);
|
|
}
|
|
}
|
|
|
|
public byte[] mac(byte[] key_m, byte[] data) {
|
|
try {
|
|
Mac mac = Mac.getInstance("HmacSHA256", provider);
|
|
mac.init(new SecretKeySpec(key_m, "HmacSHA256"));
|
|
return mac.doFinal(data);
|
|
} catch (GeneralSecurityException e) {
|
|
throw new RuntimeException(e);
|
|
}
|
|
}
|
|
|
|
public 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),
|
|
nonceTrialsPerByte, extraBytes, features);
|
|
}
|
|
|
|
public BigInteger keyToBigInt(byte[] privateKey) {
|
|
return new BigInteger(1, privateKey);
|
|
}
|
|
|
|
public long randomNonce() {
|
|
return RANDOM.nextLong();
|
|
}
|
|
}
|