Skip to content

Commit 93fe705

Browse files
committed
use a hash set for supportedCiphers + more Cipher cleanup and some unit tests
1 parent b5a9252 commit 93fe705

File tree

2 files changed

+151
-82
lines changed

2 files changed

+151
-82
lines changed

src/main/java/org/jruby/ext/openssl/Cipher.java

Lines changed: 76 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@
2828
package org.jruby.ext.openssl;
2929

3030
import java.io.PrintStream;
31-
import java.util.ArrayList;
31+
import java.util.Collection;
3232
import java.util.HashSet;
33-
import java.util.List;
33+
import java.util.LinkedHashSet;
3434
import java.util.Set;
3535

3636
import java.security.GeneralSecurityException;
@@ -42,11 +42,11 @@
4242
import javax.crypto.spec.RC2ParameterSpec;
4343

4444
import org.jruby.Ruby;
45+
import org.jruby.RubyArray;
4546
import org.jruby.RubyClass;
4647
import org.jruby.RubyModule;
4748
import org.jruby.RubyNumeric;
4849
import org.jruby.RubyObject;
49-
import org.jruby.common.IRubyWarnings;
5050
import org.jruby.common.IRubyWarnings.ID;
5151
import org.jruby.anno.JRubyMethod;
5252
import org.jruby.exceptions.RaiseException;
@@ -79,30 +79,30 @@ public static void createCipher(final Ruby runtime, final RubyModule mOSSL) {
7979
}
8080

8181
public static boolean isSupportedCipher(final String name) {
82-
initializeCiphers();
83-
return CIPHERS.contains( name.toUpperCase() );
82+
final Collection<String> ciphers = getSupportedCiphers();
83+
return ciphers.contains( name.toUpperCase() );
8484
}
8585

