-
Notifications
You must be signed in to change notification settings - Fork 0
/
timestamp.go
136 lines (123 loc) · 3.76 KB
/
timestamp.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
package data
import (
"bytes"
"fmt"
"github.com/docker/go/canonical/json"
"github.com/docker/notary"
)
// SignedTimestamp is a fully unpacked timestamp.json
type SignedTimestamp struct {
Signatures []Signature
Signed Timestamp
Dirty bool
}
// Timestamp is the Signed component of a timestamp.json
type Timestamp struct {
SignedCommon
Meta Files `json:"meta"`
}
// IsValidTimestampStructure returns an error, or nil, depending on whether the content of the struct
// is valid for timestamp metadata. This does not check signatures or expiry, just that
// the metadata content is valid.
func IsValidTimestampStructure(t Timestamp) error {
expectedType := TUFTypes[CanonicalTimestampRole]
if t.Type != expectedType {
return ErrInvalidMetadata{
role: CanonicalTimestampRole, msg: fmt.Sprintf("expected type %s, not %s", expectedType, t.Type)}
}
if t.Version < 1 {
return ErrInvalidMetadata{
role: CanonicalTimestampRole, msg: "version cannot be less than one"}
}
// Meta is a map of FileMeta, so if the role isn't in the map it returns
// an empty FileMeta, which has an empty map, and you can check on keys
// from an empty map.
//
// For now sha256 is required and sha512 is not.
if _, ok := t.Meta[CanonicalSnapshotRole].Hashes[notary.SHA256]; !ok {
return ErrInvalidMetadata{
role: CanonicalTimestampRole, msg: "missing snapshot sha256 checksum information"}
}
if err := CheckValidHashStructures(t.Meta[CanonicalSnapshotRole].Hashes); err != nil {
return ErrInvalidMetadata{
role: CanonicalTimestampRole, msg: fmt.Sprintf("invalid snapshot checksum information, %v", err)}
}
return nil
}
// NewTimestamp initializes a timestamp with an existing snapshot
func NewTimestamp(snapshot *Signed) (*SignedTimestamp, error) {
snapshotJSON, err := json.Marshal(snapshot)
if err != nil {
return nil, err
}
snapshotMeta, err := NewFileMeta(bytes.NewReader(snapshotJSON), NotaryDefaultHashes...)
if err != nil {
return nil, err
}
return &SignedTimestamp{
Signatures: make([]Signature, 0),
Signed: Timestamp{
SignedCommon: SignedCommon{
Type: TUFTypes[CanonicalTimestampRole],
Version: 0,
Expires: DefaultExpires(CanonicalTimestampRole),
},
Meta: Files{
CanonicalSnapshotRole: snapshotMeta,
},
},
}, nil
}
// ToSigned partially serializes a SignedTimestamp such that it can
// be signed
func (ts *SignedTimestamp) ToSigned() (*Signed, error) {
s, err := defaultSerializer.MarshalCanonical(ts.Signed)
if err != nil {
return nil, err
}
signed := json.RawMessage{}
err = signed.UnmarshalJSON(s)
if err != nil {
return nil, err
}
sigs := make([]Signature, len(ts.Signatures))
copy(sigs, ts.Signatures)
return &Signed{
Signatures: sigs,
Signed: &signed,
}, nil
}
// GetSnapshot gets the expected snapshot metadata hashes in the timestamp metadata,
// or nil if it doesn't exist
func (ts *SignedTimestamp) GetSnapshot() (*FileMeta, error) {
snapshotExpected, ok := ts.Signed.Meta[CanonicalSnapshotRole]
if !ok {
return nil, ErrMissingMeta{Role: CanonicalSnapshotRole}
}
return &snapshotExpected, nil
}
// MarshalJSON returns the serialized form of SignedTimestamp as bytes
func (ts *SignedTimestamp) MarshalJSON() ([]byte, error) {
signed, err := ts.ToSigned()
if err != nil {
return nil, err
}
return defaultSerializer.Marshal(signed)
}
// TimestampFromSigned parsed a Signed object into a fully unpacked
// SignedTimestamp
func TimestampFromSigned(s *Signed) (*SignedTimestamp, error) {
ts := Timestamp{}
if err := defaultSerializer.Unmarshal(*s.Signed, &ts); err != nil {
return nil, err
}
if err := IsValidTimestampStructure(ts); err != nil {
return nil, err
}
sigs := make([]Signature, len(s.Signatures))
copy(sigs, s.Signatures)
return &SignedTimestamp{
Signatures: sigs,
Signed: ts,
}, nil
}