checking in for the night.
key generation should be done, need to finish packing/formatting. also need to start on moduli generation.
This commit is contained in:
parent
99a01d843e
commit
86266685f5
3
TODO
3
TODO
@ -1,3 +1,4 @@
|
|||||||
-sshkeys (need to figure out generation process)
|
-sshkeys (see ref/<type>/parse_poc_<keytype>.go for POC)
|
||||||
|
--hostkeys (https://security.stackexchange.com/questions/211106/what-is-the-difference-between-host-and-client-ssh-key-generation)
|
||||||
-moduli dhparams generation (dh.c? moduli.c?)
|
-moduli dhparams generation (dh.c? moduli.c?)
|
||||||
--ssh-keygen.c, ~L3565
|
--ssh-keygen.c, ~L3565
|
1
moduli/const.go
Normal file
1
moduli/const.go
Normal file
@ -0,0 +1 @@
|
|||||||
|
package moduli
|
1
moduli/func.go
Normal file
1
moduli/func.go
Normal file
@ -0,0 +1 @@
|
|||||||
|
package moduli
|
5
moduli/main.go
Normal file
5
moduli/main.go
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
package moduli
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
}
|
6
moduli/ref/sources
Normal file
6
moduli/ref/sources
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
https://man7.org/linux/man-pages/man5/moduli.5.html
|
||||||
|
https://security.stackexchange.com/questions/41941/consequences-of-tampered-etc-ssh-moduli
|
||||||
|
https://access.redhat.com/blogs/766093/posts/2177481
|
||||||
|
https://security.stackexchange.com/a/113058
|
||||||
|
https://github.com/Luzifer/go-dhparam
|
||||||
|
https://github.com/mimoo/test_DHparams
|
1
moduli/struct.go
Normal file
1
moduli/struct.go
Normal file
@ -0,0 +1 @@
|
|||||||
|
package moduli
|
@ -17,6 +17,10 @@ const (
|
|||||||
defRounds uint32 = 100
|
defRounds uint32 = 100
|
||||||
defRSABitSize uint32 = 4096
|
defRSABitSize uint32 = 4096
|
||||||
defSaltLen int = 16
|
defSaltLen int = 16
|
||||||
|
// bcrypt_pbkdf maxes out at 32 for private key gen (sk is actually 64; sk+pk)
|
||||||
|
// But per OpenSSH code, we pass a key len of kdfKeyLen + len(salt)
|
||||||
|
kdfKeyLen int = 32
|
||||||
|
kdfSplit int = 32
|
||||||
)
|
)
|
||||||
|
|
||||||
// Cipher names. I believe only AES256-CTR is supported upstream currently.
|
// Cipher names. I believe only AES256-CTR is supported upstream currently.
|
||||||
@ -51,7 +55,10 @@ const (
|
|||||||
|
|
||||||
// Key/Block sizes.
|
// Key/Block sizes.
|
||||||
const (
|
const (
|
||||||
keyEd25519 uint32 = 32
|
keyEd25519 uint32 = 32
|
||||||
|
// Is this correct? Based on PROTOCOL.key's "padlen % 255", it seems to be.
|
||||||
|
blockPad uint32 = 255
|
||||||
blockEd25519 uint32 = 16
|
blockEd25519 uint32 = 16
|
||||||
blockNull uint32 = 8
|
// Blocksize for RSA depends on key bits, I think.
|
||||||
|
blockNull uint32 = 8
|
||||||
)
|
)
|
||||||
|
24
sshkeys/ed25519.go
Normal file
24
sshkeys/ed25519.go
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
package sshkeys
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/ed25519"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (k *SSHPrivKey) generateEd25519() error {
|
||||||
|
if k.Key != nil || k.PublicKey.Key != nil {
|
||||||
|
return nil // Just no-op; already generated.
|
||||||
|
}
|
||||||
|
// We cast "pk" (public key) to _ because it's an interface{} that we can't seem to
|
||||||
|
// assert to []byte. I've tried iterating, a type assertion, initializing,... nada.
|
||||||
|
// Luckily it's at a fixed half of the secret key.
|
||||||
|
if _, s, err := ed25519.GenerateKey(nil); err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
k.Key = s
|
||||||
|
ks := len(s) - ed25519.PublicKeySize
|
||||||
|
k.PublicKey.Key = s[ks:]
|
||||||
|
k.BitSize = ed25519Len
|
||||||
|
k.PublicKey.KeyType = KeyEd25519
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
104
sshkeys/func.go
104
sshkeys/func.go
@ -1,6 +1,9 @@
|
|||||||
package sshkeys
|
package sshkeys
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/aes"
|
||||||
|
"crypto/cipher"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -52,7 +55,7 @@ func (k *EncryptedSSHKeyV1) Generate(force bool) error {
|
|||||||
if err := k.validate(); err != nil {
|
if err := k.validate(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if k.PrivateKeys != nil && !force {
|
if len(k.Keys) > 0 && !force {
|
||||||
return nil // Already generated.
|
return nil // Already generated.
|
||||||
}
|
}
|
||||||
if k.KDFOpts.Salt == nil {
|
if k.KDFOpts.Salt == nil {
|
||||||
@ -64,10 +67,7 @@ func (k *EncryptedSSHKeyV1) Generate(force bool) error {
|
|||||||
if k.KDFOpts.Rounds == 0 {
|
if k.KDFOpts.Rounds == 0 {
|
||||||
k.KDFOpts.Rounds = defRounds
|
k.KDFOpts.Rounds = defRounds
|
||||||
}
|
}
|
||||||
if k.DefKeyType == KeyRsa && k.BitSize == 0 {
|
if k.DefKeyType == KeyEd25519 {
|
||||||
k.BitSize = defRSABitSize
|
|
||||||
} else if k.DefKeyType == KeyEd25519 {
|
|
||||||
k.BitSize = ed25519Len
|
|
||||||
k.KeySize = keyEd25519
|
k.KeySize = keyEd25519
|
||||||
k.BlockSize = blockEd25519
|
k.BlockSize = blockEd25519
|
||||||
}
|
}
|
||||||
@ -80,25 +80,95 @@ func (k *EncryptedSSHKeyV1) Generate(force bool) error {
|
|||||||
if _, err := rand.Read(pk.Checksum); err != nil {
|
if _, err := rand.Read(pk.Checksum); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Upstream only currently supports bcrypt_pbkdf ("bcrypt").
|
switch k.DefKeyType {
|
||||||
// This should always eval to true, but is here for future planning in case other KDF are implemented.
|
case KeyRsa:
|
||||||
if k.KDFName == KdfBcrypt {
|
if err := pk.generateRsa(); err != nil {
|
||||||
if pk.Key, err = bcrypt_pbkdf.Key(k.Passphrase, k.KDFOpts.Salt, int(k.KDFOpts.Rounds), int(k.KeySize)); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
case KeyEd25519:
|
||||||
|
if err := pk.generateEd25519(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return errors.New("unknown key type; could not generate private/public keypair")
|
||||||
}
|
}
|
||||||
|
k.Keys = append(k.Keys, pk)
|
||||||
|
// We also need an encrypter/decrypter since this is an encrypted key.
|
||||||
|
// 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.
|
||||||
|
switch k.KDFName {
|
||||||
|
case KdfBcrypt:
|
||||||
|
if k.Crypt.CryptKey, err = bcrypt_pbkdf.Key(k.Passphrase, k.KDFOpts.Salt, int(k.KDFOpts.Rounds), kdfKeyLen+len(k.KDFOpts.Salt)); err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
k.Crypt.PrivateKey = k.Crypt.CryptKey[0:kdfSplit]
|
||||||
|
k.Crypt.CryptSalt = k.Crypt.CryptKey[kdfSplit:]
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return errors.New("could not find KDF")
|
||||||
|
}
|
||||||
|
switch k.CipherName {
|
||||||
|
case CipherAes256Ctr:
|
||||||
|
if k.Crypt.Cipher, err = aes.NewCipher(k.Crypt.PrivateKey); err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
k.Crypt.Stream = cipher.NewCTR(k.Crypt.Cipher, k.Crypt.CryptSalt)
|
||||||
|
// Can then be used as k.Crypt.Stream.XORKeyStream(dst []byte, src []byte)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return errors.New("could not find Cipher")
|
||||||
|
}
|
||||||
|
k.build()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *SSHKeyV1) validate() error {
|
func (k *SSHKeyV1) validate() error {
|
||||||
return nil
|
var validKT bool
|
||||||
}
|
for _, v := range allowed_keytypes {
|
||||||
|
if v == k.DefKeyType {
|
||||||
func (k *SSHKeyV1) GeneratePrivate(force bool) error {
|
validKT = true
|
||||||
k.validate()
|
}
|
||||||
if k.PrivateKeys != nil && !force {
|
}
|
||||||
return nil // Already generated.
|
if !validKT {
|
||||||
|
return errors.New("invalid DefKeyType specified")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (k *SSHKeyV1) Generate(force bool) error {
|
||||||
|
if len(k.Keys) > 0 && !force {
|
||||||
|
return nil // Already generated.
|
||||||
|
}
|
||||||
|
if k.DefKeyType == KeyEd25519 {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
switch k.DefKeyType {
|
||||||
|
case KeyRsa:
|
||||||
|
if err := pk.generateRsa(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case KeyEd25519:
|
||||||
|
if err := pk.generateEd25519(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return errors.New("unknown key type; could not generate private/public keypair")
|
||||||
|
}
|
||||||
|
k.Keys = append(k.Keys, pk)
|
||||||
|
k.build()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *SSHKeyV1) build() {
|
||||||
|
// We actually assemble the key buffer here. Translation to bytes where needed, case switches (ED25519 vs. RSA), etc.
|
||||||
|
}
|
||||||
|
@ -1,6 +0,0 @@
|
|||||||
package sshkeys
|
|
||||||
|
|
||||||
type OpenSSHKeypair interface {
|
|
||||||
GeneratePrivate(force bool) error
|
|
||||||
GeneratePublic(force bool) error
|
|
||||||
}
|
|
@ -1,10 +1,13 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
`crypto/cipher`
|
"crypto/aes"
|
||||||
`crypto/rsa`
|
"crypto/cipher"
|
||||||
`encoding/hex`
|
"crypto/rsa"
|
||||||
`fmt`
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/dchest/bcrypt_pbkdf"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ssh-keygen -f /tmp/tmp2xzvpjhn -q -o -t rsa -b 4096 -C "This is a comment string" -N test -a 100
|
// ssh-keygen -f /tmp/tmp2xzvpjhn -q -o -t rsa -b 4096 -C "This is a comment string" -N test -a 100
|
||||||
@ -379,24 +382,38 @@ bc2a63a20ebb309cc6f3e65db301a058b8dace07e71b38f3f3595433f69b198f
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
const (
|
const (
|
||||||
passphrase string = "test"
|
passphrase string = "test"
|
||||||
rounds int = 100
|
saltLen int = 16 // 3.0.0.0
|
||||||
keySize int = 4096
|
rounds int = 100 // 3.0.0.1
|
||||||
lenPlain int = 160
|
keySize int = 4096
|
||||||
publicExponent int = 65537
|
crtLen int = 256 // 4.0.1.4
|
||||||
|
dLen int = 512 // 4.0.1.3
|
||||||
|
e int = 65537 // 4.0.0.1
|
||||||
|
nLen int = 513 // 4.0.0.2
|
||||||
|
pLen int = 257 // 4.0.1.5
|
||||||
|
qLen int = 257 // 4.0.1.6
|
||||||
|
dataLen int = 1872 // 4.0.1
|
||||||
)
|
)
|
||||||
var salt []byte
|
var salt []byte
|
||||||
var bcryptKey []byte
|
var bcryptKey []byte
|
||||||
var sk []byte
|
var crt []byte
|
||||||
var pk []byte
|
var d []byte
|
||||||
var pubkey rsa.PublicKey
|
var n []byte
|
||||||
|
var p []byte
|
||||||
|
var q []byte
|
||||||
var key rsa.PrivateKey
|
var key rsa.PrivateKey
|
||||||
var decrypted []byte
|
var decrypted []byte
|
||||||
var aesCtx cipher.Block
|
var aesCtx cipher.Block
|
||||||
var encData []byte
|
var encData []byte
|
||||||
|
|
||||||
decrypted = make([]byte, lenPlain)
|
crt = make([]byte, crtLen)
|
||||||
encData = make([]byte, lenPlain)
|
d = make([]byte, dLen)
|
||||||
|
n = make([]byte, nLen)
|
||||||
|
p = make([]byte, pLen)
|
||||||
|
q = make([]byte, qLen)
|
||||||
|
decrypted = make([]byte, dataLen)
|
||||||
|
encData = make([]byte, dataLen)
|
||||||
|
salt = make([]byte, saltLen)
|
||||||
|
|
||||||
// Import salt
|
// Import salt
|
||||||
if s, err := hex.DecodeString("07d4b07c0b128348916488008d6e130b"); err != nil {
|
if s, err := hex.DecodeString("07d4b07c0b128348916488008d6e130b"); err != nil {
|
||||||
@ -474,11 +491,64 @@ func main() {
|
|||||||
encData = b
|
encData = b
|
||||||
}
|
}
|
||||||
|
|
||||||
// RSA keys
|
// Bcrypt_pbkdf derivation (used for deriving decryption key for AES encrypted private key)
|
||||||
// This is used to validate decrypted keys.
|
if k, err := bcrypt_pbkdf.Key([]byte(passphrase), salt, rounds, 32+16); err != nil {
|
||||||
if rk, err := hex.DecodeString(""); err != nil {
|
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
|
return
|
||||||
} else {
|
} else {
|
||||||
key = rk
|
bcryptKey = k
|
||||||
}
|
}
|
||||||
|
realBcryptKey := bcryptKey[0:32]
|
||||||
|
realIV := bcryptKey[32:]
|
||||||
|
|
||||||
|
// N
|
||||||
|
if b, err := hex.DecodeString(
|
||||||
|
"00b7cec04601ce2a12f0c924cb9a30eb990066812cb14369193f30b2b9fdd4af" +
|
||||||
|
"cb300c918f2a77d64410f3617ae7c8ca318c257d3c4df4e2c4108bbbe93a8689" +
|
||||||
|
"4ba14b3575f2f72150bc381dcbfb742c7a196866fd3184ace96761adda0fc299" +
|
||||||
|
"2f6c866d7569919fc22d9c4bf0de405a8c76d519aa2a5329dc6825777229a5d0" +
|
||||||
|
"b753a7825a89b95275f9c025e215343c6c88cd6690a221f8ae9ef675ee464dc7" +
|
||||||
|
"d118da410507ea5d6b6489dd60afd8a6646492db3e279f1a78240db8abbda6c5" +
|
||||||
|
"0714c9636650a72081e7fa5d472c1428b07eae5d15b64ea1e2a7508512fe9ab6" +
|
||||||
|
"55f86a313486d3cca1dd8e90acc5c9fba4d6e767507fbab9f3a7f68c748142af" +
|
||||||
|
"2a3701d31a8a9b7511958aa77187ba702ed934d385afcee42380e95e0e7e9bc0" +
|
||||||
|
"f4d23367fc770374167b7f0926fb6fdb6d05aad1cfd191824845b014e18153bf" +
|
||||||
|
"0d1d3c3b1fadbb25a3f1d151f9b684633d8c1690fcd8cad05aac2aeb23dbf19a" +
|
||||||
|
"37e480a008910319c116d47bd924b39942543b88a0f6127952b2d8e1290f3029" +
|
||||||
|
"f542aebe9c0c8e36cf3296865cd6643c8924d566ebf4971809399a1ac096fe1e" +
|
||||||
|
"dc3b5f871bf5ef0b4d44e0ea27620d205142e0bfcf677b4db025532121a3f074" +
|
||||||
|
"5aa4d0586331733257855a5cecbe3ac4403d04ff0cc0c58b7c04904b402125c2" +
|
||||||
|
"bc2a63a20ebb309cc6f3e65db301a058b8dace07e71b38f3f3595433f69b198f" +
|
||||||
|
"07",
|
||||||
|
); err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
n = b
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decrypter
|
||||||
|
if a, err := aes.NewCipher(realBcryptKey); err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
aesCtx = a
|
||||||
|
}
|
||||||
|
|
||||||
|
// Actual cipher setup. AES256-CTR
|
||||||
|
decryptor := cipher.NewCTR(aesCtx, realIV)
|
||||||
|
decryptor.XORKeyStream(decrypted, encData)
|
||||||
|
|
||||||
|
// Print comparisons
|
||||||
|
fmt.Printf("Salt: %v\n", hex.EncodeToString(salt))
|
||||||
|
fmt.Printf("Bcrypt key: %v\n", hex.EncodeToString(bcryptKey))
|
||||||
|
fmt.Printf("CRT: %v\n", hex.EncodeToString(crt))
|
||||||
|
fmt.Printf("d: %v\n", hex.EncodeToString(d))
|
||||||
|
fmt.Printf("n: %v\n", hex.EncodeToString(n))
|
||||||
|
fmt.Printf("p: %v\n", hex.EncodeToString(p))
|
||||||
|
fmt.Printf("q: %v\n", hex.EncodeToString(q))
|
||||||
|
fmt.Printf("key: %v\n", key)
|
||||||
|
// var aesCtx cipher.Block
|
||||||
|
fmt.Printf("encData: %v\n", hex.EncodeToString(encData))
|
||||||
|
fmt.Printf("Decrypted?: %v\n", hex.EncodeToString(decrypted))
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
https://peterlyons.com/problog/2017/12/openssh-ed25519-private-key-file-format/
|
|
||||||
https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key
|
https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key
|
||||||
canonical: https://cvsweb.openbsd.org/src/usr.bin/ssh/PROTOCOL.key?annotate=HEAD
|
canonical: https://cvsweb.openbsd.org/src/usr.bin/ssh/PROTOCOL.key?annotate=HEAD
|
||||||
|
https://peterlyons.com/problog/2017/12/openssh-ed25519-private-key-file-format/
|
||||||
https://stackoverflow.com/a/56300901/733214
|
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://coolaj86.com/articles/openssh-vs-openssl-key-formats/
|
||||||
|
https://coolaj86.com/articles/ssh-pubilc-key-fingerprints/
|
||||||
https://crypto.stackexchange.com/a/40910
|
https://crypto.stackexchange.com/a/40910
|
||||||
https://flak.tedunangst.com/post/new-openssh-key-format-and-bcrypt-pbkdf
|
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.)"
|
("(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.)"
|
||||||
@ -12,6 +14,21 @@ http://www.tedunangst.com/flak/post/bcrypt-pbkdf
|
|||||||
https://xorhash.gitlab.io/xhblog/0010.html
|
https://xorhash.gitlab.io/xhblog/0010.html
|
||||||
https://blog.rebased.pl/2020/02/10/ssh-key-internals.html
|
https://blog.rebased.pl/2020/02/10/ssh-key-internals.html
|
||||||
https://blog.rebased.pl/2020/03/24/basic-key-security.html
|
https://blog.rebased.pl/2020/03/24/basic-key-security.html
|
||||||
|
https://github.com/pwnedkeys/openssl-additions/blob/master/lib/openssl/ssh_pkey.rb
|
||||||
|
https://stackoverflow.com/a/25181584/733214
|
||||||
|
https://crypto.stackexchange.com/a/68732
|
||||||
|
|
||||||
|
RSA:
|
||||||
|
d: 512
|
||||||
|
n: 512
|
||||||
|
p: 256
|
||||||
|
q: 256
|
||||||
|
QInv: 256
|
||||||
|
d: 4.0.1.3.0
|
||||||
|
n: 4.0.0.2.0, 4.0.1.2.1.0 (prefix nullbyte)
|
||||||
|
p: 4.0.1.5.0 (prefix nullbyte)
|
||||||
|
q: 4.0.1.6.0 (prefix nullbyte)
|
||||||
|
QInv: 4.0.1.4.0
|
||||||
|
|
||||||
## UPSTREAM
|
## UPSTREAM
|
||||||
https://github.com/openssh/openssh-portable/blob/master/sshkey.c
|
https://github.com/openssh/openssh-portable/blob/master/sshkey.c
|
||||||
|
23
sshkeys/rsa.go
Normal file
23
sshkeys/rsa.go
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
package sshkeys
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/rsa"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (k *SSHPrivKey) generateRsa() error {
|
||||||
|
if k.BitSize == 0 {
|
||||||
|
k.BitSize = defRSABitSize
|
||||||
|
}
|
||||||
|
if k.Key != nil || k.PublicKey.Key != nil {
|
||||||
|
return nil // A no-op; key already exists.
|
||||||
|
}
|
||||||
|
if sk, err := rsa.GenerateKey(rand.Reader, int(k.BitSize)); err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
k.Key = sk // See https://golang.org/pkg/crypto/rsa/#PrivateKey
|
||||||
|
k.PublicKey.KeyType = KeyRsa
|
||||||
|
k.PublicKey.Key = k.Key.PublicKey
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
@ -1,13 +1,29 @@
|
|||||||
package sshkeys
|
package sshkeys
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/cipher"
|
||||||
|
)
|
||||||
|
|
||||||
// EncryptedSSHKeyV1 represents an encrypted private key.
|
// EncryptedSSHKeyV1 represents an encrypted private key.
|
||||||
type EncryptedSSHKeyV1 struct {
|
type EncryptedSSHKeyV1 struct {
|
||||||
SSHKeyV1
|
SSHKeyV1
|
||||||
CipherName string
|
CipherName string
|
||||||
|
Crypt SSHCrypt
|
||||||
KDFOpts SSHKDFOpts
|
KDFOpts SSHKDFOpts
|
||||||
Passphrase []byte
|
Passphrase []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SSHEncryptionKey contains the PublicKey and PrivateKey bytes (as derived by KDF, different from the actual SSH keypair),
|
||||||
|
// the Cipher, and the stream.
|
||||||
|
type SSHCrypt struct {
|
||||||
|
Stream cipher.Stream
|
||||||
|
Cipher cipher.Block
|
||||||
|
CryptSalt []byte
|
||||||
|
PrivateKey []byte
|
||||||
|
CryptKey []byte
|
||||||
|
}
|
||||||
|
|
||||||
// SSHKDFOpts contains a set of KDF options.
|
// SSHKDFOpts contains a set of KDF options.
|
||||||
type SSHKDFOpts struct {
|
type SSHKDFOpts struct {
|
||||||
Salt []byte // Also referred to as IV (initialization vector). (https://en.wikipedia.org/wiki/Initialization_vector)
|
Salt []byte // Also referred to as IV (initialization vector). (https://en.wikipedia.org/wiki/Initialization_vector)
|
||||||
@ -18,27 +34,29 @@ type SSHKDFOpts struct {
|
|||||||
// We don't bother with the legacy (pre v1) keys. Sorry not sorry.
|
// We don't bother with the legacy (pre v1) keys. Sorry not sorry.
|
||||||
// Patch your shit.
|
// Patch your shit.
|
||||||
type SSHKeyV1 struct {
|
type SSHKeyV1 struct {
|
||||||
Magic string
|
Magic string
|
||||||
BitSize uint32
|
DefKeyType string
|
||||||
DefKeyType string
|
KDFName string
|
||||||
KDFName string
|
KeySize uint32
|
||||||
KeySize uint32
|
BlockSize uint32
|
||||||
BlockSize uint32
|
Keys []SSHPrivKey // 1 by default.
|
||||||
PublicKeys []SSHPubKey
|
Buffer bytes.Buffer
|
||||||
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 {
|
||||||
PrivateKey *SSHPrivKey
|
KeyType string
|
||||||
KeyType string
|
Key interface{}
|
||||||
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
|
||||||
Key []byte
|
BitSize uint32
|
||||||
Checksum []byte
|
Key interface{}
|
||||||
Comment string
|
// ED25519 keys are actually "sk + pk", where sk is the secret key and pk is the pubkey.
|
||||||
|
// We store that here.
|
||||||
|
KeyAlt []byte
|
||||||
|
Checksum []byte
|
||||||
|
Comment string
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user