2020-09-18 18:01:16 -04:00
|
|
|
/*
|
|
|
|
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 <https://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
2020-09-18 04:04:39 -04:00
|
|
|
package moduli
|
2020-09-24 04:38:29 -04:00
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"encoding/hex"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"net/http"
|
2021-07-03 23:01:58 -04:00
|
|
|
"time"
|
2020-09-24 04:38:29 -04:00
|
|
|
|
2021-07-03 23:01:58 -04:00
|
|
|
"github.com/Luzifer/go-dhparam"
|
2020-09-24 04:38:29 -04:00
|
|
|
"golang.org/x/crypto/sha3"
|
|
|
|
)
|
|
|
|
|
2021-05-04 03:39:43 -04:00
|
|
|
// NewModuli returns a Moduli populated with Entry items.
|
2021-07-03 23:01:58 -04:00
|
|
|
func NewModuli(usePreGen ...bool) (m *Moduli, err error) {
|
2021-05-04 03:39:43 -04:00
|
|
|
|
|
|
|
var doPreGen bool
|
|
|
|
|
|
|
|
m = new(Moduli)
|
|
|
|
|
|
|
|
if usePreGen != nil {
|
|
|
|
doPreGen = usePreGen[0]
|
|
|
|
} else {
|
|
|
|
doPreGen = false
|
|
|
|
}
|
|
|
|
|
|
|
|
if doPreGen {
|
|
|
|
if err = GetPreGen(m); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
// This may take a while.
|
|
|
|
if err = m.Harden(); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if err = Generate(m); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetPreGen gets the pregenerated moduli from upstream mirror.
|
|
|
|
func GetPreGen(m *Moduli) (err error) {
|
|
|
|
|
|
|
|
var b []byte
|
|
|
|
var goodCksum []byte
|
|
|
|
var resp *http.Response
|
|
|
|
|
2020-09-24 04:38:29 -04:00
|
|
|
// get the pregenerated moduli
|
2021-05-04 03:39:43 -04:00
|
|
|
resp, err = http.Get(pregenURL)
|
2020-09-24 04:38:29 -04:00
|
|
|
if err != nil {
|
2021-05-04 03:39:43 -04:00
|
|
|
return
|
2020-09-24 04:38:29 -04:00
|
|
|
}
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
2021-05-04 03:39:43 -04:00
|
|
|
err = errors.New(fmt.Sprintf("returned status code %v: %v", resp.StatusCode, resp.Status))
|
|
|
|
return
|
2020-09-24 04:38:29 -04:00
|
|
|
}
|
|
|
|
defer resp.Body.Close()
|
2021-05-04 03:39:43 -04:00
|
|
|
|
|
|
|
b = make([]byte, resp.ContentLength)
|
2020-09-24 04:38:29 -04:00
|
|
|
if _, err = resp.Body.Read(b); err != nil {
|
2021-05-04 03:39:43 -04:00
|
|
|
return
|
2020-09-24 04:38:29 -04:00
|
|
|
}
|
2021-05-04 03:39:43 -04:00
|
|
|
|
2020-09-24 04:38:29 -04:00
|
|
|
// and compare the SHA3-512 (NIST) checksum.
|
|
|
|
s := sha3.New512()
|
|
|
|
if _, err = s.Write(b); err != nil {
|
2021-05-04 03:39:43 -04:00
|
|
|
return
|
2020-09-24 04:38:29 -04:00
|
|
|
}
|
2021-05-04 03:39:43 -04:00
|
|
|
goodCksum, err = hex.DecodeString(pregenCksum)
|
2020-09-24 04:38:29 -04:00
|
|
|
if err != nil {
|
2021-05-04 03:39:43 -04:00
|
|
|
return
|
2020-09-24 04:38:29 -04:00
|
|
|
}
|
2021-05-04 03:39:43 -04:00
|
|
|
|
2020-09-24 04:38:29 -04:00
|
|
|
// We just compare the bytestrings.
|
|
|
|
if bytes.Compare(s.Sum(nil), goodCksum) != 0 {
|
2021-05-04 03:39:43 -04:00
|
|
|
err = errors.New("checksums do not match")
|
|
|
|
return
|
2020-09-24 04:38:29 -04:00
|
|
|
}
|
2021-05-04 03:39:43 -04:00
|
|
|
|
2020-09-27 03:23:58 -04:00
|
|
|
if err := Unmarshal(b, m); err != nil {
|
2021-05-04 03:39:43 -04:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Generate generates new moduli with Entry items. It's more secure than using GetPreGen (LogJam), but takes a *LOT* longer.
|
|
|
|
func Generate(m *Moduli) (err error) {
|
|
|
|
|
|
|
|
var dh *dhparam.DH
|
|
|
|
|
|
|
|
for _, bitLen := range genBits {
|
|
|
|
for _, generator := range genGenerators {
|
|
|
|
|
|
|
|
var e Entry
|
|
|
|
|
|
|
|
e = Entry{
|
2021-07-03 23:01:58 -04:00
|
|
|
Time: time.Now(),
|
|
|
|
Size: bitLen,
|
2021-05-04 03:39:43 -04:00
|
|
|
Generator: uint8(generator),
|
|
|
|
/*
|
2021-07-03 23:01:58 -04:00
|
|
|
Type: 0,
|
|
|
|
Tests: 0,
|
|
|
|
Trials: 0,
|
2021-05-04 03:39:43 -04:00
|
|
|
*/
|
|
|
|
}
|
|
|
|
|
|
|
|
if dh, err = dhparam.Generate(int(bitLen), generator, nil); err != nil {
|
|
|
|
continue // TODO: log/print
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check() applies big.Int.ProbablyPrime() (Miller-Rabin - 0x04 in ssh moduli - and Baillie-PSW test), so it's probably fine.
|
|
|
|
if errs, ok := dh.Check(); !ok {
|
|
|
|
_ = errs // TODO: log/print
|
|
|
|
continue
|
|
|
|
} else {
|
|
|
|
e.Time = time.Now()
|
|
|
|
// e.Type =
|
|
|
|
// e.Tests =
|
|
|
|
// e.Trials =
|
|
|
|
e.Modulus = *dh.P
|
|
|
|
|
|
|
|
// TODO: https://stackoverflow.com/questions/18499352/golang-concurrency-how-to-append-to-the-same-slice-from-different-goroutines
|
2021-07-03 23:01:58 -04:00
|
|
|
m.Groups = append(m.Groups, e)
|
2021-05-04 03:39:43 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2020-09-27 03:23:58 -04:00
|
|
|
}
|
2021-05-04 03:39:43 -04:00
|
|
|
|
|
|
|
return
|
2020-09-24 04:38:29 -04:00
|
|
|
}
|