RFC 3447

Public-Key Cryptography Standards (PKCS) #1: RSA Cryptography Specifications Version 2.1

๐Ÿ“… published February 2003, from ietf

RFC 3447 ๐ŸŽ€ Public-Key Cryptography Standards (PKCS) #1: RSA Cryptography Specifications Version 2.1

okay bestie, this one is the RSA Cryptography Specifications Version 2.1 ๐Ÿ”โœจ It's from the Network Working Group, written by J. Jonsson and B. Kaliski of RSA Laboratories, and it's dated February 2003 ๐Ÿ“…. It's Informational (not a standard, just here to share info with the internet community), and it obsoletes RFC 2437 ๐Ÿ’… There's no Updates line in the header, so it doesn't update anything else.


Status of This Memo ๐Ÿ“

this memo is informational only ๐ŸŒธ It gives info to the Internet community and does not specify an Internet standard of any kind. distribution is unlimited, so share it with everyone ๐Ÿ’œ

the copyright is held by the Internet Society, and the exact notice is:

Copyright (C) The Internet Society (2003). All Rights Reserved.

Abstract ๐Ÿงš

so this memo is basically a republication of PKCS #1 v2.1 from RSA Laboratories' Public-Key Cryptography Standards (PKCS) series ๐ŸŒˆ change control stays inside the PKCS process, and the body of this document comes straight from the PKCS #1 v2.1 document, with a few corrections made during the publication process ๐Ÿซถ

Table of Contents ๐Ÿ“š

1.       Introduction...............................................2
2.       Notation...................................................3
3.       Key types..................................................6
   3.1      RSA public key..........................................6
   3.2      RSA private key.........................................7
4.       Data conversion primitives.................................8
   4.1      I2OSP...................................................9
   4.2      OS2IP...................................................9
5.       Cryptographic primitives..................................10
   5.1      Encryption and decryption primitives...................10
   5.2      Signature and verification primitives..................12
6.       Overview of schemes.......................................14
7.       Encryption schemes........................................15
   7.1      RSAES-OAEP.............................................16
   7.2      RSAES-PKCS1-v1_5.......................................23
8.       Signature schemes with appendix...........................27
   8.1      RSASSA-PSS.............................................29
   8.2      RSASSA-PKCS1-v1_5......................................32
9.       Encoding methods for signatures with appendix.............35

   9.1      EMSA-PSS...............................................36
   9.2      EMSA-PKCS1-v1_5........................................41
Appendix A. ASN.1 syntax...........................................44
   A.1      RSA key representation.................................44
   A.2      Scheme identification..................................46
Appendix B. Supporting techniques..................................52
   B.1      Hash functions.........................................52
   B.2      Mask generation functions..............................54
Appendix C. ASN.1 module...........................................56
Appendix D. Intellectual Property Considerations...................63
Appendix E. Revision history.......................................64
Appendix F. References.............................................65
Appendix G. About PKCS.............................................70
Appendix H. Corrections Made During RFC Publication Process........70
Security Considerations............................................70
Acknowledgements...................................................71
Authors' Addresses.................................................71
Full Copyright Statement...........................................72

1. Introduction ๐Ÿฆ‹

okay so this doc gives recommendations for implementing public-key cryptography based on the RSA algorithm [42] โœจ it covers these aspects:

  • Cryptographic primitives
  • Encryption schemes
  • Signature schemes with appendix
  • ASN.1 syntax for representing keys and for identifying the schemes

these recommendations are meant for general use in computer and communications systems, so they've got a fair amount of flexibility ๐ŸŒท application standards built on top of them may add extra constraints. the recommendations are meant to be compatible with the standard IEEE-1363-2000 [26], plus draft standards being developed by the ANSI X9F1 [1] and IEEE P1363 [27] working groups ๐Ÿ’…

this document replaces PKCS #1 version 2.0 [35][44], but it keeps compatible techniques, so nothing gets left behind ๐Ÿซถ

here's how the doc is laid out:

  • Section 1 is the introduction.
  • Section 2 defines some notation used in this document.
  • Section 3 defines the RSA public and private key types.
  • Sections 4 and 5 define several primitives, which are basic mathematical operations. Data conversion primitives are in Section 4, and cryptographic primitives (encryption-decryption, signature-verification) are in Section 5.
  • Sections 6, 7, and 8 deal with the encryption and signature schemes in this document. Section 6 gives an overview. Along with the methods found in PKCS #1 v1.5, Section 7 defines an OAEP-based [3] encryption scheme, and Section 8 defines a PSS-based [4][5] signature scheme with appendix.
  • Section 9 defines the encoding methods for the signature schemes in Section 8.
  • Appendix A defines the ASN.1 syntax for the keys defined in Section 3 and the schemes in Sections 7 and 8.
  • Appendix B defines the hash functions and the mask generation function used in this document, including ASN.1 syntax for the techniques.
  • Appendix C gives an ASN.1 module.
  • Appendices D, E, F and G cover intellectual property issues, outline the revision history of PKCS #1, give references to other publications and standards, and provide general information about the Public-Key Cryptography Standards.

2. Notation ๐ŸŒธ

this section is the symbol cheat sheet, so screenshot it ๐Ÿ“ธ here's what each symbol means:

  • c ciphertext representative, an integer between 0 and n-1
  • C ciphertext, an octet string
  • d RSA private exponent
  • d_i additional factor r_i's CRT exponent, a positive integer such that
e * d_i == 1 (mod (r_i-1)), i = 3, ..., u
  • dP p's CRT exponent, a positive integer such that
e * dP == 1 (mod (p-1))
  • dQ q's CRT exponent, a positive integer such that
e * dQ == 1 (mod (q-1))
  • e RSA public exponent
  • EM encoded message, an octet string
  • emBits (intended) length in bits of an encoded message EM
  • emLen (intended) length in octets of an encoded message EM
  • GCD(. , .) greatest common divisor of two nonnegative integers
  • Hash hash function
  • hLen output length in octets of hash function Hash
  • k length in octets of the RSA modulus n
  • K RSA private key
  • L optional RSAES-OAEP label, an octet string
  • LCM(., ..., .) least common multiple of a list of nonnegative integers
  • m message representative, an integer between 0 and n-1
  • M message, an octet string
  • mask MGF output, an octet string
  • maskLen (intended) length of the octet string mask
  • MGF mask generation function
  • mgfSeed seed from which mask is generated, an octet string
  • mLen length in octets of a message M
  • n RSA modulus, n = r_1 * r_2 * ... * r_u , u >= 2
  • (n, e) RSA public key
  • p, q first two prime factors of the RSA modulus n
  • qInv CRT coefficient, a positive integer less than p such that
q * qInv == 1 (mod p)
  • r_i prime factors of the RSA modulus n, including r_1 = p, r_2 = q, and additional factors if any
  • s signature representative, an integer between 0 and n-1
  • S signature, an octet string
  • sLen length in octets of the EMSA-PSS salt
  • t_i additional prime factor r_i's CRT coefficient, a positive integer less than r_i such that
r_1 * r_2 * ... * r_(i-1) * t_i == 1 (mod r_i) ,
i = 3, ... , u
  • u number of prime factors of the RSA modulus, u >= 2
  • x a nonnegative integer
  • X an octet string corresponding to x
  • xLen (intended) length of the octet string X
  • 0x indicator of hexadecimal representation of an octet or an octet string; "0x48" denotes the octet with hexadecimal value 48; "(0x)48 09 0e" denotes the string of three consecutive octets with hexadecimal value 48, 09, and 0e, respectively
  • \lambda(n) LCM(r_1-1, r_2-1, ... , r_u-1)
  • \xor bit-wise exclusive-or of two octet strings
  • \ceil(.) ceiling function; \ceil(x) is the smallest integer larger than or equal to the real number x
  • || concatenation operator
  • == congruence symbol; a == b (mod n) means that the integer n divides the integer a - b

Note. the CRT can be applied in a non-recursive way or a recursive way ๐Ÿง  this document uses a recursive approach following Garner's algorithm [22]. see also Note 1 in Section 3.2 โœจ

3. Key types ๐Ÿ”‘

two key types get used in the primitives and schemes here: the RSA public key and the RSA private key ๐Ÿ’– together, a public key and a private key form an RSA key pair.

this spec supports so-called "multi-prime" RSA, where the modulus can have more than two prime factors ๐ŸŒˆ the benefit is lower computational cost for the decryption and signature primitives, as long as the CRT (Chinese Remainder Theorem) is used. it runs better on single-processor platforms, but the gains are bigger on multiprocessor platforms, where the modular exponentiations can run in parallel ๐Ÿš€

if you want to know how multi-prime affects the security of the RSA cryptosystem, check out [49] ๐Ÿง

3.1 RSA public key ๐Ÿซง

for this document, an RSA public key has two components:

  • n the RSA modulus, a positive integer
  • e the RSA public exponent, a positive integer

in a valid RSA public key, the RSA modulus n is a product of u distinct odd primes r_i, i = 1, 2, ..., u, where u >= 2 ๐Ÿ”ฅ the RSA public exponent e is an integer between 3 and n - 1 that satisfies GCD(e, \lambda(n)) = 1, where \lambda(n) = LCM(r_1 - 1, ..., r_u - 1). by convention, the first two primes r_1 and r_2 can also be called p and q, respectively ๐Ÿ‘‘

a recommended syntax for swapping RSA public keys between implementations is in Appendix A.1.1, but an implementation's internal representation can be different ๐Ÿ’œ

3.2 RSA private key โœจ

okay so an RSA private key can come in two flavours for this doc, and I'm obsessed with how they're built ๐Ÿ’…

  1. The first representation is the pair (n, d). Here's what the bits mean:
n        the RSA modulus, a positive integer
d        the RSA private exponent, a positive integer
  1. The second representation is a quintuple (p, q, dP, dQ, qInv) plus a (possibly empty) list of triplets (r_i, d_i, t_i), with i = 3, ..., u, one triplet for each prime that isn't in the quintuple. Here's the breakdown:
p        the first factor, a positive integer
q        the second factor, a positive integer
dP       the first factor's CRT exponent, a positive integer
dQ       the second factor's CRT exponent, a positive integer
qInv     the (first) CRT coefficient, a positive integer
r_i      the i-th factor, a positive integer
d_i      the i-th factor's CRT exponent, a positive integer
t_i      the i-th factor's CRT coefficient, a positive integer

Now for what makes a key valid ๐Ÿ”ฅ. With the first representation, the modulus n is the same as in the matching RSA public key, and it's the product of u distinct odd primes r_i (i = 1, 2, ..., u), where u >= 2. The private exponent d is a positive integer smaller than n that satisfies:

e * d == 1 (mod \lambda(n))

Here e is the matching RSA public exponent, and \lambda(n) is defined back in Section 3.1 ๐Ÿฆ‹

With the second representation, p and q are the first two prime factors of n (aka r_1 and r_2). The CRT exponents dP and dQ are positive integers smaller than p and q respectively, and they satisfy:

e * dP == 1 (mod (p-1))
e * dQ == 1 (mod (q-1))

The CRT coefficient qInv is a positive integer smaller than p, and it satisfies:

q * qInv == 1 (mod p).

If u > 2, the representation has one or more triplets (r_i, d_i, t_i) for i = 3, ..., u. The r_i values are the extra prime factors of n. Each CRT exponent d_i (for i = 3, ..., u) satisfies:

e * d_i == 1 (mod (r_i - 1)).

Each CRT coefficient t_i (for i = 3, ..., u) is a positive integer smaller than r_i, and it satisfies:

R_i * t_i == 1 (mod r_i) ,

where R_i = r_1 * r_2 * ... * r_(i-1).

If you want to swap RSA private keys between implementations (including bits from both representations), there's a recommended syntax in Appendix A.1.2. An implementation's internal storage can look totally different though, so don't stress ๐ŸŒธ

Notes.

  1. The CRT coefficient definitions here, plus the formulas that use them in the Section 5 primitives, mostly follow Garner's algorithm [22] (see also Algorithm 14.71 in [37]). BUT, for compatibility with the RSA private key representations in PKCS #1 v2.0 and earlier versions, p and q have their roles flipped compared to the other primes. So the first CRT coefficient, qInv, is defined as the inverse of q mod p, not as the inverse of R_1 mod r_2 (which is the inverse of p mod q). Plot twist ๐Ÿ™ˆ
  2. Quisquater and Couvreur [40] noticed the perks of using the Chinese Remainder Theorem for RSA operations. Iconic of them ๐Ÿ‘‘

4. Data conversion primitives โœจ

Two data conversion primitives show up in the schemes this document defines:

  • I2OSP - Integer-to-Octet-String primitive
  • OS2IP - Octet-String-to-Integer primitive

For this doc, and matching ASN.1 syntax, an octet string is an ordered sequence of octets (eight-bit bytes). It's indexed from first (conventionally the leftmost) to last (the rightmost). When converting to and from integers, the first octet counts as the most significant one in these conversion primitives ๐Ÿงš

4.1 I2OSP

I2OSP turns a nonnegative integer into an octet string of a specified length. Here's the signature:

I2OSP (x, xLen)

Input:
x        nonnegative integer to be converted
xLen     intended length of the resulting octet string

Output:
X        corresponding octet string of length xLen

Error: "integer too large"

Steps:

  1. If x >= 256^xLen, output "integer too large" and stop. Too big bestie, can't fit ๐Ÿ™…โ€โ™€๏ธ
  2. Write the integer x in its unique xLen-digit representation in base 256:
x = x_(xLen-1) 256^(xLen-1) + x_(xLen-2) 256^(xLen-2) + ...
+ x_1 256 + x_0,

where 0 <= x_i < 256. (Heads up: one or more leading digits will be zero if x is less than 256^(xLen-1).) ๐Ÿ’–

  1. Let the octet X_i have the integer value x_(xLen-i) for 1 <= i <= xLen. Then output the octet string:
X = X_1 X_2 ... X_xLen.

4.2 OS2IP

OS2IP turns an octet string into a nonnegative integer. Signature time:

OS2IP (X)

Input:
X        octet string to be converted

Output:
x        corresponding nonnegative integer

Steps:

  1. Let X_1 X_2 ... X_xLen be the octets of X from first to last, and let x_(xLen-i) be the integer value of the octet X_i for 1 <= i <= xLen.
  2. Let:
x = x_(xLen-1) 256^(xLen-1) + x_(xLen-2) 256^(xLen-2) + ...
+ x_1 256 + x_0.
  1. Output x. Ta-da ๐ŸŽ€

5. Cryptographic primitives โœจ

Cryptographic primitives are the basic maths operations that cryptographic schemes get built on. They're meant to be implemented in hardware or as software modules, and they don't give you any security by themselves. The security comes from the scheme they sit inside ๐Ÿฆ‹

This document specifies four primitives, grouped into two pairs: encryption and decryption, and signature and verification.

The primitive specs assume the inputs already meet certain conditions, most importantly that the RSA public and private keys are valid. Keep that in mind ๐Ÿ”‘

5.1 Encryption and decryption primitives

An encryption primitive turns a message representative into a ciphertext representative, controlled by a public key. A decryption primitive takes the ciphertext representative and recovers the message representative, controlled by the matching private key ๐Ÿ’œ

This document has one pair of encryption and decryption primitives, used by its encryption schemes, and it's specified right here: RSAEP/RSADP. RSAEP and RSADP do the same maths operation, just with different keys going in.

These primitives are the same as IFEP-RSA/IFDP-RSA in IEEE Std 1363-2000 [26] (with multi-prime RSA support added), and they're compatible with PKCS #1 v1.5.

The main operation in each primitive is exponentiation.

5.1.1 RSAEP

