checking in some more research and primitive functions. i cannot, for the life of me, figure out why i can't (seemingly) properly decrypt private keys.
This commit is contained in:
parent
5f5d77a2a6
commit
6b956a3a49
3
TODO
Normal file
3
TODO
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
-sshkeys (need to figure out generation process)
|
||||||
|
-moduli dhparams generation (dh.c? moduli.c?)
|
||||||
|
--ssh-keygen.c, ~L3565
|
14
const.go
14
const.go
@ -1,14 +0,0 @@
|
|||||||
package sshsecure
|
|
||||||
|
|
||||||
import (
|
|
||||||
"git.square-r00t.net/sshsecure/sshkeys"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
RoundsDefUser uint = 100
|
|
||||||
RoundsDefHost uint = 100
|
|
||||||
RSABitSize uint = 4096
|
|
||||||
DefKeyType string = sshkeys.KEY_ED25519
|
|
||||||
DefCipher string = sshkeys.CIPHER_AES256_CTR
|
|
||||||
DefKDF string = sshkeys.KDF_BCRYPT
|
|
||||||
)
|
|
5
go.mod
5
go.mod
@ -1,3 +1,8 @@
|
|||||||
module git.square-r00t.net/sshsecure
|
module git.square-r00t.net/sshsecure
|
||||||
|
|
||||||
go 1.15
|
go 1.15
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/dchest/bcrypt_pbkdf v0.0.0-20150205184540-83f37f9c154a
|
||||||
|
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a // indirect
|
||||||
|
)
|
||||||
|
9
go.sum
Normal file
9
go.sum
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
github.com/dchest/bcrypt_pbkdf v0.0.0-20150205184540-83f37f9c154a h1:saTgr5tMLFnmy/yg3qDTft4rE5DY2uJ/cCxCe3q0XTU=
|
||||||
|
github.com/dchest/bcrypt_pbkdf v0.0.0-20150205184540-83f37f9c154a/go.mod h1:Bw9BbhOJVNR+t0jCqx2GC6zv0TGBsShs56Y3gfSCvl0=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a h1:vclmkQCjlDX5OydZ9wv8rBCcS0QyQY66Mpf/7BZbInM=
|
||||||
|
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
@ -5,26 +5,53 @@ const (
|
|||||||
KeyV1Magic string = "openssh-key-v1"
|
KeyV1Magic string = "openssh-key-v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Cipher names. I believe only AES256-CTR is supported upstream currently.
|
// "Meta". Used for comment strings, etc.
|
||||||
|
|
||||||
|
const projUrl = "https://git.square-r00t.net/SSHSecure"
|
||||||
|
|
||||||
|
// Defaults.
|
||||||
const (
|
const (
|
||||||
CIPHER_NULL string = "none"
|
defCipher string = CipherAes256Ctr
|
||||||
CIPHER_AES256_CTR string = "aes256-ctr"
|
defKeyType string = KeyEd25519
|
||||||
|
defKDF string = KdfBcrypt
|
||||||
|
defRounds uint32 = 100
|
||||||
|
defRSABitSize uint32 = 4096
|
||||||
|
defSaltLen int = 16
|
||||||
)
|
)
|
||||||
|
|
||||||
var allowed_ciphers = [...]string{CIPHER_NULL, CIPHER_AES256_CTR}
|
// Cipher names. I believe only AES256-CTR is supported upstream currently.
|
||||||
|
const (
|
||||||
|
CipherNull string = "none"
|
||||||
|
CipherAes256Ctr string = "aes256-ctr"
|
||||||
|
)
|
||||||
|
|
||||||
|
var allowed_ciphers = [...]string{CipherNull, CipherAes256Ctr}
|
||||||
|
|
||||||
// Key types.
|
// Key types.
|
||||||
const (
|
const (
|
||||||
KEY_ED25519 string = "ssh-ed25519"
|
KeyEd25519 string = "ssh-ed25519"
|
||||||
KEY_RSA string = "ssh-rsa"
|
KeyRsa string = "ssh-rsa"
|
||||||
)
|
)
|
||||||
|
|
||||||
var allowed_keytypes = [...]string{KEY_ED25519, KEY_RSA}
|
var allowed_keytypes = [...]string{KeyEd25519, KeyRsa}
|
||||||
|
|
||||||
// KDF names. I believe only bcrypt is supported upstream currently.
|
// KDF names. I believe only bcrypt is supported upstream currently.
|
||||||
const (
|
const (
|
||||||
KDF_NULL string = "none"
|
KdfNull string = "none"
|
||||||
KDF_BCRYPT string = "bcrypt"
|
KdfBcrypt string = "bcrypt"
|
||||||
)
|
)
|
||||||
|
|
||||||
var allowed_kdfnames = [...]string{KDF_NULL, KDF_BCRYPT}
|
var allowed_kdfnames = [...]string{KdfNull, KdfBcrypt}
|
||||||
|
|
||||||
|
// Key lengths.
|
||||||
|
const (
|
||||||
|
// ED25519 in OpenSSH uses a static key size of 64 bytes.
|
||||||
|
ed25519Len uint32 = 64
|
||||||
|
)
|
||||||
|
|
||||||
|
// Key/Block sizes.
|
||||||
|
const (
|
||||||
|
keyEd25519 uint32 = 32
|
||||||
|
blockEd25519 uint32 = 16
|
||||||
|
blockNull uint32 = 8
|
||||||
|
)
|
||||||
|
105
sshkeys/func.go
105
sshkeys/func.go
@ -1,51 +1,104 @@
|
|||||||
package sshkeys
|
package sshkeys
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/rand"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"r00t2.io/goutils/checks"
|
// `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 genPrivKey(cipherAlgo string, kdf string, salt []byte, rounds uint32, passphrase string) ([]byte, error) {
|
func (k *EncryptedSSHKeyV1) validate() error {
|
||||||
return nil, nil
|
if k.Passphrase == nil {
|
||||||
}
|
|
||||||
|
|
||||||
func genPubKey(privKey *[]byte) ([]byte, error) {
|
|
||||||
if *privKey == nil {
|
|
||||||
return nil, errors.New("must generate private key before public key")
|
|
||||||
}
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k *EncryptedSSHKeyV1) GeneratePrivate(force bool) error {
|
|
||||||
if k.Passphrase == "" {
|
|
||||||
return errors.New("cannot use encrypted key with empty passphrase")
|
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 {
|
if k.PrivateKeys != nil && !force {
|
||||||
return nil // Already generated.
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *EncryptedSSHKeyV1) GeneratePublic(force bool) error {
|
func (k *SSHKeyV1) validate() error {
|
||||||
if err := k.GeneratePrivate(force); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *SSHKeyV1) GeneratePrivate(force bool) error {
|
func (k *SSHKeyV1) GeneratePrivate(force bool) error {
|
||||||
|
k.validate()
|
||||||
if k.PrivateKeys != nil && !force {
|
if k.PrivateKeys != nil && !force {
|
||||||
return nil // Already generated.
|
return nil // Already generated.
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *SSHKeyV1) GeneratePublic(force bool) error {
|
|
||||||
if err := k.GeneratePrivate(force); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package sshkeys
|
package sshkeys
|
||||||
|
|
||||||
type OpenSSHKeypair interface {
|
type OpenSSHKeypair interface {
|
||||||
GeneratePrivate(keyType uint8) error
|
GeneratePrivate(force bool) error
|
||||||
GeneratePublic(keyType uint8) error
|
GeneratePublic(force bool) error
|
||||||
}
|
}
|
||||||
|
@ -54,12 +54,24 @@ ANNOTATED HEX:
|
|||||||
4.0.0.1 00000020 (32)
|
4.0.0.1 00000020 (32)
|
||||||
4.0.0.1.0 bfa2031aa5463113e40e16896af503c5299ead76b09cb63846f41cc4de1740f6 (bytes)
|
4.0.0.1.0 bfa2031aa5463113e40e16896af503c5299ead76b09cb63846f41cc4de1740f6 (bytes)
|
||||||
4.0.1 000000a0 (160)
|
4.0.1 000000a0 (160)
|
||||||
4.0.1 (AES256-CTR encrypted block) (bytes)
|
4.0.1.0 - 4.0.1.5 (AES256-CBC encrypted block) (bytes)
|
||||||
c49777cd0d1a7d37db77a1814991278f8ce99d57
|
c49777cd0d1a7d37db77a1814991278f
|
||||||
2e2c666b93b99867425c60da4652fddb85550985
|
8ce99d572e2c666b93b99867425c60da
|
||||||
32b51beeee2959f9db5cf5a0905052720c5de25f
|
4652fddb8555098532b51beeee2959f9
|
||||||
2c4dd87ebcc7bb5ea3d7bcbeacc6b732e4c39295
|
db5cf5a0905052720c5de25f2c4dd87e
|
||||||
d9991a97ef3f0838f8a9bfd43edb340318964908
|
bcc7bb5ea3d7bcbeacc6b732e4c39295
|
||||||
8f6cfb78946fb914e358ac6abc64691072f5f278
|
d9991a97ef3f0838f8a9bfd43edb3403
|
||||||
8534d9d42d7f406bc5090b30df23cb7dd8c5cb93
|
189649088f6cfb78946fb914e358ac6a
|
||||||
8e41facd6e38e8845b8160bff840598118d447c2
|
bc64691072f5f2788534d9d42d7f406b
|
||||||
|
c5090b30df23cb7dd8c5cb938e41facd
|
||||||
|
6e38e8845b8160bff840598118d447c2
|
||||||
|
|
||||||
|
DECRYPTED 4.0.1:
|
||||||
|
(...)
|
||||||
|
4.0.1 000000a0 (160)
|
||||||
|
4.0.1.0
|
||||||
|
4.0.1.1
|
||||||
|
4.0.1.2
|
||||||
|
4.0.2.3
|
||||||
|
4.0.2.4
|
||||||
|
4.0.2.5
|
@ -5,3 +5,27 @@ https://stackoverflow.com/a/56300901/733214
|
|||||||
https://stackoverflow.com/a/59283692/733214
|
https://stackoverflow.com/a/59283692/733214
|
||||||
https://coolaj86.com/articles/the-openssh-private-key-format/
|
https://coolaj86.com/articles/the-openssh-private-key-format/
|
||||||
https://coolaj86.com/articles/the-ssh-public-key-format/
|
https://coolaj86.com/articles/the-ssh-public-key-format/
|
||||||
|
https://crypto.stackexchange.com/a/40910
|
||||||
|
https://flak.tedunangst.com/post/new-openssh-key-format-and-bcrypt-pbkdf
|
||||||
|
("(Technical note: PBKDF2, aka PKCS #5, supports pluggable hash functions, though in practice everybody uses HMAC-SHA1. The bcrypt pbkdf essentially is PBKDF2, but with bcrypt plugged into it instead.)"
|
||||||
|
http://www.tedunangst.com/flak/post/bcrypt-pbkdf
|
||||||
|
|
||||||
|
## UPSTREAM
|
||||||
|
https://github.com/openssh/openssh-portable/blob/master/sshkey.c
|
||||||
|
funcs:
|
||||||
|
sshkey_generate (~L1714)
|
||||||
|
sshkey_private_to_blob2 (~L3833)
|
||||||
|
sshkey_private_to_fileblob (~L4413)
|
||||||
|
https://github.com/openssh/openssh-portable/blob/master/cipher.c
|
||||||
|
funcs:
|
||||||
|
cipher_ivlen
|
||||||
|
https://github.com/openssh/openssh-portable/blob/master/ed25519.c
|
||||||
|
funcs:
|
||||||
|
crypto_sign_ed25519_keypair (~L26)
|
||||||
|
https://github.com/openssh/openssh-portable/blob/master/authfile.c
|
||||||
|
funcs:
|
||||||
|
sshkey_save_private (~L68)
|
||||||
|
sshkey_save_private_blob (~L56)
|
||||||
|
https://github.com/openssh/openssh-portable/blob/master/ssh-keygen.c
|
||||||
|
funcs:
|
||||||
|
main (~L3145; ~L3673 onwards for key generation)
|
@ -4,9 +4,8 @@ package sshkeys
|
|||||||
type EncryptedSSHKeyV1 struct {
|
type EncryptedSSHKeyV1 struct {
|
||||||
SSHKeyV1
|
SSHKeyV1
|
||||||
CipherName string
|
CipherName string
|
||||||
KDFName string
|
|
||||||
KDFOpts SSHKDFOpts
|
KDFOpts SSHKDFOpts
|
||||||
Passphrase string
|
Passphrase []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
// SSHKDFOpts contains a set of KDF options.
|
// SSHKDFOpts contains a set of KDF options.
|
||||||
@ -20,19 +19,26 @@ type SSHKDFOpts struct {
|
|||||||
// Patch your shit.
|
// Patch your shit.
|
||||||
type SSHKeyV1 struct {
|
type SSHKeyV1 struct {
|
||||||
Magic string
|
Magic string
|
||||||
|
BitSize uint32
|
||||||
|
DefKeyType string
|
||||||
|
KDFName string
|
||||||
|
KeySize uint32
|
||||||
|
BlockSize uint32
|
||||||
PublicKeys []SSHPubKey
|
PublicKeys []SSHPubKey
|
||||||
PrivateKeys []SSHPrivKey
|
PrivateKeys []SSHPrivKey
|
||||||
}
|
}
|
||||||
|
|
||||||
// SSHPubKey contains the Public key of an SSH Keypair.
|
// SSHPubKey contains the Public key of an SSH Keypair.
|
||||||
type SSHPubKey struct {
|
type SSHPubKey struct {
|
||||||
KeyType string
|
|
||||||
PrivateKey *SSHPrivKey
|
PrivateKey *SSHPrivKey
|
||||||
|
KeyType string
|
||||||
|
Key []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
// SSHPrivKey contains the Private key of an SSH Keypair.
|
// SSHPrivKey contains the Private key of an SSH Keypair.
|
||||||
type SSHPrivKey struct {
|
type SSHPrivKey struct {
|
||||||
PublicKey *SSHPubKey
|
PublicKey *SSHPubKey
|
||||||
Checksum uint32
|
Key []byte
|
||||||
|
Checksum []byte
|
||||||
Comment string
|
Comment string
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user