github.com/transparency-dev/armored-witness-os@v0.1.3-0.20240514084412-27eef7325168/rpmb/op.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 16 17 import ( 18 "bytes" 19 "crypto/hmac" 20 "crypto/rand" 21 "crypto/sha256" 22 "encoding/binary" 23 "errors" 24 "fmt" 25 "log" 26 ) 27 28 const ( 29 FrameLength = 512 30 macOffset = 284 31 ) 32 33 // p99, Table 18 — RPMB Request/Response Message Types, JESD84-B51 34 const ( 35 AuthenticationKeyProgramming = iota + 1 36 WriteCounterRead 37 AuthenticatedDataWrite 38 AuthenticatedDataRead 39 ResultRead 40 AuthenticatedDeviceConfigurationWrite 41 AuthenticatedDeviceConfigurationRead 42 ) 43 44 // p100, Table 20 — RPMB Operation Results, JESD84-B51 45 const ( 46 OperationOK = iota 47 GeneralFailure 48 AuthenticationFailure 49 CounterFailure 50 AddressFailure 51 WriteFailure 52 ReadFailure 53 AuthenticationKeyNotYetProgrammed 54 ) 55 56 type OperationError struct { 57 Result uint16 58 } 59 60 func (e *OperationError) Error() string { 61 return fmt.Sprintf("operation failed (%x)", e.Result) 62 } 63 64 // Request configuration 65 type Config struct { 66 // compute request MAC before sending 67 RequestMAC bool 68 // validate response MAC after receiving 69 ResponseMAC bool 70 // set Nonce field with random value 71 RandomNonce bool 72 // get response with a result read request 73 ResultRead bool 74 } 75 76 // p98, Table 17 — Data Frame Files for RPMB, JESD84-B51 77 type DataFrame struct { 78 StuffBytes [196]byte 79 KeyMAC [32]byte 80 Data [256]byte 81 Nonce [16]byte 82 WriteCounter [4]byte 83 Address [2]byte 84 BlockCount [2]byte 85 Result [2]byte 86 Resp byte 87 Req byte 88 } 89 90 // Counter returns the data frame WriteCounter in uint32 format. 91 func (d *DataFrame) Counter() uint32 { 92 return binary.BigEndian.Uint32(d.WriteCounter[:]) 93 } 94 95 // Bytes converts the data frame structure to byte array format. 96 func (d *DataFrame) Bytes() []byte { 97 buf := new(bytes.Buffer) 98 binary.Write(buf, binary.LittleEndian, d) 99 return buf.Bytes() 100 } 101 102 func (p *RPMB) op(req *DataFrame, cfg *Config) (res *DataFrame, err error) { 103 var rel bool 104 105 p.Lock() 106 defer p.Unlock() 107 108 if !p.init { 109 return nil, errors.New("RPMB instance not initialized") 110 } 111 112 mac := hmac.New(sha256.New, p.key[:]) 113 114 if cfg.RequestMAC { 115 mac.Write(req.Bytes()[FrameLength-macOffset:]) 116 copy(req.KeyMAC[:], mac.Sum(nil)) 117 mac.Reset() 118 } 119 120 if cfg.RandomNonce { 121 copy(req.Nonce[:], rng(len(req.Nonce))) 122 } 123 124 switch req.Req { 125 case AuthenticationKeyProgramming, AuthenticatedDataWrite, AuthenticatedDeviceConfigurationWrite: 126 rel = true 127 default: 128 rel = false 129 } 130 131 // send request 132 if err = p.card.WriteRPMB(req.Bytes(), rel); err != nil { 133 return 134 } 135 136 // read result when required 137 if cfg.ResultRead { 138 resReq := DataFrame{ 139 Req: ResultRead, 140 } 141 142 // send result read request 143 if err = p.card.WriteRPMB(resReq.Bytes(), false); err != nil { 144 return 145 } 146 } 147 148 buf := make([]byte, FrameLength) 149 res = &DataFrame{} 150 151 // read response 152 if err = p.card.ReadRPMB(buf); err != nil { 153 return 154 } 155 156 // parse response 157 if err = binary.Read(bytes.NewReader(buf), binary.LittleEndian, res); err != nil { 158 return 159 } 160 161 // validate response 162 163 if cfg.ResponseMAC { 164 mac.Write(buf[FrameLength-macOffset:]) 165 166 if !hmac.Equal(res.KeyMAC[:], mac.Sum(nil)) { 167 return nil, errors.New("invalid response MAC") 168 } 169 } 170 171 if req.Req != res.Resp { 172 return nil, errors.New("request/response type mismatch") 173 } 174 175 if req.Nonce != res.Nonce { 176 return nil, errors.New("nonce mismatch") 177 } 178 179 result := binary.BigEndian.Uint16(res.Result[:]) 180 181 if result != uint16(OperationOK) { 182 return nil, &OperationError{result} 183 } 184 185 return 186 } 187 188 func (p *RPMB) transfer(kind byte, offset uint16, buf []byte) (err error) { 189 if len(buf) > FrameLength/2 { 190 return errors.New("transfer size must not exceed 256 bytes") 191 } 192 193 cfg := &Config{ 194 RequestMAC: true, 195 ResponseMAC: true, 196 } 197 198 req := &DataFrame{ 199 Req: kind, 200 } 201 202 if kind == AuthenticatedDataWrite { 203 counter, err := p.Counter(true) 204 205 if err != nil { 206 return err 207 } 208 209 binary.BigEndian.PutUint32(req.WriteCounter[:], counter) 210 211 cfg.ResultRead = true 212 } else { 213 cfg.RandomNonce = true 214 } 215 216 binary.BigEndian.PutUint16(req.BlockCount[:], 1) 217 binary.BigEndian.PutUint16(req.Address[:], offset) 218 copy(req.Data[:], buf) 219 220 res, err := p.op(req, cfg) 221 222 if err != nil { 223 return 224 } 225 226 if kind == AuthenticatedDataRead { 227 copy(buf, res.Data[:]) 228 } else if res.Counter() != req.Counter()+1 { 229 return errors.New("write counter mismatch") 230 } 231 232 return 233 } 234 235 func rng(n int) []byte { 236 buf := make([]byte, n) 237 238 if _, err := rand.Read(buf); err != nil { 239 log.Fatal(err) 240 } 241 242 return buf 243 }