Huge Zahlenklasse mit RSA Absturz bei deconstructor



  • Moin,

    versuche seit einiger Zeit in meiner Huge klasse den Fehler zu finden leider muss ich wohl Blind sein 😞 . Vielleicht findet ihr den Fehler habe den Code aus dem Buch Implementing SSL/TLS genommen und auf C++ Art zu implementieren. Das ich hier keine STL Klassen verwende liegt daran das ich Sie nicht zur Verfügung habe.

    Ausschnitt crypt.h

            class huge {
            protected:
                void expand();
                void add_magnitude(huge h2);
                void subtract_magnitude(huge h2);
                void exponentiate(huge exp);
            public:
                ~huge();
                huge();
                huge(size_t val);
                void sethuge(size_t val);
                void operator=(huge src);
                void operator=(huge *src);
                int compare(huge h2);
                bool  operator<(huge h2);
                bool  operator>(huge h2);
                bool  operator==(huge h2);
                bool  operator<=(huge h2);
                void  operator+=(huge h2);
                void  operator-=(huge h2);
                void  operator*=(huge h2);
                huge* operator*(huge multiply);
                void  operator/=(huge divisor);
                huge* operator/(huge divisor);
                /*Left shift*/
                void operator<<(int i);
                /*right shift*/
                void operator>>(int i);
    
                void load_huge(const unsigned char* bytes, size_t length);
                void unload_huge(unsigned char* bytes, size_t *length);           
                void mod_pow(huge exp, huge n);
                void inv(huge a);
                void contract();
    
                size_t size();
    
            private:
                int            _sign;
                size_t         _size;
                unsigned char* _rep;
    
                class result{
                public:
                    result();
                    ~result();
                    void  add(huge *res);
                private:
                    class data {
                    public:
                        data(huge *res);
                        ~data();
                        huge* _current;
                        data* _next;
                    } *_first, *_last;
                    sys::mutex _resmut;
                } _result;
            };
    
            typedef struct {
                huge p;
                huge g;
                huge Y; // Ys for server or Yc for client
            } dh_key;
    
            class rsa {
            private:
                void compute(huge* m, huge* e, huge* n, huge* c);
            public:
                typedef struct{
                    huge modulus;
                    huge exponent;
                }key;
    
                int encrypt(unsigned char* input,
                    unsigned int len,
                    unsigned char** output,
                    key public_key);
                int decrypt(unsigned char* input,
                    unsigned int len,
                    unsigned char** output,
                    key private_key);
            };
    

    huge.cpp

    /*******************************************************************************
    Copyright (c) 2022, Jan Koester jan.koester@gmx.net
    All rights reserved.
    
    Redistribution and use in source and binary forms, with or without
    modification, are permitted provided that the following conditions are met:
        * Redistributions of source code must retain the above copyright
          notice, this list of conditions and the following disclaimer.
        * Redistributions in binary form must reproduce the above copyright
          notice, this list of conditions and the following disclaimer in the
          documentation and/or other materials provided with the distribution.
        * Neither the name of the <organization> nor the
          names of its contributors may be used to endorse or promote products
          derived from this software without specific prior written permission.
    
    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
    ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
    DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
    DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
    ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    *******************************************************************************/
    
    #include <string.h>
    #include <stdlib.h>
    #include <systempp/syscrypt.h>
    
    
    sys::crypt::huge::huge() {
        _sign = 0;
        _size = 0;
        _rep = nullptr;
    }
    
    sys::crypt::huge::huge(size_t val) {
        _sign = 0;
        _size = 0;
        _rep = nullptr;
        sethuge(val);
    }
    
    void sys::crypt::huge::sethuge(size_t val) {
    
        size_t mask, i, shift;
        // Negative number support
        _sign = 0;  // sign of 0 means positive
    
        _size = 4;
    
        if (_rep){
           delete[] _rep;
           _rep = nullptr;
        }
    
        // Figure out the minimum amount of space this "val" will take
        // up in chars (leave at least one byte, though, if �val� is 0).
        for (mask = 0xFF000000; mask > 0x000000FF; mask >>= 8) {
            if (val & mask) {
                break;
            }
            _size--;
        }
    
        _rep = new unsigned char[_size];
    
        // Now work backwards through the int, masking off each 8-bit
        // byte (up to the first 0 byte) and copy it into the �huge�
        // array in big-endian format.
        mask = 0x000000FF;
        shift = 0;
        for (i = _size; i; i--) {
            _rep[i - 1] = (val & mask) >> shift;
            mask <<= 8;
            shift += 8;
        }
    }
    
    sys::crypt::huge::~huge() {
        if(_rep)
            delete[] _rep;
    }
    
    /**
     * Extend the space for h by 1 char and set the LSB of that int
     * to 1.
     */
    void sys::crypt::huge::expand(){
        unsigned char* tmp = _rep;
        ++_size;
        _rep = new unsigned char [_size];
        memcpy(_rep + 1, tmp,(_size - 1) * sizeof(unsigned char));
        _rep[0] = 0x01;
        delete[] tmp;
    }
    
    /**
     * Given a byte array, load it into a "huge", aligning integers
     * appropriately
     */
    void sys::crypt::huge::load_huge(const unsigned char* bytes, size_t length){
        while (!(*bytes)){
            bytes++;
            length--;
        }
    
        if (_rep) {
            delete[] _rep;
            _rep = nullptr;
        }
    
        _sign = 0;
        _size = length;
        _rep = new unsigned char [length];
        memcpy(_rep, bytes, length);
    }
    
    void sys::crypt::huge::unload_huge(unsigned char* bytes,size_t *length){
        memcpy(bytes + (int)(*length - _size), _rep, *length);
    }
    
    /**
     * Add two huges - overwrite h1 with the result.
     */
    void sys::crypt::huge::add_magnitude(huge h2){
        unsigned int i, j;
        unsigned int sum;
        unsigned int carry = 0;
    
        // Adding h2 to h1. If h2 is > h1 to begin with, resize h1. 
        if (h2._size > _size){
            unsigned char* tmp = _rep;
            _rep = new unsigned char [h2._size];
            memcpy(_rep + (h2._size - _size), tmp, _size);
            _size = h2._size;
            delete[] tmp;
        }
    
        i = _size;
        j = h2._size;
    
        do{
            i--;
            if (j){
                j--;
                sum = _rep[i] + h2._rep[j] + carry;
            }else{
                sum = _rep[i] + carry;
            }
    
            carry = sum > 0xFF;
            _rep[i] = sum;
        } while (i);
    
        if (carry){
            // Still overflowed; allocate more space 
            expand();
        }
    }
    
    /**
     * Go through h and see how many of the left-most bytes are unused.
     * Remove them and resize h appropriately.
     */
    void sys::crypt::huge::contract(){
        int i = 0;
    
        while (!(_rep[i]) && (i < _size)) { i++; }
    
        if (i && i < _size){
            unsigned char* tmp = &_rep[i];
            _rep = new unsigned char [_size - i];
            memcpy(_rep, tmp, _size - i);
            _size -= i;
        }
    }
    
    void sys::crypt::huge::subtract_magnitude(huge h2){
        int i = _size;
        int j = h2._size;
        int difference; // signed int - important! 
        unsigned int borrow = 0;
    
        do{
            i--;
    
            if (j){
                j--;
                difference = _rep[i] - h2._rep[j] - borrow;
            }else{
                difference = _rep[i] - borrow;
            }
            borrow = (difference < 0);
            _rep[i] = difference;
        } while (i);
    
        if (borrow && i){
            if (_rep[i - 1]){ // Don't borrow i 
                // negative reults are now OK 
                _rep[i - 1]--;
            }
        }
    
        contract();
    }
    
    void sys::crypt::huge::operator+=(huge h2){
        int result_sign;
    
        // First compute sign of result, then compute magnitude
        if (operator>(h2)){
            result_sign = _sign;
    
            if (_sign == h2._sign){
                add_magnitude(h2);
            }else{
                subtract_magnitude(h2);
            }
        }else{
            huge tmp(0);// initialize
    
            // put this into tmp and h2 into this to swap the operands
            tmp=this;
            
            operator=(h2);
    
            if (_sign == tmp._sign){
                result_sign = _sign;
                add_magnitude(tmp);
            }else{
                result_sign = h2._sign;
                subtract_magnitude(tmp);
            }
        }
    
        // Use the stored sign to set the result
        _sign = result_sign;
    }
    
    void sys::crypt::huge::operator-=(huge h2){
        int result_sign;
    
        // First compute sign of result, then compute magnitude
        if (operator>(h2)){
            result_sign = _sign;
    
            if (_sign == h2._sign){
                subtract_magnitude(h2);
            }else{
                add_magnitude(h2);
            }
        }else{
            huge tmp(0);// initialize
    
            // put h1 into tmp and h2 into h1 to swap the operands
            tmp=*this;
            *this=h2;
    
            if (_sign == tmp._sign){
                result_sign = ~(_sign);
                subtract_magnitude(tmp);
            }else{
                result_sign = ~(h2._sign);
                add_magnitude(tmp);
            }
        }
    
        // Use the stored sign to set the result
        _sign = result_sign;
    }
    
    void sys::crypt::huge::operator=(huge src){
        if (_rep){
            delete[] _rep;
            _rep = nullptr;
        }
    
        _sign = src._sign;
        _size = src._size;
        _rep = new unsigned char[src._size];
        memcpy(_rep, src._rep,src._size);
    }
    
    void sys::crypt::huge::operator=(huge* src) {
    
        if (_rep) {
            delete[] _rep;
            _rep = nullptr;
        }
    
        _sign = src->_sign;
        _size = src->_size;
        _rep = new unsigned char[src->_size];
        memcpy(_rep, src->_rep,src->_size);
    }
    
    void sys::crypt::huge::operator<<(int i){
        int old_carry, carry = 0;
        do{
            --i;
            old_carry = carry;
            carry = (_rep[i] & 0x80) == 0x80;
            _rep[i] = (_rep[i] << 1) | old_carry;
            // Again, if C exposed the overflow bit... 
        } while (i);
    
        if (carry){
            expand();
        }
    }
    
    
    /**
     * Multiply h1 by h2, overwriting the value of h1.
     */
    void sys::crypt::huge::operator*=(huge h2){
        unsigned int i;
        int result_sign;
        huge temp(0);
    
        temp=this;
    
        result_sign = !(_sign == h2._sign);
    
        sethuge(0);
    
        i = h2._size;
        do{
            i--;
            for (unsigned char mask = 0x01; mask; mask <<= 1){
                if (mask & h2._rep[i]){
                    operator+=(temp);
                }
                temp << temp._size;
            }
        } while (i);
    
        _sign = result_sign;
    }
    
    sys::crypt::huge* sys::crypt::huge::operator*(huge multiply) {
        unsigned char mask;
        unsigned int i;
        int result_sign;
        huge temp;
    
        huge* result = new huge;
    
        _result.add(result);
    
        temp=*this;
    
        result_sign = !(this->_sign == multiply._sign);
    
        i = multiply._size;
        do
        {
            i--;
            for (mask = 0x01; mask; mask <<= 1)
            {
                if (mask & multiply._rep[i])
                {
                    *result +=temp;
                }
                temp<<temp._size;
            }
        } while (i);
    
        result->_sign = result_sign;
    
        return result;
    }
    
    /**
     * Compare this to h2. Return:
     * 0 if this == h2
     * a positive number if this > h2
     * a negative number if this < h2
     */
    int sys::crypt::huge::compare(huge h2) {
        int i, j;
    
        if (_size > h2._size) {
            return 1;
        }
    
        if (_size < h2._size) {
            return -1;
        }
    
        // Otherwise, sizes are equal, have to actually compare.
        // only have to compare "hi-int", since the lower ints
        // can't change the comparison.
        i = j = 0;
    
        // Otherwise, keep searching through the representational integers
        // until one is bigger than another - once we've found one, it's 
        // safe to stop, since the "lower order bytes" can't affect the
        // comparison
        while (i < _size && j < h2._size) {
            if (_rep[i] < h2._rep[j]) {
                return -1;
            } else if (_rep[i] > h2._rep[j]) {
                return 1;
            }
            i++;
            j++;
        }
    
        // If we got all the way to the end without a comparison, the 
        // two are equal
        return 0;
    }
    
    bool sys::crypt::huge::operator<(huge h2) {
        if (compare(h2) < 0)
            return true;
        return false;
    }
    
    bool sys::crypt::huge::operator>(huge h2) {
        if (compare(h2) > 0)
            return true;
        return false;
    }
    
    bool sys::crypt::huge::operator==(huge h2) {
        if (compare(h2)==0)
            return true;
        return false;
    }
    
    bool sys::crypt::huge::operator<=(huge h2) {
        if (compare(h2) <= 0)
            return true;
        return false;
    }
    
    void sys::crypt::huge::operator>>(int i){
        int j = 0;
        unsigned int old_carry, carry = 0;
        do{
            old_carry = carry;
            carry = (_rep[j] & 0x01) << 7;
            _rep[j] = (_rep[j] >> 1) | old_carry;
        } while (++j < i);
    
        contract();
    }
    
    /**
     * dividend = numerator, divisor = denominator
     *
     * Note that this process destroys divisor (and, of course,
     * overwrites quotient). The dividend is the remainder of the
     * division (if that's important to the caller). The divisor will
     * be modified by this routine, but it will end up back where it
     * �started�.
     */
    void sys::crypt::huge::operator/=(huge divisor){
        int bit_size, bit_position;
    
        // "bit_position" keeps track of which bit, of the quotient, 
        // is being set or cleared on the current operation.
        bit_size = bit_position = 0;
    
        // First, left-shift divisor until it's >= than the dividend
        while (divisor.compare(*this) < 0){
            divisor << divisor._size;
            bit_size++;
        }
    
        bit_position = 8 - (bit_size % 8) - 1;
    
        do{
            if (divisor.compare(*this) <= 0){
                *this-=divisor;  // dividend -= divisor
            }
    
            if (bit_size){
                *this >> _size;
            }
            bit_position++;
        } while (bit_size--);
    }
    
    sys::crypt::huge* sys::crypt::huge::operator/(huge divisor) {
        huge temp;
        temp = *this;
        int bit_size, bit_position;
    
        huge *result = new huge;
    
        _result.add(result);
    
        // "bit_position" keeps track of which bit, of the quotient, 
        // is being set or cleared on the current operation.
        bit_size = bit_position = 0;
    
        // First, left-shift divisor until it's >= than the dividend
        while (divisor.compare(temp) < 0)
        {
            divisor << divisor._size;
            bit_size++;
        }
    
        result->_sign = false;
        result->_size = (bit_size / 8) + 1;
        result->_rep = new unsigned char[result->_size];
        memset(result->_rep, 0, result->_size);
    
        bit_position = 8 - (bit_size % 8) - 1;
    
        do
        {
            if (divisor.compare(temp) <= 0)
            {
                temp-=divisor;  // dividend -= divisor
                result->_rep[(int)(bit_position / 8)] |=
                             (0x80 >> (bit_position % 8));
            }
    
            if (bit_size){
                temp >> temp._size;
            }
            bit_position++;
        } while (bit_size--);
        return result;
    }
    
    /**
     * Raise h1 to the power of exp. Return the result in h1.
     */
    void sys::crypt::huge::exponentiate(huge exp){
        int i = exp._size, mask;
        huge tmp1(0), tmp2(0);
    
        tmp1=*this;
        sethuge(1);
    
        do{
            i--;
            for (mask = 0x01; mask; mask <<= 1){
                if (exp._rep[i] & mask){
                    *this*=tmp1;
                }
    
                // Square tmp1
                tmp2=tmp1;
                tmp1*=tmp2;
            }
        } while (i);
    }
    
    /**
     * Compute c = m^e mod n.
     *
     * Note that this same routine is used for encryption and
     * decryption; the only difference is in the exponent passed in.
     * This is the "exponentiate" algorithm, with the addition of a
     * modulo computation at each stage.
     */
    void sys::crypt::huge::mod_pow(huge exp, huge n){
        unsigned int i = exp._size;
        unsigned char mask;
        huge tmp1(0),tmp2(0);
    
        tmp1=this;
    
        sethuge(1);
    
        do{
            i--;
            for (mask = 0x01; mask; mask <<= 1){
                if (exp._rep[i] & mask){
                    operator*=(tmp1);
                    operator/=(n);
                }
                // square tmp1
                tmp2 = tmp1;
                tmp1 *= tmp2;
                tmp1 /= n;
            }
        } while (i);
    }
    
    void sys::crypt::huge::inv(huge a)
    {
        huge i(1), j(1), y2(0), y1(1), y(1), remainder(1), a_temp(1);
    
        huge quotient(1);
    
    
        i=a;
        j=this;
        if (_sign){
            j/=a;
            // force positive remainder always
            j._sign = 0;
            j-=a;
        }
    
        while (!((j._size == 1) && (!j._rep[0]))){
            remainder=i;
            i=j;
    
            quotient = remainder / j;
    
            quotient*=y1; // quotient = y1 * quotient
            y=y2;
            y-=quotient;  // y = y2 - ( y1 * quotient )
    
            j=remainder;
            y2=y1;
            y1=y;
        }
    
        operator=(y2);
        a_temp=a;
        operator/=(a_temp);  // inv_z = y2 % a
    
        if (_sign){
            _sign = 0;
            operator-=(a_temp);
            if (_sign){
                _sign = 0;
            }
        }
    }
    
    size_t sys::crypt::huge::size() {
        return _size;
    }
    
    sys::crypt::huge::result::result() {
        _resmut.lock();
        _first = nullptr;
        _last = nullptr;
        _resmut.unlock();
    }
    
    sys::crypt::huge::result::~result() {
        _resmut.lock();
        delete _first;
        _resmut.unlock();
    }
    
    sys::crypt::huge::result::data::data(huge *res) {
        _current = res;
        _next = nullptr;
    }
    
    sys::crypt::huge::result::data::~data() {
        delete _current;
        delete _next;
    }
    
    void sys::crypt::huge::result::add(huge *res) {
        _resmut.lock();
        if (_first) {
            _last->_next = new data(res);
            _last = _last->_next;
        } else {
            _first = new data(res);
            _last = _first;
        }
        _resmut.unlock();
    }
    

    rsa.cpp

    /*******************************************************************************
    Copyright (c) 2022, Jan Koester jan.koester@gmx.net
    All rights reserved.
    
    Redistribution and use in source and binary forms, with or without
    modification, are permitted provided that the following conditions are met:
        * Redistributions of source code must retain the above copyright
          notice, this list of conditions and the following disclaimer.
        * Redistributions in binary form must reproduce the above copyright
          notice, this list of conditions and the following disclaimer in the
          documentation and/or other materials provided with the distribution.
        * Neither the name of the <organization> nor the
          names of its contributors may be used to endorse or promote products
          derived from this software without specific prior written permission.
    
    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
    ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
    DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
    DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
    ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    *******************************************************************************/
    
    #include <systempp/syscrypt.h>
    #include <systempp/sysexception.h>
    
    /**
     * Compute c = m^e mod n.
     */
    void sys::crypt::rsa::compute(huge* m, huge* e, huge* n, huge* c){
        huge counter(1);
        huge one(1);
    
        c=m;
    
        while (counter.compare(*e) < 0){
            *c*=*m;
            counter+=one;
        }
    
        *c/= *n;
        // Remainder (result) is now in c
    }
    
    /**
     * The input should be broken up into n-bit blocks, where n is the
     * length in bits of the modulus. The output will always be n bits
     * or less. Per RFC 2313, there must be at least 8 bytes of padding
     * to prevent an attacker from trying all possible padding bytes.
     *
     * output will be allocated by this routine, must be freed by the
     * caller.
     *
     * returns the length of the data encrypted in output
     */
    int sys::crypt::rsa::encrypt(unsigned char* input,
        unsigned int len,
        unsigned char** output,
        key public_key){
    
        size_t modulus_length = public_key.modulus.size();
        unsigned char* padded_block = new unsigned char [modulus_length];
        int encrypted_size = 0;
    
        while (len){
            huge m;
            int block_size = (len < modulus_length - 11) ?
                len : (modulus_length - 11);
            memset(padded_block, 0, modulus_length);
            memcpy(padded_block + (modulus_length - block_size),
                input, block_size);
            // set block type
            padded_block[1] = 0x02;
    
            for (int i = 2; i < (modulus_length - block_size - 1); i++)
            {
                // TODO make these random
                padded_block[i] = i;
            }
    
            m.load_huge(padded_block, modulus_length);
            m.mod_pow(public_key.exponent, public_key.modulus);
    
            *output = new unsigned char[encrypted_size+modulus_length];
    
            m.unload_huge(*output + encrypted_size,&modulus_length);
    
            encrypted_size += modulus_length;
            len -= block_size;
            input += block_size;
        }
    
        return encrypted_size;
    }
    
    /**
     * Convert the input into key-length blocks and decrypt, unpadding
     * each time.
     * Return -1 if the input is not an even multiple of the key modulus
     * length or if the padding type is not �2�, otherwise return the
     * length of the decrypted data.
     */
    int sys::crypt::rsa::decrypt(unsigned char* input,
        unsigned int len,
        unsigned char** output,
        key private_key){
    
        huge hgt;
        int i, out_len = 0;
        huge c;
        size_t modulus_length = private_key.modulus.size();
        unsigned char* padded_block = new unsigned char [modulus_length];
    
        *output = nullptr;
    
        while (len){
            if (len < modulus_length){
                delete[] padded_block;
                SystemException excep; 
                excep[SystemException::Error] << "Error - input must be an even multiple  of key modulus "
                    << private_key.modulus.size() << " (got" << len << ")";
                throw excep;
            }
    
            c.load_huge(input, modulus_length);
            c.mod_pow(private_key.exponent,private_key.modulus);
    
            c.unload_huge(padded_block, &modulus_length);
    
            if (padded_block[1] > 0x02){
                SystemException excep;
                delete[] padded_block;
                excep[SystemException::Error] << "Decryption error or unrecognized block type " << padded_block[1];
                throw excep;
            }
    
            // Find next 0 byte after the padding type byte; this signifies
            // start-of-data
            i = 2;
            while (padded_block[i++]);
    
            out_len += modulus_length - i;
            
            unsigned char* tmp = new unsigned char[out_len];
            memcpy(tmp, *output, out_len);
            delete[] * output;
            *output = tmp;
    
            memcpy(*output + (out_len - (modulus_length - i)),
                padded_block + i, modulus_length - i);
    
            len -= modulus_length;
            input += modulus_length;
        }
    
        delete[] padded_block;
    
        return out_len;
    }
    

    Unittest.cpp

    /*******************************************************************************
    Copyright (c) 2022, Jan Koester jan.koester@gmx.net
    All rights reserved.
    
    Redistribution and use in source and binary forms, with or without
    modification, are permitted provided that the following conditions are met:
        * Redistributions of source code must retain the above copyright
          notice, this list of conditions and the following disclaimer.
        * Redistributions in binary form must reproduce the above copyright
          notice, this list of conditions and the following disclaimer in the
          documentation and/or other materials provided with the distribution.
        * Neither the name of the <organization> nor the
          names of its contributors may be used to endorse or promote products
          derived from this software without specific prior written permission.
    
    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
    ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
    DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
    DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
    ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    *******************************************************************************/
    
    #include <systempp/sysconsole.h>
    #include <systempp/syscrypt.h>
    #include <stdio.h>
    
    void show_hex(const unsigned char* array, int length) {
        while (length--) {
            printf("%.02x", *array++);
        }
        printf("\r\n");
    }
    
    const unsigned char TestModulus[] = {
    0xC4, 0xF8, 0xE9, 0xE1, 0x5D, 0xCA, 0xDF, 0x2B,
    0x96, 0xC7, 0x63, 0xD9, 0x81, 0x00, 0x6A, 0x64,
    0x4F, 0xFB, 0x44, 0x15, 0x03, 0x0A, 0x16, 0xED,
    0x12, 0x83, 0x88, 0x33, 0x40, 0xF2, 0xAA, 0x0E,
    0x2B, 0xE2, 0xBE, 0x8F, 0xA6, 0x01, 0x50, 0xB9,
    0x04, 0x69, 0x65, 0x83, 0x7C, 0x3E, 0x7D, 0x15,
    0x1B, 0x7D, 0xE2, 0x37, 0xEB, 0xB9, 0x57, 0xC2,
    0x06, 0x63, 0x89, 0x82, 0x50, 0x70, 0x3B, 0x3F
    };
    
    const unsigned char TestPrivateKey[] = {
    0x8a, 0x7e, 0x79, 0xf3, 0xfb, 0xfe, 0xa8, 0xeb,
    0xfd, 0x18, 0x35, 0x1c, 0xb9, 0x97, 0x91, 0x36,
    0xf7, 0x05, 0xb4, 0xd9, 0x11, 0x4a, 0x06, 0xd4,
    0xaa, 0x2f, 0xd1, 0x94, 0x38, 0x16, 0x67, 0x7a,
    0x53, 0x74, 0x66, 0x18, 0x46, 0xa3, 0x0c, 0x45,
    0xb3, 0x0a, 0x02, 0x4b, 0x4d, 0x22, 0xb1, 0x5a,
    0xb3, 0x23, 0x62, 0x2b, 0x2d, 0xe4, 0x7b, 0xa2,
    0x91, 0x15, 0xf0, 0x6e, 0xe4, 0x2c, 0x41
    };
    
    const unsigned char TestPublicKey[] = { 0x01, 0x00, 0x01 };
    
    int main(int argc, char* argv[]){
    	size_t exponent_len;
    	size_t modulus_len;
        size_t data_len;
        unsigned char* exponent;
        unsigned char* modulus;
        unsigned char* data;
        sys::crypt::rsa  rsa;
        sys::crypt::rsa::key public_key;
        sys::crypt::rsa::key private_key;
        sys::crypt::hex hex;
      
    	/*encrypt*/
    	data_len = hex.decode("testdata",8, &data);
    	modulus_len = sizeof(TestModulus);
    	modulus = (unsigned char*)TestModulus;
    	exponent_len = sizeof(TestPublicKey);
    	exponent = (unsigned char*)TestPublicKey;
    
    	private_key.modulus = new sys::crypt::huge();
    	private_key.exponent = new sys::crypt::huge();
    
    
    	unsigned char* encrypted;
    	size_t encrypted_len;
    
    	public_key.modulus.load_huge(modulus, modulus_len);
    	public_key.exponent.load_huge(exponent, exponent_len);
    	try {
    		encrypted_len = rsa.encrypt(data, data_len, &encrypted, public_key);
    	}catch (...) {
    		return -1;
    	}
    	show_hex(encrypted, encrypted_len);
    	delete[] encrypted;
    
        /*Decrypt*/
    	exponent_len = sizeof(TestPrivateKey);
    	exponent = (unsigned char*)TestPrivateKey;
    
    	size_t decrypted_len;
    	unsigned char* decrypted;
    
    	private_key.modulus.load_huge(modulus, modulus_len);
    	private_key.exponent.load_huge(exponent, exponent_len);
    
    	try {
    		decrypted_len = rsa.decrypt(data, data_len, &decrypted, private_key);
    	}catch(...) {
    		return -1;
    	}
    	show_hex(decrypted, decrypted_len);
    
    	delete[] decrypted;
    	delete[] data;
    
        return 0;
    }
    

    Der Komplette Code ist auch unter https://tuxist.de/git/jan.koester/systempp/-/tree/without-libc zu finden.


  • Mod

    @Tuxist1 sagte in Huge Zahlenklasse mit RSA Absturz bei deconstructor:

    Das ich hier keine STL Klassen verwende liegt daran das ich Sie nicht zur Verfügung habe.

    Wenn du new haben kannst, kannst du auch vector haben. Jedenfalls wird fehlerhafte Ressourcenverwaltung zu 99% die Fehlerursache sein. Deine Implementierung hat keinerlei Trennung zwischen der Kernfunktionalität und Ressourcenverwaltung, das werde ich mir gewiss nicht genauer durchlesen. Implementier zuerst einmal ein funktionierendes dynamisches Array (dessen Korrektheit viel einfacher feststellbar ist, wenn es nicht in einer anderen Klasse hängt), und damit dann den Rest, dann wird sich das Problem gewiss alleine lösen.



  • Ja, das was @SeppJ gesagt hat - ansonsten fällt mir beim groben Überfliegen ins Auge, dass dein Problem wahrscheinlich u.a. daran liegt, dass du die Rule of Five nicht beachtet hast (recherchier mal). Da werden möglicherweise irgendwo Kopien von huge, huge::result und huge::result::data angelegt, die den gleichen Pointer haben und ziemlich unkontrolliert ihre Destruktoren mit diesem Pointer ausführen. Dort, wo du es wahrscheinlich nicht erwartest.

    Alternativ zu std::vector kannst du auch überlegen, ob du vielleicht auch ohne dynamischen Speicher und stattdessen mit einem std::array<char, N> oder char[N] auskommst, also huge-Typen mit fester Länge. Da würde dann die Rule of Zero reichen und die Implementierung simpler und wegen der fehlenden zusätzlichen Indirektion auch etwas effizienter machen.

    Für RSA dürfte man nicht wirklich mehr als 64k Bits benötigen, was etwa 8 KiB pro huge Integer wären. Das ist für ein paar dieser Zahlen auf dem Stack durchaus noch vertretbar, je nach System worauf das laufen soll. Und wenn nicht, könnte man man diese "flachen" huge immer noch auf dem Heap anlegen (z.B. via make_unique<huge>()).


  • Mod

    Der Trick an Vector (oder eigenen Ressourcenklassen) ist ja gerade, dass auch diese für die Anwendungsklassen die Rule of 0 ermöglichen 🙂



  • @SeppJ sagte in Huge Zahlenklasse mit RSA Absturz bei deconstructor:

    Der Trick an Vector (oder eigenen Ressourcenklassen) ist ja gerade, dass auch diese für die Anwendungsklassen die Rule of 0 ermöglichen 🙂

    Ja stimmt, so hab ich das nicht gesehen. Mein Bild davon war eher, dass es immer noch beachtet werden muss, aber es eben "jemand anderes" bereits gemacht hat... für die std::vector verwendende Klasse ist das natürlich dennoch Rule of Zero 😉



  • Habe jetzt Speicher Zuordnung in eine extra klasse gepackt:

        template<>
        class array<unsigned char> {
        public:
            array() {
                _buf = nullptr;
                _size = 0;
                _length = 0;
            };
    
            array(array const& t) {
                _buf = nullptr;
                _size = t.size();
                _length = t.length();
                write(t.c_str(), t.size());
            };
    
            virtual ~array() {
                delete[] _buf;
            }
    
            array operator=(const unsigned char t) {
                clear();
                _buf = new unsigned char[1];
                _buf[0] = t;
                _size = 1;
                _length = 1;
                return *this;
            };
    
            void write(const unsigned char* t, unsigned long size) {
                if ((_size-_length) < size) {
                    resize((_length+size));
                }
                if(_length>0)
                    memcpy(_buf+(_length-1),t,size);
                else
                    memcpy(_buf,t, size);
                _length += size;
            }
    
            array operator=(array t) {
                clear();
                write(t.c_str(), t.size());
                _length = t.length();
                _size = t.size();
                return *this;
            }
    
            unsigned char& operator[](int pos) {
                return _buf[pos];
            };
    
            void resize(size_t size) {
                if (_length > size) {
                   return;
                }
                unsigned char* tmp = _buf;
                _buf = new unsigned char[size];
                memcpy(_buf,tmp,_length);
                _size = size;
                delete[] tmp;
            }
    
            size_t size() const {
                return _size;
            };
    
            size_t length() const {
                return _length;
            }
    
            void push_back(char t) {
                if (_size-_length < 1) {
                    resize(_length + 1);
                }
                _buf[_length] = t;
                ++_length;
            };
    
            void clear() {
                delete[] _buf;
                _size = 0;
                _length = 0;
                _buf = nullptr;
            };
    
            constexpr const unsigned char* c_str() const noexcept {
                return _buf;
            };
    
            size_t length() {
                return _size;
            };
    
            bool empty() {
                if (!_buf)
                    return true;
                return false;
            }
    
        private:
            unsigned char* _buf;
            size_t         _size;
            size_t         _length;
        };
    

    leider bekomme ich bei resize folgenden Fehler:

    libc.so.6!__memmove_avx_unaligned_erms() Line 254 C++



  • fehler gefunden:

            array(array const& t) {
                _buf = nullptr;
                _size = t.size();
                _length = t.length();
                write(t.c_str(), t.size());
            };
    

    zu

            array(array const& t) {
                _buf = new unsigned char[t.size()];
                memcpy(_buf,t._buf,t.length());
                _size = t.size();
                _length = t.length();
            };
    


  • @Tuxist1
    Und warum nutzt du nicht std::array oder std::vector?



  • @Tuxist1
    Ich habe noch ein paar Fragen/Anmerkungen bezüglich deiner array Implementierung:

    • Ist dein array eigentlich nicht ein std::basic_string? Wenn ja, warum spezialisierst du dann das Template?
    • Wo ist dein Move Konstruktor und deine Move Zuweisung?
    • Warum allokierst du ständig einen neuen Puffer, wenn du ein Zeichen hinzufügst? Nehmen wir mal an, du möchtest ein Bild der Größe 1920x1280 einlesen. Dann würde deine Klasse 1920x1280 = 2457600 Allokationen durchführen. Ein std::vector führt unter VS 2019 in diesem Fall aber nur um die 40 Allokationen aus. Warum? Weil gilt "Neue Containergröße = Alte Containergröße * 1.5"
    • Wie sieht es mit deiner Exception Safety aus? (Stichwort Copy-Swap Idiom)
    • Solltest du da nicht auch noch eine Small String Optimization implementieren?
    • Wird eine leere Instanz gelöscht, so rufst du die Funktion delete mit einem nullptr auf.


  • Es gibt einen zweiten branch wo ich noch die libc mit implementiere kann deshalb kein std:: einbinden kann da std:: wiederum von der libc abhängig ist.

    Das hier ist nur mein testbranch.



  • @Tuxist1 sagte in Huge Zahlenklasse mit RSA Absturz bei deconstructor:

    Es gibt einen zweiten branch wo ich noch die libc mit implementiere kann deshalb kein std:: einbinden kann da std:: wiederum von der libc abhängig ist.

    Was verwendet denn der operator new bei dir unter der Haube? Üblicherweise baut der auf malloc auf und das ist für gewöhnlich Teil der libc. Alles natürlich nicht obligatorisch, letztendlich kann man sich das alles beliebig zusammenstöpseln.

    Wenn du also irgendwo eine malloc-Implementierung hast, dann ist das meiner Einschätzung nach alles was ein std::vector oder ein std::basic_string brauchen. Ich weiss allerdings nicht, wie gut sich diese Klassen aus deiner C++-Standardbibliothek isolieren lassen, wenn malloc/free alles ist, was man hat und der Rest der libc fehlt. Die libstdc++ hab ich jedenfalls bisher immer mit einer vorhandenen libc gebaut. Vielleicht lohnt es sich das mal zu recherchieren. Eventuell auch mal libc++ anschauen. Die ist nicht so alt und könnte eventuell solche etwas unorthodoxeren Setups besser unterstützen.



  • Die Frage ist auch um was für eine (Hardware) Platform es sich hier handelt, und was der Grund ist, das auf eine libc (um welchen denn überhaupt?) verzichtet werden muss.



  • @Finnegan sagte in Huge Zahlenklasse mit RSA Absturz bei deconstructor:

    Wenn du also irgendwo eine malloc-Implementierung hast, dann ist das meiner Einschätzung nach alles was ein std::vector oder ein std::basic_string brauchen.

    Nein, da kommt einiges an Abhängigkeiten zusammen. Das fängt damit an, dass die Container nicht malloc nutzen sondern std::allocator<T>, und dann kommen da so Abhängigkeiten wie die Traits-, Exception-, … -Header dazu.



  • Noch ein Tipp von mir: Arbeite dich in Unittests ein und teste deine Module ( z.B. dein Array ) einzeln ab. Unittests sind ein wirksames mittel gegen solche Fehler wie bei deinem Resize.
    Und selbst wenn du kein Test-Framework aus irgendwelchen Gründen verwenden kannst, kannst du trotzdem Unittests programmieren, denn letztendlich prüfst du nur Rückgabewerte oder Stati von irgendwelchen Variablen und dazu braucht es nicht zwingend ein Framework wie gtest oder anderes.



  • @john-0 sagte in Huge Zahlenklasse mit RSA Absturz bei deconstructor:

    @Finnegan sagte in Huge Zahlenklasse mit RSA Absturz bei deconstructor:

    Wenn du also irgendwo eine malloc-Implementierung hast, dann ist das meiner Einschätzung nach alles was ein std::vector oder ein std::basic_string brauchen.

    Nein, da kommt einiges an Abhängigkeiten zusammen. Das fängt damit an, dass die Container nicht malloc nutzen sondern std::allocator<T>, und dann kommen da so Abhängigkeiten wie die Traits-, Exception-, … -Header dazu.

    Ich meine damit schon externe Abhängigkeiten. Der Default-Allocator std::allocator<T> dürfte mit dem malloc der libc implementiert sein und bei den anderen genannten Headern sehe ich nicht, was die aus der libc brauchen sollten. Exception Handling könnte eventuell Unterstützung einer (externen) Compiler-Runtime benötigen, die auch wiederum Abhängigkeiten haben kann. So genau weiss ich das nicht. Wie ich schon sagte, es is möglich, dass sich das je nach Implementierung der C++-Standardbibliothek alles nicht so leicht voneinander trennen lässt, aber ich sehe nicht wie ein std::vector inklusive aller Abhängigkeiten grundsätzlich mehr als die malloc-Implementierung aus der libc benötigen sollte.



  • @Finnegan sagte in Huge Zahlenklasse mit RSA Absturz bei deconstructor:

    Der Default-Allocator std::allocator<T> dürfte mit dem malloc der libc implementiert sein …

    Ja, es ist eine der Möglichkeiten, aber es gab/gibt hinreichend viele Compiler die unter UNIX/Linux (keine Ahnung wie die API auf anderen Plattformen aussieht) direkt brk/sbrk nutzen. Und die ganzen Header sollte man auch nicht unterschätzen, weil man sie ggf. erst einmal implementieren muss.



  • @john-0 sagte in Huge Zahlenklasse mit RSA Absturz bei deconstructor:

    @Finnegan sagte in Huge Zahlenklasse mit RSA Absturz bei deconstructor:

    Der Default-Allocator std::allocator<T> dürfte mit dem malloc der libc implementiert sein …

    Ja, es ist eine der Möglichkeiten, aber es gab/gibt hinreichend viele Compiler die unter UNIX/Linux (keine Ahnung wie die API auf anderen Plattformen aussieht) direkt brk/sbrk nutzen. Und die ganzen Header sollte man auch nicht unterschätzen, weil man sie ggf. erst einmal implementieren muss.

    brk/sbrk/mmap sind schon ziemlich low-level, erstere mehr wie ne Stack-Allokation und letzteres auf Speicherseiten-Ebene. Da bräuchte es auf jeden Fall noch ne generische Speicherverwaltung obendrauf die etwas platzeffizienter für beliebige angeforderte Größen ist und den Speicher auch sinnvoll freigeben und wiederverwenden kann. Eben sowas wie malloc. Die genannten Compiler müssten dann sowas selbst mitbringen.

    Ich wollte es aber nicht zu sehr abschweifen lassen. Eigentlich war ich ja hauptsächlich neugierig, wo bei @Tuxist1 der operator new herkommt ohne libc. Kann man natürlich auch separat einbauen.



  • @Tuxist1 sagte in Huge Zahlenklasse mit RSA Absturz bei deconstructor:

    Es gibt einen zweiten branch wo ich noch die libc mit implementiere kann deshalb kein std:: einbinden kann da std:: wiederum von der libc abhängig ist.

    Sorry das ich da mal nachhake.

    Aber ich verstehe nicht warum man libc bzw. STL nicht nutzen möchte.

    In der STL steckt eine Menge Hirnschmalz und lässt sich nicht so einfach nachprogrammieren. Deine array Implementierung mit c_str() Funktion z.B. sieht unter Visual Studio folgendermaßen aus:

    https://github.com/microsoft/STL/blob/main/stl/inc/xstring

    Und wo ich gerade in deinem Repo eine Datei namens atomic.cpp sehe, möchte ich dir auch die Visual Stdio Variante zeigen:

    https://github.com/microsoft/STL/blob/main/stl/inc/atomic

    8000 LOC, Holla die Waldfee



  • Und dass keine libc verwendet wird ist aktuell nicht korrekt.

    z.b. werden folgende header in verschiedenen cpp dateien inkludiert:

    Wobei eventuell der Plan ist, diese durch eine eigene Implementierung zu ersetzen?

    Wenn es nicht gerade aus reiner spielerei/wissbegierde ist, sollte man das tunlichst vermeiden so was selbst zu implementieren.



  • in dem main branch wird libc mit implementiert:

    https://tuxist.de/git/jan.koester/systempp/-/tree/main

    habe einen zweiten branch der mit standart libc gebaut wird:

    https://tuxist.de/git/jan.koester/systempp/-/tree/without-libc

    wollte erstmal in den zweiten tree https einbauen damit das backend von meinem Blog
    "http://tuxist.de" wieder funktioniert hat mit den cookie richtlinien zu tun.

    Aber jetzt mal zu systempp soll die basis von fenriros bilden, im moment bin ich unzufrieden über den wildwuchs von unix/linux konfiguartions Dateien jeder und alles macht da was er will führt dazu das es unnötig viel Arbeit konfigurations Oberflächen dafür zu schreiben. Das zweite Problem was ich sehe ist das man auf tiefere ebene zum teil sehr schwerig zu verstehende api hat und meist nur auf c ebene. Ich versuche da Abhilfe zu schaffen da ich als Rentner jetzt zeit dafür habe.

    Unterschiede Fenriros zu anderen Posix Systemen:
    -confdb mit conffs eine art registry
    -sid statt fortlaufender uid's nt kompatible
    -vollständige C++ ABI
    -bsd lizens
    -keine gnu abhängikeiten

    Nach dem https implementiert werde ich mich wieder meinem elfparser widmen damit ich endlich in System chrooten kann und danach stdio implementieren danach noch paar kleine posix geschichten damit hoffentlich llvm unter der chroot kompeliert der basis Compiler für das System. Danach Feinit damit ich ein init system habe und CMake support für Linux Kernel und Python.


Anmelden zum Antworten