Merge pull request #512 from didishe90/persistSerDir

Persistent service directory
diff --git a/src/tools/weave/Cmd_GenCACert.cpp b/src/tools/weave/Cmd_GenCACert.cpp
index 38a1516..e733dfd 100644
--- a/src/tools/weave/Cmd_GenCACert.cpp
+++ b/src/tools/weave/Cmd_GenCACert.cpp
@@ -65,8 +65,9 @@
     "\n"
     "   -k, --key <file>\n"
     "\n"
-    "       File containing the public and private keys for the new CA certificate.\n"
-    "       (File must be in PEM format).\n"
+    "       File containing the public key for the new CA certificate. The file may\n"
+    "       contain only a public key, or a private key (from which the public key\n"
+    "       can be obtained). The file must be in PEM or DER format.\n"
     "\n"
     "   -C, --ca-cert <file>\n"
     "\n"
@@ -76,7 +77,7 @@
     "   -K, --ca-key <file>\n"
     "\n"
     "       File containing CA private key to be used to sign the new CA certificate.\n"
-    "       This file must be in PEM format.\n"
+    "       This file may be in PEM or DER format.\n"
     "\n"
     "   -o, --out <file>\n"
     "\n"
@@ -245,7 +246,7 @@
     else
         newCertFile = stdout;
 
-    if (!ReadPrivateKey(gNewCertKeyFileName, "Enter password for private key:", newCertKey))
+    if (!ReadPublicKey(gNewCertKeyFileName, "Enter password for private key:", newCertKey))
         ExitNow(res = false);
 
     if (!gSelfSign)
diff --git a/src/tools/weave/KeyUtils.cpp b/src/tools/weave/KeyUtils.cpp
index b327f7d..caedb59 100644
--- a/src/tools/weave/KeyUtils.cpp
+++ b/src/tools/weave/KeyUtils.cpp
@@ -186,8 +186,10 @@
     if (keyFormat == kKeyFormat_Weave_Base64)
     {
         tmpKeyBuf = Base64Decode(keyData, keyDataLen, NULL, 0, keyDataLen);
-        if (tmpKeyBuf == NULL)
-            goto exit;
+        if (tmpKeyBuf == NULL) {
+            fprintf(stderr, "Base64 decoding error\n");
+            ExitNow(res = false);
+        }
         keyData = tmpKeyBuf;
         keyFormat = kKeyFormat_Weave_Raw;
     }
@@ -620,3 +622,101 @@
     }
     return res;
 }
+
+bool ReadPublicKey(const char *fileName, const char *prompt, EVP_PKEY *& key)
+{
+    bool res = true;
+    uint8_t *keyData = NULL;
+    uint32_t keyDataLen;
+
+    key = NULL;
+
+    res = ReadFileIntoMem(fileName, keyData, keyDataLen);
+    if (!res)
+        ExitNow();
+
+    res = DecodePublicKey(keyData, keyDataLen, kKeyFormat_Unknown, fileName, prompt, key);
+
+exit:
+    if (keyData != NULL)
+        free(keyData);
+    return res;
+}
+
+bool DecodePublicKeyOnly(const uint8_t *keyData, uint32_t keyDataLen, KeyFormat keyFormat, const char *keySource, EVP_PKEY *& key, bool noErrorOutput)
+{
+    bool res = true;
+    uint8_t *tmpKeyBuf = NULL;
+    BIO *keyBIO = NULL;
+
+    key = NULL;
+
+    if (keyFormat == kKeyFormat_Unknown)
+    {
+        keyFormat = DetectPublicKeyFormat(keyData, keyDataLen);
+    }
+
+    if (keyFormat != kKeyFormat_PEM && keyFormat != kKeyFormat_DER)
+    {
+        if (!noErrorOutput)
+            fprintf(stderr, "Unsupported public key format\n");
+        ExitNow(res = false);
+    }
+
+    keyBIO = BIO_new_mem_buf((void *)keyData, keyDataLen);
+    if (keyBIO == NULL)
+    {
+        if (!noErrorOutput)
+            fprintf(stderr, "Memory allocation error\n");
+        ExitNow(res = false);
+    }
+
+    if (keyFormat == kKeyFormat_PEM)
+    {
+        if (PEM_read_bio_PUBKEY(keyBIO, &key, NULL, NULL) == NULL)
+        {
+            if (!noErrorOutput) {
+                fprintf(stderr, "Unable to read %s\n", keySource);
+                ReportOpenSSLErrorAndExit("PEM_read_bio_PUBKEY", res = false);
+            }
+            ExitNow(res = false);
+        }
+    }
+    else
+    {
+        if (d2i_PUBKEY_bio(keyBIO, &key) == NULL)
+        {
+            if (!noErrorOutput) {
+                fprintf(stderr, "Unable to read %s\n", keySource);
+                ReportOpenSSLErrorAndExit("d2i_PUBKEY_bio", res = false);
+            }
+            ExitNow(res = false);
+        }
+    }
+
+exit:
+    if (tmpKeyBuf != NULL)
+        free(tmpKeyBuf);
+    if (keyBIO != NULL)
+        BIO_free_all(keyBIO);
+    return res;
+}
+
+bool DecodePublicKey(const uint8_t *keyData, uint32_t keyDataLen, KeyFormat keyFormat, const char *prompt, const char *keySource, EVP_PKEY *& key)
+{
+    bool res = true;
+    key = NULL;
+    res = DecodePublicKeyOnly(keyData, keyDataLen, keyFormat, keySource, key, true);
+    if (!res)
+        return DecodePrivateKey(keyData, keyDataLen, keyFormat, prompt, keySource, key);
+    return res;
+}
+
+KeyFormat DetectPublicKeyFormat(const uint8_t *key, uint32_t keyLen)
+{
+    static const char *publicKeyPEMMarker = "-----BEGIN PUBLIC KEY-----";
+
+    return ContainsPEMMarker(publicKeyPEMMarker, key, keyLen)
+        ? kKeyFormat_PEM
+        : kKeyFormat_DER;
+}
diff --git a/src/tools/weave/weave-tool.h b/src/tools/weave/weave-tool.h
index 6639bd7..e112966 100644
--- a/src/tools/weave/weave-tool.h
+++ b/src/tools/weave/weave-tool.h
@@ -132,6 +132,9 @@
 extern bool EncodePrivateKey(EVP_PKEY *key, KeyFormat keyFormat, uint8_t *& encodedKey, uint32_t& encodedKeyLen);
 extern bool WeaveEncodePrivateKey(EVP_PKEY *key, uint8_t *& encodedKey, uint32_t& encodedKeyLen);
 extern bool WeaveEncodeECPrivateKey(EC_KEY *key, bool includePubKey, uint8_t *& encodedKey, uint32_t& encodedKeyLen);
+extern bool ReadPublicKey(const char *fileName, const char *prompt, EVP_PKEY *& key);
+extern bool DecodePublicKey(const uint8_t *keyData, uint32_t keyDataLen, KeyFormat keyFormat, const char *keySource, const char *prompt, EVP_PKEY *& key);
+extern KeyFormat DetectPublicKeyFormat(const uint8_t *key, uint32_t keyLen);
 
 extern bool InitOpenSSL();
 extern OID NIDToWeaveOID(int nid);