// Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to permit // persons to whom the Software is furnished to do so, subject to the // following conditions: // // The above copyright notice and this permission notice shall be included // in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. #ifndef SRC_NODE_CRYPTO_H_ #define SRC_NODE_CRYPTO_H_ #include "node.h" #include "node_object_wrap.h" #include "v8.h" #include #include #include #include #include #include #include #include #include #ifdef OPENSSL_NPN_NEGOTIATED #include "node_buffer.h" #endif #define EVP_F_EVP_DECRYPTFINAL 101 namespace node { namespace crypto { static X509_STORE* root_cert_store; // Forward declaration class Connection; class SecureContext : ObjectWrap { public: static void Initialize(v8::Handle target); SSL_CTX *ctx_; // TODO: ca_store_ should probably be removed, it's not used anywhere. X509_STORE *ca_store_; protected: static const int kMaxSessionSize = 10 * 1024; static v8::Handle New(const v8::Arguments& args); static v8::Handle Init(const v8::Arguments& args); static v8::Handle SetKey(const v8::Arguments& args); static v8::Handle SetCert(const v8::Arguments& args); static v8::Handle AddCACert(const v8::Arguments& args); static v8::Handle AddCRL(const v8::Arguments& args); static v8::Handle AddRootCerts(const v8::Arguments& args); static v8::Handle SetCiphers(const v8::Arguments& args); static v8::Handle SetOptions(const v8::Arguments& args); static v8::Handle SetSessionIdContext(const v8::Arguments& args); static v8::Handle Close(const v8::Arguments& args); static v8::Handle LoadPKCS12(const v8::Arguments& args); static SSL_SESSION* GetSessionCallback(SSL* s, unsigned char* key, int len, int* copy); static int NewSessionCallback(SSL* s, SSL_SESSION* sess); SecureContext() : ObjectWrap() { ctx_ = NULL; ca_store_ = NULL; } void FreeCTXMem() { if (ctx_) { if (ctx_->cert_store == root_cert_store) { // SSL_CTX_free() will attempt to free the cert_store as well. // Since we want our root_cert_store to stay around forever // we just clear the field. Hopefully OpenSSL will not modify this // struct in future versions. ctx_->cert_store = NULL; } SSL_CTX_free(ctx_); ctx_ = NULL; ca_store_ = NULL; } else { assert(ca_store_ == NULL); } } ~SecureContext() { FreeCTXMem(); } private: }; class ClientHelloParser { public: enum FrameType { kChangeCipherSpec = 20, kAlert = 21, kHandshake = 22, kApplicationData = 23, kOther = 255 }; enum HandshakeType { kClientHello = 1 }; enum ParseState { kWaiting, kTLSHeader, kSSLHeader, kPaused, kEnded }; ClientHelloParser(Connection* c) : conn_(c), state_(kWaiting), offset_(0), body_offset_(0), written_(0) { } size_t Write(const uint8_t* data, size_t len); void Finish(); inline bool ended() { return state_ == kEnded; } private: Connection* conn_; ParseState state_; size_t frame_len_; uint8_t data_[18432]; size_t offset_; size_t body_offset_; size_t written_; }; class Connection : ObjectWrap { public: static void Initialize(v8::Handle target); #ifdef OPENSSL_NPN_NEGOTIATED v8::Persistent npnProtos_; v8::Persistent selectedNPNProto_; #endif #ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB v8::Persistent sniObject_; v8::Persistent sniContext_; v8::Persistent servername_; #endif protected: static v8::Handle New(const v8::Arguments& args); static v8::Handle EncIn(const v8::Arguments& args); static v8::Handle ClearOut(const v8::Arguments& args); static v8::Handle ClearPending(const v8::Arguments& args); static v8::Handle EncPending(const v8::Arguments& args); static v8::Handle EncOut(const v8::Arguments& args); static v8::Handle ClearIn(const v8::Arguments& args); static v8::Handle GetPeerCertificate(const v8::Arguments& args); static v8::Handle GetSession(const v8::Arguments& args); static v8::Handle SetSession(const v8::Arguments& args); static v8::Handle LoadSession(const v8::Arguments& args); static v8::Handle IsSessionReused(const v8::Arguments& args); static v8::Handle IsInitFinished(const v8::Arguments& args); static v8::Handle VerifyError(const v8::Arguments& args); static v8::Handle GetCurrentCipher(const v8::Arguments& args); static v8::Handle Shutdown(const v8::Arguments& args); static v8::Handle ReceivedShutdown(const v8::Arguments& args); static v8::Handle Start(const v8::Arguments& args); static v8::Handle Close(const v8::Arguments& args); #ifdef OPENSSL_NPN_NEGOTIATED // NPN static v8::Handle GetNegotiatedProto(const v8::Arguments& args); static v8::Handle SetNPNProtocols(const v8::Arguments& args); static int AdvertiseNextProtoCallback_(SSL *s, const unsigned char **data, unsigned int *len, void *arg); static int SelectNextProtoCallback_(SSL *s, unsigned char **out, unsigned char *outlen, const unsigned char* in, unsigned int inlen, void *arg); #endif #ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB // SNI static v8::Handle GetServername(const v8::Arguments& args); static v8::Handle SetSNICallback(const v8::Arguments& args); static int SelectSNIContextCallback_(SSL *s, int *ad, void* arg); #endif int HandleBIOError(BIO *bio, const char* func, int rv); enum ZeroStatus { kZeroIsNotAnError, kZeroIsAnError }; int HandleSSLError(const char* func, int rv, ZeroStatus zs); void ClearError(); void SetShutdownFlags(); static Connection* Unwrap(const v8::Arguments& args) { Connection* ss = ObjectWrap::Unwrap(args.Holder()); ss->ClearError(); return ss; } Connection() : ObjectWrap(), hello_parser_(this) { bio_read_ = bio_write_ = NULL; ssl_ = NULL; next_sess_ = NULL; } ~Connection() { if (ssl_ != NULL) { SSL_free(ssl_); ssl_ = NULL; } if (next_sess_ != NULL) { SSL_SESSION_free(next_sess_); next_sess_ = NULL; } #ifdef OPENSSL_NPN_NEGOTIATED if (!npnProtos_.IsEmpty()) npnProtos_.Dispose(); if (!selectedNPNProto_.IsEmpty()) selectedNPNProto_.Dispose(); #endif #ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB if (!sniObject_.IsEmpty()) sniObject_.Dispose(); if (!sniContext_.IsEmpty()) sniContext_.Dispose(); if (!servername_.IsEmpty()) servername_.Dispose(); #endif } private: static void SSLInfoCallback(const SSL *ssl, int where, int ret); BIO *bio_read_; BIO *bio_write_; SSL *ssl_; ClientHelloParser hello_parser_; bool is_server_; /* coverity[member_decl] */ SSL_SESSION* next_sess_; friend class ClientHelloParser; friend class SecureContext; }; class Cipher : public ObjectWrap { public: static void Initialize (v8::Handle target); bool CipherInit(char* cipherType, char* key_buf, int key_buf_len); bool CipherInitIv(char* cipherType, char* key, int key_len, char* iv, int iv_len); int CipherUpdate(char* data, int len, unsigned char** out, int* out_len); int SetAutoPadding(bool auto_padding); int CipherFinal(unsigned char** out, int *out_len); protected: static v8::Handle New(const v8::Arguments& args); static v8::Handle CipherInit(const v8::Arguments& args); static v8::Handle CipherInitIv(const v8::Arguments& args); static v8::Handle CipherUpdate(const v8::Arguments& args); static v8::Handle SetAutoPadding(const v8::Arguments& args); static v8::Handle CipherFinal(const v8::Arguments& args); Cipher() : ObjectWrap(), initialised_(false) { } ~Cipher() { if (initialised_) { EVP_CIPHER_CTX_cleanup(&ctx); } } private: EVP_CIPHER_CTX ctx; /* coverity[member_decl] */ const EVP_CIPHER *cipher; /* coverity[member_decl] */ bool initialised_; }; class Decipher : public ObjectWrap { public: static void Initialize(v8::Handle target); bool DecipherInit(char* cipherType, char* key_buf, int key_buf_len); bool DecipherInitIv(char* cipherType, char* key, int key_len, char* iv, int iv_len); int DecipherUpdate(char* data, int len, unsigned char** out, int* out_len); int SetAutoPadding(bool auto_padding); int DecipherFinal(unsigned char** out, int *out_len); protected: static v8::Handle New(const v8::Arguments& args); static v8::Handle DecipherInit(const v8::Arguments& args); static v8::Handle DecipherInitIv(const v8::Arguments& args); static v8::Handle DecipherUpdate(const v8::Arguments& args); static v8::Handle SetAutoPadding(const v8::Arguments& args); static v8::Handle DecipherFinal(const v8::Arguments& args); Decipher() : ObjectWrap(), initialised_(false) { } ~Decipher() { if (initialised_) { EVP_CIPHER_CTX_cleanup(&ctx); } } private: EVP_CIPHER_CTX ctx; const EVP_CIPHER *cipher_; bool initialised_; }; class Hmac : public ObjectWrap { public: static void Initialize (v8::Handle target); bool HmacInit(char* hashType, char* key, int key_len); int HmacUpdate(char* data, int len); int HmacDigest(unsigned char** md_value, unsigned int *md_len); protected: static v8::Handle New(const v8::Arguments& args); static v8::Handle HmacInit(const v8::Arguments& args); static v8::Handle HmacUpdate(const v8::Arguments& args); static v8::Handle HmacDigest(const v8::Arguments& args); Hmac() : ObjectWrap(), initialised_(false) { } ~Hmac() { if (initialised_) { HMAC_CTX_cleanup(&ctx); } } private: HMAC_CTX ctx; /* coverity[member_decl] */ const EVP_MD *md; /* coverity[member_decl] */ bool initialised_; }; class Hash : public ObjectWrap { public: static void Initialize (v8::Handle target); bool HashInit (const char* hashType); int HashUpdate(char* data, int len); protected: static v8::Handle New(const v8::Arguments& args); static v8::Handle HashUpdate(const v8::Arguments& args); static v8::Handle HashDigest(const v8::Arguments& args); Hash() : ObjectWrap(), initialised_(false) { } ~Hash() { if (initialised_) { EVP_MD_CTX_cleanup(&mdctx); } } private: EVP_MD_CTX mdctx; /* coverity[member_decl] */ const EVP_MD *md; /* coverity[member_decl] */ bool initialised_; }; class Sign : public ObjectWrap { public: static void Initialize(v8::Handle target); bool SignInit (const char* signType); int SignUpdate(char* data, int len); int SignFinal(unsigned char** md_value, unsigned int *md_len, char* key_pem, int key_pemLen); protected: static v8::Handle New(const v8::Arguments& args); static v8::Handle SignInit(const v8::Arguments& args); static v8::Handle SignUpdate(const v8::Arguments& args); static v8::Handle SignFinal(const v8::Arguments& args); Sign() : ObjectWrap(), initialised_(false) { } ~Sign () { if (initialised_) { EVP_MD_CTX_cleanup(&mdctx); } } private: EVP_MD_CTX mdctx; /* coverity[member_decl] */ const EVP_MD *md; /* coverity[member_decl] */ bool initialised_; }; class Verify : public ObjectWrap { public: static void Initialize (v8::Handle target); bool VerifyInit (const char* verifyType); int VerifyUpdate(char* data, int len); int VerifyFinal(char* key_pem, int key_pemLen, unsigned char* sig, int siglen); protected: static v8::Handle New (const v8::Arguments& args); static v8::Handle VerifyInit(const v8::Arguments& args); static v8::Handle VerifyUpdate(const v8::Arguments& args); static v8::Handle VerifyFinal(const v8::Arguments& args); Verify() : ObjectWrap(), initialised_(false) { } ~Verify() { if (initialised_) { EVP_MD_CTX_cleanup(&mdctx); } } private: EVP_MD_CTX mdctx; /* coverity[member_decl] */ const EVP_MD *md; /* coverity[member_decl] */ bool initialised_; }; class DiffieHellman : public ObjectWrap { public: static void Initialize(v8::Handle target); bool Init(int primeLength); bool Init(unsigned char* p, int p_len); bool Init(unsigned char* p, int p_len, unsigned char* g, int g_len); protected: static v8::Handle DiffieHellmanGroup(const v8::Arguments& args); static v8::Handle New(const v8::Arguments& args); static v8::Handle GenerateKeys(const v8::Arguments& args); static v8::Handle GetPrime(const v8::Arguments& args); static v8::Handle GetGenerator(const v8::Arguments& args); static v8::Handle GetPublicKey(const v8::Arguments& args); static v8::Handle GetPrivateKey(const v8::Arguments& args); static v8::Handle ComputeSecret(const v8::Arguments& args); static v8::Handle SetPublicKey(const v8::Arguments& args); static v8::Handle SetPrivateKey(const v8::Arguments& args); DiffieHellman() : ObjectWrap(), initialised_(false), dh(NULL) { } ~DiffieHellman() { if (dh != NULL) { DH_free(dh); } } private: bool VerifyContext(); bool initialised_; DH* dh; }; void InitCrypto(v8::Handle target); } // namespace crypto } // namespace node #endif // SRC_NODE_CRYPTO_H_