I've been looking into RSA and the attacks against weak implementations of it, such as when $e$ is small and there's no padding. On Wikipedia, the following attack is outlined for small $e$ values:

When encrypting with low encryption exponents (e.g., $e = 3$) and small values of the $m$, (i.e., $m < n^{1/e}$) the result of $m^e$ is strictly less than the modulus $n$. In this case, ciphertexts can be easily decrypted by taking the $e^\text{th}$ root of the ciphertext over the integers.

What I'm confused about here though is how do you carry out mathematical operations on ciphertext? I'm aware that all characters have numerical values, but what I'm unsure on is do you construct a single number from a whole ciphertext as you would for plain text to be encrypted (ie, convert each character to an integer then construct the number with the rightmost digit being most significant) and then carry out the operation on this number, or do you act on each individual number in the ciphertext? If it's the former, how do you determine to reconstruct the single number into a sequence of numbers that create the original plain text?

  • $\begingroup$ Neardupe crypto.stackexchange.com/questions/42344/plain-text-as-numbers . Also note in practice arbitrary byte/octet strings are often encoded into printable/displayable characters using base64 or sometimes hexadecimal; this includes not just ciphertexts signatures and hashes for crypto but also things like database identifiers, images, audio signals, and many more. These printable/displayable characters are not the actual value, and must be decoded to get the actual value, for which there are probably milllions of software libraries. $\endgroup$ – dave_thompson_085 Jan 27 '19 at 3:54
  • $\begingroup$ That reference is for encoding plaintext so it can be used as input (text to bytes to number). This one is about the ciphertext (number to bytes). $\endgroup$ – Maarten Bodewes Jan 27 '19 at 7:05

The ciphertext of RSA is actually just a number in the range 0..N that has been encoded to an octet string (another name for byte array) of the same size as the minimum modulus N. To be precise, the ciphertext is an unsigned, statically sized, big endian integer.

The PKCS#1 standard defines it this way, both for PKCS#1 as well as OAEP padding:

Convert the ciphertext representative c to a ciphertext C of length k octets (see Section 4.1):

C = I2OSP (c, k).

Where I2OSP - integer to octet string primitive - is defined here. I2OSP is just a mathematical way of defining how the number is encoded as described in the first section of the answer. Stupid implementations actually perform these kinds of calculations, most implementations require some resizing / reversing (in case little endian is used internally) and smart implementations already keep the numbers in the correct format in memory.

Now for textbook RSA, you can, of course, argue that pointing to the PKCS#1 specification is unfair because textbook RSA is not defined in that standard, and textbook RSA isn't required to follow the conventions specified in it. However, the fact that the ciphertext is just an encoded number remains. Sometimes the minimum length and / or little endian encoding is returned and of course, some implementations will simply return the number without performing encoding at all. However, in all cases, it should be easy to get the numeric value of the ciphertext - which is just the result of modular exponentiation with the public exponent after all.

Note that many languages - notably the C language - unfortunately handle bytes and characters as being the same type; they are not. The bytes generated by encoding a number can have any value and may not be printable. If you need a printable text that contains these bytes you will have to encode the bytes to a string, e.g. base 64 or hexadecimal encoding. Some libraries allow you to specify this as output size, and some bad ones even default to performing an encoding over the ciphertext bytes. To convert those back you need to first decode back into bytes and then perform the bytes-to-number conversion.

Despite the possible output values, you should never ever have to perform calculations on the individual bytes unless you cannot load any kind of library that does this for you and you will have to define your BigNum yourself (and yes, I have been there). Usually the cryptographic libraries themselves contain this kind of library; they need the functionality after all. They are quite often in the "hazmat" part of the library or not directly present in the API however.

| improve this answer | |
  • $\begingroup$ Thanks, this is a very comprehensive answer. I'm slightly unclear on the last point though - the ciphertext is indeed one single number constructed from the individual bytes, so even if I shouldn't be carrying out operations on those bytes myself, for what purpose would any existing libraries ever have to? $\endgroup$ – muke Jan 27 '19 at 20:14
  • $\begingroup$ Sorry for the confusion: you don't have to do anything with the individual bytes because the crypto libraries already can convert to a number themselves. Generally as part of e.g. a decrypt or verify operation, of course. For some kind of embedded devices such libraries may however not be available, too large or otherwise unsuitable, in which case you're screwed (like doing 32 bit integer arithmetic in the link given). $\endgroup$ – Maarten Bodewes Jan 27 '19 at 20:54

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.