Skip to content

Commit d21ad90

Browse files
authored
Adding Go tool for measuring RSA from user land. (#8)
1 parent 94e9608 commit d21ad90

File tree

4 files changed

+216
-0
lines changed

4 files changed

+216
-0
lines changed

zeta/rsa_bench/Makefile

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
all: compile
2+
3+
compile: rsa.go rsa_test.go
4+
go build -o example.exe
5+
go test -c -o bench.exe
6+
7+
example: example.exe
8+
./example.exe
9+
10+
benchmark: bench.exe
11+
./bench.exe -test.v -test.bench=.
12+
13+
clean:
14+
rm -f ./bench.exe ./example.exe

zeta/rsa_bench/readme.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
## Benchmark of In-Kernel RSA Signing from User Space
2+
3+
This program uses the Linux API to access to in-kernel cryptographic
4+
operations. This Go program makes direct syscalls to the kernel similarly
5+
to the `keyctl` utility command.
6+
7+
To run an example:
8+
$ make example
9+
10+
To run a benchmark:
11+
$ make benchmark
12+
13+
After that, the output looks like:
14+
15+
```
16+
BenchmarkRSAKernel
17+
BenchmarkRSAKernel-16 283 4283867 ns/op
18+
BenchmarkRSAGo
19+
BenchmarkRSAGo-16 1412 908581 ns/op
20+
```
21+
22+
The difference in time is expected as the program should wait for the
23+
operating system to respond the syscall, and move memory between the kernel
24+
space and the user space.
25+
26+
Known Issues:
27+
- "failed to load the private key into the keyring: bad message"
28+
This means the parser is not loaded. To solve this issue run:
29+
$ sudo modprobe pkcs8_key_parser

zeta/rsa_bench/rsa.go

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
package main
2+
3+
import (
4+
"crypto"
5+
"crypto/rand"
6+
"crypto/rsa"
7+
"crypto/sha256"
8+
"crypto/x509"
9+
"log"
10+
"syscall"
11+
"unsafe"
12+
)
13+
14+
type KeySerial int32
15+
type Keyring int32
16+
17+
const (
18+
KEY_SPEC_PROCESS_KEYRING Keyring = -2
19+
KEYCTL_PKEY_SIGN = 27
20+
)
21+
22+
var (
23+
keyTypeAsym = []byte("asymmetric\x00")
24+
sha256pkcs1 = []byte("enc=pkcs1 hash=sha256\x00")
25+
)
26+
27+
func (keyring Keyring) LoadAsym(desc string, payload []byte) (KeySerial, error) {
28+
cdesc := []byte(desc + "\x00")
29+
serial, _, errno := syscall.Syscall6(
30+
syscall.SYS_ADD_KEY,
31+
uintptr(unsafe.Pointer(&keyTypeAsym[0])),
32+
uintptr(unsafe.Pointer(&cdesc[0])),
33+
uintptr(unsafe.Pointer(&payload[0])),
34+
uintptr(len(payload)),
35+
uintptr(keyring),
36+
uintptr(0),
37+
)
38+
if errno == 0 {
39+
return KeySerial(serial), nil
40+
}
41+
42+
return KeySerial(serial), errno
43+
}
44+
45+
type pkeyParams struct {
46+
key_id KeySerial
47+
in_len uint32
48+
out_or_in2_len uint32
49+
__spare [7]uint32
50+
}
51+
52+
func (key KeySerial) Sign(info, digest, signature []byte) error {
53+
var params pkeyParams
54+
params.key_id = key
55+
params.in_len = uint32(len(digest))
56+
params.out_or_in2_len = uint32(len(signature))
57+
58+
_, _, errno := syscall.Syscall6(
59+
syscall.SYS_KEYCTL, KEYCTL_PKEY_SIGN,
60+
uintptr(unsafe.Pointer(&params)),
61+
uintptr(unsafe.Pointer(&info[0])),
62+
uintptr(unsafe.Pointer(&digest[0])),
63+
uintptr(unsafe.Pointer(&signature[0])),
64+
uintptr(0),
65+
)
66+
if errno == 0 {
67+
return nil
68+
}
69+
70+
return errno
71+
}
72+
73+
func loadKeyToKernel(key crypto.PrivateKey) KeySerial {
74+
pkcs8, err := x509.MarshalPKCS8PrivateKey(key)
75+
if err != nil {
76+
log.Fatalf("failed to serialize the private key to PKCS8 blob: %v", err)
77+
}
78+
79+
serial, err := KEY_SPEC_PROCESS_KEYRING.LoadAsym("test rsa key", pkcs8)
80+
if err != nil {
81+
log.Fatalf("failed to load the private key into the keyring: %v", err)
82+
}
83+
84+
log.Printf("Loaded key to the kernel with ID: %v", serial)
85+
86+
return serial
87+
}
88+
89+
func main() {
90+
const N = 2048
91+
92+
var (
93+
msg = []byte("hello world")
94+
digest = sha256.Sum256(msg)
95+
signature [N / 8]byte
96+
)
97+
98+
priv, err := rsa.GenerateKey(rand.Reader, N)
99+
if err != nil {
100+
log.Fatalf("failed to generate private key: %v", err)
101+
}
102+
103+
keyInKernel := loadKeyToKernel(priv)
104+
105+
err = keyInKernel.Sign(sha256pkcs1, digest[:], signature[:])
106+
if err != nil {
107+
log.Fatalf("failed to sign the digest: %v", err)
108+
}
109+
log.Printf("Signature from Kernel: %x...", signature[:10])
110+
111+
err = rsa.VerifyPKCS1v15(&priv.PublicKey, crypto.SHA256, digest[:], signature[:])
112+
log.Printf("Valid signature: %v", err == nil)
113+
if err != nil {
114+
log.Fatalf("failed to verify the signature: %v", err)
115+
}
116+
}

zeta/rsa_bench/rsa_test.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package main
2+
3+
import (
4+
"crypto"
5+
"crypto/rand"
6+
"crypto/rsa"
7+
"crypto/sha256"
8+
"log"
9+
"testing"
10+
)
11+
12+
func BenchmarkRSAKernel(b *testing.B) {
13+
const N = 2048
14+
15+
var (
16+
msg = []byte("hello world")
17+
digest = sha256.Sum256(msg)
18+
signature [N / 8]byte
19+
)
20+
21+
priv, err := rsa.GenerateKey(rand.Reader, N)
22+
if err != nil {
23+
log.Fatalf("failed to generate private key: %v", err)
24+
}
25+
26+
keyInKernel := loadKeyToKernel(priv)
27+
28+
b.ResetTimer()
29+
for i := 0; i < b.N; i++ {
30+
err := keyInKernel.Sign(sha256pkcs1, digest[:], signature[:])
31+
if err != nil {
32+
log.Fatalf("failed to sign the digest: %v", err)
33+
}
34+
}
35+
}
36+
37+
func BenchmarkRSAGo(b *testing.B) {
38+
const N = 2048
39+
40+
var (
41+
msg = []byte("hello world")
42+
digest = sha256.Sum256(msg)
43+
)
44+
45+
priv, err := rsa.GenerateKey(rand.Reader, N)
46+
if err != nil {
47+
log.Fatalf("failed to generate private key: %v", err)
48+
}
49+
50+
b.ResetTimer()
51+
for i := 0; i < b.N; i++ {
52+
_, err := priv.Sign(rand.Reader, digest[:], crypto.SHA256)
53+
if err != nil {
54+
log.Fatalf("failed to sign the digest: %v", err)
55+
}
56+
}
57+
}

0 commit comments

Comments
 (0)