From d163b5ebfc4e696f066e8675aa11d688c4cedecd Mon Sep 17 00:00:00 2001
From: chenximin <ximin.chen@rokid.com>
Date: Tue, 28 Nov 2017 11:45:10 +0800
Subject: [PATCH 01/10] fix verify crash caused by invalid cert format

---
 src/x509.cc | 101 ++++++++++++++++++++++------------------------------
 1 file changed, 43 insertions(+), 58 deletions(-)

diff --git a/src/x509.cc b/src/x509.cc
index f2c8167..a836301 100644
--- a/src/x509.cc
+++ b/src/x509.cc
@@ -58,68 +58,53 @@ NAN_METHOD(verify) {
   X509_STORE *store = NULL;
   X509_STORE_CTX *verify_ctx = NULL;
   X509 *cert = NULL;
-  BIO *cert_bio = BIO_new(BIO_s_file());
-
-  // create store
-  store = X509_STORE_new();
-  if (store == NULL) {
-    X509_STORE_free(store);
-    BIO_free_all(cert_bio);
-    Nan::ThrowError("Failed to create X509 certificate store.");
-  }
-
-  verify_ctx = X509_STORE_CTX_new();
-
-  if (verify_ctx == NULL) {
-    X509_STORE_free(store);
-    BIO_free_all(cert_bio);
-    Nan::ThrowError("Failed to create X509 verification context.");
-  }
-
-  // load file in BIO
-  int ret = BIO_read_filename(cert_bio, cert_path.c_str());
-  if (ret != 1) {
-    X509_STORE_free(store);
-    X509_free(cert);
-    BIO_free_all(cert_bio);
-    X509_STORE_CTX_free(verify_ctx);
-    Nan::ThrowError("Error reading file");
-  }
-
-  // read from BIO
-  cert = PEM_read_bio_X509(cert_bio, NULL, 0, NULL);
-  if (cert == NULL) {
-    X509_STORE_free(store);
-    X509_free(cert);
-    X509_STORE_CTX_free(verify_ctx);
-    BIO_free_all(cert_bio);
-    Nan::ThrowError("Failed to load cert");
-  }
-
-  // load CA bundle
-  ret = X509_STORE_load_locations(store, ca_bundlestr.c_str(), NULL);
-  if (ret != 1) {
-    X509_STORE_free(store);
-    X509_free(cert);
-    BIO_free_all(cert_bio);
-    X509_STORE_CTX_free(verify_ctx);
-    Nan::ThrowError("Error loading CA chain file");
-  }
-
-  // verify
-  X509_STORE_CTX_init(verify_ctx, store, cert, NULL);
-  ret = X509_verify_cert(verify_ctx);
-
-  if (ret <= 0) {
-    Nan::ThrowError(X509_verify_cert_error_string(verify_ctx->error));
-  }
-
+  BIO *cert_bio = NULL;
+  const char *error = NULL;
+
+  do {
+    store = X509_STORE_new();
+    if (store == NULL) {
+      error = "Failed to create X509 certificate store.";
+      break;
+    }
+    verify_ctx = X509_STORE_CTX_new();
+    if (verify_ctx == NULL) {
+      error = "Failed to create X509 verification context.";
+      break;
+    }
+    cert_bio = BIO_new(BIO_s_file());
+    int ret = BIO_read_filename(cert_bio, cert_path.c_str());
+    if (ret != 1) {
+      error = "Error reading file";
+      break;
+    }
+    cert = PEM_read_bio_X509(cert_bio, NULL, 0, NULL);
+    if (cert == NULL) {
+      error = "Failed to load cert";
+      break;
+    }
+    ret = X509_STORE_load_locations(store, ca_bundlestr.c_str(), NULL);
+    if (ret != 1) {
+      error = "Error loading CA chain file";
+      break;
+    }
+    X509_STORE_CTX_init(verify_ctx, store, cert, NULL);
+    ret = X509_verify_cert(verify_ctx);
+    if (ret <= 0) {
+      error =  X509_verify_cert_error_string(verify_ctx->error);
+      break;
+    }
+  } while(0);
+  
   X509_STORE_free(store);
   X509_free(cert);
   X509_STORE_CTX_free(verify_ctx);
   BIO_free_all(cert_bio);
-
- info.GetReturnValue().Set(Nan::New(true));
+  if (error != NULL) {
+    Nan::ThrowError(error);
+  } else {
+    info.GetReturnValue().Set(Nan::New(true));
+  }
 }
 
 

From 748b3c203b1301e60efc40a110fae8234c4dd059 Mon Sep 17 00:00:00 2001
From: chenximin <ximin.chen@rokid.com>
Date: Tue, 28 Nov 2017 22:13:52 +0800
Subject: [PATCH 02/10] add test case for invalid cert format

---
 test/test.js | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/test/test.js b/test/test.js
index c364fdc..c230876 100644
--- a/test/test.js
+++ b/test/test.js
@@ -38,3 +38,19 @@ x509.verify(
     assert.throws(assert.ifError.bind(null, err), /ENOENT/)
   }
 );
+
+x509.verify(
+  path.join(__dirname, 'certs/equifax.crt'),
+  path.join(__dirname, '/test.js'),
+  function(err, result) {
+    assert.notStrictEqual(err, null);
+  }
+);
+
+x509.verify(
+  path.join(__dirname, '/test.js'),
+  path.join(__dirname, 'CA_chains/enduser-example.com.chain'),
+  function(err, result) {
+    assert.notStrictEqual(err, null);
+  }
+);
\ No newline at end of file

