Engee documentation
Notebook

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.

image.png

In [ ]:
# Загрузим модель, если она еще не открыта на холсте
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]) )
Количество ошибок передачи: 0

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.

image.png

Let's run this model and verify that transmitting a message with the same bytes AAAAAAAA generates different ciphertext bytes:

In [ ]:
# Загрузим модель, если она еще не открыта на холсте
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)... ), " " ) )
Отправленное сообщение: 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41
Зашифрованное сообщение: 3f 73 25 f1 71 65 64 39 d0 4d 3f 96 b2 eb e1 24 3f 73 25 f1 71 65 64 39 d0 4d 3f 96 b2 eb e1 24 3f 73 25 f1 71 65 64 39 d0 4d
Полученное сообщение: 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41
In [ ]:
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:

image.png

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.

In [ ]:
# Загрузим модель, если она еще не открыта на холсте
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:

In [ ]:
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)... ), " " ) )
Отправленное сообщение: d0 a1 d0 be d0 be d0 b1 d1 89 d0 b5 d0 bd d0 b8 d0 b5
Зашифрованное сообщение: 7c 9c e3 60 7e 1c f7 5a 6e 9c 6b f0 f8 cc ef b6 2c 5f
Полученное сообщение: d0 a1 d0 be d0 be d0 b1 d1 89 d0 b5 d0 bd d0 b8 d0 b5
In [ ]:
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 blocks d0 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.

image.png

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 specified seed,
  • 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.