~ knotless Search/
↑↓ move↵ open⇧↵ all results
0
0
main
85 lines · 2.1 KB Raw History
package openbaoctl

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/fs"
	"os"
	"path/filepath"
)

type UnsealKeys struct {
	Threshold int      `json:"threshold"`
	Shares    int      `json:"shares"`
	Keys      []string `json:"keys_base64"`
}

var ErrNoUnsealKeys = errors.New("openbaoctl: no unseal keys")

func WriteUnsealKeys(path string, k UnsealKeys) error {
	if _, err := os.Lstat(path); err == nil {
		return fmt.Errorf("openbaoctl: %s already exists; unseal keys are written once and never replaced", path)
	}
	b, err := json.Marshal(k)
	if err != nil {
		return err
	}
	dir := filepath.Dir(path)
	if err := os.MkdirAll(dir, 0o700); err != nil {
		return err
	}
	tmp := path + ".tmp"
	f, err := os.OpenFile(tmp, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0o600)
	if err != nil {
		return err
	}
	if _, err := f.Write(append(b, '\n')); err != nil {
		f.Close()
		return err
	}
	if err := f.Sync(); err != nil {
		f.Close()
		return err
	}
	if err := f.Close(); err != nil {
		return err
	}
	if err := os.Rename(tmp, path); err != nil {
		return err
	}
	d, err := os.Open(dir)
	if err != nil {
		return err
	}
	defer d.Close()
	return d.Sync()
}

func ReadUnsealKeys(path string) (UnsealKeys, error) {
	fi, err := os.Lstat(path)
	if errors.Is(err, fs.ErrNotExist) {
		return UnsealKeys{}, fmt.Errorf("%w at %s", ErrNoUnsealKeys, path)
	}
	if err != nil {
		return UnsealKeys{}, err
	}
	if !fi.Mode().IsRegular() {
		return UnsealKeys{}, fmt.Errorf("openbaoctl: %s is not a regular file", path)
	}
	if fi.Mode().Perm()&0o077 != 0 {
		return UnsealKeys{}, fmt.Errorf("openbaoctl: %s is mode %04o; unseal keys are read only from a file no other user can read (0600)", path, fi.Mode().Perm())
	}
	b, err := os.ReadFile(path)
	if err != nil {
		return UnsealKeys{}, err
	}
	var k UnsealKeys
	if err := json.Unmarshal(b, &k); err != nil {
		return UnsealKeys{}, fmt.Errorf("openbaoctl: %s is not JSON: %w", path, err)
	}
	if k.Threshold <= 0 || len(k.Keys) < k.Threshold {
		return UnsealKeys{}, fmt.Errorf("openbaoctl: %s holds %d keys for a threshold of %d", path, len(k.Keys), k.Threshold)
	}
	return k, nil
}