From 327f6a49b80a350edb1cdeaa2da0926c42d10b06 Mon Sep 17 00:00:00 2001
From: chenximin <ximin.chen@rokid.com>
Date: Tue, 28 Nov 2017 22:42:54 +0800
Subject: [PATCH 03/10] update test case for invalid cert format

---
 test/test.js | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/test/test.js b/test/test.js
index c230876..c98818d 100644
--- a/test/test.js
+++ b/test/test.js
@@ -43,7 +43,7 @@ x509.verify(
   path.join(__dirname, 'certs/equifax.crt'),
   path.join(__dirname, '/test.js'),
   function(err, result) {
-    assert.notStrictEqual(err, null);
+    assert.throws(assert.ifError.bind(null, err), /Error loading CA chain file/)
   }
 );
 
@@ -51,6 +51,6 @@ x509.verify(
   path.join(__dirname, '/test.js'),
   path.join(__dirname, 'CA_chains/enduser-example.com.chain'),
   function(err, result) {
-    assert.notStrictEqual(err, null);
+    assert.throws(assert.ifError.bind(null, err), /Failed to load cert/)
   }
 );
\ No newline at end of file

From 9c32c21b501ff9da8e56df1396677103902f1631 Mon Sep 17 00:00:00 2001
From: chenximin <ximin.chen@rokid.com>
Date: Mon, 18 Dec 2017 11:15:41 +0800
Subject: [PATCH 04/10] implement verify from string

---
 include/x509.h |  2 ++
 index.js       | 13 ++++++++
 src/addon.cc   |  4 +++
 src/x509.cc    | 67 +++++++++++++++++++++++++++++++++++++++-
 test/test.crt  | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++
 test/test.js   | 32 +++++++++++++++++++
 6 files changed, 201 insertions(+), 1 deletion(-)
 create mode 100644 test/test.crt

diff --git a/include/x509.h b/include/x509.h
index ce1198b..11c31a4 100644
--- a/include/x509.h
+++ b/include/x509.h
@@ -24,9 +24,11 @@ NAN_METHOD(get_subject);
 NAN_METHOD(get_issuer);
 NAN_METHOD(parse_cert);
 NAN_METHOD(verify);
+NAN_METHOD(verifyFromStr);
 
 Local<Value> try_parse(const std::string& dataString);
 Local<Value> verify(const std::string& dataString);
+Local<Value> verifyFromStr(const std::string& dataString);
 Local<Value> parse_date(ASN1_TIME *date);
 Local<Value> parse_serial(ASN1_INTEGER *serial);
 Local<Object> parse_name(X509_NAME *subject);
diff --git a/index.js b/index.js
index 539a76c..35e8aa6 100644
--- a/index.js
+++ b/index.js
@@ -6,6 +6,19 @@ exports.getAltNames = x509.getAltNames;
 exports.getSubject = x509.getSubject;
 exports.getIssuer = x509.getIssuer;
 
