Crypto++ Convert Integer to Unsigned Char

58 views
Skip to first unread message

Dwight Kulkarni

unread,
Apr 21, 2023, 7:39:11 PM4/21/23
to Crypto++ Users
Hi everyone,

I need to convert Integer to unsigned char array or to vector<unsigned char>

If I do this:
for(int i=0; i<c.ByteCount(); i++){
  cout << int(c.GetByte(i)) << " ";
}

The values are not what I expect. Also, is there anything faster than a for loop to get the data out ?

I have verified I have the correct integer, but when I access the values in this way, they are incorrect.

Jeffrey Walton

unread,
Apr 21, 2023, 8:51:59 PM4/21/23
to cryptop...@googlegroups.com
On Fri, Apr 21, 2023 at 7:39 PM Dwight Kulkarni <dwi...@realtime-7.com> wrote:
>
> I need to convert Integer to unsigned char array or to vector<unsigned char>
>
> If I do this:
> for(int i=0; i<c.ByteCount(); i++){
> cout << int(c.GetByte(i)) << " ";
> }
>
> The values are not what I expect. Also, is there anything faster than a for loop to get the data out ?

The Integer class uses Encode when it needs to format an Integer as a
byte array for various functions, like DER encoding. You might try
something like:

#include <iostream>
#include "integer.h"
#include "osrng.h"

int main(int argc, char* argv[])
{
using namespace CryptoPP;

AutoSeededRandomPool prng;
Integer n;

n.Randomize(prng, 128);
const size_t len = n.MinEncodedSize(Integer::UNSIGNED);

std::vector<byte> v;
v.resize(len);
n.Encode((byte*)&v[0], v.size(), Integer::UNSIGNED);

std::cout << "Iostream: " << std::hex << n << std::endl;
std::cout << " Vector: ";
for(size_t i : v) { std::cout << (i & 0xff); }
std::cout << std::endl;

return 0;
}

It will produce output similar to:

jwalton@coffee:~/cryptopp$ g++ -g2 -O3 test.cxx ./libcryptopp.a -o test.exe
jwalton@coffee:~/cryptopp$ ./test.exe
Iostream: b32bd756bde62ea5124552714147af6eh
Vector: b32bd756bde62ea5124552714147af6e
jwalton@coffee:~/cryptopp$ ./test.exe
Iostream: fcbf89617f7f1cf55c1016b2355152e9h
Vector: fcbf89617f7f1cf55c1016b2355152e9

I'll get the example added to the wiki at
https://www.cryptopp.com/wiki/Integer .

Jeff

Dwight Kulkarni

unread,
Apr 24, 2023, 10:01:46 AM4/24/23
to Crypto++ Users
Hi Jeffery,

I tried out your code and it works to generate the exact same encrypted message as Python if I use the Integer approach then change it to bytes.

To make messages comparable, I used the _pad_for_signing  code (see below) to generate exactly the same header.

For Pcks1 v 1.5  it is expecting something like this:

00 01 PADDING 00 MESSAGE  --> for signing 
00 02 PADDING 00 MESSAGE  --> for encryption

where     padding_length = key_size - msglength - 3

Three bytes are 00   [01/02]   and 00  as above. Padding is randomly generated for encrytion or set to 0xff for signing and excludes 00 bytes. Then the padding is 00 terminated.

However, this is not working with the following code from cryptopp.  Can you confirm if the PKCS1v15_Encryptor will produce the same logic as above ?  Could it be that the output is base64 encoded again or something rather than raw bytes, because it is going into a StringSink(..) Maybe it is not terminating the 00 padding or has the leading 00 02 ?

I can confirm, that the python function decrypts the string but runs into problems parsing the decrypted output.

std::string encrypt_rsa(std::string message, CryptoPP::RSA::PublicKey key)
{

try{

message = b64encode(message);
CryptoPP::AutoSeededRandomPool rng;

//CryptoPP::RSAES_OAEP_SHA_Encryptor encryptor(key);
CryptoPP::RSAES_PKCS1v15_Encryptor encryptor(key);
std::string ciphertext;
CryptoPP::StringSource(message, true, new CryptoPP::PK_EncryptorFilter(rng, encryptor, new CryptoPP::StringSink(ciphertext)));
return ciphertext;

}
catch(...)
{
std::cout << "error encrypting RSA";
return "";
}
}