RSAEP ((n, e), m)

Input:
(n, e)   RSA public key
m        message representative, an integer between 0 and n - 1

Output:
c        ciphertext representative, an integer between 0 and n - 1

Error: "message representative out of range"

Assumption: RSA public key (n, e) is valid

Steps:

  1. If the message representative m is not between 0 and n - 1, output "message representative out of range" and stop. Out of range is a vibe-killer ๐Ÿ’€
  2. Let c = m^e mod n.
  3. Output c. โœจ

5.1.2 RSADP

RSADP (K, c)

Input:
K        RSA private key, where K has one of the following forms:
         - a pair (n, d)
         - a quintuple (p, q, dP, dQ, qInv) and a possibly empty
           sequence of triplets (r_i, d_i, t_i), i = 3, ..., u
c        ciphertext representative, an integer between 0 and n - 1

Output:
m        message representative, an integer between 0 and n - 1

Error: "ciphertext representative out of range"

Assumption: RSA private key K is valid

Steps:

  1. If the ciphertext representative c is not between 0 and n - 1, output "ciphertext representative out of range" and stop.
  2. Compute the message representative m like this:

a. If the first form (n, d) of K is used, let m = c^d mod n.

b. If the second form (p, q, dP, dQ, qInv) and (r_i, d_i, t_i) of K is used, do the following:

i. Let m_1 = c^dP mod p and m_2 = c^dQ mod q.

ii. If u > 2, let m_i = c^(d_i) mod r_i, for i = 3, ..., u.

iii. Let h = (m_1 - m_2) * qInv mod p.

iv. Let m = m_2 + q * h.

v. If u > 2, let R = r_1 and for i = 3 to u do:

  1. Let R = R * r_(i-1).
  2. Let h = (m_i - m) * t_i mod r_i.
  3. Let m = m + R * h.
  1. Output m. ๐ŸŽ‰

Note. Step 2.b can be rewritten as one single loop, as long as you reverse the order of p and q. For consistency with PKCS #1 v2.0 though, the first two primes p and q get handled separately from the additional primes. Consistency is bestie behaviour ๐Ÿซถ

5.2 Signature and verification primitives

A signature primitive turns a message representative into a signature representative, controlled by a private key. A verification primitive recovers the message representative from the signature representative, controlled by the matching public key โœ๏ธ

This document has one pair of signature and verification primitives, used by its signature schemes, and it's specified here: RSASP1/RSAVP1.

These primitives are the same as IFSP-RSA1/IFVP-RSA1 in IEEE 1363-2000 [26] (with multi-prime RSA support added), and they're compatible with PKCS #1 v1.5.

The main operation in each primitive is exponentiation, same as the encryption and decryption primitives in Section 5.1. RSASP1 and RSAVP1 are exactly the same as RSADP and RSAEP, except their input and output argument names are different. They're kept separate because they're meant for different purposes ๐ŸŒˆ

5.2.1 RSASP1

RSASP1 (K, m)

Input:
K        RSA private key, where K has one of the following forms:
         - a pair (n, d)
         - a quintuple (p, q, dP, dQ, qInv) and a (possibly empty)
           sequence of triplets (r_i, d_i, t_i), i = 3, ..., u
m        message representative, an integer between 0 and n - 1

Output:
s        signature representative, an integer between 0 and n - 1

Error: "message representative out of range"

Assumption: RSA private key K is valid

Steps:

  1. If the message representative m is not between 0 and n - 1, output "message representative out of range" and stop.
  2. Compute the signature representative s like this:

a. If the first form (n, d) of K is used, let s = m^d mod n.

b. If the second form (p, q, dP, dQ, qInv) and (r_i, d_i, t_i) of K is used, do the following:

i. Let s_1 = m^dP mod p and s_2 = m^dQ mod q.

ii. If u > 2, let s_i = m^(d_i) mod r_i, for i = 3, ..., u.

iii. Let h = (s_1 - s_2) * qInv mod p.

iv. Let s = s_2 + q * h.

v. If u > 2, let R = r_1 and for i = 3 to u do:

  1. Let R = R * r_(i-1).
  2. Let h = (s_i - s) * t_i mod r_i.
  3. Let s = s + R * h.
  1. Output s. ๐Ÿฆ‹

Note. Step 2.b can be rewritten as one single loop, as long as you reverse the order of p and q. For consistency with PKCS #1 v2.0 though, the first two primes p and q get handled separately from the additional primes.

5.2.2 RSAVP1

RSAVP1 ((n, e), s)

Input:
(n, e)   RSA public key
s        signature representative, an integer between 0 and n - 1

Output:
m        message representative, an integer between 0 and n - 1

Error: "signature representative out of range"

Assumption: RSA public key (n, e) is valid

Steps:

  1. If the signature representative s is not between 0 and n - 1, output "signature representative out of range" and stop.
  2. Let m = s^e mod n.
  3. Output m. ๐Ÿ’–

6. Overview of schemes โœจ

okay so a scheme is basically when you mix cryptographic primitives and other techniques together to hit one specific security goal ๐Ÿ”๐Ÿ’– this doc defines two kinds: encryption schemes and signature schemes with appendix ๐ŸŽ€

the schemes here are kinda minimal, bestie. all they do is process data with an RSA public or private key. they do NOT cover getting or validating the key ๐Ÿ™ˆ so in real life an app will also need its own key management stuff to pick which RSA keys to use for a scheme operation. those extra details are totally outside this doc ๐ŸŒธ

same as with the primitives in Section 5, the scheme specs assume the inputs are good, especially that the RSA public and private keys are valid โœ… if a key is invalid, the behavior of an implementation is unspecified ๐Ÿ’€ and how bad that is depends on the app. some ways to handle key validation: the app checks explicitly, the public-key infrastructure checks it, or whoever generated the key takes the blame for anything done with a bad key ๐Ÿ˜ญ

