Implement encoding/decoding RSA public keys.

diff --git a/dlls/crypt32/encode.c b/dlls/crypt32/encode.c
index 877ffca..aeb2bc8 100644
--- a/dlls/crypt32/encode.c
+++ b/dlls/crypt32/encode.c
@@ -1690,6 +1690,47 @@
     return ret;
 }
 
+static BOOL WINAPI CRYPT_AsnEncodeRsaPubKey(DWORD dwCertEncodingType,
+ LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags,
+ PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded)
+{
+    BOOL ret;
+
+    __TRY
+    {
+        const BLOBHEADER *hdr =
+         (const BLOBHEADER *)pvStructInfo;
+
+        if (hdr->bType != PUBLICKEYBLOB)
+        {
+            SetLastError(HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER));
+            ret = FALSE;
+        }
+        else
+        {
+            const RSAPUBKEY *rsaPubKey = (const RSAPUBKEY *)
+             ((const BYTE *)pvStructInfo + sizeof(BLOBHEADER));
+            CRYPT_INTEGER_BLOB blob = { rsaPubKey->bitlen / 8,
+             (BYTE *)pvStructInfo + sizeof(BLOBHEADER) + sizeof(RSAPUBKEY) };
+            struct AsnEncodeSequenceItem items[] = { 
+             { &blob, CRYPT_AsnEncodeInteger, 0 },
+             { &rsaPubKey->pubexp, CRYPT_AsnEncodeInt, 0 },
+            };
+
+            ret = CRYPT_AsnEncodeSequence(dwCertEncodingType, items,
+             sizeof(items) / sizeof(items[0]), dwFlags, pEncodePara, pbEncoded,
+             pcbEncoded);
+        }
+    }
+    __EXCEPT(page_fault)
+    {
+        SetLastError(STATUS_ACCESS_VIOLATION);
+        ret = FALSE;
+    }
+    __ENDTRY
+    return ret;
+}
+
 static BOOL WINAPI CRYPT_AsnEncodeOctets(DWORD dwCertEncodingType,
  LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags,
  PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded)
@@ -2277,6 +2318,9 @@
         case (WORD)X509_BASIC_CONSTRAINTS2:
             encodeFunc = CRYPT_AsnEncodeBasicConstraints2;
             break;
+        case (WORD)RSA_CSP_PUBLICKEYBLOB:
+            encodeFunc = CRYPT_AsnEncodeRsaPubKey;
+            break;
         case (WORD)X509_OCTET_STRING:
             encodeFunc = CRYPT_AsnEncodeOctets;
             break;
@@ -2404,8 +2448,16 @@
     }
     else if (pbEncoded[1] <= 0x7f)
     {
-        *len = pbEncoded[1];
-        ret = TRUE;
+        if (pbEncoded[1] + 1 > cbEncoded)
+        {
+            SetLastError(CRYPT_E_ASN1_EOD);
+            ret = FALSE;
+        }
+        else
+        {
+            *len = pbEncoded[1];
+            ret = TRUE;
+        }
     }
     else
     {
@@ -4356,6 +4408,80 @@
     return ret;
 }
 
