NetBurner 3.5.6
PDF Version
SSH Programming Guide

SSH Programming Guide for NetBurner Devices

Overview

The Secure Shell Protocol (SSH) is a cryptographic network protocol for secure remote access and command execution over unsecured networks. First introduced in 1995, SSH remains the industry standard for secure remote administration.

NetBurner SSH Implementation

NetBurner has supported SSH since NNDK 2.0. As of NNDK 3.3.6, we've migrated from Dropbear to wolfSSH, providing significant improvements:

┌──────────────────────────────────────────────────────────┐
│ NetBurner SSH Evolution │
├──────────────────────────────────────────────────────────┤
│ │
│ NNDK 2.0 - 3.3.5 NNDK 3.3.6+ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ Dropbear │ ──> │ wolfSSH │ │
│ │ SSH │ │ │ │
│ └─────────────┘ └─────────────┘ │
│ │
│ Features: Enhanced Features: │
│ - Server only - Server & Client support │
│ - Limited ciphers - Modern cipher suites │
│ - Separate crypto lib - Shared wolfCrypt library │
│ - Smaller code footprint │
│ - Auto-generated keys │
└──────────────────────────────────────────────────────────┘

Key Benefits of wolfSSH

Enhanced Security**

  • Updated cipher suites and algorithms
  • Support for modern cryptographic standards
  • Regular security updates

    Client Support**

  • SSH client functionality now available
  • Connect to remote SSH servers from NetBurner devices
  • Automated remote management capabilities

    Code Efficiency**

  • Shared wolfCrypt library with SSL/TLS
  • Reduced memory footprint when using both protocols
  • Optimized for embedded systems

    Simplified Key Management**

  • Automatic key generation on device
  • Multiple key format support
  • Flexible key installation methods

What's New in NNDK 3.3.6+

Major Changes Summary

┌─────────────────────────────────────────────────────────────┐
│ wolfSSH Migration │
├─────────────────────────────────────────────────────────────┤
│ │
│ [ADDED] SSH Client Support │
│ [ADDED] Onboard Key Generation │
│ [ADDED] UserAuthManager Class │
│ [ADDED] Enhanced Authentication (keys + passwords) │
│ [IMPROVED] Modern Cipher Suites │
│ [IMPROVED] Shared Crypto Library (wolfCrypt) │
│ [CHANGED] Task Management (CryptoServer) │
│ [REMOVED] Dropbear-specific Functions │
│ │
└─────────────────────────────────────────────────────────────┘
The user authorization manager class allows application developers the ability to manage user authori...
Definition UserAuthManager.h:116

Architecture Change

Previous Architecture (NNDK 3.3.5 and earlier):**

┌─────────────┐ ┌─────────────┐
│ SSH Session │ │ SSH Session │
│ (Task) │ │ (Task) │
└─────────────┘ └─────────────┘
| |
v v
┌─────────────────────────────────┐
│ Dropbear Library │
└─────────────────────────────────┘

New Architecture (NNDK 3.3.6+):**

┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ SSH Session │ │ SSH Session │ │ SSL Session │
└─────────────┘ └─────────────┘ └─────────────┘
| | |
v v v
┌───────────────────────────────────────────────────────┐
│ CryptoServer Task │
│ (Priority: SECURITY_TASK_PRIO) │
├───────────────────────────────────────────────────────┤
│ │
│ ┌──────────┐ ┌──────────┐ │
│ │ wolfSSH │ <──────> │ wolfCrypt│ │
│ └──────────┘ └──────────┘ │
│ ^ │
│ | │
│ ┌──────────┐ │
│ │ wolfSSL │ │
│ └──────────┘ │
└───────────────────────────────────────────────────────┘
#define SECURITY_TASK_PRIO
Definition constants.h:142

Key Architectural Improvements:**

  • Unified task management through CryptoServer
  • Shared cryptographic primitives (wolfCrypt)
  • Reduced resource overhead
  • Consistent security across protocols

Key Types and Formats

Supported Key Types

The SSH library supports both ECC (Elliptic Curve Cryptography) and RSA keys.

┌───────────────────────────────────────────────────────────┐
│ Supported SSH Key Types │
├───────────────────────────────────────────────────────────┤
│ │
│ ECC (Elliptic Curve) - RECOMMENDED │
│ ┌─────────────────────────────────────────────────┐ │
│ │ Key Sizes: 160 to 512 bits │ │
│ │ Advantages: - Smaller key size │ │
│ │ - Faster operations │ │
│ │ - Lower memory usage │ │
│ │ Use Case: Embedded systems, IoT devices │ │
│ └─────────────────────────────────────────────────┘ │
│ │
│ RSA (Rivest-Shamir-Adleman) │
│ ┌─────────────────────────────────────────────────┐ │
│ │ Default Size: 2048 bits │ │
│ │ Optional: 4096 bits (define ENABLE_RSA_4K) │ │
│ │ Advantages: - Widely compatible │ │
│ │ - Well-established │ │
│ │ Use Case: Legacy system compatibility │ │
│ └─────────────────────────────────────────────────┘ │
│ │
└───────────────────────────────────────────────────────────┘

