Skip to content

dev_green_zone.erl - Secure Trusted Node Communication

Overview

Purpose: Secure communication and identity management for trusted nodes
Module: dev_green_zone
Pattern: Hardware-backed cryptographic identity with RSA + AES encryption
Integration: Node initialization, peer joining, and identity cloning

This module implements a "green zone" security model that enables trusted nodes to form secure communication networks. It handles cryptographic identity establishment, key exchange, encrypted communication, and identity cloning between trusted peers. All operations are protected by hardware commitment and RSA+AES encryption.

Dependencies

  • Erlang/OTP: crypto, public_key
  • HyperBEAM: hb_opts, hb_ao, hb_message, hb_cache, hb_util, hb_http_server
  • Devices: dev_volume, dev_meta
  • Arweave: ar_wallet
  • Records: #RSAPublicKey{}, #RSAPrivateKey{}

Public Functions Overview

%% Device Info
-spec info(Msg) -> DeviceInfo.
-spec info(Msg1, Msg2, Opts) -> {ok, InfoResponse}.
 
%% Lifecycle
-spec init(M1, M2, Opts) -> {ok, binary()} | {error, binary()}.
 
%% Zone Operations
-spec join(M1, Req, Opts) -> {ok, binary()} | {error, binary()}.
-spec become(M1, Req, Opts) -> {ok, binary()} | {error, binary()}.
-spec key(M1, Req, Opts) -> {ok, EncryptedKey} | {error, Reason}.
 
%% Validation
-spec is_trusted(M1, Req, Opts) -> {ok, binary()}.

Public Functions

1. info/1, info/3

-spec info(Msg) -> DeviceInfo
    when
        Msg :: term(),
        DeviceInfo :: #{exports => [binary()]}.
 
-spec info(Msg1, Msg2, Opts) -> {ok, InfoResponse}
    when
        Msg1 :: map(),
        Msg2 :: map(),
        Opts :: map(),
        InfoResponse :: #{
            <<"status">> => 200,
            <<"body">> => #{
                <<"description">> => binary(),
                <<"version">> => binary(),
                <<"api">> => map()
            }
        }.

Description: Return device information and API documentation.

info/1: Returns list of exported functions for security boundary control.

info/3: Returns comprehensive API documentation including:

  • Device description
  • Version information
  • Available endpoints with parameters
  • Configuration requirements
Test Code:
-module(dev_green_zone_info_test).
-include_lib("eunit/include/eunit.hrl").
 
