javacardbignum

Is that possible to calculate power function of two BigNumbers in Javacard?


Consider I have two numbers a and b. Is that possible to calculate a^b inside a javacard?

Few month ago I saw a master thesis (that I could not find it again!) who had used crypto resources like RSA engine in javacard to calculate BigNumber multiplication. Does anyone know a similar or other solution to handle this problem?

Thanks in Advance,


Solution

  • Thanks to @vlp answer, I could find an example applet inside JCMathLib github that I could use for big number calculations. This is the sample applet I used:

    package JCMathLibTest ;
    
    import javacard.framework.APDU;
    import javacard.framework.Applet;
    import javacard.framework.CardRuntimeException;
    import javacard.framework.ISO7816;
    import javacard.framework.ISOException;
    import javacard.framework.JCSystem;
    import javacard.framework.PINException;
    import javacard.framework.SystemException;
    import javacard.framework.TransactionException;
    import javacard.framework.Util;
    import javacard.security.CryptoException;
    
    public class JCMathLibTest extends Applet {
        // Main instruction CLAss
        public final static byte CLA_OC_UT                  = (byte) 0xB0; // OpenCrypto Unit Tests
    
        // INStructions
        public final static byte INS_BN_ADD                 = (byte) 0x21;
        public final static byte INS_BN_SUB                 = (byte) 0x22;
        public final static byte INS_BN_MUL                 = (byte) 0x23;
        public final static byte INS_BN_EXP                 = (byte) 0x24;
        public final static byte INS_BN_MOD                 = (byte) 0x25;
        
        public final static byte INS_BN_ADD_MOD             = (byte) 0x30;
        public final static byte INS_BN_SUB_MOD             = (byte) 0x31;
        public final static byte INS_BN_MUL_MOD             = (byte) 0x32;
        public final static byte INS_BN_EXP_MOD             = (byte) 0x33;
        public final static byte INS_BN_POW2_MOD            = (byte) 0x35;
    
        public final static byte INS_PERF_SETSTOP = (byte) 0xf5;
        
        
        static boolean bIsSimulator = false; 
        static boolean bTEST_256b_CURVE = true;
        static boolean bTEST_512b_CURVE = false;
        
        short[]         m_memoryInfo = null;
        short           m_memoryInfoOffset = 0;
        
        ECConfig        m_ecc = null;
        
        ECCurve         m_testCurve = null;
    
        ECPoint         m_testPoint1 = null;
        ECPoint         m_testPoint2 = null;
        
        byte[]          m_customG = null;
        ECCurve         m_testCurveCustom = null;
        ECPoint         m_testPointCustom = null;
        
        Bignat          m_testBN1;
        Bignat          m_testBN2;
        Bignat          m_testBN3;
    
        public JCMathLibTest() {
            OperationSupport.getInstance().setCard(OperationSupport.SIMULATOR); // TODO set your card
    
            m_memoryInfo = new short[(short) (7 * 3)]; // Contains RAM and EEPROM memory required for basic library objects
            if (bTEST_256b_CURVE) {
                m_ecc = new ECConfig((short) 256);
            }
            if (bTEST_512b_CURVE) {
                m_ecc = new ECConfig((short) 512);
            }
            
    
            // Pre-allocate test objects (no new allocation for every tested operation)
            if (bTEST_256b_CURVE) {
                m_testCurve = new ECCurve(false, SecP256r1.p, SecP256r1.a, SecP256r1.b, SecP256r1.G, SecP256r1.r);
                //m_memoryInfoOffset = snapshotAvailableMemory((short) 3, m_memoryInfo, m_memoryInfoOffset);
                // m_testCurveCustom and m_testPointCustom will have G occasionally changed so we need separate ECCurve
                m_customG = new byte[(short) SecP256r1.G.length];
                Util.arrayCopyNonAtomic(SecP256r1.G, (short) 0, m_customG, (short) 0, (short) SecP256r1.G.length);
                m_testCurveCustom = new ECCurve(false, SecP256r1.p, SecP256r1.a, SecP256r1.b, m_customG, SecP256r1.r);
            }
            if (bTEST_512b_CURVE) {
                m_testCurve = new ECCurve(false, P512r1.p, P512r1.a, P512r1.b, P512r1.G, P512r1.r);
                // m_testCurveCustom and m_testPointCustom will have G occasionally changed so we need separate ECCurve
                m_customG = new byte[(short) P512r1.G.length];
                Util.arrayCopyNonAtomic(P512r1.G, (short) 0, m_customG, (short) 0, (short) P512r1.G.length);
                m_testCurveCustom = new ECCurve(false, P512r1.p, P512r1.a, P512r1.b, m_customG, P512r1.r);
            }
    
            // Testing Bignat objects used in tests
            byte memoryType = JCSystem.MEMORY_TYPE_TRANSIENT_RESET;
            m_testBN1 = new Bignat(m_ecc.MAX_BIGNAT_SIZE, memoryType, m_ecc.bnh);
            m_testBN2 = new Bignat(m_ecc.MAX_BIGNAT_SIZE, memoryType, m_ecc.bnh);
            m_testBN3 = new Bignat(m_ecc.MAX_BIGNAT_SIZE, memoryType, m_ecc.bnh);
            
            short intLen = 4;
            m_testINT1 = new Integer(intLen, m_ecc.bnh);
            m_testINT2 = new Integer(intLen, m_ecc.bnh);
        }
        
        public static void install(byte[] bArray, short bOffset, byte bLength) {
            // GP-compliant JavaCard applet registration
            if (bLength == 0) {
                bIsSimulator = true;
            }
            new JCMathLibTest().register();
        }
    
        public boolean select() {
            //updateAfterReset();
            return true;
        }
        public void process(APDU apdu) {
            byte[] apdubuf = apdu.getBuffer();
    
            // Good practice: Return 9000 on SELECT
            if (selectingApplet()) {
                return;
            }
    
            // Check CLA byte
            if (apdubuf[ISO7816.OFFSET_CLA] != CLA_OC_UT) {
                ISOException.throwIt(ISO7816.SW_CLA_NOT_SUPPORTED);
            }
    
            // Process Input
            short dataLen = apdu.setIncomingAndReceive(); // returns length of data field
    
            try {
                switch (apdubuf[ISO7816.OFFSET_INS]) {
                    
                    case INS_BN_STR:
                        test_BN_STR(apdu, dataLen);
                        break;
                        
                    case INS_BN_ADD:
                        test_BN_ADD(apdu, dataLen);
                        break;
                        
                    case INS_BN_SUB:
                        test_BN_SUB(apdu, dataLen);
                        break;
                        
                    case INS_BN_MUL:
                        test_BN_MUL(apdu, dataLen, true);
                        break;
                    
                    case INS_BN_EXP:
                        test_BN_EXP(apdu, dataLen);
                        break;
                    
                    case INS_BN_SQRT:
                        test_BN_SQRT(apdu, dataLen);
                        break;
                        
                    case INS_BN_MOD:
                        test_BN_MOD(apdu, dataLen);
                        break;
                        
                    case INS_BN_ADD_MOD:
                        test_BN_ADD_MOD(apdu, dataLen);
                        break;
                        
                    case INS_BN_SUB_MOD:
                        test_BN_SUB_MOD(apdu, dataLen);
                        break;
                        
                    case INS_BN_MUL_MOD:
                        test_BN_MUL_MOD(apdu, dataLen);
                        break;
                        
                    case INS_BN_EXP_MOD:
                        test_BN_EXP_MOD(apdu, dataLen);
                        break;
                        
                    case INS_BN_POW2_MOD:
                        test_BN_POW2_MOD(apdu, dataLen);
                        break;
    
                    default:
                        // good practice: If you don't know the INStruction, say so:
                        ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
                }
                // Capture all reasonable exceptions and change into readable ones (instead of 0x6f00) 
            } catch (ISOException e) {
                throw e; // Our exception from code, just re-emit
            } catch (ArrayIndexOutOfBoundsException e) {
                ISOException.throwIt(ReturnCodes.SW_ArrayIndexOutOfBoundsException);
            } catch (ArithmeticException e) {
                ISOException.throwIt(ReturnCodes.SW_ArithmeticException);
            } catch (ArrayStoreException e) {
                ISOException.throwIt(ReturnCodes.SW_ArrayStoreException);
            } catch (NullPointerException e) {
                ISOException.throwIt(ReturnCodes.SW_NullPointerException);
            } catch (NegativeArraySizeException e) {
                ISOException.throwIt(ReturnCodes.SW_NegativeArraySizeException);
            } catch (CryptoException e) {
                ISOException.throwIt((short) (ReturnCodes.SW_CryptoException_prefix | e.getReason()));
            } catch (SystemException e) {
                ISOException.throwIt((short) (ReturnCodes.SW_SystemException_prefix | e.getReason()));
            } catch (PINException e) {
                ISOException.throwIt((short) (ReturnCodes.SW_PINException_prefix | e.getReason()));
            } catch (TransactionException e) {
                ISOException.throwIt((short) (ReturnCodes.SW_TransactionException_prefix | e.getReason()));
            } catch (CardRuntimeException e) {
                ISOException.throwIt((short) (ReturnCodes.SW_CardRuntimeException_prefix | e.getReason()));
            } catch (Exception e) {
                ISOException.throwIt(ReturnCodes.SW_Exception);
            }
    
        }
    
        void test_BN_ADD(APDU apdu, short dataLen) {
            byte[] apdubuf = apdu.getBuffer();
            short p1 = (short) (apdubuf[ISO7816.OFFSET_P1] & 0x00FF);
    
            PM.check(PM.TRAP_BN_ADD_1);
            Bignat num1 = m_testBN1;
            num1.set_size(p1);
            PM.check(PM.TRAP_BN_ADD_2);
            Bignat num2 = m_testBN2;
            num2.set_size((short) (dataLen - p1));
            PM.check(PM.TRAP_BN_ADD_3);
            Bignat sum = m_testBN3;
            sum.set_size((short) (p1 + 1));
    
            PM.check(PM.TRAP_BN_ADD_4);
            num1.from_byte_array(p1, (short)0, apdubuf, ISO7816.OFFSET_CDATA);
            num2.from_byte_array((short) (dataLen - p1), (short)0, apdubuf, (short)(ISO7816.OFFSET_CDATA+p1));
            PM.check(PM.TRAP_BN_ADD_5);
            sum.copy(num1);
            PM.check(PM.TRAP_BN_ADD_6);
            sum.add(num2);
            PM.check(PM.TRAP_BN_ADD_7);
            short len = sum.copy_to_buffer(apdubuf, (short) 0);
            apdu.setOutgoingAndSend((short) 0, len);    
        }
                            
        void test_BN_SUB(APDU apdu, short dataLen) {
            byte[] apdubuf = apdu.getBuffer();
            short p1 = (short) (apdubuf[ISO7816.OFFSET_P1] & 0x00FF);
    
            PM.check(PM.TRAP_BN_SUB_1);
            Bignat sub1 = m_testBN1;
            sub1.set_size(p1);
            PM.check(PM.TRAP_BN_SUB_2);
            Bignat sub2 = m_testBN2;
            sub2.set_size((short) (dataLen - p1));
            PM.check(PM.TRAP_BN_SUB_3);
            Bignat result = m_testBN3;
            result.set_size((short) (p1 + 1));
            PM.check(PM.TRAP_BN_SUB_4);
            sub1.from_byte_array(dataLen, (short)0, apdubuf, ISO7816.OFFSET_CDATA);
            sub2.from_byte_array(dataLen, (short)0, apdubuf, (short)(ISO7816.OFFSET_CDATA+p1));
            PM.check(PM.TRAP_BN_SUB_5);
            result.copy(sub1);
            PM.check(PM.TRAP_BN_SUB_6);
            result.subtract(sub2);
            PM.check(PM.TRAP_BN_SUB_7);
            short len = result.copy_to_buffer(apdubuf, (short) 0);
            apdu.setOutgoingAndSend((short) 0, len);
        }       
        
        void test_BN_MUL(APDU apdu, short dataLen, boolean bFastEngine) {
            byte[] apdubuf = apdu.getBuffer();
            short p1 = (short) (apdubuf[ISO7816.OFFSET_P1] & 0x00FF);
    
            PM.check(PM.TRAP_BN_MUL_1);    
            Bignat mul1 = m_testBN1;
            mul1.set_size(p1);
            PM.check(PM.TRAP_BN_MUL_2);
            Bignat mul2 = m_testBN2;
            mul2.set_size((short) (dataLen - p1));
            PM.check(PM.TRAP_BN_MUL_3);
            Bignat product = m_testBN3;
            product.set_size(dataLen);
            PM.check(PM.TRAP_BN_MUL_4);
            mul1.from_byte_array(p1, (short)0, apdubuf, ISO7816.OFFSET_CDATA);
            mul2.from_byte_array((short)(dataLen-p1), (short)0, apdubuf, (short)(ISO7816.OFFSET_CDATA+p1));
            PM.check(PM.TRAP_BN_MUL_5);
            if (bFastEngine && !bIsSimulator) {
                product.mult_rsa_trick(mul1, mul2, null, null);
            }
            else {
                product.mult_schoolbook(mul1, mul2);        
            }
            PM.check(PM.TRAP_BN_MUL_6);
            short len = product.copy_to_buffer(apdubuf, (short) 0);
            apdu.setOutgoingAndSend((short) 0, len);
        }
        
        void test_BN_EXP(APDU apdu, short dataLen) {
            byte[] apdubuf = apdu.getBuffer();
            short p1 = (short) (apdubuf[ISO7816.OFFSET_P1] & 0x00FF);
            short p2 = (short) (apdubuf[ISO7816.OFFSET_P2] & 0x00FF);
    
            PM.check(PM.TRAP_BN_EXP_1);    
            Bignat base = m_testBN1;
            base.set_size(p1);
            PM.check(PM.TRAP_BN_EXP_2);
            Bignat exp = m_testBN2;
            exp.set_size((short) (dataLen - p1));
            PM.check(PM.TRAP_BN_EXP_3);
            Bignat res = m_testBN3;
            res.set_size((short) (m_ecc.MAX_BIGNAT_SIZE / 2));
            PM.check(PM.TRAP_BN_EXP_4);
            base.from_byte_array(p1, (short) 0, apdubuf, ISO7816.OFFSET_CDATA);
            exp.from_byte_array((short) (dataLen - p1), (short) 0, apdubuf, (short)(ISO7816.OFFSET_CDATA+p1));
            PM.check(PM.TRAP_BN_EXP_5);
            res.exponentiation(base, exp);
            PM.check(PM.TRAP_BN_EXP_6);
            short len = res.copy_to_buffer(apdubuf, (short) 0);
            apdu.setOutgoingAndSend((short) 0, len);
        }
        
        
        void test_BN_MOD(APDU apdu, short dataLen) {
            byte[] apdubuf = apdu.getBuffer();
            short p1 = (short) (apdubuf[ISO7816.OFFSET_P1] & 0x00FF);
    
            PM.check(PM.TRAP_BN_MOD_1);
            Bignat num = m_testBN1;
            num.set_size(p1);
            PM.check(PM.TRAP_BN_MOD_2);
            Bignat mod = m_testBN2;
            mod.set_size((short) (dataLen - p1));
            PM.check(PM.TRAP_BN_MOD_3);
            num.from_byte_array(p1, (short)0, apdubuf, ISO7816.OFFSET_CDATA);
            mod.from_byte_array((short)(dataLen-p1), (short)0, apdubuf, (short)(ISO7816.OFFSET_CDATA+p1));
            PM.check(PM.TRAP_BN_MOD_4);
            num.mod(mod);
            PM.check(PM.TRAP_BN_MOD_5);
            short len = num.copy_to_buffer(apdubuf, (short) 0);
            apdu.setOutgoingAndSend((short) 0, len);
        }
        
        void test_BN_ADD_MOD(APDU apdu, short dataLen) {
            byte[] apdubuf = apdu.getBuffer();
            short p1 = (short) (apdubuf[ISO7816.OFFSET_P1] & 0x00FF);
            short p2 = (short) (apdubuf[ISO7816.OFFSET_P2] & 0x00FF);
    
            PM.check(PM.TRAP_BN_ADD_MOD_1);
            Bignat num1 = m_testBN1;
            num1.set_size(p1);
            PM.check(PM.TRAP_BN_ADD_MOD_2);
            Bignat num2 = m_testBN2;
            num2.set_size(p2);
            PM.check(PM.TRAP_BN_ADD_MOD_3);
            Bignat mod = m_testBN3;
            mod.set_size((short) (dataLen - p1 - p2));
            PM.check(PM.TRAP_BN_ADD_MOD_4);
            num1.from_byte_array(p1, (short)0, apdubuf, ISO7816.OFFSET_CDATA);
            num2.from_byte_array(p2, (short)0, apdubuf, (short)(ISO7816.OFFSET_CDATA+p1));
            PM.check(PM.TRAP_BN_ADD_MOD_5);
            mod.from_byte_array((short)(dataLen-p1-p2), (short)0, apdubuf, (short)(ISO7816.OFFSET_CDATA+p1+p2));
            PM.check(PM.TRAP_BN_ADD_MOD_6);
            num1.mod_add(num2, mod);
            PM.check(PM.TRAP_BN_ADD_MOD_7);
            short len = num1.copy_to_buffer(apdubuf, (short) 0);
            apdu.setOutgoingAndSend((short) 0, len);
        }
            
        void test_BN_SUB_MOD(APDU apdu, short dataLen) {
            byte[] apdubuf = apdu.getBuffer();
            short p1 = (short) (apdubuf[ISO7816.OFFSET_P1] & 0x00FF);
            short p2 = (short) (apdubuf[ISO7816.OFFSET_P2] & 0x00FF);
    
            PM.check(PM.TRAP_BN_SUB_MOD_1);    
            Bignat num1 = m_testBN1;
            num1.set_size(p1);
            PM.check(PM.TRAP_BN_SUB_MOD_2);
            Bignat num2 = m_testBN2;
            num2.set_size(p2);
            PM.check(PM.TRAP_BN_SUB_MOD_3);
            Bignat mod = m_testBN3;
            mod.set_size((short) (dataLen - p1 - p2));
            PM.check(PM.TRAP_BN_SUB_MOD_4);
            num1.from_byte_array(p1, (short)0, apdubuf, ISO7816.OFFSET_CDATA);
            num2.from_byte_array(p2, (short)0, apdubuf, (short)(ISO7816.OFFSET_CDATA+p1));
            mod.from_byte_array((short)(dataLen-p1-p2), (short)0, apdubuf, (short)(ISO7816.OFFSET_CDATA+p1+p2));
            PM.check(PM.TRAP_BN_SUB_MOD_5);
            num1.mod_sub(num2, mod);
            PM.check(PM.TRAP_BN_SUB_MOD_6);
            short len = num1.copy_to_buffer(apdubuf, (short) 0);
            apdu.setOutgoingAndSend((short) 0, len);
        }           
        
        void test_BN_MUL_MOD(APDU apdu, short dataLen) {
            byte[] apdubuf = apdu.getBuffer();
            short p1 = (short) (apdubuf[ISO7816.OFFSET_P1] & 0x00FF);
            short p2 = (short) (apdubuf[ISO7816.OFFSET_P2] & 0x00FF);
    
            PM.check(PM.TRAP_BN_MUL_MOD_1);    
            Bignat num1 = m_testBN1;
            num1.set_size(p1);
            PM.check(PM.TRAP_BN_MUL_MOD_2);
            Bignat num2 = m_testBN2;
            num2.set_size(p2);
            PM.check(PM.TRAP_BN_MUL_MOD_3);
            Bignat mod = m_testBN3;
            mod.set_size((short) (dataLen - p1 - p2));
            PM.check(PM.TRAP_BN_MUL_MOD_4);
            num1.from_byte_array(p1, (short)0, apdubuf, ISO7816.OFFSET_CDATA);
            num2.from_byte_array(p2, (short)0, apdubuf, (short)(ISO7816.OFFSET_CDATA+p1));
            mod.from_byte_array((short)(dataLen-p1-p2), (short)0, apdubuf, (short)(ISO7816.OFFSET_CDATA+p1+p2));
            PM.check(PM.TRAP_BN_MUL_MOD_5);
            num1.mod_mult(num1, num2, mod);
            PM.check(PM.TRAP_BN_MUL_MOD_6);
            short len = num1.copy_to_buffer(apdubuf, (short) 0);
            apdu.setOutgoingAndSend((short) 0, len);
        }
        
        void test_BN_EXP_MOD(APDU apdu, short dataLen) {
            byte[] apdubuf = apdu.getBuffer();
            short p1 = (short) (apdubuf[ISO7816.OFFSET_P1] & 0x00FF);
            short p2 = (short) (apdubuf[ISO7816.OFFSET_P2] & 0x00FF);
    
            PM.check(PM.TRAP_BN_EXP_MOD_1);    
            Bignat num1 = m_testBN1;
            num1.set_size(p1);
            PM.check(PM.TRAP_BN_EXP_MOD_2);
            Bignat num2 = m_testBN2;
            num2.set_size(p2);
            PM.check(PM.TRAP_BN_EXP_MOD_3);
            Bignat mod = m_testBN3;
            mod.set_size((short) (dataLen - p1 - p2));
            PM.check(PM.TRAP_BN_EXP_MOD_4);
            num1.from_byte_array(p1, (short)0, apdubuf, ISO7816.OFFSET_CDATA);
            num2.from_byte_array(p2, (short)0, apdubuf, (short)(ISO7816.OFFSET_CDATA+p1));
            mod.from_byte_array((short)(dataLen-p1-p2), (short)0, apdubuf, (short)(ISO7816.OFFSET_CDATA+p1+p2));
            PM.check(PM.TRAP_BN_EXP_MOD_5);
            num1.mod_exp(num2, mod);
            PM.check(PM.TRAP_BN_EXP_MOD_6);
            short len = num1.copy_to_buffer(apdubuf, (short) 0);
            apdu.setOutgoingAndSend((short) 0, len);
        }
        
        void test_BN_POW2_MOD(APDU apdu, short dataLen) {
            byte[] apdubuf = apdu.getBuffer();
            short p1 = (short) (apdubuf[ISO7816.OFFSET_P1] & 0x00FF);
            short p2 = (short) (apdubuf[ISO7816.OFFSET_P2] & 0x00FF);
    
            PM.check(PM.TRAP_BN_POW2_MOD_1);
            Bignat num1 = m_testBN1;
            num1.set_size(p1);
            Bignat mod = m_testBN3;
            mod.set_size((short) (dataLen - p1));
            num1.from_byte_array(p1, (short) 0, apdubuf, ISO7816.OFFSET_CDATA);
            mod.from_byte_array((short) (dataLen - p1), (short) 0, apdubuf, (short) (ISO7816.OFFSET_CDATA + p1));
            PM.check(PM.TRAP_BN_POW2_MOD_2);
            //num1.pow2Mod_RSATrick(mod);
            num1.mod_exp2(mod);
            PM.check(PM.TRAP_BN_POW2_MOD_3);
            short len = num1.copy_to_buffer(apdubuf, (short) 0);
            apdu.setOutgoingAndSend((short) 0, len);
        }
    }
    

    and these are sample APDUs I used for my test:

    INS_BN_ADD:
    >> B0 21 02 00 04 01 11 02 11
    >> 00 03 22 90 00
    
    INS_BN_SUB:
    << B0 22 02 00 04 43 21 11 11
    >> 00 32 10 90 00
    
    INS_BN_MUL:
    << B0 23 02 00 04 00 20 00 02
    >> 00 00 00 40 90 00
    
    INS_BN_EXP:
    << B0 24 01 00 02 02 05
    >> 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 20 90 00
    
    INS_BN_MOD:
    << B0 25 01 00 02 05 03
    >> 02 90 00
    
    INS_BN_ADD_MOD:
    << B0 30 01 01 03 05 03 03
    >> 02 90 00
    << B0 30 01 01 03 05 03 04
    >> 90 00
    
    INS_BN_SUB_MOD:
    << B0 31 01 01 03 08 03 04
    >> 01 90 00
    
    INS_BN_MUL_MOD:
    << B0 32 01 01 03 04 03 05
    >> 02 90 00
    << B0 32 20 20 60 E3 9D E1 72 91 BC E3 C2 93 54 42 4B 95 9F 33 0C BC E9 CA B4 0C 3F 7C 76 FD 4F DD 8F C3 1F F5 53 9E C0 3B 35 BC 2F 44 9E CD 4E 16 72 43 20 D9 DF 17 F1 8C D5 F0 4D BE 44 1D 16 C2 34 3E F0 02 F3 16 69 C4 B9 E5 A0 3F 13 82 F6 61 BE AF 89 B1 FF 45 1D D6 5A 2A 0A AF 19 27 65 D7 80 11 D2 B3 45
    >> 0B 00 0B 05 B7 B0 C6 91 86 6B 14 CD 7F C1 02 4C 39 BA 3E D2 79 99 9C F2 0C D3 B6 AE 1A 7C C6 1D 90 00
    
    INS_BN_EXP_MOD:
    << B0 33 01 01 03 02 03 06
    >> 02 90 00
    << B0 33 08 03 0E 11 22 33 44 55 66 77 88 00 11 22 04 23 00
    >> 02 79 00 90 00
    
    INS_BN_POW2_MOD
    << B0 35 01 00 02 03 04
    >> 01 90 00
    

    I did'nt add dependencies to my answer to be brief, they can be found in the github link above.

    Thank you again vlp.