Home | History | Annotate | Download | only in math
      1 /*
      2  * Copyright (C) 2008 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package java.math;
     18 
     19 /*
     20  * In contrast to BigIntegers this class doesn't fake two's complement representation.
     21  * Any Bit-Operations, including Shifting, solely regard the unsigned magnitude.
     22  * Moreover BigInt objects are mutable and offer efficient in-place-operations.
     23  */
     24 final class BigInt {
     25 
     26     /* Fields used for the internal representation. */
     27     transient int bignum = 0;
     28 
     29     @Override protected void finalize() throws Throwable {
     30         try {
     31             if (this.bignum != 0) {
     32                 NativeBN.BN_free(this.bignum);
     33                 this.bignum = 0;
     34             }
     35         } finally {
     36             super.finalize();
     37         }
     38     }
     39 
     40     @Override
     41     public String toString() {
     42         return this.decString();
     43     }
     44 
     45     int getNativeBIGNUM() {
     46         return this.bignum;
     47     }
     48 
     49     static int consumeErrors(StringBuilder sb) {
     50         int cnt = 0;
     51         int e, reason;
     52         while ((e = NativeBN.ERR_get_error()) != 0) {
     53             reason = e & 255;
     54             if (reason == 103) {
     55                 throw new ArithmeticException("BigInteger division by zero");
     56             }
     57             if (reason == 108) {
     58                 throw new ArithmeticException("BigInteger not invertible");
     59             }
     60             if (reason == 65) {
     61                 throw new OutOfMemoryError();
     62             }
     63             sb.append(e).append(": ");
     64             String s = NativeBN.ERR_error_string(e);
     65             sb.append(s);
     66             cnt++;
     67         }
     68         return cnt;
     69     }
     70 
     71     private static void Check(boolean success) {
     72         if (!success) {
     73             StringBuilder sb = new StringBuilder("(openssl)ERR: ");
     74             int cnt = consumeErrors(sb);
     75             if (cnt > 0)
     76                 throw new ArithmeticException(sb.toString());
     77         }
     78     }
     79 
     80 
     81     private void makeValid() {
     82         if (this.bignum == 0) {
     83             this.bignum = NativeBN.BN_new();
     84             Check(this.bignum != 0);
     85         }
     86     }
     87 
     88     private static BigInt newBigInt() {
     89         BigInt bi = new BigInt();
     90         bi.bignum = NativeBN.BN_new();
     91         Check(bi.bignum != 0);
     92         return bi;
     93     }
     94 
     95 
     96     static int cmp(BigInt a, BigInt b) {
     97         return NativeBN.BN_cmp(a.bignum, b.bignum);
     98     }
     99 
    100 
    101     void putCopy(BigInt from) {
    102         this.makeValid();
    103         Check(NativeBN.BN_copy(this.bignum, from.bignum));
    104     }
    105 
    106     BigInt copy() {
    107         BigInt bi = new BigInt();
    108         bi.putCopy(this);
    109         return bi;
    110     }
    111 
    112 
    113     void putLongInt(long val) {
    114         this.makeValid();
    115         Check(NativeBN.putLongInt(this.bignum, val));
    116     }
    117 
    118     void putULongInt(long val, boolean neg) {
    119         this.makeValid();
    120         Check(NativeBN.putULongInt(this.bignum, val, neg));
    121     }
    122 
    123     private NumberFormatException invalidBigInteger(String s) {
    124         throw new NumberFormatException("Invalid BigInteger: " + s);
    125     }
    126 
    127     void putDecString(String original) {
    128         String s = checkString(original, 10);
    129         this.makeValid();
    130         int usedLen = NativeBN.BN_dec2bn(this.bignum, s);
    131         Check((usedLen > 0));
    132         if (usedLen < s.length()) {
    133             throw invalidBigInteger(original);
    134         }
    135     }
    136 
    137     void putHexString(String original) {
    138         String s = checkString(original, 16);
    139         this.makeValid();
    140         int usedLen = NativeBN.BN_hex2bn(this.bignum, s);
    141         Check((usedLen > 0));
    142         if (usedLen < s.length()) {
    143             throw invalidBigInteger(original);
    144         }
    145     }
    146 
    147     /**
    148      * Returns a string suitable for passing to OpenSSL.
    149      * Throws if 's' doesn't match Java's rules for valid BigInteger strings.
    150      * BN_dec2bn and BN_hex2bn do very little checking, so we need to manually
    151      * ensure we comply with Java's rules.
    152      * http://code.google.com/p/android/issues/detail?id=7036
    153      */
    154     String checkString(String s, int base) {
    155         if (s == null) {
    156             throw new NullPointerException("s == null");
    157         }
    158         // A valid big integer consists of an optional '-' or '+' followed by
    159         // one or more digit characters appropriate to the given base,
    160         // and no other characters.
    161         int charCount = s.length();
    162         int i = 0;
    163         if (charCount > 0) {
    164             char ch = s.charAt(0);
    165             if (ch == '+') {
    166                 // Java supports leading +, but OpenSSL doesn't, so we need to strip it.
    167                 s = s.substring(1);
    168                 --charCount;
    169             } else if (ch == '-') {
    170                 ++i;
    171             }
    172         }
    173         if (charCount - i == 0) {
    174             throw invalidBigInteger(s);
    175         }
    176         boolean nonAscii = false;
    177         for (; i < charCount; ++i) {
    178             char ch = s.charAt(i);
    179             if (Character.digit(ch, base) == -1) {
    180                 throw invalidBigInteger(s);
    181             }
    182             if (ch > 128) {
    183                 nonAscii = true;
    184             }
    185         }
    186         return nonAscii ? toAscii(s, base) : s;
    187     }
    188 
    189     // Java supports non-ASCII decimal digits, but OpenSSL doesn't.
    190     // We need to translate the decimal digits but leave any other characters alone.
    191     // This method assumes it's being called on a string that has already been validated.
    192     private static String toAscii(String s, int base) {
    193         int length = s.length();
    194         StringBuilder result = new StringBuilder(length);
    195         for (int i = 0; i < length; ++i) {
    196             char ch = s.charAt(i);
    197             int value = Character.digit(ch, base);
    198             if (value >= 0 && value <= 9) {
    199                 ch = (char) ('0' + value);
    200             }
    201             result.append(ch);
    202         }
    203         return result.toString();
    204     }
    205 
    206     void putBigEndian(byte[] a, boolean neg) {
    207         this.makeValid();
    208         Check(NativeBN.BN_bin2bn(a, a.length, neg, this.bignum));
    209     }
    210 
    211     void putLittleEndianInts(int[] a, boolean neg) {
    212         this.makeValid();
    213         Check(NativeBN.litEndInts2bn(a, a.length, neg, this.bignum));
    214     }
    215 
    216     void putBigEndianTwosComplement(byte[] a) {
    217         this.makeValid();
    218         Check(NativeBN.twosComp2bn(a, a.length, this.bignum));
    219     }
    220 
    221 
    222     long longInt() {
    223         return NativeBN.longInt(this.bignum);
    224     }
    225 
    226     String decString() {
    227         return NativeBN.BN_bn2dec(this.bignum);
    228     }
    229 
    230     String hexString() {
    231         return NativeBN.BN_bn2hex(this.bignum);
    232     }
    233 
    234     byte[] bigEndianMagnitude() {
    235         return NativeBN.BN_bn2bin(this.bignum);
    236     }
    237 
    238     int[] littleEndianIntsMagnitude() {
    239         return NativeBN.bn2litEndInts(this.bignum);
    240     }
    241 
    242     int sign() {
    243         return NativeBN.sign(this.bignum);
    244     }
    245 
    246     void setSign(int val) {
    247         if (val > 0) {
    248             NativeBN.BN_set_negative(this.bignum, 0);
    249         } else {
    250             if (val < 0) NativeBN.BN_set_negative(this.bignum, 1);
    251         }
    252     }
    253 
    254     boolean twosCompFitsIntoBytes(int desiredByteCount) {
    255         int actualByteCount = (NativeBN.bitLength(this.bignum) + 7) / 8;
    256         return actualByteCount <= desiredByteCount;
    257     }
    258 
    259     int bitLength() {
    260         return NativeBN.bitLength(this.bignum);
    261     }
    262 
    263     boolean isBitSet(int n) {
    264         return NativeBN.BN_is_bit_set(this.bignum, n);
    265     }
    266 
    267     // n > 0: shift left (multiply)
    268     static BigInt shift(BigInt a, int n) {
    269         BigInt r = newBigInt();
    270         Check(NativeBN.BN_shift(r.bignum, a.bignum, n));
    271         return r;
    272     }
    273 
    274     void shift(int n) {
    275         Check(NativeBN.BN_shift(this.bignum, this.bignum, n));
    276     }
    277 
    278     void addPositiveInt(int w) {
    279         Check(NativeBN.BN_add_word(this.bignum, w));
    280     }
    281 
    282     void multiplyByPositiveInt(int w) {
    283         Check(NativeBN.BN_mul_word(this.bignum, w));
    284     }
    285 
    286     static int remainderByPositiveInt(BigInt a, int w) {
    287         int rem = NativeBN.BN_mod_word(a.bignum, w);
    288         Check(rem != -1);
    289         return rem;
    290     }
    291 
    292     static BigInt addition(BigInt a, BigInt b) {
    293         BigInt r = newBigInt();
    294         Check(NativeBN.BN_add(r.bignum, a.bignum, b.bignum));
    295         return r;
    296     }
    297 
    298     void add(BigInt a) {
    299         Check(NativeBN.BN_add(this.bignum, this.bignum, a.bignum));
    300     }
    301 
    302     static BigInt subtraction(BigInt a, BigInt b) {
    303         BigInt r = newBigInt();
    304         Check(NativeBN.BN_sub(r.bignum, a.bignum, b.bignum));
    305         return r;
    306     }
    307 
    308 
    309     static BigInt gcd(BigInt a, BigInt b) {
    310         BigInt r = newBigInt();
    311         Check(NativeBN.BN_gcd(r.bignum, a.bignum, b.bignum));
    312         return r;
    313     }
    314 
    315     static BigInt product(BigInt a, BigInt b) {
    316         BigInt r = newBigInt();
    317         Check(NativeBN.BN_mul(r.bignum, a.bignum, b.bignum));
    318         return r;
    319     }
    320 
    321     static BigInt bigExp(BigInt a, BigInt p) {
    322         // Sign of p is ignored!
    323         BigInt r = newBigInt();
    324         Check(NativeBN.BN_exp(r.bignum, a.bignum, p.bignum));
    325         return r;
    326     }
    327 
    328     static BigInt exp(BigInt a, int p) {
    329         // Sign of p is ignored!
    330         BigInt power = new BigInt();
    331         power.putLongInt(p);
    332         return bigExp(a, power);
    333         // OPTIONAL:
    334         // int BN_sqr(BigInteger r, BigInteger a, BN_CTX ctx);
    335         // int BN_sqr(BIGNUM *r, const BIGNUM *a,BN_CTX *ctx);
    336     }
    337 
    338     static void division(BigInt dividend, BigInt divisor,
    339             BigInt quotient, BigInt remainder) {
    340         int quot, rem;
    341         if (quotient != null) {
    342             quotient.makeValid();
    343             quot = quotient.bignum;
    344         } else {
    345             quot = 0;
    346         }
    347         if (remainder != null) {
    348             remainder.makeValid();
    349             rem = remainder.bignum;
    350         } else {
    351             rem = 0;
    352         }
    353         Check(NativeBN.BN_div(quot, rem, dividend.bignum, divisor.bignum));
    354     }
    355 
    356     static BigInt modulus(BigInt a, BigInt m) {
    357         // Sign of p is ignored! ?
    358         BigInt r = newBigInt();
    359         Check(NativeBN.BN_nnmod(r.bignum, a.bignum, m.bignum));
    360         return r;
    361     }
    362 
    363     static BigInt modExp(BigInt a, BigInt p, BigInt m) {
    364         // Sign of p is ignored!
    365         BigInt r = newBigInt();
    366         Check(NativeBN.BN_mod_exp(r.bignum, a.bignum, p.bignum, m.bignum));
    367 
    368         // OPTIONAL:
    369         // int BN_mod_sqr(BIGNUM *r, const BIGNUM *a, const BIGNUM *m, BN_CTX *ctx);
    370         return r;
    371     }
    372 
    373 
    374     static BigInt modInverse(BigInt a, BigInt m) {
    375         BigInt r = newBigInt();
    376         Check(NativeBN.BN_mod_inverse(r.bignum, a.bignum, m.bignum));
    377         return r;
    378     }
    379 
    380 
    381     static BigInt generatePrimeDefault(int bitLength) {
    382         BigInt r = newBigInt();
    383         Check(NativeBN.BN_generate_prime_ex(r.bignum, bitLength, false, 0, 0, 0));
    384         return r;
    385     }
    386 
    387     boolean isPrime(int certainty) {
    388         return NativeBN.BN_is_prime_ex(bignum, certainty, 0);
    389     }
    390 }
    391