|
| 1 | +\documentclass{article} |
| 2 | +\usepackage[utf8]{inputenc} |
| 3 | +\usepackage{amsmath,amssymb} |
| 4 | +\usepackage{hyperref} |
| 5 | +\usepackage{xurl} |
| 6 | + |
| 7 | +\title{Serai BTC code audit} |
| 8 | +\author{Cypher Stack\thanks{\url{https://cypherstack.com}}} |
| 9 | +\date{\today} |
| 10 | + |
| 11 | +\begin{document} |
| 12 | + |
| 13 | +\maketitle |
| 14 | + |
| 15 | +This report describes the findings of a partial code audit of Serai. |
| 16 | +It reflects a limited scope provided by Serai and represents a best effort; as with any review or audit, it cannot guarantee that any protocol or implementation is suitably secure for a particular use case, nor that the contents of this report reflect all issues or vulnerabilities that may exist. |
| 17 | +The author asserts no warranty and disclaims liability for its use. |
| 18 | +The author further expresses no endorsement of Serai or its associated entities. |
| 19 | +This report has not undergone any further formal or peer review. |
| 20 | + |
| 21 | + |
| 22 | +\tableofcontents |
| 23 | + |
| 24 | + |
| 25 | +\section{Overview} |
| 26 | + |
| 27 | + |
| 28 | +\subsection{Introduction} |
| 29 | + |
| 30 | +Serai is a distributed exchange protocol and Rust implementation. |
| 31 | +This report represents an audit of specific areas of underlying cryptographic libraries and functionality in the Serai codebase. |
| 32 | + |
| 33 | +Serai and Cypher Stack identified the following goals for this audit: |
| 34 | +\begin{itemize} |
| 35 | +\item Assert that implementations of external specifications or protocols are done correctly |
| 36 | +\item Determine if malicious or unintended edge cases can be exploited |
| 37 | +\item Identify cases where the code may panic unexpectedly |
| 38 | +\item Note situations where documentation is insufficient to convey intent or design |
| 39 | +\item Ensure that secret data is handled as safely as is feasible with respect to memory clearing and constant-time operations |
| 40 | +\item Identify areas of the implementation where efficiency gains are possible and reasonable |
| 41 | +\item Determine the extent to which the implementation contains relevant and comprehensive tests |
| 42 | +\end{itemize} |
| 43 | + |
| 44 | + |
| 45 | +\subsection{Summary of findings} |
| 46 | + |
| 47 | +As with a previous audit of Serai code, we find that the implementation is well designed and carefully written with secure deployment in mind. |
| 48 | +We did not identify any findings of particular immediate concern. |
| 49 | + |
| 50 | +Consequently, the findings described in this report primarily identify recommendations for improvement. |
| 51 | +These deal with inconsistencies, documentation, tests, or areas of the codebase where fixes or changes could mitigate future issues. |
| 52 | + |
| 53 | +However, we carefully note that we do not assign severity ratings to the issues contained herein. |
| 54 | +Such ratings are often subjective, and typically depend on the ease or difficulty of triggering specific behavior, which can be challenging to assess in a consistent way. |
| 55 | + |
| 56 | + |
| 57 | +\section{Scope} |
| 58 | + |
| 59 | +The scope of this audit was limited to the \texttt{coins/bitcoin} directory of the \texttt{bitcoin-audit} branch of the Serai repository at a specific commit\footnote{\url{https://github.com/serai-dex/serai/tree/21026136bd0c7f341ae93f08e6a0bb15fb9b250f}}. |
| 60 | + |
| 61 | +Review of this directory entailed determining if it implements Serai- and Bitcoin-related cryptographic code correctly, and if it presents transaction-related issues as part of its implementation. |
| 62 | + |
| 63 | + |
| 64 | +\section{Findings} |
| 65 | + |
| 66 | +In the subsequent findings, we include actions taken and responses provided by Serai in response to an initial version of this report. |
| 67 | +Where listed, commits based on these actions refer to the \texttt{bitcoin-audit} repository branch, and function as hyperlinks if supported by your document reader software. |
| 68 | + |
| 69 | + |
| 70 | +\subsection{Unnecessary HTTP client creation} |
| 71 | + |
| 72 | +When making an RPC call, the implementation creates a new HTTP \texttt{Client} for the call. |
| 73 | +This seems unnecessary and could be inefficient, as sharing a single client across multiple requests could allow for connection pooling if supported. |
| 74 | + |
| 75 | +Recommended fix is to refactor in order to reuse a single \texttt{Client} if feasible. |
| 76 | + |
| 77 | +\textbf{Action:} Addressed in commit \href{https://github.com/serai-dex/serai/commit/c878d38c606e9add77ac1d1a058060accd1a51c8}{\texttt{c878d38}}, which refactors to reuse a \texttt{Client} within an \texttt{Rpc} struct. |
| 78 | + |
| 79 | + |
| 80 | +\subsection{Lack of HTTP error differentiation} |
| 81 | + |
| 82 | +When making RPC calls and receiving responses, there is no differentiation between errors returned during the associated HTTP request and those returned on receipt of the response. |
| 83 | +It may be useful to either propagate detailed error information for debugging or handling purposes, or to simply define distinct request and response errors. |
| 84 | + |
| 85 | +Recommended fix is to return differentiated errors, unless the single error is intentional to mitigate side channels. |
| 86 | + |
| 87 | +\textbf{Action:} Determined to be unnecessary due to a desire not to rely on library-specific error handling. |
| 88 | + |
| 89 | + |
| 90 | +\subsection{Connections are checked using block information} |
| 91 | + |
| 92 | +When creating a new RPC client, the connection is checked for validity by fetching the latest block number from the server and discarding the result. |
| 93 | +However, failure of this process may not adequately differentate to the user between connectivity problems and internal node status, such as being online but not fully synchronized to the Bitcoin network. |
| 94 | + |
| 95 | +Recommended fix is to consider a more robust RPC server and node health check, if feasible. |
| 96 | + |
| 97 | +\textbf{Action:} Addressed in commit \href{https://github.com/serai-dex/serai/commit/7fa5d291b8c60da1c26525b45f02610180bc90c8}{\texttt{7fa5d29}}, which queries for support of a set of required methods. |
| 98 | + |
| 99 | + |
| 100 | +\subsection{Lack of error differentiation on decoding} |
| 101 | + |
| 102 | +When decoding response data, the implementation often generically maps various decoding errors to a single \texttt{InvalidResponse} error. |
| 103 | +This may not properly capture the difference between high-level encoding errors and lower-level errors within responses, which could hinder debugging. |
| 104 | + |
| 105 | +Recommended fix is to consider more error differentiation on response decoding. |
| 106 | + |
| 107 | +\textbf{Action:} Addressed in commit \href{https://github.com/serai-dex/serai/commit/3480fc5e166d5d4c2b7cfcbfe086534997857770}{\texttt{3480fc5}}, which includes more fine-grained error reporting. |
| 108 | + |
| 109 | + |
| 110 | +\subsection{Assumption of available RPC methods} |
| 111 | + |
| 112 | +The implementation assumes that the \texttt{getblock}, \texttt{sendrawtransaction}, and \texttt{getrawtransaction} RPC calls are enabled and available on the server node. |
| 113 | +This is not guaranteed, and depends on the node configuration. |
| 114 | +Relying on generic failure responses may be insufficient to determine these capabilities. |
| 115 | + |
| 116 | +Recommended fix is to consider testing for call availability when instantiating the RPC client. |
| 117 | + |
| 118 | +\textbf{Action:} Addressed in commit \href{https://github.com/serai-dex/serai/commit/7fa5d291b8c60da1c26525b45f02610180bc90c8}{\texttt{7fa5d29}}, which queries for support of a set of required methods. |
| 119 | + |
| 120 | + |
| 121 | +\subsection{Use of \texttt{unwrap} in production} |
| 122 | + |
| 123 | +The implementation uses \texttt{unwrap} in non-test production code where logic indicates a result must be valid. |
| 124 | +The use of \texttt{unwrap} in this way is largely a matter of developer preference, and is not inherently dangerous or unsafe. |
| 125 | +However, when it is used, it may be preferable to use \texttt{expect} instead, in order to admit more controlled and actionable debugging messages. |
| 126 | +This can also help to avoid edge cases where non-public data could be leaked during an \texttt{unwrap} execution. |
| 127 | + |
| 128 | +Recommended fix is to consider uses of non-test \texttt{unwrap}, and further consider replacing with \texttt{expect} where helpful. |
| 129 | + |
| 130 | +\textbf{Action:} Addressed in commits \href{https://github.com/serai-dex/serai/commit/d75115ce1396949e17c87be6ffab886c2ee5a975}{\texttt{d75115c}} and \href{https://github.com/serai-dex/serai/commit/677b9b681f5e2819fe34599beb0947adc22afabd}{\texttt{677b9b6}}, with remaining uses deemed safe. |
| 131 | + |
| 132 | + |
| 133 | +\subsection{Safe but unbounded loop in offset registration} |
| 134 | + |
| 135 | +When constructing an address using an offset, the implementation increments the candidate offset and attempts to generate a valid address with it, additionally checking that the offset is not already registered. |
| 136 | + |
| 137 | +Assuming that the address generation callee operates correctly, the loop is guaranteed to terminate since incrementing must yield a group element corresponding to a valid address. |
| 138 | +If the callee were to malfunction (which is not an assumption here), it could be possible that the loop does not terminate. |
| 139 | + |
| 140 | +Recommendation is to add a brief comment explaining why the loop must terminate. |
| 141 | + |
| 142 | +\textbf{Action:} Addressed in commit \href{https://github.com/serai-dex/serai/commit/fa1b569b78890cc5edb22dce91f08acd64cef854}{\texttt{fa1b569}}, which adds such a comment. |
| 143 | + |
| 144 | + |
| 145 | +\subsection{Transaction scanning uses a fallible cast} |
| 146 | + |
| 147 | +When scanning a transaction, the implementation iterates over its outputs and casts the output index from \texttt{usize} to \texttt{u32} using an \texttt{unwrap}. |
| 148 | +While it should not be possible to construct a valid transaction with such a structure that could trigger a panic, it is not clear whether a malicious node could trigger it in a way that is not caught by the parser. |
| 149 | + |
| 150 | +Recommendation is to check for this case and simply fail to include any failure cases in the returned vector of received outputs. |
| 151 | + |
| 152 | +\textbf{Action:} Addressed in commit \href{https://github.com/serai-dex/serai/commit/677b9b681f5e2819fe34599beb0947adc22afabd}{\texttt{677b9b6}}, which performs a check. |
| 153 | + |
| 154 | + |
| 155 | +\subsection{Transaction machine instantiation uses a fallible cast} |
| 156 | + |
| 157 | +When setting up a transaction machine for a signable transaction, the implementation iterates over the transaction's inputs and casts the input index from \texttt{usize} to \texttt{u32} using an \texttt{unwrap}. |
| 158 | +While it should not be possible to construct a valid transaction with such a structure that could trigger a panic, it is not clear whether a malicious node could trigger it in a way that is not caught by the parser. |
| 159 | + |
| 160 | +Recommendation is to check for this case and simply fail to produce the transaction machine if it is triggered. |
| 161 | + |
| 162 | +\textbf{Action:} Addressed in commit \href{https://github.com/serai-dex/serai/commit/677b9b681f5e2819fe34599beb0947adc22afabd}{\texttt{677b9b6}}, which performs a check. |
| 163 | + |
| 164 | + |
| 165 | +\subsection{Offset addresses use a hardcoded network} |
| 166 | + |
| 167 | +When registering address offsets, the implementation generates the addresses using a hardcoded network. |
| 168 | +This may limit functionality for use in other networks, especially for test cases where inadvertent use of a production network could be dangerous or risk fund loss. |
| 169 | + |
| 170 | +\textbf{Action:} Addressed in commit \href{https://github.com/serai-dex/serai/commit/f66fe3c1cb56f90f9d1f1ba8c4d2ff4dc6fa9768}{\texttt{f66fe3c}}, which generalizes networks. |
| 171 | + |
| 172 | + |
| 173 | +\subsection{Address generation assumes correct generation} |
| 174 | + |
| 175 | +When generating a Taproot address from a key, the implementations uses library functions that assume the key has undergone any necessary ``tweak'' to ensure safe use in practice. |
| 176 | +While the implementation uses it safely, the generator function itself is public and does not include documentation indicating what preparation (if any) should be done to the input key. |
| 177 | + |
| 178 | +Recommendation is to improve the documentation for Taproot address generation, and consider limiting the function visibility if feasible to avoid unintentional misuse. |
| 179 | + |
| 180 | +\textbf{Action:} Addressed in commit \href{https://github.com/serai-dex/serai/commit/6f9d02fdf83e426b0a4a0df9c851de5911067a5e}{\texttt{6f9d02f}}, which adds such comments. |
| 181 | + |
| 182 | + |
| 183 | +\subsection{Offsets and addresses are not bijective} |
| 184 | + |
| 185 | +When constructing an offset address, the implementation attempts to apply the requested scalar offset, incrementing if necessary to produce an unused valid address. |
| 186 | +This design implies that there is a many-to-one mapping between offsets and their corresponding addresses. |
| 187 | +While this is not inherently unsafe or incorrect, even with the existing documentation, a caller might unintentionally make this assumption as part of a future design or protocol. |
| 188 | + |
| 189 | +Recommendation is to improve the documentation relating to this property. |
| 190 | + |
| 191 | +\textbf{Action:} Addressed in commit \href{https://github.com/serai-dex/serai/commit/df67b7d94c56ef8f75994e02885bb5c78314ef13}{\texttt{df67b7d}}, which adds such comments. |
| 192 | + |
| 193 | + |
| 194 | +\subsection{Dust constant is incorrect} |
| 195 | + |
| 196 | +The implementation checks candidate output data to ensure that values are above a constant dust limit that would cause nodes not to relay the resulting transaction. |
| 197 | + |
| 198 | +This constant is set to 674, with a link to external Bitcoin transaction test source code. |
| 199 | +However, this reference test appears to be a non-standard case that uses a custom fee rate. |
| 200 | +Closer examination of the transaction tests and associated constants indicates that nodes using the default relay fee settings will use a dust limit value of 546. |
| 201 | +It may be the case that the implementation's higher limit is intentional, as the reference test discusses the effects of rounding. |
| 202 | + |
| 203 | +Recommendation is to determine if the dust constant is set as expected, and to change it if not. |
| 204 | + |
| 205 | +\textbf{Action:} Addressed in commits \href{https://github.com/serai-dex/serai/commit/1eb3b364f46e003b92baed89dcdef3e4878c79aa}{\texttt{1eb3b36}} and \href{https://github.com/serai-dex/serai/commit/5121ca75199dff7bd34230880a1fdd793012068c}{\texttt{5121ca7}}, which address the handling of fees. |
| 206 | + |
| 207 | + |
| 208 | +\end{document} |
0 commit comments