package no.ntnu.item.csv.csvobject;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.HashMap;
import java.util.Map;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import no.ntnu.item.cryptoutil.Cryptoutil;
import no.ntnu.item.csv.capability.Capability;
import no.ntnu.item.csv.capability.CapabilityImpl;
import no.ntnu.item.csv.capability.CapabilityType;
public class CSVFolder implements CSVObject {
private PublicKey pubkey;
private PrivateKey privkey;
private byte[] encPrivKey;
private Capability capability;
private Map<String, Capability> contents;
private byte[] ciphertext;
public byte[] plainText;
private byte[] iv;
private byte[] signature;
public boolean hack_do_not_create_plaintext = false;
public CSVFolder() {
generateKeys();
this.contents = new HashMap<String, Capability>();
}
public CSVFolder(Capability capability) {
this.capability = capability;
}
public void download(byte[] packet) {
byte[] pubkey = new byte[Cryptoutil.ASYM_SIZE / 8 + 1 + 3];
System.arraycopy(packet, 0, pubkey, 0, pubkey.length);
this.setPubKey(pubkey);
byte[] signature = new byte[Cryptoutil.ASYM_SIZE / 8];
System.arraycopy(packet, pubkey.length, signature, 0, signature.length);
this.signature = signature;
byte[] iv = new byte[Cryptoutil.SYM_BLOCK_SIZE / 8];
System.arraycopy(packet, pubkey.length + signature.length, iv, 0,
iv.length);
this.iv = iv;
byte[] encPrivKey = new byte[2 * (Cryptoutil.ASYM_SIZE / 8) + 1 + 15];
System.arraycopy(packet, pubkey.length + signature.length + iv.length,
encPrivKey, 0, encPrivKey.length);
this.encPrivKey = encPrivKey;
byte[] cipherText = new byte[packet.length - pubkey.length
- signature.length - iv.length - encPrivKey.length];
System.arraycopy(packet, pubkey.length + signature.length + iv.length
+ encPrivKey.length, cipherText, 0, cipherText.length);
this.ciphertext = cipherText;
byte tmp[] = Cryptoutil.symECBDecrypt(encPrivKey, new SecretKeySpec(
this.getCapability().getKey(), Cryptoutil.SYM_CIPHER));
this.privkey = Cryptoutil.createRSAPrivateKey(tmp);
this.decrypt();
// this.ciphertext = null;
// this.signature = null;
}
private void generateKeys() {
KeyPair pair = Cryptoutil.generateAsymmetricKeys();
this.pubkey = pair.getPublic();
this.privkey = pair.getPrivate();
byte[] write = Cryptoutil.hash(this.privkey.getEncoded(),
Cryptoutil.SYM_SIZE / 8);
RSAPublicKey pub = (RSAPublicKey) this.pubkey;
byte[] mod = pub.getModulus().toByteArray();
byte[] pubexp = pub.getPublicExponent().toByteArray();
byte[] to_hash = new byte[mod.length + pubexp.length];
System.arraycopy(mod, 0, to_hash, 0, mod.length);
System.arraycopy(pubexp, 0, to_hash, mod.length, pubexp.length);
byte[] verify = Cryptoutil.hash(to_hash, Cryptoutil.SYM_SIZE / 8);
Capability writecap = new CapabilityImpl(CapabilityType.RW, write,
verify, false);
this.capability = writecap;
}
public void sign() {
assert this.ciphertext != null;
byte[] hash = Cryptoutil.hash(this.ciphertext, -1);
this.signature = Cryptoutil.signature(hash, this.privkey);
}
public void encrypt() {
byte[] read;
if (this.capability.getType() == CapabilityType.RW) {
read = Cryptoutil.hash(this.capability.getKey(),
Cryptoutil.SYM_SIZE / 8);
} else {
read = this.capability.getKey();
}
// 1
if (!this.hack_do_not_create_plaintext) {
this.createPlainText();
}
SecretKeySpec sks = new SecretKeySpec(read, Cryptoutil.SYM_CIPHER);
this.iv = Cryptoutil.generateIV();
this.ciphertext = Cryptoutil.symEncrypt(this.plainText, sks,
new IvParameterSpec(this.iv));
sign();
if (this.encPrivKey == null) {
this.encPrivKey = Cryptoutil.symECBEncrypt(Cryptoutil
.serializePrivateKey((RSAPrivateKey) this.privkey),
new SecretKeySpec(this.capability.getKey(),
Cryptoutil.SYM_CIPHER));
}
}
private void decrypt() {
assert this.ciphertext != null;
byte[] read;
if (this.capability.getType() == CapabilityType.RW) {
read = Cryptoutil.hash(this.capability.getKey(),
Cryptoutil.SYM_SIZE / 8);
} else {
read = this.capability.getKey();
}
SecretKeySpec sks = new SecretKeySpec(read, Cryptoutil.SYM_CIPHER);
this.plainText = Cryptoutil.symDecrypt(this.ciphertext, sks,
new IvParameterSpec(this.iv));
this.createContentsFromPlainText();
}
@Override
public boolean isValid() {
byte[] verify = this.capability.getVerificationKey();
byte[] fromServer = this.getPublicKeyHash();
int n = fromServer.length;
if (verify.length != fromServer.length) {
return false;
}
for (int i = 0; i < n; i++) {
if (verify[i] != fromServer[i]) {
return false;
}
}
byte[] hash = Cryptoutil.hash(this.ciphertext, -1);
return Cryptoutil.signature_valid(this.signature, hash, this.pubkey);
}
protected byte[] getCipherText() {
return this.ciphertext;
}
@Override
public Capability getCapability() {
return this.capability;
}
@Override
public void setCapability(Capability capability) {
this.capability = capability;
}
public Map<String, Capability> getContents() {
if (this.contents == null && this.ciphertext != null) {
decrypt();
}
return this.contents;
}
protected void setPubKey(byte[] pubKey) {
this.pubkey = Cryptoutil.createRSAPublicKey(pubKey);
}
public void addContent(String alias, Capability capability) {
this.contents.put(alias, capability);
}
protected byte[] getPubKey() {
return Cryptoutil.serializePublicKey((RSAPublicKey) this.pubkey);
}
public byte[] upload() {
this.encrypt();
this.sign();
byte[] pub = Cryptoutil.serializePublicKey((RSAPublicKey) this.pubkey);
byte[] transfer = new byte[pub.length + this.signature.length
+ this.iv.length + this.encPrivKey.length
+ this.ciphertext.length];
System.arraycopy(pub, 0, transfer, 0, pub.length);
System.arraycopy(this.signature, 0, transfer, pub.length,
this.signature.length);
System.arraycopy(this.iv, 0, transfer, pub.length
+ this.signature.length, this.iv.length);
System.arraycopy(this.encPrivKey, 0, transfer, pub.length
+ this.signature.length + this.iv.length,
this.encPrivKey.length);
System.arraycopy(this.ciphertext, 0, transfer, pub.length
+ this.signature.length + this.iv.length
+ this.encPrivKey.length, this.ciphertext.length);
return transfer;
}
protected void createContentsFromPlainText() {
Map<String, Capability> contents = new HashMap<String, Capability>();
String strCont = new String(this.plainText);
String[] lines = strCont.split("\n");
if (lines.length == 1 && lines[0].length() < 1) {
this.contents = contents;
return;
}
for (int i = 0; i < lines.length; i++) {
String[] lineCont = lines[i].split(";");
Capability cap = CapabilityImpl.fromString(lineCont[1]);
String alias = lineCont[0];
contents.put(alias, cap);
}
this.contents = contents;
}
public void createPlainText() {
if (this.contents != null) {
String plaintext = "";
for (Map.Entry<String, Capability> entry : this.getContents()
.entrySet()) {
plaintext += entry.getKey() + ";" + entry.getValue().toString()
+ "\n";
}
this.plainText = plaintext.getBytes();
}
}
public byte[] getPublicKeyHash() {
RSAPublicKey pub = (RSAPublicKey) this.pubkey;
byte[] mod = pub.getModulus().toByteArray();
byte[] pubexp = pub.getPublicExponent().toByteArray();
byte[] to_hash = new byte[mod.length + pubexp.length];
System.arraycopy(mod, 0, to_hash, 0, mod.length);
System.arraycopy(pubexp, 0, to_hash, mod.length, pubexp.length);
return Cryptoutil.hash(to_hash, Cryptoutil.SYM_SIZE / 8);
}
}