starting moduli.c port from OpenSSH
This commit is contained in:
parent
d3a5f039c1
commit
a31d50359a
@ -19,9 +19,8 @@
|
|||||||
|
|
||||||
## Why?
|
## Why?
|
||||||
Compared to something like [`rsh`](https://en.wikipedia.org/wiki/Remote_Shell), SSH (*Secure SHell*) is a vast step ahead in terms of security. Since its birth, it's seen
|
Compared to something like [`rsh`](https://en.wikipedia.org/wiki/Remote_Shell), SSH (*Secure SHell*) is a vast step ahead in terms of security. Since its birth, it's seen
|
||||||
functionality
|
functionality increase by leaps and bounds.
|
||||||
increase
|
[OpenSSH](https://www.openssh.com/), by far the most deployed SSH implementation, pays special attention to security. However, due to:
|
||||||
by leaps and bounds. [OpenSSH](https://www.openssh.com/), by far the most deployed SSH implementation, pays special attention to security. However, due to:
|
|
||||||
|
|
||||||
* making various compromises for ease of use
|
* making various compromises for ease of use
|
||||||
* unexpected vulnerabilities (are there ever any *expected* vulnerabilities?) such as [Logjam](https://weakdh.org/)
|
* unexpected vulnerabilities (are there ever any *expected* vulnerabilities?) such as [Logjam](https://weakdh.org/)
|
||||||
|
51
dh/README
Normal file
51
dh/README
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
The functions found in this sub-component are ported almost directly from the
|
||||||
|
openssh-portable[0]'s `moduli.c`[1] code (with, of course, changes made where
|
||||||
|
appropriate to match and take advantage of Golang).
|
||||||
|
|
||||||
|
The OpenBSD and OpenSSH(-portable) teams have my gratitude.
|
||||||
|
|
||||||
|
OpenSSH/OpenSSH portable are released under a combination of the following licenses[2]:
|
||||||
|
|
||||||
|
* public domain
|
||||||
|
* "BSD-style"
|
||||||
|
* 2-, 3-, and 4-clause BSD
|
||||||
|
* Beerware
|
||||||
|
|
||||||
|
The license in full for OpenSSH/OpenSSH-Portable can be found at [2].
|
||||||
|
|
||||||
|
The license for OpenSSH-Portable's `moduli.c` is as follows:
|
||||||
|
|
||||||
|
###########################################################################
|
||||||
|
Copyright 1994 Phil Karn <karn@qualcomm.com>
|
||||||
|
Copyright 1996-1998, 2003 William Allen Simpson <wsimpson@greendragon.com>
|
||||||
|
Copyright 2000 Niels Provos <provos@citi.umich.edu>
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions
|
||||||
|
are met:
|
||||||
|
1. Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||||
|
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||||
|
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
###########################################################################
|
||||||
|
|
||||||
|
|
||||||
|
[0] https://www.openssh.com/portable.html
|
||||||
|
https://anongit.mindrot.org/openssh.git
|
||||||
|
|
||||||
|
[1] https://anongit.mindrot.org/openssh.git/tree/moduli.c
|
||||||
|
|
||||||
|
[2] https://anongit.mindrot.org/openssh.git/tree/LICENCE
|
40
dh/const.go
Normal file
40
dh/const.go
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
package dh
|
||||||
|
|
||||||
|
const (
|
||||||
|
// QSizeMinimum Specifies the number of the most significant bit (0 to M).
|
||||||
|
// WARNING: internally, usually 1 to N.
|
||||||
|
QSizeMinimum = 511
|
||||||
|
|
||||||
|
// Prime sieving constants
|
||||||
|
// Assuming 8 bit bytes and 32 bit words.
|
||||||
|
ShiftBit = 3
|
||||||
|
ShiftByte = 2
|
||||||
|
ShiftWord = ShiftBit + ShiftByte
|
||||||
|
ShiftMegabyte = 20
|
||||||
|
ShiftMegaWord = ShiftMegabyte - ShiftBit
|
||||||
|
|
||||||
|
// Memory limits.
|
||||||
|
// LargeMinimum is 8 megabytes
|
||||||
|
LargeMinimum = uint32(8) // Originally an 8UL in moduli.c
|
||||||
|
// LargeMaximum is 127MB.
|
||||||
|
LargeMaximum = uint32(127)
|
||||||
|
// The largest sieve prime has to be < 2**32 on 32-bit systems.
|
||||||
|
SmallMaximum = uint32(0xffffffff) // 4294967295
|
||||||
|
// Can sieve all primes less than 2**32, as 65537**2 > 2**32-1.
|
||||||
|
TinyNumber = uint32(1) << 16
|
||||||
|
// Ensure enough bit space for testing 2*q.
|
||||||
|
TestMaximum = uint32(1) << 16
|
||||||
|
TestMinimum = QSizeMinimum + 1 // (uint32(1) << (ShiftWord - TestPower))
|
||||||
|
TestPower = 3 // 2**n, n < ShiftWord
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
)
|
||||||
|
|
||||||
|
// Bit* functions operate on 32-bit words
|
||||||
|
func BitClear(a []uint32, n uint32) (i uint32) {
|
||||||
|
|
||||||
|
i = a[n >> ShiftWord] &= ~(uint32(1) << (n & 31))
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
36
dh/func_gen.go
Normal file
36
dh/func_gen.go
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package dh
|
||||||
|
|
||||||
|
/*
|
||||||
|
OpenSSH does prime generation and primality checking a *little* weird.
|
||||||
|
|
||||||
|
The seemingly go-to package for DH parameter generation in Golang, github.com/Luzifer/go-dhparam,
|
||||||
|
does implement safety checking in a way I believe to be safe (with the huge caveat that I am nowhere
|
||||||
|
near a professional, expert, guru, etc. in mathematics, cryptography, or the like).
|
||||||
|
|
||||||
|
However, it is incompatible with OpenSSH's methodology for DH parameter generation.
|
||||||
|
|
||||||
|
1.) First, primes are generated via the Sieve of Eratosthenes.
|
||||||
|
a.) They must also be Sophie Germain primes (where p is selected prime, 2p+1 is also prime).
|
||||||
|
2.) Then they are filtered via Probabilistic Miller-Rabin primality tests (on both q and p, where q is (p-1)/2).
|
||||||
|
3.) OpenSSH fully supports generators of 2, 3, and 5 whereas go-dhparam only fully supports 2 and 5.
|
||||||
|
|
||||||
|
And that's why I'm a sad panda and porting moduli.c to native Golang.
|
||||||
|
*/
|
12
go.mod
Normal file
12
go.mod
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
module r00t2.io/sshsecure
|
||||||
|
|
||||||
|
go 1.16
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/Luzifer/go-dhparam v1.1.0
|
||||||
|
github.com/dchest/bcrypt_pbkdf v0.0.0-20150205184540-83f37f9c154a
|
||||||
|
github.com/go-restruct/restruct v1.2.0-alpha
|
||||||
|
github.com/pkg/errors v0.9.1
|
||||||
|
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b
|
||||||
|
r00t2.io/sysutils v0.0.0-20210427083717-fbf1049fd285
|
||||||
|
)
|
30
go.sum
Normal file
30
go.sum
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
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/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/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=
|
||||||
|
github.com/go-restruct/restruct v1.2.0-alpha h1:2Lp474S/9660+SJjpVxoKuWX09JsXHSrdV7Nv3/gkvc=
|
||||||
|
github.com/go-restruct/restruct v1.2.0-alpha/go.mod h1:KqrpKpn4M8OLznErihXTGLlsXFGeLxHUrLRRI/1YjGk=
|
||||||
|
github.com/jszwec/csvutil v1.5.0/go.mod h1:Rpu7Uu9giO9subDyMCIQfHVDuLrcaC36UA4YcJjGBkg=
|
||||||
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||||
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
|
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b h1:7mWr3k41Qtv8XlltBkDkl8LoP3mpSgBW8BUoxtEdbXg=
|
||||||
|
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||||
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw=
|
||||||
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||||
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
r00t2.io/sysutils v0.0.0-20210427083717-fbf1049fd285 h1:sEVsqJv1YsPToyhclbGjaZoF50w8heLifkQLZX2Jw7Y=
|
||||||
|
r00t2.io/sysutils v0.0.0-20210427083717-fbf1049fd285/go.mod h1:XzJkBF6SHAODEszJlOcjtGoTHwYnZZNmseA6PyOujes=
|
@ -21,6 +21,7 @@ package moduli
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
`github.com/Luzifer/go-dhparam`
|
||||||
"r00t2.io/sshsecure/sharedconsts"
|
"r00t2.io/sshsecure/sharedconsts"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -39,7 +40,21 @@ const (
|
|||||||
// The recommended minimum moduli to have available.
|
// The recommended minimum moduli to have available.
|
||||||
recMinMod int = 400
|
recMinMod int = 400
|
||||||
// The minimum bits for filtering. It's generally bits - 1
|
// The minimum bits for filtering. It's generally bits - 1
|
||||||
minBits uint8 = 4095
|
minBits uint16 = 4096
|
||||||
|
)
|
||||||
|
|
||||||
|
// Generation iterables.
|
||||||
|
var (
|
||||||
|
genBits = []uint16{
|
||||||
|
4096,
|
||||||
|
6144,
|
||||||
|
7680,
|
||||||
|
8192,
|
||||||
|
}
|
||||||
|
genGenerators = []dhparam.Generator{
|
||||||
|
dhparam.GeneratorTwo,
|
||||||
|
dhparam.GeneratorFive,
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// The header line on the /etc/ssh/moduli file.
|
// The header line on the /etc/ssh/moduli file.
|
||||||
@ -57,8 +72,7 @@ const (
|
|||||||
timeFormat string = "20060102150405" // %Y%m%d%H%M%S
|
timeFormat string = "20060102150405" // %Y%m%d%H%M%S
|
||||||
)
|
)
|
||||||
|
|
||||||
// For validation. Currently unused.
|
// For validation. TODO.
|
||||||
/*
|
|
||||||
var (
|
var (
|
||||||
validTypes = []uint8{
|
validTypes = []uint8{
|
||||||
0, // Unknown, not tested
|
0, // Unknown, not tested
|
||||||
@ -72,4 +86,3 @@ var (
|
|||||||
0x04, // Probabilistic Miller-Rabin primality tests.
|
0x04, // Probabilistic Miller-Rabin primality tests.
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
*/
|
|
||||||
|
118
moduli/func.go
118
moduli/func.go
@ -24,42 +24,130 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
`time`
|
||||||
|
|
||||||
|
`github.com/Luzifer/go-dhparam`
|
||||||
"golang.org/x/crypto/sha3"
|
"golang.org/x/crypto/sha3"
|
||||||
)
|
)
|
||||||
|
|
||||||
// getPregen gets the pregenerated moduli from upstream mirror.
|
// NewModuli returns a Moduli populated with Entry items.
|
||||||
func getPregen() (Moduli, error) {
|
func NewModuli(usePreGen ... bool) (m *Moduli, err error) {
|
||||||
m := Moduli{}
|
|
||||||
|
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
|
||||||
|
|
||||||
// get the pregenerated moduli
|
// get the pregenerated moduli
|
||||||
resp, err := http.Get(pregenURL)
|
resp, err = http.Get(pregenURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return m, err
|
return
|
||||||
}
|
}
|
||||||
if resp.StatusCode != http.StatusOK {
|
if resp.StatusCode != http.StatusOK {
|
||||||
return m, errors.New(fmt.Sprintf("returned status code %v: %v", resp.StatusCode, resp.Status))
|
err = errors.New(fmt.Sprintf("returned status code %v: %v", resp.StatusCode, resp.Status))
|
||||||
|
return
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
b := make([]byte, resp.ContentLength)
|
|
||||||
|
b = make([]byte, resp.ContentLength)
|
||||||
if _, err = resp.Body.Read(b); err != nil {
|
if _, err = resp.Body.Read(b); err != nil {
|
||||||
return m, err
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// and compare the SHA3-512 (NIST) checksum.
|
// and compare the SHA3-512 (NIST) checksum.
|
||||||
s := sha3.New512()
|
s := sha3.New512()
|
||||||
if _, err = s.Write(b); err != nil {
|
if _, err = s.Write(b); err != nil {
|
||||||
// TODO: return nil instead of b?
|
return
|
||||||
return m, err
|
|
||||||
}
|
}
|
||||||
goodCksum, err := hex.DecodeString(pregenCksum)
|
goodCksum, err = hex.DecodeString(pregenCksum)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return m, err
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// We just compare the bytestrings.
|
// We just compare the bytestrings.
|
||||||
if bytes.Compare(s.Sum(nil), goodCksum) != 0 {
|
if bytes.Compare(s.Sum(nil), goodCksum) != 0 {
|
||||||
return m, errors.New("checksums do not match")
|
err = errors.New("checksums do not match")
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := Unmarshal(b, m); err != nil {
|
if err := Unmarshal(b, m); err != nil {
|
||||||
return m, err
|
return
|
||||||
}
|
}
|
||||||
return m, nil
|
|
||||||
|
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{
|
||||||
|
Time: time.Now(),
|
||||||
|
Size: bitLen,
|
||||||
|
Generator: uint8(generator),
|
||||||
|
/*
|
||||||
|
Type: 0,
|
||||||
|
Tests: 0,
|
||||||
|
Trials: 0,
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
m.Params = append(m.Params, e)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
104
moduli/parser.go
104
moduli/parser.go
@ -35,102 +35,136 @@ 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
|
// TODO: remember to write newline at end
|
||||||
func (m *Moduli) Marshal() ([]byte, 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.Params {
|
||||||
line, err := i.marshalEntry()
|
line, err := i.marshalEntry()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return b.Bytes(), err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
b.Write(line)
|
b.Write(line)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return b.Bytes(), nil
|
|
||||||
|
bytesOut = b.Bytes()
|
||||||
|
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// marshalEntry is used to parse a specific DH entry into the moduli.
|
// marshalEntry is used to parse an Entry into the moduli(5) format.
|
||||||
func (m *Entry) marshalEntry() ([]byte, error) {
|
func (m *Entry) marshalEntry() (sum []byte, err error) {
|
||||||
|
|
||||||
mod := hex.EncodeToString(m.Modulus.Bytes())
|
mod := hex.EncodeToString(m.Modulus.Bytes())
|
||||||
|
|
||||||
s := fmt.Sprintf(
|
s := fmt.Sprintf(
|
||||||
"%v %v %v %v %v %v %v\n",
|
"%v %v %v %v %v %v %v\n",
|
||||||
m.Time.Format(timeFormat),
|
m.Time.Format(timeFormat),
|
||||||
string(m.Type),
|
string(m.Type),
|
||||||
string(m.Tests),
|
string(m.Tests),
|
||||||
string(m.Trials),
|
string(m.Trials),
|
||||||
string(m.Size),
|
strconv.Itoa(int(m.Size)-1), // see this thread https://twitter.com/SysAdm_Podcast/status/1386714803679399940
|
||||||
string(m.Generator),
|
string(m.Generator),
|
||||||
mod,
|
mod,
|
||||||
)
|
)
|
||||||
return []byte(s), nil
|
|
||||||
|
sum = []byte(s)
|
||||||
|
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unmarshal writes the Moduli format into m from the /etc/ssh/moduli format in data.
|
// Unmarshal populates a Moduli from the /etc/ssh/moduli format.
|
||||||
func Unmarshal(data []byte, m Moduli) error {
|
func Unmarshal(data []byte, m *Moduli) (err error) {
|
||||||
|
|
||||||
var lines []string
|
var lines []string
|
||||||
var entries []Entry
|
var entries []Entry
|
||||||
|
|
||||||
lines = strings.Split(string(data), "\n")
|
lines = strings.Split(string(data), "\n")
|
||||||
for _, line := range lines {
|
for _, line := range lines {
|
||||||
|
|
||||||
e := Entry{}
|
e := Entry{}
|
||||||
|
|
||||||
if reSkipLine.MatchString(line) {
|
if reSkipLine.MatchString(line) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
l := strings.Fields(line)
|
l := strings.Fields(line)
|
||||||
if err := unmarshalEntry(l, e); err != nil {
|
if err := unmarshalEntry(l, e); err != nil {
|
||||||
return err
|
return
|
||||||
}
|
|
||||||
entries = append(entries, e)
|
|
||||||
}
|
|
||||||
m.Params = entries
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func unmarshalEntry(line []string, m Entry) error {
|
entries = append(entries, e)
|
||||||
var err error
|
}
|
||||||
|
|
||||||
|
m.Params = entries
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// unmarshalEntry unmarshals a single line from an /etc/ssh/moduli into an Entry.
|
||||||
|
func unmarshalEntry(line []string, m Entry) (err error) {
|
||||||
|
|
||||||
|
var modb []byte
|
||||||
|
|
||||||
if len(line) != 7 {
|
if len(line) != 7 {
|
||||||
return errors.New("field count mismatch")
|
err = errors.New("field count mismatch")
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if m.Time, err = time.Parse(timeFormat, line[0]); err != nil {
|
if m.Time, err = time.Parse(timeFormat, line[0]); err != nil {
|
||||||
return err
|
return
|
||||||
}
|
}
|
||||||
// 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.
|
// Numeric types. Cast to uint16. There's probably a better way to do this but golang's pretty ugly with this stuff no matter what.
|
||||||
|
// The worst part is all of them are uint8 except size (uint16).
|
||||||
// Type, Tests, Trials, Size, Generator
|
// Type, Tests, Trials, Size, Generator
|
||||||
conv := [5]uint8{}
|
conv := [5]uint16{}
|
||||||
for idx := 1; idx <= 5; idx++ {
|
for idx := 1; idx <= 5; idx++ {
|
||||||
v := line[idx]
|
v := line[idx]
|
||||||
newv, err := strconv.Atoi(v)
|
newv, err := strconv.Atoi(v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
conv[idx-1] = uint8(newv)
|
conv[idx-1] = uint16(newv)
|
||||||
}
|
}
|
||||||
m.Type = conv[0]
|
|
||||||
m.Tests = conv[1]
|
m.Type = uint8(conv[0])
|
||||||
m.Trials = conv[2]
|
m.Tests = uint8(conv[1])
|
||||||
m.Size = conv[3]
|
m.Trials = uint8(conv[2])
|
||||||
m.Generator = conv[4]
|
m.Size = conv[3] + 1 // see this thread https://twitter.com/SysAdm_Podcast/status/1386714803679399940
|
||||||
|
m.Generator = uint8(conv[4])
|
||||||
|
|
||||||
// And the modulus convert to big.Int.
|
// And the modulus convert to big.Int.
|
||||||
modb, err := hex.DecodeString(line[6])
|
if modb, err = hex.DecodeString(line[6]); err != nil {
|
||||||
if err != nil {
|
return
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
m.Modulus = big.Int{}
|
m.Modulus = big.Int{}
|
||||||
m.Modulus.SetBytes(modb)
|
m.Modulus.SetBytes(modb)
|
||||||
return nil
|
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Moduli) Harden() error {
|
func (m *Moduli) Harden() (err error) {
|
||||||
|
|
||||||
var entries []Entry
|
var entries []Entry
|
||||||
|
|
||||||
for _, e := range m.Params {
|
for _, e := range m.Params {
|
||||||
|
|
||||||
|
e.Time = time.Now()
|
||||||
|
|
||||||
if e.Size >= minBits {
|
if e.Size >= minBits {
|
||||||
entries = append(entries, e)
|
entries = append(entries, e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
m.Params = entries
|
m.Params = entries
|
||||||
|
|
||||||
if len(m.Params) < recMinMod {
|
if len(m.Params) < recMinMod {
|
||||||
return errors.New("does not meet recommended minimum moduli")
|
err = errors.New("does not meet recommended minimum moduli")
|
||||||
}
|
return
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: find way of testing/sieving primes
|
// TODO: find way of testing/sieving primes
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
@ -6,3 +6,4 @@ https://github.com/Luzifer/go-dhparam
|
|||||||
https://github.com/mimoo/test_DHparams
|
https://github.com/mimoo/test_DHparams
|
||||||
https://github.com/hyperreality/cryptopals-2/blob/517c1907b2041e6f7ef18930eca2aa3a24fb73d8/dh.go
|
https://github.com/hyperreality/cryptopals-2/blob/517c1907b2041e6f7ef18930eca2aa3a24fb73d8/dh.go
|
||||||
https://sosedoff.com/2016/07/16/golang-struct-tags.html
|
https://sosedoff.com/2016/07/16/golang-struct-tags.html
|
||||||
|
port moduli gen? https://github.com/openssh/openssh-portable/blob/master/moduli.c
|
||||||
|
@ -29,7 +29,7 @@ type Moduli struct {
|
|||||||
Params []Entry
|
Params []Entry
|
||||||
}
|
}
|
||||||
|
|
||||||
// Moduli 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.
|
||||||
type Entry struct {
|
type Entry struct {
|
||||||
Time time.Time // YYYYMMDDHHSS
|
Time time.Time // YYYYMMDDHHSS
|
||||||
/*
|
/*
|
||||||
@ -64,7 +64,7 @@ type Entry struct {
|
|||||||
// man 5 moduli:
|
// man 5 moduli:
|
||||||
Decimal number indicating the size of the prime in bits.
|
Decimal number indicating the size of the prime in bits.
|
||||||
*/
|
*/
|
||||||
Size uint8
|
Size uint16
|
||||||
/*
|
/*
|
||||||
// man 5 moduli:
|
// man 5 moduli:
|
||||||
The recommended generator for use with this modulus (hexadecimal).
|
The recommended generator for use with this modulus (hexadecimal).
|
||||||
|
Loading…
Reference in New Issue
Block a user