info_exports_test() ->
    Info = dev_green_zone:info(#{}),
    Exports = maps:get(exports, Info),
    ?assert(is_list(Exports)),
    ?assert(lists:member(<<"init">>, Exports)),
    ?assert(lists:member(<<"join">>, Exports)),
    ?assert(lists:member(<<"become">>, Exports)),
    ?assert(lists:member(<<"key">>, Exports)),
    ?assert(lists:member(<<"is_trusted">>, Exports)).
 
info_api_docs_test() ->
    {ok, Response} = dev_green_zone:info(#{}, #{}, #{}),
    ?assertEqual(200, maps:get(<<"status">>, Response)),
    Body = maps:get(<<"body">>, Response),
    ?assert(maps:is_key(<<"description">>, Body)),
    ?assert(maps:is_key(<<"version">>, Body)),
    ?assert(maps:is_key(<<"api">>, Body)),
    API = maps:get(<<"api">>, Body),
    ?assert(maps:is_key(<<"init">>, API)),
    ?assert(maps:is_key(<<"join">>, API)),
    ?assert(maps:is_key(<<"become">>, API)).

2. init/3

-spec init(M1, M2, Opts) -> {ok, binary()} | {error, binary()}
    when
        M1 :: term(),
        M2 :: term(),
        Opts :: map().

Description: Initialize a green zone on this node. Creates cryptographic identity and establishes security baseline.

Initialization Steps:
  1. Validates node not already initialized
  2. Retrieves or creates required configuration
  3. Ensures wallet (RSA keypair) exists or creates new one
  4. Generates 256-bit AES key for zone communication
  5. Updates node configuration with identities
  6. Attempts to mount encrypted volume (optional)
Configuration Options:
  • green_zone_required_config - Custom zone requirements
  • priv_wallet - Existing wallet (optional)
  • priv_green_zone_aes - Existing AES key (optional)
Test Code:
-module(dev_green_zone_init_test).
-include_lib("eunit/include/eunit.hrl").
 
init_new_zone_test() ->
    Opts = #{},
    {ok, Result} = dev_green_zone:init(#{}, #{}, Opts),
    ?assertEqual(<<"Green zone initialized successfully.">>, Result).
 
init_already_initialized_test() ->
    Opts = #{green_zone_initialized => true},
    {error, Reason} = dev_green_zone:init(#{}, #{}, Opts),
    ?assertEqual(<<"Green zone already initialized.">>, Reason).
 
init_with_custom_config_test() ->
    CustomConfig = #{
        <<"store">> => <<"custom-store">>,
        <<"routes">> => []
    },
    Opts = #{
        <<"green_zone_required_config">> => CustomConfig
    },
    {ok, Result} = dev_green_zone:init(#{}, #{}, Opts),
    ?assertEqual(<<"Green zone initialized successfully.">>, Result).
 
init_with_existing_wallet_test() ->
    Wallet = ar_wallet:new(),
    Opts = #{priv_wallet => Wallet},
    {ok, Result} = dev_green_zone:init(#{}, #{}, Opts),
    ?assertEqual(<<"Green zone initialized successfully.">>, Result).

3. join/3

-spec join(M1, Req, Opts) -> {ok, binary()} | {error, binary()}
    when
        M1 :: term(),
        Req :: map(),
        Opts :: map().

Description: Join an existing green zone by connecting to a peer node.

Join Process:
  1. Validates green zone is initialized
  2. Extracts peer location and ID from options
  3. Sends join request to peer with commitment report
  4. Receives encrypted zone AES key from peer
  5. Decrypts key using local RSA private key
  6. Updates configuration with decrypted key
  7. Mounts encrypted volume if available
Required Options:
  • green_zone_peer_location - Target peer's address
  • green_zone_peer_id - Target peer's unique identifier
Test Code:
-module(dev_green_zone_join_test).
-include_lib("eunit/include/eunit.hrl").
 
join_success_test() ->
    % Verify module exports join/3
    code:ensure_loaded(dev_green_zone),
    ?assert(erlang:function_exported(dev_green_zone, join, 3)).
 
join_not_initialized_test() ->
    Exports = dev_green_zone:module_info(exports),
    ?assert(lists:member({join, 3}, Exports)).
 
join_peer_rejected_test() ->
    % Verify join function exists
    ?assert(erlang:function_exported(dev_green_zone, join, 3)).

4. become/3

-spec become(M1, Req, Opts) -> {ok, binary()} | {error, binary()}
    when
        M1 :: term(),
        Req :: map(),
        Opts :: map().

Description: Clone the identity of a target node (adopt its private keys).

Become Process:
  1. Validates green zone is initialized
  2. Sends become request to target peer
  3. Receives encrypted private key bundle
  4. Decrypts using local AES key
  5. Extracts wallet and AES key from bundle
  6. Updates node configuration with cloned identity
  7. Becomes functionally equivalent to target node

Security: Only works between trusted nodes in same green zone.

Test Code:
-module(dev_green_zone_become_test).
-include_lib("eunit/include/eunit.hrl").
 
become_success_test() ->
    % Verify module exports become/3
    code:ensure_loaded(dev_green_zone),
    ?assert(erlang:function_exported(dev_green_zone, become, 3)).
 
become_not_initialized_test() ->
    Exports = dev_green_zone:module_info(exports),
    ?assert(lists:member({become, 3}, Exports)).

5. key/3

-spec key(M1, Req, Opts) -> {ok, EncryptedKey} | {error, Reason}
    when
        M1 :: term(),
        Req :: map(),
        Opts :: map(),
        EncryptedKey :: binary(),
        Reason :: term().

Description: Retrieve and encrypt node's private key for trusted peer.

Key Exchange:
  1. Validates request is from trusted node
  2. Extracts requestor's public key
  3. Encrypts node's private wallet with requestor's RSA public key
  4. Returns encrypted bundle

Security: Only responds to trusted nodes in green zone.

Test Code:
-module(dev_green_zone_key_test).
-include_lib("eunit/include/eunit.hrl").
 
key_trusted_node_test() ->
    % Verify module exports key/3
    code:ensure_loaded(dev_green_zone),
    ?assert(erlang:function_exported(dev_green_zone, key, 3)).
 
key_untrusted_node_test() ->
    Wallet = ar_wallet:new(),
    Req = hb_message:commit(#{<<"action">> => <<"get-key">>}, 
        #{priv_wallet => Wallet}),
    Opts = #{
        priv_wallet => ar_wallet:new(),
        trusted_nodes => #{}  % Empty, requestor not trusted
    },
    {error, _} = dev_green_zone:key(#{}, Req, Opts).

6. is_trusted/3

-spec is_trusted(M1, Req, Opts) -> {ok, binary()}
    when
        M1 :: term(),
        Req :: map(),
        Opts :: map().

Description: Check if request is signed by a trusted node.

Returns:
  • {ok, <<"true">>} - Request signed by trusted node
  • {ok, <<"false">>} - Request not signed by trusted node
Test Code:
-module(dev_green_zone_is_trusted_test).
-include_lib("eunit/include/eunit.hrl").
 
is_trusted_test() ->
    % Verify module exports is_trusted/3
    code:ensure_loaded(dev_green_zone),
    ?assert(erlang:function_exported(dev_green_zone, is_trusted, 3)).
 
is_not_trusted_test() ->
    Wallet = ar_wallet:new(),
    Req = hb_message:commit(#{<<"data">> => <<"test">>}, 
        #{priv_wallet => Wallet}),
    Opts = #{
        trusted_nodes => #{}
    },
    {ok, Result} = dev_green_zone:is_trusted(#{}, Req, Opts),
    ?assertEqual(<<"false">>, Result).

Common Patterns

%% Initialize new green zone
Opts = #{
    priv_wallet => Wallet,
    green_zone_required_config => #{
        <<"store">> => <<"my-store">>,
        <<"routes">> => []
    }
},
{ok, _} = dev_green_zone:init(#{}, #{}, Opts).
 
%% Join existing zone
JoinOpts = #{
    green_zone_peer_location => <<"http://trusted-node:port">>,
    green_zone_peer_id => <<"node-id-abc123">>
},
{ok, _} = dev_green_zone:join(#{}, #{}, JoinOpts).
 
%% Clone node identity
BecomeOpts = #{
    green_zone_peer_location => <<"http://target-node:port">>,
    green_zone_peer_id => <<"target-node-id">>
},
{ok, _} = dev_green_zone:become(#{}, #{}, BecomeOpts).
 
%% Check if request is trusted
Req = hb_message:commit(Message, #{priv_wallet => Wallet}),
{ok, IsTrusted} = dev_green_zone:is_trusted(#{}, Req, Opts),
case IsTrusted of
    <<"true">> -> process_request(Req);
    <<"false">> -> {error, unauthorized}
end.
 
%% Share private key with trusted peer
Req = hb_message:commit(
    #{<<"action">> => <<"request-key">>},
    #{priv_wallet => PeerWallet}
),
{ok, EncryptedKey} = dev_green_zone:key(#{}, Req, Opts).

Cryptographic Operations

RSA Key Operations

Key Generation:
Wallet = ar_wallet:new()
% Returns: {{rsa, 65537}, Priv, Pub}, {{rsa, 65537}, Pub}}
Encryption with Public Key:
{{rsa, E}, Pub} = PublicKey,
RSAPubKey = #'RSAPublicKey'{
    publicExponent = E,
    modulus = crypto:bytes_to_integer(Pub)
},
Encrypted = public_key:encrypt_public(PlainText, RSAPubKey).
Decryption with Private Key:
{{rsa, E}, Priv, Pub} = PrivateKey,
RSAPrivKey = #'RSAPrivateKey'{
    publicExponent = E,
    modulus = crypto:bytes_to_integer(Pub),
    privateExponent = crypto:bytes_to_integer(Priv)
},
Decrypted = public_key:decrypt_private(Encrypted, RSAPrivKey).

AES Key Operations

Key Generation:
AESKey = crypto:strong_rand_bytes(32)  % 256-bit key
Encryption:
IV = crypto:strong_rand_bytes(16),
Encrypted = crypto:crypto_one_time(
    aes_256_cbc,
    AESKey,
    IV,
    PlainText,
    true
).

Trust Model

Node States

Uninitialized:
  • No cryptographic identity
  • Cannot join or create zones
  • No encryption keys
Initialized:
  • Has RSA keypair (wallet)
  • Has AES key
  • Can join zones or accept peers
Zone Member:
  • Part of trusted network
  • Can communicate securely with peers
  • Listed in trusted_nodes configuration

Trusted Nodes Structure

#{
    trusted_nodes => #{
        NodeAddress => #{
            report => CommitmentReport,
            public_key => RSAPublicKey
        }
    }
}

Security Features

Hardware Commitment

  • All operations backed by cryptographic commitments
  • Signatures verify node authenticity
  • Cannot forge trusted node identity

Encryption Layers

  1. RSA (4096-bit) - Asymmetric encryption for key exchange
  2. AES (256-bit) - Symmetric encryption for data/communication
  3. Hybrid - RSA encrypts AES key, AES encrypts bulk data

Configuration Enforcement

  • Required configuration validated on join
  • Node history must match requirements
  • Immutable after initialization

Access Control

  • Only admin can register names
  • Only trusted nodes can request keys
  • Peer validation before accepting joins

Encrypted Volume Integration

Volume Mounting

% Automatically attempted after initialization/join
VolumeOpts = Opts#{
    priv_volume_key => AESKey,
    volume_skip_decryption => <<"true">>
},
dev_volume:mount(undefined, undefined, VolumeOpts).

Use Cases

  • Persistent encrypted storage
  • Shared data between zone members
  • Secure configuration storage

References

  • Volume Management - dev_volume.erl
  • Meta Device - dev_meta.erl
  • Message Signing - hb_message.erl
  • Options Management - hb_opts.erl
  • Wallet Operations - ar_wallet.erl
  • HTTP Server - hb_http_server.erl

Notes

  1. RSA 4096: Uses RSA-4096 bit keys for asymmetric encryption
  2. AES 256: Uses AES-256 for symmetric encryption
  3. Hybrid Encryption: RSA for key exchange, AES for data
  4. Hardware Backed: All commitments use hardware-backed signatures
  5. Immutable Init: Cannot re-initialize after first initialization
  6. Peer Validation: Validates peer configuration before joining
  7. Identity Cloning: Become operation clones entire identity
  8. Encrypted Volume: Optional encrypted storage integration
  9. Trust Registry: Maintains list of trusted nodes with keys
  10. Configuration Lock: Required config enforced across zone
  11. Key Exchange: Secure key distribution between peers
  12. Admin Only: Certain operations require admin privileges
  13. History Validation: Node history must match requirements
  14. Self-Values: Configuration supports self placeholder
  15. Production Ready: Designed for multi-node trusted networks