diff --git a/go.mod b/go.mod index 2615fea..ff30578 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.15 require ( github.com/sirupsen/logrus v1.7.0 go.bug.st/serial v1.1.1 + golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad golang.org/x/sys v0.0.0-20200909081042-eff7692f9009 gopkg.in/yaml.v2 v2.4.0 ) diff --git a/go.sum b/go.sum index 1d58cb2..48a8658 100644 --- a/go.sum +++ b/go.sum @@ -15,9 +15,16 @@ github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJy github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= go.bug.st/serial v1.1.1 h1:5J1DpaIaSIruBi7jVnKXnhRS+YQ9+2PLJMtIZKoIgnc= go.bug.st/serial v1.1.1/go.mod h1:VmYBeyJWp5BnJ0tw2NUJHZdJTGl2ecBGABHlzRK1knY= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY= +golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200909081042-eff7692f9009 h1:W0lCpv29Hv0UaM1LXb9QlBHLNP8UFfcKjblhVCWftOM= golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= diff --git a/signer/command_processor.go b/signer/command_processor.go index 0d6aae5..a479e31 100644 --- a/signer/command_processor.go +++ b/signer/command_processor.go @@ -2,6 +2,7 @@ package signer import ( "bytes" + "crypto" "crypto/x509" "encoding/hex" "encoding/pem" @@ -18,6 +19,7 @@ import ( "git.cacert.org/cacert-gosigner/datastructures" "git.cacert.org/cacert-gosigner/shared" + "git.cacert.org/cacert-gosigner/signer/openpgp_ops" "git.cacert.org/cacert-gosigner/signer/x509_ops" ) @@ -248,7 +250,7 @@ func (p *CommandProcessor) revokeX509(system *IdSystemParameters, request []byte if err != nil { log.Warnf("could not generate xdelta: %v", err) } - log.Tracef("xdelte produced %d bytes", len(content)) + log.Tracef("xdelta produced %d bytes", len(content)) } if content == nil { content = pem.EncodeToMemory(&pem.Block{ @@ -309,7 +311,17 @@ func (p *CommandProcessor) signX509Certificate(system *IdSystemParameters, days } func (p *CommandProcessor) signOpenpgpKey(system *IdSystemParameters, days uint16, pubKey []byte) ([]byte, error) { - return nil, errors.New("signOpenpgpKey is not implemented yet") + openPgpRoot := system.Root.(*openpgp_ops.OpenPGPRoot) + signatureAlgorithm := system.MessageDigestAlgorithm.(*crypto.Hash) + + log.Debugf("sign openpgp for root %s", openPgpRoot) + + content, err := openPgpRoot.SignPublicKey(pubKey, signatureAlgorithm, days) + if err != nil { + return nil, fmt.Errorf("could not sign openpgp public key with root %s: %v", openPgpRoot, err) + } + + return content, nil } func NewCommandProcessorSettings() *CommandProcessorSettings { diff --git a/signer/openpgp_ops/operations.go b/signer/openpgp_ops/operations.go index 2a51ee3..cd6352e 100644 --- a/signer/openpgp_ops/operations.go +++ b/signer/openpgp_ops/operations.go @@ -1,11 +1,82 @@ package openpgp_ops +import ( + "bytes" + "crypto" + "fmt" + "os" + "time" + + log "github.com/sirupsen/logrus" + "golang.org/x/crypto/openpgp" + "golang.org/x/crypto/openpgp/packet" +) + type OpenPGPRoot struct { Name string SecretKeyRing string Identifier string } +func (r *OpenPGPRoot) SignPublicKey(pubKey []byte, algorithm *crypto.Hash, days uint16) ([]byte, error) { + signingKey, err := r.findSigningKey(r.Identifier) + if err != nil { + return nil, fmt.Errorf("could not find a signing key matching %s: %v", r.Identifier, err) + } + + pubKeyRing, err := openpgp.ReadKeyRing(bytes.NewReader(pubKey)) + if err != nil { + return nil, fmt.Errorf("could not read openpgp keyring: %v", err) + } + + signatures := make([]*openpgp.Entity, 0) + + for _, pe := range pubKeyRing { + log.Tracef("found %+v", pe) + for _, i := range pe.Identities { + if !i.SelfSignature.KeyExpired(time.Now()) { + signConfig := &packet.Config{ + DefaultHash: *algorithm, + Time: func() { return calculateExpiry(i, days) }, + } + pe.SignIdentity(i.Name, signingKey, signConfig) + } + } + } +} + +func calculateExpiry(i *openpgp.Identity, days uint16) time.Time { + maxExpiry := i.SelfSignature.CreationTime.Add(time.Second * time.Duration(*i.SelfSignature.KeyLifetimeSecs)) + calcExpiry := time.Now().Add(time.Hour * 24 * time.Duration(days)) + + if calcExpiry.After(maxExpiry) { + return maxExpiry + } + return maxExpiry +} + +func (r *OpenPGPRoot) findSigningKey(identifier string) (*openpgp.Entity, error) { + keyring, err := os.Open(r.SecretKeyRing) + if err != nil { + return nil, fmt.Errorf("could not open secret keyring: %v", err) + } + defer func() { _ = keyring.Close() }() + + el, err := openpgp.ReadKeyRing(keyring) + if err != nil { + return nil, fmt.Errorf("could not read keyring: %v", err) + } + for _, e := range el { + log.Tracef("found %+v", e) + for _, i := range e.Identities { + if i.UserId.Email == identifier && len(e.Revocations) == 0 && !i.SelfSignature.KeyExpired(time.Now()) { + return e, nil + } + } + } + return nil, fmt.Errorf("no matching key found") +} + type OpenPGPProfile struct { Name string }