Skip to content

Commit

Permalink
wip: key sign & verify
Browse files Browse the repository at this point in the history
  • Loading branch information
hacdias committed Nov 29, 2023
1 parent 2864f63 commit 3ef2748
Show file tree
Hide file tree
Showing 2 changed files with 162 additions and 0 deletions.
2 changes: 2 additions & 0 deletions core/commands/commands_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,8 @@ func TestCommands(t *testing.T) {
"/key/rename",
"/key/rm",
"/key/rotate",
"/key/sign",
"/key/verify",
"/log",
"/log/level",
"/log/ls",
Expand Down
160 changes: 160 additions & 0 deletions core/commands/keystore.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
migrations "github.com/ipfs/kubo/repo/fsrepo/migrations"
"github.com/libp2p/go-libp2p/core/crypto"
peer "github.com/libp2p/go-libp2p/core/peer"
mbase "github.com/multiformats/go-multibase"
)

var KeyCmd = &cmds.Command{
Expand Down Expand Up @@ -51,6 +52,8 @@ publish'.
"rename": keyRenameCmd,
"rm": keyRmCmd,
"rotate": keyRotateCmd,
"sign": keySignCmd,
"verify": keyVerifyCmd,
},
}

Expand Down Expand Up @@ -688,6 +691,163 @@ func keyOutputListEncoders() cmds.EncoderFunc {
})
}

type KeySignOutput struct {
Key string // CIDv1-Libp2p-Key
Signature string
}

var keySignCmd = &cmds.Command{
Helptext: cmds.HelpText{
Tagline: "Generates a signature for the given data with a specified key.",
},
Options: []cmds.Option{
cmds.StringOption("key", "k", "The name of the key to use for signing."),
},
Arguments: []cmds.Argument{
cmds.FileArg("data", true, false, "The data to sign.").EnableStdin(),
},
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
sk, err := getKeyForSignVerify(req, env)
if err != nil {
return err
}

pid, err := peer.IDFromPrivateKey(sk)
if err != nil {
return err
}

// Read given data.
data, err := readDataForSignVerify(req)
if err != nil {
return err
}

// Sign it!
sig, err := sk.Sign(data)
if err != nil {
return err
}

encoder, err := mbase.EncoderByName("base64url")
if err != nil {
return err
}

return res.Emit(&KeySignOutput{
Key: peer.ToCid(pid).String(),
Signature: encoder.Encode(sig),
})
},
Type: KeySignOutput{},
}

type KeyVerifyOutput struct {
Key string // CIDv1-Libp2p-Key
SignatureValid bool
}

var keyVerifyCmd = &cmds.Command{
Helptext: cmds.HelpText{
Tagline: "Verify that the given data and signature match.",
},
Options: []cmds.Option{
cmds.StringOption("key", "k", "The name of the key to use for signing."),
cmds.StringOption("signature", "s", "Multibase-encoded signature to verify."),
},
Arguments: []cmds.Argument{
cmds.FileArg("data", true, false, "The data to verify against the given signature.").EnableStdin(),
},
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
var (
pk crypto.PubKey
pid peer.ID
)

name, _ := req.Options["key"].(string)
if sk, err := getKeyForSignVerify(req, env); err == nil {
pk = sk.GetPublic()
pid, err = peer.IDFromPublicKey(pk)
if err != nil {
return err
}
} else if pid, err = peer.Decode(name); err == nil {
pk, err = pid.ExtractPublicKey()
if err != nil {
return err
}
} else {
return err
}

// Read signature
signatureString, _ := req.Options["signature"].(string)
_, signature, err := mbase.Decode(signatureString)
if err != nil {
return err
}

// Read given data.
data, err := readDataForSignVerify(req)
if err != nil {
return err
}

// Verify
valid, err := pk.Verify(data, signature)
if err != nil {
return err
}

return res.Emit(&KeyVerifyOutput{
Key: peer.ToCid(pid).String(),
SignatureValid: valid,
})
},
Type: KeyVerifyOutput{},
}

func getKeyForSignVerify(req *cmds.Request, env cmds.Environment) (crypto.PrivKey, error) {
name, _ := req.Options["key"].(string)
if name == "" || name == "self" {
node, err := cmdenv.GetNode(env)
if err != nil {
return nil, err
}

return node.PrivateKey, nil
} else {
cfgRoot, err := cmdenv.GetConfigRoot(env)
if err != nil {
return nil, err
}

// Signing is read-only: safe to read key without acquiring repo lock
// (this makes sign work when ipfs daemon is already running)
ksp := filepath.Join(cfgRoot, "keystore")
ks, err := keystore.NewFSKeystore(ksp)
if err != nil {
return nil, err
}

return ks.Get(name)
}
}

func readDataForSignVerify(req *cmds.Request) ([]byte, error) {
file, err := cmdenv.GetFileArg(req.Files.Entries())
if err != nil {
return nil, err
}
defer file.Close()

data, err := io.ReadAll(file)
if err != nil {
return nil, err
}
return append([]byte("libp2p-key signed message:"), data...), nil
}

// DaemonNotRunning checks to see if the ipfs repo is locked, indicating that
// the daemon is running, and returns and error if the daemon is running.
func DaemonNotRunning(req *cmds.Request, env cmds.Environment) error {
Expand Down

0 comments on commit 3ef2748

Please sign in to comment.