Nerdy creations with numbers, words, sounds, and pixels

Experimental, Science

Pendulo

Introduction

True randomness, as unachievable as it seems, would revolutionize the world of cryptography because attackers could no longer make predictions. Forget the pseudo-random number generators that rely on timing and user hardware, as they are not cryptographically secure. Better are systems based on physical processes, for example, radioactive decay or filming 100 lava lamps at the same time.

If you ever saw the chaotic behavior of a double pendulum, you might agree that its motions appear almost random to the observer. It gets even more chaotic when you attach additional pendula, forming a chain of masses connected by rods, called an n-pendulum (or multi-pendulum). By tracking the current state of the pendula, for example, position or angular velocity, you can obtain numbers that are extremely difficult to predict by an attacker who does not know the pendulum configuration, the initial state, or how much time has passed since then. Furthermore, the n-pendulum is non-deterministic, which means that you cannot compute any future states without computing all states in between.

But there is no need to build a physical test rig and wire it with lots of sensors because the n-pendulum can be simulated by numerically solving a system of differential equations. In fact, I did something similar for my Brushing MBS (multi-body system), which was more complex than the two-dimensional n-pendulum for several reasons:

  • It was three-dimensional, requiring two angular coordinates per pendulum instead of just one.
  • Elasticity was considered, which would be undesirable for a chaotic cipher system as it would force the system back into its initial state.
  • The mass of each segment (or rod) was in its center of gravity, whereas the n-pendulum uses massless rods and zero-dimensional masses.
  • Concerning the numerical solver, physical accuracy was more relevant than performance.

To calculate the equations of motion for both systems, you can use Lagrange’s equation of the second kind, which is shown below. It involves the Lagrangian L, which is the difference between the kinetic energy T (speed-related) and the potential energy V (gravity-related). The minimal coordinates, in this case, the angles of the pendulum rods, are represented by φ and the pendulum index k, while the dot denotes the angular velocity. On a side note, Edis is the energy dissipation, which may be included for stability.

Back in 2018, I proved that I could derive such a system of differential equations on a dozen pages of paper. I even got the result tattooed on my right arm to always remind myself that I could do it. Nonetheless, I feel that I am past my prime when it comes to math. Hence, I relied on a neat and efficient MATLAB implementation by my compatriot Tillmann Stübler, because why reinvent the wheel every time? I merely translated it into Python and changed the solver from Runge-Kutta to Euler. The latter is actually a step back in terms of accuracy and stability, but I was hoping to increase performance due to the differential function being accessed just once instead of four to five times.

So much for the physical part, but by now, you may want to know how this could be useful for encryption. Before I dive into the equally complex, albeit more arbitrary cryptographic aspects of Pendulo, let me show you an animated GIF of the simulation to keep you hooked.

n-pendulum with n = 8 pendula, each with a different mass and rod length

Hash Function

Not only do I use the numerically solved n-pendulum for the encryption of text, but also a step earlier to build a hash function. In a nutshell, a hash function generates a number of specified length (hash value) from a number of arbitrary length (password or key). There are more requirements, for example, reproducibility and key sensitivity, meaning that a small change in the key should change all digits of the hash value. Additionally, it should be downright impossible to find any key for a given hash value (one-way function), and at least practically impossible to find any two hash values that are alike although their keys are different.

For the n-pendulum hash function, I let the user choose any key composed of Unicode characters. The individual characters are then converted into their integer representation using ord() (e.g., “A” becomes “65” and “a” becomes “97”) and joined to a string, similar to my cipher Lexigraph:

key: SecretKey<123>
integer string: 831019911410111675101121604950516244

The first digits of the integer string, up to a total of 20, are appended as decimals to the (constant) simulation timestep, which should make guessing previous and subsequent simulation states in regard to the current state more difficult:

0.001 s → 0.00183101991141011167510 s

But more importantly, the integer string is used to set the initial conditions of the n-pendulum, particularly the masses, rod lengths, initial angles, and initial angular velocities of all pendula. To work for both short and long keys, i.e., yielding a sufficient number of decimals for all 4 · n conditions, the number of pendula n is chosen based on the key length L:

n = 1 + ceil(L / 16)

Coincidentally, this makes long keys more secure because a large number of pendula leads to more unpredictable behavior. For the 4 · n initial conditions, the integer string is split into single digits, which are distributed around to form smaller numbers. To keep the simulation stable, each initial condition is restricted to a number between 0 and 10. This can be achieved by simply inserting a decimal point after the first digit (or inserting a leading 1 to prevent all-zero values).

