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  }