1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225
|
package nacl
import (
"crypto/rand"
"fmt"
"io/ioutil"
"os"
"github.com/pkg/errors"
"github.com/smallstep/cli/command"
"github.com/smallstep/cli/errs"
"github.com/smallstep/cli/flags"
"github.com/smallstep/cli/ui"
"github.com/smallstep/cli/utils"
"github.com/urfave/cli"
"golang.org/x/crypto/nacl/sign"
)
func signCommand() cli.Command {
return cli.Command{
Name: "sign",
Usage: "sign small messages using public-key cryptography",
UsageText: "step crypto nacl sign <subcommand> [arguments] [global-flags] [subcommand-flags]",
Description: `**step crypto nacl sign** command group uses public-key cryptography to sign and
verify messages. The implementation is based on NaCl's crypto_sign function.
NaCl crypto_sign is crypto_sign_edwards25519sha512batch, a particular
combination of Curve25519 in Edwards form and SHA-512 into a signature scheme
suitable for high-speed batch verification. This function is conjectured to meet
the standard notion of unforgeability under chosen-message attacks.
These commands are interoperable with NaCl: https://nacl.cr.yp.to/sign.html
## EXAMPLES
Create a keypair for verifying and signing messages:
'''
$ step crypto nacl sign keypair nacl.sign.pub nacl.sign.priv
'''
Sign a message using the private key:
'''
$ step crypto nacl sign sign nacl.sign.priv
Please enter text to sign: ********
rNrOfqsv4svlRnVPSVYe2REXodL78yEMHtNkzAGNp4MgHuVGoyayp0zx4D5rjTzYVVrD2HRP306ZILT62ohvCG1lc3NhZ2U
$ cat message.txt | step crypto nacl sign sign ~/step/keys/nacl.recipient.sign.priv
rNrOfqsv4svlRnVPSVYe2REXodL78yEMHtNkzAGNp4MgHuVGoyayp0zx4D5rjTzYVVrD2HRP306ZILT62ohvCG1lc3NhZ2U
'''
Verify the signed message using the public key:
'''
$ echo rNrOfqsv4svlRnVPSVYe2REXodL78yEMHtNkzAGNp4MgHuVGoyayp0zx4D5rjTzYVVrD2HRP306ZILT62ohvCG1lc3NhZ2U \
| step crypto nacl sign open nacl.sign.pub
message
'''`,
Subcommands: cli.Commands{
signKeypairCommand(),
signOpenCommand(),
signSignCommand(),
},
}
}
func signKeypairCommand() cli.Command {
return cli.Command{
Name: "keypair",
Action: command.ActionFunc(signKeypairAction),
Usage: "generate a pair for use with sign and open",
UsageText: "**step crypto nacl sign keypair** <pub-file> <priv-file>",
Description: `**step crypto nacl sign keypair** generates a secret key and a corresponding
public key valid for verifying and signing messages.
This command uses an implementation of NaCl's crypto_sign_keypair function.
For examples, see **step help crypto nacl sign**.`,
Flags: []cli.Flag{flags.Force},
}
}
func signOpenCommand() cli.Command {
return cli.Command{
Name: "open",
Action: cli.ActionFunc(signOpenAction),
Usage: "verify a signed message produced by sign",
UsageText: "**step crypto nacl sign open** <pub-file>",
Description: `**step crypto nacl sign open** verifies the signature of a message using the
signer's public key.
This command uses an implementation of NaCl's crypto_sign_open function.
For examples, see **step help crypto nacl sign**.`,
Flags: []cli.Flag{
cli.BoolFlag{
Name: "raw",
Usage: "Indicates that input is not base64 encoded",
},
},
}
}
func signSignCommand() cli.Command {
return cli.Command{
Name: "sign",
Action: cli.ActionFunc(signSignAction),
Usage: "sign a message using Ed25519",
UsageText: "**step crypto nacl sign sign** <priv-file>",
Description: `**step crypto nacl sign sign** signs a message m using the signer's private
key.
This command uses an implementation of NaCl's crypto_sign function.
For examples, see **step help crypto nacl sign**.`,
Flags: []cli.Flag{
cli.BoolFlag{
Name: "raw",
Usage: "Do not base64 encode output",
},
},
}
}
func signKeypairAction(ctx *cli.Context) error {
if err := errs.NumberOfArguments(ctx, 2); err != nil {
return err
}
args := ctx.Args()
pubFile, privFile := args[0], args[1]
if pubFile == privFile {
return errs.EqualArguments(ctx, "<pub-file>", "<priv-file>")
}
pub, priv, err := sign.GenerateKey(rand.Reader)
if err != nil {
return errors.Wrap(err, "error generating key")
}
if err := utils.WriteFile(pubFile, pub[:], 0600); err != nil {
return errs.FileError(err, pubFile)
}
if err := utils.WriteFile(privFile, priv[:], 0600); err != nil {
return errs.FileError(err, privFile)
}
ui.Printf("Your public key has been saved in %s.\n", pubFile)
ui.Printf("Your private key has been saved in %s.\n", privFile)
return nil
}
func signOpenAction(ctx *cli.Context) error {
if err := errs.NumberOfArguments(ctx, 1); err != nil {
return err
}
pubFile := ctx.Args().Get(0)
pub, err := ioutil.ReadFile(pubFile)
if err != nil {
return errs.FileError(err, pubFile)
} else if len(pub) != 32 {
return errors.New("invalid public key: key size is not 32 bytes")
}
input, err := utils.ReadInput("Write signed message to open")
if err != nil {
return errors.Wrap(err, "error reading input")
}
var rawInput []byte
if ctx.Bool("raw") {
rawInput = input
} else {
// DecodeLen returns the maximum length,
// Decode will return the actual length.
rawInput = make([]byte, b64Encoder.DecodedLen(len(input)))
n, err := b64Encoder.Decode(rawInput, input)
if err != nil {
return errors.Wrap(err, "error decoding base64 input")
}
rawInput = rawInput[:n]
}
var pb [32]byte
copy(pb[:], pub)
raw, ok := sign.Open(nil, rawInput, &pb)
if !ok {
return errors.New("error authenticating input")
}
os.Stdout.Write(raw)
return nil
}
func signSignAction(ctx *cli.Context) error {
if err := errs.NumberOfArguments(ctx, 1); err != nil {
return err
}
privFile := ctx.Args().Get(0)
priv, err := ioutil.ReadFile(privFile)
if err != nil {
return errs.FileError(err, privFile)
} else if len(priv) != 64 {
return errors.New("invalid private key: key size is not 64 bytes")
}
input, err := utils.ReadInput("Please enter text to sign")
if err != nil {
return errors.Wrap(err, "error reading input")
}
var pv [64]byte
copy(pv[:], priv)
raw := sign.Sign(nil, input, &pv)
if ctx.Bool("raw") {
os.Stdout.Write(raw)
} else {
fmt.Println(b64Encoder.EncodeToString(raw))
}
return nil
}
|