package se.chalmers.gdcn.hashcash;
import se.chalmers.gdcn.network.WorkerID;
import javax.crypto.SecretKey;
import java.io.UnsupportedEncodingException;
import java.security.*;
/**
* Created by weeeeeew on 2014-03-31.
*/
public class HashCash {
public final static String HASH_ALGORITHM = "SHA-1";
private final int hardDifficulty, easyDifficulty;
private final SecretKey key;
public static enum Purpose { REG, AUTH, NONE }
/**
* Creates a new HashCash-cookie instance with standard difficulties.
* The supplied key is used to validate solutions.
* @param key A key for MACing, must be compatible with javax.crypto.Mac. At least SHA-256 is recommended,
* which can be generated with KeyGenerator.getInstance("HmacSHA256").generateKey()
*/
public HashCash(SecretKey key) throws InvalidKeyException {
this(key,15,20);
//TODO Insert real numbers for easy and hard difficulties.
// 28-30 seems good for REG. Lowers this for debugging purposes.
// perhaps 22-25 for AUTH
}
/**
* Creates a new HashCash-cookie instance with custom difficulties.
* The supplied key is used to validate solutions.
* @param key A key for MACing, must be compatible with javax.crypto.Mac. At least SHA-256 is recommended,
* which can be generated with KeyGenerator.getInstance("HmacSHA256").generateKey()
* @param easy The difficulty of easy challenges.
* @param hard The difficulty of hard challenges.
*/
public HashCash(SecretKey key, int easy, int hard) {
this.key = key;
hardDifficulty = hard;
easyDifficulty = easy;
}
/**
* Generates a new Challenge without a purpose.
* @param seed The seed of the challenge.
* @param difficulty The difficulty of the challenge.
* @return The challenge.
*/
public Challenge generateChallenge(String seed, int difficulty) {
return generateChallenge(Purpose.NONE, seed, difficulty);
}
/**
* Generates a new Challenge with a purpose.
* @param purpose The purpose of the challenge.
* @param seed The seed of the challenge.
* @param difficulty The difficulty of the challenge.
* @return The challenge.
*/
public Challenge generateChallenge(Purpose purpose, String seed, int difficulty) {
try {
return new Challenge(purpose, hash(seed), difficulty, key);
} catch (InvalidKeyException e) {
e.printStackTrace();
//TODO Tell the user that the key supplied to the HashCash is invalid.
return null;
}
}
/**
* Generates a hard challenge for registration purposes.
* @param jobOwner The issuer of the challenge.
* @param worker The worker that wants to register.
* @param score The worker's current score.
* @return The challenge.
*/
public Challenge generateRegistrationChallenge(WorkerID jobOwner, WorkerID worker, int score) {
String seed = jobOwner.toString() + worker + score;
return generateChallenge(Purpose.REG, seed, hardDifficulty);
}
/**
* Generates an easy challenge for authentication purposes.
* @param jobOwner The issuer of the challenge.
* @param worker The worker that wants to register.
* @param score The worker's current score.
* @return The challenge.
*/
public Challenge generateAuthenticationChallenge(WorkerID jobOwner, WorkerID worker, int score) {
String seed = jobOwner.toString() + worker + score;
return generateChallenge(Purpose.AUTH, seed, easyDifficulty);
}
/**
* Checks the validity and authenticity of a solution.
* @param solution The solution to check.
* @param jobOwner The issuer of the challenge.
* @param worker The worker that claims to have solved a challenge.
* @param score The worker's current score.
* @return True if the worker solved the solution, false otherwise.
*/
public boolean validateSolution(Solution solution, WorkerID jobOwner, WorkerID worker, int score) throws InvalidKeyException {
byte[] seed = hash(jobOwner.toString() + worker + score);
return solution.isValid(key,seed);
}
private static byte[] hash(String message) {
MessageDigest md = null;
try {
md = MessageDigest.getInstance(HASH_ALGORITHM);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
//TODO Something went really wrong here, present it to the user in some way?
}
try {
md.update(message.getBytes("UTF-8"));
} catch (UnsupportedEncodingException e) {
System.out.println("HashCash: UTF-8 is required! Things will fail now.");
}
return md.digest();
}
}