In August 2019, MikroTik issued a RouterOS software update to version 6.45.1 which removed plaintext password storage on all routers that upgraded to the new firmware. While enhancing router security, this came as a blow for researchers, network administrators, and tinkerers who used customized tooling with MikroTik proprietary protocols such as MAC Telnet and Winbox. MikroTik has since failed to detail the new authentication procedure despite user requests for assistance. Margin Research is excited to illuminate the authentication procedure and offer Python proof of concept (POC) implementations for Winbox and MAC Telnet authentication.

## SRP Overview

MikroTik's new authentication process uses a variation of Secure Remote Password (SRP). SRP is a type of Password Authentication Key Exchange protocol which incorporates the password within key exchange calculations. Knowledge of the correct password is required to compute the shared secret, so identical secrets indicate successful authentication. The SRP specification dates back over two decades and mainstream use gained traction in the recent past, highlighted by its use in OpenSSL. However, MikroTik does not implement this SRP protocol. Rather, RouterOS employs elliptic curve SRP (EC-SRP), specifically EC-SRP5. This protocol, which is much less prevalent, is defined by the following sequence:

#### User registration

- A user registers with the system, providing a
`username`

and`password`

- The router generates a 16-byte user
`salt`

and calculates the user’s password verifier,`v`

, according to the equation`v = ECDH(SHA2(salt | SHA2(username | “:” | password)))`

^{[1]} - The router stores
`username`

,`salt`

, and`v`

’s x coordinate,`v`

, in memory in_{x}`/rw/store/user.dat`

#### Client Authentication

- The client generates an ephemeral private key,
`T`

, and computes its ephemeral public key point_{c}`W`

. The client sends an authentication request including username and the point’s x coordinate,_{c}= ECDH(T_{c})`W`

, as its ephemeral public key_{cx}- Since each elliptic curve x coordinate has two y solutions,
`(x, y)`

and`(x, -y)`

, the client also transmits the parity of the generated point’s y coordinate,`W`

, to ensure the server calculates the identical point and not its negation_{cy}

- Since each elliptic curve x coordinate has two y solutions,
- The server retrieves the associated client’s salt and
`v`

. The server then generates an ephemeral private key,_{x}`T`

, and computes a password-entangled public key point,_{s}`W`

_{s}= ECDH(T_{s}) + plot(SHA2(v_{x}))^{[2]}. Finally, the server responds with`W`

and_{sx}`salt`

- The server similarly calculates and transmits the parity of its public point’s y coordinate,
`W`

_{sy}

- The server similarly calculates and transmits the parity of its public point’s y coordinate,

- The client, now knowing the user’s salt and the server’s public key, can compute the shared secret and confirmation code as follows:
- Calculates
`v`

's private key,`v`

_{p}= SHA2(salt | SHA2(username | “:” | password)) - Plots
`v`

as the server did during registration and retrieves the x coordinate,`v`

_{x} - Calculates pseudo-random point
`e = plot(SHA2(v`

, as the server computed in the preceding step_{x})) - Computes the server public point
`W`

using_{s}`plot(W`

_{sx}) - Performs point subtraction to disentangle the password, calculating
`u = W`

_{s}- e - Calculates hash
`h = SHA2(W`

_{c}+ W_{s}) - Performs scalar addition and multiplication to compute
`T`

_{c}+ h.v_{p} - Calculates the shared point,
`z`

, using elliptic curve multiplication with the previously calculated`u`

point to find`z = u(T`

_{c}+ h.v) - Computes a client-side confirmation code,
`C`

_{c}= SHA2(h + z_{x})

- Calculates
- The client completes this handshake step by transmitting
`C`

._{c} - The server computes the shared secret point,
`z`

, as follows:- Calculates hash
`h = SHA2(W`

_{c}+ W_{s}) - Multiplies
`h.v`

over the elliptic curve - Computes
`plot(W`

on the elliptic curve, generating_{cx})`W`

_{c} - Adds
`W`

over the elliptic curve_{c}+ h.v - Performs a final elliptic curve scalar multiplication using the server’s private key to yield
`z = T`

_{s}(W_{c}+ h.v)

- Calculates hash
- The server validates the username and password by similarly calculating
`C`

and checking against the client provided value._{c}^{[3]}

#### Encryption

The server and client can encrypt data once confirming an equivalent shared secret. MikroTik employs a MAC-then-encrypt strategy, leveraging AES-CBC as the encryption algorithm and HMAC as the authentication algorithm. RouterOS also uses separate send and receive keys for both HMAC and AES, labeled below as `AES`

