Skip to content

Instantly share code, notes, and snippets.

@ddz
Created August 19, 2024 01:42
Show Gist options
  • Save ddz/ded489b1a5b05afb5f883c18ae005340 to your computer and use it in GitHub Desktop.
Save ddz/ded489b1a5b05afb5f883c18ae005340 to your computer and use it in GitHub Desktop.
Load a key into Linux kernel keyring and use it via Crypto API userland interface (Linux >= 6.1)
package main
import (
"encoding/hex"
"fmt"
"log"
"io"
"os"
"golang.org/x/sys/unix"
)
func main() {
keySerial, err := addKey()
if err != nil {
log.Fatal(err)
}
hashfd, err := makeCipher(algTypeHash, algNameHmacSha1, keySerial)
if err != nil {
log.Fatal(err)
}
// Assume hashfd is already configured using the setup process.
hash := os.NewFile(hashfd, "sha1")
// Hash an input string and read the results. Each Write discards
// previous hash state. Read always reads the current state.
b := make([]byte, 20)
for i := 0; i < 2; i++ {
io.WriteString(hash, "Hello, world.")
hash.Read(b)
fmt.Println(hex.EncodeToString(b))
}
fmt.Println("Hello, World!")
}
// Linux keyring support
func addKey() (int, error) {
ringId, err :=
unix.KeyctlGetKeyringID(unix.KEY_SPEC_PROCESS_KEYRING, true)
if err != nil {
return 0, err
}
// "logon" keys cannot be read from userspace, but can be used
keyId, err := unix.AddKey("logon", "testing:", []byte("thisisthekey"),
ringId)
if err != nil {
return 0, fmt.Errorf("AddKey: %w", err)
}
return keyId, nil
}
//
// Linux Crypto API userland interface:
//
// References:
// https://pkg.go.dev/golang.org/x/sys/unix#SockaddrALG
// http://www.chronox.de/crypto-API/crypto/userspace-if.html
//
type algType string
const (
algTypeHash algType = "hash"
)
type algName string
const (
algNameSha1 algName = "sha1"
algNameHmacSha1 = "hmac(sha1)"
)
func makeCipher(t algType, n algName, keySerial int) (uintptr, error) {
algFd, err := unix.Socket(unix.AF_ALG, unix.SOCK_SEQPACKET, 0)
if err != nil {
return 0, err
}
addr := &unix.SockaddrALG{Type: string(t), Name: string(n)}
err = unix.Bind(algFd, addr)
if err != nil {
return 0, err
}
//
// NB: Contrary to kernel docs, key must be set on bound socket,
// not socket returned by accept
//
err = unix.SetsockoptInt(int(algFd), unix.SOL_ALG,
unix.ALG_SET_KEY_BY_KEY_SERIAL, keySerial)
if err != nil {
return 0, fmt.Errorf("setsockopt %d", err)
}
//
// NB: unix.Accept does not work at this time; must invoke accept()
// manually using unix.Syscall.
//
fd, _, errno := unix.Syscall(unix.SYS_ACCEPT, uintptr(algFd), 0, 0)
if errno != 0 {
return 0, err
}
return fd, nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment