Refactoring masterpassphrase cmd to encryptionkey

The below code changes the `airshipctl secret generate masterpassphrase`
command to `airshipctl secret generate encryptionkey`. This command will
be further enhanced to support regex fields while generating encryption key.

Relates-To: #398
Change-Id: I87626cb410fd19beeb8cd23a5b86d61c48b63e63
This commit is contained in:
Sirisha Gopigiri
2020-11-10 13:18:49 +05:30
parent 9c9369088c
commit 62ae2133ba
9 changed files with 98 additions and 52 deletions

View File

@@ -12,28 +12,27 @@
limitations under the License.
*/
package generate
package encryptionkey
import (
"fmt"
"github.com/spf13/cobra"
"opendev.org/airship/airshipctl/pkg/secret"
"opendev.org/airship/airshipctl/pkg/secret/generate"
)
// NewGenerateMasterPassphraseCommand creates a new command for generating secret information
func NewGenerateMasterPassphraseCommand() *cobra.Command {
masterPassphraseCmd := &cobra.Command{
Use: "masterpassphrase",
// TODO(howell): Make this more expressive
Short: "Generates a secure master passphrase",
// NewGenerateEncryptionKeyCommand creates a new command for generating secret information
func NewGenerateEncryptionKeyCommand() *cobra.Command {
encryptionKeyCmd := &cobra.Command{
Use: "encryptionkey",
Short: "Generates a secure encryption key or passphrase",
Run: func(cmd *cobra.Command, args []string) {
engine := secret.NewPassphraseEngine(nil)
masterPassphrase := engine.GeneratePassphrase()
fmt.Fprintln(cmd.OutOrStdout(), masterPassphrase)
engine := generate.NewEncryptionKeyEngine(nil)
encryptionKey := engine.GenerateEncryptionKey()
fmt.Fprintln(cmd.OutOrStdout(), encryptionKey)
},
}
return masterPassphraseCmd
return encryptionKeyCmd
}

View File

@@ -0,0 +1,36 @@
/*
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package encryptionkey_test
import (
"testing"
"opendev.org/airship/airshipctl/cmd/secret/generate/encryptionkey"
"opendev.org/airship/airshipctl/testutil"
)
func TestGenerateEncryptionKey(t *testing.T) {
cmdTests := []*testutil.CmdTest{
{
Name: "generate-encryptionkey-cmd-with-help",
CmdLine: "--help",
Cmd: encryptionkey.NewGenerateEncryptionKeyCommand(),
},
}
for _, tt := range cmdTests {
testutil.RunTest(t, tt)
}
}

View File

@@ -0,0 +1,7 @@
Generates a secure encryption key or passphrase
Usage:
encryptionkey [flags]
Flags:
-h, --help help for encryptionkey

View File

@@ -14,7 +14,11 @@
package generate
import "github.com/spf13/cobra"
import (
"github.com/spf13/cobra"
"opendev.org/airship/airshipctl/cmd/secret/generate/encryptionkey"
)
// NewGenerateCommand creates a new command for generating secret information
func NewGenerateCommand() *cobra.Command {
@@ -24,7 +28,7 @@ func NewGenerateCommand() *cobra.Command {
Short: "Generate various secrets",
}
generateRootCmd.AddCommand(NewGenerateMasterPassphraseCommand())
generateRootCmd.AddCommand(encryptionkey.NewGenerateEncryptionKeyCommand())
return generateRootCmd
}

View File

@@ -22,5 +22,5 @@ Generate various secrets
### SEE ALSO
* [airshipctl secret](airshipctl_secret.md) - Manage secrets
* [airshipctl secret generate masterpassphrase](airshipctl_secret_generate_masterpassphrase.md) - Generates a secure master passphrase
* [airshipctl secret generate encryptionkey](airshipctl_secret_generate_encryptionkey.md) - Generates a secure encryption key or passphrase

View File

@@ -1,19 +1,19 @@
## airshipctl secret generate masterpassphrase
## airshipctl secret generate encryptionkey
Generates a secure master passphrase
Generates a secure encryption key or passphrase
### Synopsis
Generates a secure master passphrase
Generates a secure encryption key or passphrase
```
airshipctl secret generate masterpassphrase [flags]
airshipctl secret generate encryptionkey [flags]
```
### Options
```
-h, --help help for masterpassphrase
-h, --help help for encryptionkey
```
### Options inherited from parent commands

View File

@@ -12,7 +12,7 @@
limitations under the License.
*/
package secret
package generate
import (
"math/rand"
@@ -41,59 +41,59 @@ func init() {
pool = append(pool, []byte(asciiSymbols)...)
}
// PassphraseEngine is used to generate secure random passphrases
type PassphraseEngine struct {
// EncryptionKeyEngine is used to generate secure random passphrases
type EncryptionKeyEngine struct {
rng *rand.Rand
pool []byte
}
// NewPassphraseEngine creates an PassphraseEngine using src. If src is nil,
// NewEncryptionKeyEngine creates an PassphraseEngine using src. If src is nil,
// the returned PassphraseEngine will use the default Source
func NewPassphraseEngine(src rand.Source) *PassphraseEngine {
func NewEncryptionKeyEngine(src rand.Source) *EncryptionKeyEngine {
if src == nil {
src = &Source{}
}
return &PassphraseEngine{
return &EncryptionKeyEngine{
rng: rand.New(src),
pool: pool,
}
}
// GeneratePassphrase returns a secure random string of length 24,
// GenerateEncryptionKey returns a secure random string of length 24,
// containing at least one of each from the following sets:
// [0-9]
// [a-z]
// [A-Z]
// [@#&-+=?]
func (e *PassphraseEngine) GeneratePassphrase() string {
return e.GeneratePassphraseN(defaultLength)
func (e *EncryptionKeyEngine) GenerateEncryptionKey() string {
return e.GenerateEncryptionKeyN(defaultLength)
}
// GeneratePassphraseN returns a secure random string containing at least
// GenerateEncryptionKeyN returns a secure random string containing at least
// one of each from the following sets. Its length will be max(length, 24)
// [0-9]
// [a-z]
// [A-Z]
// [@#&-+=?]
func (e *PassphraseEngine) GeneratePassphraseN(length int) string {
func (e *EncryptionKeyEngine) GenerateEncryptionKeyN(length int) string {
if length < defaultLength {
length = defaultLength
}
var passPhrase string
for !e.isValidPassphrase(passPhrase) {
var encryptionkey string
for !e.isValidEncryptionKey(encryptionkey) {
var sb strings.Builder
for i := 0; i < length; i++ {
randIndex := e.rng.Intn(len(e.pool))
randChar := e.pool[randIndex]
sb.WriteString(string(randChar))
}
passPhrase = sb.String()
encryptionkey = sb.String()
}
return passPhrase
return encryptionkey
}
func (e *PassphraseEngine) isValidPassphrase(passPhrase string) bool {
if len(passPhrase) < defaultLength {
func (e *EncryptionKeyEngine) isValidEncryptionKey(encryptionkey string) bool {
if len(encryptionkey) < defaultLength {
return false
}
@@ -104,7 +104,7 @@ func (e *PassphraseEngine) isValidPassphrase(passPhrase string) bool {
asciiSymbols,
}
for _, charSet := range charSets {
if !strings.ContainsAny(passPhrase, charSet) {
if !strings.ContainsAny(encryptionkey, charSet) {
return false
}
}

View File

@@ -12,7 +12,7 @@
limitations under the License.
*/
package secret_test
package generate_test
import (
"math/rand"
@@ -21,7 +21,7 @@ import (
"github.com/stretchr/testify/assert"
"opendev.org/airship/airshipctl/pkg/secret"
"opendev.org/airship/airshipctl/pkg/secret/generate"
)
const (
@@ -33,13 +33,13 @@ const (
defaultLength = 24
)
func TestDeterministicGenerateValidPassphrase(t *testing.T) {
func TestDeterministicGenerateValidEncryptionKey(t *testing.T) {
assert := assert.New(t)
testSource := rand.NewSource(42)
engine := secret.NewPassphraseEngine(testSource)
engine := generate.NewEncryptionKeyEngine(testSource)
// pre-calculated for rand.NewSource(42)
expectedPassphrases := []string{
expectedEncryptionKey := []string{
"erx&97vfqd7LN3HJ?t@oPhds",
"##Xeuvf5Njy@hNWSaRoleFkf",
"jB=kirg7acIt-=fx1Fb-tZ+7",
@@ -52,13 +52,13 @@ func TestDeterministicGenerateValidPassphrase(t *testing.T) {
"XC?bDaHTa3mrBYLMG@#B=Q0B",
}
for i, expected := range expectedPassphrases {
actual := engine.GeneratePassphrase()
for i, expected := range expectedEncryptionKey {
actual := engine.GenerateEncryptionKey()
assert.Equal(expected, actual, "Test #%d failed", i)
}
}
func TestNondeterministicGenerateValidPassphrase(t *testing.T) {
func TestNondeterministicGenerateValidEncryptionKey(t *testing.T) {
assert := assert.New(t)
// Due to the nondeterminism of random number generators, this
// functionality is impossible to fully test. Let's just generate
@@ -72,9 +72,9 @@ func TestNondeterministicGenerateValidPassphrase(t *testing.T) {
asciiSymbols,
}
engine := secret.NewPassphraseEngine(nil)
engine := generate.NewEncryptionKeyEngine(nil)
for i := 0; i < 10000; i++ {
passphrase := engine.GeneratePassphrase()
passphrase := engine.GenerateEncryptionKey()
assert.Truef(len(passphrase) >= defaultLength,
"%s does not meet the length requirement", passphrase)
@@ -86,10 +86,10 @@ func TestNondeterministicGenerateValidPassphrase(t *testing.T) {
}
}
func TestGenerateValidPassphraseN(t *testing.T) {
func TestGenerateValidEncryptionKeyN(t *testing.T) {
assert := assert.New(t)
testSource := rand.NewSource(42)
engine := secret.NewPassphraseEngine(testSource)
engine := generate.NewEncryptionKeyEngine(testSource)
tests := []struct {
inputLegth int
expectedLength int
@@ -109,7 +109,7 @@ func TestGenerateValidPassphraseN(t *testing.T) {
}
for _, tt := range tests {
passphrase := engine.GeneratePassphraseN(tt.inputLegth)
passphrase := engine.GenerateEncryptionKeyN(tt.inputLegth)
assert.Len(passphrase, tt.expectedLength)
}
}

View File

@@ -12,7 +12,7 @@
limitations under the License.
*/
package secret
package generate
import (
crypto "crypto/rand"