Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

signature: add OpenPGP signing mechanism based on Sequoia #2569

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ require (
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 // indirect
github.com/tchap/go-patricia/v2 v2.3.1 // indirect
github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 // indirect
github.com/ueno/podman-sequoia/go v0.0.0-20240917014836-c53d571fb375 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
go.mongodb.org/mongo-driver v1.14.0 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,8 @@ github.com/tchap/go-patricia/v2 v2.3.1 h1:6rQp39lgIYZ+MHmdEq4xzuk1t7OdC35z/xm0BG
github.com/tchap/go-patricia/v2 v2.3.1/go.mod h1:VZRHKAb53DLaG+nA9EaYYiaEx6YztwDlLElMsnSHD4k=
github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 h1:e/5i7d4oYZ+C1wj2THlRK+oAhjeS/TRQwMfkIuet3w0=
github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399/go.mod h1:LdwHTNJT99C5fTAzDz0ud328OgXz+gierycbcIx2fRs=
github.com/ueno/podman-sequoia/go v0.0.0-20240917014836-c53d571fb375 h1:k6F/0KdIf86gLWN2WWJkA9Dz+yD24y7k+QZlLKUOHEU=
github.com/ueno/podman-sequoia/go v0.0.0-20240917014836-c53d571fb375/go.mod h1:rL7oFaeDd+3z0KyQI+GTz3ZE0ZvO4mT2+zMAXxZiPvM=
github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc=
github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/vbatts/tar-split v0.11.5 h1:3bHCTIheBm1qFTcgh9oPu+nNBtX+XJIupG/vacinCts=
Expand Down
4 changes: 2 additions & 2 deletions signature/mechanism_gpgme.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//go:build !containers_image_openpgp
// +build !containers_image_openpgp
//go:build !containers_image_openpgp && !containers_image_sequoia
// +build !containers_image_openpgp,!containers_image_sequoia
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we default to sequoia or not?


Eventually the build tag, whichever tag it ends up being, should be documented in README.md.


package signature

Expand Down
4 changes: 2 additions & 2 deletions signature/mechanism_gpgme_test.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//go:build !containers_image_openpgp
// +build !containers_image_openpgp
//go:build !containers_image_openpgp && !containers_image_sequoia
// +build !containers_image_openpgp,!containers_image_sequoia

package signature

Expand Down
131 changes: 131 additions & 0 deletions signature/mechanism_sequoia.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
//go:build containers_image_sequoia
// +build containers_image_sequoia

package signature

import (
"github.com/containers/storage/pkg/homedir"
"github.com/ueno/podman-sequoia/go/sequoia"
"os"
"path"
)

// A GPG/OpenPGP signing mechanism, implemented using Sequoia.
type sequoiaSigningMechanism struct {
inner *sequoia.SigningMechanism
}

// newGPGSigningMechanismInDirectory returns a new GPG/OpenPGP signing mechanism, using optionalDir if not empty.
// The caller must call .Close() on the returned SigningMechanism.
func newGPGSigningMechanismInDirectory(optionalDir string) (signingMechanismWithPassphrase, error) {
// For compatibility reasons, we allow both sequoiaHome and
// gpgHome to be the same directory as designated by
// optionalDir or GNUPGHOME.
envHome := os.Getenv("GNUPGHOME")

gpgHome := optionalDir
if gpgHome == "" {
gpgHome = envHome
if gpgHome == "" {
gpgHome = path.Join(homedir.Get(), ".gnupg")
}
}

sequoiaHome := optionalDir
if sequoiaHome == "" {
sequoiaHome = envHome
if sequoiaHome == "" {
dataHome, err := homedir.GetDataHome()
if err != nil {
return nil, err
}
sequoiaHome = path.Join(dataHome, "sequoia")
}
}

mech, err := sequoia.NewMechanismFromDirectory(sequoiaHome)
if err != nil {
return nil, err
}

// Migrate GnuPG keyrings if exist.
for _, keyring := range []string{"pubring.gpg", "secring.gpg"} {
blob, err := os.ReadFile(path.Join(gpgHome, keyring))
if err != nil {
if !os.IsNotExist(err) {
return nil, err
}
} else if _, err := mech.ImportKeys(blob); err != nil {
return nil, err
}
}

return &sequoiaSigningMechanism{
inner: mech,
}, nil
}

// newEphemeralGPGSigningMechanism returns a new GPG/OpenPGP signing mechanism which
// recognizes _only_ public keys from the supplied blobs, and returns the identities
// of these keys.
// The caller must call .Close() on the returned SigningMechanism.
func newEphemeralGPGSigningMechanism(blobs [][]byte) (signingMechanismWithPassphrase, []string, error) {
mech, err := sequoia.NewEphemeralMechanism()
if err != nil {
return nil, nil, err
}
keyIdentities := []string{}
for _, blob := range blobs {
ki, err := mech.ImportKeys(blob)
if err != nil {
return nil, nil, err
}
keyIdentities = append(keyIdentities, ki...)
}

return &sequoiaSigningMechanism{
inner: mech,
}, keyIdentities, nil
}

func (m *sequoiaSigningMechanism) Close() error {
return m.inner.Close()
}

// SupportsSigning returns nil if the mechanism supports signing, or a SigningNotSupportedError.
func (m *sequoiaSigningMechanism) SupportsSigning() error {
return m.inner.SupportsSigning()
}

// Sign creates a (non-detached) signature of input using keyIdentity and passphrase.
// Fails with a SigningNotSupportedError if the mechanism does not support signing.
func (m *sequoiaSigningMechanism) SignWithPassphrase(input []byte, keyIdentity string, passphrase string) ([]byte, error) {
return m.inner.SignWithPassphrase(input, keyIdentity, passphrase)
}

// Sign creates a (non-detached) signature of input using keyIdentity.
// Fails with a SigningNotSupportedError if the mechanism does not support signing.
func (m *sequoiaSigningMechanism) Sign(input []byte, keyIdentity string) ([]byte, error) {
return m.inner.Sign(input, keyIdentity)
}

// Verify parses unverifiedSignature and returns the content and the signer's identity
func (m *sequoiaSigningMechanism) Verify(unverifiedSignature []byte) (contents []byte, keyIdentity string, err error) {
return m.inner.Verify(unverifiedSignature)
}

// UntrustedSignatureContents returns UNTRUSTED contents of the signature WITHOUT ANY VERIFICATION,
// along with a short identifier of the key used for signing.
// WARNING: The short key identifier (which corresponds to "Key ID" for OpenPGP keys)
// is NOT the same as a "key identity" used in other calls to this interface, and
// the values may have no recognizable relationship if the public key is not available.
func (m *sequoiaSigningMechanism) UntrustedSignatureContents(untrustedSignature []byte) (untrustedContents []byte, shortKeyIdentifier string, err error) {
return gpgUntrustedSignatureContents(untrustedSignature)
}

func init() {
err := sequoia.Init()
if err != nil {
panic("sequoia cannot be loaded: " + err.Error())
}
}
2 changes: 1 addition & 1 deletion signature/mechanism_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package signature

// These tests are expected to pass unmodified for _both_ mechanism_gpgme.go and mechanism_openpgp.go.
// These tests are expected to pass unmodified for _all_ mechanism_sequoia.go, mechanism_gpgme.go, and mechanism_openpgp.go.

import (
"bytes"
Expand Down