diff --git a/TODO b/TODO
index 7c8e358..45c53f9 100644
--- a/TODO
+++ b/TODO
@@ -19,6 +19,7 @@
-- Check if haveged is running. If not and installed, start it.
-- Generate moduli
-- Render to /etc/ssh/moduli format
+--- custom moduli marshaler/unmarshaler? (e.g. https://stackoverflow.com/a/50211222)
-- Write to dest
- Config
diff --git a/go.mod b/go.mod
index 6da26fe..2549b8b 100644
--- a/go.mod
+++ b/go.mod
@@ -1,4 +1,4 @@
-module git.square-r00t.net/sshsecure
+module r00t2.io/sshsecure
go 1.15
diff --git a/moduli/const.go b/moduli/const.go
index df5bbf7..bf5cec0 100644
--- a/moduli/const.go
+++ b/moduli/const.go
@@ -17,3 +17,55 @@
*/
package moduli
+
+import (
+ "fmt"
+
+ "r00t2.io/sshsecure/sharedconsts"
+)
+
+// Misc
+const (
+ // Where to find an up-to-date copy of the upstream moduli and its SHA3-512 (NIST) checksum.
+ pregenURL string = "https://anongit.mindrot.org/openssh.git/tree/moduli"
+ // This is the best way I could think of to verify integrity, since the file itself doesn't have a signature or anything like that.
+ pregenCksum string = "106EDB19A936608D065D2E8E81F7BDE7" +
+ "434AF80EF81102E9440B99ACB98FBEF8" +
+ "CC2F4B6BFD76828337BDB1F2CF34D859" +
+ "045285DCE6B0DE7D7D93A9EE61F8CC96"
+ // The tag name to use for struct tags (marshal/unmarshaling)
+ parseTag string = "sshmoduli"
+ // The recommended minimum moduli to have available.
+ recMinMod int = 400
+ // The
+)
+
+// The header line on the /etc/ssh/moduli file.
+var header = string(
+ fmt.Sprintf(
+ "# %v\n"+
+ "# Time Type Tests Tries Size Generator Modulus\n", sharedconsts.IDCmnt,
+ ),
+)
+
+// For parsing/rendering /etc/ssh/moduli
+const (
+ // Golang has no strftime formatting codes. It operates on *display of a specific time*.
+ // What a dumb language.
+ timeFormat string = "20060102150405" // %Y%m%d%H%M%S
+)
+
+// For validation
+var (
+ validTypes = []uint8{
+ 0, // Unknown, not tested
+ 2, // "Safe" prime; (p-1)/2 is also prime.
+ 4, // Sophie Germain; 2p+1 is also prime.
+ }
+ validTests = []byte{
+ 0x00, // Not tested.
+ 0x01, // Composite number - not prime.
+ 0x02, // Sieve of Eratosthenes.
+ 0x04, // Probabilistic Miller-Rabin primality tests.
+ }
+)
diff --git a/moduli/func.go b/moduli/func.go
index df5bbf7..e946058 100644
--- a/moduli/func.go
+++ b/moduli/func.go
@@ -17,3 +17,44 @@
*/
package moduli
+
+import (
+ "bytes"
+ "encoding/hex"
+ "errors"
+ "fmt"
+ "net/http"
+
+ "golang.org/x/crypto/sha3"
+)
+
+func getPregen() ([]byte, error) {
+ // get the pregenerated moduli
+ resp, err := http.Get(pregenURL)
+ if err != nil {
+ return nil, err
+ }
+ if resp.StatusCode != http.StatusOK {
+ return nil, errors.New(fmt.Sprintf("returned status code %v: %v", resp.StatusCode, resp.Status))
+ }
+ defer resp.Body.Close()
+ b := make([]byte, resp.ContentLength)
+ if _, err = resp.Body.Read(b); err != nil {
+ return nil, err
+ }
+ // and compare the SHA3-512 (NIST) checksum.
+ s := sha3.New512()
+ if _, err = s.Write(b); err != nil {
+ // TODO: return nil instead of b?
+ return b, err
+ }
+ goodCksum, err := hex.DecodeString(pregenCksum)
+ if err != nil {
+ return nil, err
+ }
+ // We just compare the bytestrings.
+ if bytes.Compare(s.Sum(nil), goodCksum) != 0 {
+ return nil, errors.New("checksums do not match")
+ }
+ return b, nil
+}
diff --git a/moduli/parser.go b/moduli/parser.go
new file mode 100644
index 0000000..d503bee
--- /dev/null
+++ b/moduli/parser.go
@@ -0,0 +1,119 @@
+/*
+ SSHSecure - a program to harden OpenSSH from defaults
+ Copyright (C) 2020 Brent Saner
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+package moduli
+
+import (
+ "bytes"
+ "encoding/hex"
+ "errors"
+ "fmt"
+ "math/big"
+ "regexp"
+ "strconv"
+ "strings"
+ "time"
+)
+
+var reSkipLine, _ = regexp.Compile(`^\s*(#.*)?$`)
+
+// Marshal returns the /etc/ssh/moduli format of m.
+// Format of: Time Type Tests Tries Size Generator Modulus
+// TODO: remember to write newline at end
+func (m *Moduli) Marshal() ([]byte, error) {
+ var b bytes.Buffer
+ b.Write([]byte(header))
+ for _, i := range m.Params {
+ line, err := i.marshalEntry()
+ if err != nil {
+ return b.Bytes(), err
+ } else {
+ b.Write(line)
+ }
+ }
+ return b.Bytes(), nil
+}
+
+// marshalEntry is used to parse a specific DH entry into the moduli.
+func (m *ModuliEntry) marshalEntry() ([]byte, error) {
+ mod := hex.EncodeToString(m.Modulus.Bytes())
+ s := fmt.Sprintf(
+ "%v %v %v %v %v %v %v\n",
+ m.Time.Format(timeFormat),
+ string(m.Type),
+ string(m.Tests),
+ string(m.Trials),
+ string(m.Size),
+ string(m.Generator),
+ mod,
+ )
+ return []byte(s), nil
+}
+
+// Unmarshal writes the Moduli format into m from the /etc/ssh/moduli format in data.
+func Unmarshal(data []byte, m Moduli) error {
+ var lines []string
+ var entries []ModuliEntry
+ lines = strings.Split(string(data), "\n")
+ for _, line := range lines {
+ e := ModuliEntry{}
+ if reSkipLine.MatchString(line) {
+ continue
+ }
+ l := strings.Fields(line)
+ if err := unmarshalEntry(l, e); err != nil {
+ return err
+ }
+ entries = append(entries, e)
+ }
+ m.Params = entries
+ return nil
+}
+
+func unmarshalEntry(line []string, m ModuliEntry) error {
+ if len(line) != 7 {
+ return errors.New("field count mismatch")
+ }
+ if m.Time, err = time.Parse(timeFormat, line[0]); err != nil {
+ return err
+ }
+ // Numeric types. Cast to uint8. There's probably a better way to do this but golang's pretty ugly with this stuff no matter what.
+ // Type, Tests, Trials, Size, Generator
+ conv := [5]uint8{}
+ for idx := 1; idx <= 5; idx++ {
+ v := line[idx]
+ newv, err := strconv.Atoi(v)
+ if err != nil {
+ return err
+ }
+ conv[idx-1] = uint8(newv)
+ }
+ m.Type = conv[0]
+ m.Tests = conv[1]
+ m.Trials = conv[2]
+ m.Size = conv[3]
+ m.Generator = conv[4]
+ // And the modulus convert to big.Int.
+ modb, err := hex.DecodeString(line[6])
+ if err != nil {
+ return err
+ }
+ m.Modulus = big.Int{}
+ m.Modulus.SetBytes(modb)
+ return nil
+}
diff --git a/moduli/ref/sources b/moduli/ref/sources
index 02a9624..ba2788a 100644
--- a/moduli/ref/sources
+++ b/moduli/ref/sources
@@ -4,4 +4,5 @@ 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
-https://github.com/hyperreality/cryptopals-2/blob/517c1907b2041e6f7ef18930eca2aa3a24fb73d8/dh.go
\ No newline at end of file
+https://github.com/hyperreality/cryptopals-2/blob/517c1907b2041e6f7ef18930eca2aa3a24fb73d8/dh.go
+https://sosedoff.com/2016/07/16/golang-struct-tags.html
\ No newline at end of file
diff --git a/moduli/struct.go b/moduli/struct.go
index df5bbf7..ccff1f2 100644
--- a/moduli/struct.go
+++ b/moduli/struct.go
@@ -17,3 +17,62 @@
*/
package moduli
+
+import (
+ "math/big"
+ "time"
+)
+
+// Moduli contains all data needed for generated /etc/ssh/moduli. of ModuliEntry entries.
+type Moduli struct {
+ Header string
+ Params []ModuliEntry
+}
+
+// Moduli is a struct reflecting the format of a single /etc/ssh/moduli entry. See moduli(5) for details.
+type ModuliEntry struct {
+ Time time.Time // YYYYMMDDHHSS
+ /*
+ // man 5 moduli:
+ Decimal number specifying the internal structure of the prime modulus. Supported types are:
+ 0 Unknown, not tested.
+ 2 "Safe" prime; (p-1)/2 is also prime.
+ 4 Sophie Germain; 2p+1 is also prime.
+ Moduli candidates initially produced by ssh-keygen(1) are Sophie Germain primes (type 4).
+ Further primality testing with ssh-keygen(1) produces safe prime moduli (type 2) that are ready for use in sshd(8).
+ Other types are not used by OpenSSH.
+ */
+ Type uint8
+ /*
+ // man 5 moduli:
+ Decimal number indicating the type of primality tests that the number has been
+ subjected to represented as a bitmask of the following values:
+ 0x00 Not tested.
+ 0x01 Composite number – not prime.
+ 0x02 Sieve of Eratosthenes.
+ 0x04 Probabilistic Miller-Rabin primality tests.
+ The ssh-keygen(1) moduli candidate generation uses the Sieve of Eratosthenes (flag 0x02).
+ Subsequent ssh-keygen(1) primality tests are Miller-Rabin tests (flag 0x04).
+ */
+ Tests uint8
+ /*
+ // man 5 moduli:
+ Decimal number indicating the number of primality trials that have been performed on the modulus.
+ */
+ Trials uint8
+ /*
+ // man 5 moduli:
+ Decimal number indicating the size of the prime in bits.
+ */
+ Size uint8
+ /*
+ // man 5 moduli:
+ The recommended generator for use with this modulus (hexadecimal).
+ */
+ Generator uint8
+ /*
+ // man 5 moduli:
+ The modulus itself in hexadecimal.
+ */
+ Modulus big.Int
+}
diff --git a/sharedconsts/const.go b/sharedconsts/const.go
new file mode 100644
index 0000000..854ab83
--- /dev/null
+++ b/sharedconsts/const.go
@@ -0,0 +1,13 @@
+package sharedconsts
+
+import (
+ "fmt"
+)
+
+// "Meta". Used for comment strings, etc.
+const (
+ projName = "SSHSecure"
+ projUrl = "https://git.square-r00t.net/SSHSecure"
+)
+
+var IDCmnt = string(fmt.Sprintf("Autogenerated by %v (%v)", projName, projUrl))
diff --git a/sshkeys/const.go b/sshkeys/const.go
index 8eaddd2..77df225 100644
--- a/sshkeys/const.go
+++ b/sshkeys/const.go
@@ -23,10 +23,6 @@ const (
KeyV1Magic string = "openssh-key-v1"
)
-// "Meta". Used for comment strings, etc.
-
-const projUrl = "https://git.square-r00t.net/SSHSecure"
-
// Defaults.
const (
defCipher string = CipherAes256Ctr
diff --git a/sshkeys/func.go b/sshkeys/func.go
index 8ef71ca..305fdfe 100644
--- a/sshkeys/func.go
+++ b/sshkeys/func.go
@@ -19,10 +19,12 @@
package sshkeys
import (
- `bytes`
+ "bytes"
"crypto/rand"
"errors"
"fmt"
+
+ "r00t2.io/sshsecure/sharedconsts"
)
func (k *EncryptedSSHKeyV1) validate() error {
@@ -86,7 +88,7 @@ func (k *EncryptedSSHKeyV1) Generate(force bool) error {
// 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),
+ Comment: sharedconsts.IDCmnt,
}
pk.Checksum = make([]byte, 4)
if _, err := rand.Read(pk.Checksum); err != nil {
@@ -181,7 +183,7 @@ func (k *EncryptedSSHKeyV1) buildKeybuf() error {
// Since this is encrypted, the private key blobs are encrypted.
// OpenSSH keys currently only support 1 keypair but support more *in theory*. This *seems* to be how they plan on doing it.
// But boy, is it a pain. So much wasted RAM and CPU cycles. They should use terminating byte sequences IMHO but whatever.
- for _, i := range k.Keys { // 4.0.0 and 4.0.1
+ for _, i := range k.Keys { // 4.0.0 and 4.0.1
switch k.CipherName {
case CipherAes256Ctr:
i.BlockSize = k.Crypt.Cipher.BlockSize()
@@ -209,7 +211,7 @@ func (k *SSHKeyV1) buildKeybuf() error {
// Add the keypairs.
// OpenSSH keys currently only support 1 keypair but support more *in theory*. This *seems* to be how they plan on doing it.
// But boy, is it a pain. So much wasted RAM and CPU cycles. They should use terminating byte sequences IMHO but whatever.
- for _, i := range k.Keys { // 4.0.0 and 4.0.1
+ for _, i := range k.Keys { // 4.0.0 and 4.0.1
i.BlockSize = 8
kbPtr, err := i.keyBlob(nil, false)
if err != nil {
@@ -224,19 +226,19 @@ func (k *SSHKeyV1) addHeader() error {
// TODO: error handling for each .Write()?
// First we need to do some prep for the plaintext header.
var kdfOptsBytes []byte
- kdfOptsBytes = k.getKdfOptBytes() // 3.0.0
- cipherBytes := []byte(k.CipherName) // 1.0
- kdf := []byte(k.KDFName) // 2.0.0
+ kdfOptsBytes = k.getKdfOptBytes() // 3.0.0
+ cipherBytes := []byte(k.CipherName) // 1.0
+ kdf := []byte(k.KDFName) // 2.0.0
// This is just cast to an array for visual readability.
commonHeader := [][]byte{
- []byte(KeyV1Magic + "\x00"), // 0
- getBytelenByteArr(cipherBytes), // 1.0
- cipherBytes, // 1.0.0
- getBytelenByteArr(kdf), // 2.0
- kdf, // 2.0.0
- getBytelenByteArr(kdfOptsBytes), // 3.0
- kdfOptsBytes, // 3.0.0
- getByteInt(len(k.Keys)), // 4.0
+ []byte(KeyV1Magic + "\x00"), // 0
+ getBytelenByteArr(cipherBytes), // 1.0
+ cipherBytes, // 1.0.0
+ getBytelenByteArr(kdf), // 2.0
+ kdf, // 2.0.0
+ getBytelenByteArr(kdfOptsBytes), // 3.0
+ kdfOptsBytes, // 3.0.0
+ getByteInt(len(k.Keys)), // 4.0
}
for _, v := range commonHeader {
if _, err := k.Buffer.Write(v); err != nil {
@@ -247,28 +249,28 @@ func (k *SSHKeyV1) addHeader() error {
}
func (k *EncryptedSSHKeyV1) getKdfOptBytes() []byte {
- var kdfOptsBytes []byte // 3.0.0
+ var kdfOptsBytes []byte // 3.0.0
// This is *probably* more efficient than using a buffer just for these bytes.
- kdfOptsBytes = append(kdfOptsBytes, byte(len(k.KDFOpts.Salt))) // 3.0.0.0
- kdfOptsBytes = append(kdfOptsBytes, k.KDFOpts.Salt...) // 3.0.0.0.0
- kdfOptsBytes = append(kdfOptsBytes, byte(k.KDFOpts.Rounds)) // 3.0.0.1
+ kdfOptsBytes = append(kdfOptsBytes, byte(len(k.KDFOpts.Salt))) // 3.0.0.0
+ kdfOptsBytes = append(kdfOptsBytes, k.KDFOpts.Salt...) // 3.0.0.0.0
+ kdfOptsBytes = append(kdfOptsBytes, byte(k.KDFOpts.Rounds)) // 3.0.0.1
return kdfOptsBytes
}
func (k *SSHKeyV1) getKdfOptBytes() []byte {
- var kdfOptsBytes []byte // 3.0.0
+ var kdfOptsBytes []byte // 3.0.0
// No-op; unencrypted keys' KDFOpts are encapsulated by a single null byte (which the caller implements).
return kdfOptsBytes
}
func (pk *SSHPrivKey) keyBlob(c *SSHCrypt, encrypt bool) (*[]byte, error) {
// TODO: error handling for each .Write()?
- var keypairBytes bytes.Buffer // (4.0's children)
- var pubkeyBytes bytes.Buffer // 4.0.0 children (4.0.0 itself is handled before writing to keypairBytes)
- var privkeyBytes bytes.Buffer // 4.0.1 (and children)
- pubkeyName := []byte(pk.PublicKey.KeyType) // (4.0.0.0.0, cast to var because I'm lazy)
- pubkeyBytes.Write(getBytelenByteArr(pubkeyName)) // 4.0.0.0
- pubkeyBytes.Write(pubkeyName) // 4.0.0.0.0
+ var keypairBytes bytes.Buffer // (4.0's children)
+ var pubkeyBytes bytes.Buffer // 4.0.0 children (4.0.0 itself is handled before writing to keypairBytes)
+ var privkeyBytes bytes.Buffer // 4.0.1 (and children)
+ pubkeyName := []byte(pk.PublicKey.KeyType) // (4.0.0.0.0, cast to var because I'm lazy)
+ pubkeyBytes.Write(getBytelenByteArr(pubkeyName)) // 4.0.0.0
+ pubkeyBytes.Write(pubkeyName) // 4.0.0.0.0
// TODO: Optimize?
/*
THE PUBLIC KEY
@@ -280,17 +282,17 @@ func (pk *SSHPrivKey) keyBlob(c *SSHCrypt, encrypt bool) (*[]byte, error) {
pubkeyBytes.Write(pk.PublicKey.Key.([]byte)) // 4.0.0.1.0
case KeyRsa:
// How messy.
- var en bytes.Buffer // 4.0.0.1 and 4.0.0.2
+ var en bytes.Buffer // 4.0.0.1 and 4.0.0.2
// TODO: does e need getByteInt()?
- e := pk.PublicKey.Key.E.Bytes() // 4.0.0.1.0
+ e := pk.PublicKey.Key.E.Bytes() // 4.0.0.1.0
// TODO: does n need nullbyte prefix?
- n := pk.PublicKey.Key.N.Bytes() // 4.0.0.2.0
- en.Write(getBytelenByteArr(e)) // 4.0.0.1
- en.Write(e) // 4.0.0.1.0
- en.Write(getBytelenByteArr(n)) // 4.0.0.2
- en.Write(n) // 4.0.0.2.0
- pubkeyBytes.Write(getBytelenByteArr(en.Bytes())) // 4.0.0
- if _, err := en.WriteTo(&pubkeyBytes); err != nil { // (4.0.0 children)
+ n := pk.PublicKey.Key.N.Bytes() // 4.0.0.2.0
+ en.Write(getBytelenByteArr(e)) // 4.0.0.1
+ en.Write(e) // 4.0.0.1.0
+ en.Write(getBytelenByteArr(n)) // 4.0.0.2
+ en.Write(n) // 4.0.0.2.0
+ pubkeyBytes.Write(getBytelenByteArr(en.Bytes())) // 4.0.0
+ if _, err := en.WriteTo(&pubkeyBytes); err != nil { // (4.0.0 children)
return nil, err
}
}
@@ -300,7 +302,7 @@ func (pk *SSHPrivKey) keyBlob(c *SSHCrypt, encrypt bool) (*[]byte, error) {
*/
// First we need two checksums.
for i := 1; i <= 2; i++ {
- privkeyBytes.Write(pk.Checksum) // 4.0.1.0 and 4.0.1.1
+ privkeyBytes.Write(pk.Checksum) // 4.0.1.0 and 4.0.1.1
}
// And then add the public keys. Yes, the public keys are included in the private keys.
privkeyBytes.Write(getBytelenByteArr(pubkeyName)) // 4.0.1.2.0
@@ -308,57 +310,57 @@ func (pk *SSHPrivKey) keyBlob(c *SSHCrypt, encrypt bool) (*[]byte, error) {
switch pk.PublicKey.KeyType {
case KeyEd25519:
// This is easy.
- privkeyBytes.Write(pubkeyBytes.Bytes()) // 4.0.1.2.1
- if _, err := pubkeyBytes.WriteTo(&privkeyBytes); err != nil { // 4.0.1.2.1.0
+ privkeyBytes.Write(pubkeyBytes.Bytes()) // 4.0.1.2.1
+ if _, err := pubkeyBytes.WriteTo(&privkeyBytes); err != nil { // 4.0.1.2.1.0
return nil, err
}
case KeyRsa:
// This is not. We more or less have to do the same thing as the public key, BUT with e and n flipped. Gorram it.
- var ne bytes.Buffer // (4.0.1.2 children)
+ var ne bytes.Buffer // (4.0.1.2 children)
// TODO: does n need nullbyte prefix?
- n := pk.PublicKey.Key.N.Bytes() // 4.0.1.2.1.0
+ n := pk.PublicKey.Key.N.Bytes() // 4.0.1.2.1.0
// TODO: does e need getByteInt()?
- e := pk.PublicKey.Key.E.Bytes() // 4.0.1.2.2.0
- ne.Write(getBytelenByteArr(n)) // 4.0.1.2.1
- ne.Write(n) // 4.0.1.2.1.0
- ne.Write(getBytelenByteArr(e)) // 4.0.1.2.2
- ne.Write(e) // 4.0.1.2.2.0
- if _, err := ne.WriteTo(&privkeyBytes); err != nil { // (4.0.1.2 children)
+ e := pk.PublicKey.Key.E.Bytes() // 4.0.1.2.2.0
+ ne.Write(getBytelenByteArr(n)) // 4.0.1.2.1
+ ne.Write(n) // 4.0.1.2.1.0
+ ne.Write(getBytelenByteArr(e)) // 4.0.1.2.2
+ ne.Write(e) // 4.0.1.2.2.0
+ if _, err := ne.WriteTo(&privkeyBytes); err != nil { // (4.0.1.2 children)
return nil, err
}
}
// And then we add the *actual* private keys.
switch pk.PublicKey.KeyType {
case KeyEd25519:
- privkeyBytes.Write(getBytelenByteArr(pk.KeyAlt)) // 4.0.1.3
- privkeyBytes.Write(pk.KeyAlt) // 4.0.1.3.0
+ privkeyBytes.Write(getBytelenByteArr(pk.KeyAlt)) // 4.0.1.3
+ privkeyBytes.Write(pk.KeyAlt) // 4.0.1.3.0
case KeyRsa:
- var dcpq bytes.Buffer // 4.0.1.3 to 4.0.1.6
- d := pk.Key.D.Bytes() // 4.0.1.3.0
- crt := pk.Key.Precomputed.Qinv.Bytes() // 4.0.1.4.0
+ var dcpq bytes.Buffer // 4.0.1.3 to 4.0.1.6
+ d := pk.Key.D.Bytes() // 4.0.1.3.0
+ crt := pk.Key.Precomputed.Qinv.Bytes() // 4.0.1.4.0
// TODO: does p need nullbyte prefix?
- p := pk.Key.Primes[0].Bytes() // 4.0.1.5.0
+ p := pk.Key.Primes[0].Bytes() // 4.0.1.5.0
// TODO: does q need nullbyte prefix?
- q := pk.Key.Primes[1].Bytes() // 4.0.1.6.0
- dcpq.Write(getBytelenByteArr(d)) // 4.0.1.3
- dcpq.Write(d) // 4.0.1.3.0
- dcpq.Write(getBytelenByteArr(crt)) // 4.0.1.4
- dcpq.Write(crt) // 4.0.1.4.0
- dcpq.Write(getBytelenByteArr(p)) // 4.0.1.5
- dcpq.Write(p) // 4.0.1.5.0
- dcpq.Write(getBytelenByteArr(q)) // 4.0.1.6
- dcpq.Write(q) // 4.0.1.6.0
- if _, err := dcpq.WriteTo(&privkeyBytes); err != nil { // 4.0.1.3 to 4.0.1.6
+ q := pk.Key.Primes[1].Bytes() // 4.0.1.6.0
+ dcpq.Write(getBytelenByteArr(d)) // 4.0.1.3
+ dcpq.Write(d) // 4.0.1.3.0
+ dcpq.Write(getBytelenByteArr(crt)) // 4.0.1.4
+ dcpq.Write(crt) // 4.0.1.4.0
+ dcpq.Write(getBytelenByteArr(p)) // 4.0.1.5
+ dcpq.Write(p) // 4.0.1.5.0
+ dcpq.Write(getBytelenByteArr(q)) // 4.0.1.6
+ dcpq.Write(q) // 4.0.1.6.0
+ if _, err := dcpq.WriteTo(&privkeyBytes); err != nil { // 4.0.1.3 to 4.0.1.6
return nil, err
}
}
// Add the comment.
- privkeyBytes.Write(getBytelenByteArr([]byte(pk.Comment))) // 4.0.1.4 (ED25519), 4.0.1.7 (RSA)
- privkeyBytes.Write([]byte(pk.Comment)) // 4.0.1.4.0 (ED25519), 4.0.1.7.0 (RSA)
+ privkeyBytes.Write(getBytelenByteArr([]byte(pk.Comment))) // 4.0.1.4 (ED25519), 4.0.1.7 (RSA)
+ privkeyBytes.Write([]byte(pk.Comment)) // 4.0.1.4.0 (ED25519), 4.0.1.7.0 (RSA)
// Add padding
pad := 0
n := 0
- for len(privkeyBytes.Bytes()) % pk.BlockSize != 0 { // 4.0.1.5 (ED25519), 4.0.1.8 (RSA)
+ for len(privkeyBytes.Bytes())%pk.BlockSize != 0 { // 4.0.1.5 (ED25519), 4.0.1.8 (RSA)
n++
pad = n & pk.BlockSize
privkeyBytes.Write(getSingleByteInt(pad))
@@ -377,15 +379,15 @@ func (pk *SSHPrivKey) keyBlob(c *SSHCrypt, encrypt bool) (*[]byte, error) {
encBytes = []byte{}
}
// Get the respective lengths and add child buffers to buffer.
- keypairBytes.Write(getByteInt(len(pubkeyBytes.Bytes()))) // 4.0.0
- if _, err := pubkeyBytes.WriteTo(&keypairBytes); err != nil { // (4.0.0 children)
+ keypairBytes.Write(getByteInt(len(pubkeyBytes.Bytes()))) // 4.0.0
+ if _, err := pubkeyBytes.WriteTo(&keypairBytes); err != nil { // (4.0.0 children)
return nil, err
}
- keypairBytes.Write(getByteInt(len(privkeyBytes.Bytes()))) // 4.0.1
- if _, err := privkeyBytes.WriteTo(&keypairBytes); err != nil { // (4.0.1 children)
+ keypairBytes.Write(getByteInt(len(privkeyBytes.Bytes()))) // 4.0.1
+ if _, err := privkeyBytes.WriteTo(&keypairBytes); err != nil { // (4.0.1 children)
return nil, err
}
// Done!
kpSlice := keypairBytes.Bytes()
return &kpSlice, nil
-}
\ No newline at end of file
+}