imfg/imfg.go (221 lines of code) (raw):

/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package imfg import ( "bytes" "encoding/binary" "fmt" "sort" "strings" "github.com/apache/mynewt-artifact/errors" "github.com/apache/mynewt-artifact/flash" "github.com/apache/mynewt-artifact/manifest" "github.com/apache/mynewt-artifact/mfg" "mynewt.apache.org/imgmod/iutil" ) type NameBlobMap map[string][]byte func (to NameBlobMap) Union(from NameBlobMap) { for k, v := range from { to[k] = v } } func errInvalidArea(areaName string, format string, args ...interface{}) error { suffix := fmt.Sprintf(format, args...) return errors.Errorf("invalid flash area \"%s\": %s", areaName, suffix) } func verifyArea(area flash.FlashArea, minOffset int) error { if area.Offset < minOffset { return errInvalidArea(area.Name, "invalid offset %d; expected >= %d", area.Offset, minOffset) } if area.Size < 0 { return errInvalidArea(area.Name, "invalid size %d", area.Size) } return nil } // `areas` must be sorted by device ID, then by offset. func VerifyAreas(areas []flash.FlashArea) error { prevDevice := -1 off := 0 for _, area := range areas { if area.Device != prevDevice { off = 0 } if err := verifyArea(area, off); err != nil { return err } off += area.Size } return nil } // areaDataEnd calculates the end offset of valid data in a flash area. "Valid // data" includes all target binaries and raw sections. If it cannot determine // the end of any constituent part, it returns an error. func areaDataEnd(area flash.FlashArea, man manifest.MfgManifest) (int, error) { end := 0 // Updates `end` if the specified region extends beyond our current concept // of "the end". checkOne := func(offset int, size int) error { a := man.FindWithinFlashAreaDevOff(man.Device, offset) if a.Id != area.Id { // Data belongs to a different area. return nil } // Older manifests do not contain target size information. if size <= 0 { return errors.Errorf( "failed to calculate end offset of data at %d: "+ "manifest lacks size", offset) } subEnd := offset - a.Offset + size if subEnd > end { end = subEnd } return nil } for _, t := range man.Targets { err := checkOne(t.Offset, t.Size) if err != nil { return 0, err } } for _, r := range man.Raws { err := checkOne(r.Offset, r.Size) if err != nil { return 0, err } } if man.Meta != nil { err := checkOne(man.Meta.EndOffset-1, 1) if err != nil { return 0, err } } return end, nil } func Split(mfgBin []byte, man manifest.MfgManifest, areas []flash.FlashArea) (NameBlobMap, error) { mm := NameBlobMap{} for _, area := range areas { if _, ok := mm[area.Name]; ok { return nil, errors.Errorf( "two or more flash areas with same name: \"%s\"", area.Name) } if area.Device == man.Device { var areaBin []byte if area.Offset < len(mfgBin) { end := area.Offset + area.Size overflow := end - len(mfgBin) if overflow > 0 { end -= overflow } areaBin = mfgBin[area.Offset:end] } dataEnd, err := areaDataEnd(area, man) if err != nil { // Failed to determine the data end offset. Just strip all // trailing erase-val bytes. areaBin = StripPadding(areaBin, man.EraseVal) } else { areaBin = areaBin[:dataEnd] } if len(areaBin) > 0 { mm[area.Name] = areaBin } } } return mm, nil } // `areas` must be sorted by device ID, then by offset. func Join(mm NameBlobMap, eraseVal byte, areas []flash.FlashArea) ([]byte, error) { // Keep track of which areas we haven't seen yet. unseen := map[string]struct{}{} for name, _ := range mm { unseen[name] = struct{}{} } joined := []byte{} for _, area := range areas { bin := mm[area.Name] // Only include this area if it belongs to the mfg image we are // joining. if bin != nil { delete(unseen, area.Name) // Pad remainder of previous area in this section. padSize := area.Offset - len(joined) if padSize > 0 { joined = mfg.AddPadding(joined, eraseVal, padSize) } // Append data to joined binary. binstr := "" if len(bin) >= 4 { binstr = fmt.Sprintf("%x", bin[:4]) } iutil.Printf("inserting %s (%s) at offset %d (0x%x)\n", area.Name, binstr, len(joined), len(joined)) joined = append(joined, bin...) } } // Ensure we processed every area in the map. if len(unseen) > 0 { names := []string{} for name, _ := range unseen { names = append(names, name) } sort.Strings(names) return nil, errors.Errorf( "unprocessed flash areas: %s", strings.Join(names, ", ")) } // Strip padding from the end of the joined binary. joined = StripPadding(joined, eraseVal) return joined, nil } func replaceKey(mfgBin []byte, okey []byte, nkey []byte) (int, error) { if len(okey) > len(mfgBin) { return 0, errors.Errorf( "key longer than flash section (%d > %d)", len(okey), len(mfgBin)) } idx := bytes.Index(mfgBin, okey) if idx == -1 { return 0, errors.Errorf("old key not present in flash section") } lastIdx := bytes.LastIndex(mfgBin, okey) if idx != lastIdx { return 0, errors.Errorf( "multiple instances of old key in flash section") } iutil.PrintfVerbose("Replacing key at offset %d\n", idx) copy(mfgBin[idx:idx+len(okey)], nkey) return idx, nil } func ReplaceIsk(mfgBin []byte, okey []byte, nkey []byte) error { if len(nkey) != len(okey) { return errors.Errorf( "key lengths differ (%d != %d)", len(nkey), len(okey)) } if _, err := replaceKey(mfgBin, okey, nkey); err != nil { return err } return nil } func ReplaceKek(mfgBin []byte, okey []byte, nkey []byte) error { if len(nkey) > len(okey) { return errors.Errorf( "new key longer than old key (%d > %d)", len(nkey), len(okey)) } keyIdx, err := replaceKey(mfgBin, okey, nkey) if err != nil { return err } // The key length is an unsigned int immediately prior to the key. var kl uint32 klIdx := keyIdx - 4 buf := bytes.NewBuffer(mfgBin[klIdx : klIdx+4]) if err := binary.Read(buf, binary.LittleEndian, &kl); err != nil { return errors.Wrapf(err, "failed to read key length") } if int(kl) != len(okey) { return errors.Errorf( "embedded key length (off=%d) has unexpected value; "+ "want=%d have=%d", klIdx, len(okey), kl) } buf = &bytes.Buffer{} kl = uint32(len(nkey)) if err := binary.Write(buf, binary.LittleEndian, kl); err != nil { return errors.Wrapf(err, "failed to write key length") } copy(mfgBin[klIdx:klIdx+4], buf.Bytes()) return nil } func StripPadding(b []byte, eraseVal byte) []byte { var pad int for pad = 0; pad < len(b); pad++ { off := len(b) - pad - 1 if b[off] != eraseVal { break } } return b[:len(b)-pad] }