added get loop and new key type xor
This commit is contained in:
parent
8f350b4b02
commit
9e68b46346
8 changed files with 122 additions and 24 deletions
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
if app.ID == id && app.IP == ip {
|
||||||
s = app.Status
|
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]"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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))
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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,15 +79,23 @@ 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 loop {
|
||||||
|
key, err = c.GetKeyLoop(ctx, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
exit(err)
|
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 {
|
||||||
exit(err)
|
exit(err)
|
||||||
|
|
@ -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 == "" {
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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]
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue