Erik Romijn

Crypto weaknesses in the Ectual energy meter

A while ago I was a mentor at the Apps for Energy hackathon, where energy company Enexis had a demo setup of their Ectual energy meter. A customer can hook this device up to their existing energy meter, and it’ll read out energy usage every 10 seconds and make that available to apps or other services.

10-second energy data of homes is very sensitive, as it can say a lot about the inhabitants and their lives. So security for this data is essential. Fortunately, all sensitive data is encrypted with AES-128-CGM. The key is derived from an app password set by the user, which also means that if they change their password, all old apps will instantly lose access. However, the crypto is significantly weaker than it could be: some of the choices in the crypto make it significantly easier to find the password. Although the actual viability of such an attack is difficult to determine, the margin of safety is awfully small, and there are missed opportunities that would make the design dramatically stronger.

How does the Ectual encrypt user data in transit?

The Ectual has two ways to communicate:

  • On the local network, in the home of the user, an app can connect directly to it and request data.
  • It can be configured to push data to a server anywhere on the internet every minute.

Either connection consists of two phases: mutual authentication with a challenge-response scheme, followed by data transfer encrypted by a session key. None of the communication is protected by TLS, so the custom encryption is the only layer of protection.

Both phases use AES-128-GCM. AES in GCM mode is very similar to the most common mode, CBC, but GCM adds authentication. In CBC mode, damaged or intentionally modified encrypted data can still be decrypted. With GCM, modified data would be rejected, which prevents numerous possible vulnerabilities. To enable this validation, AES-GCM encryption produces both a ciphertext and an authentication tag, the latter being needed to verify the data on decryption. There’s also an additional option: additional authenticated data (AAD). This is data that will not be encrypted, but will be authenticated. Like CBC, there is also an initialisation vector (IV), which must be random but does not have to be secret.

Here’s how the authentication works in the Ectual:

The P1-device is the Ectual. CHALL1 and CHALL2 are 128 random bits generated by either side. The RSP generation is an AES-GCM encryption where the key is the passkey, the IV is CHALL1, AAD is CHALL2 and the authentication tag is the RSP output. RSP2 follows the same method, with CHALL1 and CHALL2 reversed.

For this post we’re going to cover mostly push messages, because they have other interesting properties and are generally easier to intercept due to their wider reach. In push messages, the connection is initiated from the Ectual: in the diagram the sides are now reversed. The Ectual will also send a cleartext ID to the server in every request.

The passkey is the interesting part: this is a SHA-256 hash of the password configured by the user, trimmed to only use the first 128 bits of the hash.

Brute forcing the password

A reason why AES-128 is generally safe, is that it is prohibitively expensive to try all 2128 possible keys. However, this only applies when the keys are truly random. In the Ectual, the key is derived from the user’s password using SHA-256, a very fast hash function. Just my simple MacBook can generate millions of SHA-256 hashes per second. As the hashing is so fast, and user passwords have notoriously low entropy, it may become feasible to brute force the password.

Password brute forcing is not a new attack, and so most password authentication systems implement some kind of lock out: after a certain number of tries, the account or the source IP is locked for a certain amount of time. This makes brute forcing very slow. However, the Ectual provides us with enough data to brute force the user’s password offline, where lockouts do not apply. All we need is to intercept a few packets.

Let’s look at the initial exchange of an Ectual sending a push message to a server, which I intercepted from the demo setup at the hackathon. The Ectual connects and requests a session key creation:

ECTUAL-deviceID: ……

{"action": "sessionrequest"}

The server replies with a generated CHALL1, in base64:

{"action":"authchallenge",
 "challenge1":"iOctjgxChgsfW4xq2oWDFw=="}

The Ectual replies with CHALL2 and the RSP1 it generated. As this is derived from the passkey, which was not transmitted, this can prove that the Ectual knows the key:

ECTUAL-deviceID: ……

{"action": "authrequest",
 "response1": "nDnm7IKN6s5PkXhvNTCyCg==",
 "challenge2": "jRRJ6dDSpbpHNGzKWr6iGA=="}

The server replies with RSP2, derived from CHALL1, CHALL2 and the passkey, proving that it knows the passkey as well:

{"action":"authresponse",
 "response2":"iFRJmYUnKhnJtBaNayCkmQ=="}

All this happens in cleartext, and this exchange contains enough information to try to brute force the password offline, without either side taking notice. With the example above, we simply execute each attempt as:

