diff --git a/zeta/rsa_bench/Makefile b/zeta/rsa_bench/Makefile new file mode 100644 index 0000000000000..d221307a1e289 --- /dev/null +++ b/zeta/rsa_bench/Makefile @@ -0,0 +1,14 @@ +all: compile + +compile: rsa.go rsa_test.go + go build -o example.exe + go test -c -o bench.exe + +example: example.exe + ./example.exe + +benchmark: bench.exe + ./bench.exe -test.v -test.bench=. + +clean: + rm -f ./bench.exe ./example.exe \ No newline at end of file diff --git a/zeta/rsa_bench/readme.md b/zeta/rsa_bench/readme.md new file mode 100644 index 0000000000000..25e6f7ef916c9 --- /dev/null +++ b/zeta/rsa_bench/readme.md @@ -0,0 +1,29 @@ +## Benchmark of In-Kernel RSA Signing from User Space + +This program uses the Linux API to access to in-kernel cryptographic +operations. This Go program makes direct syscalls to the kernel similarly +to the `keyctl` utility command. + +To run an example: + $ make example + +To run a benchmark: + $ make benchmark + +After that, the output looks like: + +``` +BenchmarkRSAKernel +BenchmarkRSAKernel-16 283 4283867 ns/op +BenchmarkRSAGo +BenchmarkRSAGo-16 1412 908581 ns/op +``` + +The difference in time is expected as the program should wait for the +operating system to respond the syscall, and move memory between the kernel +space and the user space. + +Known Issues: +- "failed to load the private key into the keyring: bad message" + This means the parser is not loaded. To solve this issue run: + $ sudo modprobe pkcs8_key_parser diff --git a/zeta/rsa_bench/rsa.go b/zeta/rsa_bench/rsa.go new file mode 100644 index 0000000000000..386c5ba7299b4 --- /dev/null +++ b/zeta/rsa_bench/rsa.go @@ -0,0 +1,116 @@ +package main + +import ( + "crypto" + "crypto/rand" + "crypto/rsa" + "crypto/sha256" + "crypto/x509" + "log" + "syscall" + "unsafe" +) + +type KeySerial int32 +type Keyring int32 + +const ( + KEY_SPEC_PROCESS_KEYRING Keyring = -2 + KEYCTL_PKEY_SIGN = 27 +) + +var ( + keyTypeAsym = []byte("asymmetric\x00") + sha256pkcs1 = []byte("enc=pkcs1 hash=sha256\x00") +) + +func (keyring Keyring) LoadAsym(desc string, payload []byte) (KeySerial, error) { + cdesc := []byte(desc + "\x00") + serial, _, errno := syscall.Syscall6( + syscall.SYS_ADD_KEY, + uintptr(unsafe.Pointer(&keyTypeAsym[0])), + uintptr(unsafe.Pointer(&cdesc[0])), + uintptr(unsafe.Pointer(&payload[0])), + uintptr(len(payload)), + uintptr(keyring), + uintptr(0), + ) + if errno == 0 { + return KeySerial(serial), nil + } + + return KeySerial(serial), errno +} + +type pkeyParams struct { + key_id KeySerial + in_len uint32 + out_or_in2_len uint32 + __spare [7]uint32 +} + +func (key KeySerial) Sign(info, digest, signature []byte) error { + var params pkeyParams + params.key_id = key + params.in_len = uint32(len(digest)) + params.out_or_in2_len = uint32(len(signature)) + + _, _, errno := syscall.Syscall6( + syscall.SYS_KEYCTL, KEYCTL_PKEY_SIGN, + uintptr(unsafe.Pointer(¶ms)), + uintptr(unsafe.Pointer(&info[0])), + uintptr(unsafe.Pointer(&digest[0])), + uintptr(unsafe.Pointer(&signature[0])), + uintptr(0), + ) + if errno == 0 { + return nil + } + + return errno +} + +func loadKeyToKernel(key crypto.PrivateKey) KeySerial { + pkcs8, err := x509.MarshalPKCS8PrivateKey(key) + if err != nil { + log.Fatalf("failed to serialize the private key to PKCS8 blob: %v", err) + } + + serial, err := KEY_SPEC_PROCESS_KEYRING.LoadAsym("test rsa key", pkcs8) + if err != nil { + log.Fatalf("failed to load the private key into the keyring: %v", err) + } + + log.Printf("Loaded key to the kernel with ID: %v", serial) + + return serial +} + +func main() { + const N = 2048 + + var ( + msg = []byte("hello world") + digest = sha256.Sum256(msg) + signature [N / 8]byte + ) + + priv, err := rsa.GenerateKey(rand.Reader, N) + if err != nil { + log.Fatalf("failed to generate private key: %v", err) + } + + keyInKernel := loadKeyToKernel(priv) + + err = keyInKernel.Sign(sha256pkcs1, digest[:], signature[:]) + if err != nil { + log.Fatalf("failed to sign the digest: %v", err) + } + log.Printf("Signature from Kernel: %x...", signature[:10]) + + err = rsa.VerifyPKCS1v15(&priv.PublicKey, crypto.SHA256, digest[:], signature[:]) + log.Printf("Valid signature: %v", err == nil) + if err != nil { + log.Fatalf("failed to verify the signature: %v", err) + } +} diff --git a/zeta/rsa_bench/rsa_test.go b/zeta/rsa_bench/rsa_test.go new file mode 100644 index 0000000000000..77396febe4117 --- /dev/null +++ b/zeta/rsa_bench/rsa_test.go @@ -0,0 +1,57 @@ +package main + +import ( + "crypto" + "crypto/rand" + "crypto/rsa" + "crypto/sha256" + "log" + "testing" +) + +func BenchmarkRSAKernel(b *testing.B) { + const N = 2048 + + var ( + msg = []byte("hello world") + digest = sha256.Sum256(msg) + signature [N / 8]byte + ) + + priv, err := rsa.GenerateKey(rand.Reader, N) + if err != nil { + log.Fatalf("failed to generate private key: %v", err) + } + + keyInKernel := loadKeyToKernel(priv) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + err := keyInKernel.Sign(sha256pkcs1, digest[:], signature[:]) + if err != nil { + log.Fatalf("failed to sign the digest: %v", err) + } + } +} + +func BenchmarkRSAGo(b *testing.B) { + const N = 2048 + + var ( + msg = []byte("hello world") + digest = sha256.Sum256(msg) + ) + + priv, err := rsa.GenerateKey(rand.Reader, N) + if err != nil { + log.Fatalf("failed to generate private key: %v", err) + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, err := priv.Sign(rand.Reader, digest[:], crypto.SHA256) + if err != nil { + log.Fatalf("failed to sign the digest: %v", err) + } + } +}