cmd: continue lint fixes.
This commit is contained in:
@@ -86,7 +86,6 @@ linters:
|
||||
- godoclint # checks Golang's documentation practice
|
||||
- godot # checks if comments end in a period
|
||||
- gomoddirectives # manages the use of 'replace', 'retract', and 'excludes' directives in go.mod
|
||||
- goprintffuncname # checks that printf-like functions are named with f at the end
|
||||
- gosec # inspects source code for security problems
|
||||
- govet # reports suspicious constructs, such as Printf calls whose arguments do not align with the format string
|
||||
- iface # checks the incorrect use of interfaces, helping developers avoid interface pollution
|
||||
@@ -465,6 +464,8 @@ linters:
|
||||
linters: [ testableexamples ]
|
||||
- path: 'main.go'
|
||||
linters: [ forbidigo, mnd, reassign ]
|
||||
- path: 'cmd/cruntar/main.go'
|
||||
linters: [ unparam ]
|
||||
- source: 'TODO'
|
||||
linters: [ godot ]
|
||||
- text: 'should have a package comment'
|
||||
|
||||
126
cache/lru/lru_internal_test.go
vendored
126
cache/lru/lru_internal_test.go
vendored
@@ -1,87 +1,87 @@
|
||||
package lru
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/benbjohnson/clock"
|
||||
"github.com/benbjohnson/clock"
|
||||
)
|
||||
|
||||
// These tests mirror the MRU-style behavior present in this LRU package
|
||||
// implementation (eviction removes the most-recently-used entry).
|
||||
func TestBasicCacheEviction(t *testing.T) {
|
||||
mock := clock.NewMock()
|
||||
c := NewStringKeyCache[int](2)
|
||||
c.clock = mock
|
||||
mock := clock.NewMock()
|
||||
c := NewStringKeyCache[int](2)
|
||||
c.clock = mock
|
||||
|
||||
if err := c.ConsistencyCheck(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := c.ConsistencyCheck(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if c.Len() != 0 {
|
||||
t.Fatal("cache should have size 0")
|
||||
}
|
||||
if c.Len() != 0 {
|
||||
t.Fatal("cache should have size 0")
|
||||
}
|
||||
|
||||
c.evict()
|
||||
if err := c.ConsistencyCheck(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
c.evict()
|
||||
if err := c.ConsistencyCheck(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
c.Store("raven", 1)
|
||||
if err := c.ConsistencyCheck(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
c.Store("raven", 1)
|
||||
if err := c.ConsistencyCheck(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(c.store) != 1 {
|
||||
t.Fatalf("store should have length=1, have length=%d", len(c.store))
|
||||
}
|
||||
if len(c.store) != 1 {
|
||||
t.Fatalf("store should have length=1, have length=%d", len(c.store))
|
||||
}
|
||||
|
||||
mock.Add(time.Second)
|
||||
c.Store("owl", 2)
|
||||
if err := c.ConsistencyCheck(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
mock.Add(time.Second)
|
||||
c.Store("owl", 2)
|
||||
if err := c.ConsistencyCheck(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(c.store) != 2 {
|
||||
t.Fatalf("store should have length=2, have length=%d", len(c.store))
|
||||
}
|
||||
if len(c.store) != 2 {
|
||||
t.Fatalf("store should have length=2, have length=%d", len(c.store))
|
||||
}
|
||||
|
||||
mock.Add(time.Second)
|
||||
c.Store("goat", 3)
|
||||
if err := c.ConsistencyCheck(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
mock.Add(time.Second)
|
||||
c.Store("goat", 3)
|
||||
if err := c.ConsistencyCheck(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(c.store) != 2 {
|
||||
t.Fatalf("store should have length=2, have length=%d", len(c.store))
|
||||
}
|
||||
if len(c.store) != 2 {
|
||||
t.Fatalf("store should have length=2, have length=%d", len(c.store))
|
||||
}
|
||||
|
||||
// Since this implementation evicts the most-recently-used item, inserting
|
||||
// "goat" when full evicts "owl" (the most recent at that time).
|
||||
mock.Add(time.Second)
|
||||
if _, ok := c.Get("owl"); ok {
|
||||
t.Fatal("store should not have an entry for owl (MRU-evicted)")
|
||||
}
|
||||
if err := c.ConsistencyCheck(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// Since this implementation evicts the most-recently-used item, inserting
|
||||
// "goat" when full evicts "owl" (the most recent at that time).
|
||||
mock.Add(time.Second)
|
||||
if _, ok := c.Get("owl"); ok {
|
||||
t.Fatal("store should not have an entry for owl (MRU-evicted)")
|
||||
}
|
||||
if err := c.ConsistencyCheck(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
mock.Add(time.Second)
|
||||
c.Store("elk", 4)
|
||||
if err := c.ConsistencyCheck(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
mock.Add(time.Second)
|
||||
c.Store("elk", 4)
|
||||
if err := c.ConsistencyCheck(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !c.Has("elk") {
|
||||
t.Fatal("store should contain an entry for 'elk'")
|
||||
}
|
||||
if !c.Has("elk") {
|
||||
t.Fatal("store should contain an entry for 'elk'")
|
||||
}
|
||||
|
||||
// Before storing elk, keys were: raven (older), goat (newer). Evict MRU -> goat.
|
||||
if !c.Has("raven") {
|
||||
t.Fatal("store should contain an entry for 'raven'")
|
||||
}
|
||||
// Before storing elk, keys were: raven (older), goat (newer). Evict MRU -> goat.
|
||||
if !c.Has("raven") {
|
||||
t.Fatal("store should contain an entry for 'raven'")
|
||||
}
|
||||
|
||||
if c.Has("goat") {
|
||||
t.Fatal("store should not contain an entry for 'goat'")
|
||||
}
|
||||
if c.Has("goat") {
|
||||
t.Fatal("store should not contain an entry for 'goat'")
|
||||
}
|
||||
}
|
||||
|
||||
64
cache/lru/timestamps_internal_test.go
vendored
64
cache/lru/timestamps_internal_test.go
vendored
@@ -1,50 +1,50 @@
|
||||
package lru
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/benbjohnson/clock"
|
||||
"github.com/benbjohnson/clock"
|
||||
)
|
||||
|
||||
// These tests validate timestamps ordering semantics for the LRU package.
|
||||
// Note: The LRU timestamps are sorted with most-recent-first (descending by t).
|
||||
func TestTimestamps(t *testing.T) {
|
||||
ts := newTimestamps[string](3)
|
||||
mock := clock.NewMock()
|
||||
ts := newTimestamps[string](3)
|
||||
mock := clock.NewMock()
|
||||
|
||||
// raven
|
||||
ts.Update("raven", mock.Now().UnixNano())
|
||||
// raven
|
||||
ts.Update("raven", mock.Now().UnixNano())
|
||||
|
||||
// raven, owl
|
||||
mock.Add(time.Millisecond)
|
||||
ts.Update("owl", mock.Now().UnixNano())
|
||||
// raven, owl
|
||||
mock.Add(time.Millisecond)
|
||||
ts.Update("owl", mock.Now().UnixNano())
|
||||
|
||||
// raven, owl, goat
|
||||
mock.Add(time.Second)
|
||||
ts.Update("goat", mock.Now().UnixNano())
|
||||
// raven, owl, goat
|
||||
mock.Add(time.Second)
|
||||
ts.Update("goat", mock.Now().UnixNano())
|
||||
|
||||
if err := ts.ConsistencyCheck(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := ts.ConsistencyCheck(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// make owl the most recent
|
||||
mock.Add(time.Millisecond)
|
||||
ts.Update("owl", mock.Now().UnixNano())
|
||||
if err := ts.ConsistencyCheck(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// make owl the most recent
|
||||
mock.Add(time.Millisecond)
|
||||
ts.Update("owl", mock.Now().UnixNano())
|
||||
if err := ts.ConsistencyCheck(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// For LRU timestamps: most recent first. Expected order: owl, goat, raven.
|
||||
if ts.K(0) != "owl" {
|
||||
t.Fatalf("first key should be owl, have %s", ts.K(0))
|
||||
}
|
||||
// For LRU timestamps: most recent first. Expected order: owl, goat, raven.
|
||||
if ts.K(0) != "owl" {
|
||||
t.Fatalf("first key should be owl, have %s", ts.K(0))
|
||||
}
|
||||
|
||||
if ts.K(1) != "goat" {
|
||||
t.Fatalf("second key should be goat, have %s", ts.K(1))
|
||||
}
|
||||
if ts.K(1) != "goat" {
|
||||
t.Fatalf("second key should be goat, have %s", ts.K(1))
|
||||
}
|
||||
|
||||
if ts.K(2) != "raven" {
|
||||
t.Fatalf("third key should be raven, have %s", ts.K(2))
|
||||
}
|
||||
if ts.K(2) != "raven" {
|
||||
t.Fatalf("third key should be raven, have %s", ts.K(2))
|
||||
}
|
||||
}
|
||||
|
||||
28
cache/mru/mru_internal_test.go
vendored
28
cache/mru/mru_internal_test.go
vendored
@@ -8,9 +8,9 @@ import (
|
||||
)
|
||||
|
||||
func TestBasicCacheEviction(t *testing.T) {
|
||||
mock := clock.NewMock()
|
||||
c := NewStringKeyCache[int](2)
|
||||
c.clock = mock
|
||||
mock := clock.NewMock()
|
||||
c := NewStringKeyCache[int](2)
|
||||
c.clock = mock
|
||||
|
||||
if err := c.ConsistencyCheck(); err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -55,18 +55,18 @@ func TestBasicCacheEviction(t *testing.T) {
|
||||
}
|
||||
|
||||
mock.Add(time.Second)
|
||||
v, ok := c.Get("owl")
|
||||
if !ok {
|
||||
t.Fatal("store should have an entry for owl")
|
||||
}
|
||||
if err := c.ConsistencyCheck(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
v, ok := c.Get("owl")
|
||||
if !ok {
|
||||
t.Fatal("store should have an entry for owl")
|
||||
}
|
||||
if err := c.ConsistencyCheck(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
itm := v
|
||||
if err := c.ConsistencyCheck(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
itm := v
|
||||
if err := c.ConsistencyCheck(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if itm != 2 {
|
||||
t.Fatalf("stored item should be 2, have %d", itm)
|
||||
|
||||
4
cache/mru/timestamps_internal_test.go
vendored
4
cache/mru/timestamps_internal_test.go
vendored
@@ -8,8 +8,8 @@ import (
|
||||
)
|
||||
|
||||
func TestTimestamps(t *testing.T) {
|
||||
ts := newTimestamps[string](3)
|
||||
mock := clock.NewMock()
|
||||
ts := newTimestamps[string](3)
|
||||
mock := clock.NewMock()
|
||||
|
||||
// raven
|
||||
ts.Update("raven", mock.Now().UnixNano())
|
||||
|
||||
@@ -1,20 +1,21 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.wntrmute.dev/kyle/goutils/certlib"
|
||||
hosts "git.wntrmute.dev/kyle/goutils/certlib/hosts"
|
||||
"git.wntrmute.dev/kyle/goutils/certlib/revoke"
|
||||
"git.wntrmute.dev/kyle/goutils/fileutil"
|
||||
"git.wntrmute.dev/kyle/goutils/certlib"
|
||||
hosts "git.wntrmute.dev/kyle/goutils/certlib/hosts"
|
||||
"git.wntrmute.dev/kyle/goutils/certlib/revoke"
|
||||
"git.wntrmute.dev/kyle/goutils/fileutil"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -78,17 +79,17 @@ func processTarget(target string) (string, error) {
|
||||
}
|
||||
|
||||
func checkFile(path string) (string, error) {
|
||||
// Prefer high-level helpers from certlib to load certificates from disk
|
||||
if certs, err := certlib.LoadCertificates(path); err == nil && len(certs) > 0 {
|
||||
// Evaluate the first certificate (leaf) by default
|
||||
return evaluateCert(certs[0])
|
||||
}
|
||||
// Prefer high-level helpers from certlib to load certificates from disk
|
||||
if certs, err := certlib.LoadCertificates(path); err == nil && len(certs) > 0 {
|
||||
// Evaluate the first certificate (leaf) by default
|
||||
return evaluateCert(certs[0])
|
||||
}
|
||||
|
||||
cert, err := certlib.LoadCertificate(path)
|
||||
if err != nil || cert == nil {
|
||||
return strUnknown, err
|
||||
}
|
||||
return evaluateCert(cert)
|
||||
cert, err := certlib.LoadCertificate(path)
|
||||
if err != nil || cert == nil {
|
||||
return strUnknown, err
|
||||
}
|
||||
return evaluateCert(cert)
|
||||
}
|
||||
|
||||
func checkSite(hostport string) (string, error) {
|
||||
@@ -99,18 +100,27 @@ func checkSite(hostport string) (string, error) {
|
||||
}
|
||||
|
||||
d := &net.Dialer{Timeout: timeout}
|
||||
conn, err := tls.DialWithDialer(
|
||||
d,
|
||||
"tcp",
|
||||
target.String(),
|
||||
&tls.Config{InsecureSkipVerify: true, ServerName: target.Host}, // #nosec G402
|
||||
)
|
||||
tcfg := &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
ServerName: target.Host,
|
||||
} // #nosec G402 -- CLI tool only verifies revocation
|
||||
td := &tls.Dialer{NetDialer: d, Config: tcfg}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||
defer cancel()
|
||||
|
||||
conn, err := td.DialContext(ctx, "tcp", target.String())
|
||||
if err != nil {
|
||||
return strUnknown, err
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
state := conn.ConnectionState()
|
||||
tconn, ok := conn.(*tls.Conn)
|
||||
if !ok {
|
||||
return strUnknown, errors.New("connection is not TLS")
|
||||
}
|
||||
|
||||
state := tconn.ConnectionState()
|
||||
if len(state.PeerCertificates) == 0 {
|
||||
return strUnknown, errors.New("no peer certificates presented")
|
||||
}
|
||||
@@ -118,23 +128,24 @@ func checkSite(hostport string) (string, error) {
|
||||
}
|
||||
|
||||
func evaluateCert(cert *x509.Certificate) (string, error) {
|
||||
// Delegate validity and revocation checks to certlib/revoke helper.
|
||||
// It returns revoked=true for both revoked and expired/not-yet-valid.
|
||||
// Map those cases back to our statuses using the returned error text.
|
||||
revoked, ok, err := revoke.VerifyCertificateError(cert)
|
||||
if revoked {
|
||||
if err != nil {
|
||||
msg := err.Error()
|
||||
if strings.Contains(msg, "expired") || strings.Contains(msg, "isn't valid until") || strings.Contains(msg, "not valid until") {
|
||||
return strExpired, err
|
||||
}
|
||||
}
|
||||
return strRevoked, err
|
||||
}
|
||||
if !ok {
|
||||
// Revocation status could not be determined
|
||||
return strUnknown, err
|
||||
}
|
||||
// Delegate validity and revocation checks to certlib/revoke helper.
|
||||
// It returns revoked=true for both revoked and expired/not-yet-valid.
|
||||
// Map those cases back to our statuses using the returned error text.
|
||||
revoked, ok, err := revoke.VerifyCertificateError(cert)
|
||||
if revoked {
|
||||
if err != nil {
|
||||
msg := err.Error()
|
||||
if strings.Contains(msg, "expired") || strings.Contains(msg, "isn't valid until") ||
|
||||
strings.Contains(msg, "not valid until") {
|
||||
return strExpired, err
|
||||
}
|
||||
}
|
||||
return strRevoked, err
|
||||
}
|
||||
if !ok {
|
||||
// Revocation status could not be determined
|
||||
return strUnknown, err
|
||||
}
|
||||
|
||||
return strOK, nil
|
||||
return strOK, nil
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"git.wntrmute.dev/kyle/goutils/die"
|
||||
)
|
||||
@@ -27,13 +28,15 @@ func main() {
|
||||
die.If(err)
|
||||
|
||||
details := conn.ConnectionState()
|
||||
var chainSb30 strings.Builder
|
||||
for _, cert := range details.PeerCertificates {
|
||||
p := pem.Block{
|
||||
Type: "CERTIFICATE",
|
||||
Bytes: cert.Raw,
|
||||
}
|
||||
chain += string(pem.EncodeToMemory(&p))
|
||||
chainSb30.WriteString(string(pem.EncodeToMemory(&p)))
|
||||
}
|
||||
chain += chainSb30.String()
|
||||
|
||||
fmt.Fprintln(os.Stdout, chain)
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
//lint:file-ignore SA1019 allow strict compatibility for old certs
|
||||
package main
|
||||
|
||||
import (
|
||||
@@ -101,30 +102,30 @@ func extUsage(ext []x509.ExtKeyUsage) string {
|
||||
}
|
||||
|
||||
func showBasicConstraints(cert *x509.Certificate) {
|
||||
fmt.Fprint(os.Stdout, "\tBasic constraints: ")
|
||||
if cert.BasicConstraintsValid {
|
||||
fmt.Fprint(os.Stdout, "valid")
|
||||
} else {
|
||||
fmt.Fprint(os.Stdout, "invalid")
|
||||
}
|
||||
fmt.Fprint(os.Stdout, "\tBasic constraints: ")
|
||||
if cert.BasicConstraintsValid {
|
||||
fmt.Fprint(os.Stdout, "valid")
|
||||
} else {
|
||||
fmt.Fprint(os.Stdout, "invalid")
|
||||
}
|
||||
|
||||
if cert.IsCA {
|
||||
fmt.Fprint(os.Stdout, ", is a CA certificate")
|
||||
if !cert.BasicConstraintsValid {
|
||||
fmt.Fprint(os.Stdout, " (basic constraint failure)")
|
||||
}
|
||||
} else {
|
||||
fmt.Fprint(os.Stdout, "is not a CA certificate")
|
||||
if cert.KeyUsage&x509.KeyUsageKeyEncipherment != 0 {
|
||||
fmt.Fprint(os.Stdout, " (key encipherment usage enabled!)")
|
||||
}
|
||||
}
|
||||
if cert.IsCA {
|
||||
fmt.Fprint(os.Stdout, ", is a CA certificate")
|
||||
if !cert.BasicConstraintsValid {
|
||||
fmt.Fprint(os.Stdout, " (basic constraint failure)")
|
||||
}
|
||||
} else {
|
||||
fmt.Fprint(os.Stdout, "is not a CA certificate")
|
||||
if cert.KeyUsage&x509.KeyUsageKeyEncipherment != 0 {
|
||||
fmt.Fprint(os.Stdout, " (key encipherment usage enabled!)")
|
||||
}
|
||||
}
|
||||
|
||||
if (cert.MaxPathLen == 0 && cert.MaxPathLenZero) || (cert.MaxPathLen > 0) {
|
||||
fmt.Fprintf(os.Stdout, ", max path length %d", cert.MaxPathLen)
|
||||
}
|
||||
if (cert.MaxPathLen == 0 && cert.MaxPathLenZero) || (cert.MaxPathLen > 0) {
|
||||
fmt.Fprintf(os.Stdout, ", max path length %d", cert.MaxPathLen)
|
||||
}
|
||||
|
||||
fmt.Fprintln(os.Stdout)
|
||||
fmt.Fprintln(os.Stdout)
|
||||
}
|
||||
|
||||
const oneTrueDateFormat = "2006-01-02T15:04:05-0700"
|
||||
@@ -135,41 +136,43 @@ var (
|
||||
)
|
||||
|
||||
func wrapPrint(text string, indent int) {
|
||||
tabs := ""
|
||||
for i := 0; i < indent; i++ {
|
||||
tabs += "\t"
|
||||
}
|
||||
tabs := ""
|
||||
var tabsSb140 strings.Builder
|
||||
for range indent {
|
||||
tabsSb140.WriteString("\t")
|
||||
}
|
||||
tabs += tabsSb140.String()
|
||||
|
||||
fmt.Fprintf(os.Stdout, tabs+"%s\n", wrap(text, indent))
|
||||
fmt.Fprintf(os.Stdout, tabs+"%s\n", wrap(text, indent))
|
||||
}
|
||||
|
||||
func displayCert(cert *x509.Certificate) {
|
||||
fmt.Fprintln(os.Stdout, "CERTIFICATE")
|
||||
if showHash {
|
||||
fmt.Fprintln(os.Stdout, wrap(fmt.Sprintf("SHA256: %x", sha256.Sum256(cert.Raw)), 0))
|
||||
}
|
||||
fmt.Fprintln(os.Stdout, wrap("Subject: "+displayName(cert.Subject), 0))
|
||||
fmt.Fprintln(os.Stdout, wrap("Issuer: "+displayName(cert.Issuer), 0))
|
||||
fmt.Fprintf(os.Stdout, "\tSignature algorithm: %s / %s\n", sigAlgoPK(cert.SignatureAlgorithm),
|
||||
sigAlgoHash(cert.SignatureAlgorithm))
|
||||
fmt.Fprintln(os.Stdout, "Details:")
|
||||
wrapPrint("Public key: "+certPublic(cert), 1)
|
||||
fmt.Fprintf(os.Stdout, "\tSerial number: %s\n", cert.SerialNumber)
|
||||
fmt.Fprintln(os.Stdout, "CERTIFICATE")
|
||||
if showHash {
|
||||
fmt.Fprintln(os.Stdout, wrap(fmt.Sprintf("SHA256: %x", sha256.Sum256(cert.Raw)), 0))
|
||||
}
|
||||
fmt.Fprintln(os.Stdout, wrap("Subject: "+displayName(cert.Subject), 0))
|
||||
fmt.Fprintln(os.Stdout, wrap("Issuer: "+displayName(cert.Issuer), 0))
|
||||
fmt.Fprintf(os.Stdout, "\tSignature algorithm: %s / %s\n", sigAlgoPK(cert.SignatureAlgorithm),
|
||||
sigAlgoHash(cert.SignatureAlgorithm))
|
||||
fmt.Fprintln(os.Stdout, "Details:")
|
||||
wrapPrint("Public key: "+certPublic(cert), 1)
|
||||
fmt.Fprintf(os.Stdout, "\tSerial number: %s\n", cert.SerialNumber)
|
||||
|
||||
if len(cert.AuthorityKeyId) > 0 {
|
||||
fmt.Fprintf(os.Stdout, "\t%s\n", wrap("AKI: "+dumpHex(cert.AuthorityKeyId), 1))
|
||||
}
|
||||
if len(cert.SubjectKeyId) > 0 {
|
||||
fmt.Fprintf(os.Stdout, "\t%s\n", wrap("SKI: "+dumpHex(cert.SubjectKeyId), 1))
|
||||
}
|
||||
if len(cert.AuthorityKeyId) > 0 {
|
||||
fmt.Fprintf(os.Stdout, "\t%s\n", wrap("AKI: "+dumpHex(cert.AuthorityKeyId), 1))
|
||||
}
|
||||
if len(cert.SubjectKeyId) > 0 {
|
||||
fmt.Fprintf(os.Stdout, "\t%s\n", wrap("SKI: "+dumpHex(cert.SubjectKeyId), 1))
|
||||
}
|
||||
|
||||
wrapPrint("Valid from: "+cert.NotBefore.Format(dateFormat), 1)
|
||||
fmt.Fprintf(os.Stdout, "\t until: %s\n", cert.NotAfter.Format(dateFormat))
|
||||
fmt.Fprintf(os.Stdout, "\tKey usages: %s\n", keyUsages(cert.KeyUsage))
|
||||
fmt.Fprintf(os.Stdout, "\t until: %s\n", cert.NotAfter.Format(dateFormat))
|
||||
fmt.Fprintf(os.Stdout, "\tKey usages: %s\n", keyUsages(cert.KeyUsage))
|
||||
|
||||
if len(cert.ExtKeyUsage) > 0 {
|
||||
fmt.Fprintf(os.Stdout, "\tExtended usages: %s\n", extUsage(cert.ExtKeyUsage))
|
||||
}
|
||||
if len(cert.ExtKeyUsage) > 0 {
|
||||
fmt.Fprintf(os.Stdout, "\tExtended usages: %s\n", extUsage(cert.ExtKeyUsage))
|
||||
}
|
||||
|
||||
showBasicConstraints(cert)
|
||||
|
||||
@@ -217,19 +220,19 @@ func displayCert(cert *x509.Certificate) {
|
||||
}
|
||||
|
||||
func displayAllCerts(in []byte, leafOnly bool) {
|
||||
certs, err := certlib.ParseCertificatesPEM(in)
|
||||
if err != nil {
|
||||
certs, _, err = certlib.ParseCertificatesDER(in, "")
|
||||
if err != nil {
|
||||
_, _ = lib.Warn(err, "failed to parse certificates")
|
||||
return
|
||||
}
|
||||
}
|
||||
certs, err := certlib.ParseCertificatesPEM(in)
|
||||
if err != nil {
|
||||
certs, _, err = certlib.ParseCertificatesDER(in, "")
|
||||
if err != nil {
|
||||
_, _ = lib.Warn(err, "failed to parse certificates")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if len(certs) == 0 {
|
||||
_, _ = lib.Warnx("no certificates found")
|
||||
return
|
||||
}
|
||||
_, _ = lib.Warnx("no certificates found")
|
||||
return
|
||||
}
|
||||
|
||||
if leafOnly {
|
||||
displayCert(certs[0])
|
||||
@@ -243,11 +246,11 @@ func displayAllCerts(in []byte, leafOnly bool) {
|
||||
|
||||
func displayAllCertsWeb(uri string, leafOnly bool) {
|
||||
ci := getConnInfo(uri)
|
||||
conn, err := tls.Dial("tcp", ci.Addr, permissiveConfig())
|
||||
if err != nil {
|
||||
_, _ = lib.Warn(err, "couldn't connect to %s", ci.Addr)
|
||||
return
|
||||
}
|
||||
conn, err := tls.Dial("tcp", ci.Addr, permissiveConfig())
|
||||
if err != nil {
|
||||
_, _ = lib.Warn(err, "couldn't connect to %s", ci.Addr)
|
||||
return
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
state := conn.ConnectionState()
|
||||
@@ -260,34 +263,34 @@ func displayAllCertsWeb(uri string, leafOnly bool) {
|
||||
state = conn.ConnectionState()
|
||||
}
|
||||
conn.Close()
|
||||
} else {
|
||||
_, _ = lib.Warn(err, "TLS verification error with server name %s", ci.Host)
|
||||
}
|
||||
} else {
|
||||
_, _ = lib.Warn(err, "TLS verification error with server name %s", ci.Host)
|
||||
}
|
||||
|
||||
if len(state.PeerCertificates) == 0 {
|
||||
_, _ = lib.Warnx("no certificates found")
|
||||
return
|
||||
}
|
||||
if len(state.PeerCertificates) == 0 {
|
||||
_, _ = lib.Warnx("no certificates found")
|
||||
return
|
||||
}
|
||||
|
||||
if leafOnly {
|
||||
displayCert(state.PeerCertificates[0])
|
||||
return
|
||||
}
|
||||
|
||||
if len(state.VerifiedChains) == 0 {
|
||||
_, _ = lib.Warnx("no verified chains found; using peer chain")
|
||||
for i := range state.PeerCertificates {
|
||||
displayCert(state.PeerCertificates[i])
|
||||
}
|
||||
} else {
|
||||
fmt.Fprintln(os.Stdout, "TLS chain verified successfully.")
|
||||
for i := range state.VerifiedChains {
|
||||
fmt.Fprintf(os.Stdout, "--- Verified certificate chain %d ---%s", i+1, "\n")
|
||||
for j := range state.VerifiedChains[i] {
|
||||
displayCert(state.VerifiedChains[i][j])
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(state.VerifiedChains) == 0 {
|
||||
_, _ = lib.Warnx("no verified chains found; using peer chain")
|
||||
for i := range state.PeerCertificates {
|
||||
displayCert(state.PeerCertificates[i])
|
||||
}
|
||||
} else {
|
||||
fmt.Fprintln(os.Stdout, "TLS chain verified successfully.")
|
||||
for i := range state.VerifiedChains {
|
||||
fmt.Fprintf(os.Stdout, "--- Verified certificate chain %d ---%s", i+1, "\n")
|
||||
for j := range state.VerifiedChains[i] {
|
||||
displayCert(state.VerifiedChains[i][j])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
@@ -298,28 +301,28 @@ func main() {
|
||||
flag.Parse()
|
||||
|
||||
if flag.NArg() == 0 || (flag.NArg() == 1 && flag.Arg(0) == "-") {
|
||||
certs, err := io.ReadAll(os.Stdin)
|
||||
if err != nil {
|
||||
_, _ = lib.Warn(err, "couldn't read certificates from standard input")
|
||||
os.Exit(1)
|
||||
}
|
||||
certs, err := io.ReadAll(os.Stdin)
|
||||
if err != nil {
|
||||
_, _ = lib.Warn(err, "couldn't read certificates from standard input")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// This is needed for getting certs from JSON/jq.
|
||||
certs = bytes.TrimSpace(certs)
|
||||
certs = bytes.Replace(certs, []byte(`\n`), []byte{0xa}, -1)
|
||||
certs = bytes.ReplaceAll(certs, []byte(`\n`), []byte{0xa})
|
||||
certs = bytes.Trim(certs, `"`)
|
||||
displayAllCerts(certs, leafOnly)
|
||||
} else {
|
||||
for _, filename := range flag.Args() {
|
||||
fmt.Fprintf(os.Stdout, "--%s ---%s", filename, "\n")
|
||||
fmt.Fprintf(os.Stdout, "--%s ---%s", filename, "\n")
|
||||
if strings.HasPrefix(filename, "https://") {
|
||||
displayAllCertsWeb(filename, leafOnly)
|
||||
} else {
|
||||
in, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
_, _ = lib.Warn(err, "couldn't read certificate")
|
||||
continue
|
||||
}
|
||||
in, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
_, _ = lib.Warn(err, "couldn't read certificate")
|
||||
continue
|
||||
}
|
||||
|
||||
displayAllCerts(in, leafOnly)
|
||||
}
|
||||
@@ -26,94 +26,79 @@ var keyUsage = map[x509.KeyUsage]string{
|
||||
}
|
||||
|
||||
var extKeyUsages = map[x509.ExtKeyUsage]string{
|
||||
x509.ExtKeyUsageAny: "any",
|
||||
x509.ExtKeyUsageServerAuth: "server auth",
|
||||
x509.ExtKeyUsageClientAuth: "client auth",
|
||||
x509.ExtKeyUsageCodeSigning: "code signing",
|
||||
x509.ExtKeyUsageEmailProtection: "s/mime",
|
||||
x509.ExtKeyUsageIPSECEndSystem: "ipsec end system",
|
||||
x509.ExtKeyUsageIPSECTunnel: "ipsec tunnel",
|
||||
x509.ExtKeyUsageIPSECUser: "ipsec user",
|
||||
x509.ExtKeyUsageTimeStamping: "timestamping",
|
||||
x509.ExtKeyUsageOCSPSigning: "ocsp signing",
|
||||
x509.ExtKeyUsageMicrosoftServerGatedCrypto: "microsoft sgc",
|
||||
x509.ExtKeyUsageNetscapeServerGatedCrypto: "netscape sgc",
|
||||
x509.ExtKeyUsageMicrosoftCommercialCodeSigning: "microsoft commercial code signing",
|
||||
x509.ExtKeyUsageMicrosoftKernelCodeSigning: "microsoft kernel code signing",
|
||||
}
|
||||
|
||||
func pubKeyAlgo(a x509.PublicKeyAlgorithm) string {
|
||||
switch a {
|
||||
case x509.UnknownPublicKeyAlgorithm:
|
||||
return "unknown public key algorithm"
|
||||
case x509.RSA:
|
||||
return "RSA"
|
||||
case x509.ECDSA:
|
||||
return "ECDSA"
|
||||
case x509.DSA:
|
||||
return "DSA"
|
||||
case x509.Ed25519:
|
||||
return "Ed25519"
|
||||
default:
|
||||
return "unknown public key algorithm"
|
||||
}
|
||||
x509.ExtKeyUsageAny: "any",
|
||||
x509.ExtKeyUsageServerAuth: "server auth",
|
||||
x509.ExtKeyUsageClientAuth: "client auth",
|
||||
x509.ExtKeyUsageCodeSigning: "code signing",
|
||||
x509.ExtKeyUsageEmailProtection: "s/mime",
|
||||
x509.ExtKeyUsageIPSECEndSystem: "ipsec end system",
|
||||
x509.ExtKeyUsageIPSECTunnel: "ipsec tunnel",
|
||||
x509.ExtKeyUsageIPSECUser: "ipsec user",
|
||||
x509.ExtKeyUsageTimeStamping: "timestamping",
|
||||
x509.ExtKeyUsageOCSPSigning: "ocsp signing",
|
||||
x509.ExtKeyUsageMicrosoftServerGatedCrypto: "microsoft sgc",
|
||||
x509.ExtKeyUsageNetscapeServerGatedCrypto: "netscape sgc",
|
||||
x509.ExtKeyUsageMicrosoftCommercialCodeSigning: "microsoft commercial code signing",
|
||||
x509.ExtKeyUsageMicrosoftKernelCodeSigning: "microsoft kernel code signing",
|
||||
}
|
||||
|
||||
func sigAlgoPK(a x509.SignatureAlgorithm) string {
|
||||
switch a {
|
||||
case x509.MD2WithRSA, x509.MD5WithRSA, x509.SHA1WithRSA, x509.SHA256WithRSA, x509.SHA384WithRSA, x509.SHA512WithRSA:
|
||||
return "RSA"
|
||||
case x509.SHA256WithRSAPSS, x509.SHA384WithRSAPSS, x509.SHA512WithRSAPSS:
|
||||
return "RSA-PSS"
|
||||
case x509.ECDSAWithSHA1, x509.ECDSAWithSHA256, x509.ECDSAWithSHA384, x509.ECDSAWithSHA512:
|
||||
return "ECDSA"
|
||||
case x509.DSAWithSHA1, x509.DSAWithSHA256:
|
||||
return "DSA"
|
||||
case x509.PureEd25519:
|
||||
return "Ed25519"
|
||||
case x509.UnknownSignatureAlgorithm:
|
||||
return "unknown public key algorithm"
|
||||
default:
|
||||
return "unknown public key algorithm"
|
||||
}
|
||||
switch a {
|
||||
case x509.MD2WithRSA, x509.MD5WithRSA, x509.SHA1WithRSA, x509.SHA256WithRSA, x509.SHA384WithRSA, x509.SHA512WithRSA:
|
||||
return "RSA"
|
||||
case x509.SHA256WithRSAPSS, x509.SHA384WithRSAPSS, x509.SHA512WithRSAPSS:
|
||||
return "RSA-PSS"
|
||||
case x509.ECDSAWithSHA1, x509.ECDSAWithSHA256, x509.ECDSAWithSHA384, x509.ECDSAWithSHA512:
|
||||
return "ECDSA"
|
||||
case x509.DSAWithSHA1, x509.DSAWithSHA256:
|
||||
return "DSA"
|
||||
case x509.PureEd25519:
|
||||
return "Ed25519"
|
||||
case x509.UnknownSignatureAlgorithm:
|
||||
return "unknown public key algorithm"
|
||||
default:
|
||||
return "unknown public key algorithm"
|
||||
}
|
||||
}
|
||||
|
||||
func sigAlgoHash(a x509.SignatureAlgorithm) string {
|
||||
switch a {
|
||||
case x509.MD2WithRSA:
|
||||
return "MD2"
|
||||
case x509.MD5WithRSA:
|
||||
return "MD5"
|
||||
case x509.SHA1WithRSA, x509.ECDSAWithSHA1, x509.DSAWithSHA1:
|
||||
return "SHA1"
|
||||
case x509.SHA256WithRSA, x509.ECDSAWithSHA256, x509.DSAWithSHA256:
|
||||
return "SHA256"
|
||||
case x509.SHA256WithRSAPSS:
|
||||
return "SHA256"
|
||||
case x509.SHA384WithRSA, x509.ECDSAWithSHA384:
|
||||
return "SHA384"
|
||||
case x509.SHA384WithRSAPSS:
|
||||
return "SHA384"
|
||||
case x509.SHA512WithRSA, x509.ECDSAWithSHA512:
|
||||
return "SHA512"
|
||||
case x509.SHA512WithRSAPSS:
|
||||
return "SHA512"
|
||||
case x509.PureEd25519:
|
||||
return "SHA512"
|
||||
case x509.UnknownSignatureAlgorithm:
|
||||
return "unknown hash algorithm"
|
||||
default:
|
||||
return "unknown hash algorithm"
|
||||
}
|
||||
switch a {
|
||||
case x509.MD2WithRSA:
|
||||
return "MD2"
|
||||
case x509.MD5WithRSA:
|
||||
return "MD5"
|
||||
case x509.SHA1WithRSA, x509.ECDSAWithSHA1, x509.DSAWithSHA1:
|
||||
return "SHA1"
|
||||
case x509.SHA256WithRSA, x509.ECDSAWithSHA256, x509.DSAWithSHA256:
|
||||
return "SHA256"
|
||||
case x509.SHA256WithRSAPSS:
|
||||
return "SHA256"
|
||||
case x509.SHA384WithRSA, x509.ECDSAWithSHA384:
|
||||
return "SHA384"
|
||||
case x509.SHA384WithRSAPSS:
|
||||
return "SHA384"
|
||||
case x509.SHA512WithRSA, x509.ECDSAWithSHA512:
|
||||
return "SHA512"
|
||||
case x509.SHA512WithRSAPSS:
|
||||
return "SHA512"
|
||||
case x509.PureEd25519:
|
||||
return "SHA512"
|
||||
case x509.UnknownSignatureAlgorithm:
|
||||
return "unknown hash algorithm"
|
||||
default:
|
||||
return "unknown hash algorithm"
|
||||
}
|
||||
}
|
||||
|
||||
const maxLine = 78
|
||||
|
||||
func makeIndent(n int) string {
|
||||
s := " "
|
||||
for i := 0; i < n; i++ {
|
||||
s += " "
|
||||
var sSb97 strings.Builder
|
||||
for range n {
|
||||
sSb97.WriteString(" ")
|
||||
}
|
||||
s += sSb97.String()
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -121,7 +106,7 @@ func indentLen(n int) int {
|
||||
return 4 + (8 * n)
|
||||
}
|
||||
|
||||
// this isn't real efficient, but that's not a problem here
|
||||
// this isn't real efficient, but that's not a problem here.
|
||||
func wrap(s string, indent int) string {
|
||||
if indent > 3 {
|
||||
indent = 3
|
||||
@@ -144,9 +129,11 @@ func wrap(s string, indent int) string {
|
||||
|
||||
func dumpHex(in []byte) string {
|
||||
var s string
|
||||
var sSb130 strings.Builder
|
||||
for i := range in {
|
||||
s += fmt.Sprintf("%02X:", in[i])
|
||||
sSb130.WriteString(fmt.Sprintf("%02X:", in[i]))
|
||||
}
|
||||
s += sSb130.String()
|
||||
|
||||
return strings.Trim(s, ":")
|
||||
}
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.wntrmute.dev/kyle/goutils/certlib"
|
||||
"git.wntrmute.dev/kyle/goutils/die"
|
||||
"git.wntrmute.dev/kyle/goutils/lib"
|
||||
"git.wntrmute.dev/kyle/goutils/certlib"
|
||||
"git.wntrmute.dev/kyle/goutils/die"
|
||||
"git.wntrmute.dev/kyle/goutils/lib"
|
||||
)
|
||||
|
||||
var warnOnly bool
|
||||
@@ -53,7 +53,7 @@ func displayName(name pkix.Name) string {
|
||||
}
|
||||
|
||||
func expires(cert *x509.Certificate) time.Duration {
|
||||
return cert.NotAfter.Sub(time.Now())
|
||||
return time.Until(cert.NotAfter)
|
||||
}
|
||||
|
||||
func inDanger(cert *x509.Certificate) bool {
|
||||
@@ -79,21 +79,21 @@ func main() {
|
||||
flag.DurationVar(&leeway, "t", leeway, "warn if certificates are closer than this to expiring")
|
||||
flag.Parse()
|
||||
|
||||
for _, file := range flag.Args() {
|
||||
in, err := os.ReadFile(file)
|
||||
if err != nil {
|
||||
_, _ = lib.Warn(err, "failed to read file")
|
||||
continue
|
||||
}
|
||||
for _, file := range flag.Args() {
|
||||
in, err := os.ReadFile(file)
|
||||
if err != nil {
|
||||
_, _ = lib.Warn(err, "failed to read file")
|
||||
continue
|
||||
}
|
||||
|
||||
certs, err := certlib.ParseCertificatesPEM(in)
|
||||
if err != nil {
|
||||
_, _ = lib.Warn(err, "while parsing certificates")
|
||||
continue
|
||||
}
|
||||
certs, err := certlib.ParseCertificatesPEM(in)
|
||||
if err != nil {
|
||||
_, _ = lib.Warn(err, "while parsing certificates")
|
||||
continue
|
||||
}
|
||||
|
||||
for _, cert := range certs {
|
||||
checkCert(cert)
|
||||
}
|
||||
}
|
||||
for _, cert := range certs {
|
||||
checkCert(cert)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"crypto/x509"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
@@ -68,7 +67,7 @@ func main() {
|
||||
lib.ProgName())
|
||||
}
|
||||
|
||||
fileData, err := ioutil.ReadFile(flag.Arg(0))
|
||||
fileData, err := os.ReadFile(flag.Arg(0))
|
||||
die.If(err)
|
||||
|
||||
chain, err := certlib.ParseCertificatesPEM(fileData)
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"errors"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"bufio"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"git.wntrmute.dev/kyle/goutils/lib"
|
||||
"github.com/pkg/sftp"
|
||||
@@ -83,7 +83,7 @@ func scanner(host string, in io.Reader, out io.Writer) {
|
||||
}
|
||||
}
|
||||
|
||||
func logError(host string, err error, format string, args ...interface{}) {
|
||||
func logError(host string, err error, format string, args ...any) {
|
||||
msg := fmt.Sprintf(format, args...)
|
||||
log.Printf("[%s] FAILED: %s: %v\n", host, msg, err)
|
||||
}
|
||||
@@ -93,12 +93,12 @@ func exec(wg *sync.WaitGroup, user, host string, commands []string) {
|
||||
|
||||
defer func() {
|
||||
for i := len(shutdown) - 1; i >= 0; i-- {
|
||||
err := shutdown[i]()
|
||||
if err != nil && !errors.Is(err, io.EOF) {
|
||||
logError(host, err, "shutting down")
|
||||
}
|
||||
}
|
||||
}()
|
||||
err := shutdown[i]()
|
||||
if err != nil && !errors.Is(err, io.EOF) {
|
||||
logError(host, err, "shutting down")
|
||||
}
|
||||
}
|
||||
}()
|
||||
defer wg.Done()
|
||||
|
||||
conf := sshConfig(user)
|
||||
@@ -150,12 +150,12 @@ func upload(wg *sync.WaitGroup, user, host, local, remote string) {
|
||||
|
||||
defer func() {
|
||||
for i := len(shutdown) - 1; i >= 0; i-- {
|
||||
err := shutdown[i]()
|
||||
if err != nil && !errors.Is(err, io.EOF) {
|
||||
logError(host, err, "shutting down")
|
||||
}
|
||||
}
|
||||
}()
|
||||
err := shutdown[i]()
|
||||
if err != nil && !errors.Is(err, io.EOF) {
|
||||
logError(host, err, "shutting down")
|
||||
}
|
||||
}
|
||||
}()
|
||||
defer wg.Done()
|
||||
|
||||
conf := sshConfig(user)
|
||||
@@ -200,13 +200,13 @@ func upload(wg *sync.WaitGroup, user, host, local, remote string) {
|
||||
fmt.Printf("[%s] wrote %d-byte chunk\n", host, n)
|
||||
}
|
||||
|
||||
if errors.Is(err, io.EOF) {
|
||||
break
|
||||
} else if err != nil {
|
||||
logError(host, err, "reading chunk")
|
||||
return
|
||||
}
|
||||
}
|
||||
if errors.Is(err, io.EOF) {
|
||||
break
|
||||
} else if err != nil {
|
||||
logError(host, err, "reading chunk")
|
||||
return
|
||||
}
|
||||
}
|
||||
fmt.Printf("[%s] %s uploaded to %s\n", host, remote, local)
|
||||
}
|
||||
|
||||
@@ -215,12 +215,12 @@ func download(wg *sync.WaitGroup, user, host, local, remote string) {
|
||||
|
||||
defer func() {
|
||||
for i := len(shutdown) - 1; i >= 0; i-- {
|
||||
err := shutdown[i]()
|
||||
if err != nil && !errors.Is(err, io.EOF) {
|
||||
logError(host, err, "shutting down")
|
||||
}
|
||||
}
|
||||
}()
|
||||
err := shutdown[i]()
|
||||
if err != nil && !errors.Is(err, io.EOF) {
|
||||
logError(host, err, "shutting down")
|
||||
}
|
||||
}
|
||||
}()
|
||||
defer wg.Done()
|
||||
|
||||
conf := sshConfig(user)
|
||||
@@ -266,12 +266,12 @@ func download(wg *sync.WaitGroup, user, host, local, remote string) {
|
||||
fmt.Printf("[%s] wrote %d-byte chunk\n", host, n)
|
||||
}
|
||||
|
||||
if errors.Is(err, io.EOF) {
|
||||
break
|
||||
} else if err != nil {
|
||||
logError(host, err, "reading chunk")
|
||||
return
|
||||
}
|
||||
if errors.Is(err, io.EOF) {
|
||||
break
|
||||
} else if err != nil {
|
||||
logError(host, err, "reading chunk")
|
||||
return
|
||||
}
|
||||
}
|
||||
fmt.Printf("[%s] %s downloaded to %s\n", host, remote, local)
|
||||
}
|
||||
|
||||
@@ -262,15 +262,14 @@ func main() {
|
||||
|
||||
tfr := tar.NewReader(r)
|
||||
for {
|
||||
hdr, err := tfr.Next()
|
||||
if errors.Is(err, io.EOF) {
|
||||
break
|
||||
}
|
||||
die.If(err)
|
||||
hdr, err := tfr.Next()
|
||||
if errors.Is(err, io.EOF) {
|
||||
break
|
||||
}
|
||||
die.If(err)
|
||||
|
||||
err = processFile(tfr, hdr, top)
|
||||
die.If(err)
|
||||
|
||||
}
|
||||
|
||||
r.Close()
|
||||
|
||||
@@ -47,8 +47,8 @@ func main() {
|
||||
Bytes: out,
|
||||
}
|
||||
|
||||
err = os.WriteFile(fileName+".pub", pem.EncodeToMemory(p), 0o644)
|
||||
die.If(err)
|
||||
fmt.Fprintf(os.Stdout, "[+] wrote %s.\n", fileName+".pub")
|
||||
err = os.WriteFile(fileName+".pub", pem.EncodeToMemory(p), 0o644)
|
||||
die.If(err)
|
||||
fmt.Fprintf(os.Stdout, "[+] wrote %s.\n", fileName+".pub")
|
||||
}
|
||||
}
|
||||
@@ -163,7 +163,6 @@ func init() {
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
var logLevel, mountDir, syncDir, target string
|
||||
var dryRun, quietMode, noSyslog, verboseRsync bool
|
||||
|
||||
|
||||
@@ -15,43 +15,41 @@ import (
|
||||
const defaultHashAlgorithm = "sha256"
|
||||
|
||||
var (
|
||||
hAlgo string
|
||||
hAlgo string
|
||||
debug = dbg.New()
|
||||
)
|
||||
|
||||
|
||||
func openImage(imageFile string) (image *os.File, hash []byte, err error) {
|
||||
image, err = os.Open(imageFile)
|
||||
func openImage(imageFile string) (*os.File, []byte, error) {
|
||||
f, err := os.Open(imageFile)
|
||||
if err != nil {
|
||||
return
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
hash, err = ahash.SumReader(hAlgo, image)
|
||||
h, err := ahash.SumReader(hAlgo, f)
|
||||
if err != nil {
|
||||
return
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
_, err = image.Seek(0, 0)
|
||||
if err != nil {
|
||||
return
|
||||
if _, err := f.Seek(0, 0); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
debug.Printf("%s %x\n", imageFile, hash)
|
||||
return
|
||||
debug.Printf("%s %x\n", imageFile, h)
|
||||
return f, h, nil
|
||||
}
|
||||
|
||||
func openDevice(devicePath string) (device *os.File, err error) {
|
||||
func openDevice(devicePath string) (*os.File, error) {
|
||||
fi, err := os.Stat(devicePath)
|
||||
if err != nil {
|
||||
return
|
||||
return nil, err
|
||||
}
|
||||
|
||||
device, err = os.OpenFile(devicePath, os.O_RDWR|os.O_SYNC, fi.Mode())
|
||||
device, err := os.OpenFile(devicePath, os.O_RDWR|os.O_SYNC, fi.Mode())
|
||||
if err != nil {
|
||||
return
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return
|
||||
return device, nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
@@ -3,28 +3,30 @@ package main
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"git.wntrmute.dev/kyle/goutils/die"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"git.wntrmute.dev/kyle/goutils/die"
|
||||
)
|
||||
|
||||
func usage(w io.Writer, exc int) {
|
||||
fmt.Fprintln(w, `usage: dumpbytes <file>`)
|
||||
fmt.Fprintln(w, `usage: dumpbytes -n tabs <file>`)
|
||||
os.Exit(exc)
|
||||
}
|
||||
|
||||
func printBytes(buf []byte) {
|
||||
fmt.Printf("\t")
|
||||
for i := 0; i < len(buf); i++ {
|
||||
for i := range buf {
|
||||
fmt.Printf("0x%02x, ", buf[i])
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
func dumpFile(path string, indentLevel int) error {
|
||||
indent := ""
|
||||
for i := 0; i < indentLevel; i++ {
|
||||
indent += "\t"
|
||||
var indent strings.Builder
|
||||
for range indentLevel {
|
||||
indent.WriteByte('\t')
|
||||
}
|
||||
|
||||
file, err := os.Open(path)
|
||||
@@ -34,13 +36,13 @@ func dumpFile(path string, indentLevel int) error {
|
||||
|
||||
defer file.Close()
|
||||
|
||||
fmt.Printf("%svar buffer = []byte{\n", indent)
|
||||
fmt.Printf("%svar buffer = []byte{\n", indent.String())
|
||||
for {
|
||||
buf := make([]byte, 8)
|
||||
n, err := file.Read(buf)
|
||||
if err == io.EOF {
|
||||
if n > 0 {
|
||||
fmt.Printf("%s", indent)
|
||||
fmt.Printf("%s", indent.String())
|
||||
printBytes(buf[:n])
|
||||
}
|
||||
break
|
||||
@@ -50,11 +52,11 @@ func dumpFile(path string, indentLevel int) error {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("%s", indent)
|
||||
fmt.Printf("%s", indent.String())
|
||||
printBytes(buf[:n])
|
||||
}
|
||||
|
||||
fmt.Printf("%s}\n", indent)
|
||||
fmt.Printf("%s}\n", indent.String())
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"git.wntrmute.dev/kyle/goutils/die"
|
||||
)
|
||||
|
||||
// size of a kilobit in bytes
|
||||
// size of a kilobit in bytes.
|
||||
const kilobit = 128
|
||||
const pageSize = 4096
|
||||
|
||||
@@ -29,7 +29,7 @@ func main() {
|
||||
fillByte := uint8(*fill)
|
||||
|
||||
buf := make([]byte, pageSize)
|
||||
for i := 0; i < pageSize; i++ {
|
||||
for i := range pageSize {
|
||||
buf[i] = fillByte
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ func main() {
|
||||
die.If(err)
|
||||
defer file.Close()
|
||||
|
||||
for i := 0; i < pages; i++ {
|
||||
for range pages {
|
||||
_, err = file.Write(buf)
|
||||
die.If(err)
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ func main() {
|
||||
var fmtStr string
|
||||
|
||||
if !*quiet {
|
||||
maxLine := fmt.Sprintf("%d", len(lines))
|
||||
maxLine := strconv.Itoa(len(lines))
|
||||
fmtStr = fmt.Sprintf("%%0%dd: %%s", len(maxLine))
|
||||
}
|
||||
|
||||
@@ -95,12 +95,12 @@ func main() {
|
||||
return false
|
||||
}
|
||||
|
||||
fmtStr += "\n"
|
||||
for i := start; !endFunc(i); i++ {
|
||||
if *quiet {
|
||||
fmt.Fprintln(os.Stdout, lines[i])
|
||||
} else {
|
||||
fmt.Fprintf(os.Stdout, fmtStr, i, lines[i])
|
||||
}
|
||||
}
|
||||
fmtStr += "\n"
|
||||
for i := start; !endFunc(i); i++ {
|
||||
if *quiet {
|
||||
fmt.Fprintln(os.Stdout, lines[i])
|
||||
} else {
|
||||
fmt.Fprintf(os.Stdout, fmtStr, i, lines[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,51 +1,51 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"git.wntrmute.dev/kyle/goutils/lib"
|
||||
"git.wntrmute.dev/kyle/goutils/lib"
|
||||
)
|
||||
|
||||
func prettify(file string, validateOnly bool) error {
|
||||
var in []byte
|
||||
var err error
|
||||
|
||||
if file == "-" {
|
||||
in, err = io.ReadAll(os.Stdin)
|
||||
} else {
|
||||
in, err = os.ReadFile(file)
|
||||
}
|
||||
if file == "-" {
|
||||
in, err = io.ReadAll(os.Stdin)
|
||||
} else {
|
||||
in, err = os.ReadFile(file)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
_, _ = lib.Warn(err, "ReadFile")
|
||||
return err
|
||||
}
|
||||
if err != nil {
|
||||
_, _ = lib.Warn(err, "ReadFile")
|
||||
return err
|
||||
}
|
||||
|
||||
var buf = &bytes.Buffer{}
|
||||
err = json.Indent(buf, in, "", " ")
|
||||
if err != nil {
|
||||
_, _ = lib.Warn(err, "%s", file)
|
||||
return err
|
||||
}
|
||||
if err != nil {
|
||||
_, _ = lib.Warn(err, "%s", file)
|
||||
return err
|
||||
}
|
||||
|
||||
if validateOnly {
|
||||
return nil
|
||||
}
|
||||
|
||||
if file == "-" {
|
||||
_, err = os.Stdout.Write(buf.Bytes())
|
||||
} else {
|
||||
err = os.WriteFile(file, buf.Bytes(), 0o644)
|
||||
}
|
||||
if file == "-" {
|
||||
_, err = os.Stdout.Write(buf.Bytes())
|
||||
} else {
|
||||
err = os.WriteFile(file, buf.Bytes(), 0o644)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
_, _ = lib.Warn(err, "WriteFile")
|
||||
}
|
||||
if err != nil {
|
||||
_, _ = lib.Warn(err, "WriteFile")
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
@@ -54,44 +54,44 @@ func compact(file string, validateOnly bool) error {
|
||||
var in []byte
|
||||
var err error
|
||||
|
||||
if file == "-" {
|
||||
in, err = io.ReadAll(os.Stdin)
|
||||
} else {
|
||||
in, err = os.ReadFile(file)
|
||||
}
|
||||
if file == "-" {
|
||||
in, err = io.ReadAll(os.Stdin)
|
||||
} else {
|
||||
in, err = os.ReadFile(file)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
_, _ = lib.Warn(err, "ReadFile")
|
||||
return err
|
||||
}
|
||||
if err != nil {
|
||||
_, _ = lib.Warn(err, "ReadFile")
|
||||
return err
|
||||
}
|
||||
|
||||
var buf = &bytes.Buffer{}
|
||||
err = json.Compact(buf, in)
|
||||
if err != nil {
|
||||
_, _ = lib.Warn(err, "%s", file)
|
||||
return err
|
||||
}
|
||||
if err != nil {
|
||||
_, _ = lib.Warn(err, "%s", file)
|
||||
return err
|
||||
}
|
||||
|
||||
if validateOnly {
|
||||
return nil
|
||||
}
|
||||
|
||||
if file == "-" {
|
||||
_, err = os.Stdout.Write(buf.Bytes())
|
||||
} else {
|
||||
err = os.WriteFile(file, buf.Bytes(), 0o644)
|
||||
}
|
||||
if file == "-" {
|
||||
_, err = os.Stdout.Write(buf.Bytes())
|
||||
} else {
|
||||
err = os.WriteFile(file, buf.Bytes(), 0o644)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
_, _ = lib.Warn(err, "WriteFile")
|
||||
}
|
||||
if err != nil {
|
||||
_, _ = lib.Warn(err, "WriteFile")
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func usage() {
|
||||
progname := lib.ProgName()
|
||||
fmt.Fprintf(os.Stdout, `Usage: %s [-h] files...
|
||||
progname := lib.ProgName()
|
||||
fmt.Fprintf(os.Stdout, `Usage: %s [-h] files...
|
||||
%s is used to lint and prettify (or compact) JSON files. The
|
||||
files will be updated in-place.
|
||||
|
||||
@@ -100,7 +100,6 @@ func usage() {
|
||||
-h Print this help message.
|
||||
-n Don't prettify; only perform validation.
|
||||
`, progname, progname)
|
||||
|
||||
}
|
||||
|
||||
func init() {
|
||||
@@ -1,68 +1,68 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"compress/flate"
|
||||
"compress/gzip"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"compress/flate"
|
||||
"compress/gzip"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const gzipExt = ".gz"
|
||||
|
||||
func compress(path, target string, level int) error {
|
||||
sourceFile, err := os.Open(path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("opening file for read: %w", err)
|
||||
}
|
||||
sourceFile, err := os.Open(path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("opening file for read: %w", err)
|
||||
}
|
||||
defer sourceFile.Close()
|
||||
|
||||
destFile, err := os.Create(target)
|
||||
if err != nil {
|
||||
return fmt.Errorf("opening file for write: %w", err)
|
||||
}
|
||||
destFile, err := os.Create(target)
|
||||
if err != nil {
|
||||
return fmt.Errorf("opening file for write: %w", err)
|
||||
}
|
||||
defer destFile.Close()
|
||||
|
||||
gzipCompressor, err := gzip.NewWriterLevel(destFile, level)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid compression level: %w", err)
|
||||
}
|
||||
gzipCompressor, err := gzip.NewWriterLevel(destFile, level)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid compression level: %w", err)
|
||||
}
|
||||
defer gzipCompressor.Close()
|
||||
|
||||
_, err = io.Copy(gzipCompressor, sourceFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("compressing file: %w", err)
|
||||
}
|
||||
_, err = io.Copy(gzipCompressor, sourceFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("compressing file: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func uncompress(path, target string) error {
|
||||
sourceFile, err := os.Open(path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("opening file for read: %w", err)
|
||||
}
|
||||
sourceFile, err := os.Open(path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("opening file for read: %w", err)
|
||||
}
|
||||
defer sourceFile.Close()
|
||||
|
||||
gzipUncompressor, err := gzip.NewReader(sourceFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("reading gzip headers: %w", err)
|
||||
}
|
||||
gzipUncompressor, err := gzip.NewReader(sourceFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("reading gzip headers: %w", err)
|
||||
}
|
||||
defer gzipUncompressor.Close()
|
||||
|
||||
destFile, err := os.Create(target)
|
||||
if err != nil {
|
||||
return fmt.Errorf("opening file for write: %w", err)
|
||||
}
|
||||
destFile, err := os.Create(target)
|
||||
if err != nil {
|
||||
return fmt.Errorf("opening file for write: %w", err)
|
||||
}
|
||||
defer destFile.Close()
|
||||
|
||||
_, err = io.Copy(destFile, gzipUncompressor)
|
||||
if err != nil {
|
||||
return fmt.Errorf("uncompressing file: %w", err)
|
||||
}
|
||||
_, err = io.Copy(destFile, gzipUncompressor)
|
||||
if err != nil {
|
||||
return fmt.Errorf("uncompressing file: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -106,9 +106,9 @@ func pathForUncompressing(source, dest string) (string, error) {
|
||||
}
|
||||
|
||||
source = filepath.Base(source)
|
||||
if !strings.HasSuffix(source, gzipExt) {
|
||||
return "", fmt.Errorf("%s is a not gzip-compressed file", source)
|
||||
}
|
||||
if !strings.HasSuffix(source, gzipExt) {
|
||||
return "", fmt.Errorf("%s is a not gzip-compressed file", source)
|
||||
}
|
||||
outFile := source[:len(source)-len(gzipExt)]
|
||||
outFile = filepath.Join(dest, outFile)
|
||||
return outFile, nil
|
||||
@@ -120,9 +120,9 @@ func pathForCompressing(source, dest string) (string, error) {
|
||||
}
|
||||
|
||||
source = filepath.Base(source)
|
||||
if strings.HasSuffix(source, gzipExt) {
|
||||
return "", fmt.Errorf("%s is a gzip-compressed file", source)
|
||||
}
|
||||
if strings.HasSuffix(source, gzipExt) {
|
||||
return "", fmt.Errorf("%s is a gzip-compressed file", source)
|
||||
}
|
||||
|
||||
dest = filepath.Join(dest, source+gzipExt)
|
||||
return dest, nil
|
||||
|
||||
@@ -40,14 +40,14 @@ func main() {
|
||||
usage()
|
||||
}
|
||||
|
||||
min, err := strconv.Atoi(flag.Arg(1))
|
||||
minVal, err := strconv.Atoi(flag.Arg(1))
|
||||
dieIf(err)
|
||||
|
||||
max, err := strconv.Atoi(flag.Arg(2))
|
||||
maxVal, err := strconv.Atoi(flag.Arg(2))
|
||||
dieIf(err)
|
||||
|
||||
code := kind << 6
|
||||
code += (min << 3)
|
||||
code += max
|
||||
fmt.Fprintf(os.Stdout, "%0o\n", code)
|
||||
code := kind << 6
|
||||
code += (minVal << 3)
|
||||
code += maxVal
|
||||
fmt.Fprintf(os.Stdout, "%0o\n", code)
|
||||
}
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
@@ -47,7 +46,7 @@ func help(w io.Writer) {
|
||||
}
|
||||
|
||||
func loadDatabase() {
|
||||
data, err := ioutil.ReadFile(dbFile)
|
||||
data, err := os.ReadFile(dbFile)
|
||||
if err != nil && os.IsNotExist(err) {
|
||||
partsDB = &database{
|
||||
Version: dbVersion,
|
||||
@@ -74,7 +73,7 @@ func writeDB() {
|
||||
data, err := json.Marshal(partsDB)
|
||||
die.If(err)
|
||||
|
||||
err = ioutil.WriteFile(dbFile, data, 0644)
|
||||
err = os.WriteFile(dbFile, data, 0644)
|
||||
die.If(err)
|
||||
}
|
||||
|
||||
|
||||
@@ -4,14 +4,13 @@ import (
|
||||
"encoding/pem"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
)
|
||||
|
||||
var ext = ".bin"
|
||||
|
||||
func stripPEM(path string) error {
|
||||
data, err := ioutil.ReadFile(path)
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -22,7 +21,7 @@ func stripPEM(path string) error {
|
||||
fmt.Fprintf(os.Stderr, " (only the first object will be decoded)\n")
|
||||
}
|
||||
|
||||
return ioutil.WriteFile(path+ext, p.Bytes, 0644)
|
||||
return os.WriteFile(path+ext, p.Bytes, 0644)
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
@@ -18,21 +18,21 @@ func main() {
|
||||
var in []byte
|
||||
var err error
|
||||
|
||||
path := flag.Arg(0)
|
||||
if path == "-" {
|
||||
in, err = io.ReadAll(os.Stdin)
|
||||
} else {
|
||||
in, err = os.ReadFile(flag.Arg(0))
|
||||
}
|
||||
path := flag.Arg(0)
|
||||
if path == "-" {
|
||||
in, err = io.ReadAll(os.Stdin)
|
||||
} else {
|
||||
in, err = os.ReadFile(flag.Arg(0))
|
||||
}
|
||||
if err != nil {
|
||||
lib.Err(lib.ExitFailure, err, "couldn't read file")
|
||||
}
|
||||
|
||||
p, _ := pem.Decode(in)
|
||||
if p == nil {
|
||||
lib.Errx(lib.ExitFailure, "%s isn't a PEM-encoded file", flag.Arg(0))
|
||||
}
|
||||
if _, err := os.Stdout.Write(p.Bytes); err != nil {
|
||||
lib.Err(lib.ExitFailure, err, "writing body")
|
||||
}
|
||||
if p == nil {
|
||||
lib.Errx(lib.ExitFailure, "%s isn't a PEM-encoded file", flag.Arg(0))
|
||||
}
|
||||
if _, err := os.Stdout.Write(p.Bytes); err != nil {
|
||||
lib.Err(lib.ExitFailure, err, "writing body")
|
||||
}
|
||||
}
|
||||
@@ -70,7 +70,7 @@ func main() {
|
||||
lib.Err(lib.ExitFailure, err, "failed to read input")
|
||||
}
|
||||
case argc > 1:
|
||||
for i := 0; i < argc; i++ {
|
||||
for i := range argc {
|
||||
path := flag.Arg(i)
|
||||
err = copyFile(path, buf)
|
||||
if err != nil {
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
for _, fileName := range flag.Args() {
|
||||
data, err := os.ReadFile(fileName)
|
||||
data, err := os.ReadFile(fileName)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "[!] %s: %v\n", fileName, err)
|
||||
continue
|
||||
}
|
||||
|
||||
fmt.Fprintf(os.Stdout, "[+] %s:\n", fileName)
|
||||
rest := data[:]
|
||||
fmt.Fprintf(os.Stdout, "[+] %s:\n", fileName)
|
||||
rest := data
|
||||
for {
|
||||
var p *pem.Block
|
||||
p, rest = pem.Decode(rest)
|
||||
@@ -33,7 +33,7 @@ func main() {
|
||||
break
|
||||
}
|
||||
|
||||
fmt.Fprintf(os.Stdout, "\t%+v\n", cert.Subject.CommonName)
|
||||
fmt.Fprintf(os.Stdout, "\t%+v\n", cert.Subject.CommonName)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -43,7 +43,7 @@ func newName(path string) (string, error) {
|
||||
return hashName(path, encodedHash), nil
|
||||
}
|
||||
|
||||
func move(dst, src string, force bool) (err error) {
|
||||
func move(dst, src string, force bool) error {
|
||||
if fileutil.FileDoesExist(dst) && !force {
|
||||
return fmt.Errorf("%s exists (pass the -f flag to overwrite)", dst)
|
||||
}
|
||||
@@ -52,21 +52,23 @@ func move(dst, src string, force bool) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
defer func(e error) {
|
||||
var retErr error
|
||||
defer func(e *error) {
|
||||
dstFile.Close()
|
||||
if e != nil {
|
||||
if *e != nil {
|
||||
os.Remove(dst)
|
||||
}
|
||||
}(err)
|
||||
}(&retErr)
|
||||
|
||||
srcFile, err := os.Open(src)
|
||||
if err != nil {
|
||||
retErr = err
|
||||
return err
|
||||
}
|
||||
defer srcFile.Close()
|
||||
|
||||
_, err = io.Copy(dstFile, srcFile)
|
||||
if err != nil {
|
||||
if _, err = io.Copy(dstFile, srcFile); err != nil {
|
||||
retErr = err
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -109,27 +111,27 @@ func main() {
|
||||
|
||||
for _, file := range flag.Args() {
|
||||
renamed, err := newName(file)
|
||||
if err != nil {
|
||||
_, _ = lib.Warn(err, "failed to get new file name")
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
_, _ = lib.Warn(err, "failed to get new file name")
|
||||
continue
|
||||
}
|
||||
|
||||
if verbose && !printChanged {
|
||||
fmt.Fprintln(os.Stdout, file)
|
||||
}
|
||||
if verbose && !printChanged {
|
||||
fmt.Fprintln(os.Stdout, file)
|
||||
}
|
||||
|
||||
if renamed != file {
|
||||
if !dryRun {
|
||||
err = move(renamed, file, force)
|
||||
if err != nil {
|
||||
_, _ = lib.Warn(err, "failed to rename file from %s to %s", file, renamed)
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
_, _ = lib.Warn(err, "failed to rename file from %s to %s", file, renamed)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if printChanged && !verbose {
|
||||
fmt.Fprintln(os.Stdout, file, "->", renamed)
|
||||
}
|
||||
if printChanged && !verbose {
|
||||
fmt.Fprintln(os.Stdout, file, "->", renamed)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,11 +17,11 @@ func rollDie(count, sides int) []int {
|
||||
sum := 0
|
||||
var rolls []int
|
||||
|
||||
for i := 0; i < count; i++ {
|
||||
roll := rand.IntN(sides) + 1
|
||||
sum += roll
|
||||
rolls = append(rolls, roll)
|
||||
}
|
||||
for range count {
|
||||
roll := rand.IntN(sides) + 1
|
||||
sum += roll
|
||||
rolls = append(rolls, roll)
|
||||
}
|
||||
|
||||
rolls = append(rolls, sum)
|
||||
return rolls
|
||||
|
||||
@@ -53,7 +53,7 @@ func init() {
|
||||
project = wd[len(gopath):]
|
||||
}
|
||||
|
||||
func walkFile(path string, info os.FileInfo, err error) error {
|
||||
func walkFile(path string, _ os.FileInfo, err error) error {
|
||||
if ignores[path] {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
@@ -62,13 +62,17 @@ func walkFile(path string, info os.FileInfo, err error) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
debug.Println(path)
|
||||
|
||||
f, err := parser.ParseFile(fset, path, nil, parser.ImportsOnly)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
debug.Println(path)
|
||||
|
||||
f, err2 := parser.ParseFile(fset, path, nil, parser.ImportsOnly)
|
||||
if err2 != nil {
|
||||
return err2
|
||||
}
|
||||
|
||||
for _, importSpec := range f.Imports {
|
||||
importPath := strings.Trim(importSpec.Path.Value, `"`)
|
||||
if stdLibRegexp.MatchString(importPath) {
|
||||
@@ -102,7 +106,7 @@ func main() {
|
||||
ignores["vendor"] = true
|
||||
}
|
||||
|
||||
for _, word := range strings.Split(ignoreLine, ",") {
|
||||
for word := range strings.SplitSeq(ignoreLine, ",") {
|
||||
ignores[strings.TrimSpace(word)] = true
|
||||
}
|
||||
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/rsa"
|
||||
"crypto/sha1"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/asn1"
|
||||
"encoding/pem"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
"bytes"
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/rsa"
|
||||
"crypto/sha1"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/asn1"
|
||||
"encoding/pem"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"git.wntrmute.dev/kyle/goutils/die"
|
||||
"git.wntrmute.dev/kyle/goutils/lib"
|
||||
"git.wntrmute.dev/kyle/goutils/die"
|
||||
"git.wntrmute.dev/kyle/goutils/lib"
|
||||
)
|
||||
|
||||
func usage(w io.Writer) {
|
||||
@@ -38,15 +38,15 @@ func init() {
|
||||
flag.Usage = func() { usage(os.Stderr) }
|
||||
}
|
||||
|
||||
func parse(path string) (public []byte, kt, ft string) {
|
||||
data, err := os.ReadFile(path)
|
||||
func parse(path string) ([]byte, string, string) {
|
||||
data, err := os.ReadFile(path)
|
||||
die.If(err)
|
||||
|
||||
data = bytes.TrimSpace(data)
|
||||
p, rest := pem.Decode(data)
|
||||
if len(rest) > 0 {
|
||||
_, _ = lib.Warnx("trailing data in PEM file")
|
||||
}
|
||||
if len(rest) > 0 {
|
||||
_, _ = lib.Warnx("trailing data in PEM file")
|
||||
}
|
||||
|
||||
if p == nil {
|
||||
die.With("no PEM data found")
|
||||
@@ -54,6 +54,12 @@ func parse(path string) (public []byte, kt, ft string) {
|
||||
|
||||
data = p.Bytes
|
||||
|
||||
var (
|
||||
public []byte
|
||||
kt string
|
||||
ft string
|
||||
)
|
||||
|
||||
switch p.Type {
|
||||
case "PRIVATE KEY", "RSA PRIVATE KEY", "EC PRIVATE KEY":
|
||||
public, kt = parseKey(data)
|
||||
@@ -68,11 +74,11 @@ func parse(path string) (public []byte, kt, ft string) {
|
||||
die.With("unknown PEM type %s", p.Type)
|
||||
}
|
||||
|
||||
return
|
||||
return public, kt, ft
|
||||
}
|
||||
|
||||
func parseKey(data []byte) (public []byte, kt string) {
|
||||
privInterface, err := x509.ParsePKCS8PrivateKey(data)
|
||||
func parseKey(data []byte) ([]byte, string) {
|
||||
privInterface, err := x509.ParsePKCS8PrivateKey(data)
|
||||
if err != nil {
|
||||
privInterface, err = x509.ParsePKCS1PrivateKey(data)
|
||||
if err != nil {
|
||||
@@ -84,6 +90,7 @@ func parseKey(data []byte) (public []byte, kt string) {
|
||||
}
|
||||
|
||||
var priv crypto.Signer
|
||||
var kt string
|
||||
switch p := privInterface.(type) {
|
||||
case *rsa.PrivateKey:
|
||||
priv = p
|
||||
@@ -95,17 +102,18 @@ func parseKey(data []byte) (public []byte, kt string) {
|
||||
die.With("unknown private key type %T", privInterface)
|
||||
}
|
||||
|
||||
public, err = x509.MarshalPKIXPublicKey(priv.Public())
|
||||
public, err := x509.MarshalPKIXPublicKey(priv.Public())
|
||||
die.If(err)
|
||||
|
||||
return
|
||||
return public, kt
|
||||
}
|
||||
|
||||
func parseCertificate(data []byte) (public []byte, kt string) {
|
||||
func parseCertificate(data []byte) ([]byte, string) {
|
||||
cert, err := x509.ParseCertificate(data)
|
||||
die.If(err)
|
||||
|
||||
pub := cert.PublicKey
|
||||
var kt string
|
||||
switch pub.(type) {
|
||||
case *rsa.PublicKey:
|
||||
kt = "RSA"
|
||||
@@ -115,16 +123,17 @@ func parseCertificate(data []byte) (public []byte, kt string) {
|
||||
die.With("unknown public key type %T", pub)
|
||||
}
|
||||
|
||||
public, err = x509.MarshalPKIXPublicKey(pub)
|
||||
public, err := x509.MarshalPKIXPublicKey(pub)
|
||||
die.If(err)
|
||||
return
|
||||
return public, kt
|
||||
}
|
||||
|
||||
func parseCSR(data []byte) (public []byte, kt string) {
|
||||
func parseCSR(data []byte) ([]byte, string) {
|
||||
csr, err := x509.ParseCertificateRequest(data)
|
||||
die.If(err)
|
||||
|
||||
pub := csr.PublicKey
|
||||
var kt string
|
||||
switch pub.(type) {
|
||||
case *rsa.PublicKey:
|
||||
kt = "RSA"
|
||||
@@ -134,16 +143,18 @@ func parseCSR(data []byte) (public []byte, kt string) {
|
||||
die.With("unknown public key type %T", pub)
|
||||
}
|
||||
|
||||
public, err = x509.MarshalPKIXPublicKey(pub)
|
||||
public, err := x509.MarshalPKIXPublicKey(pub)
|
||||
die.If(err)
|
||||
return
|
||||
return public, kt
|
||||
}
|
||||
|
||||
func dumpHex(in []byte) string {
|
||||
var s string
|
||||
var sSb153 strings.Builder
|
||||
for i := range in {
|
||||
s += fmt.Sprintf("%02X:", in[i])
|
||||
sSb153.WriteString(fmt.Sprintf("%02X:", in[i]))
|
||||
}
|
||||
s += sSb153.String()
|
||||
|
||||
return strings.Trim(s, ":")
|
||||
}
|
||||
@@ -170,10 +181,10 @@ func main() {
|
||||
|
||||
var subPKI subjectPublicKeyInfo
|
||||
_, err := asn1.Unmarshal(public, &subPKI)
|
||||
if err != nil {
|
||||
_, _ = lib.Warn(err, "failed to get subject PKI")
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
_, _ = lib.Warn(err, "failed to get subject PKI")
|
||||
continue
|
||||
}
|
||||
|
||||
pubHash := sha1.Sum(subPKI.SubjectPublicKey.Bytes)
|
||||
pubHashString := dumpHex(pubHash[:])
|
||||
@@ -181,10 +192,10 @@ func main() {
|
||||
ski = pubHashString
|
||||
}
|
||||
|
||||
if shouldMatch && ski != pubHashString {
|
||||
_, _ = lib.Warnx("%s: SKI mismatch (%s != %s)",
|
||||
path, ski, pubHashString)
|
||||
}
|
||||
if shouldMatch && ski != pubHashString {
|
||||
_, _ = lib.Warnx("%s: SKI mismatch (%s != %s)",
|
||||
path, ski, pubHashString)
|
||||
}
|
||||
fmt.Printf("%s %s (%s %s)\n", path, pubHashString, kt, ft)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,19 +10,19 @@ import (
|
||||
)
|
||||
|
||||
func proxy(conn net.Conn, inside string) error {
|
||||
proxyConn, err := net.Dial("tcp", inside)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
proxyConn, err := net.Dial("tcp", inside)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer proxyConn.Close()
|
||||
defer conn.Close()
|
||||
|
||||
go func() {
|
||||
_, _ = io.Copy(conn, proxyConn)
|
||||
}()
|
||||
_, err = io.Copy(proxyConn, conn)
|
||||
return err
|
||||
go func() {
|
||||
_, _ = io.Copy(conn, proxyConn)
|
||||
}()
|
||||
_, err = io.Copy(proxyConn, conn)
|
||||
return err
|
||||
}
|
||||
|
||||
func main() {
|
||||
@@ -34,17 +34,17 @@ func main() {
|
||||
l, err := net.Listen("tcp", "0.0.0.0:"+outside)
|
||||
die.If(err)
|
||||
|
||||
for {
|
||||
conn, err := l.Accept()
|
||||
if err != nil {
|
||||
_, _ = lib.Warn(err, "accept failed")
|
||||
continue
|
||||
}
|
||||
for {
|
||||
conn, err := l.Accept()
|
||||
if err != nil {
|
||||
_, _ = lib.Warn(err, "accept failed")
|
||||
continue
|
||||
}
|
||||
|
||||
go func() {
|
||||
if err := proxy(conn, "127.0.0.1:"+inside); err != nil {
|
||||
_, _ = lib.Warn(err, "proxy error")
|
||||
}
|
||||
}()
|
||||
}
|
||||
go func() {
|
||||
if err := proxy(conn, "127.0.0.1:"+inside); err != nil {
|
||||
_, _ = lib.Warn(err, "proxy error")
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,18 +45,18 @@ func main() {
|
||||
os.Exit(1)
|
||||
}
|
||||
cfg.Certificates = append(cfg.Certificates, cert)
|
||||
if sysRoot != "" {
|
||||
pemList, err := os.ReadFile(sysRoot)
|
||||
die.If(err)
|
||||
if sysRoot != "" {
|
||||
pemList, err := os.ReadFile(sysRoot)
|
||||
die.If(err)
|
||||
|
||||
roots := x509.NewCertPool()
|
||||
if !roots.AppendCertsFromPEM(pemList) {
|
||||
fmt.Printf("[!] no valid roots found")
|
||||
roots = nil
|
||||
}
|
||||
roots := x509.NewCertPool()
|
||||
if !roots.AppendCertsFromPEM(pemList) {
|
||||
fmt.Printf("[!] no valid roots found")
|
||||
roots = nil
|
||||
}
|
||||
|
||||
cfg.RootCAs = roots
|
||||
}
|
||||
cfg.RootCAs = roots
|
||||
}
|
||||
|
||||
l, err := net.Listen("tcp", listenAddr)
|
||||
if err != nil {
|
||||
@@ -64,46 +64,46 @@ func main() {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
for {
|
||||
conn, err := l.Accept()
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
continue
|
||||
}
|
||||
handleConn(conn, cfg)
|
||||
}
|
||||
for {
|
||||
conn, err := l.Accept()
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
continue
|
||||
}
|
||||
handleConn(conn, cfg)
|
||||
}
|
||||
}
|
||||
|
||||
// handleConn performs a TLS handshake, extracts the peer chain, and writes it to a file.
|
||||
func handleConn(conn net.Conn, cfg *tls.Config) {
|
||||
defer conn.Close()
|
||||
raddr := conn.RemoteAddr()
|
||||
tconn := tls.Server(conn, cfg)
|
||||
if err := tconn.Handshake(); err != nil {
|
||||
fmt.Printf("[+] %v: failed to complete handshake: %v\n", raddr, err)
|
||||
return
|
||||
}
|
||||
cs := tconn.ConnectionState()
|
||||
if len(cs.PeerCertificates) == 0 {
|
||||
fmt.Printf("[+] %v: no chain presented\n", raddr)
|
||||
return
|
||||
}
|
||||
defer conn.Close()
|
||||
raddr := conn.RemoteAddr()
|
||||
tconn := tls.Server(conn, cfg)
|
||||
if err := tconn.Handshake(); err != nil {
|
||||
fmt.Printf("[+] %v: failed to complete handshake: %v\n", raddr, err)
|
||||
return
|
||||
}
|
||||
cs := tconn.ConnectionState()
|
||||
if len(cs.PeerCertificates) == 0 {
|
||||
fmt.Printf("[+] %v: no chain presented\n", raddr)
|
||||
return
|
||||
}
|
||||
|
||||
var chain []byte
|
||||
for _, cert := range cs.PeerCertificates {
|
||||
p := &pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw}
|
||||
chain = append(chain, pem.EncodeToMemory(p)...)
|
||||
}
|
||||
var chain []byte
|
||||
for _, cert := range cs.PeerCertificates {
|
||||
p := &pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw}
|
||||
chain = append(chain, pem.EncodeToMemory(p)...)
|
||||
}
|
||||
|
||||
var nonce [16]byte
|
||||
if _, err := rand.Read(nonce[:]); err != nil {
|
||||
fmt.Printf("[+] %v: failed to generate filename nonce: %v\n", raddr, err)
|
||||
return
|
||||
}
|
||||
fname := fmt.Sprintf("%v-%v.pem", raddr, hex.EncodeToString(nonce[:]))
|
||||
if err := os.WriteFile(fname, chain, 0o644); err != nil {
|
||||
fmt.Printf("[+] %v: failed to write %v: %v\n", raddr, fname, err)
|
||||
return
|
||||
}
|
||||
fmt.Printf("%v: [+] wrote %v.\n", raddr, fname)
|
||||
var nonce [16]byte
|
||||
if _, err := rand.Read(nonce[:]); err != nil {
|
||||
fmt.Printf("[+] %v: failed to generate filename nonce: %v\n", raddr, err)
|
||||
return
|
||||
}
|
||||
fname := fmt.Sprintf("%v-%v.pem", raddr, hex.EncodeToString(nonce[:]))
|
||||
if err := os.WriteFile(fname, chain, 0o644); err != nil {
|
||||
fmt.Printf("[+] %v: failed to write %v: %v\n", raddr, fname, err)
|
||||
return
|
||||
}
|
||||
fmt.Printf("%v: [+] wrote %v.\n", raddr, fname)
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"encoding/pem"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
|
||||
@@ -23,7 +22,7 @@ func main() {
|
||||
flag.Parse()
|
||||
|
||||
if sysRoot != "" {
|
||||
pemList, err := ioutil.ReadFile(sysRoot)
|
||||
pemList, err := os.ReadFile(sysRoot)
|
||||
die.If(err)
|
||||
|
||||
roots := x509.NewCertPool()
|
||||
@@ -61,7 +60,7 @@ func main() {
|
||||
chain = append(chain, pem.EncodeToMemory(p)...)
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(site+".pem", chain, 0644)
|
||||
err = os.WriteFile(site+".pem", chain, 0644)
|
||||
die.If(err)
|
||||
fmt.Printf("[+] wrote %s.pem.\n", site)
|
||||
}
|
||||
@@ -57,16 +57,16 @@ func getSubjectInfoHash(cert *x509.Certificate, issuer bool) []byte {
|
||||
}
|
||||
|
||||
func printDigests(paths []string, issuer bool) {
|
||||
for _, path := range paths {
|
||||
cert, err := certlib.LoadCertificate(path)
|
||||
if err != nil {
|
||||
_, _ = lib.Warn(err, "failed to load certificate from %s", path)
|
||||
continue
|
||||
}
|
||||
for _, path := range paths {
|
||||
cert, err := certlib.LoadCertificate(path)
|
||||
if err != nil {
|
||||
_, _ = lib.Warn(err, "failed to load certificate from %s", path)
|
||||
continue
|
||||
}
|
||||
|
||||
digest := getSubjectInfoHash(cert, issuer)
|
||||
fmt.Printf("%x %s\n", digest, path)
|
||||
}
|
||||
fmt.Printf("%x %s\n", digest, path)
|
||||
}
|
||||
}
|
||||
|
||||
func matchDigests(paths []string, issuer bool) {
|
||||
@@ -75,22 +75,21 @@ func matchDigests(paths []string, issuer bool) {
|
||||
}
|
||||
|
||||
var invalid int
|
||||
for {
|
||||
if len(paths) == 0 {
|
||||
break
|
||||
}
|
||||
for len(paths) > 0 {
|
||||
fst := paths[0]
|
||||
snd := paths[1]
|
||||
paths = paths[2:]
|
||||
|
||||
fstCert, err := certlib.LoadCertificate(fst)
|
||||
die.If(err)
|
||||
|
||||
sndCert, err := certlib.LoadCertificate(snd)
|
||||
die.If(err)
|
||||
if !bytes.Equal(getSubjectInfoHash(fstCert, issuer), getSubjectInfoHash(sndCert, issuer)) {
|
||||
_, _ = lib.Warnx("certificates don't match: %s and %s", fst, snd)
|
||||
invalid++
|
||||
}
|
||||
|
||||
if !bytes.Equal(getSubjectInfoHash(fstCert, issuer), getSubjectInfoHash(sndCert, issuer)) {
|
||||
_, _ = lib.Warnx("certificates don't match: %s and %s", fst, snd)
|
||||
invalid++
|
||||
}
|
||||
}
|
||||
|
||||
if invalid > 0 {
|
||||
|
||||
@@ -37,7 +37,6 @@ func printConnectionDetails(state tls.ConnectionState) {
|
||||
|
||||
func tlsVersion(version uint16) string {
|
||||
switch version {
|
||||
|
||||
case tls.VersionTLS13:
|
||||
return "TLS 1.3"
|
||||
case tls.VersionTLS12:
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"bytes"
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"git.wntrmute.dev/kyle/goutils/die"
|
||||
"git.wntrmute.dev/kyle/goutils/die"
|
||||
)
|
||||
|
||||
var validPEMs = map[string]bool{
|
||||
@@ -30,7 +30,7 @@ const (
|
||||
curveP521
|
||||
)
|
||||
|
||||
func getECCurve(pub interface{}) int {
|
||||
func getECCurve(pub any) int {
|
||||
switch pub := pub.(type) {
|
||||
case *ecdsa.PublicKey:
|
||||
switch pub.Curve {
|
||||
@@ -53,72 +53,72 @@ func getECCurve(pub interface{}) int {
|
||||
// matchRSA compares an RSA public key from certificate against RSA public key from private key.
|
||||
// It returns true on match.
|
||||
func matchRSA(certPub *rsa.PublicKey, keyPub *rsa.PublicKey) bool {
|
||||
return keyPub.N.Cmp(certPub.N) == 0 && keyPub.E == certPub.E
|
||||
return keyPub.N.Cmp(certPub.N) == 0 && keyPub.E == certPub.E
|
||||
}
|
||||
|
||||
// matchECDSA compares ECDSA public keys for equality and compatible curve.
|
||||
// It returns match=true when they are on the same curve and have the same X/Y.
|
||||
// If curves mismatch, match is false.
|
||||
func matchECDSA(certPub *ecdsa.PublicKey, keyPub *ecdsa.PublicKey) bool {
|
||||
if getECCurve(certPub) != getECCurve(keyPub) {
|
||||
return false
|
||||
}
|
||||
if keyPub.X.Cmp(certPub.X) != 0 {
|
||||
return false
|
||||
}
|
||||
if keyPub.Y.Cmp(certPub.Y) != 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
if getECCurve(certPub) != getECCurve(keyPub) {
|
||||
return false
|
||||
}
|
||||
if keyPub.X.Cmp(certPub.X) != 0 {
|
||||
return false
|
||||
}
|
||||
if keyPub.Y.Cmp(certPub.Y) != 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// matchKeys determines whether the certificate's public key matches the given private key.
|
||||
// It returns true if they match; otherwise, it returns false and a human-friendly reason.
|
||||
func matchKeys(cert *x509.Certificate, priv crypto.Signer) (bool, string) {
|
||||
switch keyPub := priv.Public().(type) {
|
||||
case *rsa.PublicKey:
|
||||
switch certPub := cert.PublicKey.(type) {
|
||||
case *rsa.PublicKey:
|
||||
if matchRSA(certPub, keyPub) {
|
||||
return true, ""
|
||||
}
|
||||
return false, "public keys don't match"
|
||||
case *ecdsa.PublicKey:
|
||||
return false, "RSA private key, EC public key"
|
||||
default:
|
||||
return false, fmt.Sprintf("unsupported certificate public key type: %T", cert.PublicKey)
|
||||
}
|
||||
case *ecdsa.PublicKey:
|
||||
switch certPub := cert.PublicKey.(type) {
|
||||
case *ecdsa.PublicKey:
|
||||
if matchECDSA(certPub, keyPub) {
|
||||
return true, ""
|
||||
}
|
||||
// Determine a more precise reason
|
||||
kc := getECCurve(keyPub)
|
||||
cc := getECCurve(certPub)
|
||||
if kc == curveInvalid {
|
||||
return false, "invalid private key curve"
|
||||
}
|
||||
if cc == curveRSA {
|
||||
return false, "private key is EC, certificate is RSA"
|
||||
}
|
||||
if kc != cc {
|
||||
return false, "EC curves don't match"
|
||||
}
|
||||
return false, "public keys don't match"
|
||||
case *rsa.PublicKey:
|
||||
return false, "private key is EC, certificate is RSA"
|
||||
default:
|
||||
return false, fmt.Sprintf("unsupported certificate public key type: %T", cert.PublicKey)
|
||||
}
|
||||
default:
|
||||
return false, fmt.Sprintf("unrecognised private key type: %T", priv.Public())
|
||||
}
|
||||
switch keyPub := priv.Public().(type) {
|
||||
case *rsa.PublicKey:
|
||||
switch certPub := cert.PublicKey.(type) {
|
||||
case *rsa.PublicKey:
|
||||
if matchRSA(certPub, keyPub) {
|
||||
return true, ""
|
||||
}
|
||||
return false, "public keys don't match"
|
||||
case *ecdsa.PublicKey:
|
||||
return false, "RSA private key, EC public key"
|
||||
default:
|
||||
return false, fmt.Sprintf("unsupported certificate public key type: %T", cert.PublicKey)
|
||||
}
|
||||
case *ecdsa.PublicKey:
|
||||
switch certPub := cert.PublicKey.(type) {
|
||||
case *ecdsa.PublicKey:
|
||||
if matchECDSA(certPub, keyPub) {
|
||||
return true, ""
|
||||
}
|
||||
// Determine a more precise reason
|
||||
kc := getECCurve(keyPub)
|
||||
cc := getECCurve(certPub)
|
||||
if kc == curveInvalid {
|
||||
return false, "invalid private key curve"
|
||||
}
|
||||
if cc == curveRSA {
|
||||
return false, "private key is EC, certificate is RSA"
|
||||
}
|
||||
if kc != cc {
|
||||
return false, "EC curves don't match"
|
||||
}
|
||||
return false, "public keys don't match"
|
||||
case *rsa.PublicKey:
|
||||
return false, "private key is EC, certificate is RSA"
|
||||
default:
|
||||
return false, fmt.Sprintf("unsupported certificate public key type: %T", cert.PublicKey)
|
||||
}
|
||||
default:
|
||||
return false, fmt.Sprintf("unrecognised private key type: %T", priv.Public())
|
||||
}
|
||||
}
|
||||
|
||||
func loadKey(path string) (crypto.Signer, error) {
|
||||
in, err := os.ReadFile(path)
|
||||
in, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -132,7 +132,7 @@ func loadKey(path string) (crypto.Signer, error) {
|
||||
in = p.Bytes
|
||||
}
|
||||
|
||||
priv, err := x509.ParsePKCS8PrivateKey(in)
|
||||
priv, err := x509.ParsePKCS8PrivateKey(in)
|
||||
if err != nil {
|
||||
priv, err = x509.ParsePKCS1PrivateKey(in)
|
||||
if err != nil {
|
||||
@@ -143,16 +143,15 @@ func loadKey(path string) (crypto.Signer, error) {
|
||||
}
|
||||
}
|
||||
|
||||
switch p := priv.(type) {
|
||||
case *rsa.PrivateKey:
|
||||
return p, nil
|
||||
case *ecdsa.PrivateKey:
|
||||
return p, nil
|
||||
default:
|
||||
// should never reach here
|
||||
return nil, errors.New("invalid private key")
|
||||
}
|
||||
|
||||
switch p := priv.(type) {
|
||||
case *rsa.PrivateKey:
|
||||
return p, nil
|
||||
case *ecdsa.PrivateKey:
|
||||
return p, nil
|
||||
default:
|
||||
// should never reach here
|
||||
return nil, errors.New("invalid private key")
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
@@ -161,7 +160,7 @@ func main() {
|
||||
flag.StringVar(&certFile, "c", "", "TLS `certificate` file")
|
||||
flag.Parse()
|
||||
|
||||
in, err := os.ReadFile(certFile)
|
||||
in, err := os.ReadFile(certFile)
|
||||
die.If(err)
|
||||
|
||||
p, _ := pem.Decode(in)
|
||||
@@ -177,11 +176,11 @@ func main() {
|
||||
priv, err := loadKey(keyFile)
|
||||
die.If(err)
|
||||
|
||||
matched, reason := matchKeys(cert, priv)
|
||||
if matched {
|
||||
fmt.Println("Match.")
|
||||
return
|
||||
}
|
||||
fmt.Printf("No match (%s).\n", reason)
|
||||
os.Exit(1)
|
||||
matched, reason := matchKeys(cert, priv)
|
||||
if matched {
|
||||
fmt.Println("Match.")
|
||||
return
|
||||
}
|
||||
fmt.Printf("No match (%s).\n", reason)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
@@ -201,10 +201,6 @@ func init() {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if fromLoc == time.UTC {
|
||||
|
||||
}
|
||||
|
||||
toLoc = time.UTC
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"gopkg.in/yaml.v2"
|
||||
@@ -12,7 +11,7 @@ import (
|
||||
|
||||
type empty struct{}
|
||||
|
||||
func errorf(format string, args ...interface{}) {
|
||||
func errorf(format string, args ...any) {
|
||||
format += "\n"
|
||||
fmt.Fprintf(os.Stderr, format, args...)
|
||||
}
|
||||
@@ -44,7 +43,7 @@ func main() {
|
||||
|
||||
if flag.NArg() == 1 && flag.Arg(0) == "-" {
|
||||
path := "stdin"
|
||||
in, err := ioutil.ReadAll(os.Stdin)
|
||||
in, err := io.ReadAll(os.Stdin)
|
||||
if err != nil {
|
||||
errorf("%s FAILED: %s", path, err)
|
||||
os.Exit(1)
|
||||
@@ -65,7 +64,7 @@ func main() {
|
||||
}
|
||||
|
||||
for _, path := range flag.Args() {
|
||||
in, err := ioutil.ReadFile(path)
|
||||
in, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
errorf("%s FAILED: %s", path, err)
|
||||
continue
|
||||
|
||||
@@ -18,7 +18,7 @@ import (
|
||||
|
||||
const defaultDirectory = ".git/objects"
|
||||
|
||||
func errorf(format string, a ...interface{}) {
|
||||
func errorf(format string, a ...any) {
|
||||
fmt.Fprintf(os.Stderr, format, a...)
|
||||
if format[len(format)-1] != '\n' {
|
||||
fmt.Fprintf(os.Stderr, "\n")
|
||||
@@ -93,7 +93,7 @@ func searchFile(path string, search *regexp.Regexp) error {
|
||||
}
|
||||
|
||||
func buildWalker(searchExpr *regexp.Regexp) filepath.WalkFunc {
|
||||
return func(path string, info os.FileInfo, err error) error {
|
||||
return func(path string, info os.FileInfo, _ error) error {
|
||||
if info.Mode().IsRegular() {
|
||||
return searchFile(path, searchExpr)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build freebsd darwin,386 netbsd
|
||||
//go:build bsd
|
||||
|
||||
package lib
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build unix linux openbsd darwin,amd64
|
||||
//go:build unix || linux || openbsd || (darwin && amd64)
|
||||
|
||||
package lib
|
||||
|
||||
@@ -18,7 +18,7 @@ type FileTime struct {
|
||||
|
||||
func timeSpecToTime(ts unix.Timespec) time.Time {
|
||||
// The casts to int64 are needed because on 386, these are int32s.
|
||||
return time.Unix(int64(ts.Sec), int64(ts.Nsec))
|
||||
return time.Unix(ts.Sec, ts.Nsec)
|
||||
}
|
||||
|
||||
// LoadFileTime returns a FileTime associated with the file.
|
||||
|
||||
42
lib/lib.go
42
lib/lib.go
@@ -10,6 +10,12 @@ import (
|
||||
|
||||
var progname = filepath.Base(os.Args[0])
|
||||
|
||||
const (
|
||||
daysInYear = 365
|
||||
digitWidth = 10
|
||||
hoursInQuarterDay = 6
|
||||
)
|
||||
|
||||
// ProgName returns what lib thinks the program name is, namely the
|
||||
// basename of argv0.
|
||||
//
|
||||
@@ -20,7 +26,7 @@ func ProgName() string {
|
||||
|
||||
// Warnx displays a formatted error message to standard error, à la
|
||||
// warnx(3).
|
||||
func Warnx(format string, a ...interface{}) (int, error) {
|
||||
func Warnx(format string, a ...any) (int, error) {
|
||||
format = fmt.Sprintf("[%s] %s", progname, format)
|
||||
format += "\n"
|
||||
return fmt.Fprintf(os.Stderr, format, a...)
|
||||
@@ -28,7 +34,7 @@ func Warnx(format string, a ...interface{}) (int, error) {
|
||||
|
||||
// Warn displays a formatted error message to standard output,
|
||||
// appending the error string, à la warn(3).
|
||||
func Warn(err error, format string, a ...interface{}) (int, error) {
|
||||
func Warn(err error, format string, a ...any) (int, error) {
|
||||
format = fmt.Sprintf("[%s] %s", progname, format)
|
||||
format += ": %v\n"
|
||||
a = append(a, err)
|
||||
@@ -37,7 +43,7 @@ func Warn(err error, format string, a ...interface{}) (int, error) {
|
||||
|
||||
// Errx displays a formatted error message to standard error and exits
|
||||
// with the status code from `exit`, à la errx(3).
|
||||
func Errx(exit int, format string, a ...interface{}) {
|
||||
func Errx(exit int, format string, a ...any) {
|
||||
format = fmt.Sprintf("[%s] %s", progname, format)
|
||||
format += "\n"
|
||||
fmt.Fprintf(os.Stderr, format, a...)
|
||||
@@ -47,7 +53,7 @@ func Errx(exit int, format string, a ...interface{}) {
|
||||
// Err displays a formatting error message to standard error,
|
||||
// appending the error string, and exits with the status code from
|
||||
// `exit`, à la err(3).
|
||||
func Err(exit int, err error, format string, a ...interface{}) {
|
||||
func Err(exit int, err error, format string, a ...any) {
|
||||
format = fmt.Sprintf("[%s] %s", progname, format)
|
||||
format += ": %v\n"
|
||||
a = append(a, err)
|
||||
@@ -62,31 +68,31 @@ func Itoa(i int, wid int) string {
|
||||
// Assemble decimal in reverse order.
|
||||
var b [20]byte
|
||||
bp := len(b) - 1
|
||||
for i >= 10 || wid > 1 {
|
||||
for i >= digitWidth || wid > 1 {
|
||||
wid--
|
||||
q := i / 10
|
||||
b[bp] = byte('0' + i - q*10)
|
||||
q := i / digitWidth
|
||||
b[bp] = byte('0' + i - q*digitWidth)
|
||||
bp--
|
||||
i = q
|
||||
}
|
||||
// i < 10
|
||||
|
||||
b[bp] = byte('0' + i)
|
||||
return string(b[bp:])
|
||||
}
|
||||
|
||||
var (
|
||||
dayDuration = 24 * time.Hour
|
||||
yearDuration = (365 * dayDuration) + (6 * time.Hour)
|
||||
yearDuration = (daysInYear * dayDuration) + (hoursInQuarterDay * time.Hour)
|
||||
)
|
||||
|
||||
// Duration returns a prettier string for time.Durations.
|
||||
func Duration(d time.Duration) string {
|
||||
var s string
|
||||
if d >= yearDuration {
|
||||
years := int64(d / yearDuration)
|
||||
s += fmt.Sprintf("%dy", years)
|
||||
d -= time.Duration(years) * yearDuration
|
||||
}
|
||||
var s string
|
||||
if d >= yearDuration {
|
||||
years := int64(d / yearDuration)
|
||||
s += fmt.Sprintf("%dy", years)
|
||||
d -= time.Duration(years) * yearDuration
|
||||
}
|
||||
|
||||
if d >= dayDuration {
|
||||
days := d / dayDuration
|
||||
@@ -97,9 +103,9 @@ func Duration(d time.Duration) string {
|
||||
return s
|
||||
}
|
||||
|
||||
d %= 1 * time.Second
|
||||
hours := int64(d / time.Hour)
|
||||
d -= time.Duration(hours) * time.Hour
|
||||
d %= 1 * time.Second
|
||||
hours := int64(d / time.Hour)
|
||||
d -= time.Duration(hours) * time.Hour
|
||||
s += fmt.Sprintf("%dh%s", hours, d)
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package logging
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
// File writes its logs to file.
|
||||
@@ -60,12 +60,12 @@ func NewSplitFile(outpath, errpath string, overwrite bool) (*File, error) {
|
||||
fl.fe, err = os.OpenFile(errpath, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0600)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if closeErr := fl.Close(); closeErr != nil {
|
||||
return nil, fmt.Errorf("failed to open error log: %w", errors.Join(closeErr, err))
|
||||
}
|
||||
return nil, fmt.Errorf("failed to open error log: %w", err)
|
||||
}
|
||||
if err != nil {
|
||||
if closeErr := fl.Close(); closeErr != nil {
|
||||
return nil, fmt.Errorf("failed to open error log: %w", errors.Join(closeErr, err))
|
||||
}
|
||||
return nil, fmt.Errorf("failed to open error log: %w", err)
|
||||
}
|
||||
|
||||
fl.LogWriter = NewLogWriter(fl.fo, fl.fe)
|
||||
return fl, nil
|
||||
@@ -95,13 +95,13 @@ func (fl *File) Flush() error {
|
||||
}
|
||||
|
||||
func (fl *File) Chmod(mode os.FileMode) error {
|
||||
if err := fl.fo.Chmod(mode); err != nil {
|
||||
return fmt.Errorf("failed to chmod output log: %w", err)
|
||||
}
|
||||
if err := fl.fo.Chmod(mode); err != nil {
|
||||
return fmt.Errorf("failed to chmod output log: %w", err)
|
||||
}
|
||||
|
||||
if err := fl.fe.Chmod(mode); err != nil {
|
||||
return fmt.Errorf("failed to chmod error log: %w", err)
|
||||
}
|
||||
if err := fl.fe.Chmod(mode); err != nil {
|
||||
return fmt.Errorf("failed to chmod error log: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user