Ever needed a simple key-value store with an encryption backend to handle your app passwords?
Look no further! This is a simple password manger (library) written in Go. You can use it with Go, REST calls and any other language that links with C.
And the best of it all: it's free - BSD licensed - just don't sue me if you loose your passwords (no pun intended, ha!)
go mod tidy
automatically fetches the necessary dependencies when you add the import statement to your code (see also: Go's module support):
import "github.com/image357/password"
Alternatively, use go get
in your project to prefetch all dependencies:
go get -u github.com/image357/password/...@latest
You can use cmake
to build and install the C interface library:
mkdir build; cd build
cmake -DCMAKE_INSTALL_PREFIX=/full/path/to/install/dir ..
cmake --build .
cmake --install .
Then, simply find the installed package in your CMakeLists.txt
:
find_package(password)
message(STATUS "${password_DLL_FILE}")
add_executable(main main.cpp)
target_link_libraries(main PRIVATE password::cinterface)
On Windows you will need MinGW to build the library. This is because cgo doesn't support any other compiler backend yet. Once compiled, you can use the library with any compiler, though. You can also have a look at the release section to see if there are any pre-built binaries for your platform.
package main
import (
"fmt"
"github.com/image357/password"
"github.com/image357/password/log"
"github.com/image357/password/rest"
"log/slog"
)
func main() {
// create password with id
password.Overwrite("myid", "mypassword", "storage_key")
// get password with id
pwd, _ := password.Get("myid", "storage_key")
fmt.Println(pwd)
// start a multi password rest service on localhost:8080
rest.StartMultiService(
":8080", "/prefix", "storage_key",
func(string, string, string, string) bool { return true },
)
// make logging more verbose
log.Level(slog.LevelDebug)
}
#include <stdio.h>
#include <password/cinterface.h>
bool callback(cchar_t *token, cchar_t *ip, cchar_t *resource, cchar_t *id) {
return true;
}
int main() {
// create password with id
CPWD__Overwrite("myid", "mypassword", "storage_key");
// get password with id
char buffer[256];
CPWD__Get("myid", "storage_key", buffer, 256);
printf("%s\n", buffer);
// start a multi password rest service on localhost:8080
CPWD__StartMultiService(":8080", "/prefix", "storage_key", callback);
// make logging more verbose
CPWD__LogLevel(CPWD__LevelDebug);
return 0;
}
There is an example REST service, which you can use for testing. Install it with:
go install github.com/image357/password/cmd/exampleservice@latest
Warning: do not use this service for production use-cases. It doesn't have any access control and the storage key hides in plain sight!
When you have your REST service running (see above) you can make calls with, e.g., python
import requests
# create password with id
requests.put("http://localhost:8080/prefix/overwrite", json={"id": "myid", "password": "mypassword", "accessToken": "some_token"})
# get password with id
r = requests.get("http://localhost:8080/prefix/get", json={"id": "myid", "accessToken": "some_token"})
print(r.content)
# output: b'{"password":"mypassword"}'
The REST API mirrors the Go and C\C++ interface. This means that the signature is the same - with three exceptions:
- For REST you need an
accessToken
. ForGo/C/C++
you need astorage_key
. - The C/C++ API accepts result pointers and returns status codes. The Go API directly returns values and errors.
- The simple service does not require "id" properties and will not bind to
Clean
andList
calls.
Here is a full overview:
Overwrite:
Go: -> password.Overwrite(id string, password string, key string)
C/C++ -> CPWD__Overwrite(const char *id, const char *password, const char *key)
REST: -> (PUT) /prefix/overwrite
Example JSON for REST request:
{
"accessToken": "my_token"
"id": "my_id"
"password": "my_password"
}
Return: {}
Get:
Go: -> password.Get(id string, key string)
C/C++ -> CPWD__Get(const char *id, const char *key, char *buffer, int length)
REST: -> (GET) /prefix/get
Example JSON for REST request:
{
"accessToken": "my_token"
"id": "my_id"
}
Return: {"password": "stored_password"}
Check:
Go: -> password.Get(id string, password string, key string)
C/C++ -> CPWD__Check(const char *id, const char *password, const char *key, bool *result)
REST: -> (GET) /prefix/check
Example JSON for REST request:
{
"accessToken": "my_token"
"id": "my_id"
"password": "password_to_check"
}
Return: {"result": true/false}
Set:
Go: -> password.Set(id string, oldPassword string, newPassword string, key string)
C/C++ -> CPWD__Set(const char *id, const char *oldPassword, const char *newPassword, const char *key)
REST: -> (PUT) /prefix/set
Example JSON for REST request:
{
"accessToken": "my_token"
"id": "my_id"
"oldPassword": "password1"
"newPassword": "password2"
}
Return: {}
Unset:
Go: -> password.Unset(id string, password string, key string)
C/C++ -> CPWD__Unset(const char *id, const char *password, const char *key)
REST: -> (DELETE) /prefix/unset
Example JSON for REST request:
{
"accessToken": "my_token"
"id": "my_id"
"password": "password_to_check"
}
Return: {}
Exists:
Go: -> password.Exists(id string)
C/C++ -> CPWD__Exists(const char *id, bool *result)
REST: -> (GET) /prefix/exists
Example JSON for REST request:
{
"accessToken": "my_token"
"id": "my_id"
}
Return: {"result": true/false}
List:
Go: -> password.List()
C/C++ -> CPWD__List(char *buffer, int length, const char *delim)
REST: -> (GET) /prefix/list
Example JSON for REST request:
{
"accessToken": "my_token"
}
Return: {"ids": ["stored_id", "another_stored_id", ...]}
Delete:
Go: -> password.Delete(id string)
C/C++ -> CPWD__Delete(const char *id)
REST: -> (DELETE) /prefix/delete
Example JSON for REST request:
{
"accessToken": "my_token"
"id": "my_id"
}
Return: {}
Clean:
Go: -> password.Clean()
C/C++ -> CPWD__Clean()
REST: -> (DELETE) /prefix/clean
Example JSON for REST request:
{
"accessToken": "my_token"
}
Return: {}
Files and folders - it's that simple. To make the storage backend cross-platform compatible, ids have the following constraints:
- Forward- and backward-slashes are treated as the same character.
- Upper- and lower-case characters are treated as the same character.
Yes, the usual - AES256, hashed secrets, etc. For more info have a look at the source code.
For full documentation see: docs
There are some additional convenience functions in Go and C/C++ that control storage path, logging and recovery mode. Additionally, you can just explore the source code with any godoc compatible IDE.
Check it out!