Skip to content
This repository has been archived by the owner on Dec 15, 2022. It is now read-only.

Port to Windows #1

Merged
merged 7 commits into from
Oct 20, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Gruntfile.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ module.exports = (grunt) ->
filters:
build:
include: false
namespaces: false
legal:
copyright: false
readability:
Expand All @@ -39,7 +40,7 @@ module.exports = (grunt) ->
failOnError: true

test:
command: 'jasmine-focused --captureExceptions --coffee spec/'
command: 'node node_modules/jasmine-focused/bin/jasmine-focused --captureExceptions --coffee spec/'
options:
stdout: true
stderr: true
Expand Down
31 changes: 28 additions & 3 deletions binding.gyp
Original file line number Diff line number Diff line change
@@ -1,8 +1,33 @@
{
"targets": [
'targets': [
{
"target_name": "keytar",
"sources": [ "src/keytar_mac.cc" ],
'target_name': 'keytar',
'sources': [
'src/main.cc',
'src/keytar.h',
],
'conditions': [
['OS=="mac"', {
'sources': [
'src/keytar_mac.cc',
],
}],
['OS=="win"', {
'sources': [
'src/keytar_win.cc',
],
'msvs_disabled_warnings': [
4267, # conversion from 'size_t' to 'int', possible loss of data
4530, # C++ exception handler used, but unwind semantics are not enabled
4506, # no definition for inline function
],
'link_settings': {
'libraries': [
'-lCrypt32.lib',
],
},
}],
],
}
]
}
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@
"grunt-cli": "~0.1.7",
"grunt-shell": "~0.2.2",
"coffee-script": "~1.6.2",
"jasmine-focused": "~0.7.0",
"jasmine-focused": "~0.15.0",
"rimraf": "~2.1.4",
"node-cpplint": "~0.1.4",
"node-cpplint": "~0.1.5",
"grunt-coffeelint": "0.0.6"
},
"dependencies": {
Expand Down
21 changes: 21 additions & 0 deletions src/keytar.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#ifndef SRC_KEYTAR_H_
#define SRC_KEYTAR_H_

#include <string>

namespace keytar {

bool AddPassword(const std::string& service,
const std::string& account,
const std::string& password);

bool GetPassword(const std::string& service,
const std::string& account,
std::string* password);

bool DeletePassword(const std::string& service,
const std::string& account);

} // namespace keytar

#endif // SRC_KEYTAR_H_
105 changes: 33 additions & 72 deletions src/keytar_mac.cc
Original file line number Diff line number Diff line change
@@ -1,29 +1,12 @@
#include <node.h>
#include <Security/Security.h>
#include <v8.h>
#include <string>

using ::v8::Arguments;
using ::v8::FunctionTemplate;
using ::v8::HandleScope;
using ::v8::Local;
using ::v8::Object;
using ::v8::Null;
using ::v8::String;
using ::v8::Value;

using ::std::string;
#include "keytar.h"

v8::Handle<Value> AddPassword(const Arguments& args) {
String::Utf8Value utf8Service(Local<String>::Cast(args[0]));
string service(*utf8Service);

String::Utf8Value utf8Account(Local<String>::Cast(args[1]));
string account(*utf8Account);
#include <Security/Security.h>

String::Utf8Value utf8Password(Local<String>::Cast(args[2]));
string password(*utf8Password);
namespace keytar {

bool AddPassword(const std::string& service,
const std::string& account,
const std::string& password) {
OSStatus status = SecKeychainAddGenericPassword(NULL,
service.length(),
service.data(),
Expand All @@ -32,69 +15,47 @@ v8::Handle<Value> AddPassword(const Arguments& args) {
password.length(),
password.data(),
NULL);
HandleScope scope;
return scope.Close(v8::Boolean::New(status == errSecSuccess));
return status == errSecSuccess;
}

v8::Handle<Value> GetPassword(const Arguments& args) {
String::Utf8Value utf8Service(Local<String>::Cast(args[0]));
string service(*utf8Service);

String::Utf8Value utf8Account(Local<String>::Cast(args[1]));
string account(*utf8Account);

UInt32 passwordLength;
void *password;
bool GetPassword(const std::string& service,
const std::string& account,
std::string* password) {
void *data;
UInt32 length;
OSStatus status = SecKeychainFindGenericPassword(NULL,
service.length(),
service.data(),
account.length(),
account.data(),
&passwordLength,
&password,
&length,
&data,
NULL);
HandleScope scope;
if (status != errSecSuccess)
return scope.Close(Null());
return false;

return scope.Close(String::NewSymbol((const char*)password, passwordLength));
*password = std::string(reinterpret_cast<const char*>(data), length);
SecKeychainItemFreeContent(NULL, data);
return true;
}

v8::Handle<Value> DeletePassword(const Arguments& args) {
String::Utf8Value utf8Service(Local<String>::Cast(args[0]));
string service(*utf8Service);

String::Utf8Value utf8Account(Local<String>::Cast(args[1]));
string account(*utf8Account);

String::Utf8Value utf8Password(Local<String>::Cast(args[2]));
string password(*utf8Password);

bool DeletePassword(const std::string& service,
const std::string& account) {
SecKeychainItemRef item;
OSStatus status = SecKeychainFindGenericPassword(NULL,
service.length(),
service.data(),
account.length(),
account.data(),
NULL,
NULL,
&item);
if (status == errSecSuccess) {
status = SecKeychainItemDelete(item);
CFRelease(item);
}

HandleScope scope;
return scope.Close(v8::Boolean::New(status == errSecSuccess));
}
service.length(),
service.data(),
account.length(),
account.data(),
NULL,
NULL,
&item);
if (status != errSecSuccess)
return false;

void init(v8::Handle<Object> exports) {
exports->Set(String::NewSymbol("getPassword"),
FunctionTemplate::New(GetPassword)->GetFunction());
exports->Set(String::NewSymbol("addPassword"),
FunctionTemplate::New(AddPassword)->GetFunction());
exports->Set(String::NewSymbol("deletePassword"),
FunctionTemplate::New(DeletePassword)->GetFunction());
status = SecKeychainItemDelete(item);
CFRelease(item);
return status == errSecSuccess;
}

NODE_MODULE(keytar, init)
} // namespace keytar
176 changes: 176 additions & 0 deletions src/keytar_win.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
#include "keytar.h"

#include <windows.h>
#include <Shlobj.h>

#include <algorithm>
#include <vector>

namespace keytar {

namespace {

bool FileExists(const std::string& path) {
DWORD attribute = ::GetFileAttributes(path.c_str());

return (attribute != INVALID_FILE_ATTRIBUTES &&
!(attribute & FILE_ATTRIBUTE_DIRECTORY));
}

bool WriteToFile(const std::string& path, const std::vector<char>& data) {
HANDLE file = ::CreateFile(path.c_str(),
GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (file == INVALID_HANDLE_VALUE)
return false;

DWORD written;
bool r = ::WriteFile(file, data.data(), data.size(), &written, NULL) == TRUE;
if (written != data.size())
r = false;

::CloseHandle(file);
return r;
}

bool ReadFromFile(const std::string& path, std::vector<char>* data) {
HANDLE file = ::CreateFile(path.c_str(),
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (file == INVALID_HANDLE_VALUE)
return false;

DWORD size = ::GetFileSize(file, NULL);
if (size == INVALID_FILE_SIZE) {
::CloseHandle(file);
return false;
}

data->resize(size);
DWORD read;
bool r = ::ReadFile(file, data->data(), data->size(), &read, NULL) == TRUE;
if (read != data->size())
r = false;

::CloseHandle(file);
return r;
}

bool CreatePath(const std::string& path) {
if (!::CreateDirectory(path.c_str(), NULL) &&
::GetLastError() != ERROR_ALREADY_EXISTS)
return false;

return true;
}

bool GetPasswordPath(const std::string& service,
const std::string& account,
std::string* password_path) {
char path[MAX_PATH] = { 0 };
HRESULT r = ::SHGetFolderPath(NULL,
CSIDL_APPDATA | CSIDL_FLAG_CREATE,
NULL,
SHGFP_TYPE_CURRENT,
path);
if (FAILED(r))
return false;

std::string keytar_path = std::string(path) + "\\node-keytar";
if (!CreatePath(keytar_path))
return false;

std::string service_path = keytar_path + '\\' + service;
if (!CreatePath(service_path))
return false;

*password_path = service_path + '\\' + account;
return true;
}

} // namespace

bool AddPassword(const std::string& service,
const std::string& account,
const std::string& password) {
std::string password_path;
if (!GetPasswordPath(service, account, &password_path))
return false;

if (FileExists(password_path))
return false;

DATA_BLOB data_in, data_out;
data_in.pbData = (BYTE*)(password.c_str()); // NOLINT
data_in.cbData = password.length();
if (!::CryptProtectData(&data_in,
L"Password stored by node-keytar.",
NULL,
NULL,
NULL,
0,
&data_out))
return false;

std::vector<char> encrypted(data_out.cbData);
std::copy(data_out.pbData, data_out.pbData + encrypted.size(),
encrypted.begin());
::LocalFree(data_out.pbData);

WriteToFile(password_path, encrypted);
return true;
}

bool GetPassword(const std::string& service,
const std::string& account,
std::string* password) {
std::string password_path;
if (!GetPasswordPath(service, account, &password_path))
return false;

if (!FileExists(password_path))
return false;

std::vector<char> encrypted;
if (!ReadFromFile(password_path, &encrypted))
return false;

DATA_BLOB data_in, data_out;
data_in.pbData = reinterpret_cast<BYTE*>(encrypted.data());
data_in.cbData = encrypted.size();
if (!::CryptUnprotectData(&data_in,
NULL,
NULL,
NULL,
NULL,
0,
&data_out))
return false;

*password = std::string(reinterpret_cast<char*>(data_out.pbData),
data_out.cbData);
::LocalFree(data_out.pbData);
return true;
}

bool DeletePassword(const std::string& service,
const std::string& account) {
std::string password_path;
if (!GetPasswordPath(service, account, &password_path))
return false;

if (!FileExists(password_path))
return false;

return ::DeleteFile(password_path.c_str()) == TRUE;
}

} // namespace keytar
Loading