Julian Hall | e76ade8 | 2020-11-25 03:07:21 +0100 | [diff] [blame] | 1 | Service Access Protocols |
| 2 | ======================== |
| 3 | |
| 4 | A trusted service is accessed by calling service-specific methods via an RPC mechanism. The set of callable methods forms the |
| 5 | public interface exposed by a service. This section is concerned with interface conventions and protocols used for serializing |
| 6 | method parameters and return values. It is anticipated that there will be a need to support different parameter serialization |
| 7 | schemes to suite different needs. The project accommodates this with the following: |
| 8 | |
| 9 | - The Protocols directory structure allows for different protocol definitions for the same service. |
| 10 | - Message serialization code is decoupled from service provider code using an abstract 'serializer' interface. Alternative |
| 11 | concrete serializers may provide implementations of the interface. |
| 12 | |
| 13 | RPC Session |
| 14 | ----------- |
| 15 | |
| 16 | Before a client can call trusted service methods, an RPC session must be established where an association is made between an RPC |
| 17 | Caller and a call endpoint that corresponds to the required service provider instance. To establish the session, the client |
| 18 | must provide: |
| 19 | |
| 20 | - An identifier for the service provider instance. |
| 21 | - Any client credentials that allow RPC layer access control to be applied if needed. |
| 22 | |
| 23 | .. uml:: uml/RpcSessionClassDiagram.puml |
| 24 | |
| 25 | Once the RPC session is established, the client may call service methods via an abstract RPC Caller interface that takes the |
| 26 | following parameters: |
| 27 | |
| 28 | - The opcode that identifies the method to call. |
| 29 | - A buffer for the serialized method parameters. |
| 30 | - A buffer for the serialized return values. |
| 31 | |
| 32 | A deployment independent interface for locating services and establishing RPC sessions is described here: :ref:`Service Locator` |
| 33 | |
| 34 | Status Codes |
| 35 | ------------ |
| 36 | |
| 37 | On returning from a request to invoke a service method, two status codes are returned as follows: |
| 38 | |
| 39 | - *RPC status* - A generic status code that corresponds to the RPC call transaction. RPC status codes are standardized across |
| 40 | all services. |
| 41 | - *Operation status* - a service specific status code. |
| 42 | |
| 43 | Separation of status codes by layer allows service specific status codes to be accommodated while keeping RPC status codes |
| 44 | common. |
| 45 | |
| 46 | A client should only check the returned operation status if the returned RPC status value is RPC_CALL_ACCEPTED. All other RPC |
| 47 | status values indicate that an error occurred in delivering the RPC request. An RPC status of RPC_CALL_ACCEPTED does not |
| 48 | indicate that the service operation was successful. It merely indicates that the request was delivered, a suitable handler was |
| 49 | identified and the request parameters were understood. |
| 50 | |
| 51 | Service Access Protocol Definition Conventions |
| 52 | ---------------------------------------------- |
| 53 | |
| 54 | A service access protocol defines the following: |
| 55 | |
| 56 | - Opcodes used for identifying service methods. |
| 57 | - Request parameters for each method. |
| 58 | - Response parameters for method return values. |
| 59 | - Operation status code. |
| 60 | |
| 61 | Details of how public interface definition files for trusted services are organized, see: :ref:`Project Structure` |
| 62 | |
| 63 | It is possible that for certain deployments, it will be necessary to customize which parameter encoding scheme is used. Many |
| 64 | schemes are possible such as Protocol Buffers, CBOR, JSON, TLV, TPM commands or packed C structures. To make scheme |
| 65 | customization straight forward, serilize/deserialize operations should be encapsulated behind a common interface to decouple |
| 66 | service provider code from any particular serialization scheme. A section below describes a pattern for achieving this. |
| 67 | |
| 68 | Service Namespace |
| 69 | ''''''''''''''''' |
| 70 | |
| 71 | Definitions that form a service access protocol should live within a namespace that is unique for the particular service. Using |
| 72 | a namespace for service definitions avoids possible clashes between similarly named definitions that belong to different |
| 73 | services. How the namespace is implemented depends on how the access protocol is defined. For example, the Protocol Buffers |
| 74 | definitions for the crypto service all live within the ts_crypto package. The recommended convention for forming a trusted |
| 75 | service namespace is as follows:: |
| 76 | |
| 77 | ts_<service_name> |
| 78 | |
| 79 | e.g. |
| 80 | ts_crypto |
| 81 | ts_secure_storage |
| 82 | |
| 83 | Language Independent Protocol Definitions |
| 84 | ''''''''''''''''''''''''''''''''''''''''' |
| 85 | |
| 86 | By defining service access protocols using an interface description language (IDL) with good support for different programming |
| 87 | languages, it should be straight forward to access trusted services from clients written in a range of languages. On Arm |
| 88 | Cortex-A deployments, it is common for user applications to be implemented using a range of languages such as Go, Python or |
| 89 | Java. Rather than relying on a binding to a C client library, native client code may be generated from the formal protocol |
| 90 | definition files. Initial protocol definitions use Google Protocol Buffers as the IDL. The project structure allows for use of |
| 91 | alternative definition schemes and serializations. |
| 92 | |
| 93 | Opcode Definition |
| 94 | ````````````````` |
| 95 | |
| 96 | Opcodes are integer values that identify methods implemented by a service endpoint. Opcodes only need to be unique within the |
| 97 | scope of a particular service. The mapping of opcode to method is an important part of a service interface definition and |
| 98 | should be readily available to clients written in a variety of programming languages. For a Protocol Buffers based definition, |
| 99 | opcodes are defined in a file called:: |
| 100 | |
| 101 | opcodes.proto |
| 102 | |
| 103 | For example, for the Crypto trusted service, the Protocol Buffers opcode definitions are in:: |
| 104 | |
| 105 | protocols/service/crypto/protobuf/opcodes.proto |
| 106 | |
| 107 | Alternative definitions for light-weight C clients using the packed-c scheme are in:: |
| 108 | |
| 109 | protocols/service/crypto/packed-c/opcodes.h |
| 110 | |
| 111 | Parameter Definition |
| 112 | ```````````````````` |
| 113 | |
| 114 | The convention used for serializing method parameters and return values may be specific to a particular service. The definition |
| 115 | file will include message definitions for both request and response parameters. Common objects that are used for multiple |
| 116 | methods should be defined in separate files. When using Protobufs, the following naming convention for method parameter files |
| 117 | should be used:: |
| 118 | |
| 119 | <method_name>.proto |
| 120 | |
| 121 | For example, the Crypto export_public_key method is defined in a file called:: |
| 122 | |
| 123 | protocols/service/crypto/protobuf/export_public_key.proto |
| 124 | |
| 125 | RPC Status Codes |
| 126 | ```````````````` |
| 127 | |
| 128 | Generic RPC status code definitions using different definition schemes are defined here:: |
| 129 | |
| 130 | protocols/rpc/common/protobuf/status.proto |
| 131 | protocols/rpc/common/packed-c/status.h |
| 132 | |
| 133 | Service Status Codes |
| 134 | ```````````````````` |
| 135 | |
| 136 | Service specific status code definitions using different definition schemes are defined here (using crypto service as an |
| 137 | example):: |
| 138 | |
| 139 | protocols/service/crypto/protobuf/status.proto |
| 140 | protocols/service/crypto/packed-c/status.h |
| 141 | |
| 142 | Status code definitions may also be shared between services. For example, services that conform to PSA API conventions will use |
| 143 | standardized PSA status codes, defined here:: |
| 144 | |
| 145 | protocols/service/psa/protobuf/status.proto |
| 146 | protocols/service/psa/packed-c/status.h |
| 147 | |
| 148 | Use of Protocol Buffers |
| 149 | ----------------------- |
| 150 | |
| 151 | When Protocol Buffers is used for protocol definition and parameter serialization, the following conventions have been adopted. |
| 152 | |
| 153 | .proto File Style Guide |
| 154 | ''''''''''''''''''''''' |
| 155 | |
| 156 | The style of the .proto files should follow Google's Protocol Buffers Style Guide. |
| 157 | |
| 158 | Protocol Buffer Library for Trusted Services |
| 159 | '''''''''''''''''''''''''''''''''''''''''''' |
| 160 | |
| 161 | Protocol Buffers standardizes how service interfaces are defined and the on-wire encoding for messages. Because of this, service |
| 162 | clients and service providers are free to use any conformant implementation. However for trusted services that may be deployed |
| 163 | across a range of environments, some of which may be resource constrained, a lightweight library should be used for C/C++ code |
| 164 | that implement or use trusted services. For this purpose, Nanobp (https://github.com/nanopb/nanopb) should be used. |
| 165 | |
| 166 | Serialization Protocol Flexibility |
| 167 | ---------------------------------- |
| 168 | |
| 169 | Many different serialization protocols exist for encoding and decoding message parameters. Hard-wiring a particular protocol |
| 170 | into a trusted service provider implementation isn't desirable for the following reasons: |
| 171 | |
| 172 | - Depending on the complexity of serialization operations, mixing serialization logic with protocol-independent code makes |
| 173 | trusted service provider code bigger and more difficult to maintain. |
| 174 | - Different protocols may be needed for different deployments. It should be possible to make a build-time or even a |
| 175 | run-time selection of which protocol to use. |
| 176 | - The number of supported serializations protocols is likely to grow. Adding a new protocol shouldn't require you to make |
| 177 | extensive code changes and definitely shouldn't break support for existing protocols. |
| 178 | |
| 179 | These problems can be avoided by implementing protocol specific operations behind a common interface. Serialize/deserialize |
| 180 | operations will have the following pattern:: |
| 181 | |
| 182 | int serialize_for_method(msg_buffer *buf, in args...); |
| 183 | int deserialize_for_method(const msg_buffer *buf, out args...); |
| 184 | |
| 185 | To extend a service provider to support a new serialization encoding, the following steps are required: |
| 186 | |
| 187 | 1. Define a new encoding identifier string if a suitable one doesn't exist. Currently used identifiers are protobuf and |
| 188 | packed-c. The identifier will be used as a directory name so it needs to be filename-friendly. Some likely candidate |
| 189 | identifiers could be cbor and json. |
| 190 | 2. Add a new RPC encoding ID to *protocols/rpc/common/packed-c/encoding.h*. This is used by a caller to identify the encoding |
| 191 | used for RPC parameters. This is analogous to the content-type header parameter used in HTTP. |
| 192 | 3. Under the protocols parent directory, add a new access protocol definition for the service that needs extending. This will |
| 193 | be a representation of existing service access protocols but using a definition notation compatible with the new encoding. |
| 194 | 4. Add a new serializer implementation under the service provider's serializer directory e.g. for the crypto service - |
| 195 | *components/service/crypto/provider/serializer*. |
| 196 | 5. Add registration of the new serializer to any deployment initialization code where the new encoding is needed. |
| 197 | |
| 198 | -------------- |
| 199 | |
| 200 | *Copyright (c) 2020-2021, Arm Limited and Contributors. All rights reserved.* |
| 201 | |
| 202 | SPDX-License-Identifier: BSD-3-Clause |