func getSerial(t *state.Task, privKey asserts.PrivateKey, device *auth.DeviceState, cfg *serialRequestConfig) (*asserts.Serial, error) { var serialSup serialSetup err := t.Get("serial-setup", &serialSup) if err != nil && err != state.ErrNoState { return nil, err } if serialSup.Serial != "" { // we got a serial, just haven't managed to save its info yet a, err := asserts.Decode([]byte(serialSup.Serial)) if err != nil { return nil, fmt.Errorf("internal error: cannot decode previously saved serial: %v", err) } return a.(*asserts.Serial), nil } client := &http.Client{Timeout: 30 * time.Second} // NB: until we get at least an Accepted (202) we need to // retry from scratch creating a new request-id because the // previous one used could have expired if serialSup.SerialRequest == "" { serialRequest, err := prepareSerialRequest(t, privKey, device, client, cfg) if err != nil { // errors & retries return nil, err } serialSup.SerialRequest = serialRequest } serial, err := submitSerialRequest(t, serialSup.SerialRequest, client, cfg) if err == errPoll { // we can/should reuse the serial-request t.Set("serial-setup", serialSup) return nil, errPoll } if err != nil { // errors & retries return nil, err } keyID := privKey.PublicKey().ID() if serial.BrandID() != device.Brand || serial.Model() != device.Model || serial.DeviceKey().ID() != keyID { return nil, fmt.Errorf("obtained serial assertion does not match provided device identity information (brand, model, key id): %s / %s / %s != %s / %s / %s", serial.BrandID(), serial.Model(), serial.DeviceKey().ID(), device.Brand, device.Model, keyID) } serialSup.Serial = string(asserts.Encode(serial)) t.Set("serial-setup", serialSup) if repeatRequestSerial == "after-got-serial" { // For testing purposes, ensure a crash in this state works. return nil, &state.Retry{} } return serial, nil }
func prepareSerialRequest(t *state.Task, privKey asserts.PrivateKey, device *auth.DeviceState, client *http.Client, cfg *serialRequestConfig) (string, error) { st := t.State() st.Unlock() defer st.Lock() req, err := http.NewRequest("POST", cfg.requestIDURL, nil) if err != nil { return "", fmt.Errorf("internal error: cannot create request-id request %q", cfg.requestIDURL) } cfg.applyHeaders(req) resp, err := client.Do(req) if err != nil { return "", retryErr(t, "cannot retrieve request-id for making a request for a serial: %v", err) } defer resp.Body.Close() if resp.StatusCode != 200 { return "", retryErr(t, "cannot retrieve request-id for making a request for a serial: unexpected status %d", resp.StatusCode) } dec := json.NewDecoder(resp.Body) var requestID requestIDResp err = dec.Decode(&requestID) if err != nil { // assume broken i/o return "", retryErr(t, "cannot read response with request-id for making a request for a serial: %v", err) } encodedPubKey, err := asserts.EncodePublicKey(privKey.PublicKey()) if err != nil { return "", fmt.Errorf("internal error: cannot encode device public key: %v", err) } headers := map[string]interface{}{ "brand-id": device.Brand, "model": device.Model, "request-id": requestID.RequestID, "device-key": string(encodedPubKey), } if cfg.proposedSerial != "" { headers["serial"] = cfg.proposedSerial } serialReq, err := asserts.SignWithoutAuthority(asserts.SerialRequestType, headers, cfg.body, privKey) if err != nil { return "", err } return string(asserts.Encode(serialReq)), nil }
// NewSigningDB creates a test signing assertion db with the given defaults. It panics on error. func NewSigningDB(authorityID string, privKey asserts.PrivateKey) *SigningDB { db, err := asserts.OpenDatabase(&asserts.DatabaseConfig{}) if err != nil { panic(err) } err = db.ImportKey(privKey) if err != nil { panic(err) } return &SigningDB{ AuthorityID: authorityID, KeyID: privKey.PublicKey().ID(), Database: db, } }
// NewStoreStack creates a new store assertion stack. It panics on error. func NewStoreStack(authorityID string, rootPrivKey, storePrivKey asserts.PrivateKey) *StoreStack { rootSigning := NewSigningDB(authorityID, rootPrivKey) ts := time.Now().Format(time.RFC3339) trustedAcct := NewAccount(rootSigning, authorityID, map[string]interface{}{ "account-id": authorityID, "validation": "certified", "timestamp": ts, }, "") trustedKey := NewAccountKey(rootSigning, trustedAcct, map[string]interface{}{ "name": "root", "since": ts, }, rootPrivKey.PublicKey(), "") trusted := []asserts.Assertion{trustedAcct, trustedKey} db, err := asserts.OpenDatabase(&asserts.DatabaseConfig{ Backstore: asserts.NewMemoryBackstore(), Trusted: trusted, }) if err != nil { panic(err) } err = db.ImportKey(storePrivKey) if err != nil { panic(err) } storeKey := NewAccountKey(rootSigning, trustedAcct, map[string]interface{}{ "name": "store", }, storePrivKey.PublicKey(), "") err = db.Add(storeKey) if err != nil { panic(err) } return &StoreStack{ TrustedAccount: trustedAcct, TrustedKey: trustedKey, Trusted: trusted, RootSigning: rootSigning, SigningDB: &SigningDB{ AuthorityID: authorityID, KeyID: storeKey.PublicKeyID(), Database: db, }, } }
func prepareSerialRequest(t *state.Task, privKey asserts.PrivateKey, device *auth.DeviceState, client *http.Client) (string, error) { st := t.State() st.Unlock() defer st.Lock() resp, err := client.Post(requestIDURL, "", nil) if err != nil { return "", retryErr(t, "cannot retrieve request-id for making a request for a serial: %v", err) } defer resp.Body.Close() if resp.StatusCode != 200 { return "", retryErr(t, "cannot retrieve request-id for making a request for a serial: unexpected status %d", resp.StatusCode) } dec := json.NewDecoder(resp.Body) var requestID requestIDResp err = dec.Decode(&requestID) if err != nil { // assume broken i/o return "", retryErr(t, "cannot read response with request-id for making a request for a serial: %v", err) } encodedPubKey, err := asserts.EncodePublicKey(privKey.PublicKey()) if err != nil { return "", fmt.Errorf("internal error: cannot encode device public key: %v", err) } serialReq, err := asserts.SignWithoutAuthority(asserts.SerialRequestType, map[string]interface{}{ "brand-id": device.Brand, "model": device.Model, "request-id": requestID.RequestID, "device-key": string(encodedPubKey), }, nil, privKey) // XXX: fill body with some agreed hardware details if err != nil { return "", err } return string(asserts.Encode(serialReq)), nil }