-
-
Notifications
You must be signed in to change notification settings - Fork 3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #7515 from ipfs/petar/rollover
- Loading branch information
Showing
4 changed files
with
209 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |