001    package edu.harvard.deas.hyperenc;
002    
003    import java.io.Serializable;
004    import java.util.ArrayList;
005    import java.util.Date;
006    import java.util.List;
007    
008    /**
009     * A HyperMessage is a hyper-encrypted message sent from one HyperEncryption
010     * client to another. It encompasses reconciliation and other protocol messages,
011     * as well as actual encrypted messages containing user-specified content.
012     * <p>
013     * A HyperMessage has several abstract fields:
014     * <ul>
015     * <li><i>id</i> - a unique ID number assigned to each HyperMessage</li>
016     * <li><i>sender</i> - the person who sent the message</li>
017     * <li><i>recipient</i> - the person to whom the message is addressed</li>
018     * <li><i>type</i> - what type of message this is: whether it contains user
019     * content, reconciliation info, etc.</li>
020     * <li><i>padsUsed</i> - an ordered list of identifiers that indicate what parts
021     * of the sender and recipient's shared one-time pad were used to encrypt this
022     * message</li>
023     * <li><i>mac</i> - a <code>HyperMAC</code> object representing the HEMAC of
024     * this message</li>
025     * <li><i>content</i> - the contents of the message</li>
026     * <li><i>date</i> - the date of the message; the date at which the message was
027     * sent or received</li>
028     * </ul>
029     * 
030     * HyperMessages are immutable.
031     */
032    public class HyperMessage implements Serializable {
033      private static final long serialVersionUID = 1L;
034      
035      private final int id;
036      private final HyperMessageType type;
037      private final Contact sender;
038      private final Contact recipient;
039      private final String subject;
040      private final String content;
041      private final HyperMAC hemac;
042      private final List<Integer> padsUsed;
043      private final Date date;
044    
045      /**
046       * The ID that will be assigned to the next new HyperMessage. (Should not be
047       * modified directly!)
048       */
049      private static int nextID = 0;
050    
051      /**
052       * Returns the ID that should be assigned to the next new HyperMessage
053       * created. The first time this method is called, it returns 0; every
054       * subsequent time, the return value increases by 1.
055       * 
056       * @return the next ID that should be allocated to a new HyperMessage.
057       */
058      protected static final synchronized int getNextID() {
059        return nextID++;
060      }
061      
062      // This constructor is private so that the class may not be subclassed, thus
063      // enforcing the immutability requirement.
064      private HyperMessage(HyperMessageType type, Contact sender, Contact recipient,
065                           String subject, String content, List<Integer> padsUsed,
066                           HyperMAC hemac, Date date) {
067        // Check validity of arguments
068        if (type == null)
069          throw new NullPointerException("type may not be null");
070        if (sender == null)
071          throw new NullPointerException("sender may not be null");
072        if (recipient == null)
073          throw new NullPointerException("recipient may not be null");
074        if (subject == null)
075          throw new NullPointerException("subject may not be null");
076        if (content == null)
077          throw new NullPointerException("content may not be null");
078        if (date == null)
079          throw new NullPointerException("date may not be null");
080        
081        this.id = getNextID();
082        this.type = type;
083        this.sender = sender;
084        this.recipient = recipient;
085        this.hemac = hemac;
086        this.subject = subject;
087        this.content = content;
088        if (padsUsed == null)
089          this.padsUsed = null;
090        else
091          this.padsUsed = new ArrayList<Integer>(padsUsed);
092        this.date = new Date(date.getTime());
093      }
094    
095      /**
096       * Creates and returns a new HyperMessage. The only arguments that may be
097       * <code>null</code> are <code>padsUsed</code> and <code>hemac</code>. A
098       * <code>null</code> value for <code>padsUsed</code> indicates that the
099       * message content is not encrypted. A <code>null</code> value for
100       * <code>hemac</code> indicates that no authentication information is
101       * included.
102       * <p>
103       * <code>padsUsed</code> is copied before it is stored in this HyperMessage.
104       * 
105       * @param type
106       *        The type of this message.
107       * @param sender
108       *        The sender of this message.
109       * @param recipient
110       *        The recipient of this message.
111       * @param subject
112       *        The subject of this message.
113       * @param content
114       *        The content of this message.
115       * @param padsUsed
116       *        An ordered list of the IDs of the one-time pad blocks that were used
117       *        to encrypt this message.
118       * @param date
119       *        The date of this message.
120       * @throws NullPointerException
121       *         if any argument other than <code>padsUsed</code> is
122       *         <code>null</code>
123       * @return a new HyperMessage with the given parameters
124       */
125      public static HyperMessage getInstance(HyperMessageType type, Contact sender,
126          Contact recipient, String subject, String content,
127          List<Integer> padsUsed, HyperMAC hemac, Date date) {
128        return new HyperMessage(type, sender, recipient, subject, content,
129                                padsUsed, hemac, date);
130      }
131      
132      /**
133       * Returns the unique ID of this message.
134       * @return The unique ID of this message.
135       */
136      public int getID() {
137        return id;
138      }
139      
140      /**
141       * Returns the type of this message.
142       * @return The type of this message.
143       */
144      public HyperMessageType getType() {
145        return type;
146      }
147      
148      /**
149       * Returns the sender of this message.
150       * @return The sender of this message.
151       */
152      public Contact getSender() {
153        return sender;
154      }
155      
156      /**
157       * Returns the recipient of this message.
158       * @return The recipient of this message.
159       */
160      public Contact getRecipient() {
161        return recipient;
162      }
163      
164      /**
165       * Returns the subject of this message.
166       * @return The subject of this message.
167       */
168      public String getSubject() {
169        return subject;
170      }
171      
172      /**
173       * Returns the content of this message.
174       * @return The content of this message.
175       */
176      public String getContent() {
177        return content;
178      }
179    
180      /**
181       * Returns a list of IDs of blocks used to encrypt this message. The list is a
182       * copy; callers are free to mutate the returned list.
183       * @return The IDs of blocks used to encrypt this message.
184       */
185      public List<Integer> getPadsUsed() {
186        if (padsUsed == null)
187          return null;
188        return new ArrayList<Integer>(padsUsed);
189      }
190      
191      /**
192       * Returns the HEMAC for this message.
193       * @return the HEMAC for this message
194       */
195      public HyperMAC getMac() {
196        return hemac;
197      }
198      
199      /**
200       * Returns the date of this message.
201       * @return The date of this message.
202       */
203      public Date getDate() {
204        return new Date(date.getTime());
205      }
206    
207      /**
208       * Compares this HyperMessage to the specified object. The result is
209       * <code>true</code> if and only if the argument is not <code>null</code> and
210       * is a HyperMessage object with the same ID as this HyperMessage.
211       */
212      @Override
213      public boolean equals(Object o) {
214        if (o == null || !(o instanceof HyperMessage)) {
215          return false;
216        }
217        
218        HyperMessage hm = (HyperMessage)o;
219        return this.id == hm.getID();
220      }
221      
222      @Override
223      public int hashCode() {
224        return id;
225      }
226    }