Skip to content

ASN.1 encoders produce zero content for non-optional items #16026

@levitte

Description

@levitte

i2d_RSAPrivateKey() will happily encode an RSA structure where only n and e are present, i.e. a public key. The following program demonstrates it:

#include <stdio.h>

#include <openssl/bn.h>
#include <openssl/rsa.h>
#include <openssl/x509.h>

static int TEST(const char *expr, int b)
{
  if (b)
    return 1;
  fprintf(stderr, "Failed '%s'\n", expr);
  return 0;
}

#define TEST_true(e) TEST(#e " != 0", (e) != 0)
#define TEST_ptr(p) TEST(#p " != NULL", (p) != NULL)
#define TEST_int_le(a,b) TEST(#a " <= " #b, (a) <= (b))

int main()
{
  RSA *rsa = NULL;
  BIGNUM *n = NULL;
  BIGNUM *e = NULL;
  int ok = 0;

  if (!TEST_ptr(rsa = RSA_new())
      || !TEST_ptr(n = BN_new())
      || !TEST_true(BN_set_word(n, 123456))
      || !TEST_ptr(e = BN_new())
      || !TEST_true(BN_set_word(e, 0x10001))
      || !TEST_true(RSA_set0_key(rsa, n, e, NULL)))
    goto end;

  n = e = NULL;                /* They are now "owned" by |rsa| */

  /*
   * This SHOULD fail, as we're trying to encode a public key as a private
   * key.  The private key bits MUST be present for a proper RSAPrivateKey.
   */
  if (TEST_int_le(i2d_RSAPrivateKey_fp(stdout, rsa), 0))
    ok = 1;

 end:
  RSA_free(rsa);
  BN_free(n);
  BN_free(e);
  return !ok;             /* exit code 0 for ok == 1 and vice versa */
}
$ cc -o asn1-RSAPrivateKey asn1-RSAPrivateKey.c -lcrypto
$ ./asn1-RSAPrivateKey > foo.der
Failed 'i2d_RSAPrivateKey_fp(stdout, rsa) <= 0'
$ echo $?
1
$ openssl asn1parse -i -inform d -in foo.der
    0:d=0  hl=2 l=  13 cons: SEQUENCE          
    2:d=1  hl=2 l=   1 prim:  INTEGER           :00
    5:d=1  hl=2 l=   3 prim:  INTEGER           :01E240
   10:d=1  hl=2 l=   3 prim:  INTEGER           :010001

The ASN.1 structure (https://datatracker.ietf.org/doc/html/rfc8017#appendix-A.1.2) is this:

         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
         }

This isn't specifically about RSA, it's deep in the ASN.1 encoder, where the code for primitives is a bit lax with regards to zero content... it doesn't check if the item is optional or not.

Metadata

Metadata

Assignees

No one assigned

    Labels

    branch: 1.1.1Applies to OpenSSL_1_1_1-stable branch (EOL)branch: masterApplies to master branchtriaged: bugThe issue/pr is/fixes a bug

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions