added shamir keys
This commit is contained in:
parent
13780f1c74
commit
be0f532628
14 changed files with 238 additions and 43 deletions
|
|
@ -51,7 +51,6 @@ func NewServer(log logr.Logger, listen, socket string) (*Server, error) {
|
|||
s.listen = l
|
||||
|
||||
sock, err := unixListener(socket)
|
||||
net.Listen("unix", socket)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -83,14 +83,14 @@ func (c *Client) GetKey(ctx context.Context, id string) (key schema.Key, err err
|
|||
return
|
||||
}
|
||||
|
||||
func (c *Client) CreateKey(ctx context.Context, key schema.NewKey) (err error) {
|
||||
func (c *Client) CreateKey(ctx context.Context, newKey schema.NewKey) (key schema.Key, err error) {
|
||||
buf := new(bytes.Buffer)
|
||||
path := keyPath
|
||||
err = key.Validate()
|
||||
err = newKey.Validate()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = json.NewEncoder(buf).Encode(key)
|
||||
err = json.NewEncoder(buf).Encode(newKey)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
@ -98,7 +98,7 @@ func (c *Client) CreateKey(ctx context.Context, key schema.NewKey) (err error) {
|
|||
if err != nil {
|
||||
return
|
||||
}
|
||||
_, err = c.c.Do(req, nil)
|
||||
_, err = c.c.Do(req, &key)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,10 @@
|
|||
package schema
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"git.giftfish.de/ston1th/keyctl/pkg/api/types"
|
||||
"git.giftfish.de/ston1th/keyctl/pkg/core"
|
||||
|
|
@ -22,6 +25,7 @@ type NewKey struct {
|
|||
Name string `json:"name"`
|
||||
Size int `json:"size"`
|
||||
Encoding core.Encoding `json:"encoding"`
|
||||
Type core.Type `json:"type"`
|
||||
}
|
||||
|
||||
var (
|
||||
|
|
@ -58,13 +62,52 @@ type Key struct {
|
|||
Created int64 `json:"created"`
|
||||
Size int `json:"size"`
|
||||
Encoding core.Encoding `json:"encoding"`
|
||||
Type core.Type `json:"type"`
|
||||
Key string `json:"key,omitempty"`
|
||||
}
|
||||
|
||||
func (k Key) Print(share string) (string, error) {
|
||||
switch k.Type {
|
||||
case core.Shamir:
|
||||
part1, err := k.Encoding.Decode(k.Key)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
part2, err := hex.DecodeString(share)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
dec, err := k.Type.Decode(append(part1, part2...))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return k.Encoding.EncodeToString(dec), nil
|
||||
}
|
||||
return k.Key, nil
|
||||
}
|
||||
|
||||
type Keys []Key
|
||||
|
||||
func (Keys) Less(a, b Key) bool { return a.Name < b.Name }
|
||||
func (k Keys) Sort() { slices.SortStableFunc(k, k.Less) }
|
||||
func (k Keys) Len() int { return len(k) }
|
||||
func (k Keys) Fields() string {
|
||||
return "ID\t Name\t Created\t Size\t Encoding\t Type"
|
||||
}
|
||||
func (k Keys) Values() []string {
|
||||
vals := make([]string, len(k))
|
||||
for i, v := range k {
|
||||
vals[i] = fmt.Sprintf("%s\t %s\t %s\t %d\t %s\t %s",
|
||||
v.ID,
|
||||
v.Name,
|
||||
timeFmt(v.Created),
|
||||
v.Size,
|
||||
v.Encoding,
|
||||
v.Type,
|
||||
)
|
||||
}
|
||||
return vals
|
||||
}
|
||||
|
||||
func NewKeys(keys core.Keys) (k Keys) {
|
||||
for _, key := range keys {
|
||||
|
|
@ -87,6 +130,24 @@ type Approvals []Approval
|
|||
|
||||
func (Approvals) Less(a, b Approval) bool { return a.Name < b.Name }
|
||||
func (a Approvals) Sort() { slices.SortStableFunc(a, a.Less) }
|
||||
func (a Approvals) Len() int { return len(a) }
|
||||
func (a Approvals) Fields() string {
|
||||
return "ID\t Approval ID\t Name\t Status\t Created\t IP"
|
||||
}
|
||||
func (a Approvals) Values() []string {
|
||||
vals := make([]string, len(a))
|
||||
for i, v := range a {
|
||||
vals[i] = fmt.Sprintf("%s\t %s\t %s\t %s\t %s\t %s",
|
||||
v.ID,
|
||||
v.AID,
|
||||
v.Name,
|
||||
v.Status,
|
||||
timeFmt(v.Created),
|
||||
v.IP,
|
||||
)
|
||||
}
|
||||
return vals
|
||||
}
|
||||
|
||||
func NewApprovals(m types.ApprovalMap) (a Approvals) {
|
||||
for k, v := range m {
|
||||
|
|
@ -102,3 +163,7 @@ func NewApprovals(m types.ApprovalMap) (a Approvals) {
|
|||
a.Sort()
|
||||
return
|
||||
}
|
||||
|
||||
func timeFmt(t int64) string {
|
||||
return time.Unix(t, 0).Format(time.RFC3339)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ func newHandler(ctx *types.Context) {
|
|||
ctx.Err(types.ErrInvalid)
|
||||
return
|
||||
}
|
||||
key, err := ctx.Data.DB.CreateKey(newKey.Name, newKey.Size, newKey.Encoding)
|
||||
key, err := ctx.Data.DB.CreateKey(newKey.Name, newKey.Size, newKey.Encoding, newKey.Type)
|
||||
if err != nil {
|
||||
ctx.Log.Error(err, "error writing key", "name", newKey.Name)
|
||||
ctx.Err(types.ErrISE)
|
||||
|
|
|
|||
|
|
@ -5,9 +5,11 @@ package cli
|
|||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"text/tabwriter"
|
||||
|
||||
apiclient "git.giftfish.de/ston1th/keyctl/pkg/api/client"
|
||||
clientv1 "git.giftfish.de/ston1th/keyctl/pkg/api/v1/client"
|
||||
|
|
@ -27,33 +29,64 @@ func Run(arg string) {
|
|||
ctx := context.Background()
|
||||
switch arg {
|
||||
case "new":
|
||||
name, encoding, size := newKeyFlags()
|
||||
name, encoding, t, size, err := newKeyFlags()
|
||||
if err != nil {
|
||||
exit(err)
|
||||
}
|
||||
newKey := schema.NewKey{
|
||||
Name: name,
|
||||
Size: size,
|
||||
Encoding: core.EncodingFromString(encoding),
|
||||
Type: core.TypeFromString(t),
|
||||
}
|
||||
c := client()
|
||||
err = c.CreateKey(ctx, newKey)
|
||||
k, err := c.CreateKey(ctx, newKey)
|
||||
if err != nil {
|
||||
exit(err)
|
||||
}
|
||||
switch k.Type {
|
||||
case core.Plain:
|
||||
fmt.Println("Key ID:", k.ID)
|
||||
case core.Shamir:
|
||||
fmt.Println("Key ID:", k.ID)
|
||||
fmt.Println("Key Share:", k.Key)
|
||||
}
|
||||
case "del":
|
||||
id := idFlags("key id")
|
||||
id, err := idFlags("key id")
|
||||
if err != nil {
|
||||
exit(err)
|
||||
}
|
||||
c := client()
|
||||
err = c.DeleteKey(ctx, id)
|
||||
case "approve":
|
||||
id := idFlags("approval id")
|
||||
id, err := idFlags("approval id")
|
||||
if err != nil {
|
||||
exit(err)
|
||||
}
|
||||
c := client()
|
||||
err = c.AcceptApproval(ctx, id)
|
||||
case "reject":
|
||||
id := idFlags("approval id")
|
||||
id, err := idFlags("approval id")
|
||||
if err != nil {
|
||||
exit(err)
|
||||
}
|
||||
c := client()
|
||||
err = c.RejectApproval(ctx, id)
|
||||
case "get":
|
||||
id, json := getFlags()
|
||||
id, share, json, err := getFlags()
|
||||
if err != nil {
|
||||
exit(err)
|
||||
}
|
||||
c := client()
|
||||
key, err := c.GetKey(ctx, id)
|
||||
if err != nil {
|
||||
exit(err)
|
||||
}
|
||||
out, err := key.Print(share)
|
||||
if err != nil {
|
||||
exit(err)
|
||||
}
|
||||
key.Key = out
|
||||
if json {
|
||||
jsonOutput(key) // does not return
|
||||
}
|
||||
|
|
@ -68,15 +101,7 @@ func Run(arg string) {
|
|||
if json {
|
||||
jsonOutput(keys) // does not return
|
||||
}
|
||||
for _, k := range keys {
|
||||
fmt.Printf("%s %s %d %s %d\n",
|
||||
k.ID,
|
||||
k.Name,
|
||||
k.Size,
|
||||
k.Encoding,
|
||||
k.Created,
|
||||
)
|
||||
}
|
||||
print(keys)
|
||||
case "req":
|
||||
json := outputFlags()
|
||||
c := client()
|
||||
|
|
@ -87,16 +112,7 @@ func Run(arg string) {
|
|||
if json {
|
||||
jsonOutput(approvals) // does not return
|
||||
}
|
||||
for _, a := range approvals {
|
||||
fmt.Printf("%s %s %s %s %d %s\n",
|
||||
a.ID,
|
||||
a.AID,
|
||||
a.Name,
|
||||
a.Status,
|
||||
a.Created,
|
||||
a.IP,
|
||||
)
|
||||
}
|
||||
print(approvals)
|
||||
}
|
||||
if err != nil {
|
||||
exit(err)
|
||||
|
|
@ -126,16 +142,23 @@ func jsonOutput(v any) {
|
|||
os.Exit(0)
|
||||
}
|
||||
|
||||
func idFlags(desc string) (id string) {
|
||||
func idFlags(desc string) (id string, err error) {
|
||||
fs.StringVar(&id, "id", "", desc)
|
||||
fs.Parse(os.Args[2:])
|
||||
if id == "" {
|
||||
err = errors.New("missing flag: id")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func getFlags() (id string, json bool) {
|
||||
func getFlags() (id, share string, json bool, err error) {
|
||||
fs.StringVar(&id, "id", "", "key id")
|
||||
fs.StringVar(&share, "share", "", "shamir key share")
|
||||
fs.BoolVar(&json, "json", false, "json output")
|
||||
fs.Parse(os.Args[2:])
|
||||
if id == "" {
|
||||
err = errors.New("missing flag: id")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -145,10 +168,26 @@ func outputFlags() (json bool) {
|
|||
return
|
||||
}
|
||||
|
||||
func newKeyFlags() (name, encoding string, size int) {
|
||||
func newKeyFlags() (name, encoding, t string, size int, err error) {
|
||||
fs.StringVar(&name, "name", "", "key name")
|
||||
fs.StringVar(&encoding, "enc", "hex", "key encoding scheme [hex|base32|base64|base64url]")
|
||||
fs.StringVar(&t, "type", "plain", "key type [plain|shamir]")
|
||||
fs.IntVar(&size, "size", 16, "key size in bytes")
|
||||
fs.Parse(os.Args[2:])
|
||||
if name == "" {
|
||||
err = errors.New("missing flag: name")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func print(tw core.TableWriter) {
|
||||
if tw.Len() == 0 {
|
||||
return
|
||||
}
|
||||
w := tabwriter.NewWriter(os.Stdout, 1, 0, 1, ' ', tabwriter.Debug)
|
||||
fmt.Fprintln(w, tw.Fields())
|
||||
for _, v := range tw.Values() {
|
||||
fmt.Fprintln(w, v)
|
||||
}
|
||||
w.Flush()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,6 +36,18 @@ func (e Encoding) EncodeToString(src []byte) string {
|
|||
return hex.EncodeToString(src)
|
||||
}
|
||||
|
||||
func (e Encoding) Decode(src string) ([]byte, error) {
|
||||
switch e {
|
||||
case Base32:
|
||||
return b32raw.DecodeString(src)
|
||||
case Base64:
|
||||
return base64.RawStdEncoding.DecodeString(src)
|
||||
case Base64URL:
|
||||
return base64.RawURLEncoding.DecodeString(src)
|
||||
}
|
||||
return hex.DecodeString(src)
|
||||
}
|
||||
|
||||
func EncodingFromString(e string) Encoding {
|
||||
switch e {
|
||||
case "base32":
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ type Key struct {
|
|||
Created int64
|
||||
Size int
|
||||
Encoding Encoding
|
||||
Type Type
|
||||
Key string
|
||||
}
|
||||
|
||||
|
|
|
|||
9
pkg/core/tablewriter.go
Normal file
9
pkg/core/tablewriter.go
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
// Copyright (C) 2023 Marius Schellenberger
|
||||
|
||||
package core
|
||||
|
||||
type TableWriter interface {
|
||||
Len() int
|
||||
Fields() string
|
||||
Values() []string
|
||||
}
|
||||
55
pkg/core/type.go
Normal file
55
pkg/core/type.go
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
// Copyright (C) 2023 Marius Schellenberger
|
||||
|
||||
package core
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/hashicorp/vault/shamir"
|
||||
)
|
||||
|
||||
type Type int
|
||||
|
||||
func (t Type) String() string {
|
||||
switch t {
|
||||
case Shamir:
|
||||
return "shamir"
|
||||
}
|
||||
return "plain"
|
||||
}
|
||||
|
||||
func (t Type) Decode(src []byte) ([]byte, error) {
|
||||
switch t {
|
||||
case Shamir:
|
||||
plen := len(src) / 2
|
||||
part1 := src[0:plen]
|
||||
part2 := src[plen:]
|
||||
return shamir.Combine([][]byte{part1, part2})
|
||||
}
|
||||
return src, nil
|
||||
}
|
||||
|
||||
func (t Type) Encode(src []byte) ([]byte, error) {
|
||||
switch t {
|
||||
case Shamir:
|
||||
parts, err := shamir.Split(src, 2, 2)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return bytes.Join(parts, nil), nil
|
||||
}
|
||||
return src, nil
|
||||
}
|
||||
|
||||
func TypeFromString(t string) Type {
|
||||
switch t {
|
||||
case "shamir":
|
||||
return Shamir
|
||||
}
|
||||
return Plain
|
||||
}
|
||||
|
||||
const (
|
||||
Plain Type = iota
|
||||
Shamir
|
||||
)
|
||||
|
|
@ -31,12 +31,12 @@ func (db *DB) GetKeyWithSecret(id string) (k core.Key, err error) {
|
|||
return
|
||||
}
|
||||
|
||||
func (db *DB) CreateKey(name string, size int, enc core.Encoding) (k core.Key, err error) {
|
||||
k, err = key.Generate(name, size, enc)
|
||||
func (db *DB) CreateKey(name string, size int, enc core.Encoding, t core.Type) (k core.Key, err error) {
|
||||
kstore, k, err := key.Generate(name, size, enc, t)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = db.store.Set(keyPrefix+k.ID, &k)
|
||||
err = db.store.Set(keyPrefix+k.ID, &kstore)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ func GenerateID() (string, error) {
|
|||
return hex.EncodeToString(id), nil
|
||||
}
|
||||
|
||||
func Generate(name string, size int, enc core.Encoding) (k core.Key, err error) {
|
||||
func Generate(name string, size int, enc core.Encoding, t core.Type) (kstore, k core.Key, err error) {
|
||||
id, err := GenerateID()
|
||||
if err != nil {
|
||||
return
|
||||
|
|
@ -36,13 +36,24 @@ func Generate(name string, size int, enc core.Encoding) (k core.Key, err error)
|
|||
if err != nil {
|
||||
return
|
||||
}
|
||||
k = core.Key{
|
||||
key, err = t.Encode(key)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
kstore = core.Key{
|
||||
Name: name,
|
||||
ID: id,
|
||||
Size: size,
|
||||
Encoding: enc,
|
||||
Key: enc.EncodeToString(key),
|
||||
Type: t,
|
||||
Created: time.Now().Unix(),
|
||||
}
|
||||
k = kstore
|
||||
if t == core.Shamir {
|
||||
klen := len(key) / 2
|
||||
k.Key = core.Hex.EncodeToString(key[klen:])
|
||||
key = key[0:klen]
|
||||
}
|
||||
kstore.Key = enc.EncodeToString(key)
|
||||
return
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue