Title: [1111] trunk/jopenssl: Fix for JRUBY-1692, making sure that the IV gets updated in the same way OpenSSL does it.
Revision
1111
Author
olabini
Date
2008-08-13 09:18:12 -0400 (Wed, 13 Aug 2008)

Log Message

Fix for JRUBY-1692, making sure that the IV gets updated in the same way OpenSSL does it. Also make DSS digest work the same as SHA in more places.

Modified Paths

Diff

Modified: trunk/jopenssl/src/java/org/jruby/ext/openssl/Cipher.java (1110 => 1111)


--- trunk/jopenssl/src/java/org/jruby/ext/openssl/Cipher.java	2008-08-12 15:54:11 UTC (rev 1110)
+++ trunk/jopenssl/src/java/org/jruby/ext/openssl/Cipher.java	2008-08-13 13:18:12 UTC (rev 1111)
@@ -57,7 +57,6 @@
  * @author <a href="" PROTECTED]">Ola Bini</a>
  */
 public class Cipher extends RubyObject {
- 
     // set to enable debug output
     private static final boolean DEBUG = false;
     private static ObjectAllocator CIPHER_ALLOCATOR = new ObjectAllocator() {
@@ -197,7 +196,8 @@
     //private IRubyObject[] modeParams;
     private boolean ciphInited = false;
     private byte[] key;
-    private byte[] iv;
+    private byte[] realIV;
+    private byte[] orgIV;
     private String padding;
     
     void dumpVars() {
@@ -214,8 +214,9 @@
         System.out.println("encryptMode = " + encryptMode);
         System.out.println("ciphInited = " + ciphInited);
         System.out.println("key.length = " + (key == null ? 0 : key.length));
-        System.out.println("iv.length = " + (iv == null ? 0 : iv.length));
+        System.out.println("iv.length = " + (this.realIV == null ? 0 : this.realIV.length));
         System.out.println("padding = " + padding);
+        System.out.println("ciphAlgo = " + ciph.getAlgorithm());
         System.out.println("*******************************");
     }
 
@@ -293,12 +294,13 @@
         } else {
             key = null;
         }
-        if(((Cipher)obj).iv != null) {
-            iv = new byte[((Cipher)obj).iv.length];
-            System.arraycopy(((Cipher)obj).iv,0,iv,0,iv.length);
+        if(((Cipher)obj).realIV != null) {
+            this.realIV = new byte[((Cipher)obj).realIV.length];
+            System.arraycopy(((Cipher)obj).realIV,0,this.realIV,0,this.realIV.length);
         } else {
-            iv = null;
+            this.realIV = null;
         }
+        this.orgIV = this.realIV;
         padding = ((Cipher)obj).padding;
 
         ciph = getCipher();
@@ -362,7 +364,8 @@
         if(ivBytes.length < ivLen) {
             throw new RaiseException(getRuntime(), ciphErr, "iv length to short", true);
         }
-        this.iv = ivBytes;
+        this.realIV = ivBytes;
+        this.orgIV = this.realIV;
         return iv;
     }
 
@@ -414,24 +417,28 @@
 
             OpenSSLImpl.KeyAndIv result = OpenSSLImpl.EVP_BytesToKey(keyLen,ivLen,digest,iv,pass,2048);
             this.key = result.getKey();
-            this.iv = iv;
+            this.realIV = iv;
+            this.orgIV = this.realIV;
         }
     }
 
     @JRubyMethod(optional=2)
     public IRubyObject encrypt(IRubyObject[] args) {
+        this.realIV = orgIV;
         init(args, true);
         return this;
     }
 
     @JRubyMethod(optional=2)
     public IRubyObject decrypt(IRubyObject[] args) {
+        this.realIV = orgIV;
         init(args, false);
         return this;
     }
 
     @JRubyMethod
     public IRubyObject reset() {
+        this.realIV = orgIV;
         doInitialize();
         return this;
     }
@@ -501,7 +508,8 @@
 
         OpenSSLImpl.KeyAndIv result = OpenSSLImpl.EVP_BytesToKey(keyLen, ivLen, digest, salt, pass, iter);
         this.key = result.getKey();
-        this.iv = result.getIv();
+        this.realIV = result.getIv();
+        this.orgIV = this.realIV;
 
         doInitialize();
 