+exports.verifyFromStr = function(certStr, CABundleStr, cb) {
+  let caughtErr = null;
+  try {
+    x509.verifyFromStr(certStr, CABundleStr);
+  }
+  catch (verificationError) {
+    caughtErr = verificationError;
+  }
+  finally {
+    cb(caughtErr);
+  }
+};
+
 exports.verify = function(certPath, CABundlePath, cb) {
   if (!certPath) {
     throw new TypeError('Certificate path is required');
diff --git a/src/addon.cc b/src/addon.cc
index f0aab0c..47c2bed 100644
--- a/src/addon.cc
+++ b/src/addon.cc
@@ -10,6 +10,10 @@ void init(Local<Object> exports) {
   Nan::Set(exports,
     Nan::New<String>("version").ToLocalChecked(),
     Nan::New<String>(VERSION).ToLocalChecked());
+  
+  Nan::Set(exports,
+    Nan::New<String>("verifyFromStr").ToLocalChecked(),
+    Nan::New<FunctionTemplate>(verifyFromStr)->GetFunction());
 
   Nan::Set(exports,
     Nan::New<String>("verify").ToLocalChecked(),
diff --git a/src/x509.cc b/src/x509.cc
index a836301..de2fcbf 100644
--- a/src/x509.cc
+++ b/src/x509.cc
@@ -46,7 +46,72 @@ std::string parse_args(const Nan::FunctionCallbackInfo<v8::Value>& info) {
   return *String::Utf8Value(info[0]->ToString());
 }
 
-
+NAN_METHOD(verifyFromStr) {
+  Nan::HandleScope scope;
+  OpenSSL_add_all_algorithms();
+  std::string cert_str = *String::Utf8Value(info[0]->ToString());
+  std::string ca_str = *String::Utf8Value(info[1]->ToString());
+  
+  X509_STORE *store = NULL;
+  X509_STORE_CTX *verify_ctx = NULL;
+  X509 *ca_cert = NULL;
+  BIO *ca_bio = NULL;
+  X509 *cert = NULL;
+  BIO *cert_bio = NULL;
+  const char *error = NULL;
+  do {
+    store = X509_STORE_new();
+    if (store == NULL) {
+      error = "Failed to create X509 certificate store.";
+      break;
+    }
+    verify_ctx = X509_STORE_CTX_new();
+    if (verify_ctx == NULL) {
+      error = "Failed to create X509 verification context.";
+      break;
+    }
+    cert_bio = BIO_new(BIO_s_mem());
+    size_t ret = BIO_puts(cert_bio, cert_str.c_str());
+    if (ret != cert_str.length()) {
+      error = "Error reading cert content";
+      break;
+    }
+    cert = PEM_read_bio_X509(cert_bio, NULL, 0, NULL);
+    if (cert == NULL) {
+      error = "Failed to load cert";
+      break;
+    }
+    ca_bio = BIO_new(BIO_s_mem());
+    ret = BIO_puts(ca_bio, ca_str.c_str());
+    if (ret != ca_str.length()) {
+      error = "Error reading ca content";
+      break;
+    }
+    ca_cert = PEM_read_bio_X509(ca_bio, NULL, 0, NULL);
+    if (ca_cert == NULL) {
+      error = "Failed to load ca";
+      break;
+    }
+    X509_STORE_CTX_init(verify_ctx, store, ca_cert, NULL);
+    X509_STORE_add_cert(store, cert);
+    ret = X509_verify_cert(verify_ctx);
+    if (ret < 1) {
+      error =  X509_verify_cert_error_string(verify_ctx->error);
+      break;
+    }
+  } while(0);
+  X509_STORE_free(store);
+  X509_STORE_CTX_free(verify_ctx);
+  X509_free(ca_cert);
+  BIO_free_all(ca_bio);
+  X509_free(cert);
+  BIO_free_all(cert_bio);
+  if (error != NULL) {
+    Nan::ThrowError(error);
+  } else {
+    info.GetReturnValue().Set(Nan::New(true));
+  }
+}
 
 NAN_METHOD(verify) {
   Nan::HandleScope scope;
diff --git a/test/test.crt b/test/test.crt
new file mode 100644
index 0000000..ef48d6f
--- /dev/null
+++ b/test/test.crt
@@ -0,0 +1,84 @@
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 4 (0x4)
+    Signature Algorithm: sha1WithRSAEncryption
+        Issuer: C=CN, ST=Hangzhou, L=Hangzhou, O=Rokid, OU=homebase, CN=Rokid
+        Validity
+            Not Before: Dec 13 13:57:27 2017 GMT
+            Not After : Dec 13 13:57:27 2018 GMT
+        Subject: C=CN, ST=sss, O=zzz, OU=ddd, CN=ttt
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (2048 bit)
+                Modulus:
+                    00:a1:91:5e:39:ce:04:73:3c:db:5e:67:f8:c5:a8:
+                    e0:63:e0:df:ab:21:54:0d:89:d7:63:04:68:ae:1a:
+                    f5:1c:46:20:77:a3:41:9c:aa:a3:6d:58:dd:07:43:
+                    c6:3f:81:cc:24:c2:85:59:10:99:c6:bb:a9:34:6d:
+                    70:c6:cf:63:57:5d:65:b8:42:ec:05:51:52:7a:b0:
+                    5f:d6:09:60:9f:ae:47:28:af:97:94:76:b7:4b:77:
+                    97:31:ec:eb:d2:a9:08:4f:0b:b0:54:6a:bd:40:9d:
+                    cb:13:05:2d:2f:b2:51:3e:ff:42:54:89:53:92:96:
+                    e8:3a:7d:d6:8d:42:55:72:e3:5f:51:e2:ee:64:0c:
+                    76:c0:38:e8:5c:86:09:ef:d5:57:92:2f:e8:fd:f4:
+                    84:b9:57:41:7a:4c:0c:23:c9:9e:10:17:a3:70:d8:
+                    43:1c:39:18:9b:92:4a:e4:01:1c:f4:0c:3d:a6:7a:
+                    bf:02:93:ab:57:3d:16:a5:f9:21:fd:e2:15:d5:ab:
+                    de:46:cf:bf:ad:32:de:96:01:3a:fc:f5:50:9b:cb:
+                    51:c3:97:c4:13:73:59:61:da:6d:1e:06:9a:c8:11:
+                    14:c2:52:78:59:65:fe:0f:c1:fe:66:5e:47:47:04:
+                    ca:57:7b:7d:b0:0d:1c:91:44:fb:29:ae:8a:ea:27:
+                    3a:31
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Basic Constraints: 
+                CA:FALSE
+            Netscape Comment: 
+                OpenSSL Generated Certificate
+            X509v3 Subject Key Identifier: 
+                21:28:5D:C7:BB:E1:30:B8:BD:0C:84:F8:98:49:CF:3F:6D:B1:48:4D
+            X509v3 Authority Key Identifier: 
+                DirName:/C=CN/ST=Hangzhou/L=Hangzhou/O=Rokid/OU=homebase/CN=Rokid
+                serial:94:4E:2C:AE:6B:DA:31:6B
+
+    Signature Algorithm: sha1WithRSAEncryption
+         13:83:55:67:e4:b3:4c:7e:91:92:79:e5:5a:c8:b3:d3:b6:05:
+         c4:74:ff:6b:82:b8:2b:51:40:d1:b7:6d:2f:0d:f0:c5:65:44:
+         05:8f:eb:31:ce:a0:64:6e:02:fe:25:75:ce:24:6d:8d:93:9d:
+         d2:fb:f9:89:d5:e7:09:fd:09:de:cf:91:c8:ab:0b:ad:ad:12:
+         be:55:8d:83:ce:8c:77:f1:40:f1:b8:cc:ae:74:90:18:8b:f3:
+         42:51:ab:47:21:52:8a:3d:d8:de:3a:b9:2b:6e:0d:9e:33:c7:
+         9c:52:33:15:8b:99:38:d6:d5:27:d8:b4:03:28:d6:de:dc:2b:
+         19:73:61:fa:8e:3a:43:83:c8:3b:79:0b:23:2a:ed:2f:d1:eb:
+         22:5b:00:ac:00:6a:89:04:dc:79:fa:29:42:53:af:b0:9e:fe:
+         15:2a:31:06:35:ef:38:88:71:50:c8:6e:20:57:dd:a1:bf:93:
+         94:49:26:9b:1a:f3:4b:79:75:53:e0:1c:87:a9:2c:cc:78:61:
+         cb:ee:56:55:1d:08:9a:36:fd:8e:33:a9:f0:a3:f3:c4:85:de:
+         a8:94:a1:cd:ee:38:99:84:f8:8e:a8:b2:19:c9:b3:f9:5e:3e:
+         24:03:ab:c8:9b:6f:f0:1d:9f:55:47:12:4f:18:53:ed:01:07:
+         8f:0c:01:90
+-----BEGIN CERTIFICATE-----
+MIIEBTCCAu2gAwIBAgIBBDANBgkqhkiG9w0BAQUFADBmMQswCQYDVQQGEwJDTjER
+MA8GA1UECAwISGFuZ3pob3UxETAPBgNVBAcMCEhhbmd6aG91MQ4wDAYDVQQKDAVS
+b2tpZDERMA8GA1UECwwIaG9tZWJhc2UxDjAMBgNVBAMMBVJva2lkMB4XDTE3MTIx
+MzEzNTcyN1oXDTE4MTIxMzEzNTcyN1owRTELMAkGA1UEBhMCQ04xDDAKBgNVBAgM
+A3NzczEMMAoGA1UECgwDenp6MQwwCgYDVQQLDANkZGQxDDAKBgNVBAMMA3R0dDCC
+ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKGRXjnOBHM8215n+MWo4GPg
+36shVA2J12MEaK4a9RxGIHejQZyqo21Y3QdDxj+BzCTChVkQmca7qTRtcMbPY1dd
+ZbhC7AVRUnqwX9YJYJ+uRyivl5R2t0t3lzHs69KpCE8LsFRqvUCdyxMFLS+yUT7/
+QlSJU5KW6Dp91o1CVXLjX1Hi7mQMdsA46FyGCe/VV5Iv6P30hLlXQXpMDCPJnhAX
+o3DYQxw5GJuSSuQBHPQMPaZ6vwKTq1c9FqX5If3iFdWr3kbPv60y3pYBOvz1UJvL
+UcOXxBNzWWHabR4GmsgRFMJSeFll/g/B/mZeR0cEyld7fbANHJFE+ymuiuonOjEC
+AwEAAaOB3jCB2zAJBgNVHRMEAjAAMCwGCWCGSAGG+EIBDQQfFh1PcGVuU1NMIEdl
+bmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUIShdx7vhMLi9DIT4mEnPP22x
+SE0wgYAGA1UdIwR5MHehaqRoMGYxCzAJBgNVBAYTAkNOMREwDwYDVQQIDAhIYW5n
+emhvdTERMA8GA1UEBwwISGFuZ3pob3UxDjAMBgNVBAoMBVJva2lkMREwDwYDVQQL
+DAhob21lYmFzZTEOMAwGA1UEAwwFUm9raWSCCQCUTiyua9oxazANBgkqhkiG9w0B
+AQUFAAOCAQEAE4NVZ+SzTH6RknnlWsiz07YFxHT/a4K4K1FA0bdtLw3wxWVEBY/r
+Mc6gZG4C/iV1ziRtjZOd0vv5idXnCf0J3s+RyKsLra0SvlWNg86Md/FA8bjMrnSQ
+GIvzQlGrRyFSij3Y3jq5K24NnjPHnFIzFYuZONbVJ9i0AyjW3twrGXNh+o46Q4PI
+O3kLIyrtL9HrIlsArABqiQTcefopQlOvsJ7+FSoxBjXvOIhxUMhuIFfdob+TlEkm
+mxrzS3l1U+Ach6kszHhhy+5WVR0Imjb9jjOp8KPzxIXeqJShze44mYT4jqiyGcmz
++V4+JAOryJtv8B2fVUcSTxhT7QEHjwwBkA==
+-----END CERTIFICATE-----
diff --git a/test/test.js b/test/test.js
index c98818d..2cf8708 100644
--- a/test/test.js
+++ b/test/test.js
@@ -53,4 +53,36 @@ x509.verify(
   function(err, result) {
     assert.throws(assert.ifError.bind(null, err), /Failed to load cert/)
   }
+);
+
+x509.verifyFromStr(
+  fs.readFileSync(path.join(__dirname, 'certs/enduser-example.com.crt')),
+  fs.readFileSync(path.join(__dirname, 'CA_chains/enduser-example.com.chain')),
+  function(err, result) {
+    assert.strictEqual(err, null)
+  }
+);
+
+x509.verifyFromStr(
+  fs.readFileSync(path.join(__dirname, 'certs/acaline.com.crt')),
+  fs.readFileSync(path.join(__dirname, 'CA_chains/enduser-example.com.chain')),
+  function(err, result) {
+    assert.throws(assert.ifError.bind(null, err), /self signed certificate/)
+  }
+);
+
+x509.verifyFromStr(
+  fs.readFileSync(path.join(__dirname, 'test.js')),
+  fs.readFileSync(path.join(__dirname, 'CA_chains/enduser-example.com.chain')),
+  function(err, result) {
+    assert.throws(assert.ifError.bind(null, err), /Failed to load cert/)
+  }
+);
+
+x509.verifyFromStr(
+  fs.readFileSync(path.join(__dirname, 'certs/acaline.com.crt')),
+  fs.readFileSync(path.join(__dirname, 'test.js')),
+  function(err, result) {
+    assert.throws(assert.ifError.bind(null, err), /Failed to load ca/)
+  }
 );
\ No newline at end of file

From 3d6b34f8f756d159ba77f9ea9cdcc28b4bc44c84 Mon Sep 17 00:00:00 2001
From: chenximin <ximin.chen@rokid.com>
Date: Mon, 18 Dec 2017 11:23:34 +0800
Subject: [PATCH 05/10] Update README.md

---
 README.md | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/README.md b/README.md
index 6a49152..606a5bc 100644
--- a/README.md
+++ b/README.md
@@ -137,6 +137,21 @@ x509.verify(
 
 ```
 
+#### x509.verifyFromStr(`certStr`, `caStr`, function(err, result){ /*...*/})
+
+It is the same with verify.
+
+```js
+const x509 = require('x509');
+
+x509.verify(
+  path.readFileSync(__dirname + '/certs/user.com.crt'),
+  path.readFileSync(__dirname + 'enduser-example.com.chain'),
+  function(err, result){ /*...*/}
+);
+
+```
+
 ## Examples
 Checking the date to make sure the certificate is active:
 ```js

From 354a3511a83b9caf0cef19a0a9fafc6f9fe0755a Mon Sep 17 00:00:00 2001
From: qile222 <chhxxc@gmail.com>
Date: Mon, 18 Dec 2017 11:28:20 +0800
Subject: [PATCH 06/10] Delete test.crt

---
 test/test.crt | 84 ---------------------------------------------------
 1 file changed, 84 deletions(-)
 delete mode 100644 test/test.crt

diff --git a/test/test.crt b/test/test.crt
deleted file mode 100644
index ef48d6f..0000000
--- a/test/test.crt
+++ /dev/null
@@ -1,84 +0,0 @@
-Certificate:
-    Data:
-        Version: 3 (0x2)
-        Serial Number: 4 (0x4)
-    Signature Algorithm: sha1WithRSAEncryption
-        Issuer: C=CN, ST=Hangzhou, L=Hangzhou, O=Rokid, OU=homebase, CN=Rokid
-        Validity
-            Not Before: Dec 13 13:57:27 2017 GMT
-            Not After : Dec 13 13:57:27 2018 GMT
-        Subject: C=CN, ST=sss, O=zzz, OU=ddd, CN=ttt
-        Subject Public Key Info:
-            Public Key Algorithm: rsaEncryption
-                Public-Key: (2048 bit)
-                Modulus:
-                    00:a1:91:5e:39:ce:04:73:3c:db:5e:67:f8:c5:a8:
-                    e0:63:e0:df:ab:21:54:0d:89:d7:63:04:68:ae:1a:
-                    f5:1c:46:20:77:a3:41:9c:aa:a3:6d:58:dd:07:43:
-                    c6:3f:81:cc:24:c2:85:59:10:99:c6:bb:a9:34:6d:
-                    70:c6:cf:63:57:5d:65:b8:42:ec:05:51:52:7a:b0:
-                    5f:d6:09:60:9f:ae:47:28:af:97:94:76:b7:4b:77:
-                    97:31:ec:eb:d2:a9:08:4f:0b:b0:54:6a:bd:40:9d:
-                    cb:13:05:2d:2f:b2:51:3e:ff:42:54:89:53:92:96:
-                    e8:3a:7d:d6:8d:42:55:72:e3:5f:51:e2:ee:64:0c:
-                    76:c0:38:e8:5c:86:09:ef:d5:57:92:2f:e8:fd:f4:
-                    84:b9:57:41:7a:4c:0c:23:c9:9e:10:17:a3:70:d8:
-                    43:1c:39:18:9b:92:4a:e4:01:1c:f4:0c:3d:a6:7a:
-                    bf:02:93:ab:57:3d:16:a5:f9:21:fd:e2:15:d5:ab:
-                    de:46:cf:bf:ad:32:de:96:01:3a:fc:f5:50:9b:cb:
-                    51:c3:97:c4:13:73:59:61:da:6d:1e:06:9a:c8:11:
-                    14:c2:52:78:59:65:fe:0f:c1:fe:66:5e:47:47:04:
-                    ca:57:7b:7d:b0:0d:1c:91:44:fb:29:ae:8a:ea:27:
-                    3a:31
-                Exponent: 65537 (0x10001)
-        X509v3 extensions:
-            X509v3 Basic Constraints: 
-                CA:FALSE
-            Netscape Comment: 
-                OpenSSL Generated Certificate
-            X509v3 Subject Key Identifier: 
-                21:28:5D:C7:BB:E1:30:B8:BD:0C:84:F8:98:49:CF:3F:6D:B1:48:4D
-            X509v3 Authority Key Identifier: 
-                DirName:/C=CN/ST=Hangzhou/L=Hangzhou/O=Rokid/OU=homebase/CN=Rokid
-                serial:94:4E:2C:AE:6B:DA:31:6B
-
-    Signature Algorithm: sha1WithRSAEncryption
-         13:83:55:67:e4:b3:4c:7e:91:92:79:e5:5a:c8:b3:d3:b6:05:
-         c4:74:ff:6b:82:b8:2b:51:40:d1:b7:6d:2f:0d:f0:c5:65:44:
-         05:8f:eb:31:ce:a0:64:6e:02:fe:25:75:ce:24:6d:8d:93:9d:
-         d2:fb:f9:89:d5:e7:09:fd:09:de:cf:91:c8:ab:0b:ad:ad:12:
-         be:55:8d:83:ce:8c:77:f1:40:f1:b8:cc:ae:74:90:18:8b:f3:
-         42:51:ab:47:21:52:8a:3d:d8:de:3a:b9:2b:6e:0d:9e:33:c7:
-         9c:52:33:15:8b:99:38:d6:d5:27:d8:b4:03:28:d6:de:dc:2b:
-         19:73:61:fa:8e:3a:43:83:c8:3b:79:0b:23:2a:ed:2f:d1:eb:
-         22:5b:00:ac:00:6a:89:04:dc:79:fa:29:42:53:af:b0:9e:fe:
-         15:2a:31:06:35:ef:38:88:71:50:c8:6e:20:57:dd:a1:bf:93:
-         94:49:26:9b:1a:f3:4b:79:75:53:e0:1c:87:a9:2c:cc:78:61:
-         cb:ee:56:55:1d:08:9a:36:fd:8e:33:a9:f0:a3:f3:c4:85:de:
-         a8:94:a1:cd:ee:38:99:84:f8:8e:a8:b2:19:c9:b3:f9:5e:3e:
-         24:03:ab:c8:9b:6f:f0:1d:9f:55:47:12:4f:18:53:ed:01:07:
-         8f:0c:01:90
------BEGIN CERTIFICATE-----
-MIIEBTCCAu2gAwIBAgIBBDANBgkqhkiG9w0BAQUFADBmMQswCQYDVQQGEwJDTjER
-MA8GA1UECAwISGFuZ3pob3UxETAPBgNVBAcMCEhhbmd6aG91MQ4wDAYDVQQKDAVS
-b2tpZDERMA8GA1UECwwIaG9tZWJhc2UxDjAMBgNVBAMMBVJva2lkMB4XDTE3MTIx
-MzEzNTcyN1oXDTE4MTIxMzEzNTcyN1owRTELMAkGA1UEBhMCQ04xDDAKBgNVBAgM
-A3NzczEMMAoGA1UECgwDenp6MQwwCgYDVQQLDANkZGQxDDAKBgNVBAMMA3R0dDCC
-ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKGRXjnOBHM8215n+MWo4GPg
-36shVA2J12MEaK4a9RxGIHejQZyqo21Y3QdDxj+BzCTChVkQmca7qTRtcMbPY1dd
-ZbhC7AVRUnqwX9YJYJ+uRyivl5R2t0t3lzHs69KpCE8LsFRqvUCdyxMFLS+yUT7/
-QlSJU5KW6Dp91o1CVXLjX1Hi7mQMdsA46FyGCe/VV5Iv6P30hLlXQXpMDCPJnhAX
-o3DYQxw5GJuSSuQBHPQMPaZ6vwKTq1c9FqX5If3iFdWr3kbPv60y3pYBOvz1UJvL
-UcOXxBNzWWHabR4GmsgRFMJSeFll/g/B/mZeR0cEyld7fbANHJFE+ymuiuonOjEC
-AwEAAaOB3jCB2zAJBgNVHRMEAjAAMCwGCWCGSAGG+EIBDQQfFh1PcGVuU1NMIEdl
-bmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUIShdx7vhMLi9DIT4mEnPP22x
-SE0wgYAGA1UdIwR5MHehaqRoMGYxCzAJBgNVBAYTAkNOMREwDwYDVQQIDAhIYW5n
-emhvdTERMA8GA1UEBwwISGFuZ3pob3UxDjAMBgNVBAoMBVJva2lkMREwDwYDVQQL
-DAhob21lYmFzZTEOMAwGA1UEAwwFUm9raWSCCQCUTiyua9oxazANBgkqhkiG9w0B
-AQUFAAOCAQEAE4NVZ+SzTH6RknnlWsiz07YFxHT/a4K4K1FA0bdtLw3wxWVEBY/r
-Mc6gZG4C/iV1ziRtjZOd0vv5idXnCf0J3s+RyKsLra0SvlWNg86Md/FA8bjMrnSQ
-GIvzQlGrRyFSij3Y3jq5K24NnjPHnFIzFYuZONbVJ9i0AyjW3twrGXNh+o46Q4PI
-O3kLIyrtL9HrIlsArABqiQTcefopQlOvsJ7+FSoxBjXvOIhxUMhuIFfdob+TlEkm
-mxrzS3l1U+Ach6kszHhhy+5WVR0Imjb9jjOp8KPzxIXeqJShze44mYT4jqiyGcmz
-+V4+JAOryJtv8B2fVUcSTxhT7QEHjwwBkA==
------END CERTIFICATE-----

From 46b1dd77b64b6b2b78764453caabb5227d5bcd6e Mon Sep 17 00:00:00 2001
From: chenximin <ximin.chen@rokid.com>
Date: Mon, 18 Dec 2017 11:47:21 +0800
Subject: [PATCH 07/10] Replace var with let

---
 index.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/index.js b/index.js
index 35e8aa6..7251753 100644
--- a/index.js
+++ b/index.js
@@ -7,7 +7,7 @@ exports.getSubject = x509.getSubject;
 exports.getIssuer = x509.getIssuer;
 
 exports.verifyFromStr = function(certStr, CABundleStr, cb) {
-  let caughtErr = null;
+  var caughtErr = null;
   try {
     x509.verifyFromStr(certStr, CABundleStr);
   }

From 90be4221c350cd007ae53c68ff21e29625b20677 Mon Sep 17 00:00:00 2001
From: chenximin <ximin.chen@rokid.com>
Date: Wed, 27 Jun 2018 15:35:44 +0800
Subject: [PATCH 08/10] optmize code style; add test case; check args

---
 README.md      |  2 +-
 include/x509.h |  4 ++--
 index.js       | 26 +++++++++++++++++++-------
 src/addon.cc   |  4 ++--
 src/x509.cc    |  2 +-
 test/test.js   | 19 ++++++++++++++++++-
 6 files changed, 43 insertions(+), 14 deletions(-)

diff --git a/README.md b/README.md
index 606a5bc..c8eff2b 100644
--- a/README.md
+++ b/README.md
@@ -137,7 +137,7 @@ x509.verify(
 
 ```
 
-#### x509.verifyFromStr(`certStr`, `caStr`, function(err, result){ /*...*/})
+#### x509.verifyFromStr(`certStr`, `CABundleStr`, function(err, result){ /*...*/})
 
 It is the same with verify.
 
diff --git a/include/x509.h b/include/x509.h
index 11c31a4..0eb05c1 100644
--- a/include/x509.h
+++ b/include/x509.h
@@ -24,11 +24,11 @@ NAN_METHOD(get_subject);
 NAN_METHOD(get_issuer);
 NAN_METHOD(parse_cert);
 NAN_METHOD(verify);
-NAN_METHOD(verifyFromStr);
+NAN_METHOD(verify_from_str);
 
 Local<Value> try_parse(const std::string& dataString);
 Local<Value> verify(const std::string& dataString);
-Local<Value> verifyFromStr(const std::string& dataString);
+Local<Value> verify_from_str(const std::string& dataString);
 Local<Value> parse_date(ASN1_TIME *date);
 Local<Value> parse_serial(ASN1_INTEGER *serial);
 Local<Object> parse_name(X509_NAME *subject);
diff --git a/index.js b/index.js
index 7251753..58f7ab5 100644
--- a/index.js
+++ b/index.js
@@ -7,14 +7,27 @@ exports.getSubject = x509.getSubject;
 exports.getIssuer = x509.getIssuer;
 
 exports.verifyFromStr = function(certStr, CABundleStr, cb) {
+  if (typeof cb !== 'function') {
+    throw new Error('cb should be function')
+  }
+  if (certStr instanceof Buffer) {
+    certStr = certStr.toString()
+  } else if (typeof certStr !== 'string') {
+    cb(new Error('certStr should be string or buffer'))
+    return
+  }
+  if (CABundleStr instanceof Buffer) {
+    CABundleStr = CABundleStr.toString()
+  } else if (typeof CABundleStr !== 'string') {
+    cb(new Error('CABundleStr should be string or buffer'))
+    return
+  }
   var caughtErr = null;
   try {
-    x509.verifyFromStr(certStr, CABundleStr);
-  }
-  catch (verificationError) {
+    x509.verify_from_str(certStr, CABundleStr);
+  } catch (verificationError) {
     caughtErr = verificationError;
-  }
-  finally {
+  } finally {
     cb(caughtErr);
   }
 };
@@ -42,8 +55,7 @@ exports.verify = function(certPath, CABundlePath, cb) {
       try {
         x509.verify(certPath, CABundlePath);
         cb(null);
-      }
-      catch (verificationError) {
+      } catch (verificationError) {
         cb(verificationError);
       }
     });
diff --git a/src/addon.cc b/src/addon.cc
index 47c2bed..faf0a47 100644
--- a/src/addon.cc
+++ b/src/addon.cc
@@ -12,8 +12,8 @@ void init(Local<Object> exports) {
     Nan::New<String>(VERSION).ToLocalChecked());
   
   Nan::Set(exports,
-    Nan::New<String>("verifyFromStr").ToLocalChecked(),
-    Nan::New<FunctionTemplate>(verifyFromStr)->GetFunction());
+    Nan::New<String>("verify_from_str").ToLocalChecked(),
+    Nan::New<FunctionTemplate>(verify_from_str)->GetFunction());
 
   Nan::Set(exports,
     Nan::New<String>("verify").ToLocalChecked(),
diff --git a/src/x509.cc b/src/x509.cc
index de2fcbf..a6fdecf 100644
--- a/src/x509.cc
+++ b/src/x509.cc
@@ -46,7 +46,7 @@ std::string parse_args(const Nan::FunctionCallbackInfo<v8::Value>& info) {
   return *String::Utf8Value(info[0]->ToString());
 }
 
-NAN_METHOD(verifyFromStr) {
+NAN_METHOD(verify_from_str) {
   Nan::HandleScope scope;
   OpenSSL_add_all_algorithms();
   std::string cert_str = *String::Utf8Value(info[0]->ToString());
diff --git a/test/test.js b/test/test.js
index 2cf8708..2fb854c 100644
--- a/test/test.js
+++ b/test/test.js
@@ -85,4 +85,21 @@ x509.verifyFromStr(
   function(err, result) {
     assert.throws(assert.ifError.bind(null, err), /Failed to load ca/)
   }
-);
\ No newline at end of file
+);
+
+x509.verifyFromStr(
+  123456,
+  fs.readFileSync(path.join(__dirname, 'CA_chains/enduser-example.com.chain')),
+  function(err, result) {
+    assert.throws(assert.ifError.bind(null, err), /certStr should be string or buffer/)
+  }
+)
+
+try {
+  x509.verifyFromStr(
+    fs.readFileSync(path.join(__dirname, 'certs/acaline.com.crt')),
+    fs.readFileSync(path.join(__dirname, 'CA_chains/enduser-example.com.chain'))
+  )
+} catch (err) {
+  assert.throws(assert.ifError.bind(null, err), /cb should be function/)
+}
\ No newline at end of file

From 22e520ccbf20d4d0dd0d01d5e478fcbc0311e6d8 Mon Sep 17 00:00:00 2001
From: chenximin <ximin.chen@rokid.com>
Date: Thu, 26 Jul 2018 21:55:16 +0800
Subject: [PATCH 09/10] fix style

---
 index.js | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/index.js b/index.js
index 58f7ab5..c97c232 100644
--- a/index.js
+++ b/index.js
@@ -8,19 +8,19 @@ exports.getIssuer = x509.getIssuer;
 
 exports.verifyFromStr = function(certStr, CABundleStr, cb) {
   if (typeof cb !== 'function') {
-    throw new Error('cb should be function')
+    throw new Error('cb should be function');
   }
   if (certStr instanceof Buffer) {
-    certStr = certStr.toString()
+    certStr = certStr.toString();
   } else if (typeof certStr !== 'string') {
-    cb(new Error('certStr should be string or buffer'))
-    return
+    cb(new Error('certStr should be string or buffer'));
+    return;
   }
   if (CABundleStr instanceof Buffer) {
-    CABundleStr = CABundleStr.toString()
+    CABundleStr = CABundleStr.toString();
   } else if (typeof CABundleStr !== 'string') {
-    cb(new Error('CABundleStr should be string or buffer'))
-    return
+    cb(new Error('CABundleStr should be string or buffer'));
+    return;
   }
   var caughtErr = null;
   try {

From de6fbd9c550b62b07da639b1d8511acd00aa3cbc Mon Sep 17 00:00:00 2001
From: chenximin <ximin.chen@rokid.com>
Date: Thu, 20 Dec 2018 15:30:32 +0800
Subject: [PATCH 10/10] lint fix

---
 README.md |  2 +-
 index.js  | 12 ++++++------
 2 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/README.md b/README.md
index c8eff2b..177bd48 100644
--- a/README.md
+++ b/README.md
@@ -137,7 +137,7 @@ x509.verify(
 
 ```
 
-#### x509.verifyFromStr(`certStr`, `CABundleStr`, function(err, result){ /*...*/})
+#### x509.verifyFromStr(`certStr`, `caBundleStr`, function(err, result){ /*...*/})
 
 It is the same with verify.
 
diff --git a/index.js b/index.js
index c97c232..35e1aff 100644
--- a/index.js
+++ b/index.js
@@ -6,7 +6,7 @@ exports.getAltNames = x509.getAltNames;
 exports.getSubject = x509.getSubject;
 exports.getIssuer = x509.getIssuer;
 
-exports.verifyFromStr = function(certStr, CABundleStr, cb) {
+exports.verifyFromStr = function(certStr, caBundleStr, cb) {
   if (typeof cb !== 'function') {
     throw new Error('cb should be function');
   }
@@ -16,15 +16,15 @@ exports.verifyFromStr = function(certStr, CABundleStr, cb) {
     cb(new Error('certStr should be string or buffer'));
     return;
   }
-  if (CABundleStr instanceof Buffer) {
-    CABundleStr = CABundleStr.toString();
-  } else if (typeof CABundleStr !== 'string') {
-    cb(new Error('CABundleStr should be string or buffer'));
+  if (caBundleStr instanceof Buffer) {
+    caBundleStr = caBundleStr.toString();
+  } else if (typeof caBundleStr !== 'string') {
+    cb(new Error('caBundleStr should be string or buffer'));
     return;
   }
   var caughtErr = null;
   try {
-    x509.verify_from_str(certStr, CABundleStr);
+    x509.verify_from_str(certStr, caBundleStr);
   } catch (verificationError) {
     caughtErr = verificationError;
   } finally {