package edu.uw.cse.netlab.reputation.storage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.RandomAccessFile;
import java.io.Serializable;
import java.security.KeyPair;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.Signature;
import java.util.Date;
import java.util.Set;
import org.gudy.azureus2.core3.util.ByteFormatter;
import edu.uw.cse.netlab.reputation.LocalIdentity;
import edu.uw.cse.netlab.utils.BloomFilter;
import edu.uw.cse.netlab.utils.KeyManipulation;
public class Receipt implements Serializable
{
private static final long serialVersionUID = 1L;
PublicKey mSigning = null;
PublicKey mEncodingStateFor = null;
/**
* This ensures that the receiver updates to mediating intermediaries will
* be faithful to the reported attribution sent after the top K set. This is
* null if the exchange is not mediated by any intermediaries.
*/
BloomFilter mOnBehalfOf = null;
/**
* We use this locally to tag a connection's attribution with incoming
* receipts so we know which intermediaries to update. Although this
* information isn't sent over the wire, we need to keep track of it in our
* local DB in case the intermediary is not available during the lifetime of
* the connection.
*/
private transient Set<Long> mPreferredIntermediaries = null;
public Set<Long> getPreferredIntermediaries() {
return mPreferredIntermediaries;
}
public void setPreferredIntermediaries( Set<Long> inPrefs ) {
mPreferredIntermediaries = inPrefs;
}
transient long mSenderID = -1, mReceiverID = -1;
transient byte[] mHash = null;
/*
* A subset of the information available in the state table -- this reflects
* the fact that we're only interested in mIntermediary's opinion as an
* intermediary (remaining info can be recovered by requesting a receipt
* from the peer)
*/
long sent_direct = 0, received_direct = 0;
long peer_received_due_to_reco = 0, peer_sent_to_recos = 0;
/*
* Since receipts also serve as a diff to mediating intermediaries, include
* the sent_direct_diff
*/
int sent_direct_diff = 0;
/*
* This is scratch to hold the offset from the receipt bundle. We use this
* later during verification of receipts to check for cheating, but no need
* to send it over the wire twice (further, it can't be signed by the
* intermediary that signed this receipt)
*/
transient int peer_received_due_to_reco_offset = 0;
byte[] mSignature = null;
public byte[] getSignature() {
return mSignature;
}
Date timestamp = null;
public Date getTimestamp() {
return timestamp;
}
public BloomFilter getOnBehalfOf() {
return mOnBehalfOf;
}
public int get_received_due_to_reco_offset() {
return peer_received_due_to_reco_offset;
}
public void set_received_due_to_reco_offset( int inOffset ) {
peer_received_due_to_reco_offset = inOffset;
}
public boolean equals( Object rhs ) {
if (rhs instanceof Receipt)
{
Receipt r = (Receipt) rhs;
boolean mOnBehalf = false;
if (mOnBehalfOf != null && r.mOnBehalfOf != null)
mOnBehalf = mOnBehalfOf.equals(r.mOnBehalfOf);
else
mOnBehalf = mOnBehalfOf == null && r.mOnBehalfOf == null;
return r.mSigning.equals(mSigning)
&& r.mEncodingStateFor.equals(mEncodingStateFor)
&& mOnBehalf
&& r.sent_direct == this.sent_direct
&& r.sent_direct_diff == this.sent_direct_diff
&& r.received_direct == this.received_direct
&& r.peer_received_due_to_reco == this.peer_received_due_to_reco
&& r.peer_sent_to_recos == this.peer_sent_to_recos
&& r.timestamp.equals(this.timestamp);
}
return false;
}
public long get_sent_direct() {
return sent_direct;
}
public long get_received_direct() {
return received_direct;
}
public long get_peer_received_due_to_reco() {
return peer_received_due_to_reco;
}
public long get_peer_sent_to_recos() {
return peer_sent_to_recos;
}
public PublicKey getSigningKey() {
return mSigning;
}
public PublicKey getEncodingStateForKey() {
return mEncodingStateFor;
}
public int get_sent_direct_diff() {
return sent_direct_diff;
}
public long getSigningID() throws IOException {
return ReputationDAO.get().get_internal_id(mSigning);
}
public long getEncodingStateForID() throws IOException {
return ReputationDAO.get()
.get_internal_id(mEncodingStateFor);
}
private byte[] get_protected_bytes() throws IOException {
ByteArrayOutputStream summary_bytes = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(summary_bytes);
dos.writeLong(get_sent_direct());
dos.writeLong(get_received_direct());
dos.writeLong(get_peer_received_due_to_reco());
dos.writeLong(get_peer_sent_to_recos());
dos.writeInt(get_sent_direct_diff());
summary_bytes.write(mSigning.getEncoded());
summary_bytes.write(mEncodingStateFor.getEncoded());
ObjectOutputStream oos = new ObjectOutputStream(summary_bytes);
oos.writeObject(timestamp);
oos.writeObject(mOnBehalfOf);
oos.close();
return summary_bytes.toByteArray();
}
public void generate_signature() throws IOException {
byte[] bytes = get_protected_bytes();
try
{
Signature s = Signature.getInstance("SHA1withRSA");
s.initSign(LocalIdentity.get().getKeys().getPrivate());
s.update(bytes);
byte[] sigBytes = s.sign();
mSignature = sigBytes;
} catch( Exception e )
{
System.err.println(e);
e.printStackTrace();
throw new IOException(e.toString());
}
}
private void writeObject( ObjectOutputStream out ) throws IOException {
if (mSignature == null)
generate_signature();
out.defaultWriteObject();
}
private void readObject( ObjectInputStream in ) throws IOException,
ClassNotFoundException {
in.defaultReadObject();
verify_signature();
}
public void verify_signature() throws IOException {
byte[] bytes = get_protected_bytes();
try
{
Signature s = Signature.getInstance("SHA1withRSA");
s.initVerify(mSigning);
s.update(bytes);
if (s.verify(mSignature) == false)
throw new IOException("receipt check failed!");
} catch( Exception e )
{
System.err.println(e);
e.printStackTrace();
throw new IOException(e.toString());
}
}
public Receipt(PublicKey inEncodingStateFor, BloomFilter inOnBehalfOf,
int inDiff, boolean inInflate) throws IOException
{
this(inEncodingStateFor, inOnBehalfOf);
sent_direct_diff = inDiff;
if (inInflate)
{
inflate();
}
}
private void inflate() {
/**
* TODO: magic number This is only used for indirect computation. We
* will rely on our local state for direct interaction
*/
received_direct *= 100;
}
/**
* Constructs a receipt from our perspective for inRemotePeer
*
* @param inRemotePeer
* The peer for which to mint the receipt
*/
public Receipt(PublicKey inEncodingStateFor, BloomFilter inOnBehalfOf)
throws IOException
{
mSigning = LocalIdentity.get().getKeys().getPublic();
mEncodingStateFor = inEncodingStateFor;
mOnBehalfOf = inOnBehalfOf;
ReputationDAO rep = ReputationDAO.get();
sent_direct = rep.get_sent_direct(getEncodingStateForID());
received_direct = rep.get_received_direct(getEncodingStateForID());
peer_received_due_to_reco = rep
.get_others_sent_due_to_my_reco(getEncodingStateForID());
peer_sent_to_recos = rep
.get_others_recv_due_to_my_reco(getEncodingStateForID());
timestamp = new Date();
}
public Receipt(PublicKey inEncodingStateFor, BloomFilter inOnBehalfOf,
boolean inInflate) throws IOException
{
this(inEncodingStateFor, inOnBehalfOf);
if (inInflate)
inflate();
}
/**
* @param args
*/
public static void main( String[] args ) throws Exception {
}
public byte[] getSHA1() {
if (mHash == null)
{
try
{
MessageDigest digest = MessageDigest.getInstance("SHA-1");
mHash = digest.digest(this.get_protected_bytes());
} catch( Exception e )
{
System.err.println("Error generating receipt hash: " + e);
e.printStackTrace();
return null;
}
}
return mHash;
}
public String toString() {
long sid = -1;
long bid = -1;
try
{
sid = ReputationDAO.get().get_internal_id(mSigning);
bid = ReputationDAO.get().get_internal_id(mEncodingStateFor);
} catch( Exception e )
{
e.printStackTrace();
}
return "\nSigner: " + sid + "\nEncoding for: " + bid
+ "\non behalf of: " + mOnBehalfOf + "\nSent:" + "\n\tDirect: "
+ sent_direct + " To recos: " + peer_sent_to_recos
+ "\nReceived: " + "\n\tDirect: " + received_direct
+ " To recos: " + peer_received_due_to_reco + "\nTime: "
+ timestamp.toGMTString() + "\nDiff: " + get_sent_direct_diff();
}
}