attempted_passkey = SHA256(attempted_password)[:16]
cleartext, tag = AESGCM(
    key=attempted_passkey,
    iv=chall1,
    aad=chall2)
if tag == rsp1:
    print “Found: %s” % attempted_password

RSP1, CHALL1 and CHALL2 are available in the intercepted traffic. On my MacBook Air, I can perform about 60.000 attempts/s with this method, of which most time goes to the AES-GCM. That’s enough to find a random lowercase a-z password of six characters in 1.5 hours. However, on more specialised hardware or with more optimised code, this could be done much faster. Note that the Ectual does place higher requirements on passwords, but there is a catch that we’ll get to later.

As push messages are sent every minute, you need to intercept packets for just a minute on a victim’s wifi network to extract enough data to brute force their key later. Of course, this attack works just as well for apps on the local network pulling data directly from the Ectual.

The session key is derived from CHALL1, CHALL2 and the passkey, which is known after brute forcing. Therefore, once you know the password, that’s enough to decrypt all data captured in the past or future, as long as the user’s password was the same.

Finding the key without an MITM

The previous attack depends on intercepting the actual traffic between the Ectual and it’s server. But there’s another vector to consider.

In the push model, the Ectual first authenticates itself to the server, and then the server authenticates itself to the Ectual. Either side generates an RSP to do this, and the other side should verify it. Either RSP is sufficient to run the brute force. However, there is nothing in the API design that enforces verification of RSP1 by the server, and that means there’s a fair chance developers will skip that step. For example, this validation was missing in the push server that the demo setup used.

This opens up a much more accessible vector: if a server fails to validate the RSP1 from the Ectual, and simply replies with it’s own RSP2, that data is also sufficient to brute force the password. Now we can obtain sufficient data to brute force 60.000 passwords/s on a simple laptop, simply by knowing the ID of the Ectual and any one of the servers to which it sends it’s push messages, if these servers fail to do the RSP1 validation. Intercepting communication between the real Ectual and the server is then no longer needed. This is a useful scenario in case the user changes the password, for example, in which case we’ve already intercepted the Ectual ID previously. Perhaps there are also other ways to obtain the Ectual ID.

This attack depends on the server not validating RSP1. But everything works without that validation: the simplest way to implement a push receiver server is to skip this validation, making it a likely scenario.

Actual password strength

I mentioned about 60.000 attempts/s with my naive implementation, on a 4-core MacBook, which lets me find a random lowercase a-z password with six characters in 1.5 hours, with 266 possibilities. However, the password requirements on the Ectual are much higher:

  • By default, a 9 character password is generated, with mixed case letters and digits.
  • Users can change this password themselves, with the requirement to have at least 9 characters.
  • The password change process recommends using at least one uppercase character and one digit, and includes a password strength indicator.

If we would consider nine random characters, mixed case a-z and digits, there are 629 possible passwords. The time required to try them all, with my code on my laptop, increases to about 7000 years. That number seems sufficient even if you consider a few orders of magnitude speedup on optimised hardware running optimised code: although it could be improved, it certainly seems like a serious amount of effort, which would still only let you decrypt data from a single Ectual device.

If we consider only lowercase characters, the minimum custom password that would meet the requirements, there are 269 options, taking about three years on my laptop with my code. That’s certainly still an awfully long time, but it also means that the margin of safety is rather small: if the brute forcing can be optimised to be 100 times as fast, it would complete in a mere 10 days.

However, even those numbers are still too optimistic. The custom passwords would be entered by users, who are notoriously awful at generating random passwords. Many password will likely be derived from dictionary words, for example, which are allowed. So the actual space of possibilities is much smaller.

How small is it, then? And more important, is it so small that brute forcing, even with these password requirements, is a viable option? I don’t know. It’s a well known fact that without password requirements, the chosen passwords would be so trivial that I could brute force them on my phone. But with the length requirement, I don’t have data on the actual entropy of these passwords. I also don’t know how many users will stick to the initial higher entropy password, and how far the brute forcing process can be optimised. Therefore, I can’t answer these questions. However, it does appear the margin of safety is uncomfortably small.

A few ideas for improving the Ectual’s security

Although I can’t prove how viable a brute force actually is, comparing the cost and benefits, there are still significant opportunities to improve. I’m certainly not comfortable with the margin of safety that is left here. And with any of a few simple measures, we can draw the effort into the astronomical numbers.

