blob: be80c12632bbce97751ec07834668416b711d795 [file] [log] [blame]
Asim Shankar23c84a02014-06-04 15:17:10 -07001// Package caveat provides some third-party caveat implementations for the Google runtime.
Jiri Simsa5293dcb2014-05-10 09:56:38 -07002package caveat
3
4import (
Asim Shankar23c84a02014-06-04 15:17:10 -07005 "bytes"
Jiri Simsa5293dcb2014-05-10 09:56:38 -07006 "crypto/ecdsa"
Asim Shankar23c84a02014-06-04 15:17:10 -07007 "crypto/rand"
8 "crypto/sha256"
9 "encoding/binary"
Jiri Simsa5293dcb2014-05-10 09:56:38 -070010 "fmt"
11 "math/big"
Asim Shankar23c84a02014-06-04 15:17:10 -070012 "time"
Jiri Simsa5293dcb2014-05-10 09:56:38 -070013
Asim Shankar23c84a02014-06-04 15:17:10 -070014 vcaveat "veyron/security/caveat"
Jiri Simsa5293dcb2014-05-10 09:56:38 -070015 "veyron2/security"
Asim Shankar8303f182014-06-05 00:42:33 -070016 "veyron2/security/wire"
Andres Erbsendde52bf2014-06-09 09:42:33 -070017 "veyron2/vom"
Jiri Simsa5293dcb2014-05-10 09:56:38 -070018)
19
20// nonceLength specifies the length in bytes of the random nonce used
Andres Erbsendde52bf2014-06-09 09:42:33 -070021// in publicKeyCaveat and publicKeyDischarge.
Jiri Simsa5293dcb2014-05-10 09:56:38 -070022//
23// TODO(ataly, ashankar): Nonce length of 16 bytes was chosen very conservatively.
24// The purpose of the random nonce is essentially to separate caveats that have
25// the same restriction and validation key (in order to prevent discharge replay).
26// Can we use 4bytes instead?.
27const nonceLength = 16
28
29// errPublicKeyCaveat returns an error indicating that the provided caveat has
30// an invalid or missing discharge.
Asim Shankar23c84a02014-06-04 15:17:10 -070031func errPublicKeyCaveat(c *publicKeyCaveat, err error) error {
Andres Erbsendde52bf2014-06-09 09:42:33 -070032 return fmt.Errorf("%v for publicKeyCaveat{nonce:%v location:%q}", err, c.RandNonce, c.Location())
Jiri Simsa5293dcb2014-05-10 09:56:38 -070033}
34
Asim Shankar23c84a02014-06-04 15:17:10 -070035// publicKeyCaveat implements security.ThirdPartyCaveat. It specifies a restriction,
Jiri Simsa5293dcb2014-05-10 09:56:38 -070036// a validation key and the location of the third-party responsible for
37// discharging the caveat. A discharge for this caveat is a signed assertion whose
38// signature can be verified using the validation key.
Asim Shankar23c84a02014-06-04 15:17:10 -070039type publicKeyCaveat struct {
Jiri Simsa5293dcb2014-05-10 09:56:38 -070040 // RandNonce specifies a cryptographically random nonce (of fixed length) that
41 // uniquely identifies the caveat.
42 RandNonce []uint8
43
Andres Erbsendde52bf2014-06-09 09:42:33 -070044 // DischargeMintingCaveat specifies the caveat that has to be validated
45 // before minting a discharge for a publicKeyCaveat. A byte slice containing
46 // VOM-encoded security.Caveat is used to enable a publicKeyCaveat to be
47 // validated by devices that cannot decode the discharge minting caveats.
48 DischargeMintingCaveat []byte
Jiri Simsa5293dcb2014-05-10 09:56:38 -070049
50 // ValidationKey specifies the public key of the discharging-party.
51 ValidationKey wire.PublicKey
52
53 // ThirdPartyLocation specifies the global Veyron name of the discharging-party.
54 ThirdPartyLocation string
55}
56
57// ID returns a unique 32bytes long identity for the caveat based on the random nonce
58// and SHA-256 hash of the restriction embedded in the caveat.
59//
60// TODO(ataly, ashankar): A 256bit hash is probably much stronger that what we need
61// here. Can we truncate the hash to 96bits?
Asim Shankar23c84a02014-06-04 15:17:10 -070062func (c *publicKeyCaveat) ID() security.ThirdPartyCaveatID {
Andres Erbsendde52bf2014-06-09 09:42:33 -070063 return security.ThirdPartyCaveatID(id(c.RandNonce, c.DischargeMintingCaveat))
Jiri Simsa5293dcb2014-05-10 09:56:38 -070064}
65
66// Location returns the third-party location embedded in the caveat.
Asim Shankar23c84a02014-06-04 15:17:10 -070067func (c *publicKeyCaveat) Location() string {
Jiri Simsa5293dcb2014-05-10 09:56:38 -070068 return c.ThirdPartyLocation
69}
70
Asim Shankar23c84a02014-06-04 15:17:10 -070071func (c *publicKeyCaveat) String() string {
Andres Erbsendde52bf2014-06-09 09:42:33 -070072 return fmt.Sprintf("publicKeyCaveat{DischargeMintingCaveat: (%v bytes), ThirdPartyLocation: %q}", len(c.DischargeMintingCaveat), c.ThirdPartyLocation)
Jiri Simsa5293dcb2014-05-10 09:56:38 -070073}
74
75// Validate verifies whether the caveat validates for the provided context.
Asim Shankar23c84a02014-06-04 15:17:10 -070076func (c *publicKeyCaveat) Validate(ctx security.Context) error {
Jiri Simsa5293dcb2014-05-10 09:56:38 -070077 // Check whether the context has a dicharge matching the caveat's ID.
78 dis, ok := ctx.CaveatDischarges()[c.ID()]
79 if !ok {
80 return errPublicKeyCaveat(c, fmt.Errorf("missing discharge"))
81 }
82
Asim Shankar23c84a02014-06-04 15:17:10 -070083 pkDischarge, ok := dis.(*publicKeyDischarge)
Jiri Simsa5293dcb2014-05-10 09:56:38 -070084 if !ok {
85 return errPublicKeyCaveat(c, fmt.Errorf("caveat of type %T cannot be validated with discharge of type %T", c, dis))
86 }
87
88 // Validate all caveats present on the discharge.
89 for _, cav := range pkDischarge.Caveats {
90 if err := cav.Validate(ctx); err != nil {
91 return errPublicKeyCaveat(c, err)
92 }
93 }
94
95 // Check the discharge signature with the validation key from the caveat.
96 key, err := c.ValidationKey.Decode()
97 if err != nil {
98 return errPublicKeyCaveat(c, err)
99 }
100 var r, s big.Int
101 if !ecdsa.Verify(key, pkDischarge.contentHash(), r.SetBytes(pkDischarge.Signature.R), s.SetBytes(pkDischarge.Signature.S)) {
102 return errPublicKeyCaveat(c, fmt.Errorf("discharge %v has invalid signature", dis))
103 }
104 return nil
105}
106
Asim Shankar23c84a02014-06-04 15:17:10 -0700107// publicKeyDischarge implements security.ThirdPartyDischarge. It specifies a
108// discharge for a publicKeyCaveat, and includes a signature that can be verified
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700109// with the validation key of the caveat. Additionally, the discharge may include
110// service caveats which must all be valid in order for the discharge to be
111// considered valid.
Asim Shankar23c84a02014-06-04 15:17:10 -0700112type publicKeyDischarge struct {
Andres Erbsendde52bf2014-06-09 09:42:33 -0700113 // ThirdPartyCaveatID is used to match a Discharge with the Caveat it is for.
114 ThirdPartyCaveatID security.ThirdPartyCaveatID
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700115
116 // Caveats under which this Discharge is valid.
117 Caveats []wire.Caveat
118
119 // Signature on the contents of the discharge obtained using the private key
120 // corresponding to the validaton key in the caveat.
121 Signature wire.Signature
122}
123
124// CaveatID returns a unique identity for the discharge based on the random nonce and
125// restriction embedded in the discharge.
Asim Shankar23c84a02014-06-04 15:17:10 -0700126func (d *publicKeyDischarge) CaveatID() security.ThirdPartyCaveatID {
Andres Erbsendde52bf2014-06-09 09:42:33 -0700127 return d.ThirdPartyCaveatID
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700128}
129
Asim Shankar23c84a02014-06-04 15:17:10 -0700130func (d *publicKeyDischarge) ThirdPartyCaveats() []security.ServiceCaveat {
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700131 return wire.DecodeThirdPartyCaveats(d.Caveats)
132}
Asim Shankar23c84a02014-06-04 15:17:10 -0700133
134// sign uses the provided private key to sign the contents of the discharge. The private
135// key typically belongs to the principal that minted the discharge.
136func (d *publicKeyDischarge) sign(key *ecdsa.PrivateKey) error {
137 r, s, err := ecdsa.Sign(rand.Reader, key, d.contentHash())
138 if err != nil {
139 return err
140 }
141 d.Signature.R = r.Bytes()
142 d.Signature.S = s.Bytes()
143 return nil
144}
145
146func (d *publicKeyDischarge) contentHash() []byte {
147 h := sha256.New()
148 tmp := make([]byte, binary.MaxVarintLen64)
149
Andres Erbsendde52bf2014-06-09 09:42:33 -0700150 wire.WriteBytes(h, tmp, []byte(d.ThirdPartyCaveatID))
Asim Shankar23c84a02014-06-04 15:17:10 -0700151 for _, cav := range d.Caveats {
152 wire.WriteString(h, tmp, string(cav.Service))
153 wire.WriteBytes(h, tmp, cav.Bytes)
154 }
155 return h.Sum(nil)
156}
157
158// NewPublicKeyCaveat returns a new third-party caveat from the provided restriction,
159// third-party identity, and third-party location.
Andres Erbsendde52bf2014-06-09 09:42:33 -0700160func NewPublicKeyCaveat(dischargeMintingCaveat security.Caveat, thirdParty security.PublicID, location string) (security.ThirdPartyCaveat, error) {
Asim Shankar23c84a02014-06-04 15:17:10 -0700161 nonce := make([]uint8, nonceLength)
162 if _, err := rand.Read(nonce); err != nil {
163 return nil, err
164 }
165
166 var validationKey wire.PublicKey
167 if err := validationKey.Encode(thirdParty.PublicKey()); err != nil {
168 return nil, err
169 }
170
Andres Erbsendde52bf2014-06-09 09:42:33 -0700171 var mintingCaveatEncoded bytes.Buffer
172 vom.NewEncoder(&mintingCaveatEncoded).Encode(dischargeMintingCaveat)
Asim Shankar23c84a02014-06-04 15:17:10 -0700173 return &publicKeyCaveat{
Andres Erbsendde52bf2014-06-09 09:42:33 -0700174 RandNonce: nonce,
175 DischargeMintingCaveat: mintingCaveatEncoded.Bytes(),
176 ValidationKey: validationKey,
177 ThirdPartyLocation: location,
Asim Shankar23c84a02014-06-04 15:17:10 -0700178 }, nil
179}
180
181// NewPublicKeyDischarge returns a new discharge for the provided caveat
Andres Erbsendde52bf2014-06-09 09:42:33 -0700182// after verifying that the caveats for minting a discharge are met.
Asim Shankar23c84a02014-06-04 15:17:10 -0700183//
184// The CaveatID of the discharge is the same as the ID of the caveat, and
185// the discharge includes the provided service caveats along with a universal
186// expiry caveat for the provided duration. The discharge also includes a
187// signature over its contents obtained from the provided private key.
Andres Erbsendde52bf2014-06-09 09:42:33 -0700188func NewPublicKeyDischarge(caveat security.ThirdPartyCaveat, ctx security.Context, dischargingKey *ecdsa.PrivateKey, duration time.Duration, caveats []security.ServiceCaveat) (security.ThirdPartyDischarge, error) {
Asim Shankar23c84a02014-06-04 15:17:10 -0700189 cav, ok := caveat.(*publicKeyCaveat)
190 if !ok {
191 return nil, fmt.Errorf("cannot mint discharges for %T", caveat)
192 }
Andres Erbsendde52bf2014-06-09 09:42:33 -0700193 var mintingCaveat security.Caveat
194 mcBuf := bytes.NewReader(cav.DischargeMintingCaveat)
195 if err := vom.NewDecoder(mcBuf).Decode(&mintingCaveat); err != nil {
196 return nil, fmt.Errorf("failed to decode DischargeMintingCaveat: %s", err)
Asim Shankar23c84a02014-06-04 15:17:10 -0700197 }
Andres Erbsendde52bf2014-06-09 09:42:33 -0700198 if err := mintingCaveat.Validate(ctx); err != nil {
199 return nil, fmt.Errorf("failed to validate DischargeMintingCaveat: %s", err)
200 }
201
202 discharge := &publicKeyDischarge{ThirdPartyCaveatID: caveat.ID()}
Asim Shankar23c84a02014-06-04 15:17:10 -0700203
204 now := time.Now()
205 expiryCaveat := &vcaveat.Expiry{IssueTime: now, ExpiryTime: now.Add(duration)}
206 caveats = append(caveats, security.UniversalCaveat(expiryCaveat))
207
208 encodedCaveats, err := wire.EncodeCaveats(caveats)
209 if err != nil {
210 return nil, err
211 }
212 discharge.Caveats = encodedCaveats
213
214 if err := discharge.sign(dischargingKey); err != nil {
215 return nil, err
216 }
217
218 return discharge, nil
219}
220
Andres Erbsendde52bf2014-06-09 09:42:33 -0700221// id calculates the sha256 hash of length-delimited byte slices
222func id(slices ...[]byte) string {
Asim Shankar23c84a02014-06-04 15:17:10 -0700223 h := sha256.New()
224 tmp := make([]byte, binary.MaxVarintLen64)
Asim Shankar23c84a02014-06-04 15:17:10 -0700225
Andres Erbsendde52bf2014-06-09 09:42:33 -0700226 for _, slice := range slices {
227 wire.WriteBytes(h, tmp, slice)
228 }
229 return string(h.Sum(nil))
230}
231
232func init() {
233 vom.Register(publicKeyCaveat{})
234 vom.Register(publicKeyDischarge{})
Asim Shankar23c84a02014-06-04 15:17:10 -0700235}