This is mostly good advice. I don't love configure scripts, I don't agree with the heavy reliance on POSIX if you intend to be compatible with Windows, and I don't love the fact that the author recommends third party data structure libraries that they haven't actually used. For container libraries in C, you really have to use them to get a feel for their usability (this sounds like a tautology but it's not.)
I disagree strongly with one recommendation. This is just an example, but it holds for larger API design in general:
> we could add a fallback to reading /dev/random. [...] However, in this case, the increased portability would require a change in interface. Since fopen() or fread() on /dev/random could fail, our function would need to return bool.
No, definitely not. It is dangerous to expect the application to sanely handle the case of randomness being unavailable when it is never going to occur in practice. On all POSIX platforms, /dev/random exists and will block until sufficient entropy is available. Something would have to go seriously wrong for this to fail. This is so rare that any error handling code for it will never be tested. The most likely outcome of forcing the caller to handle it is that the return value is ignored or improperly handled and the buffer is used uninitialized, leading to a security vulnerability.
My recommendation instead would be to error check your fopen() and fread() calls within get_random_bytes(), and print an error and abort() if they fail. This way if someone's system is improperly configured and /dev/random doesn't work the program will just crash. Same goes for macOS's SecRandomCopyBytes() and Windows' half a dozen calls to use an HCRYPTPROV. This way you still return void and there is no danger of callers improperly handling errors.
In general, unless you're writing safety-critical software, it's fine for your code (or even library code) to abort() in these sorts of exceptional situations when there is no reasonable or safe way to handle the error. If someone truly wants to handle the error, they can just not use your API and do it manually.
I disagree strongly with one recommendation. This is just an example, but it holds for larger API design in general:
> we could add a fallback to reading /dev/random. [...] However, in this case, the increased portability would require a change in interface. Since fopen() or fread() on /dev/random could fail, our function would need to return bool.
No, definitely not. It is dangerous to expect the application to sanely handle the case of randomness being unavailable when it is never going to occur in practice. On all POSIX platforms, /dev/random exists and will block until sufficient entropy is available. Something would have to go seriously wrong for this to fail. This is so rare that any error handling code for it will never be tested. The most likely outcome of forcing the caller to handle it is that the return value is ignored or improperly handled and the buffer is used uninitialized, leading to a security vulnerability.
My recommendation instead would be to error check your fopen() and fread() calls within get_random_bytes(), and print an error and abort() if they fail. This way if someone's system is improperly configured and /dev/random doesn't work the program will just crash. Same goes for macOS's SecRandomCopyBytes() and Windows' half a dozen calls to use an HCRYPTPROV. This way you still return void and there is no danger of callers improperly handling errors.
In general, unless you're writing safety-critical software, it's fine for your code (or even library code) to abort() in these sorts of exceptional situations when there is no reasonable or safe way to handle the error. If someone truly wants to handle the error, they can just not use your API and do it manually.