diff --git a/certlib/certgen/config.go b/certlib/certgen/config.go index 96296ce..6c54a21 100644 --- a/certlib/certgen/config.go +++ b/certlib/certgen/config.go @@ -19,13 +19,21 @@ type KeySpec struct { Size int `yaml:"size"` } +func (ks KeySpec) String() string { + if strings.ToLower(ks.Algorithm) == nameEd25519 { + return nameEd25519 + } + + return fmt.Sprintf("%s-%d", ks.Algorithm, ks.Size) +} + func (ks KeySpec) Generate() (crypto.PublicKey, crypto.PrivateKey, error) { switch strings.ToLower(ks.Algorithm) { case "rsa": return GenerateKey(x509.RSA, ks.Size) case "ecdsa": return GenerateKey(x509.ECDSA, ks.Size) - case "ed25519": + case nameEd25519: return GenerateKey(x509.Ed25519, 0) default: return nil, nil, fmt.Errorf("unknown key algorithm: %s", ks.Algorithm) @@ -38,7 +46,7 @@ func (ks KeySpec) SigningAlgorithm() (x509.SignatureAlgorithm, error) { return x509.SHA512WithRSAPSS, nil case "ecdsa": return x509.ECDSAWithSHA512, nil - case "ed25519": + case nameEd25519: return x509.PureEd25519, nil default: return 0, fmt.Errorf("unknown key algorithm: %s", ks.Algorithm) @@ -88,6 +96,10 @@ func (cs CertificateRequest) Request(priv crypto.PrivateKey) (*x509.CertificateR IPAddresses: ipAddresses, } + if cs.Subject.Email != "" { + req.EmailAddresses = []string{cs.Subject.Email} + } + reqBytes, err := x509.CreateCertificateRequest(rand.Reader, req, priv) if err != nil { return nil, fmt.Errorf("failed to create certificate request: %w", err) diff --git a/certlib/certgen/keygen.go b/certlib/certgen/keygen.go index c3bff54..200b4a1 100644 --- a/certlib/certgen/keygen.go +++ b/certlib/certgen/keygen.go @@ -16,6 +16,10 @@ import ( // oidEd25519 = asn1.ObjectIdentifier{1, 3, 101, 110} //) +const ( + nameEd25519 = "ed25519" +) + func GenerateKey(algorithm x509.PublicKeyAlgorithm, bitSize int) (crypto.PublicKey, crypto.PrivateKey, error) { var key crypto.PrivateKey var pub crypto.PublicKey diff --git a/certlib/ski/ski.go b/certlib/ski/ski.go index ce5809c..9081561 100644 --- a/certlib/ski/ski.go +++ b/certlib/ski/ski.go @@ -6,6 +6,7 @@ import ( "crypto/ed25519" "crypto/rsa" "crypto/sha1" // #nosec G505 this is the standard + "crypto/tls" "crypto/x509" "crypto/x509/pkix" "encoding/asn1" @@ -15,7 +16,9 @@ import ( "git.wntrmute.dev/kyle/goutils/certlib" "git.wntrmute.dev/kyle/goutils/die" + "git.wntrmute.dev/kyle/goutils/fileutil" "git.wntrmute.dev/kyle/goutils/lib" + "git.wntrmute.dev/kyle/goutils/lib/fetch" ) const ( @@ -53,6 +56,30 @@ func (k *KeyInfo) SKI(displayMode lib.HexEncodeMode) (string, error) { return pubHashString, nil } +func Lookup(path string, tcfg *tls.Config) (*KeyInfo, error) { + if fileutil.FileDoesExist(path) { + return ParsePEM(path) + } + + server, err := fetch.ParseServer(path, tcfg) + if err != nil { + return nil, err + } + + cert, err := server.Get() + if err != nil { + return nil, err + } + + material := &KeyInfo{ + FileType: "certificate", + } + + material.PublicKey, material.KeyType = parseCertificate(cert) + + return material, nil +} + // ParsePEM parses a PEM file and returns the public key and its type. func ParsePEM(path string) (*KeyInfo, error) { material := &KeyInfo{} @@ -79,7 +106,7 @@ func ParsePEM(path string) (*KeyInfo, error) { material.PublicKey, material.KeyType = parseKey(data) material.FileType = "private key" case "CERTIFICATE": - material.PublicKey, material.KeyType = parseCertificate(data) + material.PublicKey, material.KeyType = parseCertificateFile(data) material.FileType = "certificate" case "CERTIFICATE REQUEST": material.PublicKey, material.KeyType = parseCSR(data) @@ -113,12 +140,17 @@ func parseKey(data []byte) ([]byte, string) { return public, kt } -func parseCertificate(data []byte) ([]byte, string) { +func parseCertificateFile(data []byte) ([]byte, string) { cert, err := x509.ParseCertificate(data) die.If(err) + return parseCertificate(cert) +} + +func parseCertificate(cert *x509.Certificate) ([]byte, string) { pub := cert.PublicKey var kt string + switch pub.(type) { case *rsa.PublicKey: kt = keyTypeRSA diff --git a/cmd/bcuz/main.go b/cmd/bcuz/main.go index 3dccf25..e5a72ff 100644 --- a/cmd/bcuz/main.go +++ b/cmd/bcuz/main.go @@ -2,6 +2,7 @@ package main import ( "archive/zip" + "errors" "flag" "fmt" "io" @@ -10,6 +11,8 @@ import ( "strings" ) +var unrestrictedDecompression bool + var keepArchive bool func removedir(dir string, existed bool) { @@ -62,14 +65,21 @@ func unpackFile(path string) error { return err } - out, err := os.Create(filepath.Join(dir, f.FileHeader.Name)) + if f.UncompressedSize64 > (f.CompressedSize64*32) && !unrestrictedDecompression { + rc.Close() + removedir(dir, existed) + return errors.New("file is too large to decompress (maybe a zip bomb)") + } + + var out *os.File + out, err = os.Create(filepath.Join(dir, f.FileHeader.Name)) if err != nil { rc.Close() removedir(dir, existed) return err } - _, err = io.Copy(out, rc) + _, err = io.Copy(out, rc) // #nosec G110: handled with size check above if err != nil { rc.Close() removedir(dir, existed) @@ -88,6 +98,7 @@ func unpackFile(path string) error { func main() { flag.BoolVar(&keepArchive, "k", false, "don't remove the archive file after unpacking") + flag.BoolVar(&unrestrictedDecompression, "u", false, "allow unrestricted decompression") flag.Parse() for _, path := range flag.Args() { diff --git a/cmd/kgz/main.go b/cmd/kgz/main.go index d526f91..a4e5160 100644 --- a/cmd/kgz/main.go +++ b/cmd/kgz/main.go @@ -111,7 +111,7 @@ func buildExtraForPath(st unix.Stat_t, path string, setUID, setGID int) []byte { ctns = clampToInt32(ft.Changed.Nanosecond()) } - return buildKGExtra(uid, gid, mode, cts, ctns) + return buildKGExtra(uid, gid, uint32(mode), cts, ctns) } // parseKGExtra scans a gzip Extra blob and returns kgz metadata if present.