@@ -516,9 +524,9 @@
         ciphInited = true;
         try {
             assert (key.length * 8 == keyLen) || (key.length == keyLen) : "Key wrong length";
-            assert (iv.length * 8 == ivLen) || (iv.length == ivLen): "IV wrong length";
-            if(!"ECB".equalsIgnoreCase(cryptoMode) && this.iv != null) {
-                this.ciph.init(encryptMode ? javax.crypto.Cipher.ENCRYPT_MODE : javax.crypto.Cipher.DECRYPT_MODE, new SimpleSecretKey(this.key), new IvParameterSpec(this.iv));
+            assert (this.realIV.length * 8 == ivLen) || (this.realIV.length == ivLen): "IV wrong length";
+            if(!"ECB".equalsIgnoreCase(cryptoMode) && this.realIV != null) {
+                this.ciph.init(encryptMode ? javax.crypto.Cipher.ENCRYPT_MODE : javax.crypto.Cipher.DECRYPT_MODE, new SimpleSecretKey(this.key), new IvParameterSpec(this.realIV));
             } else {
                 this.ciph.init(encryptMode ? javax.crypto.Cipher.ENCRYPT_MODE : javax.crypto.Cipher.DECRYPT_MODE, new SimpleSecretKey(this.key));
             }
@@ -528,11 +536,12 @@
         }
     }
 
+    private byte[] lastIv = null;
+
     @JRubyMethod
     public IRubyObject update(IRubyObject data) {
         if (DEBUG) System.out.println("*** update ["+data+"]");
 
-        //TODO: implement correctly
         byte[] val = data.convertToString().getBytes();
         if(val.length == 0) {
             throw getRuntime().newArgumentError("data must not be empty");
@@ -547,10 +556,20 @@
             byte[] out = ciph.update(val);
             if(out != null) {
                 str = out;
+
+                if(this.realIV != null) {
+                    if(lastIv == null) {
+                        lastIv = new byte[ivLen];
+                    }
+                    byte[] tmpIv = encryptMode ? out : val;
+                    if(tmpIv.length >= ivLen) {
+                        System.arraycopy(tmpIv, tmpIv.length-ivLen, lastIv, 0, ivLen);
+                    }
+                }
             }
         } catch(Exception e) {
             e.printStackTrace();
-            throw new RaiseException(getRuntime(), ciphErr, null, true);
+            throw new RaiseException(getRuntime(), ciphErr, e.getMessage(), true);
         }
 
         return RubyString.newString(getRuntime(), new ByteList(str,false));
@@ -568,13 +587,26 @@
             doInitialize();
         }
 
-        //TODO: implement correctly
         ByteList str = new ByteList(ByteList.NULL_ARRAY);
         try {
             byte[] out = ciph.doFinal();
             if(out != null) {
                 str = new ByteList(out,false);
+                if(this.realIV != null && encryptMode) {
+                    if(lastIv == null) {
+                        lastIv = new byte[ivLen];
+                    }
+                    byte[] tmpIv = out;
+                    if(tmpIv.length >= ivLen) {
+                        System.arraycopy(tmpIv, tmpIv.length-ivLen, lastIv, 0, ivLen);
+                    }
+                }
             }
+
+            if(this.realIV != null) {
+                this.realIV = lastIv;
+                doInitialize();
+            }
         } catch(Exception e) {
             throw new RaiseException(getRuntime(), ciphErr, e.getMessage(), true);
         }

Modified: trunk/jopenssl/src/java/org/jruby/ext/openssl/Digest.java (1110 => 1111)


--- trunk/jopenssl/src/java/org/jruby/ext/openssl/Digest.java	2008-08-12 15:54:11 UTC (rev 1110)
+++ trunk/jopenssl/src/java/org/jruby/ext/openssl/Digest.java	2008-08-13 13:18:12 UTC (rev 1111)
@@ -114,6 +114,9 @@
     private StringBuffer data;
     private String name;
 
+    public String getRealName() {
+        return transformDigest(name);
+    }
 
     public String getName() {
         return name;

Modified: trunk/jopenssl/src/java/org/jruby/ext/openssl/PKey.java (1110 => 1111)


--- trunk/jopenssl/src/java/org/jruby/ext/openssl/PKey.java	2008-08-12 15:54:11 UTC (rev 1110)
+++ trunk/jopenssl/src/java/org/jruby/ext/openssl/PKey.java	2008-08-13 13:18:12 UTC (rev 1111)
@@ -131,12 +131,14 @@
         }
         byte[] sigBytes = ((RubyString)sig).getBytes();
         byte[] dataBytes = ((RubyString)data).getBytes();
-        String algorithm = ((Digest)digest).getName() + "with" + getAlgorithm();
+        String algorithm = ((Digest)digest).getRealName() + "with" + getAlgorithm();
         boolean valid;
         try {
             // note: not specifying "BC" provider here, as that would fail if
             // BC wasn't plugged in (as it would not be for, say, Net::SSH)
-            Signature signature = Signature.getInstance(algorithm);
+            Signature signature = OpenSSLReal.PROVIDER == null ? 
+                Signature.getInstance(algorithm) :
+                Signature.getInstance(algorithm, OpenSSLReal.PROVIDER);
             signature.initVerify(getPublicKey());
             signature.update(dataBytes);
             valid = signature.verify(sigBytes);

Modified: trunk/jopenssl/test/openssl/test_cipher.rb (1110 => 1111)


--- trunk/jopenssl/test/openssl/test_cipher.rb	2008-08-12 15:54:11 UTC (rev 1110)
+++ trunk/jopenssl/test/openssl/test_cipher.rb	2008-08-13 13:18:12 UTC (rev 1111)
@@ -1,3 +1,10 @@
+if defined?(JRUBY_VERSION)
+  require "java"
+  base = File.join(File.dirname(__FILE__), '..', '..')
+  $CLASSPATH << File.join(base, 'pkg', 'classes')
+  $CLASSPATH << File.join(base, 'lib', 'bcprov-jdk14-139.jar')
+end
+
 begin
   require "openssl"
 rescue LoadError

Modified: trunk/jopenssl/test/test_cipher.rb (1110 => 1111)


--- trunk/jopenssl/test/test_cipher.rb	2008-08-12 15:54:11 UTC (rev 1110)
+++ trunk/jopenssl/test/test_cipher.rb	2008-08-13 13:18:12 UTC (rev 1111)
@@ -1,3 +1,10 @@
+if defined?(JRUBY_VERSION)
+  require "java"
+  base = File.dirname(__FILE__)
+  $CLASSPATH << File.join(base, '..', 'pkg', 'classes')
+  $CLASSPATH << File.join(base, '..', 'lib', 'bcprov-jdk14-139.jar')
+end
+
 begin
   require "openssl"
 rescue LoadError
@@ -12,4 +19,63 @@
     data = ""
     data << enc.final
   end
+
+  IV_TEMPLATE  = "aaaabbbbccccddddeeeeffffgggghhhhiiiijjjjj"
+  KEY_TEMPLATE = "aaaabbbbccccddddeeeeffffgggghhhhiiiijjjjj"
+
+  # JRUBY-1692
+  def test_repeated_des
+    do_repeated_test(
+                     "des-ede3-cbc",
+                     "foobarbazboofarf",
+                     ":\022Q\211ex\370\332\374\274\214\356\301\260V\025",
+                     "B\242\3531\003\362\3759\363s\203\374\240\030|\230"
+                     )
+  end
+
+  # JRUBY-1692
+  def test_repeated_aes
+    do_repeated_test(
+                     "aes-128-cbc",
+                     "foobarbazboofarf",
+                     "\342\260Y\344\306\227\004^\272|/\323<\016,\226",
+                     "jqO\305/\211\216\b\373\300\274\bw\213]\310"
+                     )
+  end
+
+  private
+  def do_repeated_test(algo, string, enc1, enc2)
+    do_repeated_encrypt_test(algo, string, enc1, enc2)
+    do_repeated_decrypt_test(algo, string, enc1, enc2)
+  end
+  
+  def do_repeated_encrypt_test(algo, string, result1, result2)
+    cipher = OpenSSL::Cipher::Cipher.new(algo)
+    cipher.encrypt
+
+    cipher.padding = 0
+    cipher.iv      = IV_TEMPLATE[0, cipher.iv_len]
+    cipher.key     = KEY_TEMPLATE[0, cipher.key_len]
+
+    assert_equal result1, cipher.update(string)
+    cipher.final
+
+    assert_equal result2, cipher.update(string)
+    cipher.final
+  end
+
+  def do_repeated_decrypt_test(algo, result, string1, string2)
+    cipher = OpenSSL::Cipher::Cipher.new(algo)
+    cipher.decrypt
+
+    cipher.padding = 0
+    cipher.iv      = IV_TEMPLATE[0, cipher.iv_len]
+    cipher.key     = KEY_TEMPLATE[0, cipher.key_len]
+
+    assert_equal result, cipher.update(string1)
+    cipher.final
+
+    assert_equal result, cipher.update(string2)
+    cipher.final
+  end
 end
_______________________________________________
Jruby-extras-devel mailing list
[email protected]
http://rubyforge.org/mailman/listinfo/jruby-extras-devel

Reply via email to