What is the difference between the different padding types on iOS?

mbinna picture mbinna · Feb 19, 2011 · Viewed 10.5k times · Source

On iOS, the Certificate, Key, and Trust Services API contains the following padding types:

  • kSecPaddingNone
  • kSecPaddingPKCS1
  • kSecPaddingPKCS1MD2
  • kSecPaddingPKCS1MD5
  • kSecPaddingPKCS1SHA1

A user on the Apple CDSA mailing list says that "kSecPaddingPKCS1 [...] is the same as PKCS #1 1.5". The Certificate, Key, and Trust Services Reference annotates the latter three padding types (kSecPaddingPKCS1MD2, kSecPaddingPKCS1MD5, and kSecPaddingPKCS1SAH) with "Standard ASN.1 padding will be done, as well as PKCS1 padding of the underlying RSA operation".

  1. What is the difference to kSecPaddingPKCS1?
  2. Is kSecPaddingPKCS1 just the raw padding of the underlying RSA operation according to RFC 3447?
  3. When signing a SHA-256, SHA-384, or SHA-512 digest with SecKeyRawSign(), does a developer need to use kSecPaddingPKCS1 and perform the ASN.1 padding herself? Is the ASN.1 padding necessary or can it be omitted?

Any hint that points me in the right direction is highly appreciated.

Answer

Thomas Pornin picture Thomas Pornin · Feb 21, 2011

PKCS#1 contains two "paddings" for signatures with RSA, the "new" one (called PSS, added in version 2.1) and the "old" one (renamed "v1.5" since it was already in version 1.5 of PKCS#1). We are talking about the v1.5 padding.

When some data is signed, it is first hashed with a suitable hash function (e.g. SHA-1), then the hash value (20 bytes if using SHA-1) is wrapped into two successive layers:

  1. The hash value is encoded into an ASN.1-based structure which also specifies which hash function was used. In practice, if the hash value is H, then the first wrapping results in the sequence of bytes A || H where "||" is concatenation, and "A" is a header which is specific to the hash function (typically 15 to 20 bytes).

  2. The "A || H" is expanded with some extra bytes:

0x00 0x01 0xFF 0xFF ... 0xFF 0x00 || A || H

The number of bytes of value 0xFF is adjusted to that the total size equals the size of the RSA modulus (i.e. 128 bytes for a 1024-bit RSA key).

The second step is what PKCS#1 calls "type 1 padding".

kSecPaddingPKCS1 means that the function performs only the second step: it assumes that the input data is already the proper "A || H". Note that SSL/TLS (up to version 1.1) uses a signature variant which requires this mode (there's no "A", but there are two hash functions). With kSecPaddingPKCS1SHA1, the signature function expects the hash value as input, and adds the "A" header itself.

For a proper, standards-compliant signature which can be verified by third-party implementations, the "A" header must be added at some point. You can add it yourself and use kSecPaddingPKCS1, or use kSecpaddingPKCS1SHA1 and let the engine add it itself, which is probably less error-prone.

(As of 2011, use of SHA-1 is not recommended; you'd better switch to SHA-256 or SHA-512. Also, the API you are trying to use seem to be quite low-level, and the whole thing suspiciously looks as if you are tying to implement your own cryptographic protocol instead of using an existing library or framework.)