+#define RSA1_MAGIC 0x31415352
+
+struct DECODED_RSA_PUB_KEY
+{
+    DWORD              pubexp;
+    CRYPT_INTEGER_BLOB modulus;
+};
+
+static BOOL WINAPI CRYPT_AsnDecodeRsaPubKey(DWORD dwCertEncodingType,
+ LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
+ PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
+{
+    BOOL ret;
+
+    __TRY
+    {
+        struct AsnDecodeSequenceItem items[] = {
+         { offsetof(struct DECODED_RSA_PUB_KEY, modulus),
+           CRYPT_AsnDecodeIntegerInternal, sizeof(CRYPT_INTEGER_BLOB),
+           FALSE, TRUE, offsetof(struct DECODED_RSA_PUB_KEY, modulus.pbData),
+           0 },
+         { offsetof(struct DECODED_RSA_PUB_KEY, pubexp),
+           CRYPT_AsnDecodeInt, sizeof(DWORD), FALSE, FALSE, 0, 0 },
+        };
+        struct DECODED_RSA_PUB_KEY *decodedKey = NULL;
+        DWORD size = 0;
+
+        ret = CRYPT_AsnDecodeSequence(dwCertEncodingType, items,
+         sizeof(items) / sizeof(items[0]), pbEncoded, cbEncoded,
+         CRYPT_DECODE_ALLOC_FLAG, NULL, &decodedKey, &size, NULL);
+        if (ret)
+        {
+            DWORD bytesNeeded = sizeof(BLOBHEADER) + sizeof(RSAPUBKEY) +
+             decodedKey->modulus.cbData;
+
+            if (!pvStructInfo)
+            {
+                *pcbStructInfo = bytesNeeded;
+                ret = TRUE;
+            }
+            else if ((ret = CRYPT_DecodeEnsureSpace(dwFlags, pDecodePara,
+             pvStructInfo, pcbStructInfo, bytesNeeded)))
+            {
+                BLOBHEADER *hdr;
+                RSAPUBKEY *rsaPubKey;
+
+                if (dwFlags & CRYPT_DECODE_ALLOC_FLAG)
+                    pvStructInfo = *(BYTE **)pvStructInfo;
+                hdr = (BLOBHEADER *)pvStructInfo;
+                hdr->bType = PUBLICKEYBLOB;
+                hdr->bVersion = CUR_BLOB_VERSION;
+                hdr->reserved = 0;
+                hdr->aiKeyAlg = CALG_RSA_KEYX;
+                rsaPubKey = (RSAPUBKEY *)((BYTE *)pvStructInfo +
+                 sizeof(BLOBHEADER));
+                rsaPubKey->magic = RSA1_MAGIC;
+                rsaPubKey->pubexp = decodedKey->pubexp;
+                rsaPubKey->bitlen = decodedKey->modulus.cbData * 8;
+                memcpy((BYTE *)pvStructInfo + sizeof(BLOBHEADER) +
+                 sizeof(RSAPUBKEY), decodedKey->modulus.pbData,
+                 decodedKey->modulus.cbData);
+            }
+            LocalFree(decodedKey);
+        }
+    }
+    __EXCEPT(page_fault)
+    {
+        SetLastError(STATUS_ACCESS_VIOLATION);
+        ret = FALSE;
+    }
+    __ENDTRY
+    return ret;
+}
+
 static BOOL WINAPI CRYPT_AsnDecodeOctets(DWORD dwCertEncodingType,
  LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
  PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
@@ -5268,6 +5394,9 @@
         case (WORD)X509_BASIC_CONSTRAINTS2:
             decodeFunc = CRYPT_AsnDecodeBasicConstraints2;
             break;
+        case (WORD)RSA_CSP_PUBLICKEYBLOB:
+            decodeFunc = CRYPT_AsnDecodeRsaPubKey;
+            break;
         case (WORD)X509_OCTET_STRING:
             decodeFunc = CRYPT_AsnDecodeOctets;
             break;
diff --git a/dlls/crypt32/tests/encode.c b/dlls/crypt32/tests/encode.c
index daa014f..78880cd 100644
--- a/dlls/crypt32/tests/encode.c
+++ b/dlls/crypt32/tests/encode.c
@@ -847,16 +847,6 @@
 static const BYTE encodedIPAddr[] = { 0x30, 0x06, 0x87, 0x04, 0x7f, 0x00, 0x00,
  0x01 };
 
-static void printBytes(const char *hdr, const BYTE *pb, size_t cb)
-{
-    size_t i;
-
-    printf("%s:\n", hdr);
-    for (i = 0; i < cb; i++)
-        printf("%02x ", pb[i]);
-    putchar('\n');
-}
-
 static void test_encodeAltName(DWORD dwEncoding)
 {
     static const WCHAR nihongo[] = { 0x226f, 0x575b, 0 };
@@ -1334,6 +1324,164 @@
      "Expected CRYPT_E_ASN1_CORRUPT, got %08lx\n", GetLastError());
 }
 
+/* These are terrible public keys of course, I'm just testing encoding */
+static const BYTE modulus1[] = { 0,0,0,1,1,1,1,1 };
+static const BYTE modulus2[] = { 1,1,1,1,1,0,0,0 };
+
+struct EncodedRSAPubKey
+{
+    const BYTE *modulus;
+    size_t modulusLen;
+    const BYTE *encoded;
+    size_t decodedModulusLen;
+};
+
+struct EncodedRSAPubKey rsaPubKeys[] = {
+ { modulus1, sizeof(modulus1),
+   "\x30\x0f\x02\x08\x01\x01\x01\x01\x01\x00\x00\x00\x02\x03\x01\x00\x01",
+   sizeof(modulus1) },
+ { modulus2, sizeof(modulus2),
+   "\x30\x0c\x02\x05\x01\x01\x01\x01\x01\x02\x03\x01\x00\x01",
+   5 },
+};
+
+static void test_encodeRsaPublicKey(DWORD dwEncoding)
+{
+    BYTE toEncode[sizeof(BLOBHEADER) + sizeof(RSAPUBKEY) + sizeof(modulus1)];
+    BLOBHEADER *hdr = (BLOBHEADER *)toEncode;
+    RSAPUBKEY *rsaPubKey = (RSAPUBKEY *)(toEncode + sizeof(BLOBHEADER));
+    BOOL ret;
+    BYTE *buf = NULL;
+    DWORD bufSize = 0;
+
+    /* Try with a bogus blob type */
+    hdr->bType = 2;
+    hdr->bVersion = CUR_BLOB_VERSION;
+    hdr->reserved = 0;
+    hdr->aiKeyAlg = CALG_RSA_KEYX;
+    rsaPubKey->magic = 0x31415352;
+    rsaPubKey->bitlen = sizeof(modulus1) * 8;
+    rsaPubKey->pubexp = 65537;
+    memcpy(toEncode + sizeof(BLOBHEADER) + sizeof(RSAPUBKEY), modulus1,
+     sizeof(modulus1));
+
+    ret = CryptEncodeObjectEx(dwEncoding, RSA_CSP_PUBLICKEYBLOB,
+     toEncode, CRYPT_ENCODE_ALLOC_FLAG, NULL, (BYTE *)&buf,
+     &bufSize);
+    ok(!ret && GetLastError() == HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER),
+     "Expected HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER), got %08lx\n",
+     GetLastError());
+    /* Now with a bogus reserved field */
+    hdr->bType = PUBLICKEYBLOB;
+    hdr->reserved = 1;
+    ret = CryptEncodeObjectEx(dwEncoding, RSA_CSP_PUBLICKEYBLOB,
+     toEncode, CRYPT_ENCODE_ALLOC_FLAG, NULL, (BYTE *)&buf,
+     &bufSize);
+    if (buf)
+    {
+        ok(bufSize == rsaPubKeys[0].encoded[1] + 2,
+         "Expected size %d, got %ld\n", rsaPubKeys[0].encoded[1] + 2, bufSize);
+        ok(!memcmp(buf, rsaPubKeys[0].encoded, bufSize), "Unexpected value\n");
+        LocalFree(buf);
+    }
+    /* Now with a bogus blob version */
+    hdr->reserved = 0;
+    hdr->bVersion = 0;
+    ret = CryptEncodeObjectEx(dwEncoding, RSA_CSP_PUBLICKEYBLOB,
+     toEncode, CRYPT_ENCODE_ALLOC_FLAG, NULL, (BYTE *)&buf,
+     &bufSize);
+    if (buf)
+    {
+        ok(bufSize == rsaPubKeys[0].encoded[1] + 2,
+         "Expected size %d, got %ld\n", rsaPubKeys[0].encoded[1] + 2, bufSize);
+        ok(!memcmp(buf, rsaPubKeys[0].encoded, bufSize), "Unexpected value\n");
+        LocalFree(buf);
+    }
+    /* And with a bogus alg ID */
+    hdr->bVersion = CUR_BLOB_VERSION;
+    hdr->aiKeyAlg = CALG_DES;
+    ret = CryptEncodeObjectEx(dwEncoding, RSA_CSP_PUBLICKEYBLOB,
+     toEncode, CRYPT_ENCODE_ALLOC_FLAG, NULL, (BYTE *)&buf,
+     &bufSize);
+    if (buf)
+    {
+        ok(bufSize == rsaPubKeys[0].encoded[1] + 2,
+         "Expected size %d, got %ld\n", rsaPubKeys[0].encoded[1] + 2, bufSize);
+        ok(!memcmp(buf, rsaPubKeys[0].encoded, bufSize), "Unexpected value\n");
+        LocalFree(buf);
+    }
+    /* Finally, all valid, but change the modulus */
+    hdr->aiKeyAlg = CALG_RSA_KEYX;
+    memcpy(toEncode + sizeof(BLOBHEADER) + sizeof(RSAPUBKEY), modulus2,
+     sizeof(modulus2));
+    ret = CryptEncodeObjectEx(dwEncoding, RSA_CSP_PUBLICKEYBLOB,
+     toEncode, CRYPT_ENCODE_ALLOC_FLAG, NULL, (BYTE *)&buf,
+     &bufSize);
+    if (buf)
+    {
+        ok(bufSize == rsaPubKeys[1].encoded[1] + 2,
+         "Expected size %d, got %ld\n", rsaPubKeys[1].encoded[1] + 2, bufSize);
+        ok(!memcmp(buf, rsaPubKeys[1].encoded, bufSize), "Unexpected value\n");
+        LocalFree(buf);
+    }
+}
+
+static void test_decodeRsaPublicKey(DWORD dwEncoding)
+{
+    DWORD i;
+    LPBYTE buf = NULL;
+    DWORD bufSize = 0;
+    BOOL ret;
+
+    /* Try with a bad length */
+    ret = CryptDecodeObjectEx(dwEncoding, RSA_CSP_PUBLICKEYBLOB,
+     rsaPubKeys[0].encoded, rsaPubKeys[0].encoded[1],
+     CRYPT_DECODE_ALLOC_FLAG, NULL, (BYTE *)&buf, &bufSize);
+    ok(!ret && GetLastError() == CRYPT_E_ASN1_EOD,
+     "Expected CRYPT_E_ASN1_EOD, got %08lx\n", CRYPT_E_ASN1_EOD);
+    /* Now try success cases */
+    for (i = 0; i < sizeof(rsaPubKeys) / sizeof(rsaPubKeys[0]); i++)
+    {
+        bufSize = 0;
+        ret = CryptDecodeObjectEx(dwEncoding, RSA_CSP_PUBLICKEYBLOB,
+         rsaPubKeys[i].encoded, rsaPubKeys[i].encoded[1] + 2,
+         CRYPT_DECODE_ALLOC_FLAG, NULL, (BYTE *)&buf, &bufSize);
+        ok(ret, "CryptDecodeObjectEx failed: %08lx\n", GetLastError());
+        if (buf)
+        {
+            BLOBHEADER *hdr = (BLOBHEADER *)buf;
+            RSAPUBKEY *rsaPubKey = (RSAPUBKEY *)(buf + sizeof(BLOBHEADER));
+
+            ok(bufSize >= sizeof(BLOBHEADER) + sizeof(RSAPUBKEY) +
+             rsaPubKeys[i].decodedModulusLen,
+             "Expected size at least %d, got %ld\n",
+             sizeof(BLOBHEADER) + sizeof(RSAPUBKEY) +
+             rsaPubKeys[i].decodedModulusLen, bufSize);
+            ok(hdr->bType == PUBLICKEYBLOB,
+             "Expected type PUBLICKEYBLOB (%d), got %d\n", PUBLICKEYBLOB,
+             hdr->bType);
+            ok(hdr->bVersion == CUR_BLOB_VERSION,
+             "Expected version CUR_BLOB_VERSION (%d), got %d\n",
+             CUR_BLOB_VERSION, hdr->bVersion);
+            ok(hdr->reserved == 0, "Expected reserved 0, got %d\n",
+             hdr->reserved);
+            ok(hdr->aiKeyAlg == CALG_RSA_KEYX,
+             "Expected CALG_RSA_KEYX, got %08x\n", hdr->aiKeyAlg);
+            ok(rsaPubKey->magic == 0x31415352,
+             "Expected magic RSA1, got %08lx\n", rsaPubKey->magic);
+            ok(rsaPubKey->bitlen == rsaPubKeys[i].decodedModulusLen * 8,
+             "Expected bit len %d, got %ld\n",
+             rsaPubKeys[i].decodedModulusLen * 8, rsaPubKey->bitlen);
+            ok(rsaPubKey->pubexp == 65537, "Expected pubexp 65537, got %ld\n",
+             rsaPubKey->pubexp);
+            ok(!memcmp(buf + sizeof(BLOBHEADER) + sizeof(RSAPUBKEY),
+             rsaPubKeys[i].modulus, rsaPubKeys[i].decodedModulusLen),
+             "Unexpected modulus\n");
+            LocalFree(buf);
+        }
+    }
+}
+
 static const BYTE intSequence[] = { 0x30, 0x1b, 0x02, 0x01, 0x01, 0x02, 0x01,
  0x7f, 0x02, 0x02, 0x00, 0x80, 0x02, 0x02, 0x01, 0x00, 0x02, 0x01, 0x80, 0x02,
  0x02, 0xff, 0x7f, 0x02, 0x04, 0xba, 0xdd, 0xf0, 0x0d };
@@ -2117,12 +2265,6 @@
          info->Issuer.cbData);
         ok(!memcmp(info->Issuer.pbData, encodedCommonName, info->Issuer.cbData),
          "Unexpected issuer\n");
-        if (memcmp(info->Issuer.pbData, encodedCommonName, info->Issuer.cbData))
-        {
-            printBytes("Expected", encodedCommonName,
-             sizeof(encodedCommonName));
-            printBytes("Got", info->Issuer.pbData, info->Issuer.cbData);
-        }
     }
     /* and finally, with an extension */
     ret = CryptDecodeObjectEx(dwEncoding, X509_CERT_CRL_TO_BE_SIGNED,
@@ -2237,6 +2379,8 @@
         test_decodeBits(encodings[i]);
         test_encodeBasicConstraints(encodings[i]);
         test_decodeBasicConstraints(encodings[i]);
+        test_encodeRsaPublicKey(encodings[i]);
+        test_decodeRsaPublicKey(encodings[i]);
         test_encodeSequenceOfAny(encodings[i]);
         test_decodeSequenceOfAny(encodings[i]);
         test_encodeExtensions(encodings[i]);