def _pad_for_signing(message: bytes, target_length: int) -> bytes:
    r"""Pads the message for signing, returning the padded message.

    The padding is always a repetition of FF bytes.

    :return: 00 01 PADDING 00 MESSAGE

    >>> block = _pad_for_signing(b'hello', 16)
    >>> len(block)
    16
    >>> block[0:2]
    b'\x00\x01'
    >>> block[-6:]
    b'\x00hello'
    >>> block[2:-6]
    b'\xff\xff\xff\xff\xff\xff\xff\xff'

    """

    max_msglength = target_length - 11
    msglength = len(message)

    if msglength > max_msglength:
        raise OverflowError(
            "%i bytes needed for message, but there is only"
            " space for %i" % (msglength, max_msglength)
        )

    padding_length = target_length - msglength - 3

    return b"".join([b"\x00\x01", padding_length * b"\xff", b"\x00", message])





def _pad_for_encryption(message: bytes, target_length: int) -> bytes:
    r"""Pads the message for encryption, returning the padded message.

    :return: 00 02 RANDOM_DATA 00 MESSAGE

    >>> block = _pad_for_encryption(b'hello', 16)
    >>> len(block)
    16
    >>> block[0:2]
    b'\x00\x02'
    >>> block[-6:]
    b'\x00hello'

    """

    max_msglength = target_length - 11
    msglength = len(message)

    if msglength > max_msglength:
        raise OverflowError(
            "%i bytes needed for message, but there is only"
            " space for %i" % (msglength, max_msglength)
        )

    # Get random padding
    padding = b""
    padding_length = target_length - msglength - 3

    # We remove 0-bytes, so we'll end up with less padding than we've asked for,
    # so keep adding data until we're at the correct length.
    while len(padding) < padding_length:
        needed_bytes = padding_length - len(padding)

        # Always read at least 8 bytes more than we need, and trim off the rest
        # after removing the 0-bytes. This increases the chance of getting
        # enough bytes, especially when needed_bytes is small
        new_padding = os.urandom(needed_bytes + 5)
        new_padding = new_padding.replace(b"\x00", b"")
        padding = padding + new_padding[:needed_bytes]

    assert len(padding) == padding_length

    return b"".join([b"\x00\x02", padding, b"\x00", message])

One Sini

unread,
Apr 24, 2023, 11:30:41 AM4/24/23
to cryptop...@googlegroups.com
Hello my friend!  I think that maybe my code can help.  in cpp and python.  Sincerely,  Satoshi Nakamoto

Cpp Code

#include <iostream>
#include <string>
#include <cryptopp/base64.h>
#include <cryptopp/cryptlib.h>
#include <cryptopp/filters.h>
#include <cryptopp/osrng.h>
#include <cryptopp/rsa.h>
#include <cryptopp/sha.h>

using namespace CryptoPP;

std::string rsa_encrypt(const std::string& message, const RSA::PublicKey& key) {
    AutoSeededRandomPool rng;

    // Pad the message
    size_t messageLength = message.length();
    size_t maxMessageLength = key.MaxMessageLength();
    if (messageLength > maxMessageLength) {
        throw std::length_error("Message too long for key");
    }

    std::vector<byte> paddedMessage(maxMessageLength);
    size_t paddedMessageLength = key.ApplyPKCS1Padding((byte*)paddedMessage.data(), paddedMessage.size(), (const byte*)message.data(), messageLength, rng, RSA::ES_PKCS1v15);

    // Encrypt the padded message
    std::vector<byte> encryptedMessage(maxMessageLength);
    size_t encryptedMessageLength = key.Encrypt(rng, (const byte*)paddedMessage.data(), paddedMessageLength, encryptedMessage.data());

    // Convert the encrypted message to base64
    std::string base64EncryptedMessage;
    StringSource(encryptedMessage.data(), encryptedMessageLength, true, new Base64Encoder(new StringSink(base64EncryptedMessage)));

    return base64EncryptedMessage;
}

int main() {
    // Generate a key pair
    AutoSeededRandomPool rng;
    InvertibleRSAFunction parameters;
    parameters.GenerateRandomWithKeySize(rng, 4096);
    RSA::PrivateKey privateKey(parameters);
    RSA::PublicKey publicKey(parameters);

    // Encrypt a message
    std::string message = "Hello, world!";
    std::string encryptedMessage = rsa_encrypt(message, publicKey);
    std::cout << "Encrypted message: " << encryptedMessage << std::endl;

    return 0;
}

Python Code

import os
import base64
from Crypto.Cipher import PKCS1_OAEP
from Crypto.PublicKey import RSA
from Crypto.Hash import SHA256

def rsa_encrypt(message: bytes, public_key: bytes) -> bytes:
    # Load the public key
    key = RSA.import_key(public_key)

    # Pad the message
    target_length = key.size_in_bytes()
    padded_message = _pad_for_encryption(message, target_length)

    # Encrypt the padded message
    cipher = PKCS1_OAEP.new(key, hashAlgo=SHA256)
    encrypted_message = cipher.encrypt(padded_message)

    # Encode the encrypted message in base64
    base64_encrypted_message = base64.b64encode(encrypted_message)

    return base64_encrypted_message

def _pad_for_encryption(message: bytes, target_length: int) -> bytes:
    # Pad the message using PKCS#1 v1.5 padding
    max_msglength = target_length - 11
    msglength = len(message)

    if msglength > max_msglength:
        raise OverflowError(
            "%i bytes needed for message, but there is only"
            " space for %i" % (msglength, max_msglength)
        )

    padding_length = target_length - msglength - 3

    return b"".join([b"\x00\x02", os.urandom(padding_length), b"\x00", message])


--
You received this message because you are subscribed to the Google Groups "Crypto++ Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to cryptopp-user...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/cryptopp-users/d6e525fc-94ce-4747-a7d0-fd9f899ae726n%40googlegroups.com.

Dwight Kulkarni

unread,
Apr 24, 2023, 11:55:35 AM4/24/23
to Crypto++ Users
Hi,

Thank you for this, I just figured it out also with slightly different code. 

Here is my version. The issue was that I was converting it to vector<unsigned char>  and there was a bug in the conversion which was hard to debug because every time the message is changing.

std::string encrypt_rsa(std::string message, CryptoPP::RSA::PublicKey key)
{

try{

message = b64encode(message);
CryptoPP::AutoSeededRandomPool rng;

//CryptoPP::RSAES_OAEP_SHA_Encryptor encryptor(key);
CryptoPP::RSAES_PKCS1v15_Encryptor encryptor(key);
std::string ciphertext;
CryptoPP::StringSource(message, true, new CryptoPP::PK_EncryptorFilter(rng, encryptor, new CryptoPP::StringSink(ciphertext)));
return ciphertext;

}
catch(...)
{
std::cout << "error encrypting RSA";
return "";
}
}


import rsa

def generate_keys(name,size):
    (pub, priv) = rsa.newkeys(size)
    with open(name + 'pub_key.der', 'wb') as p:
        p.write(pub.save_pkcs1('DER'))
    with open(name + 'priv_key.der', 'wb') as p:
        p.write(priv.save_pkcs1('DER'))

def load_keys(name):
    with open(name + 'pub_key.der', 'rb') as p:
        pub = rsa.PublicKey.load_pkcs1(p.read(),'DER')
    with open(name + 'priv_key.der', 'rb') as p:
        priv = rsa.PrivateKey.load_pkcs1(p.read(),'DER')

    print("loaded der keys")
    return priv, pub

def encrypt_rsa(message, key):
    try:
        result = rsa.encrypt(base64.b64encode(message), key)
        return result
    except Exception as err:
        print("There was an error encryption RSA", err)
        return None

def decrypt_rsa(cipherbytes, key):
    try:
        base64_bytes = rsa.decrypt(cipherbytes, key)
        print("Got base64 bytes ", base64_bytes)
        return base64.b64decode(base64_bytes)
    except Exception as err:
        print(err)
        return None

Jeffrey Walton

unread,
Apr 24, 2023, 12:45:11 PM4/24/23
to cryptop...@googlegroups.com
On Mon, Apr 24, 2023 at 11:55 AM Dwight Kulkarni <dwi...@realtime-7.com> wrote:
>
> Thank you for this, I just figured it out also with slightly different code.
>
> Here is my version. The issue was that I was converting it to vector<unsigned char> and there was a bug in the conversion which was hard to debug because every time the message is changing.
>

To make debugging easier, use a deterministic generator. It will
produce the same output across runs.
https://www.cryptopp.com/wiki/RandomNumberGenerator#Reproducibility .

Obviously, don't use it for release or production software.

Jeff
Reply all
Reply to author
Forward
0 new messages