Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RSA Benchmark: Adding Go tool for measuring RSA from user land. #8

Merged
merged 1 commit into from
Feb 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions zeta/rsa_bench/Makefile
Original file line number Diff line number Diff line change
@@ -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
29 changes: 29 additions & 0 deletions zeta/rsa_bench/readme.md
Original file line number Diff line number Diff line change
@@ -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
116 changes: 116 additions & 0 deletions zeta/rsa_bench/rsa.go
Original file line number Diff line number Diff line change
@@ -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(&params)),
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)
}
}
57 changes: 57 additions & 0 deletions zeta/rsa_bench/rsa_test.go
Original file line number Diff line number Diff line change
@@ -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)
}
}
}
Loading