Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
BEGIN LICENSE BLOCK ***** Version: CPL 1.0/GPL 2.0/LGPL 2.1 The contents of this file are subject to the Common Public License Version 1.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.eclipse.org/legal/cpl-v10.html Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. Copyright (C) 2007 William N Dortch <bill.dortch@gmail.com> Alternatively, the contents of this file may be used under the terms of either of the GNU General Public License Version 2 or later (the "GPL"), or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), in which case the provisions of the GPL or the LGPL are applicable instead of those above. If you wish to allow use of your version of this file only under the terms of either the GPL or the LGPL, and not to allow others to use your version of this file under the terms of the CPL, indicate your decision by deleting the provisions above and replace them with the notice and other provisions required by the GPL or the LGPL. If you do not delete the provisions above, a recipient may use your version of this file under the terms of any one of the CPL, the GPL or the LGPL. END LICENSE BLOCK ***
 
 package org.jruby.ext.openssl;
 
 
 
 import org.jruby.Ruby;
OpenSSL::PKey::DH implementation.

Author(s):
Bill Dortch
 
 public class PKeyDH extends PKey {
     private static final long serialVersionUID = 293266329939132250L;
     
     // parameters used in generating 'p'; see [ossl]/crypto/dh/dh_gen.c #dh_builtin_genparams
     private static final BigInteger GEN_2_ADD_PARAM = BigInteger.valueOf(24);
     private static final BigInteger GEN_2_REM_PARAM = BigInteger.valueOf(11);
     private static final BigInteger GEN_5_ADD_PARAM = BigInteger.valueOf(10);
     private static final BigInteger GEN_5_REM_PARAM = BigInteger.valueOf(3);
     private static final BigInteger DEFAULT_ADD_PARAM = BigInteger.valueOf(2);
     private static final BigInteger DEFAULT_REM_PARAM = .;
 
     private static final BigInteger TWO = BigInteger.valueOf(2);
     
     // from [ossl]/crypto/dh/dh.h
     private static final int OPENSSL_DH_MAX_MODULUS_BITS = 10000;
 
     private static ObjectAllocator PKEYDH_ALLOCATOR = new ObjectAllocator() {
         public IRubyObject allocate(Ruby runtimeRubyClass klass) {
             return new PKeyDH(runtimeklass);
         }
     };
     
     public static void createPKeyDH(Ruby runtimeRubyModule pkeyModuleRubyClass pkeyClass) {
         RubyClass dh = pkeyModule.defineClassUnder("DH"pkeyClass);
 
         RubyClass pkeyError = pkeyModule.getClass("PKeyError");
         pkeyModule.defineClassUnder("DHError",pkeyError,pkeyError.getAllocator());
         
         dh.defineAnnotatedMethods(PKeyDH.class);
     }
     
     public static RaiseException newDHError(Ruby runtimeString message) {
         return Utils.newError(runtime"OpenSSL::PKey::DHError"message);
     }
     
     private static SecureRandom _secureRandom;
     
     private static SecureRandom getSecureRandom() {
         SecureRandom rand;
         if ((rand = ) != null) {
             return rand;
        }
        // FIXME: do we want a particular algorithm / provider? BC?
        return  = new SecureRandom();
    }
    // transient because: we do not want these value serialized (insecure)
    // volatile because: permits unsynchronized reads in some cases
    private transient volatile BigInteger dh_p;
    private transient volatile BigInteger dh_g;
    private transient volatile BigInteger dh_pub_key;
    private transient volatile BigInteger dh_priv_key;
    
    // FIXME! need to figure out what it means in MRI/OSSL code to
    // claim a DH is(/has) private if an engine is present -- doesn't really
    // map to Java implementation.
    //private volatile boolean haveEngine;
    
    public PKeyDH(Ruby runtimeRubyClass clazz) {
        super(runtimeclazz);
    }
    @JRubyMethod(name="initialize", rest=true)
    public synchronized IRubyObject dh_initialize(IRubyObject[] args) {
        Ruby runtime = getRuntime();
        if (this. != null || this. != null || this. != null || this. != null) {
            throw newDHError(runtime"illegal initialization");
        }
        int argc = Arity.checkArgumentCount(runtimeargs, 0, 2);
        if (argc > 0) {
            IRubyObject arg0 = args[0];
            if (argc == 1 && arg0 instanceof RubyString) {
                try {
                    DHParameterSpec spec = PEMInputOutput.readDHParameters(new StringReader(arg0.toString()));
                    if (spec == null) {
                        spec = org.jruby.ext.openssl.impl.PKey.readDHParameter(arg0.asString().getByteList().bytes());
                    }
                    if (spec == null) {
                        throw runtime.newArgumentError("invalid DH PARAMETERS");
                    }
                    this. = spec.getP();
                    this. = spec.getG();
                } catch (NoClassDefFoundError ncdfe) {
                    throw newDHError(runtime, OpenSSLReal.bcExceptionMessage(ncdfe));
                } catch (IOException e) {
                    throw runtime.newIOErrorFromException(e);
                }
            } else {
                int bits = RubyNumeric.fix2int(arg0);
                // g defaults to 2
                int gval = argc == 2 ? RubyNumeric.fix2int(args[1]) : 2;
                BigInteger p;
                try {
                    p = generateP(bitsgval);
                } catch(IllegalArgumentException e) {
                    throw runtime.newArgumentError(e.getMessage());
                }
                BigInteger g = BigInteger.valueOf(gval);
                BigInteger x = generateX(p);
                BigInteger y = generateY(pgx);
                this. = p;
                this. = g;
                this. = x;
                this. = y;
            }
        }
        return this;
    }
    
    public static BigInteger generateP(int bitsint g) {
        
        // FIXME? I'm following algorithms used in OpenSSL, could use JCE provider instead.
        // (Note that I tried that, but got mystifying values of g returned by the param generator.
        // In any case, in OpenSSL/MRI-OpenSSL, the caller supplies g, or it defaults to 2.)
        
        // see [ossl]/crypto/dh/dh_gen.c #dh_builtin_genparams
        
        if (bits < 2) throw new IllegalArgumentException("invalid bit length");
        if (g < 2) throw new IllegalArgumentException("invalid generator");
        
        // generate safe prime meeting appropriate add/rem (mod) criteria
        
        switch(g) {
        case 2:
            // add = 24, rem = 11
            return BN.generatePrime(bitstrue);
        case 5:
            // add = 10, rem = 3
            return BN.generatePrime(bitstrue);
        default:
            // add = 2, rem = 1
            return BN.generatePrime(bitstrue);
        }
    }
    
    public static BigInteger generateX(BigInteger pint limit) {
        if (limit < 0) throw new IllegalArgumentException("invalid limit");
        
        BigInteger x;
        SecureRandom secureRandom = getSecureRandom();
        // adapting algorithm from org.bouncycastle.crypto.generators.DHKeyGeneratorHelper,
        // which seems a little stronger (?) than OpenSSL's (OSSL just generates a random,
        // while BC generates a random potential prime [for limit > 0], though it's not 
        // subject to Miller-Rabin [certainty = 0], but is subject to other constraints)
        // see also [ossl]/crypto/dh/dh_key.c #generate_key
        if (limit == 0) {
            BigInteger pSub2 = p.subtract();
            do {
                x = BN.getRandomBIInRange(pSub2secureRandom);
            } while (x.equals(.));
        } else {
            do {
                // generate potential prime, though with 0 certainty (no Miller-Rabin tests)
                x = new BigInteger(limit, 0, secureRandom);
            } while (x.equals(.));
        }
        return x;
    }
    
    public static BigInteger generateX(BigInteger p) {
        // OpenSSL default l(imit) is p bits - 1 -- see [ossl]/crypto/dh/dh_key.c #generate_key
        return generateX(pp.bitLength() - 1);
    }
    
    public static BigInteger generateY(BigInteger pBigInteger gBigInteger x) {
        return g.modPow(xp);
    }
    
    public static BigInteger generateY(BigInteger pint gBigInteger x) {
        return generateY(p, BigInteger.valueOf(g), x);
    }
    
    @JRubyMethod(name="generate_key!")
    public synchronized IRubyObject dh_generate_key() {
        BigInteger pgxy;
        if ((p = this.) == null || (g = this.) == null) {
            throw newDHError(getRuntime(), "can't generate key");
        }
        if ((x = this.) == null) {
            x = generateX(p);
        }
        y = generateY(pgx);
        this. = x;
        this. = y;
        return this;
    }
    
    @JRubyMethod(name="compute_key")
    public synchronized IRubyObject dh_compute_key(IRubyObject other_pub_key) {
        BigInteger xyp;
        if ((y = BN.getBigInteger(other_pub_key)) == null) {
            throw getRuntime().newArgumentError("invalid public key");
        }
        if ((x = this.) == null || (p = this.) == null) {
            throw newDHError(getRuntime(), "can't compute key");
        }
        int plen;
        if ((plen = p.bitLength()) == 0 || plen > ) {
            throw newDHError(getRuntime(), "can't compute key");
        }
        return getRuntime().newString(new ByteList(computeKey(yxp), false));
    }
    public static byte[] computeKey(BigInteger yBigInteger xBigInteger p) {
        return y.modPow(xp).toByteArray();
    }
    
    @JRubyMethod(name="public?")
    public IRubyObject dh_is_public() {
        return getRuntime().newBoolean( != null);
    }
    
    @JRubyMethod(name="private?")
    public IRubyObject dh_is_private() {
        // FIXME! need to figure out what it means in MRI/OSSL code to
        // claim a DH is private if an engine is present -- doesn't really
        // map to Java implementation.
        return getRuntime().newBoolean( != null /* || haveEngine */);
    }
    
    @JRubyMethod(name={"export""to_pem""to_s"})
    public IRubyObject dh_export() {
        BigInteger pg;
        synchronized(this) {
            p = this.;
            g = this.;
        }
        StringWriter w = new StringWriter();
        try {
            PEMInputOutput.writeDHParameters(wnew DHParameterSpec(pg));
            w.flush();
            w.close();
        } catch (NoClassDefFoundError ncdfe) {
            throw newDHError(getRuntime(), OpenSSLReal.bcExceptionMessage(ncdfe));
        } catch (IOException e) {
            // shouldn't happen (string/buffer io only)
            throw getRuntime().newIOErrorFromException(e);
        }
        return getRuntime().newString(w.toString());
    }
    
    @JRubyMethod(name = "to_der")
    public IRubyObject dh_to_der() {
        BigInteger pg;
        synchronized (this) {
            p = this.;
            g = this.;
        }
        try {
            byte[] bytes = org.jruby.ext.openssl.impl.PKey.toDerDHKey(pg);
            return RubyString.newString(getRuntime(), bytes);
        } catch (NoClassDefFoundError ncdfe) {
            throw newDHError(getRuntime(), OpenSSLReal.bcExceptionMessage(ncdfe));
        } catch (IOException ioe) {
            throw newDHError(getRuntime(), ioe.getMessage());
        }
    }
    
    @JRubyMethod(name="params")
    public IRubyObject dh_get_params() {
        BigInteger pgxy;
        synchronized(this) {
            p = this.;
            g = this.;
            x = this.;
            y = this.;
        }
        Ruby runtime = getRuntime();
        HashMap<IRubyObjectIRubyObjectparams = new HashMap<IRubyObjectIRubyObject>();
        
        params.put(runtime.newString("p"), BN.newBN(runtimep));
        params.put(runtime.newString("g"), BN.newBN(runtimeg));
        params.put(runtime.newString("pub_key"), BN.newBN(runtimex));
        params.put(runtime.newString("priv_key"), BN.newBN(runtimey));
        
        return RubyHash.newHash(runtimeparamsruntime.getNil());
    }
    
    // don't need synchronized as value is volatile
    @JRubyMethod(name="p")
    public IRubyObject dh_get_p() {
        return getBN();
    }
    
    @JRubyMethod(name="p=")
    public synchronized IRubyObject dh_set_p(IRubyObject arg) {
        this. = BN.getBigInteger(arg);
        return arg;
    }
    // don't need synchronized as value is volatile
    @JRubyMethod(name="g")
    public IRubyObject dh_get_g() {
        return getBN();
    }
    
    @JRubyMethod(name="g=")
    public synchronized IRubyObject dh_set_g(IRubyObject arg) {
        this. = BN.getBigInteger(arg);
        return arg;
    }
    // don't need synchronized as value is volatile
    @JRubyMethod(name="pub_key")
    public IRubyObject dh_get_pub_key() {
        return getBN();
    }
    @JRubyMethod(name="pub_key=")
    public synchronized IRubyObject dh_set_pub_key(IRubyObject arg) {
        this. = BN.getBigInteger(arg);
        return arg;
    }
    // don't need synchronized as value is volatile
    @JRubyMethod(name="priv_key")
    public IRubyObject dh_get_priv_key() {
        return getBN();
    }
    @JRubyMethod(name="priv_key=")
    public synchronized IRubyObject dh_set_priv_key(IRubyObject arg) {
        this. = BN.getBigInteger(arg);
        return arg;
    }
    private IRubyObject getBN(BigInteger value) {
        if (value != null) {
            return BN.newBN(getRuntime(), value);
        }
        return getRuntime().getNil();
    }
    // override differently-named abstract method from PKey
    public IRubyObject to_der() {
        return dh_to_der();
    }
New to GrepCode? Check out our FAQ X