You should prefer state-of-the-art elliptic curve systems to RSA and finite field DH.
1,326 karma · joined July 6, 2009
You should prefer state-of-the-art elliptic curve systems to RSA and finite field DH.
Also, I would put AES side channels somewhat low on the list of practical vulnerabilities.
I don't really understand the self-driving car analogy. A better analogy would probably be three cars hitched together.
The best attack on this scheme (in a purely offline scenario) is probably just cracking weak passwords, just like it would have been with only one encryption pass.
EDIT: One more point. The FAQ says something about plans for a future streaming API. Please do not do this, at least for the decrypt operation. Streaming decryption APIs expose application developers to not-yet-authenticated plaintext.
EDIT EDIT: I think it's an API mistake to leave key generation in the hands of the user, especially by using a PBKDF. PBKDFs are a measure of last resort. If you can just generate key material randomly, you should do so.
MAC(K, M) -> T
To validate a pair (M, T), we verify: T = MAC(K, M)
Ideally, the execution time in this verification is independent of T. But many languages use string-comparison algorithms that exit immediately on failure. If Eve can detect this difference with high granularity, she has an oracle telling her how many leading bytes of a guessed tag T' are valid: O(M, T') -> n
She can use this to recover the first byte of a valid tag for M: for k in [0, 255]:
if O(M, k || 0000...) > 0:
return k
She can extend this to recover the second byte, the third, and so on. Eventually, Eve will recover T such that MAC(K, M) = T. In other words, Eve is able to forge an authentication tag T for an arbitrary message M.What she won't do is recover K. So while she can forge tags for arbitrary messages, each forgery will require a fresh, online interaction with the verifying party. She cannot work backwards from (M, T) to K.
No.
EDIT: I've been told to elaborate.
This is just direct fallout from the way Merkle-Damgard hash functions work. MD hash functions look like this:
function MD(C, h, M):
M <- pad(M)
for m in blocks(M):
h <- C(h, m)
return h
The core of an MD hash is the compression function C, which takes an input state h and a message block m and produces a new state. When you find a collision in an MD hash function, you're really finding a collision in C. That is, some state h and some pair of message blocks m1 and m2 such that C(h, m1) = C(h, m2).Now, HMAC. HMAC is defined over a hash function, and it looks like this:
function HMAC(H, k, M):
k1 <- ipad ^ k
k2 <- opad ^ k
return H(k2 || H(k1 || M))
Where "||" is the concatenation operator. A simplified view of HMAC is that the key is used to scramble the initial state of H. This prevents the attacker from finding collisions on the inner hash function, because they don't know any interior state h. (The outer hash prevents length-extension attacks.)Which makes this article pretty silly: the whole point of HMAC is that the attacker doesn't get to know interior hash states. It pretty much goes without saying that if a) an attacker can find collisions on H for arbitrary states h and b) they know some interior state h of the HMAC, then it follows directly that they can find collisions on the HMAC.
Hey, want to see some more practical "attacks" on HMAC? Here we go!
In [11]: HMAC(b'\x00', b'chili dog', sha256).hexdigest()
Out[11]: '3128a12ed7dd6cd4ad9d2720126698d1c0c3e2d991ddbf73ebbecd947581acd9'
In [12]: HMAC(b'\x00\x00', b'chili dog', sha256).hexdigest()
Out[12]: '3128a12ed7dd6cd4ad9d2720126698d1c0c3e2d991ddbf73ebbecd947581acd9'
Whoaaa, what?! Again! In [17]: HMAC(sha256(b'A'*500).digest(), b'chili dog', sha256).hexdigest()
Out[17]: '426b6ec9b0e6b5fae9d12924432c023e8bc3444aa3adc4c48fdc875357c54c2f'
In [18]: HMAC(b'A'*500, b'chili dog', sha256).hexdigest()
Out[18]: '426b6ec9b0e6b5fae9d12924432c023e8bc3444aa3adc4c48fdc875357c54c2f'
Oh nooo! HMAC!Consider the original protocol: server sends a t-bit nonce N, client signs it.
Suppose that Eve can passively observe Alice's authentications to Bob's server, but she can't actively MitM the connection. Her best generic attack is a birthday attack:
1. Eve observes 2^b authentications and remembers each (N, S(N)) pair.
2. Eve attempts to authenticate as Alice. If Bob sends an N she has seen before, she's in. If he sends a fresh N, she aborts and tries again.
Eve will succeed in step 2 after roughly 2^(t-b) attempts.
Now consider the amended protocol. In each authentication, Eve observes two signatures: S(R) and S(R^N). Each is a signature over what is essentially a random t-bit number. This allows Eve to improve her attack:
1. Eve observes 2^b authentications. She stores two (X, S(X)) pairs per authentication: one for X = R and one for X = R^N. The size of the set is 2^(b+1).
2. Eve attempts to authenticate as Alice. Bob sends N. For each X in her set of observations, Eve calculates X' = X^N. If X' is also in the set, Eve wins. If not, she aborts and tries again.
Notice that Eve is now colliding N with the set of (X, X') pairs. Since this set grows with the square of Alice's authentication attempts, Eve will succeed after roughly 2^(t-2(b+1)) attempts.
This attack is plausible for sufficiently small t (say, 64) and sufficiently large b (say, 16-20). While b will be very small in web login scenarios, large b may be plausible in automated authentication scenarios.
This can obviously be defeated by choosing a sufficiently large t.
EDIT: (I'm not 100% certain about all the math. Corrections are welcome.)
To prevent this, Alice must also sign the domain she's authenticating to. The article mentioned this, and maybe you had meant it to be implicit in your protocol, in which case: my bad.
A E B
-hello->
-hello->
<-nonce-
<-nonce-
-random->
-S(rand)>
-S(n^r)->
-random->
-S(rand)>
-S(n^r)->The unpredictability of DH output depends on the group parameters and public key validation performed by the peer. If the group contains small subgroups, Eve can send an element of small order and predict the DH output with high probability.
For example, this is one of the attack vectors for the triple handshake attack on TLS: https://www.secure-resumption.com/tlsauth.pdf.
Vlad Krasnov is also a co-author of this paper on state-of-the-art P256 implementation: https://eprint.iacr.org/2013/816.pdf.
First, as some have noted, serious crypto primitive implementations are written in assembly. This is both to achieve state-of-the-art performance as well as data-independent execution times. The latter is important to prevent timing attacks.
Second: I'm not sure if this was your point, but some have invoked Heartbleed and other native code disasters. But the kind of problems that lead to Heartbleed aren't likely to be a problem in low-level crypto implementations. This is because they tend to operate on fixed-size buffers using algorithms with little or no conditional logic. While there could certainly be mathematical flaws (i.e. producing the wrong output), something like a buffer overrun is not likely here.
If you look in basically any crypto library, you will find important primitives implemented in assembly. This is even true in the main Go repository, where AES is implemented in assembly.
What do you recommend? Autotools, bespoke shell scripts, or something else entirely?
I'm not sure what the best answer is to your second question. I wouldn't use TC for that. I would typically use PGP, but I don't think it has a good UX for general consumption. There may be room for someone to build a better product.
My professional opinion is my own. The developer's opinion is also relevant (to people who don't buy into nonsensical conspiracy theories), so I mentioned it.
Again, this is just my perception.