Enabling RSA 4096-bit Keys

To enable 4096-bit RSA key support:

  1. Open <nndk_install>\nbrtos\include\predef.h
  2. Add the following definition:
#define ENABLE_RSA_4K
  1. Rebuild your application

Supported Key Formats

Both key types support two formats:

┌─────────────────────────────────────────────────────┐
│ SSH Key Format Support │
├─────────────────────────────────────────────────────┤
│ │
│ PEM Format (Privacy-Enhanced Mail) │
│ ┌───────────────────────────────────────────┐ │
│ │ -----BEGIN RSA PRIVATE KEY----- │ │
│ │ Base64 encoded key data... │ │
│ │ -----END RSA PRIVATE KEY----- │ │
│ │ │ │
│ │ - Human readable (text) │ │
│ │ - Base64 encoding │ │
│ │ - Easier to copy/paste │ │
│ │ - Larger file size │ │
│ └───────────────────────────────────────────┘ │
│ │
│ ASN.1/DER Format (Distinguished Encoding Rules) │
│ ┌───────────────────────────────────────────┐ │
│ │ Binary encoded key data │ │
│ │ (not human readable) │ │
│ │ │ │
│ │ - Binary format │ │
│ │ - Smaller file size │ │
│ │ - Faster parsing │ │
│ │ - More efficient for embedded systems │ │
│ └───────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────┘

Migration Guide

Project Configuration

Step 1: Add Required Include Paths**

When building SSH applications in NBEclipse, add the following include paths:

${NNDK_ROOT}/libraries/include/ssh
${NNDK_ROOT}/libraries/include/crypto

Step 2: Configure in NBEclipse**

Project Configuration Flow:
1. Right-click project
|
v
2. Select "Properties"
|
v
3. Navigate to "C/C++ Build" > "Settings"
|
v
4. Select "Includes" under:
- GNU C Compiler
- GNU C++ Compiler
|
v
5. Click green "+" icon
|
v
6. Add paths:
${NNDK_ROOT}/libraries/include/ssh
${NNDK_ROOT}/libraries/include/crypto
|
v
7. Click "Apply"
|
v
8. Clean and rebuild project

Removed Functions

The following Dropbear-specific functions have been removed:

┌─────────────────────────────────────────────────────────────┐
│ Removed Functions │
├─────────────────────────────────────────────────────────────┤
│ │
│ SshConvertDecodedOpenSSLKey() │
│ ├─ Purpose: Converted PEM to Dropbear format │
│ ├─ Reason: No longer needed - native PEM/ASN.1 support │
│ └─ Action: Remove from code │
│ │
│ SshSetchansessionrequest() │
│ ├─ Purpose: Set callback for channel requests │
│ ├─ Reason: Handled internally by wolfSSH │
│ └─ Action: Remove callback - automatic handling │
│ │
│ SshSetTaskPriority() │
│ ├─ Purpose: Set SSH task priority │
│ ├─ Reason: Managed by CryptoServer task │
│ └─ Action: Remove - uses SECURITY_TASK_PRIO
│ │
└─────────────────────────────────────────────────────────────┘

Updated Functions

Replace deprecated functions with their enhanced versions:

Authentication Function Pointer

Old (Deprecated):**

// Old function pointer - password only
typedef int (*sshUserAuthenticateFn)(const char* username, const char* password);
// Old setter
// Old getter
void SshSetUserAuthenticate(sshUserAuthenticateFn sshUserAuthenticateFnPtr)
[DEPRECATED] Sets the user defined server authentication function. Please consider sshUserAuthenticat...
int(* sshUserAuthenticateFn)(const char *usernamePtr, const char *passwordPtr)
[DEPRECATED] User provided SSH username and password authenticate routine for a server....
Definition NbWolfSsh.h:107
sshUserAuthenticateFn SshGetUserAuthenticate(void)
[DEPRECATED] Gets the user defined server authentication function. Please consider SshGetUserAuthenti...

New (Recommended):**

// New function pointer - supports password AND key authentication
typedef int (*sshUserAuthenticateWithTypeFn)(const char* username,
const char* authValue,
AuthType authType);
// New setter
// New getter
void SshSetUserAuthenticateWithType(sshUserAuthenticateWithTypeFn sshUserAuthenticateFnPtr)
Sets the user defined server authentication function.
int(* sshUserAuthenticateWithTypeFn)(const char *usernamePtr, const char *authValPtr, AuthType authType)
User provided SSH user authenticate routine for a server.
Definition NbWolfSsh.h:119
sshUserAuthenticateWithTypeFn SshGetUserAuthenticateWithType(void)
Gets the user defined server authentication function..
AuthType
The types of authorization requests that are managed. These just indicate what the has value is,...
Definition UserAuthManager.h:44

