package sshkeys import ( "crypto/rand" "errors" "fmt" // `golang.org/x/crypto/ssh/internal/bcrypt_pbkdf` Golang doesn't let you import "internal" libs. Fine. Lame language. "github.com/dchest/bcrypt_pbkdf" ) func (k *EncryptedSSHKeyV1) validate() error { if k.Passphrase == nil { return errors.New("cannot use encrypted key with empty passphrase") } var validCipher bool var validKDF bool var validKT bool for _, v := range allowed_ciphers { if v == k.CipherName { validCipher = true break } } for _, v := range allowed_kdfnames { if v == k.KDFName { validKDF = true break } } for _, v := range allowed_keytypes { if v == k.DefKeyType { validKT = true } } if !validCipher || !validKDF || !validKT { return errors.New("invalid CipherName, KDFName, or DefKeyType specified") } return nil } func (k *EncryptedSSHKeyV1) Generate(force bool) error { if k.DefKeyType == "" { k.DefKeyType = defKeyType } if k.KDFName == "" { k.KDFName = defKDF } if k.CipherName == "" { k.CipherName = defCipher } if err := k.validate(); err != nil { return err } if k.PrivateKeys != nil && !force { return nil // Already generated. } if k.KDFOpts.Salt == nil { k.KDFOpts.Salt = make([]byte, defSaltLen) if _, err := rand.Read(k.KDFOpts.Salt); err != nil { return err } } if k.KDFOpts.Rounds == 0 { k.KDFOpts.Rounds = defRounds } if k.DefKeyType == KeyRsa && k.BitSize == 0 { k.BitSize = defRSABitSize } else if k.DefKeyType == KeyEd25519 { k.BitSize = ed25519Len k.KeySize = keyEd25519 k.BlockSize = blockEd25519 } // Currently, OpenSSH has an option for multiple private keys. However, it is hardcoded to 1. // If multiple key support is added in the future, will need to re-tool how I do this, perhaps, in the future. TODO. pk := SSHPrivKey{ Comment: fmt.Sprintf("Autogenerated via SSHSecure (%v)", projUrl), } pk.Checksum = make([]byte, 4) if _, err := rand.Read(pk.Checksum); err != nil { return err } // Upstream only currently supports bcrypt_pbkdf ("bcrypt"). // This should always eval to true, but is here for future planning in case other KDF are implemented. if k.KDFName == KdfBcrypt { if pk.Key, err = bcrypt_pbkdf.Key(k.Passphrase, k.KDFOpts.Salt, int(k.KDFOpts.Rounds), int(k.KeySize)); err != nil { return err } } return nil } func (k *SSHKeyV1) validate() error { return nil } func (k *SSHKeyV1) GeneratePrivate(force bool) error { k.validate() if k.PrivateKeys != nil && !force { return nil // Already generated. } return nil }