Skip to main content
2 of 10
Polish
fgrieu
  • 145.6k
  • 12
  • 319
  • 611

There are issues in the question, some serious. In reading order:

  1. ECIES(Bob's public key, M || ECDSA(Alice's static private key, M)) is sign-then-encrypt, which is perfectly fine. But count the signatures, there's only one (ECIES does not sign). Therefore this is not "sign-encrypt-sign" as described elsewhere.

  2. If we wanted to make sign-then-encrypt-then-sign, just replace the resulting ECIES result X by X || ECDSA(Alice's static private key, X). But take care that the final signature leaks who the signer is, when sign-then-encrypt protects it.

  3. The "message for sending" is said to be [Alice's ephemeral public key, Alice's static public key, ciphertext, Mac's tag]. That's not matching the form of an ECIES cryptogram per SEC 1 Ver. 2.0, 5.1.3 Action 9, where Alice's static public key is not part of the output. Strictly stick to ECIES, or use another name. If Alice's identity or public key or certificate is needded, it can be in the message itself.

  4. What's hashed in Kenc || Kmac = KDF(SK) should be the (bytetring representation of the) $x$ coordinate of SK (per 5.1.3 Action 4). I would note Kenc || Kmac = KDF(SKx) to make the distinction explicit.

  5. In Ciphertext = Encrypt(Message, Kenc) it is not stated what Message is. If that's M, then M is not signed. That Message must be M || ECDSA(Alice's static private key, M) in order to match 1. It is critical that the ECDSA signature is encrypted, else confidentiality of the message is largely jeopardized (it becomes trivial to check a guess, e.g. of a coin toss, name, card number..)

  6. ECDSA's tag = ECDSA(Ciphertext, Alice static private key) comes as a surprise at this point, since Ciphertext is currently ECIES's internal ciphertext (denoted $EM$ in 5.1.3 Action 7), and not accessible externally from the ECIES black box. That makes reuse of proper ECIES code hairy: it's result must be parsed to extract the Ciphertext portion. As an aside, the question's notation is inconsistent on order of parameters, and use of possessive for asymmetric keys.

  7. HMAC's tag = MAC(Alice's static public key || Ciphertext || ECDSA's tag, Kmac) deviates from ECIES (5.1.3 Action 8). It should be replaced by HMAC's tag = MAC(Ciphertext, Kmac) (or HMAC's tag = MAC(Ciphertext || Sharedinfo2, Kmac) where Sharedinfo2 could be some externally agreed-upon bytestring; that could be Alice's static public key, but I do not see the point).

  8. The order is wrong in "Alice make ECDSA's tag first and after that make MAC's tag based on Alice's static public key, Ciphertext, and ECDSA's tag". For sign-then-encrypt that should be:

  • Alice first makes ECDSA's signature/tag of M,
  • then enciphers the concatenation of M and that signature/tag (with said concatenation denoted Message in the question), yielding ECIES's internal ciphertext (denoted Ciphertext in the question and $EM$ in 5.1.3 Action 7),
  • then after that make MAC's tag based on Ciphertext, and optionally Sharedinfo2
  1. |Alice's ephemeral public key, Alice's static public key, Ciphertext, ECDSA's tag, MAC's tag| is wrong if we are making ECIES, as explained in 2. That should be Alice's ephemeral public key || Ciphertext || HMAC's tag, per 5.1.3 Action 9 (with the necessary conversion from public key to bytestring implicit), yielding the output of ECIES, that is ECIES(Bob's public key, M || ECDSA(Alice's static private key, M)) of 1.

  2. I fail to parse "this is secure as sign-encrypt-sign". Leaving aside "secure as", it does not hold. With the changes indicated, it is made sign-then-encrypt, which is secure. Without the change, this is sort of encrypt-then-sign-plus-MAC-of-the-symmetric-part-of-the-ciphertext, the encryption is not ECIES, and while I do not immediately see any attack, that's not a valid indication of security.

  3. "I am not sure that MAC's tag is necessary in this scheme": the MAC is necessary at least for the encryption to be what's named ECIES, and to benefit of its security argument. It also serves as a protection against poor implementations on the receiving side

  4. In ECDSA's tag = ECDSA(Alice's public key || Ciphertext || Kmac, Alice's static private key), again I do not see why Alice's public key is part of what's signed; Ciphertext is an internal variable of ECIES; and the order of parameters is inconsistent with that in the beginning of the question.


Recommendations:

  • First decide if functionally the need is to have the signer's identity apparent to anyone by examination the final cryptogram, or revealed only to the holder of the deciphering private key.
    • if the signer's identity is to be kept secret, or if in doubt, sign-then-encrypt;
    • if the signer's identity is to be apparent, encrypt-then-sign (sign-then-encrypt-then-sign also works here, but is seldom justified).
  • In the above, encrypt should be precisely ECIES, including its ephemeral key generation, symmetric encryption, MAC, and concatenation to yield the result.
  • In the above, when using ECDSA, sign should be computing the ECDSA signature (including at least one hash step of the whole input) then adding the signature to the input (that can be either by appending or pretending it, as long as it can be unambiguously extracted on verification without risk of triggering implementation bugs).
  • If the identity, public key, or certificate of the sender needs to be included, add it immediately after or immediately before a signing step, or both (that can be either by appending or pretending it, as long as it can be unambiguously extracted without risk of triggering implementation bugs).
fgrieu
  • 145.6k
  • 12
  • 319
  • 611