Skip to content

Commit

Permalink
Add base64 and signature commands
Browse files Browse the repository at this point in the history
  • Loading branch information
thequailman committed Mar 14, 2024
1 parent 8d1f167 commit 00b8acd
Show file tree
Hide file tree
Showing 29 changed files with 402 additions and 109 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Rot makes cryptography easy:
- Store your encrypted values securely in git with human-readable diffs
- Generate and view X.509 certificates and Certificate Authorities
- Generate and view JWTs
- Generate and verify signatures
- Generate SSH keys and certificates

Visit https://rotx.dev for more information.
Expand Down
2 changes: 2 additions & 0 deletions go.work.sum
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,8 @@ github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qK
github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3 h1:lLT7ZLSzGLI08vc9cpd+tYmNWjdKDqyr/2L+f6U12Fk=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w=
github.com/jackc/pgx/v5 v5.5.4 h1:Xp2aQS8uXButQdnCMWNmvx6UysWQQC+u1EoizjguY+8=
github.com/jackc/pgx/v5 v5.5.4/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
Expand Down
2 changes: 1 addition & 1 deletion go/cmdAddPrivateKey.go → go/cmdAddPK.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
"github.com/candiddev/shared/go/logger"
)

func cmdAddPrivateKey() cli.Command[*cfg] {
func cmdAddPK() cli.Command[*cfg] {
return cli.Command[*cfg]{
ArgumentsRequired: []string{
"name",
Expand Down
71 changes: 71 additions & 0 deletions go/cmdBase64.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package main

import (
"context"
"encoding/base64"

"github.com/candiddev/shared/go/cli"
"github.com/candiddev/shared/go/errs"
"github.com/candiddev/shared/go/logger"
)

func cmdBase64() cli.Command[*cfg] {
return cli.Command[*cfg]{
ArgumentsRequired: []string{
"input value or - for stdin",
},
Flags: cli.Flags{
"d": {
Usage: "Decode base64 (default: encode)",
},
"r": {
Usage: "Raw/no padding (default: padding)",
},
"u": {
Usage: "URL encoding (default: standard encoding)",
},
},
Usage: "Encode/decode a base64 value or stdin and output to stdout.",
Run: func(ctx context.Context, args []string, f cli.Flags, c *cfg) errs.Err {
v := []byte(args[1])
if string(v) == "-" {
v = cli.ReadStdin()
}

_, d := f.Value("d")
_, url := f.Value("u")
_, r := f.Value("r")

var err error

var out []byte

switch {
case d && url && r:
out, err = base64.RawURLEncoding.DecodeString(string(v))
case d && r:
out, err = base64.RawStdEncoding.DecodeString(string(v))
case d && url:
out, err = base64.URLEncoding.DecodeString(string(v))
case d:
out, err = base64.StdEncoding.DecodeString(string(v))
case r && url:
out = []byte(base64.RawURLEncoding.EncodeToString(v))
case r:
out = []byte(base64.RawStdEncoding.EncodeToString(v))
case url:
out = []byte(base64.URLEncoding.EncodeToString(v))
default:
out = []byte(base64.StdEncoding.EncodeToString(v))
}

if err != nil {
return logger.Error(ctx, errs.ErrReceiver.Wrap(err))
}

logger.Stdout.Write(out) //nolint:errcheck

return nil
},
}
}
4 changes: 2 additions & 2 deletions go/cmdDecrypt.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ import (
func cmdDecrypt() cli.Command[*cfg] {
return cli.Command[*cfg]{
ArgumentsRequired: []string{
"value, or - for stdin",
"value or path",
},
Usage: "Decrypt a value and print it to stdout.",
Usage: "Decrypt a value or unwrap a KDF value and print it to stdout.",
Run: func(ctx context.Context, args []string, _ cli.Flags, c *cfg) errs.Err {
c.decryptKeysEncrypted(ctx)

Expand Down
11 changes: 8 additions & 3 deletions go/cmdGenerateCertificate.go → go/cmdGenCrt.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@ import (
"github.com/candiddev/shared/go/logger"
)

func cmdGenerateCertificate() cli.Command[*cfg] {
func cmdGenCrt() cli.Command[*cfg] {
return cli.Command[*cfg]{
ArgumentsRequired: []string{
"private key value, encrypted value name, or - for stdin",
},
ArgumentsOptional: []string{
"public key",
"public key value, encrypted value name, or path",
"ca certificate or path",
},
Flags: cli.Flags{
Expand Down Expand Up @@ -69,7 +69,12 @@ func cmdGenerateCertificate() cli.Command[*cfg] {
var publicKey cryptolib.Key[cryptolib.KeyProviderPublic]

if len(args) >= 3 {
publicKey, _ = cryptolib.ParseKey[cryptolib.KeyProviderPublic](args[2])
var err error

publicKey, err = c.publicKey(args[2])
if err != nil {
return logger.Error(ctx, errs.ErrReceiver.Wrap(err))
}
}

var ca *x509.Certificate
Expand Down
2 changes: 1 addition & 1 deletion go/cmdGenerateJWT.go → go/cmdGenJWT.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import (
"github.com/google/uuid"
)

func cmdGenerateJWT() cli.Command[*cfg] {
func cmdGenJWT() cli.Command[*cfg] {
return cli.Command[*cfg]{
ArgumentsRequired: []string{
"private key value, encrypted value name, or - for stdin",
Expand Down
12 changes: 2 additions & 10 deletions go/cmdGenerateSSH.go → go/cmdGenSSH.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package main
import (
"context"
"fmt"
"os"
"strconv"
"strings"

Expand All @@ -13,7 +12,7 @@ import (
"github.com/candiddev/shared/go/logger"
)

func cmdGenerateSSH() cli.Command[*cfg] {
func cmdGenSSH() cli.Command[*cfg] {
return cli.Command[*cfg]{
ArgumentsRequired: []string{
"private key value, encrypted value name, or - for stdin",
Expand Down Expand Up @@ -57,14 +56,7 @@ func cmdGenerateSSH() cli.Command[*cfg] {
return logger.Error(ctx, errr)
}

k := args[2]

fi, err := os.ReadFile(k)
if err == nil {
k = string(fi)
}

publicKey, err := cryptolib.ParseKey[cryptolib.KeyProviderPublic](k)
publicKey, err := c.publicKey(args[2])
if err != nil {
return logger.Error(ctx, errs.ErrReceiver.Wrap(err))
}
Expand Down
50 changes: 50 additions & 0 deletions go/cmdGenSig.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package main

import (
"context"
"encoding/base64"

"github.com/candiddev/shared/go/cli"
"github.com/candiddev/shared/go/cryptolib"
"github.com/candiddev/shared/go/errs"
"github.com/candiddev/shared/go/logger"
)

func cmdGenSig() cli.Command[*cfg] {
return cli.Command[*cfg]{
ArgumentsRequired: []string{
"private key value or encrypted value name",
"data to sign or - for stdin",
},
Flags: cli.Flags{
"s": {
Usage: "Output just the signature",
},
},
Usage: "Generate a signature and output a standard encoding base64 string. Must specify the private key of the signer and the data to be signed. For ECP keys, the hash will be SHA256. For Ed25519, the hash is unused.",
Run: func(ctx context.Context, args []string, f cli.Flags, c *cfg) errs.Err {
data := []byte(args[2])
if string(data) == "-" {
data = cli.ReadStdin()
}

privateKey, errr := c.decryptValuePrivateKey(ctx, args[1])
if errr != nil {
return logger.Error(ctx, errr)
}

s, err := cryptolib.NewSignature(privateKey, data)
if err != nil {
return logger.Error(ctx, errs.ErrReceiver.Wrap(err))
}

if _, sig := f.Value("s"); sig {
logger.Raw(base64.StdEncoding.EncodeToString(s.Signature))
} else {
logger.Raw(s.String() + "\n")
}

return nil
},
}
}
2 changes: 1 addition & 1 deletion go/cmdShowAlgorithms.go → go/cmdShowAlg.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
"github.com/candiddev/shared/go/errs"
)

func cmdAlgorithms() cli.Command[*cfg] {
func cmdShowAlg() cli.Command[*cfg] {
return cli.Command[*cfg]{
Usage: "Show algorithms Rot understands.",
Run: func(_ context.Context, _ []string, _ cli.Flags, _ *cfg) errs.Err {
Expand Down
2 changes: 1 addition & 1 deletion go/cmdShowCertificate.go → go/cmdShowCrt.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
"github.com/candiddev/shared/go/types"
)

func cmdShowCertificate() cli.Command[*cfg] {
func cmdShowCrt() cli.Command[*cfg] {
return cli.Command[*cfg]{
ArgumentsRequired: []string{
"Certificate value, path, or - for stdin",
Expand Down
4 changes: 2 additions & 2 deletions go/cmdShowJWT.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ func cmdShowJWT() cli.Command[*cfg] {
"JWT, or - for stdin",
},
ArgumentsOptional: []string{
"public key value, can be specified multiple times",
"public key value, encrypted value name, or path. Can be specified multiple times",
},
Usage: "Show a JWT, optionally validating the signature with a public key.",
Run: func(ctx context.Context, args []string, flags cli.Flags, config *cfg) errs.Err {
Expand All @@ -29,7 +29,7 @@ func cmdShowJWT() cli.Command[*cfg] {

if len(args) > 2 {
for i := range args[2:] {
key, err := cryptolib.ParseKey[cryptolib.KeyProviderPublic](args[i+2])
key, err := config.publicKey(args[i+2])
if err != nil {
return logger.Error(ctx, errs.ErrReceiver.Wrap(err))
}
Expand Down
2 changes: 1 addition & 1 deletion go/cmdShowPublicKey.go → go/cmdShowPK.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
"github.com/candiddev/shared/go/logger"
)

func cmdShowPublicKey() cli.Command[*cfg] {
func cmdShowPK() cli.Command[*cfg] {
return cli.Command[*cfg]{
ArgumentsRequired: []string{
"name, private key, or - for stdin",
Expand Down
45 changes: 45 additions & 0 deletions go/cmdVerifySig.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package main

import (
"context"

"github.com/candiddev/shared/go/cli"
"github.com/candiddev/shared/go/cryptolib"
"github.com/candiddev/shared/go/errs"
"github.com/candiddev/shared/go/logger"
)

func cmdVerifySig() cli.Command[*cfg] {
return cli.Command[*cfg]{
ArgumentsRequired: []string{
"public key value, encrypted value name, or path",
"message or - for stdin",
"signature",
},
Usage: "Verify a signature for a message using a public key. Signature must be in the form <hash>:<signature>:<optional key id>.",
Run: func(ctx context.Context, args []string, f cli.Flags, c *cfg) errs.Err {
pk, err := c.publicKey(args[1])
if err != nil {
return logger.Error(ctx, errs.ErrReceiver.Wrap(err))
}

m := []byte(args[2])
if string(m) == "-" {
m = cli.ReadStdin()
}

s, err := cryptolib.ParseSignature(args[3])
if err != nil {
return logger.Error(ctx, errs.ErrReceiver.Wrap(err))
}

if err := s.Verify(m, cryptolib.Keys[cryptolib.KeyProviderPublic]{
pk,
}); err != nil {
return logger.Error(ctx, errs.ErrReceiver.Wrap(err))
}

return nil
},
}
}
17 changes: 17 additions & 0 deletions go/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package main
import (
"context"
"errors"
"os"
"strings"
"time"

Expand Down Expand Up @@ -154,3 +155,19 @@ func (c *cfg) encryptvalue(ctx context.Context, value []byte, name, comment stri

return logger.Error(ctx, nil)
}

func (c *cfg) publicKey(value string) (cryptolib.Key[cryptolib.KeyProviderPublic], error) {
pk := value
if !strings.Contains(pk, ":") {
pk = c.Values[value].Comment

if pk == "" {
f, err := os.ReadFile(value)
if err == nil {
pk = string(f)
}
}
}

return cryptolib.ParseKey[cryptolib.KeyProviderPublic](pk)
}
Loading

0 comments on commit 00b8acd

Please sign in to comment.