Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/quite/humla-spongycastle.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'core/src/main/j2me/org/spongycastle/crypto')
-rw-r--r--core/src/main/j2me/org/spongycastle/crypto/encodings/PKCS1Encoding.java410
-rw-r--r--core/src/main/j2me/org/spongycastle/crypto/examples/MIDPTest.java177
-rw-r--r--core/src/main/j2me/org/spongycastle/crypto/examples/midp_test.jad6
-rw-r--r--core/src/main/j2me/org/spongycastle/crypto/examples/midp_test.mf7
-rw-r--r--core/src/main/j2me/org/spongycastle/crypto/params/SkeinParameters.java258
-rw-r--r--core/src/main/j2me/org/spongycastle/crypto/tls/OCSPStatusRequest.java131
-rw-r--r--core/src/main/j2me/org/spongycastle/crypto/tls/ServerNameList.java86
-rw-r--r--core/src/main/j2me/org/spongycastle/crypto/tls/UDPTransport.java107
8 files changed, 1182 insertions, 0 deletions
diff --git a/core/src/main/j2me/org/spongycastle/crypto/encodings/PKCS1Encoding.java b/core/src/main/j2me/org/spongycastle/crypto/encodings/PKCS1Encoding.java
new file mode 100644
index 00000000..7af31f36
--- /dev/null
+++ b/core/src/main/j2me/org/spongycastle/crypto/encodings/PKCS1Encoding.java
@@ -0,0 +1,410 @@
+package org.spongycastle.crypto.encodings;
+
+import java.security.SecureRandom;
+
+import org.spongycastle.crypto.AsymmetricBlockCipher;
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.InvalidCipherTextException;
+import org.spongycastle.crypto.params.AsymmetricKeyParameter;
+import org.spongycastle.crypto.params.ParametersWithRandom;
+
+/**
+ * this does your basic PKCS 1 v1.5 padding - whether or not you should be using this
+ * depends on your application - see PKCS1 Version 2 for details.
+ */
+public class PKCS1Encoding
+ implements AsymmetricBlockCipher
+{
+ /**
+ * some providers fail to include the leading zero in PKCS1 encoded blocks. If you need to
+ * work with one of these set the system property org.spongycastle.pkcs1.strict to false.
+ * <p>
+ * The system property is checked during construction of the encoding object, it is set to
+ * true by default.
+ * </p>
+ */
+ public static final String STRICT_LENGTH_ENABLED_PROPERTY = "org.spongycastle.pkcs1.strict";
+
+ private static final int HEADER_LENGTH = 10;
+
+ private SecureRandom random;
+ private AsymmetricBlockCipher engine;
+ private boolean forEncryption;
+ private boolean forPrivateKey;
+ private boolean useStrictLength;
+ private int pLen = -1;
+ private byte[] fallback = null;
+
+ /**
+ * Basic constructor.
+ * @param cipher
+ */
+ public PKCS1Encoding(
+ AsymmetricBlockCipher cipher)
+ {
+ this.engine = cipher;
+ this.useStrictLength = useStrict();
+ }
+
+ /**
+ * Constructor for decryption with a fixed plaintext length.
+ *
+ * @param cipher The cipher to use for cryptographic operation.
+ * @param pLen Length of the expected plaintext.
+ */
+ public PKCS1Encoding(
+ AsymmetricBlockCipher cipher,
+ int pLen)
+ {
+ this.engine = cipher;
+ this.useStrictLength = useStrict();
+ this.pLen = pLen;
+ }
+
+ /**
+ * Constructor for decryption with a fixed plaintext length and a fallback
+ * value that is returned, if the padding is incorrect.
+ *
+ * @param cipher
+ * The cipher to use for cryptographic operation.
+ * @param fallback
+ * The fallback value, we don't to a arraycopy here.
+ */
+ public PKCS1Encoding(
+ AsymmetricBlockCipher cipher,
+ byte[] fallback)
+ {
+ this.engine = cipher;
+ this.useStrictLength = useStrict();
+ this.fallback = fallback;
+ this.pLen = fallback.length;
+ }
+
+
+
+ //
+ // for J2ME compatibility
+ //
+ private boolean useStrict()
+ {
+ // required if security manager has been installed.
+ String strict = System.getProperty(STRICT_LENGTH_ENABLED_PROPERTY);
+
+ return strict == null || strict.equals("true");
+ }
+
+ public AsymmetricBlockCipher getUnderlyingCipher()
+ {
+ return engine;
+ }
+
+ public void init(
+ boolean forEncryption,
+ CipherParameters param)
+ {
+ AsymmetricKeyParameter kParam;
+
+ if (param instanceof ParametersWithRandom)
+ {
+ ParametersWithRandom rParam = (ParametersWithRandom)param;
+
+ this.random = rParam.getRandom();
+ kParam = (AsymmetricKeyParameter)rParam.getParameters();
+ }
+ else
+ {
+ this.random = new SecureRandom();
+ kParam = (AsymmetricKeyParameter)param;
+ }
+
+ engine.init(forEncryption, param);
+
+ this.forPrivateKey = kParam.isPrivate();
+ this.forEncryption = forEncryption;
+ }
+
+ public int getInputBlockSize()
+ {
+ int baseBlockSize = engine.getInputBlockSize();
+
+ if (forEncryption)
+ {
+ return baseBlockSize - HEADER_LENGTH;
+ }
+ else
+ {
+ return baseBlockSize;
+ }
+ }
+
+ public int getOutputBlockSize()
+ {
+ int baseBlockSize = engine.getOutputBlockSize();
+
+ if (forEncryption)
+ {
+ return baseBlockSize;
+ }
+ else
+ {
+ return baseBlockSize - HEADER_LENGTH;
+ }
+ }
+
+ public byte[] processBlock(
+ byte[] in,
+ int inOff,
+ int inLen)
+ throws InvalidCipherTextException
+ {
+ if (forEncryption)
+ {
+ return encodeBlock(in, inOff, inLen);
+ }
+ else
+ {
+ return decodeBlock(in, inOff, inLen);
+ }
+ }
+
+ private byte[] encodeBlock(
+ byte[] in,
+ int inOff,
+ int inLen)
+ throws InvalidCipherTextException
+ {
+ if (inLen > getInputBlockSize())
+ {
+ throw new IllegalArgumentException("input data too large");
+ }
+
+ byte[] block = new byte[engine.getInputBlockSize()];
+
+ if (forPrivateKey)
+ {
+ block[0] = 0x01; // type code 1
+
+ for (int i = 1; i != block.length - inLen - 1; i++)
+ {
+ block[i] = (byte)0xFF;
+ }
+ }
+ else
+ {
+ random.nextBytes(block); // random fill
+
+ block[0] = 0x02; // type code 2
+
+ //
+ // a zero byte marks the end of the padding, so all
+ // the pad bytes must be non-zero.
+ //
+ for (int i = 1; i != block.length - inLen - 1; i++)
+ {
+ while (block[i] == 0)
+ {
+ block[i] = (byte)random.nextInt();
+ }
+ }
+ }
+
+ block[block.length - inLen - 1] = 0x00; // mark the end of the padding
+ System.arraycopy(in, inOff, block, block.length - inLen, inLen);
+
+ return engine.processBlock(block, 0, block.length);
+ }
+
+ /**
+ * Checks if the argument is a correctly PKCS#1.5 encoded Plaintext
+ * for encryption.
+ *
+ * @param encoded The Plaintext.
+ * @param pLen Expected length of the plaintext.
+ * @return Either 0, if the encoding is correct, or -1, if it is incorrect.
+ */
+ private static int checkPkcs1Encoding(byte[] encoded, int pLen) {
+ int correct = 0;
+ /*
+ * Check if the first two bytes are 0 2
+ */
+ correct |= (encoded[0] ^ 2);
+
+ /*
+ * Now the padding check, check for no 0 byte in the padding
+ */
+ int plen = encoded.length - (
+ pLen /* Lenght of the PMS */
+ + 1 /* Final 0-byte before PMS */
+ );
+
+ for (int i = 1; i < plen; i++) {
+ int tmp = encoded[i];
+ tmp |= tmp >> 1;
+ tmp |= tmp >> 2;
+ tmp |= tmp >> 4;
+ correct |= (tmp & 1) - 1;
+ }
+
+ /*
+ * Make sure the padding ends with a 0 byte.
+ */
+ correct |= encoded[encoded.length - (pLen +1)];
+
+ /*
+ * Return 0 or 1, depending on the result.
+ */
+ correct |= correct >> 1;
+ correct |= correct >> 2;
+ correct |= correct >> 4;
+ return ~((correct & 1) - 1);
+ }
+
+
+ /**
+ * Decode PKCS#1.5 encoding, and return a random value if the padding is not correct.
+ *
+ * @param in The encrypted block.
+ * @param inOff Offset in the encrypted block.
+ * @param inLen Length of the encrypted block.
+ * //@param pLen Length of the desired output.
+ * @return The plaintext without padding, or a random value if the padding was incorrect.
+ *
+ * @throws InvalidCipherTextException
+ */
+ private byte[] decodeBlockOrRandom(byte[] in, int inOff, int inLen)
+ throws InvalidCipherTextException
+ {
+ if (!forPrivateKey)
+ {
+ throw new InvalidCipherTextException("sorry, this method is only for decryption, not for signing");
+ }
+
+ byte[] block = engine.processBlock(in, inOff, inLen);
+ byte[] random = null;
+ if (this.fallback == null)
+ {
+ random = new byte[this.pLen];
+ this.random.nextBytes(random);
+ }
+ else
+ {
+ random = fallback;
+ }
+
+ /*
+ * TODO: This is a potential dangerous side channel. However, you can
+ * fix this by changing the RSA engine in a way, that it will always
+ * return blocks of the same length and prepend them with 0 bytes if
+ * needed.
+ */
+ if (block.length < getOutputBlockSize())
+ {
+ throw new InvalidCipherTextException("block truncated");
+ }
+
+ /*
+ * TODO: Potential side channel. Fix it by making the engine always
+ * return blocks of the correct length.
+ */
+ if (useStrictLength && block.length != engine.getOutputBlockSize())
+ {
+ throw new InvalidCipherTextException("block incorrect size");
+ }
+
+ /*
+ * Check the padding.
+ */
+ int correct = PKCS1Encoding.checkPkcs1Encoding(block, this.pLen);
+
+ /*
+ * Now, to a constant time constant memory copy of the decrypted value
+ * or the random value, depending on the validity of the padding.
+ */
+ byte[] result = new byte[this.pLen];
+ for (int i = 0; i < this.pLen; i++)
+ {
+ result[i] = (byte)((block[i + (block.length - pLen)] & (~correct)) | (random[i] & correct));
+ }
+
+ return result;
+ }
+
+ /**
+ * @exception InvalidCipherTextException if the decrypted block is not in PKCS1 format.
+ */
+ private byte[] decodeBlock(
+ byte[] in,
+ int inOff,
+ int inLen)
+ throws InvalidCipherTextException
+ {
+ /*
+ * If the length of the expected plaintext is known, we use a constant-time decryption.
+ * If the decryption fails, we return a random value.
+ */
+ if (this.pLen != -1) {
+ return this.decodeBlockOrRandom(in, inOff, inLen);
+ }
+
+ byte[] block = engine.processBlock(in, inOff, inLen);
+
+ if (block.length < getOutputBlockSize())
+ {
+ throw new InvalidCipherTextException("block truncated");
+ }
+
+ byte type = block[0];
+
+ if (forPrivateKey)
+ {
+ if (type != 2)
+ {
+ throw new InvalidCipherTextException("unknown block type");
+ }
+ }
+ else
+ {
+ if (type != 1)
+ {
+ throw new InvalidCipherTextException("unknown block type");
+ }
+ }
+
+ if (useStrictLength && block.length != engine.getOutputBlockSize())
+ {
+ throw new InvalidCipherTextException("block incorrect size");
+ }
+
+ //
+ // find and extract the message block.
+ //
+ int start;
+
+ for (start = 1; start != block.length; start++)
+ {
+ byte pad = block[start];
+
+ if (pad == 0)
+ {
+ break;
+ }
+ if (type == 1 && pad != (byte)0xff)
+ {
+ throw new InvalidCipherTextException("block padding incorrect");
+ }
+ }
+
+ start++; // data should start at the next byte
+
+ if (start > block.length || start < HEADER_LENGTH)
+ {
+ throw new InvalidCipherTextException("no data in block");
+ }
+
+ byte[] result = new byte[block.length - start];
+
+ System.arraycopy(block, start, result, 0, result.length);
+
+ return result;
+ }
+}
diff --git a/core/src/main/j2me/org/spongycastle/crypto/examples/MIDPTest.java b/core/src/main/j2me/org/spongycastle/crypto/examples/MIDPTest.java
new file mode 100644
index 00000000..32908a67
--- /dev/null
+++ b/core/src/main/j2me/org/spongycastle/crypto/examples/MIDPTest.java
@@ -0,0 +1,177 @@
+package org.spongycastle.crypto.examples;
+
+import java.io.*;
+import java.lang.*;
+
+import javax.microedition.midlet.MIDlet;
+import javax.microedition.lcdui.*;
+
+import org.spongycastle.util.test.*;
+import org.spongycastle.util.encoders.*;
+
+import org.spongycastle.crypto.*;
+import org.spongycastle.crypto.paddings.*;
+import org.spongycastle.crypto.engines.*;
+import org.spongycastle.crypto.modes.*;
+import org.spongycastle.crypto.params.*;
+
+/**
+ * MIDP is a simple graphics application for the J2ME CLDC/MIDP.
+ *
+ * It has hardcoded values for the key and plain text. It also performs the
+ * standard testing for the chosen cipher, and displays the results.
+ *
+ * This example shows how to use the light-weight API and a symmetric cipher.
+ *
+ */
+public class MIDPTest extends MIDlet
+{
+ private Display d = null;
+
+ private boolean doneEncrypt = false;
+
+ private String key = "0123456789abcdef0123456789abcdef";
+ private String plainText = "www.bouncycastle.org";
+ private byte[] keyBytes = null;
+ private byte[] cipherText = null;
+ private BufferedBlockCipher cipher = null;
+
+ private String[] cipherNames = {"DES", "DESede", "IDEA", "Rijndael", "Twofish"};
+
+ private Form output = null;
+
+ public void startApp()
+ {
+ Display.getDisplay(this).setCurrent(output);
+ }
+
+ public void pauseApp()
+ {
+
+ }
+
+ public void destroyApp(boolean unconditional)
+ {
+
+ }
+
+ public MIDPTest()
+ {
+ output = new Form("BouncyCastle");
+ output.append("Key: " + key.substring(0, 7) + "...\n");
+ output.append("In : " + plainText.substring(0, 7) + "...\n");
+
+ cipherText = performEncrypt(Hex.decode(key.getBytes()), plainText);
+ String ctS = new String(Hex.encode(cipherText));
+
+ output.append("\nCT : " + ctS.substring(0, 7) + "...\n");
+
+ String decryptText = performDecrypt(Hex.decode(key.getBytes()), cipherText);
+
+ output.append("PT : " + decryptText.substring(0, 7) + "...\n");
+
+ if (decryptText.compareTo(plainText) == 0)
+ {
+ output.append("Success");
+ }
+ else
+ {
+ output.append("Failure");
+ message("[" + plainText + "]");
+ message("[" + decryptText + "]");
+ }
+
+ }
+
+ private byte[] performEncrypt(byte[] key, String plainText)
+ {
+ byte[] ptBytes = plainText.getBytes();
+
+ cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(getEngineInstance()));
+
+ String name = cipher.getUnderlyingCipher().getAlgorithmName();
+ message("Using " + name);
+
+ cipher.init(true, new KeyParameter(key));
+
+ byte[] rv = new byte[cipher.getOutputSize(ptBytes.length)];
+
+ int oLen = cipher.processBytes(ptBytes, 0, ptBytes.length, rv, 0);
+ try
+ {
+ cipher.doFinal(rv, oLen);
+ }
+ catch (CryptoException ce)
+ {
+ message("Ooops, encrypt exception");
+ status(ce.toString());
+ }
+ return rv;
+ }
+
+ private String performDecrypt(byte[] key, byte[] cipherText)
+ {
+ cipher.init(false, new KeyParameter(key));
+
+ byte[] rv = new byte[cipher.getOutputSize(cipherText.length)];
+
+ int oLen = cipher.processBytes(cipherText, 0, cipherText.length, rv, 0);
+ try
+ {
+ cipher.doFinal(rv, oLen);
+ }
+ catch (CryptoException ce)
+ {
+ message("Ooops, decrypt exception");
+ status(ce.toString());
+ }
+ return new String(rv).trim();
+ }
+
+ private int whichCipher()
+ {
+ return 4; // DES
+ }
+
+ private BlockCipher getEngineInstance()
+ {
+ // returns a block cipher according to the current
+ // state of the radio button lists. This is only
+ // done prior to encryption.
+ BlockCipher rv = null;
+
+ switch (whichCipher())
+ {
+ case 0 :
+ rv = new DESEngine();
+ break;
+ case 1 :
+ rv = new DESedeEngine();
+ break;
+ case 2 :
+ rv = new IDEAEngine();
+ break;
+ case 3 :
+ rv = new RijndaelEngine();
+ break;
+ case 4 :
+ rv = new TwofishEngine();
+ break;
+ default :
+ rv = new DESEngine();
+ break;
+ }
+ return rv;
+ }
+
+ public void message(String s)
+ {
+ System.out.println("M:" + s);
+ }
+
+ public void status(String s)
+ {
+ System.out.println("S:" + s);
+ }
+
+}
diff --git a/core/src/main/j2me/org/spongycastle/crypto/examples/midp_test.jad b/core/src/main/j2me/org/spongycastle/crypto/examples/midp_test.jad
new file mode 100644
index 00000000..584f3156
--- /dev/null
+++ b/core/src/main/j2me/org/spongycastle/crypto/examples/midp_test.jad
@@ -0,0 +1,6 @@
+MIDlet-1: MIDPTest, , org.spongycastle.crypto.examples.MIDPTest
+MIDlet-Name: MIDPTest
+MIDlet-Jar-Size: 300000
+MIDlet-Jar-URL: midp_test.jar
+MIDlet-Vendor: The Legion of the Bouncy Castle
+MIDlet-Version: 1.0.0
diff --git a/core/src/main/j2me/org/spongycastle/crypto/examples/midp_test.mf b/core/src/main/j2me/org/spongycastle/crypto/examples/midp_test.mf
new file mode 100644
index 00000000..27352109
--- /dev/null
+++ b/core/src/main/j2me/org/spongycastle/crypto/examples/midp_test.mf
@@ -0,0 +1,7 @@
+MIDlet-1: MIDPTTest, , org.spongycastle.crypto.examples.MIDPTest
+MIDlet-Name: MIDPTest
+MIDlet-Version: 1.0.0
+MIDlet-Vendor: Jon Eaves
+Created-By: 1.3.1 (Sun Microsystems Inc.)
+MicroEdition-Configuration: CLDC-1.0
+MicroEdition-Profile: MIDP-1.0
diff --git a/core/src/main/j2me/org/spongycastle/crypto/params/SkeinParameters.java b/core/src/main/j2me/org/spongycastle/crypto/params/SkeinParameters.java
new file mode 100644
index 00000000..b29cd441
--- /dev/null
+++ b/core/src/main/j2me/org/spongycastle/crypto/params/SkeinParameters.java
@@ -0,0 +1,258 @@
+package org.spongycastle.crypto.params;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.digests.SkeinDigest;
+import org.spongycastle.crypto.digests.SkeinEngine;
+import org.spongycastle.crypto.macs.SkeinMac;
+import org.spongycastle.util.Integers;
+
+/**
+ * Parameters for the Skein hash function - a series of byte[] strings identified by integer tags.
+ * <p/>
+ * Parameterised Skein can be used for:
+ * <ul>
+ * <li>MAC generation, by providing a {@link SkeinParameters.Builder#setKey(byte[]) key}.</li>
+ * <li>Randomised hashing, by providing a {@link SkeinParameters.Builder#setNonce(byte[]) nonce}.</li>
+ * <li>A hash function for digital signatures, associating a
+ * {@link SkeinParameters.Builder#setPublicKey(byte[]) public key} with the message digest.</li>
+ * <li>A key derivation function, by providing a
+ * {@link SkeinParameters.Builder#setKeyIdentifier(byte[]) key identifier}.</li>
+ * <li>Personalised hashing, by providing a
+ * {@link SkeinParameters.Builder#setPersonalisation(Date, String, String) recommended format} or
+ * {@link SkeinParameters.Builder#setPersonalisation(byte[]) arbitrary} personalisation string.</li>
+ * </ul>
+ *
+ * @see SkeinEngine
+ * @see SkeinDigest
+ * @see SkeinMac
+ */
+public class SkeinParameters
+ implements CipherParameters
+{
+ /**
+ * The parameter type for a secret key, supporting MAC or KDF functions: {@value
+ * #PARAM_TYPE_KEY}.
+ */
+ public static final int PARAM_TYPE_KEY = 0;
+
+ /**
+ * The parameter type for the Skein configuration block: {@value #PARAM_TYPE_CONFIG}.
+ */
+ public static final int PARAM_TYPE_CONFIG = 4;
+
+ /**
+ * The parameter type for a personalisation string: {@value #PARAM_TYPE_PERSONALISATION}.
+ */
+ public static final int PARAM_TYPE_PERSONALISATION = 8;
+
+ /**
+ * The parameter type for a public key: {@value #PARAM_TYPE_PUBLIC_KEY}.
+ */
+ public static final int PARAM_TYPE_PUBLIC_KEY = 12;
+
+ /**
+ * The parameter type for a key identifier string: {@value #PARAM_TYPE_KEY_IDENTIFIER}.
+ */
+ public static final int PARAM_TYPE_KEY_IDENTIFIER = 16;
+
+ /**
+ * The parameter type for a nonce: {@value #PARAM_TYPE_NONCE}.
+ */
+ public static final int PARAM_TYPE_NONCE = 20;
+
+ /**
+ * The parameter type for the message: {@value #PARAM_TYPE_MESSAGE}.
+ */
+ public static final int PARAM_TYPE_MESSAGE = 48;
+
+ /**
+ * The parameter type for the output transformation: {@value #PARAM_TYPE_OUTPUT}.
+ */
+ public static final int PARAM_TYPE_OUTPUT = 63;
+
+ private Hashtable parameters;
+
+ public SkeinParameters()
+ {
+ this(new Hashtable());
+ }
+
+ private SkeinParameters(final Hashtable parameters)
+ {
+ this.parameters = parameters;
+ }
+
+ /**
+ * Obtains a map of type (Integer) to value (byte[]) for the parameters tracked in this object.
+ */
+ public Hashtable getParameters()
+ {
+ return parameters;
+ }
+
+ /**
+ * Obtains the value of the {@link #PARAM_TYPE_KEY key parameter}, or <code>null</code> if not
+ * set.
+ */
+ public byte[] getKey()
+ {
+ return (byte[])parameters.get(Integers.valueOf(PARAM_TYPE_KEY));
+ }
+
+ /**
+ * Obtains the value of the {@link #PARAM_TYPE_PERSONALISATION personalisation parameter}, or
+ * <code>null</code> if not set.
+ */
+ public byte[] getPersonalisation()
+ {
+ return (byte[])parameters.get(Integers.valueOf(PARAM_TYPE_PERSONALISATION));
+ }
+
+ /**
+ * Obtains the value of the {@link #PARAM_TYPE_PUBLIC_KEY public key parameter}, or
+ * <code>null</code> if not set.
+ */
+ public byte[] getPublicKey()
+ {
+ return (byte[])parameters.get(Integers.valueOf(PARAM_TYPE_PUBLIC_KEY));
+ }
+
+ /**
+ * Obtains the value of the {@link #PARAM_TYPE_KEY_IDENTIFIER key identifier parameter}, or
+ * <code>null</code> if not set.
+ */
+ public byte[] getKeyIdentifier()
+ {
+ return (byte[])parameters.get(Integers.valueOf(PARAM_TYPE_KEY_IDENTIFIER));
+ }
+
+ /**
+ * Obtains the value of the {@link #PARAM_TYPE_NONCE nonce parameter}, or <code>null</code> if
+ * not set.
+ */
+ public byte[] getNonce()
+ {
+ return (byte[])parameters.get(Integers.valueOf(PARAM_TYPE_NONCE));
+ }
+
+ /**
+ * A builder for {@link SkeinParameters}.
+ */
+ public static class Builder
+ {
+ private Hashtable parameters = new Hashtable();
+
+ public Builder()
+ {
+ }
+
+ public Builder(Hashtable paramsMap)
+ {
+ Enumeration keys = paramsMap.keys();
+ while (keys.hasMoreElements())
+ {
+ Integer key = (Integer)keys.nextElement();
+ parameters.put(key, paramsMap.get(key));
+ }
+ }
+
+ public Builder(SkeinParameters params)
+ {
+ Enumeration keys = params.parameters.keys();
+ while (keys.hasMoreElements())
+ {
+ Integer key = (Integer)keys.nextElement();
+ parameters.put(key, params.parameters.get(key));
+ }
+ }
+
+ /**
+ * Sets a parameters to apply to the Skein hash function.<br>
+ * Parameter types must be in the range 0,5..62, and cannot use the value {@value
+ * SkeinParameters#PARAM_TYPE_MESSAGE} (reserved for message body).
+ * <p/>
+ * Parameters with type < {@value SkeinParameters#PARAM_TYPE_MESSAGE} are processed before
+ * the message content, parameters with type > {@value SkeinParameters#PARAM_TYPE_MESSAGE}
+ * are processed after the message and prior to output.
+ *
+ * @param type the type of the parameter, in the range 5..62.
+ * @param value the byte sequence of the parameter.
+ * @return
+ */
+ public Builder set(int type, byte[] value)
+ {
+ if (value == null)
+ {
+ throw new IllegalArgumentException("Parameter value must not be null.");
+ }
+ if ((type != PARAM_TYPE_KEY)
+ && (type <= PARAM_TYPE_CONFIG || type >= PARAM_TYPE_OUTPUT || type == PARAM_TYPE_MESSAGE))
+ {
+ throw new IllegalArgumentException("Parameter types must be in the range 0,5..47,49..62.");
+ }
+ if (type == PARAM_TYPE_CONFIG)
+ {
+ throw new IllegalArgumentException("Parameter type " + PARAM_TYPE_CONFIG
+ + " is reserved for internal use.");
+ }
+ this.parameters.put(Integers.valueOf(type), value);
+ return this;
+ }
+
+ /**
+ * Sets the {@link SkeinParameters#PARAM_TYPE_KEY} parameter.
+ */
+ public Builder setKey(byte[] key)
+ {
+ return set(PARAM_TYPE_KEY, key);
+ }
+
+ /**
+ * Sets the {@link SkeinParameters#PARAM_TYPE_PERSONALISATION} parameter.
+ */
+ public Builder setPersonalisation(byte[] personalisation)
+ {
+ return set(PARAM_TYPE_PERSONALISATION, personalisation);
+ }
+
+ /**
+ * Sets the {@link SkeinParameters#PARAM_TYPE_KEY_IDENTIFIER} parameter.
+ */
+ public Builder setPublicKey(byte[] publicKey)
+ {
+ return set(PARAM_TYPE_PUBLIC_KEY, publicKey);
+ }
+
+ /**
+ * Sets the {@link SkeinParameters#PARAM_TYPE_KEY_IDENTIFIER} parameter.
+ */
+ public Builder setKeyIdentifier(byte[] keyIdentifier)
+ {
+ return set(PARAM_TYPE_KEY_IDENTIFIER, keyIdentifier);
+ }
+
+ /**
+ * Sets the {@link SkeinParameters#PARAM_TYPE_NONCE} parameter.
+ */
+ public Builder setNonce(byte[] nonce)
+ {
+ return set(PARAM_TYPE_NONCE, nonce);
+ }
+
+ /**
+ * Constructs a new {@link SkeinParameters} instance with the parameters provided to this
+ * builder.
+ */
+ public SkeinParameters build()
+ {
+ return new SkeinParameters(parameters);
+ }
+ }
+}
diff --git a/core/src/main/j2me/org/spongycastle/crypto/tls/OCSPStatusRequest.java b/core/src/main/j2me/org/spongycastle/crypto/tls/OCSPStatusRequest.java
new file mode 100644
index 00000000..ea68af90
--- /dev/null
+++ b/core/src/main/j2me/org/spongycastle/crypto/tls/OCSPStatusRequest.java
@@ -0,0 +1,131 @@
+package org.spongycastle.crypto.tls;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Vector;
+
+import org.spongycastle.asn1.ASN1Encoding;
+import org.spongycastle.asn1.ocsp.ResponderID;
+import org.spongycastle.asn1.x509.Extensions;
+
+/**
+ * RFC 3546 3.6
+ */
+public class OCSPStatusRequest
+{
+ protected Vector responderIDList;
+ protected Extensions requestExtensions;
+
+ /**
+ * @param responderIDList
+ * a {@link Vector} of {@link ResponderID}, specifying the list of trusted OCSP
+ * responders. An empty list has the special meaning that the responders are
+ * implicitly known to the server - e.g., by prior arrangement.
+ * @param requestExtensions
+ * OCSP request extensions. A null value means that there are no extensions.
+ */
+ public OCSPStatusRequest(Vector responderIDList, Extensions requestExtensions)
+ {
+ this.responderIDList = responderIDList;
+ this.requestExtensions = requestExtensions;
+ }
+
+ /**
+ * @return a {@link Vector} of {@link ResponderID}
+ */
+ public Vector getResponderIDList()
+ {
+ return responderIDList;
+ }
+
+ /**
+ * @return OCSP request extensions
+ */
+ public Extensions getRequestExtensions()
+ {
+ return requestExtensions;
+ }
+
+ /**
+ * Encode this {@link OCSPStatusRequest} to an {@link OutputStream}.
+ *
+ * @param output
+ * the {@link OutputStream} to encode to.
+ * @throws IOException
+ */
+ public void encode(OutputStream output) throws IOException
+ {
+ if (responderIDList == null || responderIDList.isEmpty())
+ {
+ TlsUtils.writeUint16(0, output);
+ }
+ else
+ {
+ ByteArrayOutputStream buf = new ByteArrayOutputStream();
+ for (int i = 0; i < responderIDList.size(); ++i)
+ {
+ ResponderID responderID = (ResponderID) responderIDList.elementAt(i);
+ byte[] derEncoding = responderID.getEncoded(ASN1Encoding.DER);
+ TlsUtils.writeOpaque16(derEncoding, buf);
+ }
+ TlsUtils.checkUint16(buf.size());
+ TlsUtils.writeUint16(buf.size(), output);
+ output.write(buf.toByteArray());
+ }
+
+ if (requestExtensions == null)
+ {
+ TlsUtils.writeUint16(0, output);
+ }
+ else
+ {
+ byte[] derEncoding = requestExtensions.getEncoded(ASN1Encoding.DER);
+ TlsUtils.checkUint16(derEncoding.length);
+ TlsUtils.writeUint16(derEncoding.length, output);
+ output.write(derEncoding);
+ }
+ }
+
+ /**
+ * Parse an {@link OCSPStatusRequest} from an {@link InputStream}.
+ *
+ * @param input
+ * the {@link InputStream} to parse from.
+ * @return an {@link OCSPStatusRequest} object.
+ * @throws IOException
+ */
+ public static OCSPStatusRequest parse(InputStream input) throws IOException
+ {
+ Vector responderIDList = new Vector();
+ {
+ int length = TlsUtils.readUint16(input);
+ if (length > 0)
+ {
+ byte[] data = TlsUtils.readFully(length, input);
+ ByteArrayInputStream buf = new ByteArrayInputStream(data);
+ do
+ {
+ byte[] derEncoding = TlsUtils.readOpaque16(buf);
+ ResponderID responderID = ResponderID.getInstance(TlsUtils.readDERObject(derEncoding));
+ responderIDList.addElement(responderID);
+ }
+ while (buf.available() > 0);
+ }
+ }
+
+ Extensions requestExtensions = null;
+ {
+ int length = TlsUtils.readUint16(input);
+ if (length > 0)
+ {
+ byte[] derEncoding = TlsUtils.readFully(length, input);
+ requestExtensions = Extensions.getInstance(TlsUtils.readDERObject(derEncoding));
+ }
+ }
+
+ return new OCSPStatusRequest(responderIDList, requestExtensions);
+ }
+}
diff --git a/core/src/main/j2me/org/spongycastle/crypto/tls/ServerNameList.java b/core/src/main/j2me/org/spongycastle/crypto/tls/ServerNameList.java
new file mode 100644
index 00000000..09817e6b
--- /dev/null
+++ b/core/src/main/j2me/org/spongycastle/crypto/tls/ServerNameList.java
@@ -0,0 +1,86 @@
+package org.spongycastle.crypto.tls;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Vector;
+
+public class ServerNameList
+{
+ protected Vector serverNameList;
+
+ /**
+ * @param serverNameList a {@link Vector} of {@link ServerName}.
+ */
+ public ServerNameList(Vector serverNameList)
+ {
+ if (serverNameList == null || serverNameList.isEmpty())
+ {
+ throw new IllegalArgumentException("'serverNameList' must not be null or empty");
+ }
+
+ this.serverNameList = serverNameList;
+ }
+
+ /**
+ * @return a {@link Vector} of {@link ServerName}.
+ */
+ public Vector getServerNameList()
+ {
+ return serverNameList;
+ }
+
+ /**
+ * Encode this {@link ServerNameList} to an {@link OutputStream}.
+ *
+ * @param output
+ * the {@link OutputStream} to encode to.
+ * @throws IOException
+ */
+ public void encode(OutputStream output) throws IOException
+ {
+ ByteArrayOutputStream buf = new ByteArrayOutputStream();
+
+ for (int i = 0; i < serverNameList.size(); ++i)
+ {
+ ServerName entry = (ServerName)serverNameList.elementAt(i);
+ entry.encode(buf);
+ }
+
+ TlsUtils.checkUint16(buf.size());
+ TlsUtils.writeUint16(buf.size(), output);
+ output.write(buf.toByteArray());
+ }
+
+ /**
+ * Parse a {@link ServerNameList} from an {@link InputStream}.
+ *
+ * @param input
+ * the {@link InputStream} to parse from.
+ * @return a {@link ServerNameList} object.
+ * @throws IOException
+ */
+ public static ServerNameList parse(InputStream input) throws IOException
+ {
+ int length = TlsUtils.readUint16(input);
+ if (length < 1)
+ {
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+ }
+
+ byte[] data = TlsUtils.readFully(length, input);
+
+ ByteArrayInputStream buf = new ByteArrayInputStream(data);
+
+ Vector server_name_list = new Vector();
+ while (buf.available() > 0)
+ {
+ ServerName entry = ServerName.parse(buf);
+ server_name_list.addElement(entry);
+ }
+
+ return new ServerNameList(server_name_list);
+ }
+}
diff --git a/core/src/main/j2me/org/spongycastle/crypto/tls/UDPTransport.java b/core/src/main/j2me/org/spongycastle/crypto/tls/UDPTransport.java
new file mode 100644
index 00000000..ea287144
--- /dev/null
+++ b/core/src/main/j2me/org/spongycastle/crypto/tls/UDPTransport.java
@@ -0,0 +1,107 @@
+package org.spongycastle.crypto.tls;
+
+import java.io.IOException;
+import javax.microedition.io.DatagramConnection;
+import javax.microedition.io.Datagram;
+
+public class UDPTransport
+ implements DatagramTransport
+{
+
+ protected final static int MIN_IP_OVERHEAD = 20;
+ protected final static int MAX_IP_OVERHEAD = MIN_IP_OVERHEAD + 64;
+ protected final static int UDP_OVERHEAD = 8;
+
+ protected final DatagramConnection socket;
+ protected final int receiveLimit, sendLimit;
+
+ public UDPTransport(DatagramConnection socket, int mtu)
+ throws IOException
+ {
+ //
+ // In 1.3 and earlier sockets were bound and connected during creation
+ //
+ //if (!socket.isBound() || !socket.isConnected())
+ //{
+ // throw new IllegalArgumentException("'socket' must be bound and connected");
+ //}
+
+ this.socket = socket;
+
+ // NOTE: As of JDK 1.6, can use NetworkInterface.getMTU
+
+ this.receiveLimit = mtu - MIN_IP_OVERHEAD - UDP_OVERHEAD;
+ this.sendLimit = mtu - MAX_IP_OVERHEAD - UDP_OVERHEAD;
+ }
+
+ public int getReceiveLimit()
+ {
+ return receiveLimit;
+ }
+
+ public int getSendLimit()
+ {
+ // TODO[DTLS] Implement Path-MTU discovery?
+ return sendLimit;
+ }
+
+ public int receive(byte[] buf, int off, int len, int waitMillis)
+ throws IOException
+ {
+ //socket.setSoTimeout(waitMillis); -- not applicable
+
+ if (off == 0)
+ {
+ Datagram packet = socket.newDatagram(buf, len);
+ socket.receive(packet);
+
+ return packet.getLength();
+ }
+ else
+ {
+ byte[] rv = new byte[len];
+
+ Datagram packet = socket.newDatagram(rv, len);
+ socket.receive(packet);
+
+ System.arraycopy(rv, 0, buf, off, packet.getLength());
+
+ return packet.getLength();
+ }
+ }
+
+ public void send(byte[] buf, int off, int len)
+ throws IOException
+ {
+ if (len > getSendLimit())
+ {
+ /*
+ * RFC 4347 4.1.1. "If the application attempts to send a record larger than the MTU,
+ * the DTLS implementation SHOULD generate an error, thus avoiding sending a packet
+ * which will be fragmented."
+ */
+ // TODO Exception
+ }
+
+ if (off == 0)
+ {
+ Datagram packet = socket.newDatagram(buf, len);
+ socket.send(packet);
+ }
+ else
+ {
+ byte[] data = new byte[len];
+
+ System.arraycopy(buf, off, data, 0, len);
+
+ Datagram packet = socket.newDatagram(data, len);
+ socket.send(packet);
+ }
+ }
+
+ public void close()
+ throws IOException
+ {
+ socket.close();
+ }
+}