added get loop and new key type xor

This commit is contained in:
ston1th 2023-03-22 22:52:19 +01:00
commit 9e68b46346
8 changed files with 122 additions and 24 deletions

View file

@ -15,6 +15,8 @@ import (
"os" "os"
"strings" "strings"
"time" "time"
"git.giftfish.de/ston1th/keyctl/pkg/api/types"
) )
type Client struct { type Client struct {
@ -181,14 +183,47 @@ func (c *Client) Do(r *http.Request, v any) (resp *http.Response, err error) {
} }
func (c *Client) FollowRedirect(r *http.Request, v any, delay time.Duration) (resp *http.Response, err error) { func (c *Client) FollowRedirect(r *http.Request, v any, delay time.Duration) (resp *http.Response, err error) {
rr := r ctx := r.Context()
rr := r.Clone(ctx)
for { for {
resp, err = c.Do(rr, v) resp, err = c.Do(rr, v)
if err == nil && resp.StatusCode == http.StatusOK {
return
}
rdr, ok := err.(Redirect) rdr, ok := err.(Redirect)
if !ok { if !ok {
return return
} }
rr, err = c.NewRequest(r.Context(), "GET", rdr.Location, nil) rr, err = c.NewRequest(ctx, "GET", rdr.Location, nil)
if err != nil {
return
}
time.Sleep(delay)
}
}
func (c *Client) FollowRedirectLoop(r *http.Request, v any, delay time.Duration) (resp *http.Response, err error) {
ctx := r.Context()
rr := r.Clone(ctx)
for {
resp, err = c.Do(rr, v)
rdr, ok := err.(Redirect)
if err != nil && !ok {
if errors.Is(err, types.ErrISE) ||
errors.Is(err, types.ErrKeyNotFound) {
return
}
if errors.Is(err, types.ErrReqNotFound) ||
errors.Is(err, types.ErrKeyRequestRejected) {
rr = r.Clone(ctx)
}
time.Sleep(delay)
continue
}
if err == nil && resp.StatusCode == http.StatusOK {
return
}
rr, err = c.NewRequest(ctx, "GET", rdr.Location, nil)
if err != nil { if err != nil {
return return
} }

View file

@ -54,8 +54,14 @@ func (a *Approver) List() (m ApprovalMap) {
} }
func (a *Approver) Status(id, aid, ip string) (s Status) { func (a *Approver) Status(id, aid, ip string) (s Status) {
a.mu.RLock() a.mu.RLock()
if app, ok := a.m[aid]; ok && app.ID == id && app.IP == ip { if app, ok := a.m[aid]; ok {
s = app.Status if app.ID == id && app.IP == ip {
s = app.Status
} else {
s = Rejected
}
} else {
s = NotFound
} }
a.mu.RUnlock() a.mu.RUnlock()
if s == Approved || s == Rejected { if s == Approved || s == Rejected {
@ -66,13 +72,16 @@ func (a *Approver) Status(id, aid, ip string) (s Status) {
return return
} }
func (a *Approver) Update(aid string, s Status) { func (a *Approver) Update(aid string, s Status) (err error) {
a.mu.Lock() a.mu.Lock()
if app, ok := a.m[aid]; ok { if app, ok := a.m[aid]; ok {
app.Status = s app.Status = s
a.m[aid] = app a.m[aid] = app
} else {
err = ErrReqNotFound
} }
a.mu.Unlock() a.mu.Unlock()
return
} }
type Approval struct { type Approval struct {
@ -88,6 +97,7 @@ const (
Pending Status = iota Pending Status = iota
Approved Approved
Rejected Rejected
NotFound
) )
func (s Status) String() string { func (s Status) String() string {
@ -98,6 +108,8 @@ func (s Status) String() string {
return "approved" return "approved"
case Rejected: case Rejected:
return "rejected" return "rejected"
case NotFound:
return "notfound"
} }
return "[invalid]" return "[invalid]"
} }

View file

@ -83,6 +83,16 @@ func (c *Client) GetKey(ctx context.Context, id string) (key schema.Key, err err
return return
} }
func (c *Client) GetKeyLoop(ctx context.Context, id string) (key schema.Key, err error) {
path := fmt.Sprintf("%s/%s", keyPath, id)
req, err := c.c.NewRequest(ctx, "GET", path, nil)
if err != nil {
return
}
_, err = c.c.FollowRedirectLoop(req, &key, time.Second*2)
return
}
func (c *Client) CreateKey(ctx context.Context, newKey schema.NewKey) (key schema.Key, err error) { func (c *Client) CreateKey(ctx context.Context, newKey schema.NewKey) (key schema.Key, err error) {
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
path := keyPath path := keyPath

View file

@ -90,7 +90,7 @@ func (Keys) Less(a, b Key) bool { return a.Name < b.Name }
func (k Keys) Sort() { slices.SortStableFunc(k, k.Less) } func (k Keys) Sort() { slices.SortStableFunc(k, k.Less) }
func (k Keys) Len() int { return len(k) } func (k Keys) Len() int { return len(k) }
func (k Keys) Fields() string { func (k Keys) Fields() string {
return "ID\t Name\t Size\t Encoding\t Type" return "Key ID\t Name\t Size\t Encoding\t Type"
} }
func (k Keys) Values() []string { func (k Keys) Values() []string {
vals := make([]string, len(k)) vals := make([]string, len(k))
@ -128,7 +128,7 @@ func (Approvals) Less(a, b Approval) bool { return a.Name < b.Name }
func (a Approvals) Sort() { slices.SortStableFunc(a, a.Less) } func (a Approvals) Sort() { slices.SortStableFunc(a, a.Less) }
func (a Approvals) Len() int { return len(a) } func (a Approvals) Len() int { return len(a) }
func (a Approvals) Fields() string { func (a Approvals) Fields() string {
return "ID\t Approval ID\t Name\t Status\t IP" return "Key ID\t Approval ID\t Name\t Status\t IP"
} }
func (a Approvals) Values() []string { func (a Approvals) Values() []string {
vals := make([]string, len(a)) vals := make([]string, len(a))

View file

@ -64,7 +64,7 @@ func keyHandler(ctx *types.Context) {
if err != nil { if err != nil {
ctx.Log.Error(err, "error reading key", "id", id) ctx.Log.Error(err, "error reading key", "id", id)
if err == store.ErrKeyNotFound { if err == store.ErrKeyNotFound {
ctx.Err(types.ErrNotFound) ctx.Err(types.ErrKeyNotFound)
return return
} }
ctx.Err(types.ErrISE) ctx.Err(types.ErrISE)
@ -77,7 +77,7 @@ func keyHandler(ctx *types.Context) {
aid, err = ctx.Data.Approver.New(id, k.Name, ip) aid, err = ctx.Data.Approver.New(id, k.Name, ip)
if err != nil { if err != nil {
ctx.Log.Error(err, "error creating approval", "id", id, "name", k.Name) ctx.Log.Error(err, "error creating approval", "id", id, "name", k.Name)
ctx.Err(types.ErrISE) ctx.Err(types.ErrTooManyApprovals)
return return
} }
ctx.Redirect(schema.ApproveURL(id, aid), http.StatusFound) ctx.Redirect(schema.ApproveURL(id, aid), http.StatusFound)
@ -91,6 +91,9 @@ func keyHandler(ctx *types.Context) {
case types.Rejected: case types.Rejected:
ctx.Err(types.ErrKeyRequestRejected) ctx.Err(types.ErrKeyRequestRejected)
return return
case types.NotFound:
ctx.Err(types.ErrReqNotFound)
return
} }
k, err := ctx.Data.DB.GetKeyWithSecret(id) k, err := ctx.Data.DB.GetKeyWithSecret(id)
if err != nil { if err != nil {
@ -142,6 +145,11 @@ func reqHandler(ctx *types.Context) {
case "DELETE": case "DELETE":
status = types.Rejected status = types.Rejected
} }
ctx.Data.Approver.Update(id, status) err := ctx.Data.Approver.Update(id, status)
if err != nil {
ctx.Log.Error(err, "error updating key request")
ctx.Err(types.ErrReqNotFound)
return
}
ctx.OK() ctx.OK()
} }

View file

@ -20,7 +20,7 @@ import (
var fs = flag.NewFlagSet("", flag.ExitOnError) var fs = flag.NewFlagSet("", flag.ExitOnError)
func exit(err error) { func exit(err error) {
fmt.Println(err) fmt.Fprintf(os.Stderr, "%s\n", err)
os.Exit(1) os.Exit(1)
} }
@ -44,7 +44,8 @@ func Run(arg string) {
exit(err) exit(err)
} }
fmt.Println("Key ID:", k.ID) fmt.Println("Key ID:", k.ID)
if k.Type == core.Shamir { if k.Type == core.Shamir || k.Type == core.Xor {
fmt.Println("Key Type:", k.Type)
fmt.Println("Key Share:", k.Key) fmt.Println("Key Share:", k.Key)
} }
case "del": case "del":
@ -78,14 +79,22 @@ func Run(arg string) {
exit(err) exit(err)
} }
case "get": case "get":
id, share, json, err := getFlags() id, share, loop, json, err := getFlags()
if err != nil { if err != nil {
exit(err) exit(err)
} }
c := client() c := client()
key, err := c.GetKey(ctx, id) var key schema.Key
if err != nil { if loop {
exit(err) key, err = c.GetKeyLoop(ctx, id)
if err != nil {
exit(err)
}
} else {
key, err = c.GetKey(ctx, id)
if err != nil {
exit(err)
}
} }
out, err := key.Print(share) out, err := key.Print(share)
if err != nil { if err != nil {
@ -153,9 +162,10 @@ func idFlags(desc string) (id string, err error) {
return return
} }
func getFlags() (id, share string, json bool, err error) { func getFlags() (id, share string, loop, json bool, err error) {
fs.StringVar(&id, "id", "", "key id") fs.StringVar(&id, "id", "", "key id")
fs.StringVar(&share, "share", "", "shamir key share") fs.StringVar(&share, "share", "", "shamir key share")
fs.BoolVar(&loop, "loop", false, "ignore network errors and loop until key is received")
fs.BoolVar(&json, "json", false, "json output") fs.BoolVar(&json, "json", false, "json output")
fs.Parse(os.Args[2:]) fs.Parse(os.Args[2:])
if id == "" { if id == "" {
@ -176,7 +186,7 @@ func outputFlags() (json bool) {
func newKeyFlags() (name, encoding, t string, size int, err error) { func newKeyFlags() (name, encoding, t string, size int, err error) {
fs.StringVar(&name, "name", "", "key name") fs.StringVar(&name, "name", "", "key name")
fs.StringVar(&encoding, "enc", "hex", "key encoding scheme [hex|base32|base64|base64url]") fs.StringVar(&encoding, "enc", "hex", "key encoding scheme [hex|base32|base64|base64url]")
fs.StringVar(&t, "type", "plain", "key type [plain|shamir]") fs.StringVar(&t, "type", "plain", "key type [plain|shamir|xor]")
fs.IntVar(&size, "size", 16, "key size in bytes") fs.IntVar(&size, "size", 16, "key size in bytes")
fs.Parse(os.Args[2:]) fs.Parse(os.Args[2:])
if name == "" { if name == "" {

View file

@ -13,12 +13,15 @@ type Type int
const ( const (
Plain Type = iota Plain Type = iota
Shamir Shamir
Xor
) )
func (t Type) String() string { func (t Type) String() string {
switch t { switch t {
case Shamir: case Shamir:
return "shamir" return "shamir"
case Xor:
return "xor"
} }
return "plain" return "plain"
} }
@ -26,14 +29,27 @@ func (t Type) String() string {
func (t Type) Decode(src []byte) ([]byte, error) { func (t Type) Decode(src []byte) ([]byte, error) {
switch t { switch t {
case Shamir: case Shamir:
plen := len(src) / 2 p1, p2 := split(src)
part1 := src[0:plen] return shamir.Combine([][]byte{p1, p2})
part2 := src[plen:] case Xor:
return shamir.Combine([][]byte{part1, part2}) return xor(src), nil
} }
return src, nil return src, nil
} }
func split(src []byte) (p1, p2 []byte) {
plen := len(src) / 2
return src[0:plen], src[plen:]
}
func xor(src []byte) []byte {
p1, p2 := split(src)
for i := 0; i < len(p1); i++ {
p1[i] ^= p2[i]
}
return p1
}
func (t Type) Encode(src []byte) ([]byte, error) { func (t Type) Encode(src []byte) ([]byte, error) {
switch t { switch t {
case Shamir: case Shamir:
@ -50,6 +66,8 @@ func TypeFromString(t string) Type {
switch t { switch t {
case "shamir": case "shamir":
return Shamir return Shamir
case "xor":
return Xor
} }
return Plain return Plain
} }

View file

@ -31,7 +31,12 @@ func Generate(name string, size int, enc core.Encoding, t core.Type) (kstore, k
if err != nil { if err != nil {
return return
} }
key, err := generate(size) var key []byte
if t == core.Xor {
key, err = generate(size * 2)
} else {
key, err = generate(size)
}
if err != nil { if err != nil {
return return
} }
@ -47,7 +52,7 @@ func Generate(name string, size int, enc core.Encoding, t core.Type) (kstore, k
Type: t, Type: t,
} }
k = kstore k = kstore
if t == core.Shamir { if t == core.Shamir || t == core.Xor {
klen := len(key) / 2 klen := len(key) / 2
k.Key = core.Hex.EncodeToString(key[klen:]) k.Key = core.Hex.EncodeToString(key[klen:])
key = key[0:klen] key = key[0:klen]