github.com/kubiko/snapd@v0.0.0-20201013125620-d4f3094d9ddf/asserts/serial_asserts.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2016 Canonical Ltd 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 3 as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 package asserts 21 22 import ( 23 "fmt" 24 "time" 25 26 "github.com/snapcore/snapd/release" 27 "github.com/snapcore/snapd/strutil" 28 ) 29 30 // Serial holds a serial assertion, which is a statement binding a 31 // device identity with the device public key. 32 type Serial struct { 33 assertionBase 34 timestamp time.Time 35 pubKey PublicKey 36 } 37 38 // BrandID returns the brand identifier of the device. 39 func (ser *Serial) BrandID() string { 40 return ser.HeaderString("brand-id") 41 } 42 43 // Model returns the model name identifier of the device. 44 func (ser *Serial) Model() string { 45 return ser.HeaderString("model") 46 } 47 48 // Serial returns the serial identifier of the device, together with 49 // brand id and model they form the unique identifier of the device. 50 func (ser *Serial) Serial() string { 51 return ser.HeaderString("serial") 52 } 53 54 // DeviceKey returns the public key of the device. 55 func (ser *Serial) DeviceKey() PublicKey { 56 return ser.pubKey 57 } 58 59 // Timestamp returns the time when the serial assertion was issued. 60 func (ser *Serial) Timestamp() time.Time { 61 return ser.timestamp 62 } 63 64 func (ser *Serial) checkConsistency(db RODatabase, acck *AccountKey) error { 65 if ser.AuthorityID() != ser.BrandID() { 66 // serial authority and brand do not match, check the model 67 a, err := db.Find(ModelType, map[string]string{ 68 "series": release.Series, 69 "brand-id": ser.BrandID(), 70 "model": ser.Model(), 71 }) 72 if err != nil && !IsNotFound(err) { 73 return err 74 } 75 if IsNotFound(err) || !strutil.ListContains(a.(*Model).SerialAuthority(), ser.AuthorityID()) { 76 return fmt.Errorf("serial with authority %q different from brand %q without model assertion with serial-authority set to to allow for them", ser.AuthorityID(), ser.BrandID()) 77 } 78 } 79 return nil 80 } 81 82 func assembleSerial(assert assertionBase) (Assertion, error) { 83 // brand-id and authority-id can diverge if the model allows 84 // for it via serial-authority, check for brand-id well-formedness 85 _, err := checkStringMatches(assert.headers, "brand-id", validAccountID) 86 if err != nil { 87 return nil, err 88 } 89 90 _, err = checkModel(assert.headers) 91 if err != nil { 92 return nil, err 93 } 94 95 encodedKey, err := checkNotEmptyString(assert.headers, "device-key") 96 if err != nil { 97 return nil, err 98 } 99 pubKey, err := DecodePublicKey([]byte(encodedKey)) 100 if err != nil { 101 return nil, err 102 } 103 keyID, err := checkNotEmptyString(assert.headers, "device-key-sha3-384") 104 if err != nil { 105 return nil, err 106 } 107 if keyID != pubKey.ID() { 108 return nil, fmt.Errorf("device key does not match provided key id") 109 } 110 111 timestamp, err := checkRFC3339Date(assert.headers, "timestamp") 112 if err != nil { 113 return nil, err 114 } 115 116 // ignore extra headers and non-empty body for future compatibility 117 return &Serial{ 118 assertionBase: assert, 119 timestamp: timestamp, 120 pubKey: pubKey, 121 }, nil 122 } 123 124 // SerialRequest holds a serial-request assertion, which is a self-signed request to obtain a full device identity bound to the device public key. 125 type SerialRequest struct { 126 assertionBase 127 pubKey PublicKey 128 } 129 130 // BrandID returns the brand identifier of the device making the request. 131 func (sreq *SerialRequest) BrandID() string { 132 return sreq.HeaderString("brand-id") 133 } 134 135 // Model returns the model name identifier of the device making the request. 136 func (sreq *SerialRequest) Model() string { 137 return sreq.HeaderString("model") 138 } 139 140 // Serial returns the optional proposed serial identifier for the device, the service taking the request might use it or ignore it. 141 func (sreq *SerialRequest) Serial() string { 142 return sreq.HeaderString("serial") 143 } 144 145 // RequestID returns the id for the request, obtained from and to be presented to the serial signing service. 146 func (sreq *SerialRequest) RequestID() string { 147 return sreq.HeaderString("request-id") 148 } 149 150 // DeviceKey returns the public key of the device making the request. 151 func (sreq *SerialRequest) DeviceKey() PublicKey { 152 return sreq.pubKey 153 } 154 155 func assembleSerialRequest(assert assertionBase) (Assertion, error) { 156 _, err := checkNotEmptyString(assert.headers, "brand-id") 157 if err != nil { 158 return nil, err 159 } 160 161 _, err = checkModel(assert.headers) 162 if err != nil { 163 return nil, err 164 } 165 166 _, err = checkNotEmptyString(assert.headers, "request-id") 167 if err != nil { 168 return nil, err 169 } 170 171 _, err = checkOptionalString(assert.headers, "serial") 172 if err != nil { 173 return nil, err 174 } 175 176 encodedKey, err := checkNotEmptyString(assert.headers, "device-key") 177 if err != nil { 178 return nil, err 179 } 180 pubKey, err := DecodePublicKey([]byte(encodedKey)) 181 if err != nil { 182 return nil, err 183 } 184 185 if pubKey.ID() != assert.SignKeyID() { 186 return nil, fmt.Errorf("device key does not match included signing key id") 187 } 188 189 // ignore extra headers and non-empty body for future compatibility 190 return &SerialRequest{ 191 assertionBase: assert, 192 pubKey: pubKey, 193 }, nil 194 } 195 196 // DeviceSessionRequest holds a device-session-request assertion, which is a request wrapping a store-provided nonce to start a session by a device signed with its key. 197 type DeviceSessionRequest struct { 198 assertionBase 199 timestamp time.Time 200 } 201 202 // BrandID returns the brand identifier of the device making the request. 203 func (req *DeviceSessionRequest) BrandID() string { 204 return req.HeaderString("brand-id") 205 } 206 207 // Model returns the model name identifier of the device making the request. 208 func (req *DeviceSessionRequest) Model() string { 209 return req.HeaderString("model") 210 } 211 212 // Serial returns the serial identifier of the device making the request, 213 // together with brand id and model it forms the unique identifier of 214 // the device. 215 func (req *DeviceSessionRequest) Serial() string { 216 return req.HeaderString("serial") 217 } 218 219 // Nonce returns the nonce obtained from store and to be presented when requesting a device session. 220 func (req *DeviceSessionRequest) Nonce() string { 221 return req.HeaderString("nonce") 222 } 223 224 // Timestamp returns the time when the device-session-request was created. 225 func (req *DeviceSessionRequest) Timestamp() time.Time { 226 return req.timestamp 227 } 228 229 func assembleDeviceSessionRequest(assert assertionBase) (Assertion, error) { 230 _, err := checkModel(assert.headers) 231 if err != nil { 232 return nil, err 233 } 234 235 _, err = checkNotEmptyString(assert.headers, "nonce") 236 if err != nil { 237 return nil, err 238 } 239 240 timestamp, err := checkRFC3339Date(assert.headers, "timestamp") 241 if err != nil { 242 return nil, err 243 } 244 245 // ignore extra headers and non-empty body for future compatibility 246 return &DeviceSessionRequest{ 247 assertionBase: assert, 248 timestamp: timestamp, 249 }, nil 250 }