Migration Example:**

// OLD CODE (NNDK 3.3.5 and earlier)
int MyAuthCallback(const char* username, const char* password) {
// Only handled password authentication
if (strcmp(username, "admin") == 0 && strcmp(password, "secret") == 0) {
return 1; // Success
}
return 0; // Failure
}
void setup() {
SshSetUserAuthenticate(MyAuthCallback);
}
// NEW CODE (NNDK 3.3.6+)
int MyAuthCallbackWithType(const char* username,
const char* authValue,
AuthType authType) {
// Handle both password and key authentication
if (authType == AUTH_TYPE_PASSWORD) {
if (strcmp(username, "admin") == 0 &&
strcmp(authValue, "secret") == 0) {
return 1; // Success
}
}
else if (authType == AUTH_TYPE_KEY) {
// Validate public key
if (ValidatePublicKey(username, authValue)) {
return 1; // Success
}
}
return 0; // Failure
}
void setup() {
SshSetUserAuthenticateWithType(MyAuthCallbackWithType);
}

Authentication Type Enum:**

typedef enum {
AUTH_TYPE_PASSWORD, // Password-based authentication
AUTH_TYPE_KEY, // Public key authentication
AUTH_TYPE_UNKNOWN // Unknown/unsupported type

Key Management

Key Installation Methods

SSH keys can be installed using three methods, checked in the following priority order:

Key Selection Priority (Checked in Order):
1. Runtime Callback
┌─────────────────────────────┐
SshSetUserGetKey() callback │
│ Returns key dynamically │
└─────────────────────────────┘
|
| Not set?
v
2. Compiled-In Key
┌─────────────────────────────┐
│ Override GetPrivateKeyPEM() │
│ Weak function override │
└─────────────────────────────┘
|
| Not overridden?
v
3. Auto-Generated Key
┌─────────────────────────────┐
│ Onboard generated key │
│ Same as SSL/TLS key │
└─────────────────────────────┘
void SshSetUserGetKey(sshUserGetKeyFn sshUserGetKeyFnPtr)
Sets the user defined callback method to provide the server key.
const char * GetPrivateKeyPEM()
Function that returns a pointer to the compiled in server key.
Definition SSH/SecureSerToEthFactoryApp/src/ssluser.cpp:101

Method 1: Runtime Callback (Highest Priority)

Use this method for dynamic key selection or key rotation.

#include <ssh.h>
// Callback signature
typedef int (*SshUserGetKeyFn)(const char** keyData,
int* keyLen,
int* keyFormat);
// Example implementation
int GetSshKey(const char** keyData, int* keyLen, int* keyFormat) {
// Load key from secure storage, file system, etc.
static const char myKey[] =
"-----BEGIN EC PRIVATE KEY-----\n"
"MHcCAQEEIIGN...key data...zqk\n"
"-----END EC PRIVATE KEY-----\n";
*keyData = myKey;
*keyLen = sizeof(myKey) - 1; // Exclude null terminator
*keyFormat = SSH_KEY_FORMAT_PEM;
return 1; // Success
}
void UserMain(void* pd) {
init();
// Set the callback
SshSetUserGetKey(GetSshKey);
// Start SSH server
StartSsh();
while(1) { OSTimeDly(TICKS_PER_SECOND); }
}
#define TICKS_PER_SECOND
System clock ticks per second.
Definition constants.h:49
void init()
System initialization. Ideally called at the beginning of all applications, since the easiest Recover...

Key Format Constants:**

#define SSH_KEY_FORMAT_PEM 0 // PEM format
#define SSH_KEY_FORMAT_DER 1 // DER/ASN.1 format

Method 2: Compiled-In Key (Medium Priority)

Override the weak function to provide a compiled-in key.

#include <ssh.h>
// Weak function (defined in SSH library)
// Override this in your application
extern const unsigned char myPrivateKey[];
extern const int myPrivateKeyLen;
// Override the weak function
const char* GetPrivateKeyPEM(void) {
return (const char*)myPrivateKey;
}
// In a separate file (e.g., ssh_key.cpp)
// Generated using compfile utility
extern const unsigned char myPrivateKey[] = {
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, // PEM header
// ... key data ...
};
extern const int myPrivateKeyLen = sizeof(myPrivateKey);

Generating Compiled Key File:**

# Convert PEM key to C array using compfile
compfile ssh_key.pem myPrivateKey myPrivateKeyLen ssh_key.cpp
# Add ssh_key.cpp to your project's makefile
CPP_SRC += src/ssh_key.cpp

Method 3: Auto-Generated Key (Lowest Priority)

Enable automatic key generation on the device.

// The device will automatically generate a key if:
// 1. No callback is set (Method 1)
// 2. GetPrivateKeyPEM() is not overridden (Method 2)
// 3. No existing key is found in storage
// The auto-generated key is the same key used for SSL/TLS
// certificates, reducing storage and memory requirements
void UserMain(void* pd) {
init();
// Start SSH - will auto-generate key if needed
StartSsh();
printf("SSH server started with auto-generated key\n");
while(1) { OSTimeDly(TICKS_PER_SECOND); }
}

Enable Auto-Regeneration:**

To automatically regenerate expired keys, add to predef.h:

// Enable automatic key regeneration
#define ENABLE_AUTOCERT_REGEN
// Optional: Set check interval (default: 1 minute)
#define AUTO_CERT_GEN_CHECK (60 * 20) // Check every 20 minutes

Key Generation Details:**

  • Generated key is stored in non-volatile memory
  • Persists across reboots
  • Shared with SSL/TLS (same key for both protocols)
  • Automatically regenerated when expired (if enabled)
  • ECC SECP384R1 by default (configurable)

    Related Examples:** See <nndk_install>/examples/SSL/SslOnboardCertGeneration/ for:

  • Simple: Basic auto-generation
  • Advanced: Custom key attributes
  • CompiledCa: Using a compiled CA

User Authentication

UserAuthManager Class

NetBurner provides the UserAuthManager class to simplify user credential management.

┌──────────────────────────────────────────────────────┐
UserAuthManager Architecture │
├──────────────────────────────────────────────────────┤
│ │
│ Application Code │
│ ┌────────────────────────────────────────────┐ │
│ │ Add/Remove/Verify Users │ │
│ └──────────────────┬─────────────────────────┘ │
│ | │
│ v │
│ ┌──────────────────────────────────────────────┐ │
│ │ UserAuthManager │ │
│ │ ┌────────────────────────────────────┐ │ │
│ │ │ - Hashed password storage │ │ │
│ │ │ - Public key storage │ │ │
│ │ │ - Authentication verification │ │ │
│ │ └────────────────────────────────────┘ │ │
│ └──────────────────┬───────────────────────────┘ │
│ | │
│ v │
│ ┌──────────────────────────────────────────────┐ │
│ │ Storage Layer (via callbacks) │ │
│ │ ┌────────────────────────────────────┐ │ │
│ │ │ LoadAuthRecordsFn callback │ │ │
│ │ │ SaveAuthRecordsFn callback │ │ │
│ │ └────────────────────────────────────┘ │ │
│ │ │ │
│ │ Choose storage: │ │
│ │ - User Param space │ │
│ │ - SD card / Flash │ │
│ │ - External EEPROM │ │
│ │ - Database │ │
│ └──────────────────────────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────┘
int(* LoadAuthRecordsFn)(UserAuthRecord *authRec)
User provided function for loading user authorization records. This allows the users to dictate where...
Definition UserAuthManager.h:107
int(* SaveAuthRecordsFn)(const UserAuthRecord *authRec)
User provided function for saving user authorization records. This allows the users to dictate where ...
Definition UserAuthManager.h:96

Key Features

Security Features:**

  • Passwords are hashed before storage
  • Keys are stored securely
  • No plain-text credential leakage
  • Secure comparison functions

    Flexibility:**

  • Custom storage backends via callbacks
  • Support for password and key authentication
  • User-defined storage format (JSON, binary, etc.)
  • In-memory or persistent storage

Basic Usage

#include <UserAuthManager.h>
// Global instance
UserAuthManager authManager;
// Initialize with storage callbacks
void InitializeAuth() {
// Set load/save callbacks (see example below)
authManager.SetLoadCallback(LoadAuthRecords);
authManager.SetSaveCallback(SaveAuthRecords);
// Load existing users from storage
authManager.Load();
}
// Add a user with password
void AddPasswordUser(const char* username, const char* password) {
if (authManager.AddUser(username, password, AUTH_TYPE_PASSWORD)) {
printf("User %s added successfully\n", username);
authManager.Save(); // Persist to storage
} else {
printf("Failed to add user %s\n", username);
}
}
// Add a user with public key
void AddKeyUser(const char* username, const char* publicKey) {
if (authManager.AddUser(username, publicKey, AUTH_TYPE_KEY)) {
printf("User %s added with key authentication\n", username);
authManager.Save();
} else {
printf("Failed to add user %s\n", username);
}
}
// Verify user credentials
int AuthenticateUser(const char* username,
const char* authValue,
AuthType authType) {
return authManager.Authenticate(username, authValue, authType);
}
// Remove a user
void RemoveUser(const char* username) {
if (authManager.RemoveUser(username)) {
printf("User %s removed\n", username);
authManager.Save();
}
}

Storage Callbacks

Implement custom storage using callbacks:

#include <UserAuthManager.h>
#include <nbjson.h>
#include <UserParams.h>
// Callback type definitions
typedef int (*LoadAuthRecordsFn)(UserAuthManager& manager);
typedef int (*SaveAuthRecordsFn)(UserAuthManager& manager);
// Load users from User Param space (example)
int LoadAuthRecords(UserAuthManager& manager) {
// Read JSON from User Param space
const char* jsonData = GetUserParam("SSH_USERS");
if (!jsonData) return 0;
// Parse JSON and populate manager
if (ParseJsonData(jsonData, jsonSet)) {
// Extract users from JSON and add to manager
for (int i = 0; i < jsonSet.m_numDataElements; i++) {
const char* username = jsonSet.FindName("username", i);
const char* authData = jsonSet.FindName("auth", i);
const char* typeStr = jsonSet.FindName("type", i);
AuthType type = (strcmp(typeStr, "key") == 0) ?
AUTH_TYPE_KEY : AUTH_TYPE_PASSWORD;
manager.AddUserHashed(username, authData, type);
}
return 1;
}
return 0;
}
// Save users to User Param space (example)
int SaveAuthRecords(UserAuthManager& manager) {
// Build JSON from manager data
char jsonBuffer[4096];
int offset = 0;
offset += sprintf(jsonBuffer + offset, "{\"users\":[");
// Iterate through users and build JSON
// (Implementation depends on UserAuthManager API)
offset += sprintf(jsonBuffer + offset, "]}");
// Save to User Param space
return SetUserParam("SSH_USERS", jsonBuffer);
}
// Register callbacks
void InitializeAuthStorage() {
authManager.SetLoadCallback(LoadAuthRecords);
authManager.SetSaveCallback(SaveAuthRecords);
authManager.Load();
}
A class to create, read, and modify a JSON object.
Definition json_lexer.h:535

Integration with SSH Authentication

#include <ssh.h>
#include <UserAuthManager.h>
// Global manager
UserAuthManager g_authManager;
// Authentication callback for SSH
int SshAuthCallback(const char* username,
const char* authValue,
AuthType authType) {
// Use UserAuthManager to verify credentials
int result = g_authManager.Authenticate(username, authValue, authType);
if (result) {
printf("SSH: User '%s' authenticated successfully\n", username);
} else {
printf("SSH: Authentication failed for user '%s'\n", username);
}
return result;
}
void UserMain(void* pd) {
init();
// Initialize authentication manager
g_authManager.SetLoadCallback(LoadAuthRecords);
g_authManager.SetSaveCallback(SaveAuthRecords);
g_authManager.Load();
// Register SSH authentication callback
// Start SSH server
StartSsh();
printf("SSH server running with UserAuthManager\n");
while(1) { OSTimeDly(TICKS_PER_SECOND); }
}

Security Best Practices

┌───────────────────────────────────────────────────────┐
UserAuthManager Security Features │
├───────────────────────────────────────────────────────┤
│ │
│ [HASH] Passwords hashed before storage │
│ - Never store plain-text passwords │
│ - Secure hash algorithms used │
│ │
│ [SAFE] Constant-time comparison │
│ - Prevents timing attacks │
│ - Secure credential verification │
│ │
│ [FLEX] Pluggable storage │
│ - Choose your storage backend │
│ - Encrypt at rest if needed │
│ - Control access permissions │
│ │
│ [KEYS] Public key support │
│ - Store SSH public keys │
│ - Key-based authentication │
│ - More secure than passwords │
│ │
└───────────────────────────────────────────────────────┘

Recommendations:**

  1. Always hash passwords before storage
  2. Use secure storage (encrypted if possible)
  3. Implement proper access controls
  4. Prefer key-based authentication
  5. Regularly audit user accounts
  6. Implement account lockout policies
  7. Log authentication attempts

Configuration

SSH Server Configuration

Basic SSH server setup:

#include <init.h>
#include <ssh.h>
const char* AppName = "SSH Server Example";
void UserMain(void* pd) {
init();
// Wait for network
// Configure SSH (optional - uses defaults if not called)
// SshSetPort(22); // Default SSH port
// Start SSH server
if (StartSsh() == 0) {
printf("SSH server started successfully\n");
} else {
printf("Failed to start SSH server\n");
}
while(1) {
OSTimeDly(TICKS_PER_SECOND);
}
}
bool WaitForActiveNetwork(uint32_t ticks_to_wait=120 *TICKS_PER_SECOND, int interface=-1)
Wait for an active network connection on at least one interface.

SSH Client Configuration

Connect to remote SSH servers:

#include <ssh.h>
void ConnectToRemoteServer() {
SshClientConfig config;
// Configure connection
config.hostname = "remote.server.com";
config.port = 22;
config.username = "admin";
config.password = "secret"; // Or use key authentication
// Connect
SSH_CLIENT* client = SshClientConnect(&config);
if (client) {
printf("Connected to remote SSH server\n");
// Execute command
char response[1024];
int result = SshClientExecute(client, "ls -la",
response, sizeof(response));
if (result > 0) {
printf("Command output:\n%s\n", response);
}
// Disconnect
SshClientDisconnect(client);
} else {
printf("Failed to connect to SSH server\n");
}
}

Advanced Configuration

#include <ssh.h>
void ConfigureSshAdvanced() {
// Set custom port
SshSetPort(2222);
// Set connection timeout
SshSetTimeout(30); // 30 seconds
// Set maximum connections
SshSetMaxConnections(5);
// Enable/disable features
SshEnablePasswordAuth(true);
SshEnableKeyAuth(true);
// Set custom banner
SshSetBanner("Welcome to NetBurner SSH Server\n");
// Configure cipher suites (if needed)
// SshSetCipherList("aes128-ctr,aes256-ctr");
// Start server with custom configuration
StartSsh();
}

Configuration Constants

Common SSH configuration constants:

// Default values
#define SSH_DEFAULT_PORT 22
#define SSH_DEFAULT_TIMEOUT 60 // seconds
#define SSH_DEFAULT_MAX_CONNECTIONS 10
#define SSH_DEFAULT_KEY_SIZE 2048 // RSA
// Task priority (defined in constants.h)
#define SECURITY_TASK_PRIO 45 // CryptoServer priority
// Buffer sizes
#define SSH_MAX_USERNAME_LEN 32
#define SSH_MAX_PASSWORD_LEN 64
#define SSH_MAX_COMMAND_LEN 1024
#define SSH_MAX_RESPONSE_LEN 4096

Examples and Best Practices

Example 1: Basic SSH Server

#include <init.h>
#include <ssh.h>
#include <UserAuthManager.h>
const char* AppName = "Basic SSH Server";
UserAuthManager authManager;
// Simple authentication callback
int AuthUser(const char* username, const char* authValue, AuthType type) {
return authManager.Authenticate(username, authValue, type);
}
// Initialize with default admin user
void InitDefaultUsers() {
authManager.AddUser("admin", "admin123", AUTH_TYPE_PASSWORD);
printf("Default user 'admin' created\n");
}
void UserMain(void* pd) {
init();
printf("Starting %s\n", AppName);
// Wait for network
// Setup authentication
InitDefaultUsers();
// Start SSH server (auto-generated keys)
StartSsh();
printf("SSH server running on port 22\n");
printf("Connect with: ssh admin@%s\n", GetIP().c_str());
while(1) {
OSTimeDly(TICKS_PER_SECOND);
}
}

Example 2: SSH Server with Persistent Users

#include <init.h>
#include <ssh.h>
#include <UserAuthManager.h>
#include <UserParams.h>
#include <nbjson.h>
UserAuthManager authManager;
// Load users from User Param space
int LoadUsers(UserAuthManager& mgr) {
const char* jsonData = GetUserParam("SSH_USERS");
if (!jsonData) return 0;
if (ParseJsonData(jsonData, jsonSet)) {
for (int i = 0; i < jsonSet.m_numDataElements; i++) {
const char* user = jsonSet.FindName("user", i);
const char* hash = jsonSet.FindName("hash", i);
const char* type = jsonSet.FindName("type", i);
AuthType authType = (strcmp(type, "key") == 0) ?
AUTH_TYPE_KEY : AUTH_TYPE_PASSWORD;
mgr.AddUserHashed(user, hash, authType);
}
return 1;
}
return 0;
}
// Save users to User Param space
int SaveUsers(UserAuthManager& mgr) {
char json[2048];
// Build JSON from manager
// ... implementation ...
return SetUserParam("SSH_USERS", json);
}
int AuthUser(const char* username, const char* authValue, AuthType type) {
int result = authManager.Authenticate(username, authValue, type);
// Log authentication attempts
if (result) {
printf("[SSH] Login success: %s\n", username);
} else {
printf("[SSH] Login failed: %s\n", username);
}
return result;
}
void UserMain(void* pd) {
init();
// Setup persistent user storage
authManager.SetLoadCallback(LoadUsers);
authManager.SetSaveCallback(SaveUsers);
authManager.Load();
// Create default user if none exist
if (authManager.GetUserCount() == 0) {
authManager.AddUser("admin", "netburner", AUTH_TYPE_PASSWORD);
authManager.Save();
printf("Created default admin user\n");
}
// Configure and start SSH
StartSsh();
printf("SSH server ready - %d users loaded\n",
authManager.GetUserCount());
while(1) { OSTimeDly(TICKS_PER_SECOND); }
}

Example 3: SSH Client

#include <init.h>
#include <ssh.h>
void ExecuteRemoteCommand(const char* host, const char* command) {
SshClientConfig config;
config.hostname = host;
config.port = 22;
config.username = "admin";
config.password = "password";
SSH_CLIENT* client = SshClientConnect(&config);
if (client) {
char response[4096];
int len = SshClientExecute(client, command,
response, sizeof(response));
if (len > 0) {
printf("Response from %s:\n%s\n", host, response);
} else {
printf("Command execution failed\n");
}
SshClientDisconnect(client);
}
}
void UserMain(void* pd) {
init();
// Execute commands on remote servers
ExecuteRemoteCommand("192.168.1.100", "uptime");
ExecuteRemoteCommand("192.168.1.101", "df -h");
while(1) { OSTimeDly(TICKS_PER_SECOND); }
}

Example 4: Custom Key Management

#include <init.h>
#include <ssh.h>
// Store multiple keys for different purposes
const char* productionKey =
"-----BEGIN EC PRIVATE KEY-----\n"
"MHcCAQEE...production key...\n"
"-----END EC PRIVATE KEY-----\n";
const char* developmentKey =
"-----BEGIN EC PRIVATE KEY-----\n"
"MHcCAQEE...development key...\n"
"-----END EC PRIVATE KEY-----\n";
// Dynamic key selection callback
int GetSshKey(const char** keyData, int* keyLen, int* keyFormat) {
// Select key based on environment
#ifdef PRODUCTION
*keyData = productionKey;
*keyLen = strlen(productionKey);
#else
*keyData = developmentKey;
*keyLen = strlen(developmentKey);
#endif
*keyFormat = SSH_KEY_FORMAT_PEM;
return 1;
}
void UserMain(void* pd) {
init();
// Set custom key callback
SshSetUserGetKey(GetSshKey);
// Start SSH server with selected key
StartSsh();
#ifdef PRODUCTION
printf("SSH: Using production key\n");
#else
printf("SSH: Using development key\n");
#endif
while(1) { OSTimeDly(TICKS_PER_SECOND); }
}

Best Practices

┌────────────────────────────────────────────────────────┐
│ SSH Security Best Practices │
├────────────────────────────────────────────────────────┤
│ │
│ [1] Key Management │
│ - Use ECC keys when possible (smaller, faster) │
│ - Rotate keys regularly │
│ - Never hardcode keys in source code │
│ - Store keys securely │
│ │
│ [2] Authentication │
│ - Prefer key-based over password authentication │
│ - Use strong passwords (12+ characters) │
│ - Implement account lockout after failed │
│ attempts │
│ - Use UserAuthManager for credential storage │
│ │
│ [3] Network Security │
│ - Use non-standard ports when possible │
│ - Implement IP whitelisting/blacklisting │
│ - Set appropriate connection timeouts │
│ - Limit maximum concurrent connections │
│ │
│ [4] Monitoring │
│ - Log all authentication attempts │
│ - Monitor for suspicious activity │
│ - Track active sessions │
│ - Alert on anomalies │
│ │
│ [5] Updates │
│ - Keep NNDK updated for security patches │
│ - Review wolfSSH release notes │
│ - Test updates in development first │
│ │
└────────────────────────────────────────────────────────┘

Quick Reference

Essential Functions

Server Functions

// Start/Stop SSH server
int StartSsh(void);
int StopSsh(void);
// Configuration
void SshSetPort(uint16_t port);
void SshSetTimeout(uint32_t seconds);
void SshSetMaxConnections(int max);
// Authentication
// Key management
void SshSetUserGetKey(SshUserGetKeyFn fn);
// Legacy (deprecated but still available)

Client Functions

// Connection
SSH_CLIENT* SshClientConnect(SshClientConfig* config);
void SshClientDisconnect(SSH_CLIENT* client);
// Command execution
int SshClientExecute(SSH_CLIENT* client, const char* command,
char* response, int maxLen);
// File transfer
int SshClientUpload(SSH_CLIENT* client, const char* localPath,
const char* remotePath);
int SshClientDownload(SSH_CLIENT* client, const char* remotePath,
const char* localPath);

UserAuthManager Functions

// User management
bool AddUser(const char* username, const char* authValue, AuthType type);
bool RemoveUser(const char* username);
bool UserExists(const char* username);
int GetUserCount(void);
// Authentication
int Authenticate(const char* username, const char* authValue,
AuthType type);
// Storage
void SetLoadCallback(LoadAuthRecordsFn fn);
void SetSaveCallback(SaveAuthRecordsFn fn);
bool Load(void);
bool Save(void);

Common Patterns

Pattern 1: Simple Server

void UserMain(void* pd) {
init();
StartSsh(); // Auto-generated key, default settings
while(1) { OSTimeDly(TICKS_PER_SECOND); }
}

Pattern 2: Server with Authentication

int AuthCallback(const char* user, const char* auth, AuthType type) {
return authManager.Authenticate(user, auth, type);
}
void UserMain(void* pd) {
init();
authManager.Load();
StartSsh();
while(1) { OSTimeDly(TICKS_PER_SECOND); }
}

Pattern 3: Client Connection

void ConnectAndExecute(const char* host, const char* cmd) {
SshClientConfig cfg = {host, 22, "admin", "password"};
SSH_CLIENT* client = SshClientConnect(&cfg);
if (client) {
char buf[1024];
SshClientExecute(client, cmd, buf, sizeof(buf));
printf("%s\n", buf);
SshClientDisconnect(client);
}
}

Troubleshooting

┌─────────────────────────────────────────────────────┐
│ Common SSH Issues & Solutions │
├─────────────────────────────────────────────────────┤
│ │
│ Connection Refused │
│ ├─ Check SSH server is started │
│ ├─ Verify port is not blocked by firewall │
│ └─ Confirm device has network connectivity │
│ │
│ Authentication Failed │
│ ├─ Verify credentials are correct │
│ ├─ Check authentication callback is registered │
│ ├─ Review UserAuthManager configuration │
│ └─ Check auth logs for details │
│ │
│ Key Not Found │
│ ├─ Verify key callback is set (if using) │
│ ├─ Check GetPrivateKeyPEM() override │
│ ├─ Ensure auto-generation is enabled │
│ └─ Review key priority order │
│ │
│ Build Errors │
│ ├─ Add required include paths: │
│ │ ${NNDK_ROOT}/libraries/include/ssh │
│ │ ${NNDK_ROOT}/libraries/include/crypto │
│ ├─ Clean and rebuild project │
│ └─ Check for deprecated function usage │
│ │
│ Memory Issues │
│ ├─ CryptoServer task runs at SECURITY_TASK_PRIO
│ ├─ Check available heap space │
│ ├─ Limit concurrent SSH connections │
│ └─ Review cipher suite configuration │
│ │
└─────────────────────────────────────────────────────┘

Debug Logging

Enable SSH debug output:

#include <ssh.h>
void UserMain(void* pd) {
init();
// Enable SSH debug logging
SshSetDebugLevel(SSH_DEBUG_VERBOSE);
// Levels:
// SSH_DEBUG_NONE - No debug output
// SSH_DEBUG_ERROR - Errors only
// SSH_DEBUG_WARN - Warnings and errors
// SSH_DEBUG_INFO - Info, warnings, and errors
// SSH_DEBUG_VERBOSE - All debug output
StartSsh();
while(1) { OSTimeDly(TICKS_PER_SECOND); }
}

Migration Checklist

When migrating from NNDK 3.3.5 or earlier:

Migration Steps:
[ ] Add include paths to project:
${NNDK_ROOT}/libraries/include/ssh
${NNDK_ROOT}/libraries/include/crypto
[ ] Remove deprecated functions:
[ ] SshConvertDecodedOpenSSLKey()
[ ] SshSetchansessionrequest()
[ ] SshSetTaskPriority()
[ ] Update authentication code:
[ ] Replace sshUserAuthenticateFn with
[ ] Replace SshSetUserAuthenticate() with
[ ] Update callback to handle AuthType parameter
[ ] Test key management:
[ ] Verify key installation method works
[ ] Test auto-generated keys if used
[ ] Confirm key format compatibility
[ ] Validate functionality:
[ ] Test SSH server connections
[ ] Verify authentication works
[ ] Test SSH client if used
[ ] Check memory usage
[ ] Review security:
[ ] Update cipher suites if needed
[ ] Review authentication policies
[ ] Test key rotation procedures
[ ] Verify logging and monitoring

Summary

This guide has covered:

  • wolfSSH migration and benefits
  • Supported key types and formats
  • Migration from NNDK 3.3.5 to 3.3.6+
  • Key management strategies
  • User authentication with UserAuthManager
  • Configuration and examples
  • Best practices and troubleshooting

Key Takeaways

┌──────────────────────────────────────────────────────┐
│ SSH Implementation Summary │
├──────────────────────────────────────────────────────┤
│ │
│ [MODERN] wolfSSH replaces Dropbear │
│ [CLIENT] SSH client support now available │
│ [SECURE] Enhanced ciphers and key management │
│ [AUTO] Automatic key generation supported │
│ [SHARED] wolfCrypt shared with SSL/TLS │
│ [MANAGED] UserAuthManager simplifies auth │
│ [FLEXIBLE] Multiple key installation methods │
│ [UPDATED] New authentication API available │
│ │
└──────────────────────────────────────────────────────┘

Recommended Approach:**

  1. Use ECC keys for embedded systems
  2. Implement key-based authentication
  3. Use UserAuthManager for credentials
  4. Enable auto-regeneration in production
  5. Monitor authentication attempts
  6. Keep NNDK updated

For the latest information and examples, refer to: