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 class BigInt {
     25 
     26     /* Fields used for the internal representation. */
     27     transient int bignum = 0;
     28 
     29     public void dispose() {
     30         if (this.bignum != 0) {
     31             NativeBN.BN_free(this.bignum);
     32             this.bignum = 0;
     33         }
     34     }
     35 
     36     @Override protected void finalize() throws Throwable {
     37         try {
     38             dispose();
     39         } finally {
     40             super.finalize();
     41         }
     42     }
     43 
     44     @Override
     45     public String toString() {
     46         return this.decString();
     47     }
     48 
     49     public int getNativeBIGNUM() {
     50         return this.bignum;
     51     }
     52 
     53     public static int consumeErrors(StringBuilder sb) {
     54         int cnt = 0;
     55         int e, reason;
     56         while ((e = NativeBN.ERR_get_error()) != 0) {
     57             reason = e & 255;
     58             if (reason == 103) {
     59                 throw new ArithmeticException("BigInteger division by zero");
     60             }
     61             if (reason == 108) {
     62                 throw new ArithmeticException("BigInteger not invertible");
     63             }
     64             if (reason == 65) {
     65                 throw new OutOfMemoryError();
     66             }
     67             sb.append(e).append(": ");
     68             String s = NativeBN.ERR_error_string(e);
     69             sb.append(s);
     70             cnt++;
     71         }
     72         return cnt;
     73     }
     74 
     75     private static void Check(boolean success) {
     76         if (!success) {
     77             StringBuilder sb = new StringBuilder("(openssl)ERR: ");
     78             int cnt = consumeErrors(sb);
     79             if (cnt > 0)
     80                 throw new ArithmeticException(sb.toString());
     81         }
     82     }
     83 
     84 
     85     private void makeValid() {
     86         if (this.bignum == 0) {
     87             this.bignum = NativeBN.BN_new();
     88             Check(this.bignum != 0);
     89         }
     90     }
     91 
     92     private static BigInt newBigInt() {
     93         BigInt bi = new BigInt();
     94         bi.bignum = NativeBN.BN_new();
     95         Check(bi.bignum != 0);
     96         return bi;
     97     }
     98 
     99 
    100     public static int cmp(BigInt a, BigInt b) {
    101         return NativeBN.BN_cmp(a.bignum, b.bignum);
    102     }
    103 
    104 
    105     public void putCopy(BigInt from) {
    106         this.makeValid();
    107         Check(NativeBN.BN_copy(this.bignum, from.bignum));
    108     }
    109 
    110     public BigInt copy() {
    111         BigInt bi = new BigInt();
    112         bi.putCopy(this);
    113         return bi;
    114     }
    115 
    116 
    117     public void putLongInt(long val) {
    118         this.makeValid();
    119         Check(NativeBN.putLongInt(this.bignum, val));
    120     }
    121 
    122     public void putULongInt(long val, boolean neg) {
    123         this.makeValid();
    124         Check(NativeBN.putULongInt(this.bignum, val, neg));
    125     }
    126 
    127     public 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 new NumberFormatException(original);
    134         }
    135     }
    136 
    137     public 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 new NumberFormatException(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     public String checkString(String s, int base) {
    155         if (s == null) {
    156             throw new NullPointerException();
    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 new NumberFormatException(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 new NumberFormatException(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     public void putBigEndian(byte[] a, boolean neg) {
    207         this.makeValid();
    208         Check(NativeBN.BN_bin2bn(a, a.length, neg, this.bignum));
    209     }
    210 
    211     public void putLittleEndianInts(int[] a, boolean neg) {
    212         this.makeValid();
    213         Check(NativeBN.litEndInts2bn(a, a.length, neg, this.bignum));
    214     }
    215 
    216     public void putBigEndianTwosComplement(byte[] a) {
    217         this.makeValid();
    218         Check(NativeBN.twosComp2bn(a, a.length, this.bignum));
    219     }
    220 
    221 
    222     public long longInt() {
    223         return NativeBN.longInt(this.bignum);
    224     }
    225 
    226     public String decString() {
    227         return NativeBN.BN_bn2dec(this.bignum);
    228     }
    229 
    230     public String hexString() {
    231         return NativeBN.BN_bn2hex(this.bignum);
    232     }
    233 
    234     public byte[] bigEndianMagnitude() {
    235         return NativeBN.BN_bn2bin(this.bignum);
    236     }
    237 
    238     public int[] littleEndianIntsMagnitude() {
    239         return NativeBN.bn2litEndInts(this.bignum);
    240     }
    241 
    242     public int sign() {
    243         return NativeBN.sign(this.bignum);
    244     }
    245 
    246     public void setSign(int val) {
    247         if (val > 0) NativeBN.BN_set_negative(this.bignum, 0);
    248         else if (val < 0) NativeBN.BN_set_negative(this.bignum, 1);
    249     }
    250 
    251     public boolean twosCompFitsIntoBytes(int desiredByteCount) {
    252         int actualByteCount = (NativeBN.bitLength(this.bignum) + 7) / 8;
    253         return actualByteCount <= desiredByteCount;
    254     }
    255 
    256     public int bitLength() {
    257         return NativeBN.bitLength(this.bignum);
    258     }
    259 
    260     public boolean isBitSet(int n) {
    261         return NativeBN.BN_is_bit_set(this.bignum, n);
    262     }
    263 
    264     // n > 0: shift left (multiply)
    265     public static BigInt shift(BigInt a, int n) {
    266         BigInt r = newBigInt();
    267         Check(NativeBN.BN_shift(r.bignum, a.bignum, n));
    268         return r;
    269     }
    270 
    271     public void shift(int n) {
    272         Check(NativeBN.BN_shift(this.bignum, this.bignum, n));
    273     }
    274 
    275     public void addPositiveInt(int w) {
    276         Check(NativeBN.BN_add_word(this.bignum, w));
    277     }
    278 
    279     public void multiplyByPositiveInt(int w) {
    280         Check(NativeBN.BN_mul_word(this.bignum, w));
    281     }
    282 
    283     public static int remainderByPositiveInt(BigInt a, int w) {
    284         int rem = NativeBN.BN_mod_word(a.bignum, w);
    285         Check(rem != -1);
    286         return rem;
    287     }
    288 
    289     public static BigInt addition(BigInt a, BigInt b) {
    290         BigInt r = newBigInt();
    291         Check(NativeBN.BN_add(r.bignum, a.bignum, b.bignum));
    292         return r;
    293     }
    294 
    295     public void add(BigInt a) {
    296         Check(NativeBN.BN_add(this.bignum, this.bignum, a.bignum));
    297     }
    298 
    299     public static BigInt subtraction(BigInt a, BigInt b) {
    300         BigInt r = newBigInt();
    301         Check(NativeBN.BN_sub(r.bignum, a.bignum, b.bignum));
    302         return r;
    303     }
    304 
    305 
    306     public static BigInt gcd(BigInt a, BigInt b) {
    307         BigInt r = newBigInt();
    308         Check(NativeBN.BN_gcd(r.bignum, a.bignum, b.bignum));
    309         return r;
    310     }
    311 
    312     public static BigInt product(BigInt a, BigInt b) {
    313         BigInt r = newBigInt();
    314         Check(NativeBN.BN_mul(r.bignum, a.bignum, b.bignum));
    315         return r;
    316     }
    317 
    318     public static BigInt bigExp(BigInt a, BigInt p) {
    319         // Sign of p is ignored!
    320         BigInt r = newBigInt();
    321         Check(NativeBN.BN_exp(r.bignum, a.bignum, p.bignum));
    322         return r;
    323     }
    324 
    325     public static BigInt exp(BigInt a, int p) {
    326         // Sign of p is ignored!
    327         BigInt power = new BigInt();
    328         power.putLongInt(p);
    329         return bigExp(a, power);
    330         // OPTIONAL:
    331 //      public int BN_sqr(BigInteger r, BigInteger a, BN_CTX ctx);
    332       // int BN_sqr(BIGNUM *r, const BIGNUM *a,BN_CTX *ctx);
    333     }
    334 
    335     public static void division(BigInt dividend, BigInt divisor,
    336             BigInt quotient, BigInt remainder) {
    337         int quot, rem;
    338         if (quotient != null) {
    339             quotient.makeValid();
    340             quot = quotient.bignum;
    341         }
    342         else quot = 0;
    343         if (remainder != null) {
    344             remainder.makeValid();
    345             rem = remainder.bignum;
    346         }
    347         else rem = 0;
    348         Check(NativeBN.BN_div(quot, rem, dividend.bignum, divisor.bignum));
    349     }
    350 
    351     public static BigInt modulus(BigInt a, BigInt m) {
    352         // Sign of p is ignored! ?
    353         BigInt r = newBigInt();
    354         Check(NativeBN.BN_nnmod(r.bignum, a.bignum, m.bignum));
    355         return r;
    356     }
    357 
    358     public static BigInt modExp(BigInt a, BigInt p, BigInt m) {
    359         // Sign of p is ignored!
    360         BigInt r = newBigInt();
    361         Check(NativeBN.BN_mod_exp(r.bignum, a.bignum, p.bignum, m.bignum));
    362 
    363         // OPTIONAL:
    364         // int BN_mod_sqr(BIGNUM *r, const BIGNUM *a, const BIGNUM *m, BN_CTX *ctx);
    365         return r;
    366     }
    367 
    368 
    369     public static BigInt modInverse(BigInt a, BigInt m) {
    370         BigInt r = newBigInt();
    371         Check(NativeBN.BN_mod_inverse(r.bignum, a.bignum, m.bignum));
    372         return r;
    373     }
    374 
    375 
    376     public static BigInt generatePrimeDefault(int bitLength) {
    377         BigInt r = newBigInt();
    378         Check(NativeBN.BN_generate_prime_ex(r.bignum, bitLength, false, 0, 0, 0));
    379         return r;
    380     }
    381 
    382     public boolean isPrime(int certainty) {
    383         return NativeBN.BN_is_prime_ex(bignum, certainty, 0);
    384     }
    385 }
    386