Skip to content

Commit

Permalink
Merge pull request #7515 from ipfs/petar/rollover
Browse files Browse the repository at this point in the history
  • Loading branch information
aschmahmann committed Jul 15, 2020
2 parents b98f797 + 7dda766 commit 3d3c036
Show file tree
Hide file tree
Showing 4 changed files with 209 additions and 1 deletion.
1 change: 1 addition & 0 deletions cmd/ipfs/ipfs.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ var commandsClientCmd = commands.CommandsCmd(Root)
var localCommands = map[string]*cmds.Command{
"daemon": daemonCmd,
"init": initCmd,
"rotate": rotateCmd,
"commands": commandsClientCmd,
}

Expand Down
115 changes: 115 additions & 0 deletions cmd/ipfs/rotate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package main

import (
"fmt"
"io"
"os"

cmds "github.com/ipfs/go-ipfs-cmds"
config "github.com/ipfs/go-ipfs-config"
oldcmds "github.com/ipfs/go-ipfs/commands"
fsrepo "github.com/ipfs/go-ipfs/repo/fsrepo"
"github.com/ipfs/interface-go-ipfs-core/options"
)

const (
oldKeyOptionName = "oldkey"
)

var rotateCmd = &cmds.Command{
Helptext: cmds.HelpText{
Tagline: "Rotates the ipfs identity.",
ShortDescription: `
Generates a new ipfs identity and saves it to the ipfs config file.
The daemon must not be running when calling this command.
ipfs uses a repository in the local file system. By default, the repo is
located at ~/.ipfs. To change the repo location, set the $IPFS_PATH
environment variable:
export IPFS_PATH=/path/to/ipfsrepo
`,
},
Arguments: []cmds.Argument{},
Options: []cmds.Option{
cmds.StringOption(oldKeyOptionName, "o", "Keystore name for the old/rotated-out key."),
cmds.StringOption(algorithmOptionName, "a", "Cryptographic algorithm to use for key generation.").WithDefault(algorithmDefault),
cmds.IntOption(bitsOptionName, "b", "Number of bits to use in the generated RSA private key."),
},
PreRun: func(req *cmds.Request, env cmds.Environment) error {
cctx := env.(*oldcmds.Context)
daemonLocked, err := fsrepo.LockedByOtherProcess(cctx.ConfigRoot)
if err != nil {
return err
}

log.Info("checking if daemon is running...")
if daemonLocked {
log.Debug("ipfs daemon is running")
e := "ipfs daemon is running. please stop it to run this command"
return cmds.ClientError(e)
}

return nil
},
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
cctx := env.(*oldcmds.Context)
nBitsForKeypair, nBitsGiven := req.Options[bitsOptionName].(int)
algorithm, _ := req.Options[algorithmOptionName].(string)
oldKey, ok := req.Options[oldKeyOptionName].(string)
if !ok {
return fmt.Errorf("keystore name for backing up old key must be provided")
}
return doRotate(os.Stdout, cctx.ConfigRoot, oldKey, algorithm, nBitsForKeypair, nBitsGiven)
},
}

