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::BN implementation. Wraps java.math.BigInteger, which provides most functionality directly; the rest is easily derived. Beware that BN's are mutable -- I don't agree with this approach, but must conform for compatibility with MRI's implementation. The offending methods are set_bit!, clear_bit!, mask_bits! and copy.

I've included a few operations (& | ^ ~) that aren't defined by MRI/OpenSSL. These are non-portable (i.e., won't work in C-Ruby), so use at your own risk.

Author(s):
Bill Dortch
 
 public class BN extends RubyObject {
     private static final long serialVersionUID = -5660938062191525498L;
 
     private static final BigInteger MAX_INT = BigInteger.valueOf(.);
     private static final BigInteger TWO = BigInteger.valueOf(2);
     private static final int DEFAULT_CERTAINTY = 100;
     private static Random _random;
     private static SecureRandom _secureRandom;
 
     private static ObjectAllocator BN_ALLOCATOR = new ObjectAllocator() {
         public IRubyObject allocate(Ruby runtimeRubyClass klass) {
             return new BN(runtimeklass.);
         }
     };
 
     public static BN newBN(Ruby runtimeBigInteger value) {
         return new BN(runtimevalue != null ? value : .);
     }
 
     public static void createBN(Ruby runtimeRubyModule ossl) {
         RubyClass openSSLError = ossl.getClass("OpenSSLError");
         ossl.defineClassUnder("BNError"openSSLErroropenSSLError.getAllocator());
 
         RubyClass bn = ossl.defineClassUnder("BN"runtime.getObject(), );
 
         bn.defineAnnotatedMethods(BN.class);
     }
 
     private volatile BigInteger value;
 
     private BN(Ruby runtimeRubyClass clazzBigInteger value) {
         super(runtimeclazz);
         this. = value;
     }
 
     private BN(Ruby runtimeBigInteger value) {
         super(runtimeruntime.getModule("OpenSSL").getClass("BN"));
         this. = value;
    }
    public BigInteger getValue() {
        return ;
    }
    
    // TODO: check whether this is really needed for JRuby 1.0x (not used in 1.1x)
    public IRubyObject doClone() {
        return newBN(getRuntime(), this.);
    }
    
    public IRubyObject initialize_copy(IRubyObject original) {
        super.initialize_copy(original);
        if (this != original) {
            this. = ((BN)original).;
        }
        return this;
    }
    @JRubyMethod(name="initialize", required=1, optional=1)
    public synchronized IRubyObject bn_initialize(IRubyObject[] args) {
        Ruby runtime = getRuntime();
        if (this. != .) { // already initialized
            throw newBNError(runtime"illegal initialization");
        }
        int argc = Arity.checkArgumentCount(runtimeargs, 1, 2);
        int base = argc == 2 ? RubyNumeric.num2int(args[1]) : 10;
        RubyString str = RubyString.stringValue(args[0]);
        switch (base) {
        case 2:
            // this seems wrong to me, but is the behavior of the
            // MRI implementation. rather than interpreting the string
            // as ASCII-encoded binary digits, the raw binary value of
            // the string is used instead. the value is always interpreted
            // as positive, hence the use of the signum version of the BI
            // constructor here:
            this. = new BigInteger(1, str.getBytes());
            break;
        case 10:
        case 16:
            // here, the ASCII-encoded decimal or hex string is used
            try {
                this. = new BigInteger(str.toString(), base);
                break;
            } catch (NumberFormatException e) {
                throw runtime.newArgumentError("value " + str + " is not legal for radix " + base);
            }
        case 0: // FIXME: not currently supporting BN_mpi2bn
            throw runtime.newArgumentError("unsupported radix: " + base);
        default:
            throw runtime.newArgumentError("illegal radix: " + base);
        }
        return this;
    }
    
    @JRubyMethod(name="copy")
    public synchronized IRubyObject bn_copy(IRubyObject other) {
        if (this != other) {
            this. = getBigInteger(other);
        }
        return this;
    }
    @JRubyMethod(name="to_s", rest=true)
    public IRubyObject bn_to_s(IRubyObject[] args) {
        Ruby runtime = getRuntime();
        int argc = Arity.checkArgumentCount(runtimeargs, 0, 1);
        int base = argc == 1 ? RubyNumeric.num2int(args[0]) : 10;
        switch (base) {
        case 2:
            // again, following MRI implementation, wherein base 2 deals
            // with strings as byte arrays rather than ASCII-encoded binary
            // digits.  note that negative values are returned as though positive:
            byte[] bytes = this..abs().toByteArray();
            // suppress leading 0 byte to conform to MRI behavior
            if (bytes[0] == 0) {
                return runtime.newString(new ByteList(bytes, 1, bytes.length - 1));
            }
            return runtime.newString(new ByteList(bytesfalse));
        case 10:
        case 16:
            return runtime.newString(.toString(base).toUpperCase());
        case 0: // FIXME: not currently supporting BN_mpi2bn
            throw runtime.newArgumentError("unsupported radix: " + base);
        default:
            throw runtime.newArgumentError("illegal radix: " + base);
        }
    }
    @JRubyMethod(name="to_i")
    public IRubyObject bn_to_i() {
        Ruby runtime = getRuntime();
        // FIXME: s/b faster way to convert than going through RubyString
        return RubyNumeric.str2inum(runtimeruntime.newString(.toString()), 10, true);
    }
    @JRubyMethod(name="to_bn")
    public IRubyObject bn_to_bn() {
        return this;
    }
    @JRubyMethod(name="coerce")
    // FIXME: is this right? don't see how it would be useful...
    public IRubyObject bn_coerce(IRubyObject other) {
        Ruby runtime = getRuntime();
        IRubyObject self;
        switch (other.getMetaClass().) {
        case .:
            self = runtime.newString(.toString());
            break;
        case .:
        case .:
            // FIXME: s/b faster way to convert than going through RubyString
            self = RubyNumeric.str2inum(runtimeruntime.newString(.toString()), 10, true);
            break;
        default:
            if (other instanceof BN) {
                self = this;
            } else {
                throw runtime.newTypeError("Don't know how to coerce");
            }
        }
        return runtime.newArray(otherself);
    }
    
    @JRubyMethod(name="zero?")
    public IRubyObject bn_is_zero() {
        return getRuntime().newBoolean(.equals(.));
    }
    @JRubyMethod(name="one?")
    public IRubyObject bn_is_one() {
        return getRuntime().newBoolean(.equals(.));
    }
    @JRubyMethod(name="odd?")
    public IRubyObject bn_is_odd() {
        return getRuntime().newBoolean(.testBit(0));
    }
    
    @JRubyMethod(name={"cmp""<=>"})
    public IRubyObject bn_cmp(IRubyObject other) {
        return getRuntime().newFixnum(.compareTo(getBigInteger(other)));
    }
    @JRubyMethod(name="ucmp")
    public IRubyObject bn_ucmp(IRubyObject other) {
        return getRuntime().newFixnum(.abs().compareTo(getBigInteger(other).abs()));
    }
    
    @JRubyMethod(name={"eql?""==""==="})
    public IRubyObject bn_eql(IRubyObject other) {
        return getRuntime().newBoolean(.equals(getBigInteger(other)));
    }
    @JRubyMethod(name="sqr")
    public IRubyObject bn_sqr() {
        // TODO: check whether mult n * n is faster
        return newBN(getRuntime(), .pow(2));
    }
    @JRubyMethod(name="~")
    public IRubyObject bn_not() {
        return newBN(getRuntime(), .not());
    }
    @JRubyMethod(name="+")
    public IRubyObject bn_add(IRubyObject other) {
        return newBN(getRuntime(), .add(getBigInteger(other)));
    }
    @JRubyMethod(name="-")
    public IRubyObject bn_sub(IRubyObject other) {
        return newBN(getRuntime(), .subtract(getBigInteger(other)));
    }
    @JRubyMethod(name="*")
    public IRubyObject bn_mul(IRubyObject other) {
        return newBN(getRuntime(), .multiply(getBigInteger(other)));
    }
    @JRubyMethod(name="%")
    public IRubyObject bn_mod(IRubyObject other) {
        try {
            return newBN(getRuntime(), .mod(getBigInteger(other)));
        } catch (ArithmeticException e) {
            throw getRuntime().newZeroDivisionError();
        }
    }
    @JRubyMethod(name="/")
    public IRubyObject bn_div(IRubyObject other) {
        Ruby runtime = getRuntime();
        try {
            BigInteger[] result = .divideAndRemainder(getBigInteger(other));
            return runtime.newArray(newBN(runtimeresult[0]), newBN(runtimeresult[1]));
        } catch (ArithmeticException e) {
            throw runtime.newZeroDivisionError();
        }
    }
    
    @JRubyMethod(name="&")
    public IRubyObject bn_and(IRubyObject other) {
        return newBN(getRuntime(), .and(getBigInteger(other)));
    }
    @JRubyMethod(name="|")
    public IRubyObject bn_or(IRubyObject other) {
        return newBN(getRuntime(), .or(getBigInteger(other)));
    }
    @JRubyMethod(name="^")
    public IRubyObject bn_xor(IRubyObject other) {
        return newBN(getRuntime(), .xor(getBigInteger(other)));
    }
    @JRubyMethod(name="**")
    public IRubyObject bn_exp(IRubyObject other) {
        // somewhat strangely, BigInteger takes int rather than BigInteger
        // as the argument to pow.  so we'll have to narrow the value, and
        // raise an exception if data would be lost. (on the other hand, an
        // exponent even approaching Integer.MAX_VALUE would be silly big, and
        // the value would take a very, very long time to calculate.)
        // we'll check for values < 0 (illegal) while we're at it
        int exp;
        switch(other.getMetaClass().) {
        case .: {
            long val = ((RubyFixnum)other).getLongValue();
            if (val >= 0 && val <= .) {
                exp = (int)val;
                break;
            }
        }
        case .:
            // Bignum is inherently too big
            throw newBNError(getRuntime(), "invalid exponent");
        default: {
            if (!(other instanceof BN)) {
                throw getRuntime().newTypeError("Cannot convert into OpenSSL::BN");
            }
            BigInteger val = ((BN)other).;
            if (val.compareTo(.) < 0 || val.compareTo() > 0) {
                throw newBNError(getRuntime(), "invalid exponent");
            }
            exp = val.intValue();
            break;
        }
        }
        try {
            return newBN(getRuntime(), .pow(exp));
        } catch (ArithmeticException e) {
            // shouldn't happen, we've already checked for < 0
            throw newBNError(getRuntime(), "invalid exponent");
        }
    }
    @JRubyMethod(name="gcd")
    public IRubyObject bn_gcd(IRubyObject other) {
        return newBN(getRuntime(), .gcd(getBigInteger(other)));
    }
    @JRubyMethod(name="mod_sqr")
    public IRubyObject bn_mod_sqr(IRubyObject other) {
        try {
            return newBN(getRuntime(), .modPow(getBigInteger(other)));
        } catch (ArithmeticException e) {
            throw getRuntime().newZeroDivisionError();
        }
    }
    
    @JRubyMethod(name="mod_inverse")
    public IRubyObject bn_mod_inverse(IRubyObject other) {
        try {
            return newBN(getRuntime(), .modInverse(getBigInteger(other)));
        } catch (ArithmeticException e) {
            throw getRuntime().newZeroDivisionError();
        }
    }
    @JRubyMethod(name="mod_add")    
    public IRubyObject bn_mod_add(IRubyObject otherIRubyObject mod) {
        try {
            return newBN(getRuntime(), .add(getBigInteger(other)).mod(getBigInteger(mod)));
        } catch (ArithmeticException e) {
            throw getRuntime().newZeroDivisionError();
        }
    }
    @JRubyMethod(name="mod_sub")
    public IRubyObject bn_mod_sub(IRubyObject otherIRubyObject mod) {
        try {
            return newBN(getRuntime(), .subtract(getBigInteger(other)).mod(getBigInteger(mod)));
        } catch (ArithmeticException e) {
            throw getRuntime().newZeroDivisionError();
        }
    }
    @JRubyMethod(name="mod_mul")
    public IRubyObject bn_mod_mul(IRubyObject otherIRubyObject mod) {
        try {
            return newBN(getRuntime(), .multiply(getBigInteger(other)).mod(getBigInteger(mod)));
        } catch (ArithmeticException e) {
            throw getRuntime().newZeroDivisionError();
        }
    }
    @JRubyMethod(name="mod_exp")
    public IRubyObject bn_mod_exp(IRubyObject otherIRubyObject mod) {
        try {
            return newBN(getRuntime(), .modPow(getBigInteger(other), getBigInteger(mod)));
        } catch (ArithmeticException e) {
            throw getRuntime().newZeroDivisionError();
        }
    }
    
    @JRubyMethod(name="set_bit!")
    public synchronized IRubyObject bn_set_bit(IRubyObject n) {
        // evil mutable BN
        int pos = RubyNumeric.num2int(n);
        BigInteger oldValue = this.;
        // FIXME? in MRI/OSSL-BIGNUM, the original sign of a BN is remembered, so if
        // you set the value of an (originally) negative number to zero (through some
        // combination of clear_bit! and/or mask_bits! calls), and later call set_bit!,
        // the resulting value will be negative.  this seems unintuitive and, frankly,
        // wrong, not to mention expensive to carry the extra sign field.
        // I'm not duplicating this behavior here at this time. -BD 
        try {
            if (oldValue.signum() >= 0) {
                this. = oldValue.setBit(pos);
            } else {
                this. = oldValue.abs().setBit(pos).negate();
            }
        } catch (ArithmeticException e) {
            throw newBNError(getRuntime(), "invalid pos");
        }
        return this;
    }
    @JRubyMethod(name="clear_bit!")
    public synchronized IRubyObject bn_clear_bit(IRubyObject n) {
        // evil mutable BN
        int pos = RubyNumeric.num2int(n);
        BigInteger oldValue = this.;
        try {
            if (oldValue.signum() >= 0) {
                this. = oldValue.clearBit(pos);
            } else {
                this. = oldValue.abs().clearBit(pos).negate();
            }
        } catch (ArithmeticException e) {
            throw newBNError(getRuntime(), "invalid pos");
        }
        return this;
    }

    
Truncates value to n bits
    @JRubyMethod(name="mask_bits!")
    public synchronized IRubyObject bn_mask_bits(IRubyObject n) {
        // evil mutable BN
        int pos = RubyNumeric.num2int(n);
        if (pos < 0) throw newBNError(getRuntime(), "invalid pos");
        
        BigInteger oldValue = this.;
        // TODO: cache 2 ** n values?
        if (oldValue.signum() >= 0) {
            if (oldValue.bitLength() < posthrow newBNError(getRuntime(), "invalid pos");
            this. = oldValue.mod(.pow(pos));
        } else {
            BigInteger absValue = oldValue.abs();
            if (absValue.bitLength() < posthrow newBNError(getRuntime(), "invalid pos");
            this. = absValue.mod(.pow(pos)).negate();
        }
        
        return this;
    }
    
    @JRubyMethod(name="bit_set?")
    public IRubyObject bn_is_bit_set(IRubyObject n) {
        int pos = RubyNumeric.num2int(n);
        BigInteger val = this.;
        try {
            if (val.signum() >= 0) {
                return getRuntime().newBoolean(val.testBit(pos));
            } else {
                return getRuntime().newBoolean(val.abs().testBit(pos));
            }
        } catch (ArithmeticException e) {
            throw newBNError(getRuntime(), "invalid pos");
        }
    }
    @JRubyMethod(name="<<")
    public IRubyObject bn_lshift(IRubyObject n) {
        int nbits = RubyNumeric.num2int(n);
        BigInteger val = this.;
        if (val.signum() >= 0) {
            return newBN(getRuntime(), val.shiftLeft(nbits));
        } else {
            return newBN(getRuntime(), val.abs().shiftLeft(nbits).negate());
        }
    }
    @JRubyMethod(name=">>")
    public IRubyObject bn_rshift(IRubyObject n) {
        int nbits = RubyNumeric.num2int(n);
        BigInteger val = this.;
        if (val.signum() >= 0) {
            return newBN(getRuntime(), val.shiftRight(nbits));
        } else {
            return newBN(getRuntime(), val.abs().shiftRight(nbits).negate());
        }
    }
    
    @JRubyMethod(name="num_bits")
    public IRubyObject bn_num_bits() {
        return getRuntime().newFixnum(this..abs().bitLength());
    }
    @JRubyMethod(name="num_bytes")
    public IRubyObject bn_num_bytes() {
        return getRuntime().newFixnum((this..abs().bitLength() + 7) / 8);
    }
    
    @JRubyMethod(name="num_bits_set")
    public IRubyObject bn_num_bits_set() {
        return getRuntime().newFixnum(this..abs().bitCount());
    }
    // note that there is a bug in the MRI version, in argument handling,
    // so apparently no one ever calls this...
    @JRubyMethod(name="prime?", rest=true)
    public IRubyObject bn_is_prime(IRubyObject[] args) {
        Ruby runtime = getRuntime();
        int argc = Arity.checkArgumentCount(runtimeargs, 0, 1);
        // BigInteger#isProbablePrime will actually limit checks to a maximum of 50, 
        // depending on bit count.
        int certainty = argc == 0 ?  : RubyNumeric.fix2int(args[0]);
        return runtime.newBoolean(this..isProbablePrime(certainty));
    }
    
    // FIXME? BigInteger doesn't supply this, so right now this is (essentially)
    // the same as bn_is_prime
    @JRubyMethod(name="prime_fasttest?", rest=true)
    public IRubyObject bn_is_prime_fasttest(IRubyObject[] args) {
        Ruby runtime = getRuntime();
        int argc = Arity.checkArgumentCount(runtimeargs, 0, 2);
        // BigInteger#isProbablePrime will actually limit checks to a maximum of 50, 
        // depending on bit count.
        int certainty = argc == 0 ?  : RubyNumeric.fix2int(args[0]);
        return runtime.newBoolean(this..isProbablePrime(certainty));
    }
    
    @JRubyMethod(name="generate_prime", meta=true, rest=true)
    public static IRubyObject bn_generate_prime(IRubyObject recvIRubyObject[] args) {
        Ruby runtime = recv.getRuntime();
        int argc = Arity.checkArgumentCount(runtimeargs, 1, 4);
        int bits = RubyNumeric.num2int(args[0]);
        boolean safe = argc > 1 ? args[1] != runtime.getFalse() : true;
        BigInteger add = argc > 2 ? getBigInteger(args[2]) : null;
        BigInteger rem = argc > 3 ? getBigInteger(args[3]) : null;
        if (bits < 3) {
            if (safethrow runtime.newArgumentError("bits < 3");
            if (bits < 2) throw runtime.newArgumentError("bits < 2");
        }
        return newBN(runtimegeneratePrime(bitssafeaddrem));
    }
    
    public static BigInteger generatePrime(int bitsboolean safeBigInteger addBigInteger rem) {
        // From OpenSSL man page BN_generate_prime(3):
        //
        // "If add is not NULL, the prime will fulfill the condition p % add == rem 
        // (p % add == 1 if rem == NULL) in order to suit a given generator."
        //
        // "If safe is true, it will be a safe prime (i.e. a prime p so that
        // (p-1)/2 is also prime)."
        //
        // see [ossl]/crypto/bn/bn_prime.c #BN_generate_prime_ex
        //
        if (add != null && rem == null) {
            rem = .;
        }
        
        // borrowing technique from org.bouncycastle.crypto.generators.DHParametersHelper
        // (unfortunately the code has package visibility), wherein for safe primes,
        // we'll use the lowest useful certainty (2) for generation of q, then if
        // p ( = 2q + 1) is prime to our required certainty (100), we'll verify that q
        // is as well.
        //
        // for typical bit lengths ( >= 1024), this should speed things up by reducing
        // initial Miller-Rabin iterations from 2 to 1 for candidate values of q.
        //
        // it's still painfully slow...
        //
        BigInteger pq;
        int qbits = bits - 1;
        SecureRandom secureRandom = getSecureRandom();
        do {
            if (safe) {
                do {
                    q = new BigInteger(qbits, 2, secureRandom);
                    p = q.shiftLeft(1).setBit(0);
                } while (!(p.isProbablePrime() && q.isProbablePrime()));
            } else {
                p = BigInteger.probablePrime(bitssecureRandom); 
            }
        } while (add != null && !p.mod(add).equals(rem));
        return p;
    }
    
    public static BigInteger generatePrime(int bitsboolean safe) {
        return generatePrime(bitssafenullnull);
    }
    
    @JRubyMethod(name="rand", meta=true, rest=true)
    public static IRubyObject bn_rand(IRubyObject recvIRubyObject[] args) {
        return getRandomBN(recv.getRuntime(), argsgetSecureRandom()); 
    }
    
    @JRubyMethod(name="pseudo_rand", meta=true, rest=true)
    public static IRubyObject bn_pseudo_rand(IRubyObject recvIRubyObject[] args) {
        return getRandomBN(recv.getRuntime(), argsgetRandom()); 
    }
    
    public static BN getRandomBN(Ruby runtimeIRubyObject[] argsRandom random) {
        int argc = Arity.checkArgumentCount(runtimeargs, 1, 3);
        int bits = RubyNumeric.num2int(args[0]);
        int top;
        boolean bottom;
        if (argc > 1) {
            top = RubyNumeric.fix2int(args[1]);
            bottom = argc == 3 ? args[2].isTrue() : false;
        } else {
            top = 0;
            bottom = false;
        }
        BigInteger value;
        try {
            value = getRandomBI(bitstopbottomrandom);
        } catch (IllegalArgumentException e) {
            throw runtime.newArgumentError(e.getMessage());
        }
        return newBN(runtimevalue);
    }
    
    public static BigInteger getRandomBI(int bitsint topboolean bottomRandom random) {
        // From OpenSSL man page BN_rand(3):
        //
        // "If top is -1, the most significant bit of the random number can be zero.
        // If top is 0, it is set to 1, and if top is 1, the two most significant bits
        // of the number will be set to 1, so that the product of two such random numbers
        // will always have 2*bits length."
        //
        // "If bottom is true, the number will be odd."
        //
        if (bits <= 0) {
            if (bits == 0) return .;
            throw new IllegalArgumentException("Illegal bit length");
        }
        if (top < -1 || top > 1) {
            throw new IllegalArgumentException("Illegal top value");
        }
        
        // top/bottom handling adapted from OpenSSL's crypto/bn/bn_rand.c
        int bytes = (bits + 7) / 8;
        int bit = (bits - 1) % 8;
        int mask = 0xff << (bit + 1);
        byte[] buf;
        random.nextBytes(buf = new byte[bytes]);
        if (top >= 0) {
            if (top == 0) {
                buf[0] |= (1 << bit);
            } else {
                if (bit == 0) {
                    buf[0] = 1;
                    buf[1] |= 0x80;
                }
                else {
                    buf[0] |= (3 << (bit - 1));
                }
            }
        }
        buf[0] &= ~mask;
        if (bottom) {
            buf[bytes-1] |= 1;
        }
        
        // treating result as unsigned
        return new BigInteger(1, buf);
    }
    
    @JRubyMethod(name="rand_range", meta=true)
    public static IRubyObject bn_rand_range(IRubyObject recvIRubyObject arg) {
        return getRandomBNInRange(recv.getRuntime(), getBigInteger(arg), getSecureRandom()); 
    }
    
    @JRubyMethod(name="pseudo_rand_range", meta=true)
    public static IRubyObject bn_pseudo_rand_range(IRubyObject recvIRubyObject arg) {
        return getRandomBNInRange(recv.getRuntime(), getBigInteger(arg), getRandom()); 
    }
    
    private static BN getRandomBNInRange(Ruby runtimeBigInteger limitRandom random) {
        BigInteger value;
        try {
            value = getRandomBIInRange(limitrandom);
        } catch (IllegalArgumentException e) {
            throw newBNError(runtime"illegal range");
        }
        return newBN(runtimevalue);
    }
    
    public static BigInteger getRandomBIInRange(BigInteger limitRandom random) {
        if (limit.signum() < 0) {
            throw new IllegalArgumentException("illegal range");
        }
        int bits = limit.bitLength();
        BigInteger value;
        do {
            value = new BigInteger(bitsrandom);
        } while (value.compareTo(limit) >= 0);
        return value;
    }
    
    private static Random getRandom() {
        Random rand;
        if ((rand = ) != null) {
            return rand;
        }
        return  = new Random();
    }
    
    private static SecureRandom getSecureRandom() {
        SecureRandom rand;
        if ((rand = ) != null) {
            return rand;
        }
        // FIXME: do we want a particular algorithm / provider? BC?
        return  = new SecureRandom();
    }
    public static RaiseException newBNError(Ruby runtimeString message) {
        return new RaiseException(runtimeruntime.getModule("OpenSSL").getClass("BNError"), messagetrue);
    }
    
    public static BigInteger getBigInteger(IRubyObject arg) {
        if (arg.isNil()) return null;
        switch(arg.getMetaClass().) {
        case .:
        case .:
            return new BigInteger(arg.toString());
        default:
            if (arg instanceof BN) {
                return ((BN)arg).;
            }
            throw arg.getRuntime().newTypeError("Cannot convert into OpenSSL::BN");
        }
    }