a generally good crypto practice is to use a given RSA key pair in only one scheme ๐Ÿ”ฅ this avoids the risk that a weakness in one scheme ruins the security of another, and it may be essential for provable security too ๐Ÿ‘‘ RSAES-PKCS1-v1_5 (Section 7.2) and RSASSA-PKCS1-v1_5 (Section 8.2) have traditionally been used together with no known bad interactions (this is literally the model PKCS #1 v1.5 introduced ๐Ÿงš), but using one key pair for both like that is not recommended for new applications ๐Ÿซถ

let's make this super clear: reusing one key pair across schemes is the thing to avoid ๐Ÿ˜ค

here's the risk in action ๐Ÿฆ‹ say a key pair is used in both RSAES-OAEP (Section 7.1) and RSAES-PKCS1-v1_5. RSAES-OAEP alone would hold up against attacks, but an opponent could exploit a weakness in the RSAES-PKCS1-v1_5 implementation and recover messages encrypted with either scheme ๐Ÿ’€ another one: say a key pair is used in both RSASSA-PSS (Section 8.1) and RSASSA-PKCS1-v1_5. then the security proof for RSASSA-PSS isn't enough anymore, because the proof doesn't account for signatures being made with a second scheme ๐Ÿ˜ญ similar concerns may apply if a key pair is shared between one of the schemes defined here and a variant defined somewhere else ๐ŸŒˆ


7. Encryption schemes ๐Ÿ’œ

okay bestie, for this doc an encryption scheme is made of an encryption operation and a decryption operation ๐ŸŽ€ the encryption operation turns a message into a ciphertext using the recipient's RSA public key, and the decryption operation gets the message back from the ciphertext using the recipient's matching RSA private key ๐Ÿ”‘

encryption schemes show up in all kinds of apps ๐ŸŒท a super typical one is a key establishment protocol, where the message carries key material that needs to get from one party to another confidentially ๐Ÿคซ for example, PKCS #7 [45] uses this kind of protocol to send a content-encryption key from a sender to a recipient, and the encryption schemes here would be suitable key-encryption algorithms in that setup ๐Ÿ“

this doc specifies two encryption schemes: RSAES-OAEP and RSAES-PKCS1-v1_5 ๐Ÿ’… RSAES-OAEP is recommended for new applications โœจ RSAES-PKCS1-v1_5 is only included for compatibility with existing applications, and is not recommended for new applications ๐Ÿฅบ

the encryption schemes follow a general model similar to the one in IEEE Std 1363-2000 [26] ๐Ÿงฉ they combine encryption and decryption primitives with an encoding method for encryption. so for encryption, the operations apply a message encoding operation to the message to get an encoded message, which then gets converted into an integer message representative ๐Ÿ”ข an encryption primitive is applied to that representative to produce the ciphertext โœจ going the other way, the decryption operations apply a decryption primitive to the ciphertext to recover a message representative, which then gets converted into an octet string encoded message ๐Ÿฆ‹ then a message decoding operation is applied to the encoded message to recover the message and check that the decryption was correct ๐Ÿ’–

one more thing, and it's a big one ๐Ÿ‘‘ to avoid implementation weaknesses in how errors are handled inside the decoding operation (see [6] and [36]), the encoding and decoding operations for RSAES-OAEP and RSAES-PKCS1-v1_5 are baked right into the specs of their respective encryption schemes instead of being defined in separate specs ๐ŸŽ€ and both encryption schemes are compatible with the matching schemes in PKCS #1 v2.0 ๐ŸŒˆ

7.1 RSAES-OAEP

Okay bestie, RSAES-OAEP is the glow-up ๐Ÿ’– It combines the RSAEP and RSADP primitives (Sections 5.1.1 and 5.1.2) with the EME-OAEP encoding method (step 1.b in Section 7.1.1 and step 3 in Section 7.1.2). EME-OAEP is based on Bellare and Rogaway's Optimal Asymmetric Encryption scheme [3]. (OAEP stands for "Optimal Asymmetric Encryption Padding.") It's also compatible with the IFES scheme from IEEE Std 1363-2000 [26], where the encryption and decryption primitives are IFEP-RSA and IFDP-RSA and the message encoding method is EME-OAEP โœจ. RSAES-OAEP can handle messages up to k - 2hLen - 2 octets, where hLen is the length of the output from the underlying hash function and k is the length in octets of the recipient's RSA modulus ๐ŸŽ€.

Assuming that computing e-th roots modulo n is infeasible and the mask generation function in RSAES-OAEP has appropriate properties, RSAES-OAEP is semantically secure against adaptive chosen-ciphertext attacks ๐Ÿ”ฅ This assurance is provable, meaning the difficulty of breaking RSAES-OAEP can be directly related to the difficulty of inverting the RSA function, as long as the mask generation function is treated as a black box or random oracle. See [21] and the note below for more tea on that.

Both the encryption and decryption operations of RSAES-OAEP take a label L as input. In this version of PKCS #1, L is the empty string ๐ŸŒธ Other uses of the label are outside the scope of this document. See Appendix A.2.1 for the relevant ASN.1 syntax.

RSAES-OAEP is parameterized by the choice of hash function and mask generation function. That choice should be fixed for a given RSA key ๐Ÿ”’ Suggested hash and mask generation functions are given in Appendix B.

Note. Recent results have clarified the security properties of the OAEP encoding method [3] (roughly the procedure in step 1.b of Section 7.1.1), and it's giving clarity ๐Ÿงš Here's the backstory. In 1994, Bellare and Rogaway [3] introduced a security concept they called plaintext awareness (PA94). They proved that if a deterministic public-key encryption primitive (e.g., RSAEP) is hard to invert without the private key, then the corresponding OAEP-based encryption scheme is plaintext-aware (in the random oracle model). Roughly, that means an adversary can't produce a valid ciphertext without actually "knowing" the underlying plaintext. Plaintext awareness is closely related to how well a scheme resists chosen-ciphertext attacks. In those attacks, an adversary gets to send queries to an oracle that simulates the decryption primitive, and then uses the results to try to decrypt a challenge ciphertext ๐Ÿ‘€

However, there are two flavors of chosen-ciphertext attacks, and PA94 only implies security against one of them ๐Ÿ’€ The difference is what the adversary is allowed to do after receiving the challenge ciphertext. The indifferent attack scenario (denoted CCA1) doesn't allow any queries to the decryption oracle after the adversary gets the challenge ciphertext. The adaptive scenario (denoted CCA2) does allow them, except the decryption oracle refuses to decrypt the challenge ciphertext once it's published. In 1998, Bellare and Rogaway, together with Desai and Pointcheval [2], came up with a new, stronger notion of plaintext awareness (PA98) that does imply security against CCA2 โœจ

To sum up, there are two possible sources of confusion: thinking PA94 and PA98 are equivalent, or thinking CCA1 and CCA2 are equivalent. Either assumption leads to the conclusion that the Bellare-Rogaway paper implies security of OAEP against CCA2, and it doesn't ๐Ÿ™ˆ

(Footnote: It might be fair to mention that PKCS #1 v2.0 cites [3] and claims that "a chosen ciphertext attack is ineffective against a plaintext-aware encryption scheme such as RSAES-OAEP" without specifying the kind of plaintext awareness or chosen ciphertext attack considered.)

OAEP has never been proven secure against CCA2. In fact, Victor Shoup [48] has demonstrated that such a proof doesn't exist in the general case ๐Ÿ˜ญ Put simply, Shoup showed that an adversary in the CCA2 scenario who knows how to partially invert the encryption primitive, but not how to invert it completely, may well be able to break the scheme. For example, you can imagine an attacker who can break RSAES-OAEP if she knows how to recover all but the first 20 bytes of a random integer encrypted with RSAEP. That attacker doesn't need to fully invert RSAEP, because she doesn't use the first 20 octets in her attack.

Still, RSAES-OAEP is secure against CCA2, which was proved by Fujisaki, Okamoto, Pointcheval, and Stern [21] shortly after Shoup's result was announced ๐Ÿฆ‹ Using clever lattice reduction techniques, they showed how to invert RSAEP completely given a sufficiently large part of the pre-image. This observation, combined with a proof that OAEP is secure against CCA2 if the underlying encryption primitive is hard to partially invert, fills the gap between what Bellack and Rogaway proved about RSAES-OAEP and what some people may have believed they proved. Somewhat paradoxically, we're saved by an ostensible weakness in RSAEP (i.e., the whole inverse can be deduced from parts of it) ๐Ÿ‘‘

Unfortunately, the security reduction isn't efficient for concrete parameters. The proof successfully relates an adversary Adv against the CCA2 security of RSAES-OAEP to an algorithm Inv that inverts RSA, but the probability of success for Inv is only approximately \epsilon^2 / 2^18, where \epsilon is the probability of success for Adv.

(Footnote: In [21] the probability of success for the inverter was \epsilon^2 / 4. The additional factor 1 / 2^16 is due to the eight fixed zero bits at the beginning of the encoded message EM, which aren't present in the variant of OAEP considered in [21] (Inv must apply Adv twice to invert RSA, and each application corresponds to a factor 1 / 2^8).)

On top of that, the running time for Inv is approximately t^2, where t is the running time of the adversary. So we can't rule out that attacking RSAES-OAEP is considerably easier than inverting RSA for concrete parameters ๐Ÿ˜ฌ Still, the existence of a security proof gives some assurance that the RSAES-OAEP construction is sounder than ad hoc constructions such as RSAES-PKCS1-v1_5 ๐Ÿ“

Heads up: the security reduction is loose for concrete parameters, so the proof alone doesn't guarantee that attacking RSAES-OAEP is as hard as inverting RSA.

Hybrid encryption schemes based on the RSA-KEM key encapsulation paradigm offer tight proofs of security that apply directly to concrete parameters. See [30] for discussion. Future versions of PKCS #1 may specify schemes based on this paradigm ๐ŸŒˆ

7.1.1 Encryption operation

Here's the big-picture function signature for encryption ๐Ÿ’…

RSAES-OAEP-ENCRYPT ((n, e), M, L)

Options:

  • Hash: hash function (hLen denotes the length in octets of the hash function output)
  • MGF: mask generation function

Input:

  • (n, e): recipient's RSA public key (k denotes the length in octets of the RSA modulus n)
  • M: message to be encrypted, an octet string of length mLen, where mLen <= k - 2hLen - 2
  • L: optional label to be associated with the message; the default value for L, if L is not provided, is the empty string

Output:

  • C: ciphertext, an octet string of length k

Errors:

  • "message too long"
  • "label too long"

Assumption: RSA public key (n, e) is valid ๐Ÿซถ

Steps:

  1. Length checking:
    • a. If the length of L is greater than the input limitation for the hash function (2^61 - 1 octets for SHA-1), output "label too long" and stop.
    • b. If mLen > k - 2hLen - 2, output "message too long" and stop.
  2. EME-OAEP encoding (see Figure 1 below) ๐ŸŽ€
    • a. If the label L is not provided, let L be the empty string. Let lHash = Hash(L), an octet string of length hLen (see the note below).
    • b. Generate an octet string PS consisting of k - mLen - 2hLen - 2 zero octets. The length of PS may be zero.
    • c. Concatenate lHash, PS, a single octet with hexadecimal value 0x01, and the message M to form a data block DB of length k - hLen - 1 octets as:

DB = lHash || PS || 0x01 || M.

  • d. Generate a random octet string seed of length hLen.
  • e. Let dbMask = MGF(seed, k - hLen - 1).
  • f. Let maskedDB = DB \xor dbMask.
  • g. Let seedMask = MGF(maskedDB, hLen).
  • h. Let maskedSeed = seed \xor seedMask.
  • i. Concatenate a single octet with hexadecimal value 0x00, maskedSeed, and maskedDB to form an encoded message EM of length k octets as:

EM = 0x00 || maskedSeed || maskedDB.

  1. RSA encryption ๐Ÿ”ฅ
    • a. Convert the encoded message EM to an integer message representative m (see Section 4.2): m = OS2IP (EM).
    • b. Apply the RSAEP encryption primitive (Section 5.1.1) to the RSA public key (n, e) and the message representative m to produce an integer ciphertext representative c: c = RSAEP ((n, e), m).
    • c. Convert the ciphertext representative c to a ciphertext C of length k octets (see Section 4.1): C = I2OSP (c, k).
  2. Output the ciphertext C โœจ

Note. If L is the empty string, the corresponding hash value lHash has the following hexadecimal representation for different choices of Hash ๐ŸŒธ

SHA-1:   (0x)da39a3ee 5e6b4b0d 3255bfef 95601890 afd80709
SHA-256: (0x)e3b0c442 98fc1c14 9afbf4c8 996fb924 27ae41e4 649b934c
             a495991b 7852b855
SHA-384: (0x)38b060a7 51ac9638 4cd9327e b1b1e36a 21fdb711 14be0743
             4c0cc7bf 63f6e1da 274edebf e76f65fb d51ad2f1 4898b95b
SHA-512: (0x)cf83e135 7eefb8bd f1542850 d66d8007 d620e405 0b5715dc
             83f4a921 d36ce9ce 47d0d13c 5d85f2b0 ff8318d2 877eec2f
             63b931bd 47417a81 a538327a f927da3e

Here's the diagram, because pictures slap ๐Ÿฆ‹

__________________________________________________________________

                             +----------+---------+-------+
                        DB = |  lHash   |    PS   |   M   |
                             +----------+---------+-------+
                                            |
                  +----------+              V
                  |   seed   |--> MGF ---> xor
                  +----------+              |
                        |                   |
               +--+     V                   |
               |00|    xor <----- MGF <-----|
               +--+     |                   |
                 |      |                   |
                 V      V                   V
               +--+----------+----------------------------+
         EM =  |00|maskedSeed|          maskedDB          |
               +--+----------+----------------------------+
__________________________________________________________________

Figure 1: EME-OAEP encoding operation. lHash is the hash of the optional label L. Decoding operation follows reverse steps to recover M and verify lHash and PS. ๐Ÿ’œ

7.1.2. Decryption operation ๐Ÿ”“โœจ

okay bestie, this is the decryption side of RSAES-OAEP, and it's giving careful ๐Ÿ’… Here's the whole setup:

RSAES-OAEP-DECRYPT (K, C, L)

Options:
Hash     hash function (hLen denotes the length in octets of the hash
         function output)
MGF      mask generation function
Input:
K        recipient's RSA private key (k denotes the length in octets
         of the RSA modulus n)
C        ciphertext to be decrypted, an octet string of length k,
         where k = 2hLen + 2
L        optional label whose association with the message is to be
         verified; the default value for L, if L is not provided, is
         the empty string

Output:
M        message, an octet string of length mLen, where mLen <= k -
         2hLen - 2

Error: "decryption error"

Now the steps ๐Ÿงš

  1. Length checking:
    • a. If the length of L is greater than the input limitation for the hash function (2^61 - 1 octets for SHA-1), output "decryption error" and stop.
    • b. If the length of the ciphertext C is not k octets, output "decryption error" and stop.
    • c. If k < 2hLen + 2, output "decryption error" and stop.
  2. RSA decryption:
    • a. Convert the ciphertext C to an integer ciphertext representative c (see Section 4.2): c = OS2IP (C).
    • b. Apply the RSADP decryption primitive (Section 5.1.2) to the RSA private key K and the ciphertext representative c to produce an integer message representative m: m = RSADP (K, c). If RSADP outputs "ciphertext representative out of range" (meaning that c >= n), output "decryption error" and stop.
    • c. Convert the message representative m to an encoded message EM of length k octets (see Section 4.1): EM = I2OSP (m, k).
  3. EME-OAEP decoding:
    • a. If the label L is not provided, let L be the empty string. Let lHash = Hash(L), an octet string of length hLen (see the note in Section 7.1.1).
    • b. Separate the encoded message EM into a single octet Y, an octet string maskedSeed of length hLen, and an octet string maskedDB of length k - hLen - 1 as EM = Y || maskedSeed || maskedDB.
    • c. Let seedMask = MGF(maskedDB, hLen).
    • d. Let seed = maskedSeed \xor seedMask.
    • e. Let dbMask = MGF(seed, k - hLen - 1).
    • f. Let DB = maskedDB \xor dbMask.
    • g. Separate DB into an octet string lHash' of length hLen, a (possibly empty) padding string PS consisting of octets with hexadecimal value 0x00, and a message M as DB = lHash' || PS || 0x01 || M. If there is no octet with hexadecimal value 0x01 to separate PS from M, if lHash does not equal lHash', or if Y is nonzero, output "decryption error" and stop. (See the note below.)
  4. Output the message M.
Note. Care must be taken to ensure that an opponent cannot distinguish the different error conditions in Step 3.g, whether by error message or timing, or, more generally, learn partial information about the encoded message EM. Otherwise an opponent may be able to obtain useful information about the decryption of the ciphertext C, leading to a chosen-ciphertext attack such as the one observed by Manger [36]. ๐Ÿ˜ฑ

7.2. RSAES-PKCS1-v1_5 ๐ŸŒธ

RSAES-PKCS1-v1_5 mixes the RSAEP and RSADP primitives (Sections 5.1.1 and 5.1.2) with the EME-PKCS1-v1_5 encoding method (step 1 in Section 7.2.1 and step 3 in Section 7.2.2). It is mathematically equivalent to the encryption scheme in PKCS #1 v1.5. RSAES-PKCS1-v1_5 can handle messages of length up to k - 11 octets (k is the octet length of the RSA modulus), although care should be taken to avoid certain attacks on low-exponent RSA due to Coppersmith, Franklin, Patarin, and Reiter when long messages are encrypted (see the third bullet in the notes below and [10]; [14] contains an improved attack). As a general rule, the use of this scheme for encrypting an arbitrary message, as opposed to a randomly generated key, is not recommended. ๐Ÿ™ˆ

It's possible to generate valid RSAES-PKCS1-v1_5 ciphertexts without knowing the corresponding plaintexts, with a reasonable probability of success. This ability can be exploited in a chosen-ciphertext attack as shown in [6]. Therefore, if RSAES-PKCS1-v1_5 is to be used, certain easily implemented countermeasures should be taken to thwart the attack found in [6]. Typical examples include the addition of structure to the data to be encoded, rigorous checking of PKCS #1 v1.5 conformance (and other redundancy) in decrypted messages, and the consolidation of error messages in a client-server protocol based on PKCS #1 v1.5. These can all be effective countermeasures and do not involve changes to a PKCS #1 v1.5-based protocol. See [7] for a further discussion of these and other countermeasures. It has recently been shown that the security of the SSL/TLS handshake protocol [17], which uses RSAES-PKCS1-v1_5 and certain countermeasures, can be related to a variant of the RSA problem; see [32] for discussion. ๐Ÿ’€

Note. The following passages describe some security recommendations pertaining to the use of RSAES-PKCS1-v1_5. Recommendations from version 1.5 of this document are included as well as new recommendations motivated by cryptanalytic advances made in the intervening years.
  • It is recommended that the pseudorandom octets in step 2 in Section 7.2.1 be generated independently for each encryption process, especially if the same data is input to more than one encryption process. Haastad's results [24] are one motivation for this recommendation. ๐Ÿ”ฅ
  • The padding string PS in step 2 in Section 7.2.1 is at least eight octets long, which is a security condition for public-key operations that makes it difficult for an attacker to recover data by trying all possible encryption blocks. ๐Ÿ›ก๏ธ
  • The pseudorandom octets can also help thwart an attack due to Coppersmith et al. [10] (see [14] for an improvement of the attack) when the size of the message to be encrypted is kept small. The attack works on low-exponent RSA when similar messages are encrypted with the same RSA public key. More specifically, in one flavor of the attack, when two inputs to RSAEP agree on a large fraction of bits (8/9) and low-exponent RSA (e = 3) is used to encrypt both of them, it may be possible to recover both inputs with the attack. Another flavor of the attack is successful in decrypting a single ciphertext when a large fraction (2/3) of the input to RSAEP is already known. For typical applications, the message to be encrypted is short (e.g., a 128-bit symmetric key) so not enough information will be known or common between two messages to enable the attack. However, if a long message is encrypted, or if part of a message is known, then the attack may be a concern. In any case, the RSAES-OAEP scheme overcomes the attack. ๐Ÿง 

7.2.1. Encryption operation โœจ

RSAES-PKCS1-V1_5-ENCRYPT ((n, e), M)

Input: (n, e) recipient's RSA public key (k denotes the length in octets of the modulus n) M message to be encrypted, an octet string of length mLen, where mLen <= k - 11

Output: C ciphertext, an octet string of length k

Error: "message too long"

Steps:

  1. Length checking: If mLen > k - 11, output "message too long" and stop.
  2. EME-PKCS1-v1_5 encoding:
    • a. Generate an octet string PS of length k - mLen - 3 consisting of pseudo-randomly generated nonzero octets. The length of PS will be at least eight octets.
    • b. Concatenate PS, the message M, and other padding to form an encoded message EM of length k octets as EM = 0x00 || 0x02 || PS || 0x00 || M.
  3. RSA encryption:
    • a. Convert the encoded message EM to an integer message representative m (see Section 4.2): m = OS2IP (EM).
    • b. Apply the RSAEP encryption primitive (Section 5.1.1) to the RSA public key (n, e) and the message representative m to produce an integer ciphertext representative c: c = RSAEP ((n, e), m).
    • c. Convert the ciphertext representative c to a ciphertext C of length k octets (see Section 4.1): C = I2OSP (c, k).
  4. Output the ciphertext C.

7.2.2. Decryption operation ๐Ÿฆ‹

RSAES-PKCS1-V1_5-DECRYPT (K, C)

Input: K recipient's RSA private key C ciphertext to be decrypted, an octet string of length k, where k is the length in octets of the RSA modulus n

Output: M message, an octet string of length at most k - 11

Error: "decryption error"

Steps:

  1. Length checking: If the length of the ciphertext C is not k octets (or if k < 11), output "decryption error" and stop.
  2. RSA decryption:
    • a. Convert the ciphertext C to an integer ciphertext representative c (see Section 4.2): c = OS2IP (C).
    • b. Apply the RSADP decryption primitive (Section 5.1.2) to the RSA private key (n, d) and the ciphertext representative c to produce an integer message representative m: m = RSADP ((n, d), c). If RSADP outputs "ciphertext representative out of range" (meaning that c >= n), output "decryption error" and stop.
    • c. Convert the message representative m to an encoded message EM of length k octets (see Section 4.1): EM = I2OSP (m, k).
  3. EME-PKCS1-v1_5 decoding: Separate the encoded message EM into an octet string PS consisting of nonzero octets and a message M as EM = 0x00 || 0x02 || PS || 0x00 || M. If the first octet of EM does not have hexadecimal value 0x00, if the second octet of EM does not have hexadecimal value 0x02, if there is no octet with hexadecimal value 0x00 to separate PS from M, or if the length of PS is less than 8 octets, output "decryption error" and stop. (See the note below.)
  4. Output M.
Note. Care shall be taken to ensure that an opponent cannot distinguish the different error conditions in Step 3, whether by error message or timing. Otherwise an opponent may be able to obtain useful information about the decryption of the ciphertext C, leading to a strengthened version of Bleichenbacher's attack [6]; compare to Manger's attack [36]. ๐Ÿšจ

8. Signature schemes with appendix โœจ

okay so for this doc, a signature scheme with appendix is basically two operations: a signature generation operation that makes a signature from a message using the signer's RSA private key, and a signature verification operation that checks that signature against the message using the signer's matching RSA public key. ๐Ÿ”‘ To verify one of these, you need the actual message, which is what makes them different from the signature schemes with message recovery. Those ones aren't supported in this document, so we're skipping them ๐Ÿ™…โ€โ™€๏ธ

These appendix schemes are super versatile. For example, they'd be solid signature algorithms for X.509 certificates [28]. Related schemes could also be used in PKCS #7 [45], but the current PKCS #7 version keeps the hash function separate from the signature scheme, which is different from how this doc does it. Check the note in Appendix A.2.3 for the full tea โ˜•

This document specifies two signature schemes with appendix: RSASSA-PSS and RSASSA-PKCS1-v1_5. No attacks are known against RSASSA-PKCS1-v1_5, but for extra robustness, RSASSA-PSS is recommended for eventual adoption in new applications. RSASSA-PKCS1-v1_5 is kept for compatibility with existing applications. It's still appropriate for new applications, but a gradual transition to RSASSA-PSS is encouraged ๐Ÿ’…

The schemes here follow a general model similar to IEEE Std 1363-2000 [26]. They combine signature and verification primitives with an encoding method for signatures. Signature generation applies a message encoding operation to the message to get an encoded message, which then gets converted into an integer message representative. A signature primitive is applied to that representative to make the signature. Going the other way, signature verification applies a signature verification primitive to the signature to recover a message representative, which is then converted into an octet string encoded message. Then a verification operation checks the message and the encoded message to see whether they're consistent ๐Ÿงฉ

If the encoding method is deterministic (like EMSA-PKCS1-v1_5), the verification operation may apply the message encoding operation to the message and compare the result to the encoded message it already derived. If they match, the signature is considered valid โœ… If the method is randomized (like EMSA-PSS), verification is typically more complicated. For example, EMSA-PSS verification pulls the random salt and a hash output out of the encoded message, then checks that the hash output, the salt, and the message are consistent with each other. The hash output is a deterministic function of the message and the salt, so that's what it's checking against ๐ŸŒธ

For both signature schemes with appendix in this document, signature generation and signature verification are readily implemented as "single-pass" operations, as long as the signature is placed after the message. For an example format with RSASSA-PKCS1-v1_5, check PKCS #7 [45] ๐ŸŽ€

8.1 RSASSA-PSS

RSASSA-PSS combines the RSASP1 and RSAVP1 primitives with the EMSA-PSS encoding method. ๐Ÿฆ‹ It's compatible with the IFSSA scheme as amended in the IEEE P1363a draft [27], where the signature and verification primitives are IFSP-RSA1 and IFVP-RSA1 as defined in IEEE Std 1363-2000 [26], and the message encoding method is EMSA4. EMSA4 is slightly more general than EMSA-PSS because it works on bit strings instead of octet strings. EMSA-PSS is equivalent to EMSA4 when the operands, plus the hash and salt values, are all octet strings.

How long a message RSASSA-PSS can handle is either unrestricted or limited by a very large number. It depends on the hash function underneath the EMSA-PSS encoding method ๐Ÿ“

Assuming that computing e-th roots modulo n is infeasible, and that the hash and mask generation functions in EMSA-PSS have the right properties, RSASSA-PSS gives you secure signatures ๐Ÿ” This is provable. The difficulty of forging signatures can be directly tied to the difficulty of inverting the RSA function, as long as the hash and mask generation functions are treated as black boxes or random oracles. The bounds in the security proof are basically "tight," meaning the success probability and running time of the best forger against RSASSA-PSS are very close to the matching parameters for the best RSA inversion algorithm. See [4][13][31] for more on this ๐Ÿค“

Here's the tricky part. RSASSA-PKCS1-v1_5 embeds a hash function identifier in its encoded message, but EMSA-PSS doesn't. So in theory, an adversary could swap in a different (and potentially weaker) hash function than the one the signer picked ๐Ÿ˜ฑ That's why it's recommended that the EMSA-PSS mask generation function be based on the same hash function. This way, the whole encoded message depends on the hash function, and it becomes hard for an opponent to substitute a different one than the signer intended. Matching hash functions only helps prevent hash function substitution. It isn't needed if that substitution is handled some other way, like the verifier accepting only one designated hash function. See [34] for more on all of this. The provable security of RSASSA-PSS doesn't depend on the hash function in the mask generation function being the same as the one applied to the message โœจ

RSASSA-PSS is different from other RSA-based signature schemes because it's probabilistic, not deterministic. It includes a randomly generated salt value. The salt boosts security by giving a "tighter" security proof than deterministic options like Full Domain Hashing (FDH). See [4] for the discussion ๐Ÿ’œ Still, the randomness isn't critical to security. If random generation isn't possible, a fixed value or a sequence number can be used instead, and the resulting provable security is similar to FDH's [12] ๐Ÿ“

8.1.1 Signature generation operation

RSASSA-PSS-SIGN (K, M)

Input:
K        signer's RSA private key
M        message to be signed, an octet string

Output:
S        signature, an octet string of length k, where k is the
         length in octets of the RSA modulus n

Errors: "message too long;" "encoding error"

Steps:

  1. EMSA-PSS encoding: Apply the EMSA-PSS encoding operation (Section 9.1.1) to the message M to produce an encoded message EM of length \ceil ((modBits - 1)/8) octets, such that the bit length of the integer OS2IP (EM) (see Section 4.2) is at most modBits - 1. Here modBits is the length in bits of the RSA modulus n:
EM = EMSA-PSS-ENCODE (M, modBits - 1).

Note that the octet length of EM will be one less than k if modBits - 1 is divisible by 8, and equal to k otherwise. If the encoding operation outputs "message too long," output "message too long" and stop. If the encoding operation outputs "encoding error," output "encoding error" and stop. ๐Ÿ›‘

  1. RSA signature:
    • a. Convert the encoded message EM to an integer message representative m (see Section 4.2):
m = OS2IP (EM).
  • b. Apply the RSASP1 signature primitive (Section 5.2.1) to the RSA private key K and the message representative m to produce an integer signature representative s:
s = RSASP1 (K, m).
  • c. Convert the signature representative s to a signature S of length k octets (see Section 4.1):
S = I2OSP (s, k).
  1. Output the signature S. ๐ŸŽ‰

8.1.2 Signature verification operation

RSASSA-PSS-VERIFY ((n, e), M, S)

Input:
(n, e)   signer's RSA public key
M        message whose signature is to be verified, an octet string
S        signature to be verified, an octet string of length k, where
         k is the length in octets of the RSA modulus n

Output:
"valid signature" or "invalid signature"

Steps:

  1. Length checking: If the length of the signature S is not k octets, output "invalid signature" and stop. ๐Ÿ™…โ€โ™€๏ธ
  2. RSA verification:
    • a. Convert the signature S to an integer signature representative s (see Section 4.2):
s = OS2IP (S).
  • b. Apply the RSAVP1 verification primitive (Section 5.2.2) to the RSA public key (n, e) and the signature representative s to produce an integer message representative m:
m = RSAVP1 ((n, e), s).

If RSAVP1 outputs "signature representative out of range," output "invalid signature" and stop.

  • c. Convert the message representative m to an encoded message EM of length emLen = \ceil ((modBits - 1)/8) octets, where modBits is the length in bits of the RSA modulus n (see Section 4.1):
EM = I2OSP (m, emLen).

Note that emLen will be one less than k if modBits - 1 is divisible by 8, and equal to k otherwise. If I2OSP outputs "integer too large," output "invalid signature" and stop. ๐Ÿ’€

  1. EMSA-PSS verification: Apply the EMSA-PSS verification operation (Section 9.1.2) to the message M and the encoded message EM to determine whether they are consistent:
Result = EMSA-PSS-VERIFY (M, EM, modBits - 1).
  1. If Result = "consistent," output "valid signature." Otherwise, output "invalid signature." ๐Ÿซถ

8.2. RSASSA-PKCS1-v1_5 ๐Ÿ’…

RSASSA-PKCS1-v1_5 is the combo where the RSASP1 and RSAVP1 primitives team up with the EMSA-PKCS1-v1_5 encoding method ๐ŸŽ€ It's giving compatibility with the IFSSA scheme from IEEE Std 1363-2000 [26] โœจ, where the signature and verification primitives are IFSP-RSA1 and IFVP-RSA1 and the message encoding method is EMSA-PKCS1-v1_5. EMSA-PKCS1-v1_5 isn't defined in IEEE Std 1363-2000, but it does show up in the IEEE P1363a draft [27] ๐Ÿ“š

The length of messages RSASSA-PKCS1-v1_5 can operate on is either unrestricted or capped by a very large number. It depends on the hash function underneath EMSA-PKCS1-v1_5 ๐ŸŒˆ

Assuming that computing e-th roots modulo n is infeasible and the hash function in EMSA-PKCS1-v1_5 has appropriate properties, RSASSA-PKCS1-v1_5 is conjectured to provide secure signatures ๐Ÿ”ฅ More precisely, forging signatures without knowing the RSA private key is conjectured to be computationally infeasible ๐Ÿ™ˆ Also, the EMSA-PKCS1-v1_5 encoding method embeds a hash function identifier in the encoding. So an adversary trying to find a message with the same signature as a previously signed message has to find collisions of the particular hash function being used. Attacking a different hash function than the one the signer picked is not useful to them ๐Ÿ’€ See [34] for further discussion ๐Ÿฆ‹

Note. As noted in PKCS #1 v1.5, the EMSA-PKCS1-v1_5 encoding method has the property that the encoded message, converted to an integer message representative, is guaranteed to be large and at least somewhat "random." This prevents attacks of the kind proposed by Desmedt and Odlyzko [16], where multiplicative relationships between message representatives are developed by factoring the message representatives into a set of small values (e.g., a set of small primes). Coron, Naccache, and Stern [15] showed that a stronger form of this type of attack could be quite effective against some instances of the ISO/IEC 9796-2 signature scheme. They also analyzed the complexity of this type of attack against the EMSA-PKCS1-v1_5 encoding method and concluded that an attack would be impractical, requiring more operations than a collision search on the underlying hash function (i.e., more than 2^80 operations). Coppersmith, Halevi, and Jutla [11] subsequently extended Coron et al.'s attack to break the ISO/IEC 9796-1 signature scheme with message recovery. The various attacks illustrate the importance of carefully constructing the input to the RSA signature primitive, particularly in a signature scheme with message recovery. Accordingly, the EMSA-PKCS-v1_5 encoding method explicitly includes a hash operation and is not intended for signature schemes with message recovery. Moreover, while no attack is known against the EMSA-PKCS-v1_5 encoding method, a gradual transition to EMSA-PSS is recommended as a precaution against future developments. ๐Ÿซถ

8.2.1 Signature generation operation โœจ

RSASSA-PKCS1-V1_5-SIGN (K, M)

Input:
K        signer's RSA private key
M        message to be signed, an octet string

Output:
S        signature, an octet string of length k, where k is the
         length in octets of the RSA modulus n

Errors: "message too long"; "RSA modulus too short"

Here's how the steps go ๐Ÿ‘‘

  1. EMSA-PKCS1-v1_5 encoding: Apply the EMSA-PKCS1-v1_5 encoding operation (Section 9.2) to the message M to produce an encoded message EM of length k octets:
EM = EMSA-PKCS1-V1_5-ENCODE (M, k).

If the encoding operation outputs "message too long," output "message too long" and stop. If the encoding operation outputs "intended encoded message length too short," output "RSA modulus too short" and stop.

  1. RSA signature:
    • a. Convert the encoded message EM to an integer message representative m (see Section 4.2):
m = OS2IP (EM).
  • b. Apply the RSASP1 signature primitive (Section 5.2.1) to the RSA private key K and the message representative m to produce an integer signature representative s:
s = RSASP1 (K, m).
  • c. Convert the signature representative s to a signature S of length k octets (see Section 4.1):
S = I2OSP (s, k).
  1. Output the signature S. ๐ŸŽ‰

8.2.2 Signature verification operation ๐Ÿ”

RSASSA-PKCS1-V1_5-VERIFY ((n, e), M, S)

Input:
(n, e)   signer's RSA public key
M        message whose signature is to be verified, an octet string
S        signature to be verified, an octet string of length k, where
         k is the length in octets of the RSA modulus n

Output:
"valid signature" or "invalid signature"

Errors: "message too long"; "RSA modulus too short"

Time to verify, bestie ๐Ÿงš

  1. Length checking: If the length of the signature S is not k octets, output "invalid signature" and stop.
  2. RSA verification:
    • a. Convert the signature S to an integer signature representative s (see Section 4.2):
s = OS2IP (S).
  • b. Apply the RSAVP1 verification primitive (Section 5.2.2) to the RSA public key (n, e) and the signature representative s to produce an integer message representative m:
m = RSAVP1 ((n, e), s).

If RSAVP1 outputs "signature representative out of range," output "invalid signature" and stop.

  • c. Convert the message representative m to an encoded message EM of length k octets (see Section 4.1):
EM' = I2OSP (m, k).

If I2OSP outputs "integer too large," output "invalid signature" and stop.

  1. EMSA-PKCS1-v1_5 encoding: Apply the EMSA-PKCS1-v1_5 encoding operation (Section 9.2) to the message M to produce a second encoded message EM' of length k octets:
EM' = EMSA-PKCS1-V1_5-ENCODE (M, k).

If the encoding operation outputs "message too long," output "message too long" and stop. If the encoding operation outputs "intended encoded message length too short," output "RSA modulus too short" and stop.

  1. Compare the encoded message EM and the second encoded message EM'. If they are the same, output "valid signature"; otherwise, output "invalid signature." ๐Ÿ’–
Note. Another way to implement the signature verification operation is to apply a "decoding" operation (not specified in this document) to the encoded message to recover the underlying hash value, and then to compare it to a newly computed hash value. This has the advantage that it requires less intermediate storage (two hash values rather than two encoded messages), but the disadvantage that it requires additional code. ๐ŸŒธ

9. Encoding methods for signatures with appendix ๐ŸŽ€

Encoding methods are operations that map between octet string messages and octet string encoded messages. Those get converted to and from integer message representatives in the schemes, and the integer message representatives are processed via the primitives. So the encoding methods are the connection between the schemes, which process messages, and the primitives ๐Ÿฆ‹

An encoding method for signatures with appendix, for the purposes of this document, consists of an encoding operation and optionally a verification operation. An encoding operation maps a message M to an encoded message EM of a specified length. A verification operation determines whether a message M and an encoded message EM are consistent, i.e., whether the encoded message EM is a valid encoding of the message M. ๐Ÿ”Ž

The encoding operation may introduce some randomness, so different applications of the encoding operation to the same message will produce different encoded messages, which has benefits for provable security ๐ŸŒˆ For such an encoding method, both an encoding and a verification operation are needed unless the verifier can reproduce the randomness (e.g., by obtaining the salt value from the signer). For a deterministic encoding method, only an encoding operation is needed. ๐Ÿ’…

Two encoding methods for signatures with appendix are employed in the signature schemes and are specified here: EMSA-PSS and EMSA-PKCS1-v1_5. โญ

9.1 EMSA-PSS ๐Ÿ’–

okay so this encoding method has three knobs you set up front: the hash function, the mask generation function, and the salt length. bestie, you should fix these for a given RSA key ๐Ÿ”‘, except the salt length, which is allowed to vary (check [31] if you want the drama on that). suggested hash and mask generation functions live in Appendix B ๐ŸŒธ. it's based on Bellare and Rogaway's Probabilistic Signature Scheme (PSS) [4][5], and it's randomized ๐ŸŽฒ with an encoding operation and a verification operation. it's giving clever. โœจ

here's the encoding operation in one picture (Figure 2) ๐Ÿฆ‹

   __________________________________________________________________

                                  +-----------+
                                  |     M     |
                                  +-----------+
                                        |
                                        V
                                      Hash
                                        |
                                        V
                          +--------+----------+----------+
                     M' = |Padding1|  mHash   |   salt   |
                          +--------+----------+----------+
                                         |
               +--------+----------+     V
         DB =  |Padding2|maskedseed|   Hash
               +--------+----------+     |
                         |               |
                         V               |    +--+
                        xor <--- MGF <---|    |bc|
                         |               |    +--+
                         |               |      |
                         V               V      V
               +-------------------+----------+--+
         EM =  |    maskedDB       |maskedseed|bc|
               +-------------------+----------+--+
   __________________________________________________________________

Figure 2: EMSA-PSS encoding operation. Verification operation follows reverse steps to recover salt, then forward steps to recompute and compare H. ๐ŸŽ€

Notes. ๐Ÿ“

  1. the encoding method here is different from Bellare and Rogaway's submission to IEEE P1363a [5] in three ways, and ngl the differences are kinda iconic ๐Ÿ‘‘:
    • it applies a hash function rather than a mask generation function to the message. even though the mask generation function is built on a hash function, it just feels more natural to apply a hash function directly ๐Ÿงš
    • the value that gets hashed together with the salt is the string (0x)00 00 00 00 00 00 00 00 || mHash, not the message M itself. mHash is the hash of M, and the hash function is the same in both steps (see Note 3 below for more tea โ˜•). also "salt" replaces "seed" because it matches the value's job better ๐Ÿง‚
    • the encoded message in EMSA-PSS has nine fixed bits. the first bit is 0, and the last eight bits form a "trailer field", the octet 0xbc. in the original scheme only the first bit was fixed. the trailer field exists for compatibility with the Rabin-Williams IFSP-RW signature primitive in IEEE Std 1363-2000 [26] and the matching primitive in the draft ISO/IEC 9796-2 [29] ๐Ÿฆ‹
  2. assuming the mask generation function is based on a hash function, it is recommended that the hash function be the same one that gets applied to the message. see Section 8.1 for more discussion ๐Ÿ’…
  3. without messing up the security proof for RSASSA-PSS, you can do steps 1 and 2 of EMSA-PSS-ENCODE and EMSA-PSS-VERIFY (the hash of the message) outside the module that computes the rest of the signature operation. then mHash, not the full message M, is what goes into the module. the security proof for RSASSA-PSS still holds even if an opponent can control the value of mHash ๐Ÿ™ˆ. this is super handy if the module has limited I/O bandwidth, like a smart card ๐Ÿ’ณ. previous versions of PSS [4][5] didn't have this property. and honestly it might be desirable for other security reasons to have the module process the full message, like if it doesn't trust the component that computes the hash, then it needs to "see" what it's signing ๐Ÿ‘€
  4. typical salt lengths in octets are hLen (the length of the output of the hash function Hash) and 0. in both cases the security of RSASSA-PSS can be closely related to how hard it is to invert RSAVP1. Bellare and Rogaway [4] give a tight lower bound for the security of the original RSA-PSS scheme, which roughly matches the first case. Coron [12] gives a lower bound for the related Full Domain Hashing scheme, which roughly matches the second case. in [13] Coron gives a general treatment with salt lengths anywhere from 0 to hLen, so see [27] for discussion. also see [31], which adapts the security proofs in [4][13] to handle the differences between the original and the present version of RSA-PSS listed in Note 1 above ๐Ÿง 
  5. as noted in IEEE P1363a [27], using randomization in signature schemes (like the salt value in EMSA-PSS) may give you a "covert channel" for sending info other than the message being signed ๐Ÿ•ต๏ธโ€โ™€๏ธ. for more on covert channels, see [50]. no thanks to sneaky salts ๐Ÿ™…โ€โ™€๏ธ

9.1.1 Encoding operation ๐ŸŒˆ

Here's the encoding recipe, bestie ๐Ÿ“:

   EMSA-PSS-ENCODE (M, emBits)

   Options:

   Hash     hash function (hLen denotes the length in octets of the hash
            function output)
   MGF      mask generation function
   sLen     intended length in octets of the salt

   Input:
   M        message to be encoded, an octet string
   emBits   maximal bit length of the integer OS2IP (EM) (see Section
            4.2), at least 8hLen + 8sLen + 9

   Output:
   EM       encoded message, an octet string of length emLen = \ceil
            (emBits/8)

   Errors:  "encoding error"; "message too long"

   Steps:

   1.  If the length of M is greater than the input limitation for the
       hash function (2^61 - 1 octets for SHA-1), output "message too
       long" and stop.

   2.  Let mHash = Hash(M), an octet string of length hLen.

   3.  If emLen < hLen + sLen + 2, output "encoding error" and stop.

   4.  Generate a random octet string salt of length sLen; if sLen = 0,
       then salt is the empty string.

   5.  Let
         M' = (0x)00 00 00 00 00 00 00 00 || mHash || salt;

       M' is an octet string of length 8 + hLen + sLen with eight
       initial zero octets.

   6.  Let H = Hash(M'), an octet string of length hLen.

   7.  Generate an octet string PS consisting of emLen - sLen - hLen - 2
       zero octets.  The length of PS may be 0.

   8.  Let DB = PS || 0x01 || salt; DB is an octet string of length
       emLen - hLen - 1.

   9.  Let dbMask = MGF(H, emLen - hLen - 1).

   10. Let maskedDB = DB \xor dbMask.

   11. Set the leftmost 8emLen - emBits bits of the leftmost octet in
       maskedDB to zero.

   12. Let EM = maskedDB || H || 0xbc.

   13. Output EM.

9.1.2 Verification operation ๐Ÿ”

and now the checking part, where we make sure everything lines up ๐Ÿ’…:

   EMSA-PSS-VERIFY (M, EM, emBits)

   Options:
   Hash     hash function (hLen denotes the length in octets of the hash
            function output)
   MGF      mask generation function
   sLen     intended length in octets of the salt

   Input:
   M        message to be verified, an octet string
   EM       encoded message, an octet string of length emLen = \ceil
            (emBits/8)
   emBits   maximal bit length of the integer OS2IP (EM) (see Section
            4.2), at least 8hLen + 8sLen + 9

   Output:
   "consistent" or "inconsistent"

   Steps:

   1.  If the length of M is greater than the input limitation for the
       hash function (2^61 - 1 octets for SHA-1), output "inconsistent"
       and stop.

   2.  Let mHash = Hash(M), an octet string of length hLen.

   3.  If emLen < hLen + sLen + 2, output "inconsistent" and stop.

   4.  If the rightmost octet of EM does not have hexadecimal value
       0xbc, output "inconsistent" and stop.

   5.  Let maskedDB be the leftmost emLen - hLen - 1 octets of EM, and
       let H be the next hLen octets.

   6.  If the leftmost 8emLen - emBits bits of the leftmost octet in
       maskedDB are not all equal to zero, output "inconsistent" and
       stop.

   7.  Let dbMask = MGF(H, emLen - hLen - 1).

   8.  Let DB = maskedDB \xor dbMask.

   9.  Set the leftmost 8emLen - emBits bits of the leftmost octet in DB
       to zero.

   10. If the emLen - hLen - sLen - 2 leftmost octets of DB are not zero
       or if the octet at position emLen - hLen - sLen - 1 (the leftmost
       position is "position 1") does not have hexadecimal value 0x01,
       output "inconsistent" and stop.

   11.  Let salt be the last sLen octets of DB.

   12.  Let
            M' = (0x)00 00 00 00 00 00 00 00 || mHash || salt ;

       M' is an octet string of length 8 + hLen + sLen with eight
       initial zero octets.

   13. Let H' = Hash(M'), an octet string of length hLen.

   14. If H = H', output "consistent." Otherwise, output "inconsistent."

that's the whole EMSA-PSS vibe, bestie, and it's literally so clean ๐ŸŒˆโœจ

9.2 EMSA-PKCS1-v1_5

Okay bestie, this encoding method is deterministic ๐Ÿ”ฎ and it only has an encoding operation. No decoding drama here. โœจ

EMSA-PKCS1-v1_5-ENCODE (M, emLen)

Option:

  • Hash: hash function (hLen is the length in octets of the hash function output)

Input:

  • M: message to be encoded
  • emLen: intended length in octets of the encoded message, at least tLen + 11, where tLen is the octet length of the DER encoding T of a certain value computed during the encoding operation

Output:

  • EM: encoded message, an octet string of length emLen

Errors:

  • "message too long"; "intended encoded message length too short"

Steps:

  1. Hash the message M to get a hash value H ๐Ÿ“:
H = Hash(M).

If the hash function outputs "message too long," output "message too long" and stop. ๐Ÿ›‘

  1. Encode the algorithm ID for the hash function and the hash value into an ASN.1 value of type DigestInfo (see Appendix A.2.4) with the Distinguished Encoding Rules (DER) ๐Ÿ’…. The type DigestInfo has the syntax:
DigestInfo ::= SEQUENCE {
    digestAlgorithm AlgorithmIdentifier,
    digest OCTET STRING
}

The first field identifies the hash function and the second contains the hash value. Let T be the DER encoding of the DigestInfo value (see the notes below) and let tLen be the length in octets of T. ๐ŸŽ€

  1. If emLen < tLen + 11, output "intended encoded message length too short" and stop. ๐Ÿ™ˆ
  2. Generate an octet string PS consisting of emLen - tLen - 3 octets with hexadecimal value 0xff. The length of PS will be at least 8 octets. ๐ŸŒธ
  3. Concatenate PS, the DER encoding T, and other padding to form the encoded message EM as:
EM = 0x00 || 0x01 || PS || 0x00 || T.
  1. Output EM. ๐Ÿ‘‘

Notes.

  1. For the six hash functions mentioned in Appendix B.1, the DER encoding T of the DigestInfo value is equal to the following:
MD2:     (0x)30 20 30 0c 06 08 2a 86 48 86 f7 0d 02 02 05 00 04
             10 || H.
MD5:     (0x)30 20 30 0c 06 08 2a 86 48 86 f7 0d 02 05 05 00 04
             10 || H.
SHA-1:   (0x)30 21 30 09 06 05 2b 0e 03 02 1a 05 00 04 14 || H.
SHA-256: (0x)30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00
             04 20 || H.
SHA-384: (0x)30 41 30 0d 06 09 60 86 48 01 65 03 04 02 02 05 00
             04 30 || H.
SHA-512: (0x)30 51 30 0d 06 09 60 86 48 01 65 03 04 02 03 05 00
                04 40 || H.
  1. In version 1.5 of this document, T was defined as the BER encoding, rather than the DER encoding, of the DigestInfo value. ๐Ÿง In particular, it is possible (at least in theory) that the verification operation defined in this document (as well as in version 2.0) rejects a signature that is valid with respect to the specification given in PKCS #1 v1.5. This happens if rules other than DER are applied to DigestInfo (for example, an indefinite length encoding of the underlying SEQUENCE type). While this is unlikely to be a concern in practice, a cautious implementer MAY choose to employ a verification operation based on a BER decoding operation as specified in PKCS #1 v1.5. In this manner, compatibility with any valid implementation based on PKCS #1 v1.5 is obtained. Such a verification operation SHOULD indicate whether the underlying BER encoding is a DER encoding and hence whether the signature is valid with respect to the specification given in this document. ๐Ÿซถ

Appendix A. ASN.1 syntax

A.1 RSA key representation

This section defines ASN.1 object identifiers for RSA public and private keys, and defines the types RSAPublicKey and RSAPrivateKey. The intended application of these definitions includes X.509 certificates, PKCS #8 [46], and PKCS #12 [47]. ๐Ÿ”‘

The object identifier rsaEncryption identifies RSA public and private keys as defined in Appendices A.1.1 and A.1.2. The parameters field associated with this OID in a value of type AlgorithmIdentifier SHALL have a value of type NULL.

rsaEncryption    OBJECT IDENTIFIER ::= { pkcs-1 1 }

The definitions in this section have been extended to support multi-prime RSA, but are backward compatible with previous versions. โœจ

A.1.1 RSA public key syntax

An RSA public key SHOULD be represented with the ASN.1 type RSAPublicKey:

RSAPublicKey ::= SEQUENCE {
    modulus           INTEGER,  -- n
    publicExponent    INTEGER   -- e
}

The fields of type RSAPublicKey have the following meanings:

  • modulus is the RSA modulus n.
  • publicExponent is the RSA public exponent e.

A.1.2 RSA private key syntax

An RSA private key SHOULD be represented with the ASN.1 type RSAPrivateKey:

RSAPrivateKey ::= SEQUENCE {
    version           Version,
    modulus           INTEGER,  -- n
    publicExponent    INTEGER,  -- e
    privateExponent   INTEGER,  -- d
    prime1            INTEGER,  -- p
    prime2            INTEGER,  -- q
    exponent1         INTEGER,  -- d mod (p-1)
    exponent2         INTEGER,  -- d mod (q-1)
    coefficient       INTEGER,  -- (inverse of q) mod p
    otherPrimeInfos   OtherPrimeInfos OPTIONAL
}

The fields of type RSAPrivateKey have the following meanings:

  • version is the version number, for compatibility with future revisions of this document. It SHALL be 0 for this version of the document, unless multi-prime is used, in which case it SHALL be 1.
Version ::= INTEGER { two-prime(0), multi(1) }
   (CONSTRAINED BY
   {-- version must be multi if otherPrimeInfos present --})
  • modulus is the RSA modulus n.
  • publicExponent is the RSA public exponent e.
  • privateExponent is the RSA private exponent d.
  • prime1 is the prime factor p of n.
  • prime2 is the prime factor q of n.
  • exponent1 is d mod (p - 1).
  • exponent2 is d mod (q - 1).
  • coefficient is the CRT coefficient q^(-1) mod p.
  • otherPrimeInfos contains the information for the additional primes r_3, ..., r_u, in order. It SHALL be omitted if version is 0 and SHALL contain at least one instance of OtherPrimeInfo if version is 1.
OtherPrimeInfos ::= SEQUENCE SIZE(1..MAX) OF OtherPrimeInfo

OtherPrimeInfo ::= SEQUENCE {
    prime             INTEGER,  -- ri
    exponent          INTEGER,  -- di
    coefficient       INTEGER   -- ti
}

The fields of type OtherPrimeInfo have the following meanings:

  • prime is a prime factor r_i of n, where i >= 3.
  • exponent is d_i = d mod (r_i - 1).
  • coefficient is the CRT coefficient t_i = (r_1 * r_2 * ... * r_(i-1))^(-1) mod r_i.

Note. It is important to protect the RSA private key against both disclosure and modification. ๐Ÿ›ก๏ธ Techniques for such protection are outside the scope of this document. Methods for storing and distributing private keys and other cryptographic data are described in PKCS #12 and #15.

A.2 Scheme identification

This section defines object identifiers for the encryption and signature schemes. The schemes compatible with PKCS #1 v1.5 have the same definitions as in PKCS #1 v1.5. The intended application of these definitions includes X.509 certificates and PKCS #7. ๐ŸŒˆ

Here are type identifier definitions for the PKCS #1 OIDs:

PKCS1Algorithms    ALGORITHM-IDENTIFIER ::= {
    { OID rsaEncryption              PARAMETERS NULL } |
    { OID md2WithRSAEncryption       PARAMETERS NULL } |
    { OID md5WithRSAEncryption       PARAMETERS NULL } |
    { OID sha1WithRSAEncryption      PARAMETERS NULL } |
    { OID sha256WithRSAEncryption    PARAMETERS NULL } |
    { OID sha384WithRSAEncryption    PARAMETERS NULL } |
    { OID sha512WithRSAEncryption    PARAMETERS NULL } |
    { OID id-RSAES-OAEP PARAMETERS RSAES-OAEP-params } |
    PKCS1PSourceAlgorithms                             |
    { OID id-RSASSA-PSS PARAMETERS RSASSA-PSS-params } ,
    ...  -- Allows for future expansion --
}

A.2.1 RSAES-OAEP โœจ

Okay bestie, the OID id-RSAES-OAEP is how we say "this is the RSAES-OAEP encryption scheme" ๐ŸŽ€

      id-RSAES-OAEP    OBJECT IDENTIFIER ::= { pkcs-1 7 }

The parameters field attached to this OID in an AlgorithmIdentifier SHALL have a value of type RSAES-OAEP-params, and it looks like this ๐Ÿ’…

      RSAES-OAEP-params ::= SEQUENCE {
          hashAlgorithm     [0] HashAlgorithm    DEFAULT sha1,
          maskGenAlgorithm  [1] MaskGenAlgorithm DEFAULT mgf1SHA1,
          pSourceAlgorithm  [2] PSourceAlgorithm DEFAULT pSpecifiedEmpty
      }

So the fields of RSAES-OAEP-params mean the following ๐Ÿฆ‹

  • hashAlgorithm names the hash function. It SHALL be an algorithm ID with an OID in the set OAEP-PSSDigestAlgorithms. For the hash functions that are supported, check Appendix B.1 ๐Ÿ”ฅ
         HashAlgorithm ::= AlgorithmIdentifier {
            {OAEP-PSSDigestAlgorithms}
         }

         OAEP-PSSDigestAlgorithms    ALGORITHM-IDENTIFIER ::= {
             { OID id-sha1 PARAMETERS NULL   }|
             { OID id-sha256 PARAMETERS NULL }|
             { OID id-sha384 PARAMETERS NULL }|
             { OID id-sha512 PARAMETERS NULL },
             ...  -- Allows for future expansion --
         }

The default hash function is SHA-1 ๐ŸŒธ

         sha1    HashAlgorithm ::= {
             algorithm   id-sha1,
             parameters  SHA1Parameters : NULL
         }

         SHA1Parameters ::= NULL
  • maskGenAlgorithm names the mask generation function. It SHALL be an algorithm ID with an OID in the set PKCS1MGFAlgorithms, which for this version SHALL consist of id-mgf1, the MGF1 mask generation function (see Appendix B.2.1). The parameters field associated with id-mgf1 SHALL be an algorithm ID with an OID in the set OAEP-PSSDigestAlgorithms, naming the hash function that MGF1 is based on. ๐Ÿงš
         MaskGenAlgorithm ::= AlgorithmIdentifier {
            {PKCS1MGFAlgorithms}
         }
         PKCS1MGFAlgorithms    ALGORITHM-IDENTIFIER ::= {
             { OID id-mgf1 PARAMETERS HashAlgorithm },
             ...  -- Allows for future expansion --
         }

The default mask generation function is MGF1 with SHA-1 โœจ

         mgf1SHA1    MaskGenAlgorithm ::= {
             algorithm   id-mgf1,
             parameters  HashAlgorithm : sha1
         }
  • pSourceAlgorithm identifies the source (and possibly the value) of the label L. It SHALL be an algorithm ID with an OID in the set PKCS1PSourceAlgorithms, which for this version SHALL consist of id-pSpecified, meaning the label is specified explicitly. The parameters field associated with id-pSpecified SHALL have a value of type OCTET STRING, holding the label. Older versions of this spec said "encoding parameters" instead of "label", which is why the type below has that name ๐Ÿ“
         PSourceAlgorithm ::= AlgorithmIdentifier {
            {PKCS1PSourceAlgorithms}
         }

         PKCS1PSourceAlgorithms    ALGORITHM-IDENTIFIER ::= {
             { OID id-pSpecified PARAMETERS EncodingParameters },
             ...  -- Allows for future expansion --
         }

         id-pSpecified    OBJECT IDENTIFIER ::= { pkcs-1 9 }

         EncodingParameters ::= OCTET STRING(SIZE(0..MAX))

The default label is an empty string, so lHash ends up holding the hash of the empty string ๐Ÿซถ

         pSpecifiedEmpty    PSourceAlgorithm ::= {
             algorithm   id-pSpecified,
             parameters  EncodingParameters : emptyString
         }

         emptyString    EncodingParameters ::= ''H

If every field of RSAES-OAEP-params uses its default value, the algorithm identifier ends up looking like this ๐Ÿ‘‘

         rSAES-OAEP-Default-Identifier  RSAES-AlgorithmIdentifier ::= {
             algorithm   id-RSAES-OAEP,
             parameters  RSAES-OAEP-params : {
                 hashAlgorithm       sha1,
                 maskGenAlgorithm    mgf1SHA1,
                 pSourceAlgorithm    pSpecifiedEmpty
             }
         }

         RSAES-AlgorithmIdentifier ::= AlgorithmIdentifier {
            {PKCS1Algorithms}
         }

A.2.2 RSAES-PKCS1-v1_5 ๐ŸŒˆ

The OID rsaEncryption (see Appendix A.1) identifies the RSAES-PKCS1-v1_5 encryption scheme. The parameters field attached to this OID in an AlgorithmIdentifier SHALL have a value of type NULL. This is the same as in PKCS #1 v1.5, so nothing new to learn here ๐Ÿฅฐ

      rsaEncryption    OBJECT IDENTIFIER ::= { pkcs-1 1 }

A.2.3 RSASSA-PSS ๐Ÿ’œ

The OID id-RSASSA-PSS identifies the RSASSA-PSS encryption scheme. (That's the name the spec uses, so we're sticking with it ๐Ÿ™ˆ)

      id-RSASSA-PSS    OBJECT IDENTIFIER ::= { pkcs-1 10 }

The parameters field attached to this OID in an AlgorithmIdentifier SHALL have a value of type RSASSA-PSS-params:

      RSASSA-PSS-params ::= SEQUENCE {
          hashAlgorithm      [0] HashAlgorithm    DEFAULT sha1,
          maskGenAlgorithm   [1] MaskGenAlgorithm DEFAULT mgf1SHA1,
          saltLength         [2] INTEGER          DEFAULT 20,
          trailerField       [3] TrailerField     DEFAULT trailerFieldBC
      }

The fields of RSASSA-PSS-params mean the following โœจ

  • hashAlgorithm identifies the hash function. It SHALL be an algorithm ID with an OID in the set OAEP-PSSDigestAlgorithms (see Appendix A.2.1). The default hash function is SHA-1.
  • maskGenAlgorithm identifies the mask generation function. It SHALL be an algorithm ID with an OID in the set PKCS1MGFAlgorithms (see Appendix A.2.1). The default mask generation function is MGF1 with SHA-1. For MGF1 (and more generally, for other mask generation functions based on a hash function), it is RECOMMENDED that the underlying hash function be the same as the one identified by hashAlgorithm. See Note 2 in Section 9.1 for further comments ๐Ÿ”ฅ
  • saltLength is the octet length of the salt. It SHALL be an integer. For a given hashAlgorithm, the default value of saltLength is the octet length of the hash value. Unlike the other fields of type RSASSA-PSS-params, saltLength does not need to be fixed for a given RSA key pair ๐Ÿ“
  • trailerField is the trailer field number, included for compatibility with the draft IEEE P1363a [27]. It SHALL be 1 for this version of the document, which represents the trailer field with hexadecimal value 0xbc. Other trailer fields (including the trailer field HashID || 0xcc in IEEE P1363a) are not supported in this document ๐Ÿฆ‹
         TrailerField ::= INTEGER { trailerFieldBC(1) }

If the default values of hashAlgorithm, maskGenAlgorithm, and trailerField are used, then the algorithm identifier ends up as ๐ŸŽ€

         rSASSA-PSS-Default-Identifier  RSASSA-AlgorithmIdentifier ::= {
             algorithm   id-RSASSA-PSS,
             parameters  RSASSA-PSS-params : {
                 hashAlgorithm       sha1,
                 maskGenAlgorithm    mgf1SHA1,
                 saltLength          20,
                 trailerField        trailerFieldBC
             }
         }

         RSASSA-AlgorithmIdentifier ::=
             AlgorithmIdentifier { {PKCS1Algorithms} }

Note. Okay, this part is lowkey a whole saga ๐Ÿงš Some applications identify the hash function separately from the rest of the signature scheme operations. For example, in PKCS #7 [45], a hash function identifier goes before the message, and a "digest encryption" algorithm identifier (covering the rest of the operations) travels with the signature. For PKCS #7 to support RSASSA-PSS, an OID would need to be defined for the operations after the hash function, the way RSAEncryption works for RSASSA-PKCS1-v1_5. S/MIME CMS [25] does it differently. A hash function identifier goes before the message, but an algorithm identifier for the full signature scheme MAY be carried with a CMS signature (this is how DSA signatures work). Following that convention, the id-RSASSA-PSS OID can be used to identify RSASSA-PSS signatures in CMS. Since CMS is seen as the successor to PKCS #7, and new work like adding RSASSA-PSS support is happening in CMS instead of PKCS #7, this version of PKCS #1 does not define an OID for the "rest of" RSASSA-PSS ๐Ÿ’…

A.2.4 RSASSA-PKCS1-v1_5 ๐Ÿ”ฅ

The object identifier for RSASSA-PKCS1-v1_5 SHALL be one of the following. Which OID you pick depends on the hash algorithm: MD2, MD5, SHA-1, SHA-256, SHA-384, or SHA-512. Note that if MD2 or MD5 is used, the OID is the same as in PKCS #1 v1.5. For each OID, the parameters field attached to it in an AlgorithmIdentifier SHALL have a value of type NULL. The OID SHOULD be chosen according to this table ๐ŸŒธ

      Hash algorithm   OID
      --------------------------------------------------------
      MD2              md2WithRSAEncryption    ::= {pkcs-1 2}
      MD5              md5WithRSAEncryption    ::= {pkcs-1 4}
      SHA-1            sha1WithRSAEncryption   ::= {pkcs-1 5}
      SHA-256          sha256WithRSAEncryption ::= {pkcs-1 11}
      SHA-384          sha384WithRSAEncryption ::= {pkcs-1 12}
      SHA-512          sha512WithRSAEncryption ::= {pkcs-1 13}

The EMSA-PKCS1-v1_5 encoding method includes an ASN.1 value of type DigestInfo, and DigestInfo has this syntax ๐Ÿงš

      DigestInfo ::= SEQUENCE {
          digestAlgorithm DigestAlgorithm,
          digest OCTET STRING
      }

digestAlgorithm identifies the hash function. It SHALL be an algorithm ID with an OID in the set PKCS1-v1-5DigestAlgorithms. For the hash functions that are supported, check Appendix B.1 โœจ

      DigestAlgorithm ::=
          AlgorithmIdentifier { {PKCS1-v1-5DigestAlgorithms} }

      PKCS1-v1-5DigestAlgorithms    ALGORITHM-IDENTIFIER ::= {
          { OID id-md2 PARAMETERS NULL    }|
          { OID id-md5 PARAMETERS NULL    }|
          { OID id-sha1 PARAMETERS NULL   }|
          { OID id-sha256 PARAMETERS NULL }|
          { OID id-sha384 PARAMETERS NULL }|
          { OID id-sha512 PARAMETERS NULL }
      }

Appendix B. Supporting techniques ๐Ÿงš

This appendix gives a bunch of examples of the underlying functions that support the encryption schemes in Section 7 and the encoding methods in Section 9 ๐ŸŽ€ It offers a range of techniques so existing applications stay compatible while people migrate to newer ones. These supporting techniques are fine for applications to implement, but none of them is REQUIRED to be implemented ๐Ÿ’– It's expected that profiles for PKCS #1 v2.1 will be written to pin down particular supporting techniques.

This appendix also gives object identifiers for the supporting techniques ๐Ÿฆ‹

B.1 Hash functions โœจ

okay so hash functions are a big deal in sections 7 and 9 ๐Ÿ”ฅ they're deterministic, which means the output is completely locked in by the input. no surprises, no cap. they take octet strings of any length and spit out a fixed length octet string every time ๐Ÿงš

the hash functions used in sections 7 and 9 should generally be collision-resistant ๐Ÿ’… that means it's infeasible to find two different inputs that give the same output. a collision-resistant hash is also one-way, so given an output, it's infeasible to find an input that hashes to it. iconic behaviour ๐Ÿ˜ญ on top of that, the hash function should yield a mask generation function (Appendix B.2) with pseudorandom output โœจ

six hash functions get used as examples for the encoding methods in this document: MD2 [33], MD5 [41], SHA-1 [38], and the proposed algorithms SHA-256, SHA-384, and SHA-512 [39] ๐ŸŒธ for RSAES-OAEP and EMSA-PSS, only SHA-1 and SHA-256/384/512 are recommended. for EMSA-PKCS1-v1_5, SHA-1 or SHA-256/384/512 are recommended for new applications. MD2 and MD5 are recommended only for compatibility with existing applications that are based on PKCS #1 v1.5 ๐Ÿซถ

the object identifiers id-md2, id-md5, id-sha1, id-sha256, id-sha384, and id-sha512 are how you identify each of these hash functions ๐Ÿฆ‹

      id-md2      OBJECT IDENTIFIER ::= {
          iso(1) member-body(2) us(840) rsadsi(113549)
          digestAlgorithm(2) 2
      }

      id-md5      OBJECT IDENTIFIER ::= {
          iso(1) member-body(2) us(840) rsadsi(113549)
          digestAlgorithm(2) 5
      }

      id-sha1    OBJECT IDENTIFIER ::= {
          iso(1) identified-organization(3) oiw(14) secsig(3)
          algorithms(2) 26
      }

      id-sha256    OBJECT IDENTIFIER ::= {
          joint-iso-itu-t(2) country(16) us(840) organization(1)
          gov(101) csor(3) nistalgorithm(4) hashalgs(2) 1
      }

      id-sha384    OBJECT IDENTIFIER ::= {
          joint-iso-itu-t(2) country(16) us(840) organization(1)
          gov(101) csor(3) nistalgorithm(4) hashalgs(2) 2
      }
      id-sha512    OBJECT IDENTIFIER ::= {
          joint-iso-itu-t(2) country(16) us(840) organization(1)
          gov(101) csor(3) nistalgorithm(4) hashalgs(2) 3
      }

the parameters field linked to id-md2 and id-md5 in an AlgorithmIdentifier value shall have a value of type NULL ๐Ÿ“

the parameters field linked to id-sha1, id-sha256, id-sha384, and id-sha512 should be omitted, but if it's there, it shall have a value of type NULL ๐Ÿ’œ

Note. version 1.5 of PKCS #1 also let you use MD4 in signature schemes ๐Ÿ™ˆ but the cryptanalysis of MD4 has moved along a lot since then. for example, Dobbertin [18] showed how to find collisions for MD4, and that the first two rounds of MD4 aren't one-way [20]. because of those results and others (e.g., [8]), MD4 is no longer recommended ๐Ÿ’€ there have also been advances in the cryptanalysis of MD2 and MD5, but not enough to justify removing them from existing applications. Rogier and Chauvaud [43] showed how to find collisions in a modified version of MD2. nobody has shown how to find collisions for the full MD5 algorithm, though partial results have been found (e.g., [9][19]) ๐Ÿ‘€

so to deal with all this, SHA-1, SHA-256, SHA-384, or SHA-512 are recommended for new applications โœจ as of today, the best known collision attacks against these hash functions are generic attacks with complexity 2^(L/2), where L is the bit length of the hash output. for the signature schemes in this document, a collision attack translates pretty easily into a signature forgery ๐Ÿ˜ญ so the value L / 2 should be at least equal to the desired security level in bits of the signature scheme (a security level of B bits means the best attack has complexity 2^B). the same rule of thumb can be applied to RSAES-OAEP. it's recommended that the bit length of the seed (which equals the bit length of the hash output) be twice the desired security level in bits ๐ŸŽ€


B.2 Mask generation functions ๐ŸŒˆ

a mask generation function takes an octet string of variable length and a desired output length as input, and outputs an octet string of that desired length. there may be restrictions on the lengths of the input and output octet strings, but those bounds are usually super large ๐Ÿงš mask generation functions are deterministic, so the output octet string is completely determined by the input octet string. the output should be pseudorandom: given one part of the output but not the input, it should be infeasible to predict another part of the output ๐Ÿฆ‹ the provable security of RSAES-OAEP and RSASSA-PSS depends on the random nature of the mask generation function's output, which in turn depends on the random nature of the underlying hash ๐Ÿ˜ค

only one mask generation function is given here: MGF1, which is based on a hash function. MGF1 matches the mask generation functions defined in IEEE Std 1363-2000 [26] and the draft ANSI X9.44 [1] โœจ future versions of this document may define other mask generation functions ๐Ÿ‘‘

B.2.1 MGF1 ๐Ÿ’–

MGF1 is a Mask Generation Function based on a hash function ๐ŸŒธ

   MGF1 (mgfSeed, maskLen)

   Options:
   Hash     hash function (hLen denotes the length in octets of the hash
            function output)

   Input:
   mgfSeed  seed from which mask is generated, an octet string
   maskLen  intended length in octets of the mask, at most 2^32 hLen

   Output:
   mask     mask, an octet string of length maskLen

   Error:   "mask too long"

here are the steps, bestie ๐Ÿ’…

  1. If maskLen > 2^32 hLen, output "mask too long" and stop.
  2. Let T be the empty octet string.
  3. For counter from 0 to \ceil (maskLen / hLen) - 1, do the following:
    • a. Convert counter to an octet string C of length 4 octets (see Section 4.1): C = I2OSP (counter, 4) .
    • b. Concatenate the hash of the seed mgfSeed and C to the octet string T: T = T || Hash(mgfSeed || C) .
  4. Output the leading maskLen octets of T as the octet string mask.

the object identifier id-mgf1 identifies the MGF1 mask generation function ๐Ÿงš

id-mgf1    OBJECT IDENTIFIER ::= { pkcs-1 8 }

the parameters field linked to this OID in an AlgorithmIdentifier value shall have a value of type hashAlgorithm, which identifies the hash function MGF1 is based on ๐ŸŽ€


Appendix C. ASN.1 module ๐Ÿฆ‹

PKCS-1 {
    iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs-1(1)
    modules(0) pkcs-1(1)
}

-- $ Revision: 2.1r1 $

-- This module has been checked for conformance with the ASN.1
-- standard by the OSS ASN.1 Tools

DEFINITIONS EXPLICIT TAGS ::=

BEGIN

-- EXPORTS ALL
-- All types and values defined in this module are exported for use
-- in other ASN.1 modules.

IMPORTS

id-sha256, id-sha384, id-sha512
    FROM NIST-SHA2 {
        joint-iso-itu-t(2) country(16) us(840) organization(1)
        gov(101) csor(3) nistalgorithm(4) modules(0) sha2(1)
    };

-- ============================
--   Basic object identifiers
-- ============================

-- The DER encoding of this in hexadecimal is:
-- (0x)06 08
--        2A 86 48 86 F7 0D 01 01
--
pkcs-1    OBJECT IDENTIFIER ::= {

    iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 1
}

--
-- When rsaEncryption is used in an AlgorithmIdentifier the
-- parameters MUST be present and MUST be NULL.
--
rsaEncryption    OBJECT IDENTIFIER ::= { pkcs-1 1 }

--
-- When id-RSAES-OAEP is used in an AlgorithmIdentifier the
-- parameters MUST be present and MUST be RSAES-OAEP-params.
--
id-RSAES-OAEP    OBJECT IDENTIFIER ::= { pkcs-1 7 }

--
-- When id-pSpecified is used in an AlgorithmIdentifier the
-- parameters MUST be an OCTET STRING.
--
id-pSpecified    OBJECT IDENTIFIER ::= { pkcs-1 9 }

-- When id-RSASSA-PSS is used in an AlgorithmIdentifier the
-- parameters MUST be present and MUST be RSASSA-PSS-params.
--
id-RSASSA-PSS    OBJECT IDENTIFIER ::= { pkcs-1 10 }

--
-- When the following OIDs are used in an AlgorithmIdentifier the
-- parameters MUST be present and MUST be NULL.
--
md2WithRSAEncryption       OBJECT IDENTIFIER ::= { pkcs-1 2 }
md5WithRSAEncryption       OBJECT IDENTIFIER ::= { pkcs-1 4 }
sha1WithRSAEncryption      OBJECT IDENTIFIER ::= { pkcs-1 5 }
sha256WithRSAEncryption    OBJECT IDENTIFIER ::= { pkcs-1 11 }
sha384WithRSAEncryption    OBJECT IDENTIFIER ::= { pkcs-1 12 }
sha512WithRSAEncryption    OBJECT IDENTIFIER ::= { pkcs-1 13 }

--
-- This OID really belongs in a module with the secsig OIDs.
--
id-sha1    OBJECT IDENTIFIER ::= {
    iso(1) identified-organization(3) oiw(14) secsig(3)
    algorithms(2) 26
}

--
-- OIDs for MD2 and MD5, allowed only in EMSA-PKCS1-v1_5.
--
id-md2 OBJECT IDENTIFIER ::= {
    iso(1) member-body(2) us(840) rsadsi(113549) digestAlgorithm(2) 2
}

id-md5 OBJECT IDENTIFIER ::= {
    iso(1) member-body(2) us(840) rsadsi(113549) digestAlgorithm(2) 5
}

--
-- When id-mgf1 is used in an AlgorithmIdentifier the parameters MUST
-- be present and MUST be a HashAlgorithm, for example sha1.
--
id-mgf1    OBJECT IDENTIFIER ::= { pkcs-1 8 }

-- ================
--   Useful types
-- ================

ALGORITHM-IDENTIFIER ::= CLASS {
    &id    OBJECT IDENTIFIER  UNIQUE,
    &Type  OPTIONAL
}
    WITH SYNTAX { OID &id [PARAMETERS &Type] }

--
-- Note: the parameter InfoObjectSet in the following definitions
-- allows a distinct information object set to be specified for sets
-- of algorithms such as:
-- DigestAlgorithms    ALGORITHM-IDENTIFIER ::= {
--     { OID id-md2  PARAMETERS NULL }|
--     { OID id-md5  PARAMETERS NULL }|
--     { OID id-sha1 PARAMETERS NULL }
-- }
--

AlgorithmIdentifier { ALGORITHM-IDENTIFIER:InfoObjectSet } ::=
SEQUENCE {
    algorithm  ALGORITHM-IDENTIFIER.&id({InfoObjectSet}),
    parameters
        ALGORITHM-IDENTIFIER.&Type({InfoObjectSet}{@.algorithm})
            OPTIONAL
}

-- ==============
--   Algorithms
-- ==============

--
-- Allowed EME-OAEP and EMSA-PSS digest algorithms.
--
OAEP-PSSDigestAlgorithms    ALGORITHM-IDENTIFIER ::= {
    { OID id-sha1 PARAMETERS NULL   }|
    { OID id-sha256 PARAMETERS NULL }|
    { OID id-sha384 PARAMETERS NULL }|
    { OID id-sha512 PARAMETERS NULL },
    ...  -- Allows for future expansion --
}

--
-- Allowed EMSA-PKCS1-v1_5 digest algorithms.
--
PKCS1-v1-5DigestAlgorithms    ALGORITHM-IDENTIFIER ::= {
    { OID id-md2 PARAMETERS NULL    }|
    { OID id-md5 PARAMETERS NULL    }|
    { OID id-sha1 PARAMETERS NULL   }|
    { OID id-sha256 PARAMETERS NULL }|
    { OID id-sha384 PARAMETERS NULL }|
    { OID id-sha512 PARAMETERS NULL }
}

-- When id-md2 and id-md5 are used in an AlgorithmIdentifier the
-- parameters MUST be present and MUST be NULL.

-- When id-sha1, id-sha256, id-sha384 and id-sha512 are used in an
-- AlgorithmIdentifier the parameters (which are optional) SHOULD
-- be omitted. However, an implementation MUST also accept
-- AlgorithmIdentifier values where the parameters are NULL.

sha1    HashAlgorithm ::= {
    algorithm   id-sha1,
    parameters  SHA1Parameters : NULL  -- included for compatibility
                                       -- with existing implementations
}

HashAlgorithm ::= AlgorithmIdentifier { {OAEP-PSSDigestAlgorithms} }

SHA1Parameters ::= NULL

--
-- Allowed mask generation function algorithms.
-- If the identifier is id-mgf1, the parameters are a HashAlgorithm.
--
PKCS1MGFAlgorithms    ALGORITHM-IDENTIFIER ::= {
    { OID id-mgf1 PARAMETERS HashAlgorithm },
    ...  -- Allows for future expansion --
}
--
-- Default AlgorithmIdentifier for id-RSAES-OAEP.maskGenAlgorithm and
-- id-RSASSA-PSS.maskGenAlgorithm.
--
mgf1SHA1    MaskGenAlgorithm ::= {
    algorithm   id-mgf1,
    parameters  HashAlgorithm : sha1
}

MaskGenAlgorithm ::= AlgorithmIdentifier { {PKCS1MGFAlgorithms} }

--
-- Allowed algorithms for pSourceAlgorithm.
--
PKCS1PSourceAlgorithms    ALGORITHM-IDENTIFIER ::= {
    { OID id-pSpecified PARAMETERS EncodingParameters },
    ...  -- Allows for future expansion --
}

EncodingParameters ::= OCTET STRING(SIZE(0..MAX))

--
-- This identifier means that the label L is an empty string, so the
-- digest of the empty string appears in the RSA block before
-- masking.
--
pSpecifiedEmpty    PSourceAlgorithm ::= {
    algorithm   id-pSpecified,
    parameters  EncodingParameters : emptyString
}

PSourceAlgorithm ::= AlgorithmIdentifier { {PKCS1PSourceAlgorithms} }

emptyString    EncodingParameters ::= ''H

--
-- Type identifier definitions for the PKCS #1 OIDs.
--
PKCS1Algorithms    ALGORITHM-IDENTIFIER ::= {
    { OID rsaEncryption              PARAMETERS NULL } |
    { OID md2WithRSAEncryption       PARAMETERS NULL } |
    { OID md5WithRSAEncryption       PARAMETERS NULL } |
    { OID sha1WithRSAEncryption      PARAMETERS NULL } |
    { OID sha256WithRSAEncryption    PARAMETERS NULL } |
    { OID sha384WithRSAEncryption    PARAMETERS NULL } |
    { OID sha512WithRSAEncryption    PARAMETERS NULL } |
    { OID id-RSAES-OAEP PARAMETERS RSAES-OAEP-params } |
    PKCS1PSourceAlgorithms                             |
    { OID id-RSASSA-PSS PARAMETERS RSASSA-PSS-params } ,
    ...  -- Allows for future expansion --
}

-- ===================
--   Main structures
-- ===================

RSAPublicKey ::= SEQUENCE {
    modulus           INTEGER,  -- n
    publicExponent    INTEGER   -- e
}

--
-- Representation of RSA private key with information for the CRT
-- algorithm.
--
RSAPrivateKey ::= SEQUENCE {
    version           Version,
    modulus           INTEGER,  -- n
    publicExponent    INTEGER,  -- e
    privateExponent   INTEGER,  -- d
    prime1            INTEGER,  -- p
    prime2            INTEGER,  -- q
    exponent1         INTEGER,  -- d mod (p-1)
    exponent2         INTEGER,  -- d mod (q-1)
    coefficient       INTEGER,  -- (inverse of q) mod p
    otherPrimeInfos   OtherPrimeInfos OPTIONAL
}

Version ::= INTEGER { two-prime(0), multi(1) }
    (CONSTRAINED BY {
        -- version must be multi if otherPrimeInfos present --
    })

OtherPrimeInfos ::= SEQUENCE SIZE(1..MAX) OF OtherPrimeInfo

OtherPrimeInfo ::= SEQUENCE {
    prime             INTEGER,  -- ri
    exponent          INTEGER,  -- di
    coefficient       INTEGER   -- ti
}

--
-- AlgorithmIdentifier.parameters for id-RSAES-OAEP.
-- Note that the tags in this Sequence are explicit.
--
RSAES-OAEP-params ::= SEQUENCE {

    hashAlgorithm      [0] HashAlgorithm     DEFAULT sha1,
    maskGenAlgorithm   [1] MaskGenAlgorithm  DEFAULT mgf1SHA1,
    pSourceAlgorithm   [2] PSourceAlgorithm  DEFAULT pSpecifiedEmpty
}

--
-- Identifier for default RSAES-OAEP algorithm identifier.
-- The DER Encoding of this is in hexadecimal:
-- (0x)30 0D
--        06 09
--           2A 86 48 86 F7 0D 01 01 07
--        30 00
-- Notice that the DER encoding of default values is "empty".
--

rSAES-OAEP-Default-Identifier    RSAES-AlgorithmIdentifier ::= {
    algorithm   id-RSAES-OAEP,
    parameters  RSAES-OAEP-params : {
        hashAlgorithm       sha1,
        maskGenAlgorithm    mgf1SHA1,
        pSourceAlgorithm    pSpecifiedEmpty
    }
}

RSAES-AlgorithmIdentifier ::=
    AlgorithmIdentifier { {PKCS1Algorithms} }

--
-- AlgorithmIdentifier.parameters for id-RSASSA-PSS.
-- Note that the tags in this Sequence are explicit.
--
RSASSA-PSS-params ::= SEQUENCE {
    hashAlgorithm      [0] HashAlgorithm      DEFAULT sha1,
    maskGenAlgorithm   [1] MaskGenAlgorithm   DEFAULT mgf1SHA1,
    saltLength         [2] INTEGER            DEFAULT 20,
    trailerField       [3] TrailerField       DEFAULT trailerFieldBC
}

TrailerField ::= INTEGER { trailerFieldBC(1) }

--
-- Identifier for default RSASSA-PSS algorithm identifier
-- The DER Encoding of this is in hexadecimal:
-- (0x)30 0D
--        06 09
--           2A 86 48 86 F7 0D 01 01 0A
--        30 00
-- Notice that the DER encoding of default values is "empty".

--
rSASSA-PSS-Default-Identifier    RSASSA-AlgorithmIdentifier ::= {
    algorithm   id-RSASSA-PSS,
    parameters  RSASSA-PSS-params : {
        hashAlgorithm       sha1,
        maskGenAlgorithm    mgf1SHA1,
        saltLength          20,
        trailerField        trailerFieldBC
    }
}

RSASSA-AlgorithmIdentifier ::=
    AlgorithmIdentifier { {PKCS1Algorithms} }

--
-- Syntax for the EMSA-PKCS1-v1_5 hash identifier.
--
DigestInfo ::= SEQUENCE {
    digestAlgorithm DigestAlgorithm,
    digest OCTET STRING
}

DigestAlgorithm ::=
    AlgorithmIdentifier { {PKCS1-v1-5DigestAlgorithms} }

END  -- PKCS1Definitions

Appendix D. Intellectual Property Considerations โœจ

okay the patent stuff is kinda a whole saga, so let's go through it ๐Ÿ’…

The RSA public-key cryptosystem is described in U.S. Patent 4,405,829, and that one expired on September 20, 2000. ๐Ÿฆ‹ RSA Security Inc. says it makes no other patent claims on the constructions in this document, although specific underlying techniques may still be covered, so keep that in mind ๐Ÿ‘€

Multi-prime RSA is described in U.S. Patent 5,848,159. ๐ŸŒธ

The University of California has a patent pending on the PSS signature scheme [5]. It also sent a letter to the IEEE P1363 working group saying that if PSS ends up in an IEEE standard, "the University of California will, when that standard is adopted, FREELY license any conforming implementation of PSS as a technique for achieving a digital signature with appendix" [23]. That's giving generous energy ๐Ÿซถ The PSS signature scheme is specified in the IEEE P1363a draft [27], which was still in ballot resolution when this document was published ๐ŸŒˆ

Copying this document is allowed, as long as it's identified as "RSA Security Inc. Public-Key Cryptography Standards (PKCS)" in all material that mentions or references it ๐Ÿ“„

RSA Security Inc. makes no other representations about intellectual property claims by other parties. Figuring out whether those claims apply is the responsibility of the user, so do your homework bestie ๐ŸŽ€

Appendix E. Revision history ๐Ÿงš

Versions 1.0 - 1.3

Versions 1.0 - 1.3 went out to participants in RSA Data Security, Inc.'s Public-Key Cryptography Standards meetings in February and March 1991 ๐Ÿ“

Version 1.4

Version 1.4 was part of the June 3, 1991 initial public release of PKCS ๐ŸŽ‰ It got published as NIST/OSI Implementors' Workshop document SEC-SIG-91-18.

Version 1.5

Version 1.5 had a bunch of editorial changes, including updated references and a brand new revision history section. The substantive changes were:

  • Section 10: "MD4 with RSA" signature and verification processes were added. ๐Ÿ”ฅ
  • Section 11: the md4WithRSAEncryption object identifier was added. ๐Ÿ’œ

Version 1.5 was republished as IETF RFC 2313 โœจ

Version 2.0

Version 2.0 was a major editorial glow-up in terms of document structure, and it introduced the RSAES-OAEP encryption scheme ๐Ÿ‘‘ It kept supporting the encryption and signature processes from version 1.5, with one big change: MD4 was no longer allowed, because cryptanalytic advances in the years since made it unsafe to keep around ๐Ÿ’€ Version 2.0 was republished as IETF RFC 2437 [35].

Version 2.1

Version 2.1 brings in multi-prime RSA and the RSASSA-PSS signature scheme with appendix, plus several editorial improvements ๐ŸŒธ It keeps supporting the schemes from version 2.0, so nothing got left behind ๐Ÿซถ

Appendix F: References โœจ

okay bestie, this is the reading list ๐Ÿ“š๐Ÿ’… fifty sources, and honestly the security papers in here are where the drama lives ๐Ÿ’€ every citation is kept exactly as written so you can go find the originals ๐Ÿซถ

  • [1] ANSI X9F1 Working Group. ANSI X9.44 Draft D2: Key Establishment Using Integer Factorization Cryptography. Working Draft, March 2002.
  • [2] M. Bellare, A. Desai, D. Pointcheval and P. Rogaway. Relations Among Notions of Security for Public-Key Encryption Schemes. In H. Krawczyk, editor, Advances in Cryptology - Crypto '98, volume 1462 of Lecture Notes in Computer Science, pp. 26 - 45. Springer Verlag, 1998.
  • [3] M. Bellare and P. Rogaway. Optimal Asymmetric Encryption - How to Encrypt with RSA. In A. De Santis, editor, Advances in Cryptology - Eurocrypt '94, volume 950 of Lecture Notes in Computer Science, pp. 92 - 111. Springer Verlag, 1995.
  • [4] M. Bellare and P. Rogaway. The Exact Security of Digital Signatures - How to Sign with RSA and Rabin. In U. Maurer, editor, Advances in Cryptology - Eurocrypt '96, volume 1070 of Lecture Notes in Computer Science, pp. 399 - 416. Springer Verlag, 1996.
  • [5] M. Bellare and P. Rogaway. PSS: Provably Secure Encoding Method for Digital Signatures. Submission to IEEE P1363 working group, August 1998. Available from http://grouper.ieee.org/groups/1363/.
  • [6] D. Bleichenbacher. Chosen Ciphertext Attacks Against Protocols Based on the RSA Encryption Standard PKCS #1. In H. Krawczyk, editor, Advances in Cryptology - Crypto '98, volume 1462 of Lecture Notes in Computer Science, pp. 1 - 12. Springer Verlag, 1998.
  • [7] D. Bleichenbacher, B. Kaliski and J. Staddon. Recent Results on PKCS #1: RSA Encryption Standard. RSA Laboratories' Bulletin No. 7, June 1998.
  • [8] B. den Boer and A. Bosselaers. An Attack on the Last Two Rounds of MD4. In J. Feigenbaum, editor, Advances in Cryptology - Crypto '91, volume 576 of Lecture Notes in Computer Science, pp. 194 - 203. Springer Verlag, 1992.
  • [9] B. den Boer and A. Bosselaers. Collisions for the Compression Function of MD5. In T. Helleseth, editor, Advances in Cryptology - Eurocrypt '93, volume 765 of Lecture Notes in Computer Science, pp. 293 - 304. Springer Verlag, 1994.
  • [10] D. Coppersmith, M. Franklin, J. Patarin and M. Reiter. Low-Exponent RSA with Related Messages. In U. Maurer, editor, Advances in Cryptology - Eurocrypt '96, volume 1070 of Lecture Notes in Computer Science, pp. 1 - 9. Springer Verlag, 1996.
  • [11] D. Coppersmith, S. Halevi and C. Jutla. ISO 9796-1 and the New Forgery Strategy. Presented at the rump session of Crypto '99, August 1999.
  • [12] J.-S. Coron. On the Exact Security of Full Domain Hashing. In M. Bellare, editor, Advances in Cryptology - Crypto 2000, volume 1880 of Lecture Notes in Computer Science, pp. 229 - 235. Springer Verlag, 2000.
  • [13] J.-S. Coron. Optimal Security Proofs for PSS and Other Signature Schemes. In L. Knudsen, editor, Advances in Cryptology - Eurocrypt 2002, volume 2332 of Lecture Notes in Computer Science, pp. 272 - 287. Springer Verlag, 2002.
  • [14] J.-S. Coron, M. Joye, D. Naccache and P. Paillier. New Attacks on PKCS #1 v1.5 Encryption. In B. Preneel, editor, Advances in Cryptology - Eurocrypt 2000, volume 1807 of Lecture Notes in Computer Science, pp. 369 - 379. Springer Verlag, 2000.
  • [15] J.-S. Coron, D. Naccache and J. P. Stern. On the Security of RSA Padding. In M. Wiener, editor, Advances in Cryptology - Crypto '99, volume 1666 of Lecture Notes in Computer Science, pp. 1 - 18. Springer Verlag, 1999.
  • [16] Y. Desmedt and A.M. Odlyzko. A Chosen Text Attack on the RSA Cryptosystem and Some Discrete Logarithm Schemes. In H.C. Williams, editor, Advances in Cryptology - Crypto '85, volume 218 of Lecture Notes in Computer Science, pp. 516 - 522. Springer Verlag, 1986.
  • [17] Dierks, T. and C. Allen, "The TLS Protocol, Version 1.0", RFC 2246, January 1999.
  • [18] H. Dobbertin. Cryptanalysis of MD4. In D. Gollmann, editor, Fast Software Encryption '96, volume 1039 of Lecture Notes in Computer Science, pp. 55 - 72. Springer Verlag, 1996.
  • [19] H. Dobbertin. Cryptanalysis of MD5 Compress. Presented at the rump session of Eurocrypt '96, May 1996.
  • [20] H. Dobbertin. The First Two Rounds of MD4 are Not One-Way. In S. Vaudenay, editor, Fast Software Encryption '98, volume 1372 in Lecture Notes in Computer Science, pp. 284 - 292. Springer Verlag, 1998.
  • [21] E. Fujisaki, T. Okamoto, D. Pointcheval and J. Stern. RSA-OAEP is Secure under the RSA Assumption. In J. Kilian, editor, Advances in Cryptology - Crypto 2001, volume 2139 of Lecture Notes in Computer Science, pp. 260 - 274. Springer Verlag, 2001.
  • [22] H. Garner. The Residue Number System. IRE Transactions on Electronic Computers, EC-8 (6), pp. 140 - 147, June 1959.
  • [23] M.L. Grell. Re: Encoding Methods PSS/PSS-R. Letter to IEEE P1363 working group, University of California, June 15, 1999. Available from http://grouper.ieee.org/groups/1363/P1363/patents.html.
  • [24] J. Haastad. Solving Simultaneous Modular Equations of Low Degree. SIAM Journal of Computing, volume 17, pp. 336 - 341, 1988.
  • [25] Housley, R., "Cryptographic Message Syntax (CMS)", RFC 3369, August 2002. Housley, R., "Cryptographic Message Syntax (CMS) Algorithms", RFC 3370, August 2002.
  • [26] IEEE Std 1363-2000: Standard Specifications for Public Key Cryptography. IEEE, August 2000.
  • [27] IEEE P1363 working group. IEEE P1363a D11: Draft Standard Specifications for Public Key Cryptography -- Amendment 1: Additional Techniques. December 16, 2002. Available from http://grouper.ieee.org/groups/1363/.
  • [28] ISO/IEC 9594-8:1997: Information technology - Open Systems Interconnection - The Directory: Authentication Framework. 1997.
  • [29] ISO/IEC FDIS 9796-2: Information Technology - Security Techniques - Digital Signature Schemes Giving Message Recovery - Part 2: Integer Factorization Based Mechanisms. Final Draft International Standard, December 2001.
  • [30] ISO/IEC 18033-2: Information Technology - Security Techniques - Encryption Algorithms - Part 2: Asymmetric Ciphers. V. Shoup, editor, Text for 2nd Working Draft, January 2002.
  • [31] J. Jonsson. Security Proof for the RSA-PSS Signature Scheme (extended abstract). Second Open NESSIE Workshop. September 2001. Full version available from http://eprint.iacr.org/2001/053/.
  • [32] J. Jonsson and B. Kaliski. On the Security of RSA Encryption in TLS. In M. Yung, editor, Advances in Cryptology - CRYPTO 2002, vol. 2442 of Lecture Notes in Computer Science, pp. 127 - 142. Springer Verlag, 2002.
  • [33] Kaliski, B., "The MD2 Message-Digest Algorithm", RFC 1319, April 1992.
  • [34] B. Kaliski. On Hash Function Identification in Signature Schemes. In B. Preneel, editor, RSA Conference 2002, Cryptographers' Track, volume 2271 of Lecture Notes in Computer Science, pp. 1 - 16. Springer Verlag, 2002.
  • [35] Kaliski, B. and J. Staddon, "PKCS #1: RSA Cryptography Specifications Version 2.0", RFC 2437, October 1998.
  • [36] J. Manger. A Chosen Ciphertext Attack on RSA Optimal Asymmetric Encryption Padding (OAEP) as Standardized in PKCS #1 v2.0. In J. Kilian, editor, Advances in Cryptology - Crypto 2001, volume 2139 of Lecture Notes in Computer Science, pp. 260 - 274. Springer Verlag, 2001.
  • [37] A. Menezes, P. van Oorschot and S. Vanstone. Handbook of Applied Cryptography. CRC Press, 1996.
  • [38] National Institute of Standards and Technology (NIST). FIPS Publication 180-1: Secure Hash Standard. April 1994.
  • [39] National Institute of Standards and Technology (NIST). Draft FIPS 180-2: Secure Hash Standard. Draft, May 2001. Available from http://www.nist.gov/sha/.
  • [40] J.-J. Quisquater and C. Couvreur. Fast Decipherment Algorithm for RSA Public-Key Cryptosystem. Electronics Letters, 18 (21), pp. 905 - 907, October 1982.
  • [41] Rivest, R., "The MD5 Message-Digest Algorithm", RFC 1321, April 1992.
  • [42] R. Rivest, A. Shamir and L. Adleman. A Method for Obtaining Digital Signatures and Public-Key Cryptosystems. Communications of the ACM, 21 (2), pp. 120-126, February 1978.
  • [43] N. Rogier and P. Chauvaud. The Compression Function of MD2 is not Collision Free. Presented at Selected Areas of Cryptography '95. Carleton University, Ottawa, Canada. May 1995.
  • [44] RSA Laboratories. PKCS #1 v2.0: RSA Encryption Standard. October 1998.
  • [45] RSA Laboratories. PKCS #7 v1.5: Cryptographic Message Syntax Standard. November 1993. (Republished as IETF RFC 2315.)
  • [46] RSA Laboratories. PKCS #8 v1.2: Private-Key Information Syntax Standard. November 1993.
  • [47] RSA Laboratories. PKCS #12 v1.0: Personal Information Exchange Syntax Standard. June 1999.
  • [48] V. Shoup. OAEP Reconsidered. In J. Kilian, editor, Advances in Cryptology - Crypto 2001, volume 2139 of Lecture Notes in Computer Science, pp. 239 - 259. Springer Verlag, 2001.
  • [49] R. D. Silverman. A Cost-Based Security Analysis of Symmetric and Asymmetric Key Lengths. RSA Laboratories Bulletin No. 13, April 2000. Available from http://www.rsasecurity.com.rsalabs/bulletins/.
  • [50] G. J. Simmons. Subliminal communication is easy using the DSA. In T. Helleseth, editor, Advances in Cryptology - Eurocrypt '93, volume 765 of Lecture Notes in Computer Science, pp. 218- 232. Springer-Verlag, 1993.

Appendix G: About PKCS ๐ŸŽ€

so, the Public-Key Cryptography Standards are specs made by RSA Laboratories together with secure systems developers all over the world ๐ŸŒ the goal was to speed up how fast public-key crypto gets deployed, and it's giving main character energy ๐Ÿ‘‘ they first came out in 1991 after meetings with a small crew of early adopters, and now the PKCS docs are everywhere โœจ

a bunch of their stuff ended up inside many formal and de facto standards, like ANSI X9 and IEEE P1363 documents, PKIX, SET, S/MIME, SSL/TLS, and WAP/WTLS. iconic ๐Ÿ”ฅ

further development happens through mailing list discussions and the occasional workshop, and suggestions for improvement are welcome ๐Ÿซถ if you want to reach out, here's the contact info:

PKCS Editor
RSA Laboratories
174 Middlesex Turnpike
Bedford, MA  01730 USA
pkcs-editor@rsasecurity.com
http://www.rsasecurity.com/rsalabs/pkcs

Appendix H: Corrections Made During RFC Publication Process โœจ

okay so bestie, these are the fixes that got made when the PKCS #1 v2.1 doc was turned into this RFC ๐ŸŽ€

  • The requirement that the parameters in an AlgorithmIdentifier value for id-sha1, id-sha256, id-sha384, and id-sha512 be NULL got switched to a recommendation that the parameters be omitted (while still allowing the parameters to be NULL). This lines up with the definitions NIST originally put out ๐Ÿ’… Implementations MUST accept AlgorithmIdentifier values both without parameters and with NULL parameters. ๐Ÿซถ
  • The notes after RSADP and RSASP1 (Secs. 5.1.2 and 5.2.1) now point to step 2.b instead of 2.a ๐Ÿ™ˆ
  • References [25], [27] and [32] got updated with new publication data ๐Ÿ“š

These corrections will be reflected in future editions of PKCS #1 v2.1 ๐ŸŒธ


Security Considerations ๐Ÿ”’

Security is the whole vibe here, bestie ๐Ÿ˜ญ Security issues are discussed throughout this memo, so don't skip anything ๐Ÿ’€

Acknowledgements ๐ŸŽ€

This document is based on a contribution of RSA Laboratories, the research center of RSA Security Inc. ๐Ÿ’– Any substantial use of the text from this document must acknowledge RSA Security Inc. RSA Security Inc. requests that all material mentioning or referencing this document identify this as "RSA Security Inc. PKCS #1 v2.1" ๐Ÿซถ


Authors' Addresses ๐Ÿฆ‹

Here are the people who made this happen, with their contact info โœจ

Jakob Jonsson
Philipps-Universitaet Marburg
Fachbereich Mathematik und Informatik
Hans Meerwein Strasse, Lahnberge
DE-35032 Marburg
Germany

Phone: +49 6421 28 25672
EMail: jonsson@mathematik.uni-marburg.de


Burt Kaliski
RSA Laboratories
174 Middlesex Turnpike
Bedford, MA 01730 USA

Phone: +1 781 515 7073
EMail: bkaliski@rsasecurity.com

Legal stuff, but it matters, so read it ๐Ÿซถ

Copyright (C) The Internet Society 2003. All Rights Reserved.

This document and translations of it may be copied and furnished to others provided that the above copyright notice and this paragraph are included on all such copies. However, this document itself may not be modified in any way, such as by removing the copyright notice or references to the Internet Society or other Internet organizations, except as required to translate it into languages other than English.

The limited permissions granted above are perpetual and will not be revoked by the Internet Society or its successors or assigns.

This document and the information contained herein is provided on an "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.


Acknowledgement ๐ŸŒˆ

Funding for the RFC Editor function is currently provided by the Internet Society ๐Ÿ’œ

๐Ÿ“ฆ where this page comes from