Date: Thu, 12 Aug 1999 15:56:09 -0400
From: Jonathan Polito - Lucent ASCC <jep@ascc.lucent.com>
To: java-security@java.sun.com
Subject: CFB Bug?
--------------61AD204211A74AA0EBD703CE
Content-Type: text/plain; charset="us-ascii"
X-Sun-Content-Length: 597
Using jce1_2-do.jar (latest JCE) CFB/NoPadding is not giving the same
results as other implementations, namely:
Cryptix 3.0.3 (java 1.1)
SSLeay/OpenSSL
Entrust/IAIK (java 1.2)
CBC mode gives the same results. I have run tests on Solaris/sparc and
Cygwin32/x86. Simply, if you encrypt some plaintext with the same key
and iv, you do not get the same results with Sun's JCE as you do with
the other packages (which all agree). Attached is the sample program for
Java2/JCE 1.2 (Solaris VM (build Solaris_JDK_1.2.1_03, native threads,
sunwjit) with JCE jce1_2-doc.jar).
Regards,
Jonathan Polito.
--------------61AD204211A74AA0EBD703CE
Content-Type: text/plain; charset="us-ascii"; name="TEno.java"
Content-Disposition: inline;
filename="TEno.java"
X-Sun-Content-Length: 22691
import java.security.*;
import java.security.interfaces.*;
import com.sun.crypto.provider.*;
import javax.crypto.*;
import javax.crypto.spec.*;
import java.io.PrintWriter;
/**
*
* <p>
* <b>Copyright</b> © 1999 Lucent Technologies
* <br>All rights reserved.
* <p>
* @author Jonathan E. Polito
* @version %W% %G%
*/
public class TEno
{
public static void main(String args[])
throws Exception {
if (args.length<1)
{
System.out.println("usage: java TEno plain-text");
System.exit(1);
}
// known key material
byte [] sk = {
(byte)0x00,(byte)0x01,(byte)0x02,(byte)0x03,
(byte)0x04,(byte)0x05,(byte)0x06,(byte)0x07
};
String mAlgorithm = "DES";
String mProvider = "SunJCE";
String mPadding = "/CFB8/NoPadding";
Provider sunJce = new com.sun.crypto.provider.SunJCE();
Security.addProvider(sunJce);
Cipher mEcipher = Cipher.getInstance(mAlgorithm+mPadding, mProvider);
// set up IV
int ivLen = mEcipher.getBlockSize();
// just use a zero IV
byte [] mIV = new byte[ivLen];
IvParameterSpec ivSpec = new IvParameterSpec(mIV);
SecretKey sessionKey = new SecretKeySpec(sk, mAlgorithm);
mEcipher.init(Cipher.ENCRYPT_MODE, sessionKey, ivSpec);
byte[] ect = null;
ect = mEcipher.doFinal(args[0].getBytes());
System.out.println(Hex.toString(ect));
}
}
// $Id: Hex.java,v 1.2 1998/01/11 08:19:36 raif Exp $
//
// $Log: Hex.java,v $
// Revision 1.2 1998/01/11 08:19:36 raif
// *** empty log message ***
//
// Revision 1.1.2 1998/01/06 raif
// + added dumpString(int[],...) methods.
//
// Revision 1.1.1.1 1997/11/20 21:04:51 hopwood
// + Moved these classes here from cryptix.core.util.*.
//
// Revision 1.1.1 1997/11/16 David Hopwood
// + Make dumpString return "null" when given a null array.
// + dumpString now uses a more concise format for short arrays
// (<= 32 bytes).
//
// Revision 1.1 1997/11/05 16:48:02 raif
// *** empty log message ***
//
// Revision 0.1.0.2 1997/08/03 David Hopwood
// + Minor documentation changes.
//
// Revision 0.1.0.1 1997/07/31 Raif Naffah
// + Removed circular reference to cryptix.mime.LegacyString.
// + Added toString(int[]...) methods.
//
// Revision 0.1.0.0 1997/07/21 David Hopwood
// + Original version (based on various other utility classes).
//
// $Endlog$
/*
* Copyright (c) 1997 Systemics Ltd
* on behalf of the Cryptix Development Team. All rights reserved.
*
* Derived partly from code Copyright (c) 1996 Type & Graphics Pty
* Limited. All rights reserved.
*/
/**
* Static methods for converting to and from hexadecimal strings.
* <p>
*
* <b>Copyright</b> © 1995-1997
* <a href="http://www.systemics.com/">Systemics Ltd</a> on behalf of the
* <a href="http://www.systemics.com/docs/cryptix/">Cryptix Development Team</a>.
* <br>All rights reserved.
*
* <p><b>$Revision: 1.2 $</b>
* @author David Hopwood
* @author Raif Naffah
* @author Systemics Ltd
* @since Cryptix 2.2.0a, 2.2.2
*/
class Hex
{
private Hex() {} // static methods only
private static final char[] hexDigits = {
'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'
};
/**
* Returns a string of hexadecimal digits from a byte array. Each
* byte is converted to 2 hex symbols.
* <p>
* If offset and length are omitted, the whole array is used.
*/
public static String toString(byte[] ba, int offset, int length) {
char[] buf = new char[length * 2];
int j = 0;
int k;
for (int i = offset; i < offset + length; i++) {
k = ba[i];
buf[j++] = hexDigits[(k >>> 4) & 0x0F];
buf[j++] = hexDigits[ k & 0x0F];
}
return new String(buf);
}
public static String toString(byte[] ba) {
return toString(ba, 0, ba.length);
}
/**
* Returns a string of hexadecimal digits from an integer array. Each
* int is converted to 4 hex symbols.
* <p>
* If offset and length are omitted, the whole array is used.
*/
public static String toString(int[] ia, int offset, int length) {
char[] buf = new char[length * 8];
int j = 0;
int k;
for (int i = offset; i < offset + length; i++) {
k = ia[i];
buf[j++] = hexDigits[(k >>> 28) & 0x0F];
buf[j++] = hexDigits[(k >>> 24) & 0x0F];
buf[j++] = hexDigits[(k >>> 20) & 0x0F];
buf[j++] = hexDigits[(k >>> 16) & 0x0F];
buf[j++] = hexDigits[(k >>> 12) & 0x0F];
buf[j++] = hexDigits[(k >>> 8) & 0x0F];
buf[j++] = hexDigits[(k >>> 4) & 0x0F];
buf[j++] = hexDigits[ k & 0x0F];
}
return new String(buf);
}
public static String toString(int[] ia) {
return toString(ia, 0, ia.length);
}
/**
* Returns a string of hexadecimal digits in reverse order from a byte array
* (i.e. the least significant byte is first, but within each byte the
* most significant hex digit is before the least significant hex digit).
* <p>
* If offset and length are omitted, the whole array is used.
*/
public static String toReversedString(byte[] b, int offset, int length) {
char[] buf = new char[length * 2];
int j = 0;
for (int i = offset + length - 1; i >= offset; i--) {
buf[j++] = hexDigits[(b[i] >>> 4) & 0x0F];
buf[j++] = hexDigits[ b[i] & 0x0F];
}
return new String(buf);
}
public static String toReversedString(byte[] b) {
return toReversedString(b, 0, b.length);
}
/**
* Returns a byte array from a string of hexadecimal digits.
*/
public static byte[] fromString(String hex) {
int len = hex.length();
byte[] buf = new byte[((len + 1) / 2)];
int i = 0, j = 0;
if ((len % 2) == 1)
buf[j++] = (byte) fromDigit(hex.charAt(i++));
while (i < len) {
buf[j++] = (byte) ((fromDigit(hex.charAt(i++)) << 4) |
fromDigit(hex.charAt(i++)));
}
return buf;
}
/**
* Returns a byte array from a string of hexadecimal digits in reverse
* order (i.e. the least significant byte is first, but within each byte the
* most significant hex digit is before the least significant hex digit).
* The string must have an even number of digits.
* <p>
* This is not really either little nor big-endian; it's just obscure. It is
* here because it is the format used for the SPEED certification data.
*/
public static byte[] fromReversedString(String hex) {
int len = hex.length();
byte[] buf = new byte[((len + 1) / 2)];
int j = 0;
if ((len % 2) == 1) throw new IllegalArgumentException(
"string must have an even number of digits");
while (len > 0) {
buf[j++] = (byte) (fromDigit(hex.charAt(--len)) |
(fromDigit(hex.charAt(--len)) << 4));
}
return buf;
}
/**
* Returns the hex digit corresponding to a number <i>n</i>, from 0 to 15.
*/
public static char toDigit(int n) {
try {
return hexDigits[n];
} catch (ArrayIndexOutOfBoundsException e) {
throw new IllegalArgumentException(n +
" is out of range for a hex digit");
}
}
/**
* Returns the number from 0 to 15 corresponding to the hex digit <i>ch</i>.
*/
public static int fromDigit(char ch) {
if (ch >= '0' && ch <= '9')
return ch - '0';
if (ch >= 'A' && ch <= 'F')
return ch - 'A' + 10;
if (ch >= 'a' && ch <= 'f')
return ch - 'a' + 10;
throw new IllegalArgumentException("invalid hex digit '" + ch + "'");
}
/**
* Returns a string of 2 hexadecimal digits (most significant digit first)
* corresponding to the lowest 8 bits of <i>n</i>.
*/
public static String byteToString(int n) {
char[] buf = { hexDigits[(n >>> 4) & 0x0F], hexDigits[n & 0x0F] };
return new String(buf);
}
/**
* Returns a string of 4 hexadecimal digits (most significant digit first)
* corresponding to the lowest 16 bits of <i>n</i>.
*/
public static String shortToString(int n) {
char[] buf = { hexDigits[(n >>> 12) & 0x0F], hexDigits[(n >>> 8) & 0x0F],
hexDigits[(n >>> 4) & 0x0F], hexDigits[ n & 0x0F] };
return new String(buf);
}
/**
* Returns a string of 8 hexadecimal digits (most significant digit first)
* corresponding to the integer <i>n</i>, which is treated as unsigned.
*/
public static String intToString(int n) {
char[] buf = new char[8];
for (int i = 7; i >= 0; i--) {
buf[i] = hexDigits[n & 0x0F];
n >>>= 4;
}
return new String(buf);
}
/**
* Returns a string of 16 hexadecimal digits (most significant digit first)
* corresponding to the long <i>n</i>, which is treated as unsigned.
*/
public static String longToString(long n) {
char[] buf = new char[16];
for (int i = 15; i >= 0; i--) {
buf[i] = hexDigits[(int)n & 0x0F];
n >>>= 4;
}
return new String(buf);
}
/**
* Dump a byte array as a string, in a format that is easy to read for
* debugging. The string <i>m</i> is prepended to the start of each line.
* <p>
* If offset and length are omitted, the whole array is used. If m is
* omitted, nothing is prepended to each line.
*
* @param data the byte array to be dumped
* @param offset the offset within <i>data</i> to start from
* @param length the number of bytes to dump
* @param m a string to be prepended to each line
* @return a String containing the dump.
*/
public static String dumpString(byte[] data, int offset, int length, String m) {
if (data == null) return m + "null\n";
StringBuffer sb = new StringBuffer(length * 3);
if (length > 32)
sb.append(m).append("Hexadecimal dump of ").append(length)
.append(" bytes...\n");
// each line will list 32 bytes in 4 groups of 8 each
int end = offset + length;
String s;
int l = Integer.toString(length).length();
if (l < 4) l = 4;
for (; offset < end; offset += 32) {
if (length > 32) {
s = " " + offset;
sb.append(m).append(s.substring(s.length()-l)).append(": ");
}
int i = 0;
for (; i < 32 && offset + i + 7 < end; i += 8)
sb.append(toString(data, offset + i, 8)).append(' ');
if (i < 32) {
for (; i < 32 && offset + i < end; i++)
sb.append(byteToString(data[offset + i]));
}
sb.append('\n');
}
return sb.toString();
}
public static String dumpString(byte[] data) {
return (data == null) ? "null\n"
: dumpString(data, 0, data.length, "");
}
public static String dumpString(byte[] data, String m) {
return (data == null) ? "null\n"
: dumpString(data, 0, data.length, m);
}
public static String dumpString(byte[] data, int offset, int length) {
return dumpString(data, offset, length, "");
}
/**
* Dump an int array as a string, in a format that is easy to read for
* debugging. The string <i>m</i> is prepended to the start of each line.
* <p>
* If offset and length are omitted, the whole array is used. If m is
* omitted, nothing is prepended to each line.
*
* @param data The int[] to dump
* @param offset The offset within <i>data</i> to start from
* @param length The number of ints to dump
* @param m A string to prepend to each line
* @return A String containing the dump.
*/
public static String dumpString(int[] data, int offset, int length, String m)
{
if (data == null) return m + "null\n";
StringBuffer sb = new StringBuffer(length * 3);
if (length > 8)
sb.append(m).append("Hexadecimal dump of ").append(length)
.append(" integers...\n");
// each line will list 32 bytes in 8 groups of 4 each (1 int)
int end = offset + length;
String s;
int x = Integer.toString(length).length();
if (x < 8) x = 8;
for ( ; offset < end; ) {
if (length > 8) {
s = " " + offset;
sb.append(m).append(s.substring(s.length()-x)).append(": ");
}
for (int i = 0; i < 8 && offset < end; i++)
sb.append(intToString(data[offset++])).append(' ');
sb.append('\n');
}
return sb.toString();
}
public static String dumpString(int[] data) {
return dumpString(data, 0, data.length, "");
}
public static String dumpString(int[] data, String m) {
return dumpString(data, 0, data.length, m);
}
public static String dumpString(int[] data, int offset, int length) {
return dumpString(data, offset, length, "");
}
// Test methods
//...........................................................................
public static void main(String[] args) {
self_test(new PrintWriter(System.out, true));
}
public static void self_test(PrintWriter out) {
String test = "Hello. This is a test string with more than 32 characters.";
byte[] buf = new byte[test.length()];
for (int i = 0; i < test.length(); i++) {
buf[i] = (byte) test.charAt(i);
}
String s;
byte[] buf2;
s = toString(buf);
out.println("Hex.toString(buf) = " + s);
buf2 = fromString(s);
if (!ArrayUtil.areEqual(buf, buf2))
System.out.println("buf != buf2");
s = toReversedString(buf);
out.println("Hex.toReversedString(buf) = " + s);
buf2 = fromReversedString(s);
if (!ArrayUtil.areEqual(buf, buf2))
out.println("buf != buf2");
out.print("Hex.dumpString(buf, 0, 28) =\n" + dumpString(buf, 0, 28));
out.print("Hex.dumpString(null) =\n" + dumpString((byte[]) null));
out.print(dumpString(buf, "+++"));
out.flush();
}
}
// $Id: ArrayUtil.java,v 1.6 1998/01/11 08:19:36 raif Exp $
//
// $Log: ArrayUtil.java,v $
// Revision 1.6 1998/01/11 08:19:36 raif
// *** empty log message ***
//
// Revision 1.5.1 1997/12/28 raif
// + cosmetics.
//
// Revision 1.5 1997/12/11 10:05:40 ianb
// Raif fixed it!
//
// Revision 1.4 1997/12/10 12:34:39 ianb
// Tried to fix isText() - still not working properly.
//
// Revision 1.3 1997/12/10 12:11:57 ianb
// Added isText() method
//
// 1997.12.09 --Ian Brown
// + added isText() method.
//
// Revision 1.2 1997/12/07 07:33:34 hopwood
// + Reduce size of zeroes array.
//
// Revision 1.1.1.1 1997/11/20 21:04:51 hopwood
// + Moved these classes here from cryptix.core.util.*.
//
// Revision 1.1.1 1997/11/20 David Hopwood
// + Added areEqual(int[], int[]).
//
// Revision 1.1 1997/11/05 16:48:02 raif
// *** empty log message ***
//
// Revision 0.1.4 1997/10/01 09:04:24 raif
// *** empty log message ***
//
// Revision 0.1.3 1997/09/29 13:19:10 raif
// *** empty log message ***
//
// Revision 0.1.0.1 1997/09/03 R. Naffah
// + Added compared([B,[B,Z).
//
// Revision 0.1.0.0 1997/07/21 David Hopwood
// + Initial version (based on Raif Naffah's subrosa.util.Util).
//
// $Endlog$
/*
* Copyright (c) 1997 Systemics Ltd
* on behalf of the Cryptix Development Team. All rights reserved.
*
* Derived partly from code Copyright (c) 1996 Type & Graphics Pty
* Limited. All rights reserved.
*/
/**
* Static methods for converting between arrays of various types, for clearing
* all or part of a byte array, and for comparing two byte arrays.
* <p>
* <b>Copyright</b> © 1997
* <a href="http://www.systemics.com/">Systemics Ltd</a> on behalf of the
* <a href="http://www.systemics.com/docs/cryptix/">Cryptix Development Team</a>.
* <br>All rights reserved.
* <p>
* <b>$Revision: 1.6 $</b>
* @since Cryptix 2.2.2
* @author Raif Naffah
* @author David Hopwood
* @author Ian Brown
*/
class ArrayUtil
{
private ArrayUtil() {} // static methods only
private static final int ZEROES_LEN = 500; // adjust for memory/speed tradeoff
private static byte[] zeroes = new byte[ZEROES_LEN];
/**
* Clears a byte array to all-zeroes.
*/
public static void clear (byte[] buf) { clear(buf, 0, buf.length); }
/**
* Clears <i>length</i> bytes of a byte array to zeroes, starting at
* <i>offset</i>.
*/
public static void clear (byte[] buf, int offset, int length) {
if (length <= ZEROES_LEN)
System.arraycopy(zeroes, 0, buf, offset, length);
else {
System.arraycopy(zeroes, 0, buf, offset, ZEROES_LEN);
int halflength = length / 2;
for (int i = ZEROES_LEN; i < length; i += i) {
System.arraycopy(buf, offset, buf, offset+i,
(i <= halflength) ? i : length-i);
}
}
}
/**
* Returns an int built from two shorts.
*
* @param s0 the least significant short
* @param s1 the most significant short
*/
public static int toInt(short s0, short s1) { return (s0 & 0xFFFF) | (s1 << 16); }
/**
* Returns a short built from two bytes.
*
* @param b0 the least significant byte
* @param b1 the most significant byte
*/
public static short toShort(byte b0, byte b1) { return (short) (b0 & 0xFF | b1 << 8); }
/**
* Returns a 4-byte array built from an int. The int's MSB is first
* (big-endian order).
*/
public static byte[] toBytes(int n) {
byte[] buf = new byte[4];
for (int i = 3; i >= 0; i--) {
buf[i] = (byte) (n & 0xFF);
n >>>= 8;
}
return buf;
}
/**
* Returns a byte array built from a short array. Each short is broken
* into 2 bytes with the short's MSB first (big-endian order).
* <p>
* If offset and length are omitted, the whole array is used.
*/
public static byte[] toBytes(short[] array, int offset, int length) {
byte[] buf = new byte[2 * length];
int j = 0;
for (int i = offset; i < offset + length; i++) {
buf[j++] = (byte) ((array[i] >>> 8) & 0xFF);
buf[j++] = (byte) (array[i] & 0xFF);
}
return buf;
}
public static byte[] toBytes(short[] array) { return toBytes(array, 0, array.length); }
/**
* Returns a short array built from a byte array. Each 2 bytes form
* a short with the first byte as the short's MSB (big-endian order).
* <p>
* If offset and length are omitted, the whole array is used.
*/
public static short[] toShorts(byte[] array, int offset, int length) {
short[] buf = new short[length / 2];
int j = 0;
for (int i = offset; i < offset + length - 1; i += 2)
buf[j++] = (short) (((array[i] & 0xFF) << 8) | (array[i + 1] & 0xFF));
return buf;
}
public static short[] toShorts(byte[] array) { return toShorts(array, 0, array.length); }
/**
* Compares two byte arrays for equality.
*
* @return true if the arrays have identical contents
*/
public static boolean areEqual(byte[] a, byte[] b) {
int aLength = a.length;
if (aLength != b.length) return false;
for (int i = 0; i < aLength; i++)
if (a[i] != b[i]) return false;
return true;
}
/**
* Compares two int arrays for equality.
*
* @return true if the arrays have identical contents
*/
public static boolean areEqual(int[] a, int[] b) {
int aLength = a.length;
if (aLength != b.length) return false;
for (int i = 0; i < aLength; i++)
if (a[i] != b[i]) return false;
return true;
}
/*
* Compare two byte arrays returning -1, 0 or +1 if the first argument
* is less than, equal to, or greater than the second one. Both arguments
* are assumed to have the same order of byte significance.
* <p>
* When last argument is true, the comparison assumes the MSB (Most
* Significant Byte) is at the highest index position, and when it's
* false, the contrary; i.e. MSB at [0].
*
* @since Cryptix 2.2.2
* @return -1, 0 or 1 if a < b, a == b and a > b respectively.
*/
public static int compared (byte[] a, byte[] b, boolean msbFirst) {
int aLength = a.length;
if (aLength < b.length) return -1;
if (aLength > b.length) return 1;
int b1, b2;
if (msbFirst)
for (int i = aLength - 1; i >= 0; i--) {
b1 = a[i] & 0xFF;
b2 = b[i] & 0xFF;
if (b1 < b2) return -1;
if (b1 > b2) return 1;
}
else
for (int i = 0; i < aLength; i++) {
b1 = a[i] & 0xFF;
b2 = b[i] & 0xFF;
if (b1 < b2) return -1;
if (b1 > b2) return 1;
}
return 0;
}
/** @return true If the data in the byte array consists of just text. */
public static boolean isText (byte[] buffer) {
int len = buffer.length;
if (len == 0) return false;
for (int i = 0; i < len; i++) {
int c = buffer[i] & 0xFF;
if (c < '\u0020' || c > '\u007F')
switch (c) { // control chars that are allowed in text files
case '\u0007': // BEL
case '\u0008': // BS
case '\t': // HT
case '\n': // LF
case '\u000B': // VT
case '\u000C': // FF
case '\r': // CR
case '\u001A': // EOF
case '\u001B': // ESC
case '\u009B': // CSI
continue;
default: // anything else, isn't.
return false;
}
}
return true;
}
}
--------------61AD204211A74AA0EBD703CE
Content-Type: text/x-vcard; charset="us-ascii"; name="jep.vcf"
Content-Description: Card for Jonathan Polito - Lucent ASCC
Content-Disposition: attachment;
filename="jep.vcf"
X-Sun-Content-Length: 402
begin:vcard
n:Polito;Jonathan
tel;fax:+1 919 380 4640
tel;work:+1 919 380 4585
x-mozilla-html:TRUE
org:Internet/IN<BR><A HREF=http://www.lucent.com><IMG BORDER=0 SRC=http://www.lucent.com/shared/images/logo_ft.gif></A>
version:2.1
email;internet:jepolito@lucent.com
adr;quoted-printable:;;Lucent Technologies=0D=0A2000 Regency Pkwy;Cary;NC;27511;USA
x-mozilla-cpt:;-12224
fn:Jonathan Polito
end:vcard
--------------61AD204211A74AA0EBD703CE--