947 → 9.47
14580 → 1.458
0552 → 0.552

The numbers are then linearly remapped to intervals that I determined to work best for masses, rod lengths, initial angles, and initial angular velocities.

mass: [0,10] → [5,15]
rod_length: [0,10] → [1,3]
init_angle: [0,10] → [0,2π]
init_angular_velocity: [0,10] → [-4π,4π]

There actually is a fifth initial condition, the damping factor, which I set as 10 * (mass + rod_length) for simplicity. Even though my fellow engineers may lose their minds over that, disregarding units and all, it ensures that heavy masses on long rods are dampened more than small masses on short rods. This increases stability while keeping energy dissipation at a reasonable level.

Now that I have offended my fellow engineers, let me also insult my fellow cryptographers (to be honest, I consider myself too much of an amateur to be counted among them). Specifically, let me explain how the hash value is generated from the motions of the n-pendulum. After discarding the first 16 or so timesteps for security, for each subsequent timestep, the state of the last (or n-th) pendulum is considered as follows:

hash_word = angle * angular_velocity * x * y

This floating-point variable, which I call “hash word” here, would already be difficult to trace back to a particular state of the n-pendulum. To gloss over any state information that it might still provide, the hash word is turned into an integer by removing the decimal point. Then, the first three digits are removed because they do not change much between neighboring timesteps. Moreover, the last three digits are removed due to possible precision errors, which—interestingly—lead to even numbers being more prevalent than odd numbers.

hash_word = 27153.739678642546800
27153739678642546800
→ 53739678642546

The “pruned” hash words are then joined until a hash value length of 512 digits is reached. Theoretically, advanced statistical tests would need to be performed to prove that the hash values appear random. I only looked at the digit frequency and found it to be close to 10% per digit with a standard deviation of about 0.05%, depending on the key and the number of generated hash values.

Alright, we are now able to generate long, seemingly random numbers. They should be reproducible on any system that runs the same code with the same key (more on that later). Only how to use these hash values for encryption, particularly with an n-pendulum?

Encryption

In case you feared more math, I can reassure you: the cipher system involves only more of the same, everything we covered up to this point. However, for performance reasons, I made three adjustments:

  • Unlike the hash function, the cipher system always uses n = 8 pendula, which is a compromise between security and stability.
  • To form hash words, not just the last (or eighth) pendulum but all pendula are considered, effectively reducing encryption time eightfold.
  • The initial angular velocities are remapped to [-16π,16π] to give the system a boost at the start, while the damping factors are reduced to mass + rod_length, allowing the system to run indefinitely (although this remains unproven).

Indeed, the non-deterministic nature of the cipher system makes it impossible to anticipate whether the simulation will become unstable eventually, resulting in a lot of NaN (“not a number”) values, which cannot be used for encryption. Therefore, I implemented a feature that decreases the simulation timestep by 20% and increases the damping factor by 20% if instability is encountered. As this is triggered by a warning the solver raises early during solving, iterative adjustment of the simulation conditions does not take much time—and rarely does it occur more than once per simulation.

Just like the hash function, the cipher system produces a continuous stream of digits, which is then split into integers of eight digits in length. Admittedly, I chose this number arbitrarily, so I am open to explanations why longer numbers might increase security, or shorter numbers might increase performance while maintaining equal security.

In case you are unfamiliar with it, this next bit requires you to understand the modulo function, fundamental in all of cryptography and much beloved by me. In fact, it gives Pendulo half of its name. For simplicity, let us put the modulo function on a level with the remainder of an integer division (although, technically, there is a difference between remainder and modulo regarding the sign when negative numbers are involved). In many programming languages, the modulo function is accessed using a percent-symbol:

5 / 3 = 1 remainder 2 → 5 % 3 = 2
22 / 7 = 3 remainder 1 → 22 % 7 = 1
2520 / 11 = 229 remainder 1 → 2520 % 11 = 1

To encrypt the most common characters in English and German, I made the following cipher alphabet (also including space character, new line, and tabulator):

ABCDEFGHIJKLMNOPQRSTUVWXYZÄÖÜÆŒabcdefghijklmnopqrstuvwxyzäöüßæœà0123456789^°!"§$%&/()=?\´`{[]}+*~#'’‘“”-–—_.:,;<>|@€µ™

The plaintext letters are then encrypted one by one. Basically, N being one generated eight-digit number, you go N times to the right, starting from the beginning once the end is reached. Of course, cycling through the alphabet hundreds of millions of times using a for-loop would be extremely inefficient, even though that is how I programmed as a 14-year-old. This is where the modulo comes in handy:

cipher_character = (plain_character + N) % alphabet_length

Because the hash function is key sensitive, the cipher system is even more key sensitive, which I want to showcase with the following examples. Ciphertext 1 is encoded with the key X Æ A-Xii, which differs from the key X Æ A-Xij used for ciphertext 2 only in the last symbol, making their integer strings almost identical. Please note that due to formatting reasons, I had to replace line breaks and tabs in the ciphertexts with spaces:

Plaintext: Confusion now hath made his masterpiece! Most sacrilegious murder hath broke ope the Lord’s anointed temple, and stole thence the life o’ th’ building.

Ciphertext 1: SP$OT'füfk™?,f0Füi4MR9(wjs?üX*™eÄMe}LPÆfà™v"G(§M$Urn#,d~`v3Wz7üL:kxw}a’Xö&°@3bö  _[ELEÄsX} n{3]\DUN<ßw"œ&mkæÄsœ0k–/SM\$~%)K“œ—=ß9°r™H<A&T(IBZj<%~}œt[”u

Ciphertext 2: hV_üÄl!hÖ :={ ~pŒ!6w.6œ, OO,"KN3M/C&Ü^ü[SWAööC~rR^"“w } )> ‘}àÖg—=U”0hœä’Dpœ/g9"O™_µLe0->GR7ö?eZK</m8@§–|Kk_nWdµÄ$;' 4oru"µuKRPI ?zdz"6!} TA;öyC{P7?´Kc

Decryption

The good news: no more math, hooray! For bad news, wait until the Discussion section. Luckily, decryption with Pendulo works just the same as encryption. Both simulations for hash function and cipher system are executed with the same parameters, yielding the same streams of decimal digits. The only difference is the minus in the following formula, which means cycling through the alphabet from right to left. This should again signify the beauty of the modulo function:

plain_character = (cipher_character – N) % alphabet_length

At this point, I refrain from giving another example because it would be of little educational value. Instead, let us move on to the Discussion section.

Discussion

Pendulo is an encryption method that is based on the chaotic behavior of an n-pendulum, which can be computed numerically. It features its own hash function and is capable of encrypting most common text characters at a ciphertext-to-plaintext length ratio of 1. Whereas modern encryption methods are applied on a bit level (0’s and 1’s) because that is how computers save and process files, I opted for a more descriptive approach using decimal numbers (0 to 9). Of course, the hash function and cipher system could easily be modified to work with binary numbers.

The method’s security relies on several factors:

  • The hash function is key sensitive and hash values are 512 decimal digits long, which is 16 digits of precision per initial condition.
  • In the case of a known-plaintext attack, the digit stream is difficult to reconstruct due to the modulo operation being a one-way function. I know of no method to guess eight-digit numbers from just the two-digit result of a modulo operation.
  • If the digit stream is known, it is likely impossible to reconstruct the current pendulum configuration because the hash words are the (trimmed) products of pendulum angles, angular velocities, and Cartesian coordinates x and y, all of which depend on the precise knowledge of the initial conditions.
  • Even if the current pendulum configuration is known, the unknown timestep makes it extremely difficult to guess other configurations.

At roughly 0.3 MB/s on my high-end computer, encryption and decryption with Pendulo are not fast. This might be attributed to single-core usage of the CPU, Python not being a compiled language, as well as certain design choices—for example, using an n-pendulum simulation for encryption. 😉 However, the n-pendulum was the sole premise of this project, and I am fine with the likelihood that it will remain an esoteric encryption method, at best appreciated by one or two physics nerds.

Much more problematic is that the implementation might not be robust enough. Because floating-point precision depends on the system on which the code is executed, it would be fatal if my Windows computer encrypted differently than your Mac or Linux device. Frankly, I need to do more testing on that. But since I am not intending to actually use it for encryption, let alone exchange secret messages with others, I believe this Python implementation suffices as a demonstration.

← Return to “Cryptology”

← Return to “Science”

Leave a Reply