diff --git a/implementations/main.cpp b/implementations/main.cpp index 2b71d38..b3b2ce2 100644 --- a/implementations/main.cpp +++ b/implementations/main.cpp @@ -499,6 +499,43 @@ void ServeRpmb(std::span sb) { // collects nblocks * 512 back. if (SecurityProtocolRetry(fd, false, frames, rp::FrameSize) == 0) rc = SecurityProtocolRetry(fd, true, frames, total); + } else { + // The authenticated write sequence, per chunk: the data frames out, a + // Result Read Request out, the result frame back. + // + // A remainder is refused rather than partially committed. The + // reference silently drops one, which would leave the store + // inconsistent with a counter that cannot be moved back. + auto plan = rp::PlanChunks(req->nblocks, req->blocksPerOp); + if (!plan.exact) { + std::println(" rpmb: {} blocks is not a whole number of {}-block chunks" + " -- refusing", req->nblocks, req->blocksPerOp); + } else { + std::array rrq{}; + rp::BuildResultReadRequest(rrq); + std::array result{}; + rc = 0; + for (std::uint32_t k = 0; k < plan.chunks && rc == 0; k++) { + std::byte* chunk = frames + static_cast(k) + * req->blocksPerOp * rp::FrameSize; + std::uint32_t bytes = req->blocksPerOp + * static_cast(rp::FrameSize); + if (SecurityProtocolRetry(fd, false, chunk, bytes) != 0 || + SecurityProtocolRetry(fd, false, rrq.data(), rp::FrameSize) != 0 || + SecurityProtocolRetry(fd, true, result.data(), rp::FrameSize) != 0) { + rc = -1; + break; + } + std::uint16_t res = rp::ResultOf(result); + std::println(" rpmb write chunk {}/{}: result=0x{:04x} ({}) counter={}", + k + 1, plan.chunks, res, rp::ResultString(res), + rp::WriteCounterOf(result)); + // Anything non-zero aborts rather than continuing into further + // chunks: the device rejected the frame and the counter state + // is not what we think it is. + if (res != rp::ResultOk) rc = -1; + } + } } ::close(fd);