9295c1f645
This change will rename the confusing "url" within HaikuDepot to be "identifier" in line with corresponding changes in pkg kit and HDS. Also at the same time support is introduced for HDS repos' meta-data to artificially match against multiple repos; as requested for the future R1B3 release process. Some tidy-ups and extensions have been made to the JSON schema-to-model and the schema-to-parser scripts. Change-Id: I402e7d610986039f58d72028bda7de977e9115e2 Reviewed-on: https://review.haiku-os.org/c/haiku/+/2986 Reviewed-by: Adrien Destugues <pulkomandy@gmail.com>
232 lines
9.5 KiB
Plaintext
232 lines
9.5 KiB
Plaintext
/*
|
|
* Copyright 2020 Haiku, Inc. All rights reserved.
|
|
* Distributed under the terms of the MIT License.
|
|
*
|
|
* Authors:
|
|
* Niels Sascha Reedijk, niels.reedijk@gmail.com
|
|
*/
|
|
|
|
/*!
|
|
\page app_keystore Password and Key Storage API
|
|
|
|
Haiku R1 introduces the first version of a system-wide key store service,
|
|
allows you as developer to outsource some of the credential and certificate
|
|
management, as well as providing an infrastructure that enables sharing
|
|
these artifacts between applications.
|
|
|
|
\warning The implementation in Haiku R1 is limited and is a promise of more
|
|
to come. Functionality beyond storing and sharing passwords is for the
|
|
future. Also please read the security section of this document,
|
|
especially when you are working with artifacts that are more sensitive.
|
|
In many cases you will find that this system service will work just as
|
|
well as making your own implementation, but there are instances in
|
|
which you may chose not to use it.
|
|
|
|
\section app_keystore_overview 1. Highlevel Overview (components)
|
|
|
|
The implementation is based around the following concepts:
|
|
- The \b keystore is the centralized repository for your keys. It is
|
|
managed by the \b keystore_server and it contains one or more
|
|
\b keyrings.
|
|
- A \b keyring is a collection of keys. There is always a
|
|
\b master \b keyring, which cannot be removed. Access is organized
|
|
around keyrings. From a user's perspective, when an application wants
|
|
to access keys in a keyring, the user will have to grant permission to
|
|
that application to access the keyring. A keyring is identified by a
|
|
name, which needs to be unique on the user's system.
|
|
- A keyring contains \b keys. These are the smallest unit in the system.
|
|
A key can be anything that you want to safeguard in the keystore. Keys
|
|
are identified by the combination of an identifier and a secondary
|
|
identifier. These should be unique within a keyring.
|
|
- The final piece is the concept of \b permissions. In the current
|
|
implementation, an application needs to ask permission to access a
|
|
keyring. The \c keystore_server will validate the permissions, and if
|
|
necessary prompt the user to grant one-time or a permant access. If the
|
|
user only allows it once, access is granted until the application
|
|
terminates.
|
|
|
|
As a user of the API, you will mostly be working with \ref BKeyStore, as
|
|
the access point that queries and modifies keyrings and keys. An individual
|
|
key is represented by the \ref BKey object.
|
|
|
|
\section app_keystore_security 2. Security
|
|
|
|
The current implementation of this API should be considered low-security.
|
|
The most important thing to know is that there is \b no \b encryption
|
|
applied when storing the keys and keyrings to the drive. This means that
|
|
the data can be read by any malicious actor that can access the drive of
|
|
your machine.
|
|
|
|
This should also puts the current locking mechanism in perspective. While
|
|
the \c keystore_server will prompt the user to grant (or deny) access to
|
|
your application, when it wants to access a keyring, this again does not
|
|
prevent any malicious actor to bypass the \c keystore_server and directly
|
|
read from (and write to!) the file.
|
|
|
|
When considering on whether to use the current API, there are a few things
|
|
to think about:
|
|
- First, consider whether you should store the keys at all. Passwords to
|
|
services with extremely sensitive personal or financial information,
|
|
such as email passwords or credentials to financial institutions, should
|
|
not be stored at all. Prompt your user for the credentials when needed,
|
|
and don't keep them for later use.
|
|
- Secondly, if you are storing credentials for use with web services,
|
|
check if the service you are using supports using access tokens. Many
|
|
APIs have them, and often use it in combination with some form of
|
|
permission system or scoping, making it possible for you to keep access
|
|
as limited as possible. Furthermore, the user often has the ability to
|
|
revoke access to a token, in case they think it is compromised.
|
|
- When you assess that you really do need to store the credentials, make
|
|
a determination first about whether or not the credentials should have
|
|
some form of encryption. For now you should consider looking for another
|
|
solution to storing sensitive data, but contributions to improve this
|
|
API are very welcome. It is beyond the scope of this document to discuss
|
|
strategies around encryption.
|
|
- When you assess the risk is low enough not to employ encryption
|
|
strategies, you may consider using this API. It is particularly
|
|
recommended if you will be sharing the credentials with more than one
|
|
application.
|
|
|
|
\warning In the end, it is up to you as a developer to be conscious of any
|
|
choices you make when it comes to user data, and credentials are no
|
|
different. When you decide that the Password and Key API does not fit
|
|
your needs, choose a framework or library that does fit your purpose.
|
|
|
|
\section app_keystore_usage 3. Practical use of the API
|
|
|
|
Below are two distinct examples on how you may use the API.
|
|
|
|
\subsection app_keystore_usage_web The Langlaufer Web Browser
|
|
|
|
We are working on the infamous Langlaufer web browser, and we are adding
|
|
a feature where we autocomplete user names and passwords. It is decided to
|
|
use the Password and Key Storage API to do our key management. Whenever we
|
|
land on a web page with a login screen, we will try to see if we have
|
|
credentials for that web page. Part of the requirements is that we support
|
|
more than one set of credentials for a web page.
|
|
|
|
It is decided that the application will store the user credentials in it's
|
|
own keyring, as we do not want to interfere with any other keys in the
|
|
master key. Additionally, we will use both the primary and secondary
|
|
identifier fields. The primary will contain the hostname of the website,
|
|
and the secondary will contain the user name.
|
|
|
|
One final design note is that all the calls to the \c keystore_server are
|
|
synchronous, meaning they will block until there is a response back from
|
|
the keystore. In the case that a user needs to be prompted for a password,
|
|
the call will be blocked until they make a decision. That is why any calls
|
|
on \ref BKeyStore should be done on a separate worker thread, instead of
|
|
within the logic of a Window.
|
|
|
|
For clarity, the example below displays the interaction with the
|
|
\ref BKeyStore and \ref BKey classes through some utility functions. It is
|
|
up to the reader to put that in a separate working thread.
|
|
|
|
\code{.cpp}
|
|
#include <Key.h>
|
|
#include <KeyStore.h>
|
|
|
|
const char *kLanglauferKeyringName = "Langlaufer";
|
|
|
|
BObjectList<BPasswordKey>
|
|
GetKeysForWebsite(const char *baseUrl) {
|
|
// There may be more than one match, so we use the iteration methods.
|
|
BKeyStore keyStore;
|
|
uint32 cookie;
|
|
BPasswordKey currentKey;
|
|
BObjectList<BPasswordKey> list;
|
|
bool next = true;
|
|
|
|
while(next) {
|
|
status_t status = keyStore.GetNextKey(kLanglauferKeyringName,
|
|
B_KEY_TYPE_PASSWORD, B_KEY_PURPOSE_WEB, cookie, currentKey);
|
|
switch(status) {
|
|
case B_OK:
|
|
// Try to see if the key matches the website
|
|
if (currentKey.Identifier() == baseUrl) {
|
|
// Add the item to the list.
|
|
list.AddItem(new BPasswordKey(currentKey));
|
|
}
|
|
break;
|
|
case B_BAD_VALUE:
|
|
// The keyring does not exist, create it, and end the
|
|
// search
|
|
CreateKeyring();
|
|
next = false;
|
|
break;
|
|
default:
|
|
// Something else went wrong, like the user did not give
|
|
// authorization, or we are at the end of the list.
|
|
// Bail out the search at this point.
|
|
next = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return list;
|
|
}
|
|
|
|
void
|
|
CreateKeyring() {
|
|
BKeyStore keyStore;
|
|
// Ignore the return value in the next line, it may fail but that won't
|
|
// interrupt the flow of our program.
|
|
keyStore.AddKeyring(kLanglauferKeyringName);
|
|
}
|
|
|
|
void
|
|
AddOrReplaceKey(const char *baseUrl, const char *user, const char *password) {
|
|
BKeyStore keyStore;
|
|
BPasswordKey key;
|
|
|
|
// Fill out the key with existing data, or create new data
|
|
if (keyStore.GetKey(kLanglauferKeyringName, B_KEY_TYPE_PASSWORD, baseUrl, user, &key) == B_OK) {
|
|
// Remove the existing key
|
|
keyStore.RemoveKey(kLanglauferKeyringName, key);
|
|
// Update the password
|
|
key.SetPassword(password);
|
|
} else {
|
|
key.SetTo(password, B_KEY_PURPOSE_WEB, user, password);
|
|
}
|
|
|
|
// Store the updated/new key in the keyring
|
|
keyStore.AddKey(kLanglauferKeyringName, key);
|
|
}
|
|
\endcode
|
|
|
|
\subsection app_keystore_usage_coolwebservice The CoolWebService Tool Suite
|
|
|
|
We are working on a set of tools that interface with a cool web service.
|
|
Instead of building one monolithic application, we make several small tools
|
|
with specific jobs for this cool web service. One of the tools does the
|
|
authentication, and stores the key in the master keyring on the system. The
|
|
other tools use this key to access the API.
|
|
|
|
Each tool requires the authentication token to be set up properly. That's
|
|
why in the \ref BApplication::ReadyToRun() hook we check for the
|
|
availability of the key. If it is not available, or it does not work, the
|
|
user will be redirected to the authentication tool. The key will be stored
|
|
as a password. It will be identified by the identifier "CoolWebService".
|
|
|
|
\code{.cpp}
|
|
void
|
|
CoolPushTool::ReadyToRun() {
|
|
BKeyStore keyStore;
|
|
BPasswordKey key;
|
|
|
|
if (keyStore.GetKey(B_KEY_TYPE_PASSWORD, "CoolWebService", key) != B_OK) {
|
|
// Terminate the application and re-authenticate
|
|
...
|
|
}
|
|
|
|
// Extract the key
|
|
BString accessToken = key.Password();
|
|
|
|
// Validate the key, and if succesful, continue
|
|
...
|
|
}
|
|
\endcode
|
|
|
|
*/
|