. Both server and client generate these keys using “magic” strings and the HMAC key derivation function, HKDF. Messages prepend encrypted data with the unique random initialization vector, _{s}, AES_{r}, HMAC_{s}, HMAC_{r}`IV`

, used in AES-CBC mode.

A representative Winbox packet is shown below.

## MikroTik Intricacies

This is undoubtedly a complicated procedure, and involves many more steps than standard elliptic curve Diffie Hellman or previous RouterOS protocols that simply checked password hashes. But the difficulty does not end there; this protocol is additionally complicated by MikroTik’s elliptic curve choices. RouterOS authentication uses a standard Montgomery curve, such as the popular donna25519 library, for some calculations, *but not all*. And it unfortunately does not use the NaCl/libsodium default Ed25519 Edwards curve. Rather, for either added efficiency or obscurity (or a bit of both), RouterOS performs most calculations over the standard Weierstrass curve: `Y`

. Furthermore, it calculates points in weighted projective space ^{2}=X^{3}+aX+b`P(2,3,1)`

for even greater computation efficiency. This yields the Weierstrass equation: `y`

, where ^{2}=x^{3}+axz^{4}+bz^{6}`Y=y/z`

and ^{3}`X=x/z`

.^{2}

Confused? Understandably so. The result of everything previously detailed is an authentication protocol with copious elliptic curve calculations across three different curve varieties: Montgomery curves, Weierstrass curves in affine form (`Z=1`

), and Weierstrass curves in weighted projective space. This requires conversions to and from different curves and projective spaces, which makes following the authentication flow extremely difficult. The Python POC examples detail the sequence specifically, though the following high-level overview might be better suited for some interested readers:

- The
`ECDH()`

equation generates a public key,`pub`

, by multiplying private key,`priv`

, by base point`g`

on the Weierstrass curve in weighted projective space. This returns the point`(x`

. The function then converts the point to Weierstrass affine form and further converts to Montgomery form; specifically, the function calculates the Weierstrass affine coordinate_{w}: y_{w}: z_{w})`X=x/z`

and converts^{2}`X`

to Montgomery form- This is odd because the private key is multiplied over the Weierstrass curve, but the public key returned is the x coordinate in Montgomery form

- The
`plot()`

function plots a given x coordinate on the Weierstrass curve in affine form. Therefore, it returns a Weierstrass point`(X : Y : 1)`

- Interestingly, it actually performs this operation by first plotting the point on the Montgomery curve and converting the x coordinate by adding constant
`a/3`

(where`a`

is the Montgomery curve constant from the curve’s equation`Y`

)^{2}=X^{3}+aX^{2}+X

- Interestingly, it actually performs this operation by first plotting the point on the Montgomery curve and converting the x coordinate by adding constant
- All point multiplication and addition over the Weierstrass curve are performed in weighted projective space. Thus, any returned parameters (e.g., shared secret point
`z`

, public keys`W`

and_{c}`W`

) must be converted to Weierstrass affine form_{s}

The picture becomes a bit clearer with those considerations in mind. The subtle choice of using the Weierstrass curve for public key calculations and converting all transmitted points to Montgomery form makes an obscure protocol even more challenging to understand. Considering all the intricacies outlined, it is no surprise that this authentication scheme was not reproduced in a public implementation until now.

## Still With Us?

We hope so. This cryptographic reverse engineering effort had a little bit of everything: an obscure protocol, multiple elliptic curve definitions, and conversions from weighted projective space. If you are hungry for more, or want to put yourself through further technical torture, please see the source code and additional commentary in our GitHub repo. But wait! For those who are truly insatiable, there's more. The single best resource we used in reverse engineering was an unfinished IEEE submission draft courtesy of the WayBack Machine. In fact, MikroTik's implementation is *nearly *identical to the draft's proposed protocol. See if you can spot the minor nuances and marvel (as we did) that the shared secret remains the same.

[1] `ECDH()`

accepts a private key as input and returns the associated elliptic curve public key, as in elliptic curve Diffie Hellman. This is replicated by the `gen_public_key`

method in `elliptic_curves.py`

’s `WCurve`

class

[2] `plot()`

accepts an x coordinate and returns the corresponding point on the elliptic curve. This is replicated by the `lift_x`

method in `elliptic_curves.py`

’s` WCurve`

class

[3] The server may complete the authentication handshake by sending a server confirmation code, `C`

, for the client to validate the server’s authenticity. In the case of the Winbox protocol, _{s}`C`

_{s} = SHA2(h + C_{c} + z_{x})

[4] source: https://crypto.stackexchange.com/questions/40947/what-is-the-projective-space