8686
@JRubyMethod(meta = true)
8787
public static IRubyObject ciphers(final ThreadContext context, final IRubyObject self) {
88-
initializeCiphers();
8988
final Ruby runtime = context.runtime;
9089

91-
ArrayList<IRubyObject> result = new ArrayList<IRubyObject>(CIPHERS.size() * 2);
92-
for (String cipher : CIPHERS) {
93-
result.add( runtime.newString(cipher) );
94-
result.add( runtime.newString(cipher.toLowerCase()) );
90+
final Collection<String> ciphers = getSupportedCiphers();
91+
final RubyArray result = runtime.newArray(ciphers.size() * 2);
92+
for ( final String cipher : ciphers ) {
93+
result.append( runtime.newString(cipher) );
94+
result.append( runtime.newString(cipher.toLowerCase()) );
9595
}
96-
return runtime.newArray(result);
96+
return result;
9797
}
9898

99-
private static boolean initialized = false;
100-
static final List<String> CIPHERS = new ArrayList<String>();
99+
private static boolean supportedCiphersInitialized = false;
100+
static final Collection<String> supportedCiphers = new LinkedHashSet<String>(72);
101101

102-
private static void initializeCiphers() {
103-
if ( initialized ) return;
104-
synchronized (CIPHERS) {
105-
if ( initialized ) return;
102+
private static Collection<String> getSupportedCiphers() {
103+
if ( supportedCiphersInitialized ) return supportedCiphers;
104+
synchronized ( supportedCiphers ) {
105+
if ( supportedCiphersInitialized ) return supportedCiphers;
106106
final String[] other = {
107107
"AES128", "AES192", "AES256",
108108
"BLOWFISH",
@@ -122,17 +122,18 @@ private static void initializeCiphers() {
122122
for ( int k = 0; k < suffixes.length; k++ ) {
123123
final String cipher;
124124
if ( tryCipher( cipher = bases[i] + suffixes[k]) ) {
125-
CIPHERS.add( cipher.toUpperCase() );
125+
supportedCiphers.add( cipher.toUpperCase() );
126126
}
127127
}
128128
}
129129
for ( int i = 0; i < other.length; i++ ) {
130130
final String cipher = other[i];
131131
if ( tryCipher( cipher ) ) {
132-
CIPHERS.add( cipher.toUpperCase() );
132+
supportedCiphers.add( cipher.toUpperCase() );
133133
}
134134
}
135-
initialized = true;
135+
supportedCiphersInitialized = true;
136+
return supportedCiphers;
136137
}
137138
}
138139

@@ -141,8 +142,7 @@ public static class Algorithm {
141142
private static final Set<String> BLOCK_MODES;
142143

143144
static {
144-
BLOCK_MODES = new HashSet<String>();
145-
145+
BLOCK_MODES = new HashSet<String>(8);
146146
BLOCK_MODES.add("CBC");
147147
BLOCK_MODES.add("CFB");
148148
BLOCK_MODES.add("CFB1");
@@ -151,53 +151,52 @@ public static class Algorithm {
151151
BLOCK_MODES.add("OFB");
152152
}
153153

154-
public static String jsseToOssl(String inName, int keyLen) {
154+
public static String jsseToOssl(final String cipherName, final int keyLen) {
155155
String cryptoBase;
156156
String cryptoVersion = null;
157157
String cryptoMode = null;
158-
String[] parts = inName.split("/");
159-
if (parts.length != 1 && parts.length != 3) {
158+
final String[] parts = cipherName.split("/");
159+
if ( parts.length != 1 && parts.length != 3 ) {
160160
return null;
161161
}
162-
cryptoBase = parts[0];
163-
if (parts.length > 2) {
162+
if ( parts.length > 2 ) {
164163
cryptoMode = parts[1];
165164
// padding: parts[2] is not used
166165
}
167-
if (!BLOCK_MODES.contains(cryptoMode)) {
166+
cryptoBase = parts[0];
167+
if ( ! BLOCK_MODES.contains(cryptoMode) ) {
168168
cryptoVersion = cryptoMode;
169169
cryptoMode = "CBC";
170170
}
171171
if (cryptoMode == null) {
172172
cryptoMode = "CBC";
173173
}
174-
if (cryptoBase.equals("DESede")) {
174+
if ( "DESede".equals(cryptoBase) ) {
175175
cryptoBase = "DES";
176176
cryptoVersion = "EDE3";
177-
} else if (cryptoBase.equals("Blowfish")) {
177+
}
178+
else if ( "Blowfish".equals(cryptoBase) ) {
178179
cryptoBase = "BF";
179180
}
180181
if (cryptoVersion == null) {
181182
cryptoVersion = String.valueOf(keyLen);
182183
}
183-
return cryptoBase + "-" + cryptoVersion + "-" + cryptoMode;
184+
return cryptoBase + '-' + cryptoVersion + '-' + cryptoMode;
184185
}
185186

186187
public static String getAlgorithmBase(javax.crypto.Cipher cipher) {
187-
String algoBase = cipher.getAlgorithm();
188-
if (algoBase.indexOf('/') != -1) {
189-
algoBase = algoBase.split("/")[0];
190-
}
191-
return algoBase;
188+
final String algorithm = cipher.getAlgorithm();
189+
final int idx = algorithm.indexOf('/');
190+
if ( idx != -1 ) return algorithm.substring(0, idx);
191+
return algorithm;
192192
}
193193

194-
public static String[] osslToJsse(String inName) {
195-
// assume PKCS5Padding
196-
return osslToJsse(inName, null);
194+
public static String[] osslToJsse(final String osslName) {
195+
return osslToJsse(osslName, null); // assume PKCS5Padding
197196
}
198197

199-
public static String[] osslToJsse(String inName, String padding) {
200-
String[] split = inName.split("-");
198+
public static String[] osslToJsse(final String osslName, final String padding) {
199+
final String[] split = osslName.split("-");
201200
String cryptoBase = split[0];
202201
String cryptoVersion = null;
203202
String cryptoMode;
@@ -214,7 +213,7 @@ public static String[] osslToJsse(String inName, String padding) {
214213
paddingType = "PKCS5Padding";
215214
}
216215

217-
if ("bf".equalsIgnoreCase(cryptoBase)) {
216+
if ( "BF".equalsIgnoreCase(cryptoBase) ) {
218217
cryptoBase = "Blowfish";
219218
}
220219

@@ -227,73 +226,71 @@ public static String[] osslToJsse(String inName, String padding) {
227226
cryptoMode = "CBC";
228227
}
229228

230-
if (cryptoBase.equalsIgnoreCase("CAST")) {
229+
if ( "CAST".equalsIgnoreCase(cryptoBase) ) {
231230
realName = "CAST5";
232-
} else if (cryptoBase.equalsIgnoreCase("DES") && "EDE3".equalsIgnoreCase(cryptoVersion)) {
231+
} else if ( "DES".equalsIgnoreCase(cryptoBase) && "EDE3".equalsIgnoreCase(cryptoVersion) ) {
233232
realName = "DESede";
234233
} else {
235234
realName = cryptoBase;
236235
}
237236

238-
if (!BLOCK_MODES.contains(cryptoMode.toUpperCase())) {
239-
cryptoVersion = cryptoMode;
240-
cryptoMode = "CBC";
241-
} else if (cryptoMode.equalsIgnoreCase("CFB1")) {
242-
// uglish SunJCE cryptoMode normalization.
243-
cryptoMode = "CFB";
237+
final String cryptoModeUpper = cryptoMode.toUpperCase();
238+
if ( ! BLOCK_MODES.contains(cryptoModeUpper) ) {
239+
cryptoVersion = cryptoMode; cryptoMode = "CBC";
240+
} else if ( "CFB1".equals(cryptoModeUpper) ) {
241+
cryptoMode = "CFB"; // uglish SunJCE cryptoMode normalization
244242
}
245243

246-
if (realName.equalsIgnoreCase("RC4")) {
247-
realName = "RC4";
248-
cryptoMode = "NONE";
249-
paddingType = "NoPadding";
244+
if ( "RC4".equalsIgnoreCase(realName) ) {
245+
realName = "RC4"; cryptoMode = "NONE"; paddingType = "NoPadding";
250246
} else {
251247
realName = realName + "/" + cryptoMode + "/" + paddingType;
252248
}
253249

254-
return new String[]{cryptoBase, cryptoVersion, cryptoMode, realName, paddingType};
250+
return new String[]{ cryptoBase, cryptoVersion, cryptoMode, realName, paddingType };
255251
}
256252

257-
public static int[] osslKeyIvLength(String name) {
258-
String[] values = Algorithm.osslToJsse(name);
259-
String cryptoBase = values[0];
260-
String cryptoVersion = values[1];
261-
//String cryptoMode = values[2];
262-
//String realName = values[3];
253+
public static int[] osslKeyIvLength(final String cipherName) {
254+
String[] name = Algorithm.osslToJsse(cipherName);
255+
final String cryptoBaseUpper = name[0].toUpperCase();
256+
final String cryptoVersion = name[1];
257+
//final String cryptoMode = name[2];
258+
//final String realName = name[3];
263259

264-
int keyLen = -1;
265-
int ivLen = -1;
260+
int keyLen = -1; int ivLen = -1;
266261

267-
if (hasLen(cryptoBase) && null != cryptoVersion) {
262+
final boolean hasLen =
263+
"AES".equals(cryptoBaseUpper) || "RC2".equals(cryptoBaseUpper) || "RC4".equals(cryptoBaseUpper);
264+
if ( hasLen && cryptoVersion != null ) {
268265
try {
269266
keyLen = Integer.parseInt(cryptoVersion) / 8;
270267
} catch (NumberFormatException e) {
271268
keyLen = -1;
272269
}
273270
}
274-
if (keyLen == -1) {
275-
if ("DES".equalsIgnoreCase(cryptoBase)) {
271+
if ( keyLen == -1 ) {
272+
if ( "DES".equals(cryptoBaseUpper) ) {
276273
ivLen = 8;
277-
if ("EDE3".equalsIgnoreCase(cryptoVersion)) {
274+
if ( "EDE3".equalsIgnoreCase(cryptoVersion) ) {
278275
keyLen = 24;
279276
} else {
280277
keyLen = 8;
281278
}
282-
} else if ("RC4".equalsIgnoreCase(cryptoBase)) {
279+
} else if ( "RC4".equals(cryptoBaseUpper) ) {
283280
ivLen = 0;
284281
keyLen = 16;
285282
} else {
286283
keyLen = 16;
287284
try {
288-
int maxLen = javax.crypto.Cipher.getMaxAllowedKeyLength(name) / 8;
285+
int maxLen = javax.crypto.Cipher.getMaxAllowedKeyLength(cipherName) / 8;
289286
if (maxLen < keyLen) keyLen = maxLen;
290287
}
291288
catch (NoSuchAlgorithmException e) { }
292289
}
293290
}
294291

295-
if (ivLen == -1) {
296-
if ("AES".equalsIgnoreCase(cryptoBase)) {
292+
if ( ivLen == -1 ) {
293+
if ( "AES".equals(cryptoBaseUpper) ) {
297294
ivLen = 16;
298295
} else {
299296
ivLen = 8;
@@ -302,15 +299,12 @@ public static int[] osslKeyIvLength(String name) {
302299
return new int[] { keyLen, ivLen };
303300
}
304301

305-
public static boolean hasLen(String cryptoBase) {
306-
return "AES".equalsIgnoreCase(cryptoBase) || "RC2".equalsIgnoreCase(cryptoBase) || "RC4".equalsIgnoreCase(cryptoBase);
307-
}
308302
}
309303

310-
private static boolean tryCipher(final String rubyName) {
311-
String transformation = Algorithm.osslToJsse(rubyName, null)[3];
304+
private static boolean tryCipher(final String osslName) {
305+
String realName = Algorithm.osslToJsse(osslName, null)[3];
312306
try {
313-
return getCipher(transformation, true) != null;
307+
return getCipher(realName, true) != null;
314308
}
315309
catch (GeneralSecurityException e) {
316310
return false;
@@ -770,7 +764,11 @@ public IRubyObject _final(final ThreadContext context) {
770764
doInitialize(runtime);
771765
}
772766
}
773-
catch (Exception e) {
767+
catch (GeneralSecurityException e) { // cipher.doFinal
768+
throw newCipherError(runtime, e.getMessage());
769+
}
770+
catch (RuntimeException e) {
771+
if ( isDebug(runtime) ) e.printStackTrace( runtime.getOut() );
774772
throw newCipherError(runtime, e.getMessage());
775773
}
776774
return runtime.newString(str);

0 commit comments

Comments
 (0)