vendor: update buildkit to master@31c870e82a48

Signed-off-by: Justin Chadwell <me@jedevc.com>
This commit is contained in:
Justin Chadwell
2023-05-15 18:32:31 +01:00
parent 167cd16acb
commit e61a8cf637
269 changed files with 25798 additions and 3371 deletions

View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2021 NYU Secure Systems Lab
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,145 @@
package cjson
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"reflect"
"regexp"
"sort"
)
/*
encodeCanonicalString is a helper function to canonicalize the passed string
according to the OLPC canonical JSON specification for strings (see
http://wiki.laptop.org/go/Canonical_JSON). String canonicalization consists of
escaping backslashes ("\") and double quotes (") and wrapping the resulting
string in double quotes (").
*/
func encodeCanonicalString(s string) string {
re := regexp.MustCompile(`([\"\\])`)
return fmt.Sprintf("\"%s\"", re.ReplaceAllString(s, "\\$1"))
}
/*
encodeCanonical is a helper function to recursively canonicalize the passed
object according to the OLPC canonical JSON specification (see
http://wiki.laptop.org/go/Canonical_JSON) and write it to the passed
*bytes.Buffer. If canonicalization fails it returns an error.
*/
func encodeCanonical(obj interface{}, result *bytes.Buffer) (err error) {
// Since this function is called recursively, we use panic if an error occurs
// and recover in a deferred function, which is always called before
// returning. There we set the error that is returned eventually.
defer func() {
if r := recover(); r != nil {
err = errors.New(r.(string))
}
}()
switch objAsserted := obj.(type) {
case string:
result.WriteString(encodeCanonicalString(objAsserted))
case bool:
if objAsserted {
result.WriteString("true")
} else {
result.WriteString("false")
}
// The wrapping `EncodeCanonical` function decodes the passed json data with
// `decoder.UseNumber` so that any numeric value is stored as `json.Number`
// (instead of the default `float64`). This allows us to assert that it is a
// non-floating point number, which are the only numbers allowed by the used
// canonicalization specification.
case json.Number:
if _, err := objAsserted.Int64(); err != nil {
panic(fmt.Sprintf("Can't canonicalize floating point number '%s'",
objAsserted))
}
result.WriteString(objAsserted.String())
case nil:
result.WriteString("null")
// Canonicalize slice
case []interface{}:
result.WriteString("[")
for i, val := range objAsserted {
if err := encodeCanonical(val, result); err != nil {
return err
}
if i < (len(objAsserted) - 1) {
result.WriteString(",")
}
}
result.WriteString("]")
case map[string]interface{}:
result.WriteString("{")
// Make a list of keys
var mapKeys []string
for key := range objAsserted {
mapKeys = append(mapKeys, key)
}
// Sort keys
sort.Strings(mapKeys)
// Canonicalize map
for i, key := range mapKeys {
// Note: `key` must be a `string` (see `case map[string]interface{}`) and
// canonicalization of strings cannot err out (see `case string`), thus
// no error handling is needed here.
encodeCanonical(key, result)
result.WriteString(":")
if err := encodeCanonical(objAsserted[key], result); err != nil {
return err
}
if i < (len(mapKeys) - 1) {
result.WriteString(",")
}
i++
}
result.WriteString("}")
default:
// We recover in a deferred function defined above
panic(fmt.Sprintf("Can't canonicalize '%s' of type '%s'",
objAsserted, reflect.TypeOf(objAsserted)))
}
return nil
}
/*
EncodeCanonical JSON canonicalizes the passed object and returns it as a byte
slice. It uses the OLPC canonical JSON specification (see
http://wiki.laptop.org/go/Canonical_JSON). If canonicalization fails the byte
slice is nil and the second return value contains the error.
*/
func EncodeCanonical(obj interface{}) ([]byte, error) {
// FIXME: Terrible hack to turn the passed struct into a map, converting
// the struct's variable names to the json key names defined in the struct
data, err := json.Marshal(obj)
if err != nil {
return nil, err
}
var jsonMap interface{}
dec := json.NewDecoder(bytes.NewReader(data))
dec.UseNumber()
if err := dec.Decode(&jsonMap); err != nil {
return nil, err
}
// Create a buffer and write the canonicalized JSON bytes to it
var result bytes.Buffer
if err := encodeCanonical(jsonMap, &result); err != nil {
return nil, err
}
return result.Bytes(), nil
}

View File

@ -0,0 +1,197 @@
/*
Package dsse implements the Dead Simple Signing Envelope (DSSE)
https://github.com/secure-systems-lab/dsse
*/
package dsse
import (
"encoding/base64"
"errors"
"fmt"
)
// ErrUnknownKey indicates that the implementation does not recognize the
// key.
var ErrUnknownKey = errors.New("unknown key")
// ErrNoSignature indicates that an envelope did not contain any signatures.
var ErrNoSignature = errors.New("no signature found")
// ErrNoSigners indicates that no signer was provided.
var ErrNoSigners = errors.New("no signers provided")
/*
Envelope captures an envelope as described by the Secure Systems Lab
Signing Specification. See here:
https://github.com/secure-systems-lab/signing-spec/blob/master/envelope.md
*/
type Envelope struct {
PayloadType string `json:"payloadType"`
Payload string `json:"payload"`
Signatures []Signature `json:"signatures"`
}
/*
DecodeB64Payload returns the serialized body, decoded
from the envelope's payload field. A flexible
decoder is used, first trying standard base64, then
URL-encoded base64.
*/
func (e *Envelope) DecodeB64Payload() ([]byte, error) {
return b64Decode(e.Payload)
}
/*
Signature represents a generic in-toto signature that contains the identifier
of the key which was used to create the signature.
The used signature scheme has to be agreed upon by the signer and verifer
out of band.
The signature is a base64 encoding of the raw bytes from the signature
algorithm.
*/
type Signature struct {
KeyID string `json:"keyid"`
Sig string `json:"sig"`
}
/*
PAE implementes the DSSE Pre-Authentic Encoding
https://github.com/secure-systems-lab/dsse/blob/master/protocol.md#signature-definition
*/
func PAE(payloadType string, payload []byte) []byte {
return []byte(fmt.Sprintf("DSSEv1 %d %s %d %s",
len(payloadType), payloadType,
len(payload), payload))
}
/*
Signer defines the interface for an abstract signing algorithm.
The Signer interface is used to inject signature algorithm implementations
into the EnevelopeSigner. This decoupling allows for any signing algorithm
and key management system can be used.
The full message is provided as the parameter. If the signature algorithm
depends on hashing of the message prior to signature calculation, the
implementor of this interface must perform such hashing.
The function must return raw bytes representing the calculated signature
using the current algorithm, and the key used (if applicable).
For an example see EcdsaSigner in sign_test.go.
*/
type Signer interface {
Sign(data []byte) ([]byte, error)
KeyID() (string, error)
}
// SignVerifer provides both the signing and verification interface.
type SignVerifier interface {
Signer
Verifier
}
// EnvelopeSigner creates signed Envelopes.
type EnvelopeSigner struct {
providers []SignVerifier
ev *EnvelopeVerifier
}
/*
NewEnvelopeSigner creates an EnvelopeSigner that uses 1+ Signer
algorithms to sign the data.
Creates a verifier with threshold=1, at least one of the providers must validate signitures successfully.
*/
func NewEnvelopeSigner(p ...SignVerifier) (*EnvelopeSigner, error) {
return NewMultiEnvelopeSigner(1, p...)
}
/*
NewMultiEnvelopeSigner creates an EnvelopeSigner that uses 1+ Signer
algorithms to sign the data.
Creates a verifier with threshold.
threashold indicates the amount of providers that must validate the envelope.
*/
func NewMultiEnvelopeSigner(threshold int, p ...SignVerifier) (*EnvelopeSigner, error) {
var providers []SignVerifier
for _, sv := range p {
if sv != nil {
providers = append(providers, sv)
}
}
if len(providers) == 0 {
return nil, ErrNoSigners
}
evps := []Verifier{}
for _, p := range providers {
evps = append(evps, p.(Verifier))
}
ev, err := NewMultiEnvelopeVerifier(threshold, evps...)
if err != nil {
return nil, err
}
return &EnvelopeSigner{
providers: providers,
ev: ev,
}, nil
}
/*
SignPayload signs a payload and payload type according to DSSE.
Returned is an envelope as defined here:
https://github.com/secure-systems-lab/dsse/blob/master/envelope.md
One signature will be added for each Signer in the EnvelopeSigner.
*/
func (es *EnvelopeSigner) SignPayload(payloadType string, body []byte) (*Envelope, error) {
var e = Envelope{
Payload: base64.StdEncoding.EncodeToString(body),
PayloadType: payloadType,
}
paeEnc := PAE(payloadType, body)
for _, signer := range es.providers {
sig, err := signer.Sign(paeEnc)
if err != nil {
return nil, err
}
keyID, err := signer.KeyID()
if err != nil {
keyID = ""
}
e.Signatures = append(e.Signatures, Signature{
KeyID: keyID,
Sig: base64.StdEncoding.EncodeToString(sig),
})
}
return &e, nil
}
/*
Verify decodes the payload and verifies the signature.
Any domain specific validation such as parsing the decoded body and
validating the payload type is left out to the caller.
Verify returns a list of accepted keys each including a keyid, public and signiture of the accepted provider keys.
*/
func (es *EnvelopeSigner) Verify(e *Envelope) ([]AcceptedKey, error) {
return es.ev.Verify(e)
}
/*
Both standard and url encoding are allowed:
https://github.com/secure-systems-lab/dsse/blob/master/envelope.md
*/
func b64Decode(s string) ([]byte, error) {
b, err := base64.StdEncoding.DecodeString(s)
if err != nil {
b, err = base64.URLEncoding.DecodeString(s)
if err != nil {
return nil, err
}
}
return b, nil
}

View File

@ -0,0 +1,146 @@
package dsse
import (
"crypto"
"errors"
"fmt"
"golang.org/x/crypto/ssh"
)
/*
Verifier verifies a complete message against a signature and key.
If the message was hashed prior to signature generation, the verifier
must perform the same steps.
If KeyID returns successfully, only signature matching the key ID will be verified.
*/
type Verifier interface {
Verify(data, sig []byte) error
KeyID() (string, error)
Public() crypto.PublicKey
}
type EnvelopeVerifier struct {
providers []Verifier
threshold int
}
type AcceptedKey struct {
Public crypto.PublicKey
KeyID string
Sig Signature
}
func (ev *EnvelopeVerifier) Verify(e *Envelope) ([]AcceptedKey, error) {
if e == nil {
return nil, errors.New("cannot verify a nil envelope")
}
if len(e.Signatures) == 0 {
return nil, ErrNoSignature
}
// Decode payload (i.e serialized body)
body, err := e.DecodeB64Payload()
if err != nil {
return nil, err
}
// Generate PAE(payloadtype, serialized body)
paeEnc := PAE(e.PayloadType, body)
// If *any* signature is found to be incorrect, it is skipped
var acceptedKeys []AcceptedKey
usedKeyids := make(map[string]string)
unverified_providers := ev.providers
for _, s := range e.Signatures {
sig, err := b64Decode(s.Sig)
if err != nil {
return nil, err
}
// Loop over the providers.
// If provider and signature include key IDs but do not match skip.
// If a provider recognizes the key, we exit
// the loop and use the result.
providers := unverified_providers
for i, v := range providers {
keyID, err := v.KeyID()
// Verifiers that do not provide a keyid will be generated one using public.
if err != nil || keyID == "" {
keyID, err = SHA256KeyID(v.Public())
if err != nil {
keyID = ""
}
}
if s.KeyID != "" && keyID != "" && err == nil && s.KeyID != keyID {
continue
}
err = v.Verify(paeEnc, sig)
if err != nil {
continue
}
acceptedKey := AcceptedKey{
Public: v.Public(),
KeyID: keyID,
Sig: s,
}
unverified_providers = removeIndex(providers, i)
// See https://github.com/in-toto/in-toto/pull/251
if _, ok := usedKeyids[keyID]; ok {
fmt.Printf("Found envelope signed by different subkeys of the same main key, Only one of them is counted towards the step threshold, KeyID=%s\n", keyID)
continue
}
usedKeyids[keyID] = ""
acceptedKeys = append(acceptedKeys, acceptedKey)
break
}
}
// Sanity if with some reflect magic this happens.
if ev.threshold <= 0 || ev.threshold > len(ev.providers) {
return nil, errors.New("Invalid threshold")
}
if len(usedKeyids) < ev.threshold {
return acceptedKeys, errors.New(fmt.Sprintf("Accepted signatures do not match threshold, Found: %d, Expected %d", len(acceptedKeys), ev.threshold))
}
return acceptedKeys, nil
}
func NewEnvelopeVerifier(v ...Verifier) (*EnvelopeVerifier, error) {
return NewMultiEnvelopeVerifier(1, v...)
}
func NewMultiEnvelopeVerifier(threshold int, p ...Verifier) (*EnvelopeVerifier, error) {
if threshold <= 0 || threshold > len(p) {
return nil, errors.New("Invalid threshold")
}
ev := EnvelopeVerifier{
providers: p,
threshold: threshold,
}
return &ev, nil
}
func SHA256KeyID(pub crypto.PublicKey) (string, error) {
// Generate public key fingerprint
sshpk, err := ssh.NewPublicKey(pub)
if err != nil {
return "", err
}
fingerprint := ssh.FingerprintSHA256(sshpk)
return fingerprint, nil
}
func removeIndex(v []Verifier, index int) []Verifier {
return append(v[:index], v[index+1:]...)
}