|
|
@ -16,9 +16,12 @@ limitations under the License.
|
|
|
|
package plugin
|
|
|
|
package plugin
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
import (
|
|
|
|
|
|
|
|
"crypto/sha256"
|
|
|
|
"fmt"
|
|
|
|
"fmt"
|
|
|
|
|
|
|
|
"io"
|
|
|
|
"os"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
"path/filepath"
|
|
|
|
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
|
|
|
|
"golang.org/x/crypto/openpgp/clearsign" //nolint
|
|
|
|
"golang.org/x/crypto/openpgp/clearsign" //nolint
|
|
|
|
|
|
|
|
|
|
|
@ -31,8 +34,8 @@ type SigningInfo struct {
|
|
|
|
// - "local dev": Plugin is a symlink (development mode)
|
|
|
|
// - "local dev": Plugin is a symlink (development mode)
|
|
|
|
// - "unsigned": No provenance file found
|
|
|
|
// - "unsigned": No provenance file found
|
|
|
|
// - "invalid provenance": Provenance file is malformed
|
|
|
|
// - "invalid provenance": Provenance file is malformed
|
|
|
|
// - "mismatched provenance": Provenance file does not exactly match plugin
|
|
|
|
// - "mismatched provenance": Provenance file does not match the installed tarball
|
|
|
|
// - "signed": Valid signature exists for this exact plugin
|
|
|
|
// - "signed": Valid signature exists for the installed tarball
|
|
|
|
Status string
|
|
|
|
Status string
|
|
|
|
IsSigned bool // True if plugin has a valid signature (even if not verified against keyring)
|
|
|
|
IsSigned bool // True if plugin has a valid signature (even if not verified against keyring)
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -88,9 +91,9 @@ func GetPluginSigningInfo(metadata Metadata) (*SigningInfo, error) {
|
|
|
|
}, nil
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Check if provenance is well-formed
|
|
|
|
// Check if provenance matches the actual tarball
|
|
|
|
blockContent := string(block.Plaintext)
|
|
|
|
blockContent := string(block.Plaintext)
|
|
|
|
if !validateProvenanceHash(blockContent) {
|
|
|
|
if !validateProvenanceHash(blockContent, tarballPath) {
|
|
|
|
return &SigningInfo{
|
|
|
|
return &SigningInfo{
|
|
|
|
Status: "mismatched provenance",
|
|
|
|
Status: "mismatched provenance",
|
|
|
|
IsSigned: false,
|
|
|
|
IsSigned: false,
|
|
|
@ -108,15 +111,48 @@ func GetPluginSigningInfo(metadata Metadata) (*SigningInfo, error) {
|
|
|
|
}, nil
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func validateProvenanceHash(blockContent string) bool {
|
|
|
|
func validateProvenanceHash(blockContent string, tarballPath string) bool {
|
|
|
|
// Parse provenance to check if it's well-formed
|
|
|
|
// Parse provenance to get the expected hash
|
|
|
|
// (No need to compare hashes since we can't recreate the original tarball)
|
|
|
|
|
|
|
|
_, sums, err := parsePluginMessageBlock([]byte(blockContent))
|
|
|
|
_, sums, err := parsePluginMessageBlock([]byte(blockContent))
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
return false
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Valid if provenance contains file checksums
|
|
|
|
|
|
|
|
return len(sums.Files) > 0
|
|
|
|
// Must have file checksums
|
|
|
|
|
|
|
|
if len(sums.Files) == 0 {
|
|
|
|
|
|
|
|
return false
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Calculate actual hash of the tarball
|
|
|
|
|
|
|
|
actualHash, err := calculateFileHash(tarballPath)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
return false
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Check if the actual hash matches the expected hash in the provenance
|
|
|
|
|
|
|
|
for filename, expectedHash := range sums.Files {
|
|
|
|
|
|
|
|
if strings.Contains(filename, filepath.Base(tarballPath)) && expectedHash == actualHash {
|
|
|
|
|
|
|
|
return true
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return false
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// calculateFileHash calculates the SHA256 hash of a file
|
|
|
|
|
|
|
|
func calculateFileHash(filePath string) (string, error) {
|
|
|
|
|
|
|
|
file, err := os.Open(filePath)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
return "", err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
defer file.Close()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
hasher := sha256.New()
|
|
|
|
|
|
|
|
if _, err := io.Copy(hasher, file); err != nil {
|
|
|
|
|
|
|
|
return "", err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return fmt.Sprintf("sha256:%x", hasher.Sum(nil)), nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// GetSigningInfoForPlugins returns signing info for multiple plugins
|
|
|
|
// GetSigningInfoForPlugins returns signing info for multiple plugins
|
|
|
|