Qt wiki will be updated on October 12th 2023 starting at 11:30 AM (EEST) and the maintenance will last around 2-3 hours. During the maintenance the site will be unavailable.

SimpleCrypt algorithm details: Difference between revisions

From Qt Wiki
Jump to navigation Jump to search
(Remove non-functioning "toc" command)
(convert {{doclinkref}} to the improved {{doclink}})
 
(2 intermediate revisions by one other user not shown)
Line 1: Line 1:
{{Cleanup | reason=Auto-imported from ExpressionEngine.}}
[[Category:Snippets]]
[[Category:Snippets]]
 
This page explains in more detail the format used by the [[Simple_encryption]] code. The encryption and decryption code works from a {{DocLink|QByteArray}} to a '''QByteArray'''. The options to use {{DocLink|QString}} are just provided for convenience. The first two sections detail how the stings and byte arrays correspond for both the plaintext and the cyphertexts. Then, the format of the binary '''QByteArray''' cyphertext is detailed.
 
= SimpleCrypt algorithm and format =
 
This page explains in more detail the format used by the [[Simple_encryption]] code. The encryption and decryption code works from a <code>QByteArray</code> to a <code>QByteArray</code>. The options to use <code>QString</code>s are just provided for convenience. The first two sections detail how the stings and byte arrays correspond for both the plaintext and the cyphertexts. Then, the format of the binary <code>QByteArray</code> cyphertext is detailed.


== String plaintext encoding ==
== String plaintext encoding ==
 
If a '''QString''' is used as the plaintext to encrypt, this string is encoded to a '''QByteArray''' by using the UTF8 codec. If a '''QString''' is requested as the result if a decryption action, the binary plaintext resulting from the decryption is interpretted using the same codec to construct a '''QString'''.
If a <code>QString</code> is used as the plaintext to encrypt, this string is encoded to a <code>QByteArray</code> by using the UTF8 codec. If a <code>QString</code> is requested as the result if a decryption action, the binary plaintext resulting from the decryption is interpretted using the same codec to construct a <code>QString</code>


== String cypher text format ==
== String cypher text format ==
'''''SimpleCrypt''''' can work with both '''QString''' and '''QByteArray''' cypher texts. The '''QString''' format is build on top of the '''QByteArray''' format. The '''QString''' is constructed from the '''QByteArray''' by creating a base64 encoded version of the binary cypher text, and using the ASCII codec to create a '''QString''' from this.


<code>SimpleCrypt</code> can work with both <code>QString</code> and <code>QByteArray</code> cypher texts. The <code>QString</code> format is build on top of the <code>QByteArray</code> format. The <code>QString</code> is constructed from the <code>QByteArray</code> by creating a base64 encoded version of the binary cypher text, and using the ASCII codec to create a <code>QString</code> from this.
For decryption, the reverse happens. The '''QString''' is translated into a binary format using the ASCII codec, and then the resulting '''QByteArray''' is decoded using the base64 codec into a binary cyper text.
 
For decryption, the reverse happens. The QString is translated into a binary format using the ASCII codec, and then the resulting <code>QByteArray</code> is decoded using the base64 codec into a binary cyper text.


== Binary cypher text format ==
== Binary cypher text format ==
The binary cypher text (represented in a '''QByteArray'''), consists of a header and a payload section:


The binary cypher text (represented in a <code>QByteArray</code>), consists of a header and a payload section:
{| border="1" class="wikitable"
 
! bytes
{|
! description
|'''bytes'''
|'''description'''
|-
|-
|<. 0-1
| 0-1
|<. Header
| Header
|-
|-
|<. 2-N
| 2-N
|<. Payload. This section is encrypted.
| Payload. This section is encrypted.
|}
|}


=== Header ===
=== Header ===
The header of the cypher text contains two fields: a version number, and a field with flags.
The header of the cypher text contains two fields: a version number, and a field with flags.


