yeah so as a temporary measure, i'm using ssh-keygen for now.

but i'll need to natively incorporate it still.
This commit is contained in:
brent s. 2021-07-03 23:01:58 -04:00
parent d7ffbea913
commit 7f98e7aa15
Signed by: bts
GPG Key ID: 8C004C2F93481F6B
9 changed files with 323 additions and 19 deletions

View File

@ -31,7 +31,7 @@ import (
"r00t2.io/sysutils/paths" "r00t2.io/sysutils/paths"
) )


func TestConfig(config *[]byte) (bool, error) { func ChkConfig(config *[]byte) (bool, error) {
var sysPaths []string var sysPaths []string
var binPath string var binPath string
var tmpConf *os.File var tmpConf *os.File

View File

@ -18,6 +18,17 @@


package dh package dh


import (
"io/ioutil"
"os"
"os/exec"
"strings"

"github.com/Masterminds/semver"
"r00t2.io/sshsecure/moduli"
"r00t2.io/sshsecure/utils"
)

/* /*
OpenSSH does prime generation and primality checking a *little* weird. OpenSSH does prime generation and primality checking a *little* weird.


@ -35,4 +46,129 @@ package dh
And that's why I'm a sad panda and porting moduli.c to native Golang. And that's why I'm a sad panda and porting moduli.c to native Golang.
*/ */


func SieveLarge() // Generate builds a slice of moduli.Entry entries.
// TODO: DO THIS NATIVELY. Idealy with goroutines and a channel? buffer? for the primes and sieving.
func Generate() (moduliEntries []moduli.Entry, err error) {

var outFile *os.File
var filteredFile string
var raw []byte
var m *moduli.Moduli = new(moduli.Moduli)

if outFile, err = genPrimes(); err != nil {
return
}

if filteredFile, err = sieve(outFile); err != nil {
return
}

if raw, err = ioutil.ReadFile(filteredFile); err != nil {
return
}

if err = moduli.Unmarshal(raw, m); err != nil {
return
}

moduliEntries = m.Groups

return
}

// genPrimes builds a slice of acceptable primes for sieve.
// TODO: DO THIS NATIVELY.
func genPrimes() (outFile *os.File, err error) {

var cmdStr []string = []string{"ssh-keygen", "-q"}
var cmdStr2 []string
var cmd *exec.Cmd
var sshVer utils.SshVerInfo
// These are various versions we need to compare against for determining features.
var ver81 *semver.Version

if outFile, err = ioutil.TempFile("", ".SSHSecure.moduli.*"); err != nil {
return
}
if err = outFile.Close(); err != nil {
return
}
if err = os.Remove(outFile.Name()); err != nil {
return
}

if sshVer, err = utils.GetSshVer(""); err != nil {
return
}

// They changed the command syntax/options in 8.1. We need to determine if we're at or above that version or not.
if ver81, err = semver.NewVersion("8.1.0"); err != nil {
return
}

if sshVer.SemVer.Compare(ver81) >= 0 {
cmdStr2 = []string{
"-M", "generate",
"-O", "bits=4096",
outFile.Name(),
}
} else {
cmdStr2 = []string{
"-b", "4096",
"-G", outFile.Name(),
}
}
cmdStr = append(cmdStr, cmdStr2...)

cmd = exec.Command(cmdStr[0], strings.Join(cmdStr[1:], " "))
if err = cmd.Run(); err != nil {
return
}

return
}

// sieve filters out unsuitable primes. See #1 and #2 in the comments at the top of this file.
// TODO: DO THIS NATIVELY.
func sieve(inFile *os.File) (newFile string, err error) {

var cmdStr []string = []string{"ssh-keygen", "-q"}
var cmdStr2 []string
var cmd *exec.Cmd
var sshVer utils.SshVerInfo
// These are various versions we need to compare against for determining features.
var ver81 *semver.Version

if sshVer, err = utils.GetSshVer(""); err != nil {
return
}

newFile = inFile.Name() + ".filtered"

// They changed the command syntax/options in 8.1. We need to determine if we're at or above that version or not.
if ver81, err = semver.NewVersion("8.1.0"); err != nil {
return
}

if sshVer.SemVer.Compare(ver81) >= 0 {
cmdStr2 = []string{
"-M", "screen",
"-O", "bits=4096",
"-f", inFile.Name(),
newFile,
}
} else {
cmdStr2 = []string{
"-T", newFile,
"-f", inFile.Name(),
}
}
cmdStr = append(cmdStr, cmdStr2...)

cmd = exec.Command(cmdStr[0], strings.Join(cmdStr[1:], " "))
if err = cmd.Run(); err != nil {
return
}

return
}

1
go.mod
View File

@ -4,6 +4,7 @@ go 1.16


require ( require (
github.com/Luzifer/go-dhparam v1.1.0 github.com/Luzifer/go-dhparam v1.1.0
github.com/Masterminds/semver v1.5.0
github.com/dchest/bcrypt_pbkdf v0.0.0-20150205184540-83f37f9c154a github.com/dchest/bcrypt_pbkdf v0.0.0-20150205184540-83f37f9c154a
github.com/go-restruct/restruct v1.2.0-alpha github.com/go-restruct/restruct v1.2.0-alpha
github.com/pkg/errors v0.9.1 github.com/pkg/errors v0.9.1

2
go.sum
View File

@ -1,5 +1,7 @@
github.com/Luzifer/go-dhparam v1.1.0 h1:uJXDwqAVy1H4zWjmsYVmaa9yUD2Pm3SsdW4KU8d27zc= github.com/Luzifer/go-dhparam v1.1.0 h1:uJXDwqAVy1H4zWjmsYVmaa9yUD2Pm3SsdW4KU8d27zc=
github.com/Luzifer/go-dhparam v1.1.0/go.mod h1:3Kuj59C67/G2EzQHjUzAryaAa70K5fqvStR2VkFLszU= github.com/Luzifer/go-dhparam v1.1.0/go.mod h1:3Kuj59C67/G2EzQHjUzAryaAa70K5fqvStR2VkFLszU=
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dchest/bcrypt_pbkdf v0.0.0-20150205184540-83f37f9c154a h1:saTgr5tMLFnmy/yg3qDTft4rE5DY2uJ/cCxCe3q0XTU= github.com/dchest/bcrypt_pbkdf v0.0.0-20150205184540-83f37f9c154a h1:saTgr5tMLFnmy/yg3qDTft4rE5DY2uJ/cCxCe3q0XTU=

View File

@ -24,14 +24,14 @@ import (
"errors" "errors"
"fmt" "fmt"
"net/http" "net/http"
`time` "time"


`github.com/Luzifer/go-dhparam` "github.com/Luzifer/go-dhparam"
"golang.org/x/crypto/sha3" "golang.org/x/crypto/sha3"
) )


// NewModuli returns a Moduli populated with Entry items. // NewModuli returns a Moduli populated with Entry items.
func NewModuli(usePreGen ... bool) (m *Moduli, err error) { func NewModuli(usePreGen ...bool) (m *Moduli, err error) {


var doPreGen bool var doPreGen bool


@ -117,13 +117,13 @@ func Generate(m *Moduli) (err error) {
var e Entry var e Entry


e = Entry{ e = Entry{
Time: time.Now(), Time: time.Now(),
Size: bitLen, Size: bitLen,
Generator: uint8(generator), Generator: uint8(generator),
/* /*
Type: 0, Type: 0,
Tests: 0, Tests: 0,
Trials: 0, Trials: 0,
*/ */
} }


@ -143,7 +143,7 @@ func Generate(m *Moduli) (err error) {
e.Modulus = *dh.P e.Modulus = *dh.P


// TODO: https://stackoverflow.com/questions/18499352/golang-concurrency-how-to-append-to-the-same-slice-from-different-goroutines // TODO: https://stackoverflow.com/questions/18499352/golang-concurrency-how-to-append-to-the-same-slice-from-different-goroutines
m.Params = append(m.Params, e) m.Groups = append(m.Groups, e)
} }


} }

View File

@ -34,13 +34,12 @@ var reSkipLine, _ = regexp.Compile(`^\s*(#.*)?$`)


// Marshal returns the /etc/ssh/moduli format of m. // Marshal returns the /etc/ssh/moduli format of m.
// Format of: Time Type Tests Tries Size Generator Modulus // Format of: Time Type Tests Tries Size Generator Modulus
// TODO: remember to write newline at end
func (m *Moduli) Marshal() (bytesOut []byte, err error) { func (m *Moduli) Marshal() (bytesOut []byte, err error) {


var b bytes.Buffer var b bytes.Buffer


b.Write([]byte(header)) b.Write([]byte(header))
for _, i := range m.Params { for _, i := range m.Groups {
line, err := i.marshalEntry() line, err := i.marshalEntry()
if err != nil { if err != nil {
return nil, err return nil, err
@ -49,6 +48,8 @@ func (m *Moduli) Marshal() (bytesOut []byte, err error) {
} }
} }


b.Write([]byte("\n"))

bytesOut = b.Bytes() bytesOut = b.Bytes()


return return
@ -98,7 +99,7 @@ func Unmarshal(data []byte, m *Moduli) (err error) {
entries = append(entries, e) entries = append(entries, e)
} }


m.Params = entries m.Groups = entries


return return
} }
@ -149,7 +150,7 @@ func (m *Moduli) Harden() (err error) {


var entries []Entry var entries []Entry


for _, e := range m.Params { for _, e := range m.Groups {


e.Time = time.Now() e.Time = time.Now()


@ -157,9 +158,9 @@ func (m *Moduli) Harden() (err error) {
entries = append(entries, e) entries = append(entries, e)
} }
} }
m.Params = entries m.Groups = entries


if len(m.Params) < recMinMod { if len(m.Groups) < recMinMod {
err = errors.New("does not meet recommended minimum moduli") err = errors.New("does not meet recommended minimum moduli")
return return
} }

View File

@ -23,10 +23,10 @@ import (
"time" "time"
) )


// Moduli contains all data needed for generated /etc/ssh/moduli. of ModuliEntry entries. // Moduli contains all data needed for generated /etc/ssh/moduli of Entry entries.
type Moduli struct { type Moduli struct {
Header string Header string
Params []Entry Groups []Entry
} }


// Entry is a struct reflecting the format of a single /etc/ssh/moduli entry. See moduli(5) for details. // Entry is a struct reflecting the format of a single /etc/ssh/moduli entry. See moduli(5) for details.

143
utils/func.go Normal file
View File

@ -0,0 +1,143 @@
package utils

import (
"fmt"
"os/exec"
"regexp"
"strconv"
"strings"
"time"

"github.com/Masterminds/semver"
)

// GetSshVer returns an SshVerInfo. If verStr is an empty string, get the version automatically.
func GetSshVer(verStr string) (ver SshVerInfo, err error) {

var newVerStr string
var sslVer OpenSslInfo
var sslVerStr string
var ptrn *regexp.Regexp
var matches []string
var verSlice [3]uint
var n int
var sver *semver.Version

if verStr == "" {
var out []byte
cmd := exec.Command("ssh", "-V")
if err = cmd.Run(); err != nil {
return
}
if out, err = cmd.Output(); err != nil {
return
}
verStr = string(out)
}

if ptrn, err = regexp.Compile(`^(?:Open|Sun_)SSH_(?P<sshver>[0-9.]+)(?:p?(?P<patchver>[0-9.]+))?,\s*(?P<sslverstr>.*)?$`); err != nil {
return
}

matches = ptrn.FindStringSubmatch(verStr)

// Reformat verStr to the combined version string.
newVerStr = fmt.Sprintf(
"%v.%v",
matches[ptrn.SubexpIndex("sshver")],
matches[ptrn.SubexpIndex("patchver")],
)

sslVerStr = matches[ptrn.SubexpIndex("sslverstr")]

if sslVer, err = GetSslVer(sslVerStr); err != nil {
return
}

for idx, s := range strings.Split(newVerStr, ".") {
if n, err = strconv.Atoi(s); err != nil {
return
}
verSlice[idx] = uint(n)
}

if sver, err = semver.NewVersion(newVerStr); err != nil {
return
}

ver = SshVerInfo{
Major: verSlice[0],
Minor: verSlice[1],
Patch: verSlice[2],
Raw: verStr,
SslInfo: sslVer,
SemVer: *sver,
}

return
}

// GetSslVer returns an OpenSslInfo. If verStr is an empty string, get the version automatically.
func GetSslVer(verStr string) (ver OpenSslInfo, err error) {

var matches []string
var verSlice [3]uint
var newVerStr string
var ptrn *regexp.Regexp
var sver *semver.Version
// TODO: confirm the day is zero-padded.
var tfmt string = `02 Jan 2006`
var n int
var t time.Time

if verStr == "" {
var out []byte
cmd := exec.Command("openssl", "version")
if err = cmd.Run(); err != nil {
return
}
if out, err = cmd.Output(); err != nil {
return
}
verStr = string(out)
}

if ptrn, err = regexp.Compile(`^(?:Open|Sun_)SSL\s+(FIPS\s+)?(?P<sslver>[0-9.]+)(?P<sslrel>[A-Za-z]+)?\s+(?P<time>(?P<day>[0-9]{1,2})\s(?P<month>[A-Za-z]+)\s(?P<year>[0-9]{4}))$`); err != nil {
return
}

matches = ptrn.FindStringSubmatch(verStr)

for idx, s := range strings.Split(matches[ptrn.SubexpIndex("sslver")], ".") {
if n, err = strconv.Atoi(s); err != nil {
return
}
verSlice[idx] = uint(n)
}

if t, err = time.Parse(tfmt, matches[ptrn.SubexpIndex("time")]); err != nil {
return
}

newVerStr = fmt.Sprintf(
"%v-%v",
matches[ptrn.SubexpIndex("sslver")],
matches[ptrn.SubexpIndex("sslrel")],
)

if sver, err = semver.NewVersion(newVerStr); err != nil {
return
}

ver = OpenSslInfo{
Major: verSlice[0],
Minor: verSlice[1],
Patch: verSlice[2],
Revision: matches[ptrn.SubexpIndex("sslrel")],
BuildDate: t,
Raw: verStr,
SemVer: *sver,
}

return
}

View File

@ -2,9 +2,30 @@ package utils


import ( import (
"time" "time"

"github.com/Masterminds/semver"
) )


type runInfo struct { type runInfo struct {
TimeStarted time.Time TimeStarted time.Time
Pid int Pid int
} }

type SshVerInfo struct {
Major uint
Minor uint
Patch uint
Raw string
SemVer semver.Version
SslInfo OpenSslInfo
}

type OpenSslInfo struct {
Major uint
Minor uint
Patch uint
Revision string
BuildDate time.Time
Raw string
SemVer semver.Version
}