server/signedcontainer/payload.go (90 lines of code) (raw):

package signedcontainer import ( "encoding/base64" "encoding/json" "encoding/pem" "errors" "fmt" ) const ( // criticalType is the value of `critical.type` in a simple signing format payload specified in // https://github.com/sigstore/cosign/blob/main/specs/SIGNATURE_SPEC.md#simple-signing criticalType = "cosign container image signature" // publicKey is the key of the public key for signature verification attached to the cosign-generated payload. publicKey = "dev.cosignproject.cosign/pub" // sigAlgURL is the key of the signing algorithm attached to the cosign-generated payload. sigAlgURL = "dev.cosignproject.cosign/sigalg" ) type signingAlgorithm int const ( // Unspecified signing algorithm. unspecified = 0 // RSASSA-PSS with a SHA256 digest. rsassaPssSha256 = 1 // RSASSA-PKCS1 v1.5 with a SHA256 digest. rsasaaPkcs1v15Sha256 = 2 // ECDSA on the P-256 Curve with a SHA256 digest. ecdsaP256Sha256 = 3 ) func (s signingAlgorithm) string() string { switch s { case unspecified: return "SIGNING_ALGORITHM_UNSPECIFIED" case rsassaPssSha256: return "RSASSA_PSS_SHA256" case rsasaaPkcs1v15Sha256: return "RSASSA_PKCS1V15_SHA256" case ecdsaP256Sha256: return "ECDSA_P256_SHA256" } return "SIGNING_ALGORITHM_UNSPECIFIED" } var unpaddedEncoding = base64.RawStdEncoding // payload follows the simple signing format specified in // https://github.com/sigstore/cosign/blob/main/specs/SIGNATURE_SPEC.md#simple-signing type payload struct { Critical critical `json:"critical"` Optional map[string]any `json:"optional"` // Optional represents optional metadata about the image, and its value shouldn't contain any "=" signs. } // critical contains data critical to correctly evaluating the validity of a signature. type critical struct { Identity identity `json:"identity"` Image image `json:"image"` Type string `json:"type"` } // identity identifies the claimed identity of the image. type identity struct { // This field is ignored for cosign semantics as it does not contain either a tag or digest for the image. DockerReference string `json:"docker-reference"` } // image identifies the container image this signature applies to. type image struct { DockerManifestDigest string `json:"docker-manifest-digest"` } // publicKey retrieves the PEM-encoded public key from the `optional` field of the payload. func (p *payload) publicKey() ([]byte, error) { publicKey, ok := p.Optional[publicKey].(string) if !ok { return nil, fmt.Errorf("public key not found in the Optional field of payload: %v", p) } // Decode the unpadded base64 encoding public key. publicKeyBytes, err := unpaddedEncoding.DecodeString(publicKey) if err != nil { return nil, fmt.Errorf("failed to decode public key string as base64 [%v]: %v", publicKey, err) } // Check if the retrieved public key is PEM formatted. if block, _ := pem.Decode(publicKeyBytes); block == nil { return nil, errors.New("could not decode public key bytes as PEM") } return publicKeyBytes, nil } var signingAlgorithmValue = map[string]signingAlgorithm{ "SIGNING_ALGORITHM_UNSPECIFIED": unspecified, "RSASSA_PSS_SHA256": rsassaPssSha256, "RSASSA_PKCS1V15_SHA256": rsasaaPkcs1v15Sha256, "ECDSA_P256_SHA256": ecdsaP256Sha256, } // sigAlg retrieves the signing algorithm from the `optional` field of the payload. func (p *payload) sigAlg() (signingAlgorithm, error) { alg, ok := p.Optional[sigAlgURL].(string) if !ok { return unspecified, fmt.Errorf("signing algorithm not found in the Optional field of payload: %v", p) } algVal, ok := signingAlgorithmValue[alg] if !ok || algVal == unspecified { return unspecified, fmt.Errorf("unsupported signing algorithm: %s", alg) } return algVal, nil } // UnmarshalAndValidate unmarshals a payload from JSON and performs checks on the payload. func unmarshalAndValidate(data []byte) (*payload, error) { var pl payload if err := json.Unmarshal(data, &pl); err != nil { return nil, err } if pl.Critical.Type != criticalType { return nil, fmt.Errorf("unknown critical type for Cosign signature payload: %s", pl.Critical.Type) } return &pl, nil }