1/* 2 * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26package com.sun.crypto.provider; 27 28import java.security.*; 29import java.security.spec.AlgorithmParameterSpec; 30 31import javax.crypto.*; 32 33/** 34 * Implementation of the ARCFOUR cipher, an algorithm apparently compatible 35 * with RSA Security's RC4(tm) cipher. The description of this algorithm was 36 * taken from Bruce Schneier's book Applied Cryptography, 2nd ed., 37 * section 17.1. 38 * 39 * We support keys from 40 to 1024 bits. ARCFOUR would allow for keys shorter 40 * than 40 bits, but that is too insecure for us to permit. 41 * 42 * Note that we subclass CipherSpi directly and do not use the CipherCore 43 * framework. That was designed to simplify implementation of block ciphers 44 * and does not offer any advantages for stream ciphers such as ARCFOUR. 45 * 46 * @since 1.5 47 * @author Andreas Sterbenz 48 */ 49public final class ARCFOURCipher extends CipherSpi { 50 51 // state array S, 256 entries. The entries are 8-bit, but we use an int[] 52 // because int arithmetic is much faster than in Java than bytes. 53 private final int[] S; 54 55 // state indices i and j. Called is and js to avoid collision with 56 // local variables. 'is' is set to -1 after a call to doFinal() 57 private int is, js; 58 59 // the bytes of the last key used (if any) 60 // we need this to re-initialize after a call to doFinal() 61 private byte[] lastKey; 62 63 // called by the JCE framework 64 public ARCFOURCipher() { 65 S = new int[256]; 66 } 67 68 // core key setup code. initializes S, is, and js 69 // assumes key is non-null and between 40 and 1024 bit 70 private void init(byte[] key) { 71 // initialize S[i] to i 72 for (int i = 0; i < 256; i++) { 73 S[i] = i; 74 } 75 76 // we avoid expanding key to 256 bytes and instead keep a separate 77 // counter ki = i mod key.length. 78 for (int i = 0, j = 0, ki = 0; i < 256; i++) { 79 int Si = S[i]; 80 j = (j + Si + key[ki]) & 0xff; 81 S[i] = S[j]; 82 S[j] = Si; 83 ki++; 84 if (ki == key.length) { 85 ki = 0; 86 } 87 } 88 89 // set indices to 0 90 is = 0; 91 js = 0; 92 } 93 94 // core crypt code. OFB style, so works for both encryption and decryption 95 private void crypt(byte[] in, int inOfs, int inLen, byte[] out, 96 int outOfs) { 97 if (is < 0) { 98 // doFinal() was called, need to reset the cipher to initial state 99 init(lastKey); 100 } 101 while (inLen-- > 0) { 102 is = (is + 1) & 0xff; 103 int Si = S[is]; 104 js = (js + Si) & 0xff; 105 int Sj = S[js]; 106 S[is] = Sj; 107 S[js] = Si; 108 out[outOfs++] = (byte)(in[inOfs++] ^ S[(Si + Sj) & 0xff]); 109 } 110 } 111 112 // Modes do not make sense with stream ciphers, but allow ECB 113 // see JCE spec. 114 protected void engineSetMode(String mode) throws NoSuchAlgorithmException { 115 if (mode.equalsIgnoreCase("ECB") == false) { 116 throw new NoSuchAlgorithmException("Unsupported mode " + mode); 117 } 118 } 119 120 // Padding does not make sense with stream ciphers, but allow NoPadding 121 // see JCE spec. 122 protected void engineSetPadding(String padding) 123 throws NoSuchPaddingException { 124 if (padding.equalsIgnoreCase("NoPadding") == false) { 125 throw new NoSuchPaddingException("Padding must be NoPadding"); 126 } 127 } 128 129 // Return 0 to indicate stream cipher 130 // see JCE spec. 131 protected int engineGetBlockSize() { 132 return 0; 133 } 134 135 // output length is always the same as input length 136 // see JCE spec 137 protected int engineGetOutputSize(int inputLen) { 138 return inputLen; 139 } 140 141 // no IV, return null 142 // see JCE spec 143 protected byte[] engineGetIV() { 144 return null; 145 } 146 147 // no parameters 148 // see JCE spec 149 protected AlgorithmParameters engineGetParameters() { 150 return null; 151 } 152 153 // see JCE spec 154 protected void engineInit(int opmode, Key key, SecureRandom random) 155 throws InvalidKeyException { 156 init(opmode, key); 157 } 158 159 // see JCE spec 160 protected void engineInit(int opmode, Key key, 161 AlgorithmParameterSpec params, SecureRandom random) 162 throws InvalidKeyException, InvalidAlgorithmParameterException { 163 if (params != null) { 164 throw new InvalidAlgorithmParameterException 165 ("Parameters not supported"); 166 } 167 init(opmode, key); 168 } 169 170 // see JCE spec 171 protected void engineInit(int opmode, Key key, 172 AlgorithmParameters params, SecureRandom random) 173 throws InvalidKeyException, InvalidAlgorithmParameterException { 174 if (params != null) { 175 throw new InvalidAlgorithmParameterException 176 ("Parameters not supported"); 177 } 178 init(opmode, key); 179 } 180 181 // init method. Check opmode and key, then call init(byte[]). 182 private void init(int opmode, Key key) throws InvalidKeyException { 183 if ((opmode < Cipher.ENCRYPT_MODE) || (opmode > Cipher.UNWRAP_MODE)) { 184 throw new InvalidKeyException("Unknown opmode: " + opmode); 185 } 186 lastKey = getEncodedKey(key); 187 init(lastKey); 188 } 189 190 // return the encoding of key if key is a valid ARCFOUR key. 191 // otherwise, throw an InvalidKeyException 192 private static byte[] getEncodedKey(Key key) throws InvalidKeyException { 193 String keyAlg = key.getAlgorithm(); 194 if (!keyAlg.equals("RC4") && !keyAlg.equals("ARCFOUR")) { 195 throw new InvalidKeyException("Not an ARCFOUR key: " + keyAlg); 196 } 197 if ("RAW".equals(key.getFormat()) == false) { 198 throw new InvalidKeyException("Key encoding format must be RAW"); 199 } 200 byte[] encodedKey = key.getEncoded(); 201 if ((encodedKey.length < 5) || (encodedKey.length > 128)) { 202 throw new InvalidKeyException 203 ("Key length must be between 40 and 1024 bit"); 204 } 205 return encodedKey; 206 } 207 208 // see JCE spec 209 protected byte[] engineUpdate(byte[] in, int inOfs, int inLen) { 210 byte[] out = new byte[inLen]; 211 crypt(in, inOfs, inLen, out, 0); 212 return out; 213 } 214 215 // see JCE spec 216 protected int engineUpdate(byte[] in, int inOfs, int inLen, 217 byte[] out, int outOfs) throws ShortBufferException { 218 if (out.length - outOfs < inLen) { 219 throw new ShortBufferException("Output buffer too small"); 220 } 221 crypt(in, inOfs, inLen, out, outOfs); 222 return inLen; 223 } 224 225 // see JCE spec 226 protected byte[] engineDoFinal(byte[] in, int inOfs, int inLen) { 227 byte[] out = engineUpdate(in, inOfs, inLen); 228 is = -1; 229 return out; 230 } 231 232 // see JCE spec 233 protected int engineDoFinal(byte[] in, int inOfs, int inLen, 234 byte[] out, int outOfs) throws ShortBufferException { 235 int outLen = engineUpdate(in, inOfs, inLen, out, outOfs); 236 is = -1; 237 return outLen; 238 } 239 240 // see JCE spec 241 protected byte[] engineWrap(Key key) throws IllegalBlockSizeException, 242 InvalidKeyException { 243 byte[] encoded = key.getEncoded(); 244 if ((encoded == null) || (encoded.length == 0)) { 245 throw new InvalidKeyException("Could not obtain encoded key"); 246 } 247 return engineDoFinal(encoded, 0, encoded.length); 248 } 249 250 // see JCE spec 251 protected Key engineUnwrap(byte[] wrappedKey, String algorithm, 252 int type) throws InvalidKeyException, NoSuchAlgorithmException { 253 byte[] encoded = engineDoFinal(wrappedKey, 0, wrappedKey.length); 254 return ConstructKeys.constructKey(encoded, algorithm, type); 255 } 256 257 // see JCE spec 258 protected int engineGetKeySize(Key key) throws InvalidKeyException { 259 byte[] encodedKey = getEncodedKey(key); 260 return encodedKey.length << 3; 261 } 262 263} 264