github.com/transparency-dev/armored-witness-os@v0.1.3-0.20240514084412-27eef7325168/rpmb/rpmb.go (about)

     1  // Copyright 2022 The Armored Witness OS authors. All Rights Reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // Package rpmb implements Replay Protected Memory Block (RPMB) configuration
    16  // and control on eMMCs accessed through TamaGo NXP uSDHC driver.
    17  //
    18  // This package is only meant to be used with `GOOS=tamago GOARCH=arm` as
    19  // supported by the TamaGo framework for bare metal Go on ARM SoCs, see
    20  // https://github.com/usbarmory/tamago.
    21  //
    22  // The API supports mitigations for CVE-2020-13799 as described in the whitepaper linked at:
    23  //
    24  //	https://www.westerndigital.com/support/productsecurity/wdc-20008-replay-attack-vulnerabilities-rpmb-protocol-applications
    25  package rpmb
    26  
    27  import (
    28  	"errors"
    29  	"fmt"
    30  	"sync"
    31  
    32  	"github.com/usbarmory/tamago/soc/nxp/usdhc"
    33  )
    34  
    35  const keyLen = 32
    36  
    37  // RPMB defines a Replay Protected Memory Block partition access instance.
    38  type RPMB struct {
    39  	sync.Mutex
    40  
    41  	card *usdhc.USDHC
    42  	key  [keyLen]byte
    43  	init bool
    44  }
    45  
    46  // Init returns a new RPMB instance for a specific MMC card and MAC key. The
    47  // dummyBlock argument is an unused sector, required for CVE-2020-13799
    48  // mitigation to invalidate uncommitted writes.
    49  func Init(card *usdhc.USDHC, key []byte, dummyBlock uint16, writeDummy bool) (p *RPMB, err error) {
    50  	if card == nil {
    51  		return nil, fmt.Errorf("no MMC card set")
    52  	}
    53  
    54  	if !card.Info().MMC {
    55  		return nil, fmt.Errorf("no MMC card detected")
    56  	}
    57  
    58  	if len(key) != keyLen {
    59  		return nil, errors.New("invalid MAC key size")
    60  	}
    61  
    62  	p = &RPMB{
    63  		card: card,
    64  		init: true,
    65  	}
    66  
    67  	copy(p.key[:], key)
    68  
    69  	// invalidate uncommitted writes (CVE-2020-13799) if the RPMB has previously been programmed
    70  	if writeDummy {
    71  		if err = p.Write(dummyBlock, nil); err != nil {
    72  			return nil, err
    73  		}
    74  	}
    75  
    76  	return
    77  }
    78  
    79  // ProgramKey programs the RPMB partition authentication key.
    80  //
    81  // *WARNING*: this is a one-time irreversible operation for the specific MMC
    82  // card associated to the RPMB partition instance.
    83  func (p *RPMB) ProgramKey() (err error) {
    84  	cfg := &Config{
    85  		ResultRead: true,
    86  	}
    87  
    88  	req := &DataFrame{
    89  		KeyMAC: p.key,
    90  		Req:    AuthenticationKeyProgramming,
    91  	}
    92  
    93  	_, err = p.op(req, cfg)
    94  
    95  	return
    96  }
    97  
    98  // Counter returns the RPMB partition write counter, the argument boolean
    99  // indicates whether the read operation should be authenticated.
   100  func (p *RPMB) Counter(auth bool) (n uint32, err error) {
   101  	cfg := &Config{
   102  		RandomNonce: auth,
   103  		ResponseMAC: auth,
   104  	}
   105  
   106  	req := &DataFrame{
   107  		Req: WriteCounterRead,
   108  	}
   109  
   110  	res, err := p.op(req, cfg)
   111  
   112  	if err != nil {
   113  		return
   114  	}
   115  
   116  	return res.Counter(), nil
   117  }
   118  
   119  // Write performs an authenticated data transfer to the card RPMB partition,
   120  // the input buffer can contain up to 256 bytes of data.
   121  //
   122  // The write operation mitigates CVE-2020-13799 by verifying that the response
   123  // counter is equal to a single increment of the request counter, otherwise an
   124  // error is returned.
   125  func (p *RPMB) Write(offset uint16, buf []byte) (err error) {
   126  	return p.transfer(AuthenticatedDataWrite, offset, buf)
   127  }
   128  
   129  // Read performs an authenticated data transfer from the card RPMB partition,
   130  // the input buffer can contain up to 256 bytes of data.
   131  func (p *RPMB) Read(offset uint16, buf []byte) (err error) {
   132  	return p.transfer(AuthenticatedDataRead, offset, buf)
   133  }