SimpleCrypt algorithm details: Difference between revisions

From Qt Wiki
Jump to navigation Jump to search
No edit summary
 
(convert {{doclinkref}} to the improved {{doclink}})
 
(6 intermediate revisions by 3 users not shown)
Line 1: Line 1:
=SimpleCrypt algorithm and format=
[[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.


This page explains in more detail the format used by the [[Simple encryption|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 ==
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 plaintext encoding==
== 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.


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>
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.


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


<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 <span class="caps">ASCII</span> codec to create a <code>QString</code> from this.
{| border="1" class="wikitable"
 
! bytes
For decryption, the reverse happens. The QString is translated into a binary format using the <span class="caps">ASCII</span> codec, and then the resulting <code>QByteArray</code> is decoded using the base64 codec into a binary cyper text.
! description
 
==Binary cypher text format==
 
The binary cypher text (represented in a <code>QByteArray</code>), consists of a header and a payload section:
 
{| class="infotable line"
| '''bytes'''
| '''description'''
|-
|-
| style="text-align: left" | 0-1
| 0-1
| style="text-align: left" | Header
| Header
|-
|-
| style="text-align: left" | 2-N
| 2-N
| style="text-align: left" | 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.


{| class="infotable line"
{| border="1" class="wikitable"
| '''bytes'''
! bytes  
| '''description'''
! description
|-
|-
| style="text-align: left" | 0
| 0
| style="text-align: left" | Version
| Version
|-
|-
| style="text-align: left" | 1
| 1
| style="text-align: left" | 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.


{| class="infotable line"
{| border="1" class="wikitable"
| '''bit'''
! bit
| '''value'''
! value
| '''description'''
! description
|-
|-
| style="text-align: left" | 0
| 0
| 0×01
| 0x01
| style="text-align: left" | Compression. If set, compression has been applied to the plaintext before encryption
| Compression. If set, compression has been applied to the plaintext before encryption
|-
|-
| style="text-align: left" | 1
| 1
| 0×02
| 0x02
| style="text-align: left" | Protection Checksum. If set, a CRC16-<span class="caps">CCITT</span> 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.
|-
|-
| style="text-align: left" | 2
| 2
| 0×04
| 0x04
| style="text-align: left" | Protection Hash. If set, an <span class="caps">SHA</span>-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 73: Line 66:
Bits 3 to 7 are reserved for future use.
Bits 3 to 7 are reserved for future use.


===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, <span class="caps">XOR</span>-ed with the key char, <span class="caps">XOR</span>-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 <span class="caps">XOR</span>-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:


{| class="infotable line"
{| border="1" class="wikitable"
| '''byte'''
! byte
| '''key byte'''
! key byte
| '''<span class="caps">XOR</span> value 2'''
! XOR value 2
|-
|-
| style="text-align: left" | 0
| 0
| 0
| style="text-align: left" | the value 0
| 0
| the value 0
|-
|-
| style="text-align: left" | 1
| 1
| 1
| style="text-align: left" | the result of this operation for byte 0
| 1
| the result of this operation for byte 0
|-
|-
| style="text-align: left" | 2
| 2
| 2
| style="text-align: left" | the result of this operation for byte 1
| 2
| the result of this operation for byte 1
|-
|-
| style="text-align: left" | …
| …
| …
| …
| …
| …
|-
|-
| style="text-align: left" | 7
| 7
| 7
| style="text-align: left" | the result of this operation for byte 6
| 7
| the result of this operation for byte 6
|-
|-
| style="text-align: left" | 8
| 8
| 0
| 0
| style="text-align: left" | the result of this operation for byte 7
| the result of this operation for byte 7
|-
|-
| style="text-align: left" | 9
| 9
| 1
| 1
| style="text-align: left" | the result of this operation for byte 8
| the result of this operation for byte 8
|-
|-
| …
| …
Line 116: Line 108:
| …
| …
|-
|-
| style="text-align: left" | N
| N
| N mod 8
| N mod 8
| style="text-align: left" | the result of this operation for byte N-1
| 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.
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===
=== 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:


{| class="infotable line"
{| border="1" class="wikitable"
| '''bytes'''
! bytes
| '''Value'''
! Value
|-
|-
| style="text-align: left" | 0
| 0  
| style="text-align: left" | A random one byte number. This number is ignored after decryption.
| A random one byte number. This number is ignored after decryption.
|-
|-
| style="text-align: left" | 1-2
| 1-2  
| style="text-align: left" | A quint16 containing a CRC16-<span class="caps">CCITT</span> checksum value of bytes 3-N.
| A quint16 containing a CRC16-CCITT checksum value of bytes 3-N.
|-
|-
| style="text-align: left" | 3-N
| 3-N  
| style="text-align: left" | (Compressed) plaintext.
| (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:


{| class="infotable line"
{| border="1" class="wikitable"
| '''bytes'''
! bytes
| '''Value'''
! Value
|-
|-
| style="text-align: left" | 0
| 0
| style="text-align: left" | A random one byte number. This number is ignored after decryption.
| A random one byte number. This number is ignored after decryption.
|-
|-
| style="text-align: left" | 1-20
| 1-20  
| style="text-align: left" | A 20 byte <span class="caps">SHA</span>-1 crypographic hash of bytes 21-N.
| A 20 byte SHA-1 crypographic hash of bytes 21-N.  
|-
|-
| style="text-align: left" | 21-N
| 21-N  
| style="text-align: left" | (Compressed) plaintext.
| (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 <span class="caps">SHA</span>-1 hash will still match. Note that the random leading number is not used for calculating the checksum or the <span class="caps">SHA</span>-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.


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


{| class="infotable line"
{| border="1" class="wikitable"
| '''bytes'''
! bytes
| '''Value'''
! Value
|-
|-
| style="text-align: left" | 0
| 0  
| style="text-align: left" | A random one byte number. This number is ignored after decryption.
| A random one byte number. This number is ignored after decryption.  
|-
|-
| style="text-align: left" | 1-N
| 1-N  
| style="text-align: left" | (Compressed) plaintext.
| (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.
===Categories:===
* [[:Category:snippets|snippets]]

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.