Skip to content

Instantly share code, notes, and snippets.

@dlenski
Last active May 10, 2022 21:17
Show Gist options
  • Save dlenski/e4a53a17c0786f492fc04c901968681d to your computer and use it in GitHub Desktop.
Save dlenski/e4a53a17c0786f492fc04c901968681d to your computer and use it in GitHub Desktop.
Fake server for RSA SecurID token generation (see https://github.com/dlenski/rsa_ct_kip#fake-server)
#!/usr/bin/env python3
# Needs: Python 3.5+, Flask, PyCryptoDome
# server.pem, rsapubkey.pem + rsaprivkey.pem (1024-bit) in the current directory
#
# What it does:
# Pretends to be the "CT-KIP" web service (https://tools.ietf.org/html/rfc3948) that
# RSA SecurID Token for Windows v5.0.x talks to to set up a new token, using an
# authentication code.
#
# This has been turned into a "real" repository.
#
# See https://github.com/dlenski/rsa_ct_kip#fake-server and use that instead of
# the (no-longer-maintained) contents of this gist.
@cemeyer
Copy link

cemeyer commented Sep 7, 2018

§3.3:

b. Based on this information, the CT-KIP server provides a random
nonce, R_S, to the CT-KIP client, along with information about
the type of key to generate, the encryption algorithm chosen to
protect sensitive data sent in the protocol. In addition, it
provides either information about a shared secret key to use for
encrypting the cryptographic token's random nonce (see below), or
its own public key.
The length of the nonce R_S may depend on
the selected key type.

c. The cryptographic token generates a random nonce R_C and encrypts
it using the selected encryption algorithm and with a key K that
is either the CT-KIP server's public key K_SERVER, or a shared
secret key K_SHARED as indicated by the CT-KIP server.
The
length of the nonce R_C may depend on the selected key type. The
CT-KIP client then sends the encrypted random nonce to the CT-KIP
server. The token also calculates a cryptographic key K_TOKEN of
the selected type from the combination of the two random nonces
R_S and R_C, the encryption key K, and possibly some other data,
using the CT-KIP-PRF function defined herein.

d. The CT-KIP server decrypts R_C, calculates K_TOKEN from the
combination of the two random nonces R_S and R_C, the encryption
key K, and possibly some other data, using the CT-KIP-PRF
function defined herein.
The server then associates K_TOKEN with
the cryptographic token in a server-side data store. The intent
is that the data store later on will be used by some service that
needs to verify or decrypt data produced by the cryptographic
token and the key.

e. Once the association has been made, the CT-KIP server sends a
confirmation message to the CT-KIP client. The confirmation
message includes an identifier for the generated key and may also
contain additional configuration information, e.g., the identity
of the CT-KIP server.

f. Upon receipt of the CT-KIP server's confirmation message, the
cryptographic token associates the provided key identifier with
the generated key K_TOKEN, and stores the provided configuration
data, if any.

And then §3.5, lol:

   dsLen = (desired length of K_TOKEN)

   K_TOKEN = CT-KIP-PRF (R_C, "Key generation" || k || R_S, dsLen)

It seems like most of the magic strings may be documented in the RFC. Worth a shot?

@dlenski
Copy link
Author

dlenski commented Sep 7, 2018

dsLen = (desired length of K_TOKEN)

K_TOKEN = CT-KIP-PRF (R_C, "Key generation" || k || R_S, dsLen)

@cemeyer, aha, good catch… thanks. A little bit above it, k is defined as the key k used to encrypt R_C. That means the final token key material is supposed to be:

K_TOKEN = CT-KIP-PRF (ClientNonce, "Key generation" || ServerRSAPubKey || ServerNonce, dsLen)

Do you agree that this is right? If so, what do you think is the representation that should be used for "the key k used to encrypt R_C"? The modulus of the key in big-endian order… the standard-ish binary .der format key… the PEM-format key… something else…?

If that's narrowed down then, based on §3.8.6:

MAC = CT-KIP-PRF (K_AUTH, "MAC 2 computation" || R_C, dsLen)
    =  CT-KIP-PRF (K_TOKEN, "MAC 2 computation" || R_C, dsLen)

(because "If no authentication key is present in the token, and no K_TOKEN existed before the CT- KIP run, K_AUTH shall be the newly generated K_TOKEN.")

It seems like most of the magic strings may be documented in the RFC. Worth a shot?

In order to verify whether any of this works we have to get a whole bunch of fiddly bits right:

  1. Ensure that we have a correct implementation of CT-KIP-PRF-AES (rather hard given lack of test vectors and the fact that no one besides RSA would use this).
  2. Guess or know the correct representation of the k used in generating K_TOKEN.
  3. Use the PRF, the k, and the nonces to generate the 16-byte K_TOKEN (assuming RSA's own software adheres to §3.5).
  4. Assuming that K_AUTH = K_TOKEN is indeed correct (as §3.8.6 suggests), use all that to generate the "MAC" and attach it to the <ServerFinished> message.
  5. If the official client software actually accepts the result, then 🍻. If not, then… keep stabbing around blindly and twiddling all of the above knobs… ?

@cemeyer
Copy link

cemeyer commented Sep 8, 2018

Yeah, that looks plausibly right to me.

I hope the RSA pubkey representation fed into the PRF isn't .der and especially PEM, but I don't really know. Could try the DER order (modulus n, followed by exponent e), but I don't know how long of an encoding e would have. Then again PKCS 1 specifies e=65537, so maybe just try the (big-endian, i.e., I2OSP) modulus (which should have an exact encoding width).

I agree that K_TOKEN for K_AUTH looks reasonable for §3.8.6.

Yeah. I've hunted around a little bit and I'm having trouble finding any other CT-KIP-PRF implementation to compare against (not the same as known test vectors, but better than nothing). I wonder if the (single) MAC in https://tools.ietf.org/html/draft-nystrom-ct-kip-two-pass-01#page-30 is a valid test vector. But I have not investigated what it is supposed to be over, or if the key is even supplied.

Um, call me crazy, but isn't CT-KIP-PRF-AES identical to DSKPP-PRF-AES? Even the diagrams and language in RFC 6063 seem derivative of RFC 4758. And indeed our friend Magnus who authored the first RFC is an author on the second. Still no explicit known vectors, but some different MACs that may or may not be correct, anyway. Or maybe there are implementations, I don't know (didn't find any with a very quick google search).

Yeah, we have a very low information oracle on whether we got it right. Hmm. Maybe reversing the client would be more straightforward.

By the way, I don't get notifications of gist replies, so I might miss further discussion here. But we have lots of options :-).

@leonardorobertolopez
Copy link

hi, I would like to ask what is the functionality of this code. Is it a localhost server where I could emulate SecureId app? Thank you very much

@dlenski
Copy link
Author

dlenski commented May 10, 2022

Is it a localhost server where I could emulate SecureId app? Thank you very much

@leonardorobertolopez Yes. To be clear, it emulates the server used for provisioning SecurID tokens using the "RSA CT-KIP" protocol.

It has been turned into a "real" repository, though. See https://github.com/dlenski/rsa_ct_kip#fake-server and use that instead.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment