src/net/java/otr4j/io/SerializationUtils.java
changeset 810 0ff0059f2ec3
child 895 b2e1b45382a4
equal deleted inserted replaced
797:fbd3585af53e 810:0ff0059f2ec3
       
     1 /*
       
     2  * otr4j, the open source java otr library.
       
     3  *
       
     4  * Distributable under LGPL license.
       
     5  * See terms of license at gnu.org.
       
     6  */
       
     7 package net.java.otr4j.io;
       
     8 
       
     9 import java.io.ByteArrayInputStream;
       
    10 import java.io.ByteArrayOutputStream;
       
    11 import java.io.IOException;
       
    12 import java.io.StringReader;
       
    13 import java.io.StringWriter;
       
    14 import java.math.BigInteger;
       
    15 import java.security.PublicKey;
       
    16 import java.util.List;
       
    17 import java.util.Vector;
       
    18 import java.util.regex.Matcher;
       
    19 import java.util.regex.Pattern;
       
    20 
       
    21 import javax.crypto.interfaces.DHPublicKey;
       
    22 
       
    23 import org.bouncycastle.util.encoders.Base64;
       
    24 
       
    25 import net.java.otr4j.io.messages.AbstractEncodedMessage;
       
    26 import net.java.otr4j.io.messages.AbstractMessage;
       
    27 import net.java.otr4j.io.messages.DHCommitMessage;
       
    28 import net.java.otr4j.io.messages.DHKeyMessage;
       
    29 import net.java.otr4j.io.messages.DataMessage;
       
    30 import net.java.otr4j.io.messages.ErrorMessage;
       
    31 import net.java.otr4j.io.messages.MysteriousT;
       
    32 import net.java.otr4j.io.messages.PlainTextMessage;
       
    33 import net.java.otr4j.io.messages.QueryMessage;
       
    34 import net.java.otr4j.io.messages.RevealSignatureMessage;
       
    35 import net.java.otr4j.io.messages.SignatureM;
       
    36 import net.java.otr4j.io.messages.SignatureMessage;
       
    37 import net.java.otr4j.io.messages.SignatureX;
       
    38 
       
    39 /**
       
    40  * 
       
    41  * @author George Politis
       
    42  */
       
    43 public class SerializationUtils {
       
    44 	// Mysterious X IO.
       
    45 	public static SignatureX toMysteriousX(byte[] b) throws IOException {
       
    46 		ByteArrayInputStream in = new ByteArrayInputStream(b);
       
    47 		OtrInputStream ois = new OtrInputStream(in);
       
    48 		SignatureX x = ois.readMysteriousX();
       
    49 		ois.close();
       
    50 		return x;
       
    51 	}
       
    52 
       
    53 	public static byte[] toByteArray(SignatureX x) throws IOException {
       
    54 		ByteArrayOutputStream out = new ByteArrayOutputStream();
       
    55 		OtrOutputStream oos = new OtrOutputStream(out);
       
    56 		oos.writeMysteriousX(x);
       
    57 		byte[] b = out.toByteArray();
       
    58 		oos.close();
       
    59 		return b;
       
    60 	}
       
    61 
       
    62 	// Mysterious M IO.
       
    63 	public static byte[] toByteArray(SignatureM m) throws IOException {
       
    64 		ByteArrayOutputStream out = new ByteArrayOutputStream();
       
    65 		OtrOutputStream oos = new OtrOutputStream(out);
       
    66 		oos.writeMysteriousX(m);
       
    67 		byte[] b = out.toByteArray();
       
    68 		oos.close();
       
    69 		return b;
       
    70 	}
       
    71 
       
    72 	// Mysterious T IO.
       
    73 	public static byte[] toByteArray(MysteriousT t) throws IOException {
       
    74 		ByteArrayOutputStream out = new ByteArrayOutputStream();
       
    75 		OtrOutputStream oos = new OtrOutputStream(out);
       
    76 		oos.writeMysteriousT(t);
       
    77 		byte[] b = out.toByteArray();
       
    78 		out.close();
       
    79 		return b;
       
    80 	}
       
    81 
       
    82 	// Basic IO.
       
    83 	public static byte[] writeData(byte[] b) throws IOException {
       
    84 		ByteArrayOutputStream out = new ByteArrayOutputStream();
       
    85 		OtrOutputStream oos = new OtrOutputStream(out);
       
    86 		oos.writeData(b);
       
    87 		byte[] otrb = out.toByteArray();
       
    88 		out.close();
       
    89 		return otrb;
       
    90 	}
       
    91 
       
    92 	// BigInteger IO.
       
    93 	public static byte[] writeMpi(BigInteger bigInt) throws IOException {
       
    94 		ByteArrayOutputStream out = new ByteArrayOutputStream();
       
    95 		OtrOutputStream oos = new OtrOutputStream(out);
       
    96 		oos.writeBigInt(bigInt);
       
    97 		byte[] b = out.toByteArray();
       
    98 		oos.close();
       
    99 		return b;
       
   100 	}
       
   101 
       
   102 	public static BigInteger readMpi(byte[] b) throws IOException {
       
   103 		ByteArrayInputStream in = new ByteArrayInputStream(b);
       
   104 		OtrInputStream ois = new OtrInputStream(in);
       
   105 		BigInteger bigint = ois.readBigInt();
       
   106 		ois.close();
       
   107 		return bigint;
       
   108 	}
       
   109 
       
   110 	// Public Key IO.
       
   111 	public static byte[] writePublicKey(PublicKey pubKey) throws IOException {
       
   112 		ByteArrayOutputStream out = new ByteArrayOutputStream();
       
   113 		OtrOutputStream oos = new OtrOutputStream(out);
       
   114 		oos.writePublicKey(pubKey);
       
   115 		byte[] b = out.toByteArray();
       
   116 		oos.close();
       
   117 		return b;
       
   118 	}
       
   119 
       
   120 	// Message IO.
       
   121 	public static String toString(AbstractMessage m) throws IOException {
       
   122 		StringWriter writer = new StringWriter();
       
   123 		writer.write(SerializationConstants.HEAD);
       
   124 
       
   125 		switch (m.messageType) {
       
   126 		case AbstractMessage.MESSAGE_ERROR:
       
   127 			ErrorMessage error = (ErrorMessage) m;
       
   128 			writer.write(SerializationConstants.HEAD_ERROR);
       
   129 			writer.write(error.error);
       
   130 			break;
       
   131 		case AbstractMessage.MESSAGE_PLAINTEXT:
       
   132 			PlainTextMessage plaintxt = (PlainTextMessage) m;
       
   133 			writer.write(plaintxt.cleanText);
       
   134 			if (plaintxt.versions != null && plaintxt.versions.size() > 0) {
       
   135 				writer.write(" \\t  \\t\\t\\t\\t \\t \\t \\t  ");
       
   136 				for (int version : plaintxt.versions) {
       
   137 					if (version == 1)
       
   138 						writer.write("  \\t\\t  \\t ");
       
   139 
       
   140 					if (version == 2)
       
   141 						writer.write(" \\t \\t  \\t ");
       
   142 				}
       
   143 			}
       
   144 			break;
       
   145 		case AbstractMessage.MESSAGE_QUERY:
       
   146 			QueryMessage query = (QueryMessage) m;
       
   147 			if (query.versions.size() == 1 && query.versions.get(0) == 1) {
       
   148 				writer.write(SerializationConstants.HEAD_QUERY_Q);
       
   149 			} else {
       
   150 				writer.write(SerializationConstants.HEAD_QUERY_V);
       
   151 				for (int version : query.versions)
       
   152 					writer.write(String.valueOf(version));
       
   153 
       
   154 				writer.write(SerializationConstants.HEAD_QUERY_Q);
       
   155 			}
       
   156 			break;
       
   157 		case AbstractEncodedMessage.MESSAGE_DHKEY:
       
   158 		case AbstractEncodedMessage.MESSAGE_REVEALSIG:
       
   159 		case AbstractEncodedMessage.MESSAGE_SIGNATURE:
       
   160 		case AbstractEncodedMessage.MESSAGE_DH_COMMIT:
       
   161 		case AbstractEncodedMessage.MESSAGE_DATA:
       
   162 			ByteArrayOutputStream o = new ByteArrayOutputStream();
       
   163 			OtrOutputStream s = new OtrOutputStream(o);
       
   164 
       
   165 			switch (m.messageType) {
       
   166 			case AbstractEncodedMessage.MESSAGE_DHKEY:
       
   167 				DHKeyMessage dhkey = (DHKeyMessage) m;
       
   168 				s.writeShort(dhkey.protocolVersion);
       
   169 				s.writeByte(dhkey.messageType);
       
   170 				s.writeDHPublicKey(dhkey.dhPublicKey);
       
   171 				break;
       
   172 			case AbstractEncodedMessage.MESSAGE_REVEALSIG:
       
   173 				RevealSignatureMessage revealsig = (RevealSignatureMessage) m;
       
   174 				s.writeShort(revealsig.protocolVersion);
       
   175 				s.writeByte(revealsig.messageType);
       
   176 				s.writeData(revealsig.revealedKey);
       
   177 				s.writeData(revealsig.xEncrypted);
       
   178 				s.writeMac(revealsig.xEncryptedMAC);
       
   179 				break;
       
   180 			case AbstractEncodedMessage.MESSAGE_SIGNATURE:
       
   181 				SignatureMessage sig = (SignatureMessage) m;
       
   182 				s.writeShort(sig.protocolVersion);
       
   183 				s.writeByte(sig.messageType);
       
   184 				s.writeData(sig.xEncrypted);
       
   185 				s.writeMac(sig.xEncryptedMAC);
       
   186 				break;
       
   187 			case AbstractEncodedMessage.MESSAGE_DH_COMMIT:
       
   188 				DHCommitMessage dhcommit = (DHCommitMessage) m;
       
   189 				s.writeShort(dhcommit.protocolVersion);
       
   190 				s.writeByte(dhcommit.messageType);
       
   191 				s.writeData(dhcommit.dhPublicKeyEncrypted);
       
   192 				s.writeData(dhcommit.dhPublicKeyHash);
       
   193 				break;
       
   194 			case AbstractEncodedMessage.MESSAGE_DATA:
       
   195 				DataMessage data = (DataMessage) m;
       
   196 				s.writeShort(data.protocolVersion);
       
   197 				s.writeByte(data.messageType);
       
   198 				s.writeByte(data.flags);
       
   199 				s.writeInt(data.senderKeyID);
       
   200 				s.writeInt(data.recipientKeyID);
       
   201 				s.writeDHPublicKey(data.nextDH);
       
   202 				s.writeCtr(data.ctr);
       
   203 				s.writeData(data.encryptedMessage);
       
   204 				s.writeMac(data.mac);
       
   205 				s.writeData(data.oldMACKeys);
       
   206 				break;
       
   207 			}
       
   208 
       
   209 			writer.write(SerializationConstants.HEAD_ENCODED);
       
   210 			writer.write(new String(Base64.encode(o.toByteArray())));
       
   211 			writer.write(".");
       
   212 			break;
       
   213 		default:
       
   214 			throw new IOException("Illegal message type.");
       
   215 		}
       
   216 
       
   217 		return writer.toString();
       
   218 	}
       
   219 
       
   220 	static final Pattern patternWhitespace = Pattern
       
   221 			.compile("( \\t  \\t\\t\\t\\t \\t \\t \\t  )(  \\t\\t  \\t )?( \\t \\t  \\t )?");
       
   222 
       
   223 	public static AbstractMessage toMessage(String s) throws IOException {
       
   224 		if (s == null || s.length() <= 1)
       
   225 			return null;
       
   226 
       
   227 		if (s.indexOf(SerializationConstants.HEAD) != 0
       
   228 				|| s.length() <= SerializationConstants.HEAD.length()) {
       
   229 			// Try to detect whitespace tag.
       
   230 			final Matcher matcher = patternWhitespace.matcher(s);
       
   231 
       
   232 			boolean v1 = false;
       
   233 			boolean v2 = false;
       
   234 			while (matcher.find()) {
       
   235 				if (!v1 && matcher.start(2) > -1)
       
   236 					v1 = true;
       
   237 
       
   238 				if (!v2 && matcher.start(3) > -1)
       
   239 					v2 = true;
       
   240 
       
   241 				if (v1 && v2)
       
   242 					break;
       
   243 			}
       
   244 
       
   245 			String cleanText = matcher.replaceAll("");
       
   246 			List<Integer> versions;
       
   247 			if (v1 && v2) {
       
   248 				versions = new Vector<Integer>(2);
       
   249 				versions.add(0, 1);
       
   250 				versions.add(0, 2);
       
   251 			} else if (v1) {
       
   252 				versions = new Vector<Integer>(1);
       
   253 				versions.add(0, 1);
       
   254 			} else if (v2) {
       
   255 				versions = new Vector<Integer>(1);
       
   256 				versions.add(2);
       
   257 			} else
       
   258 				versions = null;
       
   259 
       
   260 			return new PlainTextMessage(versions, cleanText);
       
   261 		} else {
       
   262 			char contentType = s.charAt(SerializationConstants.HEAD.length());
       
   263 			String content = s
       
   264 					.substring(SerializationConstants.HEAD.length() + 1);
       
   265 			switch (contentType) {
       
   266 			case SerializationConstants.HEAD_ENCODED:
       
   267 				ByteArrayInputStream bin = new ByteArrayInputStream(Base64
       
   268 						.decode(content.getBytes()));
       
   269 				OtrInputStream otr = new OtrInputStream(bin);
       
   270 				// We have an encoded message.
       
   271 				int protocolVersion = otr.readShort();
       
   272 				int messageType = otr.readByte();
       
   273 				switch (messageType) {
       
   274 				case AbstractEncodedMessage.MESSAGE_DATA:
       
   275 					int flags = otr.readByte();
       
   276 					int senderKeyID = otr.readInt();
       
   277 					int recipientKeyID = otr.readInt();
       
   278 					DHPublicKey nextDH = otr.readDHPublicKey();
       
   279 					byte[] ctr = otr.readCtr();
       
   280 					byte[] encryptedMessage = otr.readData();
       
   281 					byte[] mac = otr.readMac();
       
   282 					byte[] oldMacKeys = otr.readMac();
       
   283 					return new DataMessage(protocolVersion, flags, senderKeyID,
       
   284 							recipientKeyID, nextDH, ctr, encryptedMessage, mac,
       
   285 							oldMacKeys);
       
   286 				case AbstractEncodedMessage.MESSAGE_DH_COMMIT:
       
   287 					byte[] dhPublicKeyEncrypted = otr.readData();
       
   288 					byte[] dhPublicKeyHash = otr.readData();
       
   289 					return new DHCommitMessage(protocolVersion,
       
   290 							dhPublicKeyHash, dhPublicKeyEncrypted);
       
   291 				case AbstractEncodedMessage.MESSAGE_DHKEY:
       
   292 					DHPublicKey dhPublicKey = otr.readDHPublicKey();
       
   293 					return new DHKeyMessage(protocolVersion, dhPublicKey);
       
   294 				case AbstractEncodedMessage.MESSAGE_REVEALSIG: {
       
   295 					byte[] revealedKey = otr.readData();
       
   296 					byte[] xEncrypted = otr.readData();
       
   297 					byte[] xEncryptedMac = otr.readMac();
       
   298 					return new RevealSignatureMessage(protocolVersion,
       
   299 							xEncrypted, xEncryptedMac, revealedKey);
       
   300 				}
       
   301 				case AbstractEncodedMessage.MESSAGE_SIGNATURE: {
       
   302 					byte[] xEncryted = otr.readData();
       
   303 					byte[] xEncryptedMac = otr.readMac();
       
   304 					return new SignatureMessage(protocolVersion, xEncryted,
       
   305 							xEncryptedMac);
       
   306 				}
       
   307 				default:
       
   308 					throw new IOException("Illegal message type.");
       
   309 				}
       
   310 			case SerializationConstants.HEAD_ERROR:
       
   311 				return new ErrorMessage(AbstractMessage.MESSAGE_ERROR, content);
       
   312 			case SerializationConstants.HEAD_QUERY_V:
       
   313 			case SerializationConstants.HEAD_QUERY_Q:
       
   314 				List<Integer> versions = new Vector<Integer>();
       
   315 				String versionString = null;
       
   316 				if (SerializationConstants.HEAD_QUERY_Q == contentType) {
       
   317 					versions.add(1);
       
   318 					if (content.charAt(0) == 'v') {
       
   319 						versionString = content.substring(1, content
       
   320 								.indexOf('?'));
       
   321 					}
       
   322 				} else if (SerializationConstants.HEAD_QUERY_V == contentType) {
       
   323 					versionString = content.substring(0, content.indexOf('?'));
       
   324 				}
       
   325 
       
   326 				if (versionString != null) {
       
   327 					StringReader sr = new StringReader(versionString);
       
   328 					int c;
       
   329 					while ((c = sr.read()) != -1)
       
   330 						if (!versions.contains(c))
       
   331 							versions.add(Integer.parseInt(String
       
   332 									.valueOf((char) c)));
       
   333 				}
       
   334 				QueryMessage query = new QueryMessage(versions);
       
   335 				return query;
       
   336 			default:
       
   337 				throw new IOException("Uknown message type.");
       
   338 			}
       
   339 		}
       
   340 	}
       
   341 }