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
-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:- Validates node not already initialized
- Retrieves or creates required configuration
- Ensures wallet (RSA keypair) exists or creates new one
- Generates 256-bit AES key for zone communication
- Updates node configuration with identities
- Attempts to mount encrypted volume (optional)
green_zone_required_config- Custom zone requirementspriv_wallet- Existing wallet (optional)priv_green_zone_aes- Existing AES key (optional)
-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:- Validates green zone is initialized
- Extracts peer location and ID from options
- Sends join request to peer with commitment report
- Receives encrypted zone AES key from peer
- Decrypts key using local RSA private key
- Updates configuration with decrypted key
- Mounts encrypted volume if available
green_zone_peer_location- Target peer's addressgreen_zone_peer_id- Target peer's unique identifier
-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:- Validates green zone is initialized
- Sends become request to target peer
- Receives encrypted private key bundle
- Decrypts using local AES key
- Extracts wallet and AES key from bundle
- Updates node configuration with cloned identity
- 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:- Validates request is from trusted node
- Extracts requestor's public key
- Encrypts node's private wallet with requestor's RSA public key
- 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
-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}}{{rsa, E}, Pub} = PublicKey,
RSAPubKey = #'RSAPublicKey'{
publicExponent = E,
modulus = crypto:bytes_to_integer(Pub)
},
Encrypted = public_key:encrypt_public(PlainText, RSAPubKey).{{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 keyIV = 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
- Has RSA keypair (wallet)
- Has AES key
- Can join zones or accept peers
- 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
- RSA (4096-bit) - Asymmetric encryption for key exchange
- AES (256-bit) - Symmetric encryption for data/communication
- 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
- RSA 4096: Uses RSA-4096 bit keys for asymmetric encryption
- AES 256: Uses AES-256 for symmetric encryption
- Hybrid Encryption: RSA for key exchange, AES for data
- Hardware Backed: All commitments use hardware-backed signatures
- Immutable Init: Cannot re-initialize after first initialization
- Peer Validation: Validates peer configuration before joining
- Identity Cloning: Become operation clones entire identity
- Encrypted Volume: Optional encrypted storage integration
- Trust Registry: Maintains list of trusted nodes with keys
- Configuration Lock: Required config enforced across zone
- Key Exchange: Secure key distribution between peers
- Admin Only: Certain operations require admin privileges
- History Validation: Node history must match requirements
- Self-Values: Configuration supports
selfplaceholder - Production Ready: Designed for multi-node trusted networks