1package main
2
3import (
4 "fmt"
5
6 "github.com/bluesky-social/indigo/atproto/crypto"
7
8 "github.com/urfave/cli/v2"
9)
10
11var cmdKey = &cli.Command{
12 Name: "key",
13 Usage: "sub-commands for cryptographic keys",
14 Subcommands: []*cli.Command{
15 &cli.Command{
16 Name: "generate",
17 Usage: "outputs a new secret key",
18 Flags: []cli.Flag{
19 &cli.StringFlag{
20 Name: "type",
21 Aliases: []string{"t"},
22 Usage: "indicate curve type (P-256 is default)",
23 },
24 &cli.BoolFlag{
25 Name: "terse",
26 Usage: "print just the secret key, in multikey format",
27 },
28 },
29 Action: runKeyGenerate,
30 },
31 &cli.Command{
32 Name: "inspect",
33 Usage: "parses and outputs metadata about a public or secret key",
34 ArgsUsage: `<key>`,
35 Action: runKeyInspect,
36 },
37 },
38}
39
40func runKeyGenerate(cctx *cli.Context) error {
41 var priv crypto.PrivateKey
42 var privMultibase string
43 switch cctx.String("type") {
44 case "", "P-256", "p256", "ES256", "secp256r1":
45 sec, err := crypto.GeneratePrivateKeyP256()
46 if err != nil {
47 return err
48 }
49 privMultibase = sec.Multibase()
50 priv = sec
51 case "K-256", "k256", "ES256K", "secp256k1":
52 sec, err := crypto.GeneratePrivateKeyK256()
53 if err != nil {
54 return err
55 }
56 privMultibase = sec.Multibase()
57 priv = sec
58 default:
59 return fmt.Errorf("unknown key type: %s", cctx.String("type"))
60 }
61 if cctx.Bool("terse") {
62 fmt.Println(privMultibase)
63 return nil
64 }
65 pub, err := priv.PublicKey()
66 if err != nil {
67 return err
68 }
69 fmt.Printf("Key Type: %s\n", descKeyType(priv))
70 fmt.Printf("Secret Key (Multibase Syntax): save this securely (eg, add to password manager)\n\t%s\n", privMultibase)
71 fmt.Printf("Public Key (DID Key Syntax): share or publish this (eg, in DID document)\n\t%s\n", pub.DIDKey())
72 return nil
73}
74
75func descKeyType(val interface{}) string {
76 switch val.(type) {
77 case *crypto.PublicKeyP256, crypto.PublicKeyP256:
78 return "P-256 / secp256r1 / ES256 public key"
79 case *crypto.PrivateKeyP256, crypto.PrivateKeyP256:
80 return "P-256 / secp256r1 / ES256 private key"
81 case *crypto.PublicKeyK256, crypto.PublicKeyK256:
82 return "K-256 / secp256k1 / ES256K public key"
83 case *crypto.PrivateKeyK256, crypto.PrivateKeyK256:
84 return "K-256 / secp256k1 / ES256K private key"
85 default:
86 return "unknown"
87 }
88}
89
90func runKeyInspect(cctx *cli.Context) error {
91 s := cctx.Args().First()
92 if s == "" {
93 return fmt.Errorf("need to provide key as an argument")
94 }
95
96 sec, err := crypto.ParsePrivateMultibase(s)
97 if nil == err {
98 fmt.Printf("Type: %s\n", descKeyType(sec))
99 fmt.Printf("Encoding: multibase\n")
100 pub, err := sec.PublicKey()
101 if err != nil {
102 return err
103 }
104 fmt.Printf("Public (DID Key): %s\n", pub.DIDKey())
105 return nil
106 }
107
108 pub, err := crypto.ParsePublicMultibase(s)
109 if nil == err {
110 fmt.Printf("Type: %s\n", descKeyType(pub))
111 fmt.Printf("Encoding: multibase\n")
112 fmt.Printf("As DID Key: %s\n", pub.DIDKey())
113 return nil
114 }
115
116 pub, err = crypto.ParsePublicDIDKey(s)
117 if nil == err {
118 fmt.Printf("Type: %s\n", descKeyType(pub))
119 fmt.Printf("Encoding: DID Key\n")
120 fmt.Printf("As Multibase: %s\n", pub.Multibase())
121 return nil
122 }
123 return fmt.Errorf("unknown key encoding or type")
124}