Substitution-rearrangement network (SP-network) encryption model¶
We study the principles of organisation of a modern encryption system on a theoretical example.
Task description¶
Most modern information systems use the AES (or similar) cipher to encrypt messages. These ciphers are based on the substitution-replacement network algorithm (SP-network).
If you just want to encrypt or decrypt a message, Julia has libraries like
AES.jl
to encrypt and decrypt byte sequences.
The algorithm presented here is not necessarily a strong cipher. A "strong" cipher can be made strong by proper choice of S- and P-block parameters, as well as better functions for generating round keys and adding them to the ciphertext, and there are other additional mechanisms described in the standards. We only create a training example, which in some respects is convenient to analyse and refine.
General view of the model¶
An SP-net based cipher receives a data block and a key as input and performs several rounds consisting of alternating stages of substitution and permutation of bits in the block.
# Загрузим модель, если она еще не открыта на холсте
if "sp_net_cipher_model" ∉ getfield.(engee.get_all_models(), :name)
engee.load( "$(@__DIR__)/sp_net_cipher_model.engee");
end
model_data = engee.run( "sp_net_cipher_model" );
println( "Количество ошибок передачи: ", convert(Int, model_data["Кол-во ошибок"].value[end]) )
Next, we'll uncover each block of the model and see how one individual round works.
Model with three rounds of encryption¶
If you expand each of the subsystems, you can get a very visual representation of how each round of the algorithm works (model sp_net_cipher_model_3R_expand
).
It allows you to see the settings of each stage and check what will happen if the settings of substitution or rearrangement blocks are not coordinated.
Let's run this model and verify that transmitting a message with the same bytes AAAAAAAA
generates different ciphertext bytes:
# Загрузим модель, если она еще не открыта на холсте
if "sp_net_cipher_model_3R_expand" ∉ getfield.(engee.get_all_models(), :name)
engee.load( "$(@__DIR__)/sp_net_cipher_model_3R_expand.engee");
end
model_data = engee.run( "sp_net_cipher_model_3R_expand" );
source_message = collect(model_data["Блок сообщения"]).value
cipher_text = UInt16.(collect(model_data["Зашифрованный блок"]).value)
received_message = UInt16.(collect(model_data["Расшифрованный блок"]).value)
source_bytes = vcat( reverse.([reinterpret( UInt8, [UInt16(c)]) for c in source_message])... )
cipher_bytes = vcat( reverse.([reinterpret( UInt8, [UInt16(c)]) for c in cipher_text])... )
received_bytes = vcat( reverse.([reinterpret( UInt8, [UInt16(c)]) for c in received_message])... )
println( "Отправленное сообщение: ", join( vcat( string.(source_bytes, base=16)... ), " " ) )
println( "Зашифрованное сообщение: ", join( vcat( string.(cipher_bytes, base=16)... ), " " ))
println( "Полученное сообщение: ", join( vcat( string.(received_bytes, base=16)... ), " " ) )
println( "Отправленная строка: ", String( source_bytes ))
println( "Полученная строка: ", String( received_bytes ))
Note that the ciphertext starts repeating from block 17 onwards.
One-round cipher model¶
If we simplify the network to one round, the model is as follows:
We see all the familiar elements of an SP network:
- the function of mixing key bits with plaintext bits (
XOR
), - substitution blocks that take bit (
Bool
) vectors as input, - bit permutation blocks, which takes as input a vector of four combined results of the previous operation.
If all the parameters of S- and P-blocks are set correctly, we get the same message at the output as the input.
# Загрузим модель, если она еще не открыта на холсте
if "sp_net_cipher_model_1R" ∉ getfield.(engee.get_all_models(), :name)
engee.load( "$(@__DIR__)/sp_net_cipher_model_1R.engee");
end
model_data = engee.run( "sp_net_cipher_model_1R" );
Let's study the sent and received message:
source_message = collect(model_data["Блок сообщения"]).value
cipher_text = UInt16.(collect(model_data["Зашифрованный блок"]).value)
received_message = UInt16.(collect(model_data["Расшифрованный блок"]).value)
source_bytes = vcat( reverse.([reinterpret( UInt8, [UInt16(c)]) for c in source_message])... )
cipher_bytes = vcat( reverse.([reinterpret( UInt8, [UInt16(c)]) for c in cipher_text])... )
received_bytes = vcat( reverse.([reinterpret( UInt8, [UInt16(c)]) for c in received_message])... )
println( "Отправленное сообщение: ", join( vcat( string.(source_bytes, base=16)... ), " " ) )
println( "Зашифрованное сообщение: ", join( vcat( string.(cipher_bytes, base=16)... ), " " ))
println( "Полученное сообщение: ", join( vcat( string.(received_bytes, base=16)... ), " " ) )
println( "Отправленная строка: ", String( source_bytes ))
println( "Полученная строка: ", String( received_bytes ))
Creating Round Keys¶
The algorithm for creating round keys is organised as follows:
- a sequence of bytes forming a key is split into blocks of 64 bits each
- blocks are overlapped (key
d0 9a d0 bb d1 8e d1 87
generates blocksd0 9a d0 bb
,9a d0 bb d1
,d0 bb d1 8e
etc.) - the key is used cyclically
A 64-bit key block is divided into 4 round keys of 16 bits each (sequentially), of which we use only the first three round keys.
Considerations for working with the model¶
The following features are worth considering when interpreting or planning changes to the model:
- the message and key are passed to the model cyclically and never terminate,
- a complete cycle of one block is executed in one modelling cycle without any delay
- so at the top level we do not work with vectors of bits, but with numbers
UInt16
,UInt32
etc., - input message and round keys are fed into the model in blocks of 16 bits, all four S-blocks on each round take blocks of 4 bits as input, and P-blocks with vectors of 16 binary bits,
- the permutation parameters within the S- and P-blocks are not taken from the codebook, but are generated by the operation
shuffle
with the specifiedseed
, - the message is transmitted to the algorithm in non-overlapping blocks, and the key blocks are generated by a window with one byte offset.
Conclusion¶
We have created a model that allows us to study all the steps of encrypting a message using SP-network and make any changes to the architecture of this algorithm without diving into technical programming issues.