Better suited hash functions

SHA-256 is a good choice for many situations, but password hashing is not one of them: it’s too fast, allowing fast brute forcing. Several hashes have been developed specifically for passwords, like PBKDF2 and bcrypt, which are intentionally and configurably slow. The point is that a user won’t notice checking their password takes 100 miliseconds, but it will be a serious obstacle to anyone trying to brute force. These algorithms are also designed to be hard to optimise. It would reduce my 60.000 attempts/s to a mere ten attempts/s, increasing the brute forcing effort 6.000 times. All other things equal, just that change would cause a brute force of one day to now take 16 years.

Strong random passwords

As I’ve covered, users typically choose passwords with notoriously low entropy. However, there is no reason for this password to be chosen by a user. Instead of custom passwords, the Ectual could allow users to cycle their password, generating a new random password. If we would also raise the length to say, 15 characters, that would pull the effort into the astronomical numbers.

Why increase from 9 to 15 characters? We’ve already established brute forcing a truly random password, with the current design, is difficult with 9 characters. But, every random character added makes brute forcing 62 times as hard. Adding three characters already makes it 238.000 times as hard, and raising it from 9 to 15 characters would make it 56 billion times as hard. If you could brute force nine characters in one second, 15 characters would take 1800 years, so it leaves a decent safety margin. More characters would of course be better, but we still have to consider usability as well.

This creates a usability problem for mobile devices, where the password can not be easily copy pasted in and typing is harder, but e.g. a QR code displayed on screen could help out there. Most mobile devices do have camera’s, so that would be a good alternative.

In practice, the number of 62 would be slightly lower, as you’d want to avoid ambiguous characters like O, o and 0, or 1, l and I. But there’s more than enough left.

Protecting the Ectual with TLS

Another obvious solution is to protect all communication with the Ectual with TLS. Though TLS is certainly not without issues, it is orders of magnitude stronger than the current protocol. It can be problematic to get a valid certificate for inbound connections, because the Ectual is connected on an address on the local network, that is not known in advance. However, this is not a concern for outbound connections. Outbound connections also have a larger attack surface as they extend beyond the home network of the user. So a simple additional protection would be to enforce TLS with a valid certificate on all outbound connections.

For inbound connections, there are options too: Enexis could create their own certificate authority, allow Ectuals to request a certificate from that CA without costs, and tell app developers to trust that CA only. This does create the problem of making it difficult for an attacker to acquire a certificate from that CA, but in any case it creates an extra boundary. Also, using a false certificate requires active MITM, listening in on unencrypted communication can be done with the simplest passive MITM.

Forcing server validation of RSP1

In the push model, the server should validate RSP1, but is not forced to do so. Failure to validate creates an additional vector for data leakage, even if TLS would be enabled and required on these connections.

This could be enforced by having the Ectual test this when a new push URL is added. It could submit a RSP1 which it knows to be incorrect, and refuse to push real data if the server does not detect the invalid RSP1, but instead simply replies with an RSP2.

Feedback from Enexis

Upon discovering this, I got in touch with Enexis, who were kind enough to clarify a few parts, like the password requirements. However, they’ve chosen not to adopt any of my suggested improvements into the device, as they feel the current design is sufficient.

Overview

  • The communication between the Ectual and an app or push server includes cleartext data that is derived from the password.
  • If a push server does not perform the required validation, it can itself leak such data as well, if the Ectual ID is known. This ID is transmitted in cleartext. The validation step is not enforced by the Ectual.
  • The leaked data can be used to brute force the application password offline, beyond the reach of any lockout systems.
  • As an unsuitable hash function is used, brute forcing can be performed much faster than in the ideal case.
  • Whether the brute forcing is actually viable depends on the number of users that choose their own passwords, the entropy of the passwords chosen by these users within the requirements, and how far the brute forcing can be optimised. Although these numbers are unknown to me, The margin of safety seems uncomfortably small to me.
  • There are a number of options to take the brute forcing effort into astronomical numbers, but they will not be adopted.

Further reading

From this blog, you may also like:

If you’re generally interested in cryptography design problems, you should definitely do the Matasano Crypto Challenges. My experience in doing some of those is exactly what inspired me to have a closer look at the Ectual. Also, you should get a copy of Bulletproof SSL and TLS.