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
|
||||
|
||||
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"
|
||||
)
|
||||
|
||||
// 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 (
|
||||
CIPHER_NULL string = "none"
|
||||
CIPHER_AES256_CTR string = "aes256-ctr"
|
||||
defCipher string = CipherAes256Ctr
|
||||
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.
|
||||
const (
|
||||
KEY_ED25519 string = "ssh-ed25519"
|
||||
KEY_RSA string = "ssh-rsa"
|
||||
KeyEd25519 string = "ssh-ed25519"
|
||||
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.
|
||||
const (
|
||||
KDF_NULL string = "none"
|
||||
KDF_BCRYPT string = "bcrypt"
|
||||
KdfNull string = "none"
|
||||
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
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"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) {
|
||||
return nil, 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 == "" {
|
||||
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 *EncryptedSSHKeyV1) GeneratePublic(force bool) error {
|
||||
if err := k.GeneratePrivate(force); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func (k *SSHKeyV1) GeneratePublic(force bool) error {
|
||||
if err := k.GeneratePrivate(force); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
package sshkeys
|
||||
|
||||
type OpenSSHKeypair interface {
|
||||
GeneratePrivate(keyType uint8) error
|
||||
GeneratePublic(keyType uint8) error
|
||||
GeneratePrivate(force bool) error
|
||||
GeneratePublic(force bool) error
|
||||
}
|
||||
|
@ -54,12 +54,24 @@ ANNOTATED HEX:
|
||||
4.0.0.1 00000020 (32)
|
||||
4.0.0.1.0 bfa2031aa5463113e40e16896af503c5299ead76b09cb63846f41cc4de1740f6 (bytes)
|
||||
4.0.1 000000a0 (160)
|
||||
4.0.1 (AES256-CTR encrypted block) (bytes)
|
||||
c49777cd0d1a7d37db77a1814991278f8ce99d57
|
||||
2e2c666b93b99867425c60da4652fddb85550985
|
||||
32b51beeee2959f9db5cf5a0905052720c5de25f
|
||||
2c4dd87ebcc7bb5ea3d7bcbeacc6b732e4c39295
|
||||
d9991a97ef3f0838f8a9bfd43edb340318964908
|
||||
8f6cfb78946fb914e358ac6abc64691072f5f278
|
||||
8534d9d42d7f406bc5090b30df23cb7dd8c5cb93
|
||||
8e41facd6e38e8845b8160bff840598118d447c2
|
||||
4.0.1.0 - 4.0.1.5 (AES256-CBC encrypted block) (bytes)
|
||||
c49777cd0d1a7d37db77a1814991278f
|
||||
8ce99d572e2c666b93b99867425c60da
|
||||
4652fddb8555098532b51beeee2959f9
|
||||
db5cf5a0905052720c5de25f2c4dd87e
|
||||
bcc7bb5ea3d7bcbeacc6b732e4c39295
|
||||
d9991a97ef3f0838f8a9bfd43edb3403
|
||||
189649088f6cfb78946fb914e358ac6a
|
||||
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://coolaj86.com/articles/the-openssh-private-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 {
|
||||
SSHKeyV1
|
||||
CipherName string
|
||||
KDFName string
|
||||
KDFOpts SSHKDFOpts
|
||||
Passphrase string
|
||||
Passphrase []byte
|
||||
}
|
||||
|
||||
// SSHKDFOpts contains a set of KDF options.
|
||||
@ -20,19 +19,26 @@ type SSHKDFOpts struct {
|
||||
// Patch your shit.
|
||||
type SSHKeyV1 struct {
|
||||
Magic string
|
||||
BitSize uint32
|
||||
DefKeyType string
|
||||
KDFName string
|
||||
KeySize uint32
|
||||
BlockSize uint32
|
||||
PublicKeys []SSHPubKey
|
||||
PrivateKeys []SSHPrivKey
|
||||
}
|
||||
|
||||
// SSHPubKey contains the Public key of an SSH Keypair.
|
||||
type SSHPubKey struct {
|
||||
KeyType string
|
||||
PrivateKey *SSHPrivKey
|
||||
KeyType string
|
||||
Key []byte
|
||||
}
|
||||
|
||||
// SSHPrivKey contains the Private key of an SSH Keypair.
|
||||
type SSHPrivKey struct {
|
||||
PublicKey *SSHPubKey
|
||||
Checksum uint32
|
||||
Key []byte
|
||||
Checksum []byte
|
||||
Comment string
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user