Aere Network public source. Everything here can be checked against the live chain (chain id 2800, https://rpc.aere.network). Scope note, stated up front rather than buried: consensus on chain 2800 is classical secp256k1 ECDSA QBFT. The post-quantum work in this repository is at the signature, precompile, account and transport layers. Nothing here makes the consensus post-quantum, and no document in it should be read as claiming so.
342 lines
12 KiB
Solidity
342 lines
12 KiB
Solidity
|
|
// SPDX-License-Identifier: MIT
|
|
pragma solidity ^0.8.19;
|
|
|
|
import "@openzeppelin/contracts/access/Ownable.sol";
|
|
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
|
|
import "@openzeppelin/contracts/security/Pausable.sol";
|
|
|
|
/**
|
|
* @title AERE Security Contract
|
|
* @dev Manages security monitoring and threat detection with enhanced security
|
|
*/
|
|
contract AereSecurity is Ownable, ReentrancyGuard, Pausable {
|
|
|
|
enum ThreatLevel { Low, Medium, High, Critical }
|
|
enum AlertStatus { Active, Resolved, Investigating }
|
|
|
|
struct SecurityAlert {
|
|
uint256 id;
|
|
string alertType;
|
|
ThreatLevel severity;
|
|
string message;
|
|
address reporter;
|
|
uint256 timestamp;
|
|
AlertStatus status;
|
|
string resolution;
|
|
}
|
|
|
|
struct SecurityMetrics {
|
|
uint256 totalAlerts;
|
|
uint256 resolvedAlerts;
|
|
uint256 criticalAlerts;
|
|
uint256 lastScanTime;
|
|
bool systemHealthy;
|
|
}
|
|
|
|
// State variables
|
|
mapping(uint256 => SecurityAlert) public alerts;
|
|
mapping(address => bool) public authorizedReporters;
|
|
mapping(address => uint256) public reputationScores;
|
|
mapping(address => uint256) public reportCounts;
|
|
mapping(address => uint256) public lastReportTime;
|
|
|
|
uint256 public alertCount = 0;
|
|
SecurityMetrics public securityMetrics;
|
|
|
|
// Security parameters
|
|
uint256 public constant RATE_LIMIT_WINDOW = 1 hours;
|
|
uint256 public constant MAX_REPORTS_PER_WINDOW = 10;
|
|
uint256 public constant MIN_REPUTATION_TO_REPORT = 50;
|
|
|
|
// Events
|
|
event SecurityAlertCreated(uint256 indexed alertId, ThreatLevel severity, string alertType, address indexed reporter);
|
|
event AlertResolved(uint256 indexed alertId, string resolution, address indexed resolver);
|
|
event ThreatDetected(address indexed source, string threatType, ThreatLevel severity, address indexed detector);
|
|
event ReputationUpdated(address indexed entity, uint256 oldScore, uint256 newScore);
|
|
event ReporterAuthorized(address indexed reporter, address indexed authorizer);
|
|
event ReporterRevoked(address indexed reporter, address indexed revoker);
|
|
|
|
modifier onlyAuthorizedReporter() {
|
|
require(authorizedReporters[msg.sender], "AereSecurity: Not authorized to report");
|
|
require(reputationScores[msg.sender] >= MIN_REPUTATION_TO_REPORT, "AereSecurity: Insufficient reputation");
|
|
_;
|
|
}
|
|
|
|
modifier rateLimited() {
|
|
require(
|
|
reportCounts[msg.sender] < MAX_REPORTS_PER_WINDOW ||
|
|
block.timestamp > lastReportTime[msg.sender] + RATE_LIMIT_WINDOW,
|
|
"AereSecurity: Rate limit exceeded"
|
|
);
|
|
|
|
if (block.timestamp > lastReportTime[msg.sender] + RATE_LIMIT_WINDOW) {
|
|
reportCounts[msg.sender] = 0;
|
|
}
|
|
|
|
reportCounts[msg.sender]++;
|
|
lastReportTime[msg.sender] = block.timestamp;
|
|
_;
|
|
}
|
|
|
|
constructor() {
|
|
securityMetrics = SecurityMetrics({
|
|
totalAlerts: 0,
|
|
resolvedAlerts: 0,
|
|
criticalAlerts: 0,
|
|
lastScanTime: block.timestamp,
|
|
systemHealthy: true
|
|
});
|
|
|
|
// Owner is authorized reporter by default with high reputation
|
|
authorizedReporters[msg.sender] = true;
|
|
reputationScores[msg.sender] = 100;
|
|
|
|
emit ReporterAuthorized(msg.sender, msg.sender);
|
|
emit ReputationUpdated(msg.sender, 0, 100);
|
|
}
|
|
|
|
/**
|
|
* @dev Add authorized security reporter with reputation check
|
|
*/
|
|
function addAuthorizedReporter(address reporter) external onlyOwner {
|
|
require(reporter != address(0), "AereSecurity: Invalid reporter address");
|
|
require(!authorizedReporters[reporter], "AereSecurity: Reporter already authorized");
|
|
|
|
authorizedReporters[reporter] = true;
|
|
if (reputationScores[reporter] == 0) {
|
|
reputationScores[reporter] = MIN_REPUTATION_TO_REPORT;
|
|
}
|
|
|
|
emit ReporterAuthorized(reporter, msg.sender);
|
|
}
|
|
|
|
/**
|
|
* @dev Remove authorized security reporter
|
|
*/
|
|
function removeAuthorizedReporter(address reporter) external onlyOwner {
|
|
require(reporter != msg.sender, "AereSecurity: Cannot remove self");
|
|
authorizedReporters[reporter] = false;
|
|
|
|
emit ReporterRevoked(reporter, msg.sender);
|
|
}
|
|
|
|
/**
|
|
* @dev Report security alert with enhanced validation
|
|
*/
|
|
function reportSecurityAlert(
|
|
string memory alertType,
|
|
ThreatLevel severity,
|
|
string memory message
|
|
) external onlyAuthorizedReporter rateLimited whenNotPaused nonReentrant returns (uint256) {
|
|
require(bytes(alertType).length > 0 && bytes(alertType).length <= 100, "AereSecurity: Invalid alert type");
|
|
require(bytes(message).length > 0 && bytes(message).length <= 1000, "AereSecurity: Invalid message");
|
|
|
|
alertCount++;
|
|
uint256 alertId = alertCount;
|
|
|
|
alerts[alertId] = SecurityAlert({
|
|
id: alertId,
|
|
alertType: alertType,
|
|
severity: severity,
|
|
message: message,
|
|
reporter: msg.sender,
|
|
timestamp: block.timestamp,
|
|
status: AlertStatus.Active,
|
|
resolution: ""
|
|
});
|
|
|
|
securityMetrics.totalAlerts++;
|
|
if (severity == ThreatLevel.Critical) {
|
|
securityMetrics.criticalAlerts++;
|
|
securityMetrics.systemHealthy = false;
|
|
}
|
|
|
|
// Update reporter reputation (positive for reporting)
|
|
_updateReputationSafe(msg.sender, reputationScores[msg.sender] + 1);
|
|
|
|
emit SecurityAlertCreated(alertId, severity, alertType, msg.sender);
|
|
return alertId;
|
|
}
|
|
|
|
/**
|
|
* @dev Resolve security alert with enhanced validation
|
|
*/
|
|
function resolveAlert(uint256 alertId, string memory resolution) external onlyOwner nonReentrant {
|
|
require(alertId > 0 && alertId <= alertCount, "AereSecurity: Alert does not exist");
|
|
require(alerts[alertId].status != AlertStatus.Resolved, "AereSecurity: Alert already resolved");
|
|
require(bytes(resolution).length > 0 && bytes(resolution).length <= 1000, "AereSecurity: Invalid resolution");
|
|
|
|
alerts[alertId].status = AlertStatus.Resolved;
|
|
alerts[alertId].resolution = resolution;
|
|
|
|
securityMetrics.resolvedAlerts++;
|
|
|
|
// Update system health if all critical alerts resolved
|
|
if (alerts[alertId].severity == ThreatLevel.Critical) {
|
|
securityMetrics.criticalAlerts = securityMetrics.criticalAlerts > 0 ? securityMetrics.criticalAlerts - 1 : 0;
|
|
if (securityMetrics.criticalAlerts == 0) {
|
|
securityMetrics.systemHealthy = true;
|
|
}
|
|
}
|
|
|
|
emit AlertResolved(alertId, resolution, msg.sender);
|
|
}
|
|
|
|
/**
|
|
* @dev Detect and report threat with enhanced validation
|
|
*/
|
|
function detectThreat(
|
|
address source,
|
|
string memory threatType,
|
|
ThreatLevel severity
|
|
) external onlyAuthorizedReporter rateLimited whenNotPaused nonReentrant {
|
|
require(source != address(0), "AereSecurity: Invalid source address");
|
|
require(bytes(threatType).length > 0 && bytes(threatType).length <= 100, "AereSecurity: Invalid threat type");
|
|
|
|
// Update reputation score for the source (negative impact)
|
|
if (reputationScores[source] > uint256(severity) + 1) {
|
|
_updateReputationSafe(source, reputationScores[source] - (uint256(severity) + 1));
|
|
} else {
|
|
_updateReputationSafe(source, 0);
|
|
}
|
|
|
|
// Create alert
|
|
string memory message = string(abi.encodePacked("Threat detected from ", _addressToString(source)));
|
|
this.reportSecurityAlert(threatType, severity, message);
|
|
|
|
emit ThreatDetected(source, threatType, severity, msg.sender);
|
|
}
|
|
|
|
/**
|
|
* @dev Update reputation score with bounds checking
|
|
*/
|
|
function updateReputation(address entity, uint256 score) external onlyOwner {
|
|
require(entity != address(0), "AereSecurity: Invalid entity address");
|
|
require(score <= 100, "AereSecurity: Score too high");
|
|
|
|
uint256 oldScore = reputationScores[entity];
|
|
_updateReputationSafe(entity, score);
|
|
|
|
emit ReputationUpdated(entity, oldScore, score);
|
|
}
|
|
|
|
/**
|
|
* @dev Internal safe reputation update
|
|
*/
|
|
function _updateReputationSafe(address entity, uint256 score) internal {
|
|
uint256 oldScore = reputationScores[entity];
|
|
reputationScores[entity] = score > 100 ? 100 : score;
|
|
|
|
if (oldScore != reputationScores[entity]) {
|
|
emit ReputationUpdated(entity, oldScore, reputationScores[entity]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @dev Perform security scan (only owner, rate limited)
|
|
*/
|
|
function performSecurityScan() external onlyOwner {
|
|
require(block.timestamp >= securityMetrics.lastScanTime + 1 hours, "AereSecurity: Scan rate limited");
|
|
|
|
securityMetrics.lastScanTime = block.timestamp;
|
|
securityMetrics.systemHealthy = (securityMetrics.criticalAlerts == 0);
|
|
}
|
|
|
|
/**
|
|
* @dev Emergency pause function
|
|
*/
|
|
function emergencyPause() external onlyOwner {
|
|
_pause();
|
|
}
|
|
|
|
/**
|
|
* @dev Unpause function
|
|
*/
|
|
function unpause() external onlyOwner {
|
|
_unpause();
|
|
}
|
|
|
|
/**
|
|
* @dev Get alert details with existence check
|
|
*/
|
|
function getAlert(uint256 alertId) external view returns (SecurityAlert memory) {
|
|
require(alertId > 0 && alertId <= alertCount, "AereSecurity: Alert does not exist");
|
|
return alerts[alertId];
|
|
}
|
|
|
|
/**
|
|
* @dev Get security metrics
|
|
*/
|
|
function getSecurityMetrics() external view returns (SecurityMetrics memory) {
|
|
return securityMetrics;
|
|
}
|
|
|
|
/**
|
|
* @dev Get reputation score
|
|
*/
|
|
function getReputationScore(address entity) external view returns (uint256) {
|
|
return reputationScores[entity];
|
|
}
|
|
|
|
/**
|
|
* @dev Check if system is healthy
|
|
*/
|
|
function isSystemHealthy() external view returns (bool) {
|
|
return securityMetrics.systemHealthy && securityMetrics.criticalAlerts == 0 && !paused();
|
|
}
|
|
|
|
/**
|
|
* @dev Get active alerts count by severity
|
|
*/
|
|
function getActiveAlertsBySeverity(ThreatLevel severity) external view returns (uint256) {
|
|
uint256 count = 0;
|
|
for (uint256 i = 1; i <= alertCount; i++) {
|
|
if (alerts[i].severity == severity && alerts[i].status == AlertStatus.Active) {
|
|
count++;
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
/**
|
|
* @dev Check if address is authorized reporter
|
|
*/
|
|
function isAuthorizedReporter(address reporter) external view returns (bool) {
|
|
return authorizedReporters[reporter] && reputationScores[reporter] >= MIN_REPUTATION_TO_REPORT;
|
|
}
|
|
|
|
/**
|
|
* @dev Get reporter statistics
|
|
*/
|
|
function getReporterStats(address reporter) external view returns (
|
|
bool authorized,
|
|
uint256 reputation,
|
|
uint256 reportsInWindow,
|
|
uint256 lastReport
|
|
) {
|
|
return (
|
|
authorizedReporters[reporter],
|
|
reputationScores[reporter],
|
|
reportCounts[reporter],
|
|
lastReportTime[reporter]
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @dev Convert address to string
|
|
*/
|
|
function _addressToString(address account) internal pure returns (string memory) {
|
|
bytes32 value = bytes32(uint256(uint160(account)));
|
|
bytes memory alphabet = "0123456789abcdef";
|
|
bytes memory str = new bytes(42);
|
|
str[0] = '0';
|
|
str[1] = 'x';
|
|
for (uint256 i = 0; i < 20; i++) {
|
|
str[2+i*2] = alphabet[uint8(value[i + 12] >> 4)];
|
|
str[3+i*2] = alphabet[uint8(value[i + 12] & 0x0f)];
|
|
}
|
|
return string(str);
|
|
}
|
|
}
|