Skip to content

Instantly share code, notes, and snippets.

@romshark
Last active October 22, 2016 16:42
Show Gist options
  • Save romshark/a788280288de303e43fdece86e241361 to your computer and use it in GitHub Desktop.
Save romshark/a788280288de303e43fdece86e241361 to your computer and use it in GitHub Desktop.
AES CTR Testsuite for encryption and decryption performance benchmarking
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),
)
}
@romshark
Copy link
Author

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)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment