Source code: cryptix/sasl/srp/CALG.java
1 package cryptix.sasl.srp;
2
3 // $Id: CALG.java,v 1.1 2001/06/25 21:04:56 raif Exp $
4 //
5 // Copyright (c) 2000-2001 The Cryptix Foundation Limited. All rights reserved.
6 //
7 // Use, modification, copying and distribution of this software is subject to
8 // the terms and conditions of the Cryptix General Licence. You should have
9 // received a copy of the Cryptix General License along with this library; if
10 // not, you can download a copy from <http://www.cryptix.org/>.
11
12 import cryptix.sasl.ConfidentialityException;
13 import cryptix.sasl.SaslUtil;
14 import java.security.InvalidAlgorithmParameterException;
15 import java.security.InvalidKeyException;
16 import java.security.NoSuchAlgorithmException;
17 import java.util.Random;
18 import javax.crypto.BadPaddingException;
19 import javax.crypto.Cipher;
20 import javax.crypto.IllegalBlockSizeException;
21 import javax.crypto.NoSuchPaddingException;
22 import javax.crypto.SecretKey;
23 import javax.crypto.spec.IvParameterSpec;
24 import javax.crypto.spec.SecretKeySpec;
25 import javax.security.sasl.SaslException;
26
27 import org.apache.log4j.Category;
28
29 /**
30 * A Factory class that returns CALG (Confidentiality Algorithm) instances that
31 * operate as described in the draft-burdis-cat-sasl-srp-04. Specifically the
32 * following description, from the specs, is relevant:<p>
33 *
34 * <pre>
35 * The designated CALG block cipher should be used in OFB (Output
36 * Feedback Block) mode in the ISO variant, as described in [16],
37 * algorithm 7.20.
38 *
39 * Let k be the block size of the chosen symmetric cipher algorithm;
40 * e.g. for AES this is 128 bits or 16 octets. The OFB mode used shall
41 * be of length/size k.
42 *
43 * It is recommended that Block ciphers operating in OFB mode be used
44 * with an Initial Vector (the mode's IV). For the SASL mechanisms
45 * described in this document, the IV shall be an all-zero octet
46 * sequence of size k.
47 *
48 * In such a mode of operation - OFB with key re-use - the IV, which
49 * need not be secret, must be changed. Otherwise an identical
50 * keystream results; and, by XORing corresponding ciphertexts, an
51 * adversary may reduce cryptanalysis to that of a running-key cipher
52 * with one plaintext as the running key. To counter the effect of
53 * fixing the IV to an all-zero octet sequence, the sender should use a
54 * one k-octet sequence as the value of its first block, constructed as
55 * follows:
56 *
57 * o the first (most significant) (k-2) octets are random,
58 *
59 * o the octets at position #k-1 and #k, assuming the first octet is
60 * at position #1, are exact copies of those at positions #1 and #2
61 * respectively.
62 *
63 * The input data to the confidentiality protection algorithm shall be
64 * a multiple of the symmetric cipher block size k. When the input
65 * length is not a multiple of k octets, the data shall be padded
66 * according to the following scheme (described in [17] which itself is
67 * based on RFC1423 [18]):
68
69 * Assuming the length of the input is l octets, (k - (l mod k))
70 * octets, all having the value (k - (l mod k)), shall be appended
71 * to the original data. In other words, the input is padded at the
72 * trailing end with one of the following sequences:
73 *
74 * 01 -- if l mod k = k-1
75 * 02 02 -- if l mod k = k-2
76 * ...
77 * ...
78 * ...
79 * k k ... k k -- if l mod k = 0
80 *
81 * The padding can be removed unambiguously since all input is
82 * padded and no padding sequence is a suffix of another. This
83 * padding method is well-defined if and only if k < 256 octets,
84 * which is the case with symmetric block ciphers today, and in the
85 * forseeable future.
86 *
87 * The output of this stage, when it is active, is:
88 *
89 * at the sending side: CALG(K, ENCRYPT)( bytes(p1) )
90 *
91 * at the receiving side: CALG(K, DECRYPT)( bytes(p1) )
92 *
93 * If the receiver, after decrypting the first block, finds that the
94 * last two octets do not match the value of the first two, it MUST
95 * signal an exception and abort the exchange.
96 * </pre>
97 *
98 * @version $Revision: 1.1 $
99 * @since draft-burdis-cat-sasl-srp-04
100 */
101 public final class CALG
102 {
103 // Constants and variables
104 // --------------------------------------------------------------------------
105
106 private static Category cat = Category.getInstance(CALG.class);
107
108 public static final int ENCRYPT = Cipher.ENCRYPT_MODE;
109 public static final int DECRYPT = Cipher.DECRYPT_MODE;
110
111 private static final Random prng = new Random();
112
113 private String algorithm;
114 private Cipher cipher;
115 private boolean encrypting;
116 private int blockSize;
117 private boolean firstBlock = true;
118 private byte[] iv;
119
120 // Constructor(s)
121 // --------------------------------------------------------------------------
122
123 /** Trivial private constructor to enforce Singleton pattern. */
124 private CALG(String algorithm, int blockSize, Cipher cipher, boolean encrypting)
125 throws SaslException {
126 super();
127
128 this.algorithm = algorithm;
129 this.blockSize = blockSize;
130 this.cipher = cipher;
131 this.encrypting = encrypting;
132
133 iv = new byte[blockSize];
134 if (encrypting) { // build first block
135 prng.nextBytes(iv);
136 iv[blockSize-2] = (byte) iv[0];
137 iv[blockSize-1] = (byte) iv[1];
138 }
139 }
140
141 // Class methods
142 // -------------------------------------------------------------------------
143
144 /**
145 * Returns an instance of a SASL-SRP CALG implementation.
146 *
147 * @param algorithm the name of the symmetric cipher algorithm.
148 * @param K the shared secret.
149 * @param mode whether this CALG is used for encryption or decryption.
150 * @return an instance of this object.
151 */
152 public static synchronized CALG
153 getInstance(String algorithm, SecretKey K, int mode)
154 throws SaslException {
155 String alias = SRPUtil.toProviderName(algorithm);
156 Cipher cipher;
157 int blockSize;
158 try {
159 cipher = Cipher.getInstance(alias+"/OFB/PKCS7");
160 blockSize = cipher.getBlockSize();
161 } catch (NoSuchAlgorithmException x) {
162 cat.error(x);
163 throw new SaslException("getInstance()", x);
164 } catch (NoSuchPaddingException x) {
165 cat.error(x);
166 throw new SaslException("getInstance()", x);
167 }
168
169 try {
170 SecretKey sk = new SecretKeySpec(K.getEncoded(), alias);
171 IvParameterSpec iv = new IvParameterSpec(new byte[blockSize]);
172 cipher.init(mode, sk, iv);
173 } catch (InvalidKeyException x) {
174 throw new SaslException("getInstance()", x);
175 } catch (InvalidAlgorithmParameterException x) {
176 throw new SaslException("getInstance()", x);
177 }
178
179 return new CALG(alias, blockSize, cipher, mode == ENCRYPT);
180 }
181
182 // Instance methods
183 // -------------------------------------------------------------------------
184
185 /**
186 * Encrypts or decrypts, depending on the mode already set, a designated
187 * array of bytes and returns the result.
188 *
189 * @param data the data to encrypt/decrypt
190 * @return the decrypted/encrypted result.
191 * @exception ConfidentialityException if an exception occurs duirng the
192 * process.
193 */
194 public byte[] doFinal(byte[] data) throws ConfidentialityException {
195 cat.debug("==> doFinal()");
196 cat.debug(encrypting ? "plaintext: " : "ciphertext: "+SaslUtil.dumpString(data));
197
198 byte[] t, result;
199 try {
200 if (encrypting && firstBlock) {
201 firstBlock = false;
202 t = new byte[data.length+blockSize];
203 System.arraycopy(iv, 0, t, 0, blockSize);
204 System.arraycopy(data, 0, t, blockSize, data.length);
205 } else
206 t = data;
207
208 t = cipher.doFinal(t);
209
210 if (!encrypting && firstBlock) {
211 firstBlock = false;
212 result = new byte[t.length-blockSize];
213 System.arraycopy(t, 0, iv, 0, blockSize);
214 System.arraycopy(t, blockSize, result, 0, t.length-blockSize);
215 if (iv[0] != iv[blockSize-2] || iv[1] != iv[blockSize-1])
216 throw new RuntimeException("Incorrect IV");
217 } else
218 result = t;
219
220 } catch (IllegalBlockSizeException x) {
221 throw new ConfidentialityException("crypt()", x);
222 } catch (BadPaddingException x) {
223 throw new ConfidentialityException("crypt()", x);
224 } catch (Exception x) {
225 throw new ConfidentialityException("crypt()", x);
226 }
227
228 cat.debug(encrypting ? "ciphertext: " : "plaintext: "+SaslUtil.dumpString(result));
229 cat.debug("<== doFinal()");
230 return result;
231 }
232 }