func doRotate(out io.Writer, repoRoot string, oldKey string, algorithm string, nBitsForKeypair int, nBitsGiven bool) error {
// Open repo
repo, err := fsrepo.Open(repoRoot)
if err != nil {
return fmt.Errorf("opening repo (%v)", err)
}
defer repo.Close()

// Read config file from repo
cfg, err := repo.Config()
if err != nil {
return fmt.Errorf("reading config from repo (%v)", err)
}

// Generate new identity
var identity config.Identity
if nBitsGiven {
identity, err = config.CreateIdentity(out, []options.KeyGenerateOption{
options.Key.Size(nBitsForKeypair),
options.Key.Type(algorithm),
})
} else {
identity, err = config.CreateIdentity(out, []options.KeyGenerateOption{
options.Key.Type(algorithm),
})
}
if err != nil {
return fmt.Errorf("creating identity (%v)", err)
}

// Save old identity to keystore
oldPrivKey, err := cfg.Identity.DecodePrivateKey("")
if err != nil {
return fmt.Errorf("decoding old private key (%v)", err)
}
keystore := repo.Keystore()
if err := keystore.Put(oldKey, oldPrivKey); err != nil {
return fmt.Errorf("saving old key in keystore (%v)", err)
}

// Update identity
cfg.Identity = identity

// Write config file to repo
if err = repo.SetConfig(cfg); err != nil {
return fmt.Errorf("saving new key to config (%v)", err)
}
return nil
}
2 changes: 1 addition & 1 deletion test/sharness/lib/test-lib.sh
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ test_init_ipfs() {

test_expect_success "ipfs init succeeds" '
export IPFS_PATH="$(pwd)/.ipfs" &&
ipfs init --profile=test -b=2048 > /dev/null
ipfs init --profile=test > /dev/null
'

test_expect_success "prepare config -- mounting" '
Expand Down
92 changes: 92 additions & 0 deletions test/sharness/t0027-rotate.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
#!/usr/bin/env bash

test_description="Test rotate command"

. lib/test-lib.sh

test_rotate() {
FROM_ALG=$1
TO_ALG=$2

test_expect_success "ipfs init (from $FROM_ALG, to $TO_ALG)" '
export IPFS_PATH="$(pwd)/.ipfs" &&
case $FROM_ALG in
rsa)
ipfs init --profile=test -a=rsa > /dev/null
;;
ed25519)
ipfs init --profile=test -a=ed25519 > /dev/null
;;
*)
ipfs init --profile=test > /dev/null
;;
esac
'

test_expect_success "Save first ID and key" '
ipfs id -f="<id>" > first_id &&
ipfs id -f="<pubkey>" > first_key
'

test_launch_ipfs_daemon

test_kill_ipfs_daemon

test_expect_success "rotating keys" '
case $TO_ALG in
rsa)
ipfs rotate -a=rsa -b=2048 --oldkey=oldkey
;;
ed25519)
ipfs rotate -a=ed25519 --oldkey=oldkey
;;
*)
ipfs rotate --oldkey=oldkey
;;
esac
'

test_expect_success "Compare second ID and key to first" '
ipfs id -f="<id>" > second_id &&
ipfs id -f="<pubkey>" > second_key &&
! test_cmp first_id second_id &&
! test_cmp first_key second_key
'

test_expect_success "checking ID" '
ipfs config Identity.PeerID > expected-id &&
ipfs id -f "<id>\n" > actual-id &&
ipfs key list -l | grep self | cut -d " " -f1 > keystore-id &&
ipfs key list -l | grep oldkey | cut -d " " -f1 | tr -d "\n" > old-keystore-id &&
test_cmp expected-id actual-id &&
test_cmp expected-id keystore-id &&
test_cmp old-keystore-id first_id
'

test_launch_ipfs_daemon

test_expect_success "publish name with new and old keys" '
echo "hello world" > msg &&
ipfs add msg | cut -d " " -f2 | tr -d "\n" > msg_hash &&
ipfs name publish --offline --allow-offline --key=self $(cat msg_hash) &&
ipfs name publish --offline --allow-offline --key=oldkey $(cat msg_hash)
'

test_kill_ipfs_daemon

test_expect_success "clean up ipfs dir" '
rm -rf "$IPFS_PATH"
'

}
test_rotate 'rsa' ''
test_rotate 'ed25519' ''
test_rotate '' ''
test_rotate 'rsa' 'rsa'
test_rotate 'ed25519' 'rsa'
test_rotate '' 'rsa'
test_rotate 'rsa' 'ed25519'
test_rotate 'ed25519' 'ed25519'
test_rotate '' 'ed25519'

test_done

0 comments on commit 3d3c036

Please sign in to comment.