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 }