FTN8: FutoIn Security Concept
Version: 0.2DV
Date: 2017-12-29
Copyright: 2014-2018 FutoIn Project (http://futoin.org)
Authors: Andrey Galkin

CHANGES

1. Intro

Security concept is required to build a unified authentication and authorization model across security domains and use cases with separate control in open environment like internet.

There is no global trusted party is allowed. Chain of trust must be fully distributed.

1.1. Sub-specifications

2. Concept

2.1. General guidelines

  1. External parties to particular system must never hold internal security-related data even if it is encrypted and/or signed to prevent theoritical falsification attacks.
  2. There must be limit for failed attempts of bruteforcing any secret
  3. Check authentication and authorization in online mode
  4. Centralize authentication and authorization
  5. Often used secrets must be updated in reasonable period
  6. Avoid information leaks
  7. Forward Secrecy

2.2. Security Contexts

There are three major security contexts:

Each Service and each Client must trust only one AuthService.

The standard auth mechanism does not allow Client code to access protected resources in another Service directly. Such feature may be implemented as sub-spec.

In addition, the following optional services exist:

2.3. Holistic pictures

2.3.1. Request Processing

It may look as too much overhead for a single request processing, but any decent system does exactly the same in fact. However, instead of modules are used instead of separate microservices. FutoIn converts modules to services by fundamental design. There should be efficient in-process calling mechanism to minimize penalties.

Client         Service        AuthService   DefenseService
    |             .                  .            .
    |-- request ->|                  .            .
    .             |----------- onCall() --------->|
    .             .                  .     [may fail early]
    .             |<------ defense action --------|
    .             |-- checkAuth() -->|            .
    .             |<-- auth rsp -----|            .
    .             |- checkAccess() ->|            .
    .             .           [fail on error]     .
    .       [defense action]         .            .
    .         [process]              .            .
    .             |----------- onResult() ------->|
    |<- response -|                  .            .
    |             .                  .            .

2.3.2. Service to AuthService registration

It's assumed that one of Message Authentication Code approaches are used which require a preshared secret and secure updates of that.

Initial manual registration:

Operator                AuthService
    |                        .
    |--- register Service -->|
    .                 [gen initial secret]
    |<-- clear text secret --|
[manually configure Service] .
    .                        .

Initial automatic registration through secure channel, if allowed:

Service                            AuthService
   |                                   .
[gen temporary assymetric key]         .
   |----- request registration ------->|
   .                [depend on transport's MitM security]
   .                    [save request for approval]
   |<----- provide ticket ID ----------|
   .                                   .
[put ticket ID in .well-known]         |
   .                                   .
   |-- try to complete registration -->|
   .                            [ticket is not validated]
   |<-- HTTPS GET .well-known ticket --|
   |------- return ticket ID --------->|
   .                            [ticket is validated]
   |<- "pending" or "rejected" error --|
   .                                   .
[reasonable delay]               [wait approval]
   .                                   .
   |-- try to complete registration -->|
   |<- "pending" or "rejected" error --|
   .                                   .
[reasonable delay]         [operator/auto approval]
   .                                   .
   |-- try to complete registration -->|
   .                         [generate new secret]
   |<-- return new encrypted secret ---|
[decrypt secret and discard key]       .
[use new secret]                       .
   |                                   .

Shared secret secure exchange:

Service                            AuthService
   |                                   .
[gen temporary assymetric key]         .
   |----- request new secret --------->|
   .             [use current secret for MitM security]
   .                         [generate new secret]
   |<-- return new encrypted secret ---|
[decrypt secret and discard key]       .
[use new secret]                       .
   |                                   .

2.3.3. Single Sign-On (SSO)

General goal is to concentrate user authentication and access grants in single place - AuthService. See below for description of Access Request Templates.

Register authorization access request templates:

Service                           AuthService
   |                                   .
   |-------- create template --------->|
   .                 [register template of required access requests]
   |<----- return redirect details ----|
[use secret to sign & check redirects on both peers]
   |                                   .

First visit of Service ever:

Client                Service                AuthService
    |                    .                        .
    |------ visit ------>|                        .
    .      [create signed redirect from template]
    |<---- redirect -----|                        .
    |----- provide signed payload --------------->|
    .                    .                     [verify sig]
    .                    .     [drive user registration/login process]
    .                    .       [ask user for access]
    |<---- redirect user back --------------------|
    |-- signed result -->|                        .
    .              [verify sig]                   .
    .                    |-- register session --->|
    .                    |<-------- OK -----------|
    |<--- logged in -----|                        .
[use of Service]         .                        .
    .               [periodic renew]              .
    .                    |--- renew session ----->|
    .                    |<-------- OK -----------|
[use of Service]         .                        .    
    |---- logout ------->|                        .
    .                    |----- end session ----->|
    .                    |<-------- OK -----------|
    |<--- logged out ----|                        .
    |                    .                        .

Second visit of Service from known device:

Client          Service                 AuthService
    |               .                        .
    |---- visit --->|                        .
    .        [check known user]              .
    .               |--- renew session ----->|
    .               |<-------- OK -----------|
    |<- logged in --|                        .
[use of Service]    .                        .
    |               .                        .

Immediate logout:

Service                 AuthService
   |                          .
   |---- listen for events -->|
   .                          .
   .                          .
   |<--- invalidate session --|
[invalidate local session]    .
   .                          .

Foreign users (local AuthService acts as proxy):

Client          Service                 AuthService         ForeignAuthService
    .               .                        .                      .
    .               .                        |---- register self -->|
    .               .                        .                      .
    .               |-- listen for events -->|                      .
    .               .                        |- listen for events ->|
    |---- visit --->|                        .                      .
    .           [unknown user]               .                      .
    |<-- redirect --|                        .                      .
    |--- provide signed payload ------------>|                      .
    .               .                     [verify sig]              .
    .               .                [user chooses extral auth]     .
    |<----- redirect to foreign -------------|                      .
    |-------------------------- provide signed payload ------------>|
    .               .                        .                [verify sig]
    .               .                        .              [process user auth]
    |<--------- return back local AuthService ----------------------|
    |--- provide signed return ------------->|                      .
    .               .                   [verify sig]                .
    .               .                        |-- register session ->|
    .               .                        |<------ OK -----------|
    |<-- return back Service ----------------|                      .
    |-- get back -->|                        .                      .
    .          [verify sig]                  .                      .
    .               |-- register session --->|                      .
    .               |<-------- OK -----------|                      .
    |<- logged in --|                        .                      .
[use of Service]    .                        .                      .
    |               .                        .                      .

2.3.4. Access on behalf of user

On-behalf-of calls is standard feature of FTN3.

Each Service registers a list of generic access descriptors it provides which can be granted by user to another user(service).

Another Service creates Access Request Templates as a list of generic access descriptors it wants to ask from User. When user grants the required access, Service can call another Service on behalf of user.

It's assumed that user has full access to own resources protected only by required security levels. User can grant resource access to another user or Service based on Access Control descriptors.

Local user:

Client         Service1     Service2                 AuthService
    .             .             .                         .
    .             .             |- register descriptors ->|
    .             .             .                         .
    .             |-- create template ------------------->|
    .             .             .                         .
    |-- visit --->|             .                         .
    |<- redirect -|             .                         .
    |----- provide signed payload ----------------------->|
    .             .             .                   [verify sig]
    .             .             .                    [ask user]
    .             .             .                  [grant access]
    |<----- signed redirect back -------------------------|
    |- return --->|             .                         .
    .        [verify sig]       .                         .
    .             |- API call ->|                         .
    .             .     [request checking]                .
    .             .             |---- checkAuth() ------->|
    .             .             |--- checkAccess() ------>|
    .             .     [request processing]              .
    .             |<-- result --|                         .
    .             .             .                         .

Foreign user access just adds extra complexity:

  1. Both user authentication and other user/service authorization is done in foreign AuthService
  2. User can review & control all grants in home services
  3. Local to Service AccessControl has to consult with foreign AccessControl for access, cache it and revoke events similar to user sessions.
  4. AuthService acts as proxy:

2.3.5. Exceptional operation confirmation

In many cases Service needs to securely confirm some action like bank transfer approval. For that reason, Service creates special confirmation request in AuthService and redirects the user there.

Client               Service                 AuthService
    |                   .                         .
    |- request action ->|                         .
    .                   |- prepare confirmation ->|
    .                   .               [store with timeout]
    .                   |<-- provide URL ---------|
    |<---- redirect ----|                         .
    |------ use the AuthService URL ------------->|
    .                   .            [ask user confirmation]
    .                   .                  [store result]
    |<---- signed redirect back ------------------|
    |-- return -------->|                         .
    .               [verify sig]                  .
    .                   |-- verify confirmation ->|
    .                   |<----- OK ---------------|
    .            [complete action]                .
    .                   .                         .

2.4. AuthService and scope of identification

The scope of AuthService is arbitrary - it is formed by AuthService itself. However, AuthService should have a single domain name which is used as global scope identifier.

2.5. User identification

Each user has a unique local ID and global ID.

Local ID is arbitrary and assigned by AuthService.

Global ID based on local ID and scope name of home AuthService. Typically, email address is the global identifier.

2.6. Foreign users

If the Service or Client trust different AuthServices then it is responsibility of AuthService to establish communication to another AuthService. In such case, Client is called foreign AuthService user or simply foreign user.

Foreign users are detected based on mismatch of associated global ID scope name and current AuthService scope name.

Theoretically, AuthService can auto-discover and establish registration to any other foreign AuthService. However, it may be undesired from security point of view. So, only whitelisted foreign AuthServices should be allowed. Whitelist can be either local or global in form of association of AuthService providers.

2.7. Service to Service interaction (and Service to AuthService in particular)

Each Service as logical entity assumes to have own user ID in scope of related AuthService. Therefore, communication authentication follows the same pattern as Client-to-Service pattern.

2.8. Authentication assumptions

Client-to-Service and Service-to-Service communication has different natural aspects:

Therefore, specification is separated for Client(human) and Service(software) cases.

2.9. Interoperation with non-compliant AuthService scopes

There are many OpenID, OAuth, SAML and other single sign-on alternatives. Particular AuthService may easily integrate with those.

2.10. Service & AuthService in single instance

It's assumed that each Service is accompanied by unified AuthService logic to efficiently process requests on scale. Such AuthService can be either full-featured or limited to support of only foreign users.

2.11. General MAC generation requirements

MAC stays for Message Authentication Code which prevents decryptable transmission of authentication secrets and helps to ensure message integrity.

2.11.1 Rules of MAC payload generation

Note 1: MAC logic must be abstract of JSON as far as possible to be efficiently used in other message coding methods.

Note 2: research to be done to support TupleHash and non-JSON representation of fields as an option.

2.11.2. Predefined MAC algorithms

Executor may refuse to support any MAC algo and throw SecurityError.

Note: current suggested default is either HMAC-SHA-256 or HMAC-SHA-512-256

2.11.3. Response "sec" field with MAC

If request comes signed with any MAC then response must also be signed with MAC using exactly the same secret key and MAC algorithm.

Invoker must validate response "sec" field and fail on mismatch or absence of one.

2.11.4. Key derivation strategies

It is called "Strategy", but not "Function" on purpose as the same KDF may be used different ways.

2.11.4.1. General derived key ID

Derived Key ID must be transmitted as Base64 encoding string without padding. Key ID or salt should be of recommended size, if applicable.

Based on strategy, no key ID or a fixed minimal derived key ID may be used for current version of the specification to minimize performance impact and simplify Executor's derived key caching logic. Master Secret itself should provide enough entropy to ensure derived key's quality. So, key update gets bound to frequency of Master Secret update. Key derivation would be used only to get different keys based on purpose.

2.11.4.2. Key purpose name

Below is list of ASCII values to use for altering key derivation logic.

2.11.4.3. Performance tradeoff

In most cases, it's not feasibile to generate a new derived key for every message, so Invoker should be able to reuse the key at own discretion.

As a defensive measure, Executor peer is allowed to reject requests, if derived key either changes too often or used for too long. Executor should cache derived keys for reasonable time, but still prevent their leaking outside. Executor should consider that Invoker may be clustered with unique derived key at every node.

Executor can be configured to support only certain types of strategies named below and to reject requests with "SecurityError" on mismatch.

2.11.4.4. Key derivation strategies names

  1. HKDF0 - use [HKDF][] with per GlobalServiceID as "salt" and purpose name for "info" to derive unique keys per purpose from shared Master Secret.
  2. HKDF - use [HKDF][] with UUID for "salt" and purpose name for "info" to derive unique keys per purpose from shared Master Secret.

2.12. Security Levels

It's important to understand characteristics of performed user authentication in many cases.

2.13. Client fingerprints

The following fingerpints are essential to enforce extra level of protection.

2.14. Authentication rejection limits and blocking

The specification suggests the following limits to be enforced. Hit of the limits must block any processing. The blocking must be done for entire period of enforcement.

3. Interface

3.1. Common types

{
    "iface" : "futoin.auth.types",
    "version" : "{ver}",
    "ftn3rev" : "1.8",
    "imports" : [
        "futoin.types:1.0"
    ],
    "types" : {
        "LocalUserID" : "UUIDB64",
        "LocalUser" : {
            "type" : "string",
            "regex" : "^[a-zA-Z]([a-zA-Z0-9_.-]{0,30}[a-zA-Z0-9])?$"
        },
        "GlobalService" : "Domain",            
        "GlobalUserID" : [ "Email", "GlobalService" ],
        "MACAlgo" : {
            "type" : "enum",
            "items" : [
                "HMAC-MD5",
                "HMAC-SHA-224",
                "HMAC-SHA-256",
                "HMAC-SHA-384",
                "HMAC-SHA-512",
                "HMAC-SHA3-224",
                "HMAC-SHA3-256",
                "HMAC-SHA3-384",
                "HMAC-SHA3-512",
                "KMAC128",
                "KMAC256"
            ]
        },
        "StatelessSecret" : {
            "type" : "string",
            "minlen" : 8,
            "maxlen" : 32
        },
        "MACValue" : {
            "type" : "Base64",
            "minlen" : 1,
            "maxlen" : 128
        },
        "MACBase" : {
            "type" : "string",
            "minlen" : 8
        },
        "MasterSecretID" : "UUIDB64",
        "MasterScope" : "Domain",
        "KeyDerivationStrategy" : {
            "type" : "enum",
            "items" : [
                "HKDF",
                "HKDF0"
            ]
        },
        "KeyPurpose" : {
            "type" : "enum",
            "items" : [
                "MAC",
                "ENC",
                "EXPOSED"
            ]
        },
        "ExchangeKeyType" : {
            "type" : "enum",
            "items" : [
                "RSAE-2048",
                "RSAE-4096",
                "ECDHE-Curve25519",
                "ECDHE-Curve448"
            ]
        },
        "ExchangeKey" : {
            "type" : "Base64",
            "minlen" : 1,
            "maxlen" : 20000
        },
        "EncryptedKey" : {
            "type" : "Base64",
            "minlen" : 1,
            "maxlen" : 1000
        },
        "EncryptedMasterSecret" : "EncryptedKey",
        "UserAgent" : {
            "type" : "string",
            "maxlen" : 256
        },
        "X509Cert" : {
            "type" : "Base64",
            "maxlen" : 20000
        },
        "SSHPubKey" : {
            "type" : "string",
            "maxlen" : 1000
        },
        "ClientToken" : {
            "type" : "Base64",
            "maxlen" : 342,
            "desc" : "Unique per Service per Client device persistent token"
        },
        "ClientFingerprints" : {
            "type" : "map",
            "fields" : {
                "user_agent" : {
                    "type" : "UserAgent",
                    "optional" : true
                },
                "source_ip" : {
                    "type" : "IPAddress",
                    "optional" : true
                },
                "x509" : {
                    "type" : "X509Cert",
                    "optional" : true
                },
                "ssh_pubkey" : {
                    "type" : "SSHPubKey",
                    "optional" : true
                },
                "client_token" : {
                    "type" : "ClientToken",
                    "optional" : true
                },
                "misc" : {
                    "type" : "map",
                    "optional" : true
                }
            }
        },
        "AuthInfo" : {
            "type" : "map",
            "fields" : {
                "local_id" : "LocalUserID",
                "global_id" : "GlobalUserID"
            }
        },
        "RedirectURL" : {
            "type" : "string",
            "regex" : "^https?://[a-z0-9-]+(\\.[a-z0-9-]+)*\\.[a-z]{2,}/[a-Z0-9_/-]*(\\?[a-Z][a-Z0-9]*=)?$",
            "maxlen" : 128
        },
        "ResourceURL" : {
            "type" : "string",
            "regex" : "^https?://[a-z0-9-]+(\\.[a-z0-9-]+)*\\.[a-z]{2,}/[a-Z0-9_/?=%&;.-]*$",
            "maxlen" : 128
        },
        "ParamConstraint" : {
            "type" : "map",
            "elemtype" : "array"
        },
        "AccessControlDescriptor" : {
            "type" : "map",
            "fields" : {
                "iface" : {
                    "type" : "FTNFace",
                    "optional" : true
                },
                "ver" : {
                    "type" : "FTNVersion",
                    "optional" : true
                },
                "func" : {
                    "type" : "FTNFunction",
                    "optional" : true
                },
                "params" : {
                    "type" : "ParamConstraint",
                    "optional" : true,
                    "desc": "Named paramater must match one of the values"
                }
            },
            "desc" : "Granted API access constraints in scope of arbitrary Service"
        },
        "AccessControlDescriptorList" : {
            "type" : "array",
            "elemtype" : "AccessControlDescriptor",
            "desc" : "List of granted API access in scope of arbitrary Service"
        },
        "AccessGroupName" : {
            "type" : "GenericIdentifier",
            "maxlen" : 32,
            "desc" : "Service-specific arbitrary ACD grouping identifier"
        },
        "AccessGroup" : {
            "type" : "map",
            "fields" : {
                "id" : "AccessGroupName",
                "name" : "ItemTranslations",
                "desc" : "ItemTranslations",
                "acds" : "AccessControlDescriptorList",
                "icon" : "ResourceURL"
            },
            "desc" : "Services-specific arbitrary ACD grouping definition"
        },
        "AccessGroupList" : {
            "type" : "array",
            "elemtype" : "ServiceAccessGroup",
            "desc" : "List of Service-specific ACD groupings"
        },
        "ServiceAccessGroup" : {
            "type" : "map",
            "fields" : {
                "service" : "GlobalService",
                "access_group" : "AccessGroupName"
            },
            "desc" : "Global pointer to ACD group of specific Service"
        },
        "ServiceAccessGroupList" : {
            "type" : "array",
            "elemtype" : "ServiceAccessGroup",
            "desc" : "List of global pointers to Service-specific ACD groups"
        }
    }
}

3.2. AuthService management

{
    "iface" : "futoin.auth.manage",
    "version" : "{ver}",
    "ftn3rev" : "1.8",
    "imports" : [
        "futoin.ping:1.0",
        "futoin.auth.types:{ver}"
    ],
    "funcs" : {
        "setup" : {
            "params" : {
                "domain" : "Domain",
                "clear_auth" : {
                    "type" : "boolean",
                    "default" : false
                },
                "mac_auth" : {
                    "type" : "boolean",
                    "default" : true
                },
                "master_auth" : {
                    "type" : "boolean",
                    "default" : true
                },
                "master_auto_reg" : {
                    "type" : "boolean",
                    "default" : false
                },
                "auth_service" : {
                    "type" : "boolean",
                    "default" : false
                }
            },
            "result" : "boolean"
        },
        "genConfig" : {
            "result" : {
                "domain" : "Domain",
                "clear_auth" : "boolean",
                "mac_auth" : "boolean",
                "master_auth" : "boolean",
                "master_auto_reg" : "boolean"
            }
        },
        "ensureUser" : {
            "params" : {
                "user" : "LocalUser",
                "global_id" : {
                    "type": "GlobalUserID",
                    "default" : null,
                    "desc" : "Defaults to auto-generated"
                },
                "hostname" : {
                    "type" : "Domain",
                    "default" : null,
                    "desc" : "Required for Service users"
                }
            },
            "result" : "LocalUserID",
            "throws" : [
                "GlobalUserIDMismatch"
            ]
        },
        "ensureService" : {
            "params" : {
                "hostname" : {
                    "type" : "GlobalService",
                    "default" : null,
                    "desc" : "Act as both local and global user name"
                }
            },
            "result" : "LocalUserID"
        }
    },
    "requires" : [
        "SecureChannel",
        "MessageSignature"
    ]
}

=END OF SPEC=