Skip to main content
8 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 keeps it confidential to all but the holder of the decryption private key.

  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. The ECIES result is [Alice's ephemeral public key || Ciphertext || Mac's tag]. If Alice's identity or public key or certificate is needed, it can be part of 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), without the $y$ coordinate or parity bit. 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 symmetric 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 symmetrically enciphers the concatenation of M and that signature/tag (with said concatenation denoted Message in the question), yielding ECIES's internal symmetric 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
  • then concatenate the ephemeral key (which served as the source of the symmetric encryption and MAC keys), Ciphertext, and MAC (not the optional Sharedinfo2) into the ECIES result.
  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, which 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; and Ciphertext is an internal variable of ECIES, which is dubious.

  5. It can be understood that an entity reuses the same private key for ECDSA signature and ECIES decryption, which conceivably could have security implication. Formally, it makes ECIES's security argument invalid, and is explicitly against the ECDSA specification in FIPS 186-4:

ECDSA keys shall not be used for any other purpose


Recommendations:

  • First decide if functionally the need is to have the signer's identity apparent to anyone by examination of the final cryptogram, or revealed only to the holder of the deciphering private key. For asymmetric/hybrid encryption and signature:
    • 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 not necessary for security when the next two recommendations are applied; it is however sometime justified, for example if cryptograms normally carrying the signer's ID must be transformable into anonymous ones by merely stripping the external signature, but should remain signed and non-repudiable after decryption).
  • 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 performing one signing step (or both); that can be either by appending or pretending the information, as long as it can be unambiguously extracted without risk of triggering implementation bugs.
  • When possible: one purpose, one key.

Nothing in this answer should be construed as an endorsement of anything by anyone.

fgrieu
  • 145.6k
  • 12
  • 319
  • 611