{|
{| border="1" class="wikitable"
|'''bytes'''
! bytes  
|'''description'''
! description
|-
|-
|<. 0
| 0
|<. Version
| Version
|-
|-
|<. 1
| 1
|<. Flags
| Flags
|}
|}


==== Version ====
==== Version ====
 
The version number is a char. ''The current version number is 2''.
The version number is a char. The current version number is 2.


==== Flags ====
==== Flags ====
The flags is a single byte bitfield. Each bit represents a flag that describes how the payload is to be interpreted.
The flags is a single byte bitfield. Each bit represents a flag that describes how the payload is to be interpreted.


{|
{| border="1" class="wikitable"
|'''bit'''
! bit
|'''value'''
! value
|'''description'''
! description
|-
|-
|<. 0
| 0
|0x01
| 0x01
|<. Compression. If set, compression has been applied to the plaintext before encryption
| Compression. If set, compression has been applied to the plaintext before encryption
|-
|-
|<. 1
| 1
|0x02
| 0x02
|<. Protection Checksum. If set, a CRC16-CCITT checksum, encoded in a quint16, has been used to protect data integrity.
| Protection Checksum. If set, a CRC16-CCITT checksum, encoded in a quint16, has been used to protect data integrity.
|-
|-
|<. 2
| 2
|0x04
| 0x04
|<. Protection Hash. If set, an SHA-1 cryptograhpic hash (20 bytes long) has been used to protect data integrity.
| Protection Hash. If set, an SHA-1 cryptograhpic hash (20 bytes long) has been used to protect data integrity.
|}
|}


Line 79: Line 67:


=== Payload ===
=== Payload ===
 
The payload data block's contents are encrypted with a eight byte (quint64) key. They key is split up in eight chars, which are used consecutively starting again from key char 0 after key char 7 has been used. The '''QByteArray''' is encrypted by replacing each byte in the array with the value of that byte itself, XOR-ed with the key char, XOR-ed with the result of this operation for the previous byte in the '''QByteArray''' or 0 for byte 0 of the array. This operation can be reversed by XOR-ing the resulting cypher text byte again with both bytes that were used for the encryption phase.
The payload data block's contents are encrypted with a eight byte (quint64) key. They key is split up in eight chars, which are used consecutively starting again from key char 0 after key char 7 has been used. The <code>QByteArray</code> is encrypted by replacing each byte in the array with the value of that byte itself, XOR-ed with the key char, XOR-ed with the result of this operation for the previous byte in the <code>QByteArray</code> or 0 for byte 0 of the array. This operation can be reversed by XOR-ing the resulting cypher text byte again with both bytes that were used for the encryption phase.


This results in the following encryption schema:
This results in the following encryption schema:


{|
{| border="1" class="wikitable"
|'''byte'''
! byte
|'''key byte'''
! key byte
|'''XOR value 2'''
! XOR value 2
|-
|-
|<. 0
| 0
|0
| 0
|<. the value 0
| the value 0
|-
|-
|<. 1
| 1
|1
| 1
|<. the result of this operation for byte 0
| the result of this operation for byte 0
|-
|-
|<. 2
| 2
|2
| 2
|<. the result of this operation for byte 1
| the result of this operation for byte 1
|-
|-
|<.
| …
|…
| …
|…
| …
|-
|-
|<. 7
| 7
|7
| 7
|<. the result of this operation for byte 6
| the result of this operation for byte 6
|-
|-
|<. 8
| 8
|0
| 0
|<. the result of this operation for byte 7
| the result of this operation for byte 7
|-
|-
|<. 9
| 9
|1
| 1
|<. the result of this operation for byte 8
| the result of this operation for byte 8
|-
|-
|…
| …
|…
| …
|…
| …
|-
|-
|<. N
| N
|N mod 8
| N mod 8
|<. the result of this operation for byte N-1
| the result of this operation for byte N-1
|}
|}


Line 129: Line 116:


=== Decrypted payload ===
=== Decrypted payload ===
Once the payload data has been decrypted, it may need further processing before the plaintext is available and verified. What processing is needed, depends on the flags that are set in the header of the cyphertext.
Once the payload data has been decrypted, it may need further processing before the plaintext is available and verified. What processing is needed, depends on the flags that are set in the header of the cyphertext.


In case the Protection Checksum flag has been set, the layout of the decrypted payload looks like this:
In case the Protection Checksum flag has been set, the layout of the decrypted payload looks like this:


|'''bytes'''|'''Value'''|
{| border="1" class="wikitable"
|<. 0 |<. A random one byte number. This number is ignored after decryption. |
! bytes
|<. 1-2 |<. A quint16 containing a CRC16-CCITT checksum value of bytes 3-N. |
! Value
|<. 3-N |<. (Compressed) plaintext. |
|-
| 0  
| A random one byte number. This number is ignored after decryption.
|-
| 1-2  
| A quint16 containing a CRC16-CCITT checksum value of bytes 3-N.
|-
| 3-N  
| (Compressed) plaintext.
|}


In case the Protection Hash flag has been set, the layout of the decrypted payload looks like this:
In case the Protection Hash flag has been set, the layout of the decrypted payload looks like this:


|'''bytes'''|'''Value'''|
{| border="1" class="wikitable"
|<. 0 |<. A random one byte number. This number is ignored after decryption. |
! bytes
|<. 1-20 |<. A 20 byte SHA-1 crypographic hash of bytes 21-N. |
! Value
|<. 21-N |<. (Compressed) plaintext. |
|-
| 0
| A random one byte number. This number is ignored after decryption.
|-
| 1-20  
| A 20 byte SHA-1 crypographic hash of bytes 21-N.  
|-
| 21-N  
| (Compressed) plaintext.  
|}


On decryption, the checksum or hash value must be checked by recalculating it for the (compressed) plaintext, and comparing that value with the data in the corresponding bytes of the decrypted payload. If they do not match, an error flag is set, and the decryption algorithm must return an empty byte array. If a wrong key is used, it is very unlikely that a checksum will still match. It is far more unlikely that the SHA-1 hash will still match. Note that the random leading number is not used for calculating the checksum or the SHA-1 hash.
On decryption, the checksum or hash value must be checked by recalculating it for the (compressed) plaintext, and comparing that value with the data in the corresponding bytes of the decrypted payload. If they do not match, an error flag is set, and the decryption algorithm must return an empty byte array. If a wrong key is used, it is very unlikely that a checksum will still match. It is far more unlikely that the SHA-1 hash will still match. Note that the random leading number is not used for calculating the checksum or the SHA-1 hash.
Line 150: Line 154:
If neither of the Protection flags were set, the layout is simply:
If neither of the Protection flags were set, the layout is simply:


|'''bytes'''|'''Value'''|
{| border="1" class="wikitable"
|<. 0 |<. A random one byte number. This number is ignored after decryption. |
! bytes
|<. 1-N |<. (Compressed) plaintext. |
! Value
|-
| 0  
| A random one byte number. This number is ignored after decryption.  
|-
| 1-N  
| (Compressed) plaintext.  
|}


==== Rationale for using a leading random number ====
==== Rationale for using a leading random number ====
The '''''SimpleCrypt''''' algorithm uses the result of the encryption of the previous character as part of the encryption of the current character. That results in a situation that even if you have the same character at byte 0 and byte 7 of the plaintext, they will be most likely be encrypted to a different cyphertext. That makes it much harder to figure out the key used in the encryption if the attacker has the opportunity to feed in his own plaintext and can then see the resulting cyphertext. For byte 0 of the bytestream however, this is not possible. A (known) 0 value is used instead of the value of the previous byte. That leads to a weakness, especially if no Protection flags and no compression are used. The number of characters that are likely to be at the beginning of a password is not so big, thus reducing the amount of possibilities. That leads to an effective reduction in the key strength of several bits. Note that that is a loss of about an order of magnitude in strength.


The <code>SimpleCrypt</code> algorithm uses the result of the encryption of the previous character as part of the encryption of the current character. That results in a situation that even if you have the same character at byte 0 and byte 7 of the plaintext, they will be most likely be encrypted to a different cyphertext. That makes it much harder to figure out the key used in the encryption if the attacker has the opportunity to feed in his own plaintext and can then see the resulting cyphertext. For byte 0 of the bytestream however, this is not possible. A (known) 0 value is used instead of the value of the previous byte. That leads to a weakness, especially if no Protection flags and no compression are used. The number of characters that are likely to be at the beginning of a password is not so big, thus reducing the amount of possibilities. That leads to an effective reduction in the key strength of several bits. Note that that is a loss of about an order of magnitude in strength.
Putting a random number in front of the string makes it effectively impossible to use such heuristics to reduce the key strength, because there is no predictor for the random number in front (though, the situation is less than ideal, because of the use of the pseudo-random number generator. Achieving true randomness is ''very'' hard and well outside of the scope in '''''SimpleCrypt'''''.)
 
Putting a random number in front of the string makes it effectively impossible to use such heuristics to reduce the key strength, because there is no predictor for the random number in front (though, the situation is less than ideal, because of the use of the pseudo-random number generator. Achieving true randomness is ''very'' hard and well outside of te scope <code>SimpleCrypt</code>.)


Note that also the use of either of the Protection modes and the use of compression add to the security of the cypher, as both decrease the predictability of the plaintext.
Note that also the use of either of the Protection modes and the use of compression add to the security of the cypher, as both decrease the predictability of the plaintext.


=== Compression ===
=== Compression ===
 
If the Compression flag is set, the data in the examples above is compressed using {{DocLink|QByteArray|qCompress|QByteArray::qCompress()}}. The used compression level is 9 (maximum). On decryption, the data must then be uncompressed using {{DocLink|QByteArray|qUncompress|QByteArray::qUncompress()}}.
If the Compression flag is set, the data in the examples above is compressed using <code>qCompress</code>. The used compression level is 9 (maximum). On decryption, the data must then be uncompressed using <code>qUncompress</code>.


=== Result ===
=== Result ===
The resulting binary plaintext is the data retreived from the decrypted payload or, if used, by the (de) compression step above.
The resulting binary plaintext is the data retreived from the decrypted payload or, if used, by the (de) compression step above.

Latest revision as of 12:30, 21 October 2015

This page explains in more detail the format used by the Simple_encryption code. The encryption and decryption code works from a QByteArray to a QByteArray. The options to use QString are just provided for convenience. The first two sections detail how the stings and byte arrays correspond for both the plaintext and the cyphertexts. Then, the format of the binary QByteArray cyphertext is detailed.

String plaintext encoding

If a QString is used as the plaintext to encrypt, this string is encoded to a QByteArray by using the UTF8 codec. If a QString is requested as the result if a decryption action, the binary plaintext resulting from the decryption is interpretted using the same codec to construct a QString.

String cypher text format

SimpleCrypt can work with both QString and QByteArray cypher texts. The QString format is build on top of the QByteArray format. The QString is constructed from the QByteArray by creating a base64 encoded version of the binary cypher text, and using the ASCII codec to create a QString from this.

For decryption, the reverse happens. The QString is translated into a binary format using the ASCII codec, and then the resulting QByteArray is decoded using the base64 codec into a binary cyper text.

Binary cypher text format

The binary cypher text (represented in a QByteArray), consists of a header and a payload section:

bytes description
0-1 Header
2-N Payload. This section is encrypted.

Header

The header of the cypher text contains two fields: a version number, and a field with flags.

bytes description
0 Version
1 Flags

Version

The version number is a char. The current version number is 2.

Flags

The flags is a single byte bitfield. Each bit represents a flag that describes how the payload is to be interpreted.

bit value description
0 0x01 Compression. If set, compression has been applied to the plaintext before encryption
1 0x02 Protection Checksum. If set, a CRC16-CCITT checksum, encoded in a quint16, has been used to protect data integrity.
2 0x04 Protection Hash. If set, an SHA-1 cryptograhpic hash (20 bytes long) has been used to protect data integrity.

Bits 1 and 2 should not be set at the same time. If bit 1 has been set, bit 2 is ignored.

Bits 3 to 7 are reserved for future use.

Payload

The payload data block's contents are encrypted with a eight byte (quint64) key. They key is split up in eight chars, which are used consecutively starting again from key char 0 after key char 7 has been used. The QByteArray is encrypted by replacing each byte in the array with the value of that byte itself, XOR-ed with the key char, XOR-ed with the result of this operation for the previous byte in the QByteArray or 0 for byte 0 of the array. This operation can be reversed by XOR-ing the resulting cypher text byte again with both bytes that were used for the encryption phase.

This results in the following encryption schema:

byte key byte XOR value 2
0 0 the value 0
1 1 the result of this operation for byte 0
2 2 the result of this operation for byte 1
7 7 the result of this operation for byte 6
8 0 the result of this operation for byte 7
9 1 the result of this operation for byte 8
N N mod 8 the result of this operation for byte N-1

The idea is that using the result of the previous byte as part of the key for this byte makes it impossible to use a simple attack that analyzes the cyphertext as 8 separate cyphertexts, each with its own 1-byte key. This is an attack possible on the basic vigenere-type cipher.

Decrypted payload

Once the payload data has been decrypted, it may need further processing before the plaintext is available and verified. What processing is needed, depends on the flags that are set in the header of the cyphertext.

In case the Protection Checksum flag has been set, the layout of the decrypted payload looks like this:

bytes Value
0 A random one byte number. This number is ignored after decryption.
1-2 A quint16 containing a CRC16-CCITT checksum value of bytes 3-N.
3-N (Compressed) plaintext.

In case the Protection Hash flag has been set, the layout of the decrypted payload looks like this:

bytes Value
0 A random one byte number. This number is ignored after decryption.
1-20 A 20 byte SHA-1 crypographic hash of bytes 21-N.
21-N (Compressed) plaintext.

On decryption, the checksum or hash value must be checked by recalculating it for the (compressed) plaintext, and comparing that value with the data in the corresponding bytes of the decrypted payload. If they do not match, an error flag is set, and the decryption algorithm must return an empty byte array. If a wrong key is used, it is very unlikely that a checksum will still match. It is far more unlikely that the SHA-1 hash will still match. Note that the random leading number is not used for calculating the checksum or the SHA-1 hash.

If neither of the Protection flags were set, the layout is simply:

bytes Value
0 A random one byte number. This number is ignored after decryption.
1-N (Compressed) plaintext.

Rationale for using a leading random number

The SimpleCrypt algorithm uses the result of the encryption of the previous character as part of the encryption of the current character. That results in a situation that even if you have the same character at byte 0 and byte 7 of the plaintext, they will be most likely be encrypted to a different cyphertext. That makes it much harder to figure out the key used in the encryption if the attacker has the opportunity to feed in his own plaintext and can then see the resulting cyphertext. For byte 0 of the bytestream however, this is not possible. A (known) 0 value is used instead of the value of the previous byte. That leads to a weakness, especially if no Protection flags and no compression are used. The number of characters that are likely to be at the beginning of a password is not so big, thus reducing the amount of possibilities. That leads to an effective reduction in the key strength of several bits. Note that that is a loss of about an order of magnitude in strength.

Putting a random number in front of the string makes it effectively impossible to use such heuristics to reduce the key strength, because there is no predictor for the random number in front (though, the situation is less than ideal, because of the use of the pseudo-random number generator. Achieving true randomness is very hard and well outside of the scope in SimpleCrypt.)

Note that also the use of either of the Protection modes and the use of compression add to the security of the cypher, as both decrease the predictability of the plaintext.

Compression

If the Compression flag is set, the data in the examples above is compressed using QByteArray::qCompress(). The used compression level is 9 (maximum). On decryption, the data must then be uncompressed using QByteArray::qUncompress().

Result

The resulting binary plaintext is the data retreived from the decrypted payload or, if used, by the (de) compression step above.