I am looking for a method to encrypt a secret with a numeric PIN in such a way that if an attacker tries to decrypt with the wrong PIN, the ouput should be different.

For example, if the secret is ABCDE and the PIN is 5533, and the attacker tries to decrypt it with the incorrect PIN 1234, the output should be BASDAFF (a random string that looks like the secret).

I want to use this method to encode Diffie Hellman private key with PIN, so that a secret will be generated even if the PIN is wrong.


Encryption with a PIN is generally not secure as the PIN itself is not random enough: an attacker can simply brute force the encryption. I presume however that you would stop (after X times) once the Diffie-Hellman procedure would not result in the right shared secret.

You'll need a PBKDF (Password Based Key Derivation Function) to create a symmetric key from a PIN number. To do this you can leave the iteration count or work factor to a low number. The number of PIN's will be too small to protect against brute force anyway.

There is however a problem here: the secret of Diffie-Hellman is random, but it is random within a range of order n. That means that if you just use binary encryption that you run in a problem: the returned value may simply be too high and not fit into the range, immediately indicating to an attacker that the result is invalid. The order n is part of the domain parameters and thus publicly known.

Generally however the order n is chosen to be very high, generally starting with at least 8 bytes set to FFh. Check for instance the parameters here. If you're using one of these domain parameters then you can simply use counter mode (CTR) to encrypt your DH secret key. If you don't reuse the PIN for encryption (even in time) then you could use a zero byte IV, so that the ciphertext size isn't larger than the size of the plaintext secret number. Counter mode will always decrypt, there is no way to distinguish between the correct DH private key secret and an incorrect one.

You must however make sure that you encode your private key in the same number as bytes as the order, or smaller numbers for the private key will be easily identified.

So you may just need:


  1. derive key from PIN: K = PBKDF2(pw=PIN, count=1, output=128)
  2. encode DH secret key: SKE = I2OSP(SK, len(n))
  3. encrypt with derived key: C = AES_CTR(K, 0, SKE)


  1. derive key from PIN: K = PBKDF2(pw=PIN, count=1, output=128)
  2. decrypt with derived key: SKE = AES_CTR(K, 0, C)
  3. decode DH secret key: SK = OS2IP(SKE)

PBKDF2 is part of the PKCS#5 specification for password based encryption (PBE) - it is relatively common in cryptographic libraries, I2OSP and OS2IP are explained here.

There are ways of accomplishing your task even if order n is any large number. Generally what you are looking for is Format Preserving Encryption. With most FPE's you don't just get the ciphertext in the same range as the plaintext, but you would also get the plaintext in the range, even if the key is wrong. Beware though that the FPE construction must be such that the attacker cannot distinguish correctness within the decryption algorithm itself.

Fortunately though, my pal fgrieu posted a good solution to this on the cryptography site.

I apologize if this is a bit much to take in, but complexity is still better than insecurity. Better use one of the parameters with a large order n so you don't have to implement the answer given by fgrieu.


Technically, this is how unauthenticated encryption works by default - an incorrect key will produce an incorrect plaintext. Only if there's authentication or error-checking (and in ready to use libraries, there generally is) will decryption produce an error.

Note that it's trivial to brute-force the PIN due to its tiny keyspace, unless the number of attacks is very limited and they can't be distributed between many machines. The PIN can be made perishable to that end, removing the file after N attempts, provided there's a more permanent way to authenticate.

Generally, you should NOT encrypt the secret with the PIN itself, but rather with a key generated using a PBKDF (bcrypt, scrypt, argon2) from user-known secrets such as the password or the PIN and a salt. Depends on what exactly you're trying to do.

  • 1
    All of this answer is correct, but you managed to avoid some of the more hairy points, where the private DH key after decryption can still be distinguished from a random number of order n. – Maarten Bodewes Apr 24 '18 at 11:05
  • True, I just touched the surface in my response. Your solution is a lot more comprehensive. – Therac Apr 24 '18 at 13:56

I do not like this but if you are confident the client will know the secret and will know that they entered the wrong pin based the results fine. This will primarily go where the error message is given.

You can generate a random text via using

tr -dc A-Za-z0-9 </dev/urandom | head -c 1024 > random.txt

This is in linux.

Depending on your language you can modify.

Edit: I forgot to add this can lead to poor performance on a network seeing as you are required to create random text for every failed attempt so bruteforcing can lead to unnecessary cpu time.

  • On security you need to explain what to do and why it is secure. How you can do it is nice to know, but it is not the main thing we're after. The downvotes are most likely because the what and why are missing. – Maarten Bodewes Apr 24 '18 at 10:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.