Instead, we provide techniques to reduce the total time required to determine a random target key.
Number of Random Test Keys |
Minimum Time Reduced To |
Average Minimum Time Reduced To |
Average Maximum Time Reduced To |
Maximum Time Reduced To |
---|---|---|---|---|
256 | 1/16 | 1/32 | 1/128 | 1/2,097,152 |
512 | 1/16 | 1/128 | 1/256 | 1/131,072 |
1,024 | 1/32 | 1/128 | 1/512 | 1/1,048,576 |
2,048 | 1/64 | 1/512 | 1/1,024 | 1/8,388,608 |
4,096 | 1/64 | 1/1,024 | 1/2,048 | 1/16,777,216 |
8,192 | 1/64 | 1/2,048 | 1/4,096 | 1/4,194,304 |
16,384 | 1/512 | 1/4,096 | 1/8,192 | 1/16,777,216 |
32,768 | 1/256 | 1/8,192 | 1/16,384 | 1/16,777,216 |
65,536 | 1/2,048 | 1/8,192 | 1/32,768 | 1/134,217,728 |
131,072 | 1/2,048 | 1/8,192 | 1/131,072 | 1/268,435,456 |
262,144 | 1/4,096 | 1/65,536 | 1/131,072 | 1/268,435,456 |
524,288 | 1/4,096 | 1/262,144 | 1/262,144 | 1/134,217,728 |
1,048,576 | 1/2,048 | 1/262,144 | 1/524,288 | 1/536,870,912 |
2,097,152 | 1/32,768 | 1/524,288 | 1/1,048,576 | 1/34,359,738,368 |
Readers should determine if they want to utilize the methods described in this paper to assist them in breaking specific forms of encrypted file data such as AES in far less time.
Traditional brute force attacks designed to break a target symmetric key are slow and inefficient. In this paper we introduce two new methods to reduce the time required to break a randomly selected symmetric encryption key. The first method relies on drawing 1-n random test symmetric keys. Extensive reductions in time can be realized through use of a farm of GPU-equipped computers where each computer can execute 10,000 or more simultaneous threads. with each thread processing a random test key. The second method relies on the gambler’s technique of playing the odds to win. This method determines the odds of a target symmetric key containing identical byte sequences that are equal to 0. This will further reduce the time required to break a target symmetric key.
The following assumptions have been made about readers of this paper:
Mathematicians and cryptographers have taken complex approaches to breaking encryption algorithms such as AES, while failing to refine the mechanisms behind a brute force attack to determine symmetric keys. In this paper we present methods to more efficiently break a randomly selected symmetric private key. The primary method relies on randomly drawn test keys to reduce the total time to determine a target symmetric key. The secondary method determines the odds of a target key containing identical byte sequences that equal 0.
We describe in this section theoretical and practical approaches to breaking symmetric keys.
Cryptographers have publicly stated that it is not possible to break symmetric keys within the lifetime of the sun, or the age of the universe. Calculations have been made that indicate that it would take more energy than the sun emits over billions of years to break 128-bit and higher keys. (1) (2) (3) (4) They make this claim because of the combined complexity of iterating symmetric keys while attempting to decrypt and test encrypted packets of data.
The National Institute of Standards and Technology (NIST) publicly called for nominees for the new Advanced Encryption Standard in the fall of 1997. Joan Daemen and Vincent Rijmen submitted Rijndael. Rijndael was presented using a theoretical attack on 6 rounds of encryption for a 128-bit block, where AES performs 10 to 14 rounds of encryption per block based on the key size (5).
During the AES selection process, theoretical attacks were refined to handle 7 rounds for 128-bit keys, 8 rounds for 192-bit keys, and 9 rounds for 256-bit keys (6). This provided a 3 round safety margin for 128-bit keys, 4 round for 192-bit keys, and 5 round for 256-bit keys.
Several theoretical attacks (7)(8)(9)(10) have been developed, and have since been refined. One attack could theoretically break the full 12 rounds of AES with 192-bit keys using four related keys and 2,176 operations. Another attack could theoretically break 14 rounds of AES with 256-bit keys using four related keys and 2,119 operations (8). Another could theoretically break 10 of the 14 rounds of encryption with 256-bit keys using two related keys and only 245 operations (7). A recent attack (11) improves on the meet-in-the-middle attacks on 10-round AES. Many other theoretical attacks have been presented (12), with several security issues exposed (16)(17)(18)(19)(20)(21)(22) (23)(24). These techniques will not be discussed in this paper.
It is possible to greatly reduce the numeric difference between a randomly selected target symmetric key and a test symmetric key by randomly drawing 1-n test keys. Each random test key will be less than, equal to, or greater than, the value of the target key. It is impossible to determine how many test keys will be higher, equal or lower in value than the target key, but increasing the number of drawn test keys ensures that some keys will be higher, and some will be lower. A highly remote chance exists that 1 or more may be equal.
By randomly generating test keys, there will be a reduction in the time required to break a target symmetric key. This process ensures that one random test key will break the target key first. Each randomly drawn test key is set to a value that incurs no iterative cost to reach the value.
Symmetric keys can be randomly generated at a byte level, where iteration of test keys is also at a byte level. The logic of iterating byte values within a key is simple. Creating, testing, and iterating test keys as byte arrays make it simple to work with a wide range of key sizes.
All random test keys can be positively or negatively iterated until a test key equals the target key. This will eventually occur, and at least one test key will prove closer in value to the target key than the other test keys. If parallel processing on powerful hardware is used, then simultaneous threading of testing and adjustment of test keys can greatly reduce processing time. With sufficient processors and cores, each thread can be dedicated to a specific key.
We will now discuss qualification of statements made in this section by drawing on our experiences with 128-bit random values.
The following chart was prepared using 256 randomly draw values that had a potential range of 0 to 65,535. The Kryptera RNG (13) was used to generate the values. If you randomly chose a random value between 0 and 65,535 as your target key, you will find other values on the chart that are above, below or equal to the random value you have chosen. Some of the values would be closer to the target value than the others. This is the strength of randomly drawing test keys to speed up breakage of target symmetric keys.
Proof of concept software was developed to test generation of 1-n 128-bit random keys to locate a previously generated random 128-bit private key. The OpenSSL RNG and the Kryptera RNG were both used for our tests. We have provided the OpenSSL version of our source code. We used two RNGs to highlight differences that can be encountered. Both RNGs passed the TestU01 (30) (31)(32)(33)(34) BigCrush batteries of statistical tests.
Both RNGs generate high quality random numbers, where the OpenSSL RNG is a widely used RNG. The differences shown in the following data can be attributed to the different approaches that the RNGs take to generate random data, coupled with the fact that the data is random where both RNGs derived source entropy from /dev/urandom. We created several reports of processes that drew a 128-bit random target key, and then ran 500 sets of tests where a specific number of random 128-bit test keys were generated and tested against the target key. A report summary is shown below that shows minimum, maximum and average upper bit reductions using test keys that proved to be below and above the target key for each RNG:
Common Information |
Bit Reductions: OpenSSL RNG |
Bit Reductions: Kryptera RNG |
||||||
Number Test Keys |
Cost Bits |
Relative Position |
Minimum |
Maximum |
Average |
Minimum |
Maximum |
Average |
---|---|---|---|---|---|---|---|---|
4 |
2 |
Below |
||||||
4 |
2 |
Above |
||||||
8 |
3 |
Below |
||||||
8 |
3 |
Above |
||||||
16 |
4 |
Below |
||||||
16 |
4 |
Above |
||||||
32 |
5 |
Below |
||||||
32 |
5 |
Above |
||||||
64 |
6 |
Below |
||||||
64 |
6 |
Above |
||||||
128 |
7 |
Below |
||||||
128 |
7 |
Above |
||||||
256 |
8 |
Below |
||||||
256 |
8 |
Above |
||||||
512 |
9 |
Below |
||||||
512 |
9 |
Above |
||||||
1024 |
10 |
Below |
||||||
1024 |
10 |
Above |
||||||
2048 |
11 |
Below |
||||||
2048 |
11 |
Above |
||||||
4096 |
12 |
Below |
||||||
4096 |
12 |
Above |
||||||
8192 |
13 |
Below |
||||||
8192 |
13 |
Above |
||||||
16,384 |
14 |
Below |
||||||
16,384 |
14 |
Above |
||||||
32,768 |
15 |
Below |
||||||
32,768 |
15 |
Above |
||||||
65,536 |
16 |
Below |
||||||
65,536 |
16 |
Above |
||||||
131,072 |
17 |
Below |
||||||
131,072 |
17 |
Above |
||||||
262,144 |
18 |
Below |
||||||
262,144 |
18 |
Above |
||||||
524,288 |
19 |
Below |
||||||
524,288 |
19 |
Above |
||||||
1,048,576 |
20 |
Below |
||||||
1,048,576 |
20 |
Above |
||||||
2,097,152 |
21 |
Below |
||||||
2,097,152 |
21 |
Above |
Bit reductions above and below the target key are possible with random key generations. It may prove best to reduce the generation bit costs by iterating all random keys in a single direction. While a single set of keys can be incremented and decremented to potentially speed up breakage of a target key, it would be more efficient to generate a higher number of random values and iterate all test keys in the same direction. The reason is the cost of repeatedly testing each test key before key bit iteration occurs. This cost is based on the effective generation bits. Preceding bits add up to the number of generated test key, which is the potential bit cost of iterating through testing of a generation of random test keys to determine a target key. There will be a generation value of tests before any test key is iterated before the next test. At least one key will match the target key first. A test key can be removed from use when a key overflow occurs.
Generation of 256 and higher random test keys provides repeatable bit reductions when compared to randomly generated target keys.
With the OpenSSL RNG, there is an average savings of 7 bits (maximum 21 bits, minimum 4 bits) below the random target key when generating 256 random test keys.
With the Kryptera RNG, there is an average savings of 7 bits (maximum 15 bits, minimum 5 bits) below the random target key when generating 256 random test keys
A reduction of 7 bits on average reduces the maximum iterations of a 128-bit test key to 121 bits, 192-bits to 185-bits, and a 256-bits to 249-bits. Each bit reduced halves the total iterations.
A 4 bit minimum reduction reduces total key iterations to 1/16 (0.0625) of the original value.
A 5 bit minimum reduction reduces total key iterations to 1/32 (0.03125) of the original value.
A 7 bit average reduction reduces total key iterations to 1/128 (0.0078125) of the original value.
A 15 bit average reduction reduces total key iterations to 1/32,768 (0.000030517578125) of the original value.
A 21 bit maximum reduction reduces total key iterations to 1/2,097,152 (0.00000047683715820313) of the original value.
The following information pertains to test results generated using the Kryptera RNG:
A 128-bit value has a maximum value of 340.28237×10+36: |
340,282,366,920,938,463,463,374,607,431,768,211,455 |
In the 256 generation test, the target key was 329.31165×10+36: |
329,311,651,913,952,120,753,967,060,623,430,778,880 |
The target key is a reduction of 10.97072×10+36 from the maximum key value: |
10,970,715,006,986,342,709,407,546,808,337,432,575 |
The closest low random test key drawn was 329.31041×10+36: |
329,310,414,672,750,992,636,405,130,861,672,923,136 |
This reduced positive iterations to determine the target key to 1.23724×10+33: |
1,237,241,201,134,453,586,173,516,761,268,224 |
The closest high random test key drawn was 329.31228×10+36: |
329,312,275,303,853,256,998,360,124,692,765,868,032 |
This reduced negative iterations to 623.38990×10+30: |
623,389,901,129,938,993,297,780,451,049,472 |
In the 2,097,152 generation, the target key was 338.56731×10+36: |
338,567,312,335,200,856,922,166,187,030,829,072,384 |
This is a reduction of 1.71505×10+36 from the maximum key value: |
1,715,054,585,737,606,541,208,420,400,939,139,071 |
The closest low test key drawn was 338.56731×10+36: |
338,567,311,954,393,266,089,267,333,414,686,556,160 |
This reduced positive iterations to determine the target key to 380.80759×10+27: |
380,807,594,605,170,618,709,477,163,008 |
The closest high random test key drawn was 338.56731×10+36: |
338,567,313,409,779,392,444,863,291,823,656,796,160 |
This reduced negative iterations to 1.07458×10+30: |
1,074,578,526,576,870,231,761,362,092,032 |
The number of random test keys drawn to reduce the time to determine a target key will depend heavily on hardware and software techniques used, the target RNG, and the random characteristics of a pool of symmetric keys. The cost of testing each of the random keys for every key iteration can be minimized through parallel processing using powerful hardware equipped with GPUs utilizing software techniques that take full advantage of the underlying hardware. Each GPU can simultaneously execute 10,000 or more threads, where each thread can utilize a random test key to attempt decryption, test results, iterate the key and repeat.
This technique of using random test keys results in a bell curve that has the potential to greatly accelerate breaking of symmetric keys. In the lead of each curve lies the maximum bit savings by generation. This is a potential reduction of 7 to 35 bits. At the rear of the curve lies the minimum bit savings by generation, which provide a reduction in total complexity for a single key, but may fail to counter the generation cost. The majority of reductions fall between the lead and rear of the curve: there is a high probability that reasonable bit reductions can be realized.
User reports must be generated using random data provided by the target RNG to break a specific target key. Our tests using 128-bit random values indicate that randomly generated symmetric test keys of any length will follow similar upper bit reduction patterns.
A 1-bit reduction of a symmetric key reduces the total iterations by half. The tables below show 0, 8, 16, 24, and 32 bit reductions for key sizes of 128, 192, and 256 bits.
Key |
Maximum Iterations |
---|---|
128 |
340,282,366,920,938,463,463,374,607,431,768,211,456 |
120 |
1,329,227,995,784,915,872,903,807,060,280,344,576 |
112 |
5,192,296,858,534,827,628,530,496,329,220,096 |
104 |
20,282,409,603,651,670,423,947,251,286,016 |
96 |
79,228,162,514,264,337,593,543,950,34 |
Key |
Maximum Iterations |
---|---|
192 |
6,277,101,735,386,680,763,835,789,423,207,666,416,102,355,444,464,034,512,896 |
184 |
24,519,928,653,854,221,733,733,552,434,404,946,937,899,825,954,937,634,816 |
176 |
95,780,971,304,118,053,647,396,689,196,894,323,976,171,195,136,475,136 |
168 |
374,144,419,156,711,147,060,143,317,175,368,453,031,918,731,001,856 |
160 |
1,461,501,637,330,902,918,203,684,832,716,283,019,655,932,542,976 |
Key |
Maximum Iterations |
---|---|
256 |
115,792,089,237,316,195,423,570,985,008,687,907,853,269,984,665,640,564,039,457,584,007,913,129,639,936 |
248 |
452,312,848,583,266,388,373,324,160,190,187,140,051,835,877,600,158,453,279,131,187,530,910,662,656 |
240 |
1,766,847,064,778,384,329,583,297,500,742,918,515,827,483,896,875,618,958,121,606,201,292,619,776 |
232 |
6,901,746,346,790,563,787,434,755,862,277,025,452,451,108,972,170,386,555,162,524,223,799,296 |
224 |
26,959,946,667,150,639,794,667,015,087,019,630,673,637,144,422,540,572,481,103,610,249,216 |
Breaking symmetric keys will require use of powerful hardware (26)(27). While it would be best to use a super computer, most people will have to make do with far less costly hardware (28)(29). The approach taken to break a private symmetric key depends on the number of processors, core and threads that can be utilized by the hardware and underlying operating system. An enterprise class server can be equipped with several high-speed CPU cores, each of which supports at least one simultaneous thread for key testing. Most servers use Intel CPU's, which provide AES-NI support at a hardware level across all physical cores. AES decryption is extremely fast, and fully utilizes physical CPU cores when using AES-NI. The least expensive way to greatly accelerate processing is to make use of stacked high-end video cards with GPUs. Several thousand threads can be executed at the same time on each GPU, where each thread is used to process a random test key. Decryption of a subset of the target encrypted file, with subsequent parsing for known signatures, can occur within the thread using localized AES code, or can be coordinated using shared memory to communicate with the caller CPU thread to utilize AES-NI at a chip level. A good model is bitcoin mining farms, where 1-n computers are simultaneously used to solve a common problem.
The purpose of this paper is to demonstrate how to reduce the time needed to break symmetric keys. We do not demonstrate how to break encrypted data or files. We believe it is feasible to break 128-bit encrypted files today using common hardware and custom software. It is up to the reader to handle the mechanics of it.
Reducing upper bit complexity is central to lowering overall complexity. Randomly drawing test key will locate target symmetric keys in less time than it takes using conventional brute force techniques.
The conventional brute force approach to break a symmetric key requires initialization of a test key to 0. For a 128-bit key, this would set sixteen 8-bit values to zero. This is roughly equivalent to throwing 16 dice, expecting each die to come up with an identical value. If each die had 256 faces, and were repeatedly thrown, the odds would be heavily in your favour that each die face will not appear on top as 0, or any other value or sequence that you determine before throwing the dice.
In this form of a brute force attack, a test key that is initially placed in an extremely unlikely state of sixteen 0's is used to attempt decryption of cyphertext. The result is tested to determine if expected signature patterns are found. When signature patterns are not found, the key will be iterated into another extremely unlikely state, or, eventually, into what can be considered to be a random state, before attempting to decrypt cyphertext and test the result for signatures. This process will continue until decryption has proven successful, and will work when sufficient time is provided. Unfortunately it could take until the end of time due to the processing time wasted testing keys that would have an extremely low chance of being randomly drawn.
With perfect random byte streams generated by perfect random number generators using perfect sources of entropy, divide these values into the total to determine the odds:
1 byte: 28 to 1, which is 256 to 1 against, with a probability of 1/256
2 bytes: 216 to 1, which is 65,536 to 1 against, with a probability of 1/65,535
3 bytes: 224 to 1, which is 16,777,216 to 1 against, with a probability of 1/16,777,216
4 bytes: 232 to 1, which is 4,294,967,296 to 1 against, with a probability of 1/4,294,967,296
We generated 68,719,476,736 bytes of random data. 68,719,476,736 divided by 16,777,216 equals 4,096. There should be 4,096 occurrences of every unique 3-byte sequence in the perfect random byte stream. The odds of a specific 3-byte sequence occurring is 68,719,476,736 to 4,096, or 16,777,216 to 1 as shown above. These odds are calculated when working at a byte level, where you can choose 3 sequential byte values to locate, then randomly select 3 bytes from the random data in sequence. The odds are 16,777,216 to 1 against a match being found.
When the data stream is broken up into individual symmetric keys, divide the the odds by the key size in bytes to determine the correct odds of finding 3 sequential pre-selected bytes within one key. If 128-bit keys are generated, then the odds are reduced to 1,048,576 to 1 against a 3-byte sequence being found. For 192-bit keys it is 699,050 to 1 against, and 256-bit keys being 524,288 to 1 against. The odds will improve when byte sequences split on key boundaries and reduce the total number of unique byte sequences.
We know that attaining perfection is rarely possible when generating random byte streams using conventional computers. To determine the odds in the real world, we developed software to generate two sets of 4,294,967,296 (232) 128-bit random symmetric keys using two random number generators: the Kryptera RNG, and the OpenSSL RNG. We have provided the source code for the version that uses the OpenSSL RNG. During creation of the keys the software counts each byte occurrence in a generated random data stream, counts each identical byte sequence in the stream, and counts each identical byte sequence in each key. We have provided our working spreadsheet that is derived from the OpenSSL RNG report and the Kryptera RNG report. When random pools are created by a perfect random generator, all multi-byte sequences will have an equal probability of occurring within the set of random keys, where any 2, 3, and 4-byte sequence could be found to have the same average probability of occurrence. This does not matter when discussing the odds of a specific sequential byte value occurring in generated keys. The odds are highly against coming across a random target key that contains 3 or more bytes in sequence that equal 0. We have used the averages for 3 and 4 identical byte counts to determine the odds shown in the following table.
RNG |
Number of Bytes |
Count Bytes Equal 0 |
Average of Counts |
Odds of the Average |
---|---|---|---|---|
OpenSSL |
3 |
3,302 |
3,333 |
1,288,619 to 1 (232/3,333) |
OpenSSL |
4 |
17 |
18 |
238,609,294 to 1 (232/18) |
OpenSSL |
>= 3 bytes equal to 0 |
3,219 |
3,351 |
1,281,697 to 1 (232/3,351) |
Kryptera |
3 |
3,261 |
3,342 |
1,285,148 to 1 (232/3,342) |
Kryptera |
4 |
10 |
11 |
390,451,572 to 1 (232/11) |
Kryptera |
>= 3 bytes equal to 0 |
3,271 |
3,353 |
1,280,932 to 1 (232/3,353) |
There were no counts of 5 sequential bytes that equalled 0 in either test. 1 key contained 5 sequential byte values for the OpenSSL RNG, and 13 keys contained 5 sequential byte values for the Kryptera RNG.
There is a focus on identical sequences of bytes that equal 0 because lower bits clear when a higher bit is set during key iteration, where keys are made up of bytes. For example, if a 128-bit key is set to a value of 65,535, then 14 key bytes except the lowest 2 bytes will be set to 0. If the key is iterated by 1, then the lower 2 bytes will be set to 0, the next byte will be set to 1, and the remaining 13 bytes will be set to 0.
If a test symmetric key can be iterated then tested to determine if it contains 3 or more sequential bytes that equal 0, then the test key can be adjusted to the next bit pattern that does not contain sequences of 0 bytes. In keeping each test key in a state that is free of repeat sequences of 0 bytes, it will be possible to further reduce the time required to break a target symmetric key. While we could extend this approach to include all identical byte sequences, the gains may not be worth the extreme drop in odds. Excluding identical sequences of byte values between 0 and 255 that are greater than 2 would drop the odds to 5,033 to 1 against for the OpenSLL RNG, and 5,020 to 1 against for the Kryptera RNG.
We acknowledge that the potential savings would be mathematically negligible because the order-of-magnitude computational complexity will not even be reduced by a single bit. We know that any reduction in key complexity can reduce the time needed to break a target symmetric key. Combining a large potential upper bit reduction through 1-n random test key generation with playing the odds by adjusting test key values that contain 2 or more 0 bytes in sequence, greatly reduces the time needed to break a target symmetric key. It is ludicrous to disallow time reduction because it is mathematically negligible.
An attacker must develop software to prerecord each start key pattern that does not contain repeat sequences of 0 bytes, and the total number of iterations where the key will remain in a similar state. The report generated by this process can be used at run time to accelerate key iteration and adjustment for repeat sequences of 0 bytes.
The effective strength of AES was theoretically compromised when it was first evaluated, and efforts to theoretically break AES have not abated since that time. While it has not been proven that AES has been compromised, we know it is possible to greatly reduce the complexity of breaking symmetric keys. We have proven that large reductions in complexity are possible, where each bit reduction halves total complexity. Like other paradigm-altering concepts, our solutions are simple, which may indicate they are already in use today.
Network communications rely on AES to encrypt and decrypt traffic through secure web sites, banking applications, online payments, and communications between computers (14)(15). The adoption of AES has greatly accelerated since Intel incorporated AES-NI into all of their CPU's. AES-128 bit keys may be broken using the methods outlined in this paper. AES-192 and AES-256 keys may prove secure for now, but will not remain secure in the future.
Software can be developed to distribute computing load across many processor cores and computers providing an opportunity to break symmetric keys using 1-n computers with 1-n cores. Processing time can be leased on cloud servers, super computers, and quantum computers. CPU's in future will be more powerful, and will support more cores and threads per core. Video graphics cards will be more powerful, and will support more CUDA threads. Memory and storage will increase in capacity, and decrease in relative cost.
While we believe that AES-192 and AES-256 remain secure to use, the size of symmetric keys must eventually be increased. While it may prove difficult to switch technology to more secure forms of symmetric encryption, secure encryption solutions are available that will help smooth the transition.
Contact Information
Richard Evers
Contact via Email
https://kryptera.ca
Alastair Sweeny
Contact via Email
https://kryptera.ca
[2] Calculate time taken to break AES key
[3] Time and energy required to brute-force a AES-256 encryption key
[11] Meet-in-the-middle attacks on 10-round AES-256, Li, R. & Jin, C. (18 July 2015)
[12] Cryptology ePrint Archive: Search Results (All)
[13] Kryptera Random Number Generator
[14] How does HTTPS actually work?
[15] The First Few Milliseconds of an HTTPS Connection
[17] Intel's remote AMT vulnerability, May. 1st, 2017
[18] Disabling Intel ME 11 via undocumented mode, August 28, 2017
[21] Rijndael Algorithm (Advanced Encryption Standard) AES
[22] TEMPEST attacks against AES
[23] Cache-timing attacks on AES, by Daniel J. Bernstein
[24] Secret contract tied NSA and security industry pioneer (Reuter)
[25] NSA’s Divorce from ECC Causing Crypto Hand-Wringing, by Michael Mimoso, October 23, 2015
[26] The Efficiency of Parallel Processing
© Richard Evers ISBN: 978-1-927736-35-7
Published 8-April-2018, Last Update 16-March-2019