4/16/2014 - 10:13 AM

Tptacek's Review of "Practical Cryptography With Go"

Tptacek's Review of "Practical Cryptography With Go"

Wow. I've now read the whole book and much of the supporting code. I'm not a fan, and recommend against relying on it. Here's a laundry list of concerns:

  • The teaching method the book uses is badly flawed. The book's strategy is to start simple and build to complexity, which makes sense if you're teaching algebra but not if you're teaching heart surgery. The result is that each chapter culminates with the implementation of a system that is grievously insecure. Little warning is given of this, apart from allusions to future chapters improving the system. For instance, Chapter 2 closes with a chat system that uses AES-CBC without an authenticator.

  • The book is full of idiosyncratic recommendations. For instance, AES-CBC requires a padding scheme. There is a standard padding scheme. The book purports to present it, but instead of PKCS7, it presents 80h+00h..00h.

  • At one point about 1/3rd of the way through the book, it suggests using a SHA256 hash of the plaintext as an authenticator for a message. This recommendation is doubly erroneous: it confuses hash functions with MACs, and reverses the correct order of operations. Once again, the book acknowledges a "better" way of authenticating messages, but (a) doesn't warn the reader that the "worse" way is in fact broken and (b) doesn't spell out the "better" way until later.

  • The book pointlessly allocates several paragraphs to "salted hashing", before a chapter-length consideration of adaptive hashing. I'll use this point as a setting for a broader critique: the book is constantly making recommendations without complete context. Presumably the author knows the attack salts guard against, and the ways password hashes are actually attacked. But he's too eager to get the reader writing code, so instead of explanation, there's a blob of Golang code, in this case for a primitive nobody needs.

  • Total undue reverence for NIST and FIPS standards; for instance, the book recommends PBKDF2 over bcrypt and scrypt (amusingly: the book actually recommends *against * scrypt, which is too new for it) because it's standardized. If you're explaining crypto to a reader, and you're at the point where you're discussing KDFs, you'd think there'd be cause to explain that PBKDF2 is actually the weakest of those 3 KDFs, and why. Again, there's an opportunity for explanation and context, but instead: get some Golang code into the editor and move on!

  • The book suggests that there's a timing attack against password hash comparison. Doing a secure compare on password hashes isn't bad, per se, but the notion that password hash checks leak usable information puts into question whether the author actually understands timing attacks.

  • The code for secure compare is silly: the author writes their own loop using ConstantTimeByteEq, leaving the reader to wonder, "is Golang so obtuse that they provide a 'constant time byte equal' library routine, but not a 'constant time array equal'?" The answer is no, Golang is not that obtuse; the author has just missed the library function that safely compares arrays.

  • The book spends a huge amount of time on a password authentication protocol using random challenges, and even revisits it in a later chapter. The protocol is: S->C nonce, C->S HASH(pw, nonce), HEAD->DESK smash. "There's an even better way to authenticate users", the book suggests, but nowhere in this book are password-based key exchanged discussed. The book recommends that cryptographically-random nonces be used (believing that the size of the nonce is the big security issue with that protocol), but also that they be stored in a ledger to prevent reuse. It's especially painful that this chapter *follows * the chapter on password hashing, but does not avail itself of password hashing.

  • MACs aren't "keyed hashes", or for that matter a "different kind of hash".

  • The book, recommending a dubiously justified strategy of "fail as late as possible to avoid leaking information", adds (more than halfway through the book!) a MAC to an AES-CTR message, and in showing how to decrypt the message, checks the MAC and decrypts the message oblivious to the MAC check. Argh! It happens that in Golang, presuming your library doesn't use panic(), this mistake won't blow you up. But in most other languages, that pattern is extremely unsafe. Either way: the MAC fails, you chuck the message; you don't soldier on.

  • The book recommends (sort of) AES-CTR, but does not explain what a CTR nonce is; instead, it reuses the "GenerateIV" function it defined for CBC without comment. But CBC IVs and CTR nonces are not the same creature! Here, the conflation doesn't generate a security problem, but a reader following the CBC IV advice with their own code could get into extremely serious trouble using that for CTR.

  • The book *actively recommends * public key cryptography, because of concerns about key distribution. Again: bad strategy. Cryptographers use public key crypto only when absolutely required. Most settings for cryptography don't need it! Public key cryptography multiplies the number of things that can go wrong with your cryptosystem. You'd never know that to read this book; here, public key is like "cryptography 2.0".

  • In considering RSA, the book recommends /dev/random, despite having previously advised readers to avoid /dev/random in favor of /dev/urandom. The book was right the first time. Despite linking to a series of Internet posts about random vs. urandom (including my own), the second half of the book is choc-a-block full of references to "entropy pool depletion", as if that was a real thing (it is not). Later in the book, the author will go on to recommend an actual protocol change to avoid that fictitious problem.

  • The book recommends RSA, OAEP, and PSS without explaining prime numbers, or for that matter what OAEP and PSS are. This is especially painful because Golang happens to implement Rogaway's OAEP and PSS, unlike virtually all other languages, and so a reader learning crypto from this book and taking it to (say) Clojure is likely to end up with insecure RSA upon finding out that those modes aren't available. Once again: no context given, and a rush to get unexplained code onto the page.

  • A nit: the book spends a whole lot of time and screen space on a custom keychain implementation that is of no importance to security. It's understandable that one would take the time to document the boring supporting code that enables you to actually work with cryptography, but less clear why its details are scattered through the book and outweigh important concerns about security.

  • The book recommends ECDSA without explaining what ECDSA or, for that matter, DSA are. The book fails to point out the randomness requirement for DSA, presumably because Golang abstracted this away and the author was unaware of it. The randomness requirement for DSA is such a huge problem for DSA that it forms the centerpiece of Daniel Bernstein's critique of the NIST standards process. This is the bug that broke the PS3. It's discussed nowhere in the book; instead, the author benchmarks RSA against ECDSA, as if the result wasn't a foregone conclusion, and them moves on. Author: it's OK to just say "ECC is way faster than finite field crypto" and get on with important stuff.

  • "There is some concern over the NIST curves in cutting-edge research; however, these curves are still recommended for secure systems." Well, no: there is concern that the NIST curves are backdoored and should be disfavored and replaced with Curve25519 and curves of similar construction. Curve25519 was available to the author's Golang code, but isn't used. "There is also a growing movement of cryptographers positing a “cryptocalypse”. There is some research into factorisation of discrete log problems (which RSA and DH are based on)". Well, no: RSA isn't based on the DLP, for one thing; for another, my name is on that "growing movement", and it's been discredited, as the author goes on to reference but not acknowledge: "a good primer on developments in cryptography, including the cryptocalypse, can be found in the 30C3 talk" in which DJB demolishes the notion that Joux's index calculus improvements will have an impact on RSA.

  • The book writes its own Diffie-Hellman implementation and recommends it to readers.

  • The book gets the parameter checking in its Diffie-Hellman implementation wrong.

  • This book, I am not making this up, contains the string: "“We can use ASN.1 to make the format easier to parse".

  • The book contains a completely confused discussion of forward-secrecy. "If the long-term key is compromised, messages are still secure". An attacker, the book goes on, can use the compromised long-term key as an opportunity to perhaps get a client to leak information to it. No. The compromise of a long-term key in a forward-secret system is a hair-on-fire problem. Forward secrecy isn't magic pixie dust. The contact forward secrecy provides a design is that if the long-term key is compromised at point T, messages at points t < T can't retroactively be decrypted. That's all.

  • The book appears to actively recommend a protocol based on unauthenticated Diffie-Hellman, out of concern for performance. Here's a confusing snippet from the book: "This is why DH (including ECDH) is typically used in systems that require forward secrecy; it is computationally cost-prohibitive to generate new RSA keys”. Huh? Systems that use RSA don't generate ephemeral RSA keys. RSA key generation isn't the problem with RSA; the bignum operations involved in actually using an RSA key are.

  • The book has a chapter that tries to explain TLS, but rather than explaining the TLS handshake in detail (this is a book that is trying to teach readers how to design entire cryptographic transport protocols!), it spends several paragraphs on the X.509 metadata fields like "Country" and "Locality". Also, this is an author who has come into contact with ASN.1/DER and gone on to recommend the format to others.

  • The book closes with a short section on recommended primitives, such as AES-CTR over AES-CBC. But the book hasn't actually explained why it makes those recommendations. Instead, the benefits of the recommendations are implied by the structure of the book: if the author was talking about something early on, he presumes you understand that recommendation to have been bad. Schneier, in "Cryptography Engineering", closes his chapters with carefully written recommendations, most of which are correct. But more importantly, those recommendations come with restated and condensed justifications, and cohere with the earlier text on the primitives he's recommending.

  • Finally, the author of this book, having surveyed the crypto code he wrote, has decided that his abstractions over Golang pkg/crypto are superior to those of Golang pkg/crypto. So he's created and published his own high level library, using the constructions he's documented in this book. And he's called it... wait for it... CryptoBox. It's worth mentioning at this point that the book *at no point * discusses NaCl (which revolves around an abstracton named "crypto_box", which is the single most important recommendation a book on crypto could have made.