1 package in_toto
2
3 import (
4 "crypto/ecdsa"
5 "crypto/rsa"
6 "crypto/x509"
7 "encoding/json"
8 "errors"
9 "fmt"
10 "os"
11 "reflect"
12 "regexp"
13 "strconv"
14 "strings"
15 "time"
16
17 "github.com/secure-systems-lab/go-securesystemslib/cjson"
18 "github.com/secure-systems-lab/go-securesystemslib/dsse"
19 )
20
21
27 type KeyVal struct {
28 Private string `json:"private,omitempty"`
29 Public string `json:"public"`
30 Certificate string `json:"certificate,omitempty"`
31 }
32
33
38 type Key struct {
39 KeyID string `json:"keyid"`
40 KeyIDHashAlgorithms []string `json:"keyid_hash_algorithms"`
41 KeyType string `json:"keytype"`
42 KeyVal KeyVal `json:"keyval"`
43 Scheme string `json:"scheme"`
44 }
45
46
47 var ErrEmptyKeyField = errors.New("empty field in key")
48
49
50 var ErrInvalidHexString = errors.New("invalid hex string")
51
52
53 var ErrSchemeKeyTypeMismatch = errors.New("the scheme and key type are not supported together")
54
55
56 var ErrUnsupportedKeyIDHashAlgorithms = errors.New("the given keyID hash algorithm is not supported")
57
58
59 var ErrKeyKeyTypeMismatch = errors.New("the given key does not match its key type")
60
61
62 var ErrNoPublicKey = errors.New("the given key is not a public key")
63
64
65
66 var ErrCurveSizeSchemeMismatch = errors.New("the scheme does not match the curve size")
67
68
73 func matchEcdsaScheme(curveSize int, scheme string) error {
74 if !strings.HasSuffix(scheme, strconv.Itoa(curveSize)) {
75 return ErrCurveSizeSchemeMismatch
76 }
77 return nil
78 }
79
80
84 func validateHexString(str string) error {
85 formatCheck, _ := regexp.MatchString("^[a-fA-F0-9]+$", str)
86 if !formatCheck {
87 return fmt.Errorf("%w: %s", ErrInvalidHexString, str)
88 }
89 return nil
90 }
91
92
101 func validateKeyVal(key Key) error {
102 switch key.KeyType {
103 case ed25519KeyType:
104
105
106
107
108 err := validateHexString(key.KeyVal.Public)
109 if err != nil {
110 return err
111 }
112 if key.KeyVal.Private != "" {
113 err := validateHexString(key.KeyVal.Private)
114 if err != nil {
115 return err
116 }
117 }
118 case rsaKeyType, ecdsaKeyType:
119
120 _, parsedKey, err := decodeAndParse([]byte(key.KeyVal.Public))
121 if err != nil {
122 return err
123 }
124 err = matchPublicKeyKeyType(parsedKey, key.KeyType)
125 if err != nil {
126 return err
127 }
128 if key.KeyVal.Private != "" {
129
130 _, parsedKey, err := decodeAndParse([]byte(key.KeyVal.Private))
131 if err != nil {
132 return err
133 }
134 err = matchPrivateKeyKeyType(parsedKey, key.KeyType)
135 if err != nil {
136 return err
137 }
138 }
139 default:
140 return ErrUnsupportedKeyType
141 }
142 return nil
143 }
144
145
153 func matchPublicKeyKeyType(key interface{}, keyType string) error {
154 switch key.(type) {
155 case *rsa.PublicKey:
156 if keyType != rsaKeyType {
157 return ErrKeyKeyTypeMismatch
158 }
159 case *ecdsa.PublicKey:
160 if keyType != ecdsaKeyType {
161 return ErrKeyKeyTypeMismatch
162 }
163 default:
164 return ErrInvalidKey
165 }
166 return nil
167 }
168
169
177 func matchPrivateKeyKeyType(key interface{}, keyType string) error {
178
179
180
181 switch key.(type) {
182 case *rsa.PrivateKey:
183 if keyType != rsaKeyType {
184 return ErrKeyKeyTypeMismatch
185 }
186 case *ecdsa.PrivateKey:
187 if keyType != ecdsaKeyType {
188 return ErrKeyKeyTypeMismatch
189 }
190 default:
191 return ErrInvalidKey
192 }
193 return nil
194 }
195
196
203 func matchKeyTypeScheme(key Key) error {
204 switch key.KeyType {
205 case rsaKeyType:
206 for _, scheme := range getSupportedRSASchemes() {
207 if key.Scheme == scheme {
208 return nil
209 }
210 }
211 case ed25519KeyType:
212 for _, scheme := range getSupportedEd25519Schemes() {
213 if key.Scheme == scheme {
214 return nil
215 }
216 }
217 case ecdsaKeyType:
218 for _, scheme := range getSupportedEcdsaSchemes() {
219 if key.Scheme == scheme {
220 return nil
221 }
222 }
223 default:
224 return fmt.Errorf("%w: %s", ErrUnsupportedKeyType, key.KeyType)
225 }
226 return ErrSchemeKeyTypeMismatch
227 }
228
229
235 func validateKey(key Key) error {
236 err := validateHexString(key.KeyID)
237 if err != nil {
238 return err
239 }
240
241
242 if key.KeyType == "" {
243 return fmt.Errorf("%w: keytype", ErrEmptyKeyField)
244 }
245 if key.KeyVal.Public == "" && key.KeyVal.Certificate == "" {
246 return fmt.Errorf("%w: keyval.public and keyval.certificate cannot both be blank", ErrEmptyKeyField)
247 }
248 if key.Scheme == "" {
249 return fmt.Errorf("%w: scheme", ErrEmptyKeyField)
250 }
251 err = matchKeyTypeScheme(key)
252 if err != nil {
253 return err
254 }
255
256 if key.KeyIDHashAlgorithms != nil {
257 supportedKeyIDHashAlgorithms := getSupportedKeyIDHashAlgorithms()
258 if !supportedKeyIDHashAlgorithms.IsSubSet(NewSet(key.KeyIDHashAlgorithms...)) {
259 return fmt.Errorf("%w: %#v, supported are: %#v", ErrUnsupportedKeyIDHashAlgorithms, key.KeyIDHashAlgorithms, getSupportedKeyIDHashAlgorithms())
260 }
261 }
262 return nil
263 }
264
265
270 func validatePublicKey(key Key) error {
271 if key.KeyVal.Private != "" {
272 return ErrNoPublicKey
273 }
274 err := validateKey(key)
275 if err != nil {
276 return err
277 }
278 return nil
279 }
280
281
286 type Signature struct {
287 KeyID string `json:"keyid"`
288 Sig string `json:"sig"`
289 Certificate string `json:"cert,omitempty"`
290 }
291
292
293
294 func (sig Signature) GetCertificate() (Key, error) {
295 key := Key{}
296 if len(sig.Certificate) == 0 {
297 return key, errors.New("Signature has empty Certificate")
298 }
299
300 err := key.LoadKeyReaderDefaults(strings.NewReader(sig.Certificate))
301 return key, err
302 }
303
304
308 func validateSignature(signature Signature) error {
309 if err := validateHexString(signature.KeyID); err != nil {
310 return err
311 }
312 if err := validateHexString(signature.Sig); err != nil {
313 return err
314 }
315 return nil
316 }
317
318
322 func validateSliceOfSignatures(slice []Signature) error {
323 for _, signature := range slice {
324 if err := validateSignature(signature); err != nil {
325 return err
326 }
327 }
328 return nil
329 }
330
331
337 type Link struct {
338 Type string `json:"_type"`
339 Name string `json:"name"`
340 Materials map[string]interface{} `json:"materials"`
341 Products map[string]interface{} `json:"products"`
342 ByProducts map[string]interface{} `json:"byproducts"`
343 Command []string `json:"command"`
344 Environment map[string]interface{} `json:"environment"`
345 }
346
347
350 func validateArtifacts(artifacts map[string]interface{}) error {
351 for artifactName, artifact := range artifacts {
352 artifactValue := reflect.ValueOf(artifact).MapRange()
353 for artifactValue.Next() {
354 value := artifactValue.Value().Interface().(string)
355 hashType := artifactValue.Key().Interface().(string)
356 if err := validateHexString(value); err != nil {
357 return fmt.Errorf("in artifact '%s', %s hash value: %s",
358 artifactName, hashType, err.Error())
359 }
360 }
361 }
362 return nil
363 }
364
365
369 func validateLink(link Link) error {
370 if link.Type != "link" {
371 return fmt.Errorf("invalid type for link '%s': should be 'link'",
372 link.Name)
373 }
374
375 if err := validateArtifacts(link.Materials); err != nil {
376 return fmt.Errorf("in materials of link '%s': %s", link.Name,
377 err.Error())
378 }
379
380 if err := validateArtifacts(link.Products); err != nil {
381 return fmt.Errorf("in products of link '%s': %s", link.Name,
382 err.Error())
383 }
384
385 return nil
386 }
387
388
397 const LinkNameFormat = "%s.%.8s.link"
398 const PreliminaryLinkNameFormat = ".%s.%.8s.link-unfinished"
399
400
406 const LinkNameFormatShort = "%s.link"
407 const LinkGlobFormat = "%s.????????.link"
408
409
413 const SublayoutLinkDirFormat = "%s.%.8s"
414
415
419 type SupplyChainItem struct {
420 Name string `json:"name"`
421 ExpectedMaterials [][]string `json:"expected_materials"`
422 ExpectedProducts [][]string `json:"expected_products"`
423 }
424
425
429 func validateArtifactRule(rule []string) error {
430 if _, err := UnpackRule(rule); err != nil {
431 return err
432 }
433 return nil
434 }
435
436
439 func validateSliceOfArtifactRules(rules [][]string) error {
440 for _, rule := range rules {
441 if err := validateArtifactRule(rule); err != nil {
442 return err
443 }
444 }
445 return nil
446 }
447
448
453 func validateSupplyChainItem(item SupplyChainItem) error {
454 if item.Name == "" {
455 return fmt.Errorf("name cannot be empty")
456 }
457
458 if err := validateSliceOfArtifactRules(item.ExpectedMaterials); err != nil {
459 return fmt.Errorf("invalid material rule: %s", err)
460 }
461 if err := validateSliceOfArtifactRules(item.ExpectedProducts); err != nil {
462 return fmt.Errorf("invalid product rule: %s", err)
463 }
464 return nil
465 }
466
467
474 type Inspection struct {
475 Type string `json:"_type"`
476 Run []string `json:"run"`
477 SupplyChainItem
478 }
479
480
484 func validateInspection(inspection Inspection) error {
485 if err := validateSupplyChainItem(inspection.SupplyChainItem); err != nil {
486 return fmt.Errorf("inspection %s", err.Error())
487 }
488 if inspection.Type != "inspection" {
489 return fmt.Errorf("invalid Type value for inspection '%s': should be "+
490 "'inspection'", inspection.SupplyChainItem.Name)
491 }
492 return nil
493 }
494
495
503 type Step struct {
504 Type string `json:"_type"`
505 PubKeys []string `json:"pubkeys"`
506 CertificateConstraints []CertificateConstraint `json:"cert_constraints,omitempty"`
507 ExpectedCommand []string `json:"expected_command"`
508 Threshold int `json:"threshold"`
509 SupplyChainItem
510 }
511
512
513
514 func (s Step) CheckCertConstraints(key Key, rootCAIDs []string, rootCertPool, intermediateCertPool *x509.CertPool) error {
515 if len(s.CertificateConstraints) == 0 {
516 return fmt.Errorf("no constraints found")
517 }
518
519 _, possibleCert, err := decodeAndParse([]byte(key.KeyVal.Certificate))
520 if err != nil {
521 return err
522 }
523
524 cert, ok := possibleCert.(*x509.Certificate)
525 if !ok {
526 return fmt.Errorf("not a valid certificate")
527 }
528
529 for _, constraint := range s.CertificateConstraints {
530 err = constraint.Check(cert, rootCAIDs, rootCertPool, intermediateCertPool)
531 if err == nil {
532 return nil
533 }
534 }
535 if err != nil {
536 return err
537 }
538
539
540 return fmt.Errorf("unknown certificate constraint error")
541 }
542
543
547 func validateStep(step Step) error {
548 if err := validateSupplyChainItem(step.SupplyChainItem); err != nil {
549 return fmt.Errorf("step %s", err.Error())
550 }
551 if step.Type != "step" {
552 return fmt.Errorf("invalid Type value for step '%s': should be 'step'",
553 step.SupplyChainItem.Name)
554 }
555 for _, keyID := range step.PubKeys {
556 if err := validateHexString(keyID); err != nil {
557 return err
558 }
559 }
560 return nil
561 }
562
563
567 const ISO8601DateSchema = "2006-01-02T15:04:05Z"
568
569
578 type Layout struct {
579 Type string `json:"_type"`
580 Steps []Step `json:"steps"`
581 Inspect []Inspection `json:"inspect"`
582 Keys map[string]Key `json:"keys"`
583 RootCas map[string]Key `json:"rootcas,omitempty"`
584 IntermediateCas map[string]Key `json:"intermediatecas,omitempty"`
585 Expires string `json:"expires"`
586 Readme string `json:"readme"`
587 }
588
589
590
591
592
593
594 func (l *Layout) stepsAsInterfaceSlice() []interface{} {
595 stepsI := make([]interface{}, len(l.Steps))
596 for i, v := range l.Steps {
597 stepsI[i] = v
598 }
599 return stepsI
600 }
601 func (l *Layout) inspectAsInterfaceSlice() []interface{} {
602 inspectionsI := make([]interface{}, len(l.Inspect))
603 for i, v := range l.Inspect {
604 inspectionsI[i] = v
605 }
606 return inspectionsI
607 }
608
609
610 func (l *Layout) RootCAIDs() []string {
611 rootCAIDs := make([]string, 0, len(l.RootCas))
612 for rootCAID := range l.RootCas {
613 rootCAIDs = append(rootCAIDs, rootCAID)
614 }
615 return rootCAIDs
616 }
617
618 func validateLayoutKeys(keys map[string]Key) error {
619 for keyID, key := range keys {
620 if key.KeyID != keyID {
621 return fmt.Errorf("invalid key found")
622 }
623 err := validatePublicKey(key)
624 if err != nil {
625 return err
626 }
627 }
628
629 return nil
630 }
631
632
636 func validateLayout(layout Layout) error {
637 if layout.Type != "layout" {
638 return fmt.Errorf("invalid Type value for layout: should be 'layout'")
639 }
640
641 if _, err := time.Parse(ISO8601DateSchema, layout.Expires); err != nil {
642 return fmt.Errorf("expiry time parsed incorrectly - date either" +
643 " invalid or of incorrect format")
644 }
645
646 if err := validateLayoutKeys(layout.Keys); err != nil {
647 return err
648 }
649
650 if err := validateLayoutKeys(layout.RootCas); err != nil {
651 return err
652 }
653
654 if err := validateLayoutKeys(layout.IntermediateCas); err != nil {
655 return err
656 }
657
658 var namesSeen = make(map[string]bool)
659 for _, step := range layout.Steps {
660 if namesSeen[step.Name] {
661 return fmt.Errorf("non unique step or inspection name found")
662 }
663
664 namesSeen[step.Name] = true
665
666 if err := validateStep(step); err != nil {
667 return err
668 }
669 }
670 for _, inspection := range layout.Inspect {
671 if namesSeen[inspection.Name] {
672 return fmt.Errorf("non unique step or inspection name found")
673 }
674
675 namesSeen[inspection.Name] = true
676 }
677 return nil
678 }
679
680 type Metadata interface {
681 Sign(Key) error
682 VerifySignature(Key) error
683 GetPayload() any
684 Sigs() []Signature
685 GetSignatureForKeyID(string) (Signature, error)
686 Dump(string) error
687 }
688
689 func LoadMetadata(path string) (Metadata, error) {
690 jsonBytes, err := os.ReadFile(path)
691 if err != nil {
692 return nil, err
693 }
694
695 var rawData map[string]*json.RawMessage
696 if err := json.Unmarshal(jsonBytes, &rawData); err != nil {
697 return nil, err
698 }
699
700 if _, ok := rawData["payloadType"]; ok {
701 dsseEnv := &dsse.Envelope{}
702 if rawData["payload"] == nil || rawData["signatures"] == nil {
703 return nil, fmt.Errorf("in-toto metadata envelope requires 'payload' and 'signatures' parts")
704 }
705
706 if err := json.Unmarshal(jsonBytes, dsseEnv); err != nil {
707 return nil, err
708 }
709
710 if dsseEnv.PayloadType != PayloadType {
711 return nil, ErrInvalidPayloadType
712 }
713
714 return loadEnvelope(dsseEnv)
715 }
716
717 mb := &Metablock{}
718
719
720
721
722 if rawData["signed"] == nil || rawData["signatures"] == nil {
723 return nil, fmt.Errorf("in-toto metadata requires 'signed' and 'signatures' parts")
724 }
725
726
727 if err := json.Unmarshal(*rawData["signatures"], &mb.Signatures); err != nil {
728 return nil, err
729 }
730
731 payload, err := loadPayload(*rawData["signed"])
732 if err != nil {
733 return nil, err
734 }
735
736 mb.Signed = payload
737
738 return mb, nil
739 }
740
741
747 type Metablock struct {
748
749
750
751
752
753
754
755
756
757
758 Signed interface{} `json:"signed"`
759 Signatures []Signature `json:"signatures"`
760 }
761
762 type jsonField struct {
763 name string
764 omitempty bool
765 }
766
767
772 func checkRequiredJSONFields(obj map[string]interface{},
773 typ reflect.Type) error {
774
775
776 attributeCount := typ.NumField()
777 allFields := make([]jsonField, 0)
778 for i := 0; i < attributeCount; i++ {
779 fieldStr := typ.Field(i).Tag.Get("json")
780 field := jsonField{
781 name: fieldStr,
782 omitempty: false,
783 }
784
785 if idx := strings.Index(fieldStr, ","); idx != -1 {
786 field.name = fieldStr[:idx]
787 field.omitempty = strings.Contains(fieldStr[idx+1:], "omitempty")
788 }
789
790 allFields = append(allFields, field)
791 }
792
793
794 for _, field := range allFields {
795 if _, ok := obj[field.name]; !ok && !field.omitempty {
796 return fmt.Errorf("required field %s missing", field.name)
797 }
798 }
799 return nil
800 }
801
802
810 func (mb *Metablock) Load(path string) error {
811
812 jsonBytes, err := os.ReadFile(path)
813 if err != nil {
814 return err
815 }
816
817
818
819
820 var rawMb map[string]*json.RawMessage
821 if err := json.Unmarshal(jsonBytes, &rawMb); err != nil {
822 return err
823 }
824
825
826
827
828 if rawMb["signed"] == nil || rawMb["signatures"] == nil {
829 return fmt.Errorf("in-toto metadata requires 'signed' and" +
830 " 'signatures' parts")
831 }
832
833
834 if err := json.Unmarshal(*rawMb["signatures"], &mb.Signatures); err != nil {
835 return err
836 }
837
838 payload, err := loadPayload(*rawMb["signed"])
839 if err != nil {
840 return err
841 }
842
843 mb.Signed = payload
844
845 return nil
846 }
847
848
852 func (mb *Metablock) Dump(path string) error {
853
854
855 jsonBytes, err := json.MarshalIndent(mb, "", " ")
856 if err != nil {
857 return err
858 }
859
860
861 err = os.WriteFile(path, jsonBytes, 0644)
862 if err != nil {
863 return err
864 }
865
866 return nil
867 }
868
869
874 func (mb *Metablock) GetSignableRepresentation() ([]byte, error) {
875 return cjson.EncodeCanonical(mb.Signed)
876 }
877
878 func (mb *Metablock) GetPayload() any {
879 return mb.Signed
880 }
881
882 func (mb *Metablock) Sigs() []Signature {
883 return mb.Signatures
884 }
885
886
893 func (mb *Metablock) VerifySignature(key Key) error {
894 sig, err := mb.GetSignatureForKeyID(key.KeyID)
895 if err != nil {
896 return err
897 }
898
899 dataCanonical, err := mb.GetSignableRepresentation()
900 if err != nil {
901 return err
902 }
903
904 if err := VerifySignature(key, sig, dataCanonical); err != nil {
905 return err
906 }
907 return nil
908 }
909
910
911 func (mb *Metablock) GetSignatureForKeyID(keyID string) (Signature, error) {
912 for _, s := range mb.Signatures {
913 if s.KeyID == keyID {
914 return s, nil
915 }
916 }
917
918 return Signature{}, fmt.Errorf("no signature found for key '%s'", keyID)
919 }
920
921
925 func ValidateMetablock(mb Metablock) error {
926 switch mbSignedType := mb.Signed.(type) {
927 case Layout:
928 if err := validateLayout(mb.Signed.(Layout)); err != nil {
929 return err
930 }
931 case Link:
932 if err := validateLink(mb.Signed.(Link)); err != nil {
933 return err
934 }
935 default:
936 return fmt.Errorf("unknown type '%s', should be 'layout' or 'link'",
937 mbSignedType)
938 }
939
940 if err := validateSliceOfSignatures(mb.Signatures); err != nil {
941 return err
942 }
943
944 return nil
945 }
946
947
953 func (mb *Metablock) Sign(key Key) error {
954
955 dataCanonical, err := mb.GetSignableRepresentation()
956 if err != nil {
957 return err
958 }
959
960 newSignature, err := GenerateSignature(dataCanonical, key)
961 if err != nil {
962 return err
963 }
964
965 mb.Signatures = append(mb.Signatures, newSignature)
966 return nil
967 }
968
View as plain text