Last active
October 22, 2016 16:42
-
-
Save romshark/a788280288de303e43fdece86e241361 to your computer and use it in GitHub Desktop.
AES CTR Testsuite for encryption and decryption performance benchmarking
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"crypto/aes" | |
"crypto/cipher" | |
cryptorand "crypto/rand" | |
"flag" | |
"fmt" | |
"io" | |
"math/rand" | |
"time" | |
) | |
// GenerateData generates a random data buffer | |
func GenerateData(size uint64) (result []byte, duration time.Duration) { | |
begin := time.Now() | |
const symbols = "abcdefghijklmnopqrstuvwxyz1234567890" | |
result = make([]byte, size) | |
max := len(symbols) | |
for itr := uint64(0); itr < size; itr++ { | |
result[itr] = symbols[rand.Intn(max)] | |
} | |
duration = time.Since(begin) | |
return result, duration | |
} | |
// GenerateKey generates a secure random AES-128 key | |
func GenerateKey(blockSize uint) (key []byte, duration time.Duration) { | |
begin := time.Now() | |
key = make([]byte, blockSize/8) | |
_, err := cryptorand.Read(key) | |
if err != nil { | |
panic(fmt.Errorf("could not generate key: %s", err)) | |
} | |
duration = time.Since(begin) | |
return key, duration | |
} | |
// GenerateIv generate the nonce for a CTR encrypted entity | |
func GenerateIv() (iv []byte, duration time.Duration) { | |
begin := time.Now() | |
iv = make([]byte, aes.BlockSize) | |
if _, err := io.ReadFull(cryptorand.Reader, iv); err != nil { | |
panic(fmt.Errorf("could not generate IV: %s", err)) | |
} | |
duration = time.Since(begin) | |
return iv, duration | |
} | |
// Encrypt encrypts the given data using the given key and IV | |
func Encrypt(data []byte, iv []byte, key []byte) (ciphertext []byte, duration time.Duration) { | |
ciphertext = make([]byte, len(data)) | |
begin := time.Now() | |
block, err := aes.NewCipher(key) | |
if err != nil { | |
panic(fmt.Errorf("could not create cipher block: %s", err)) | |
} | |
encrypted := cipher.NewCTR(block, iv) | |
encrypted.XORKeyStream(ciphertext, data) | |
duration = time.Since(begin) | |
return ciphertext, duration | |
} | |
// Decrypt decrypts the given ciphertext using the given key and IV | |
func Decrypt(ciphertext []byte, iv []byte, key []byte) (plaintext []byte, duration time.Duration) { | |
plaintext = make([]byte, len(ciphertext)) | |
begin := time.Now() | |
block, err := aes.NewCipher(key) | |
if err != nil { | |
panic(fmt.Errorf("could not create cipher block: %s", err)) | |
} | |
stream := cipher.NewCTR(block, iv) | |
stream.XORKeyStream(plaintext, ciphertext) | |
duration = time.Since(begin) | |
return plaintext, duration | |
} | |
//ToSeconds converts nanoseconds to seconds | |
func ToSeconds(ns uint64) (s float64) { | |
return float64(ns) / 1000000000.0 | |
} | |
//ToMilliseconds converts nanoseconds to milliseconds | |
func ToMilliseconds(ns uint64) (ms float64) { | |
return float64(ns) / 1000000.0 | |
} | |
//ToMicroseconds converts nanoseconds to microseconds | |
func ToMicroseconds(ns uint64) (us float64) { | |
return float64(ns) / 1000.0 | |
} | |
func main() { | |
//read arguments | |
var blockSize uint | |
var randomDataSize uint64 | |
var iterations uint | |
flag.UintVar( | |
&blockSize, | |
"b", | |
128, | |
"Defines the AES block size (128 / 192 / 256), default is 128", | |
) | |
if blockSize != 128 && blockSize != 192 && blockSize != 256 { | |
panic(fmt.Errorf("Invalid block size: %d", blockSize)) | |
} | |
flag.Uint64Var( | |
&randomDataSize, | |
"d", | |
1048576, | |
"Defines the size of the generated random data in bytes, default is 1 MiB", | |
) | |
flag.UintVar( | |
&iterations, | |
"i", | |
1, | |
"Defines the number of times to run the encryption and decryption procedure, default is 1", | |
) | |
flag.Parse() | |
//prepare benchmark | |
var dataSizeMib float64 | |
var dataSizeByte uint64 | |
var dataGenTime uint64 | |
var totalKeyGenTime uint64 | |
var totalIvGenTime uint64 | |
var totalEncTime uint64 | |
var totalDecTime uint64 | |
var avgKeyGenTime uint64 | |
var avgIvGenTime uint64 | |
var avgEncTime uint64 | |
var avgDecTime uint64 | |
data, dataGenDuration := GenerateData(randomDataSize) | |
dataGenTime = uint64(dataGenDuration.Nanoseconds()) | |
dataSizeMib = float64(len(data)) / 1048576.0 | |
dataSizeByte = uint64(len(data)) | |
//run benchmark | |
for itr := uint(0); itr < iterations; itr++ { | |
key, keyGenDuration := GenerateKey(blockSize) | |
totalKeyGenTime += uint64(keyGenDuration.Nanoseconds()) | |
iv, ivGenDuration := GenerateIv() | |
totalIvGenTime += uint64(ivGenDuration.Nanoseconds()) | |
ciphertext, encDuration := Encrypt(data, iv, key) | |
totalEncTime += uint64(encDuration.Nanoseconds()) | |
plaintext, decDuration := Decrypt(ciphertext, iv, key) | |
totalDecTime += uint64(decDuration.Nanoseconds()) | |
if len(plaintext) != len(data) { | |
panic(fmt.Errorf( | |
"data corruption after decryption! (%d / %d)", | |
len(plaintext), | |
len(data), | |
)) | |
} | |
fmt.Printf(".") | |
} | |
avgKeyGenTime = totalKeyGenTime / uint64(iterations) | |
avgIvGenTime = totalIvGenTime / uint64(iterations) | |
avgEncTime = totalEncTime / uint64(iterations) | |
avgDecTime = totalDecTime / uint64(iterations) | |
//print benchmark results | |
fmt.Println() | |
fmt.Printf( | |
"Total iterations: %d\n", | |
iterations, | |
) | |
fmt.Printf( | |
"Block Size: %d bit\n", | |
blockSize, | |
) | |
fmt.Printf( | |
"Data Size: %.2f MiB | %.2f KiB | %d byte\n", | |
dataSizeMib, | |
float64(dataSizeByte)/1024.0, | |
dataSizeByte, | |
) | |
fmt.Printf( | |
"Data Gen Time: %.2f s | %.2f ms | %.2f μs\n", | |
ToSeconds(dataGenTime), | |
ToMilliseconds(dataGenTime), | |
ToMicroseconds(dataGenTime), | |
) | |
fmt.Printf( | |
"Data Gen Rate: %.2f MiB/s | %.0f byte/s | %.2f MBit/s\n", | |
dataSizeMib/ToSeconds(dataGenTime), | |
float64(dataSizeByte)/ToSeconds(dataGenTime), | |
float64(dataSizeByte*8/1000000)/ToSeconds(dataGenTime), | |
) | |
fmt.Printf( | |
"Key Gen Time: %.2f s | %.2f ms | %.2f μs\n", | |
ToSeconds(avgKeyGenTime), | |
ToMilliseconds(avgKeyGenTime), | |
ToMicroseconds(avgKeyGenTime), | |
) | |
fmt.Printf( | |
"Key Gen Rate: %.0f keys/s\n", | |
dataSizeMib/ToSeconds(avgKeyGenTime), | |
) | |
fmt.Printf( | |
"IV Gen Time: %.2f s | %.2f ms | %.2f μs\n", | |
ToSeconds(avgIvGenTime), | |
ToMilliseconds(avgIvGenTime), | |
ToMicroseconds(avgIvGenTime), | |
) | |
fmt.Printf( | |
"IV Gen Rate: %.0f IVs/s\n", | |
dataSizeMib/ToSeconds(avgIvGenTime), | |
) | |
fmt.Printf( | |
"Encryption Time: %.2f s | %.2f ms | %.2f μs\n", | |
ToSeconds(avgEncTime), | |
ToMilliseconds(avgEncTime), | |
ToMicroseconds(avgEncTime), | |
) | |
fmt.Printf( | |
"Encryption Rate: %.2f MiB/s | %.0f byte/s | %.2f MBit/s\n", | |
dataSizeMib/ToSeconds(avgEncTime), | |
float64(dataSizeByte)/ToSeconds(avgEncTime), | |
float64(dataSizeByte*8/1000000)/ToSeconds(avgEncTime), | |
) | |
fmt.Printf( | |
"Decryption Time: %.2f s | %.2f ms | %.2f μs\n", | |
ToSeconds(avgDecTime), | |
ToMilliseconds(avgDecTime), | |
ToMicroseconds(avgDecTime), | |
) | |
fmt.Printf( | |
"Decryption Rate: %.2f MiB/s | %.0f byte/s | %.2f MBit/s\n", | |
dataSizeMib/ToSeconds(avgDecTime), | |
float64(dataSizeByte)/ToSeconds(avgDecTime), | |
float64(dataSizeByte*8/1000000)/ToSeconds(avgDecTime), | |
) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
The compiled executable accepts the following arguments:
"-d=?" generated random data size in bytes
"-b=?" block size, aka key size (either 128, 192 or 256)
"-i=?" number of test iterations (encrypt and decrypt cycle)