Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GOLD-211: ECDSA Multi-sig feature #258

Merged
merged 3 commits into from
Aug 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions src/config/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ const SERVER_CONFIG: StrictServerConfiguration = {
extraNodesToAddInRestart: 5,
secondsToCheckForQ1: 1000, // 1 seconds in ms
hardenNewSyncingProtocol: true,
removeLostSyncingNodeFromList: true, //set to true since 1.10
removeLostSyncingNodeFromList: true, //set to true since 1.10
compareCertBinary: true,
makeReceiptBinary: true,
lostArchiverInvestigateBinary: true,
Expand Down Expand Up @@ -173,7 +173,7 @@ const SERVER_CONFIG: StrictServerConfiguration = {
requestReceiptForTxBinary: true,
lostReportBinary: true,
repairMissingAccountsBinary: true,
poqoSendReceiptBinary : true,
poqoSendReceiptBinary: true,
poqoDataAndReceiptBinary: true,
poqoSendVoteBinary: true,
rotationEdgeToAvoid: 3,
Expand All @@ -185,7 +185,7 @@ const SERVER_CONFIG: StrictServerConfiguration = {
useFactCorrespondingTell: true,
resubmitStandbyAddWaitDuration: 1000, // 1 second in ms
requiredVotesPercentage: 2 / 3.0,
timestampCacheFix: true
timestampCacheFix: true,
},
ip: {
externalIp: '0.0.0.0',
Expand Down Expand Up @@ -217,6 +217,7 @@ const SERVER_CONFIG: StrictServerConfiguration = {
countEndpointStop: -1,
hashedDevAuth: '',
devPublicKeys: {},
multisigKeys: {},
debugNoTxVoting: false,
ignoreRecieptChance: 0,
ignoreVoteChance: 0,
Expand All @@ -228,7 +229,8 @@ const SERVER_CONFIG: StrictServerConfiguration = {
oldPartitionSystem: false,
dumpAccountReportFromSQL: false,
profiler: false,
minApprovalsMultiAuth: 3,
minMultiSigRequiredForEndpoints: 2,
minMultiSigRequiredForGlobalTxs: 2,
robustQueryDebug: false,
forwardTXToSyncingNeighbors: false,
recordAcceptedTx: false,
Expand Down Expand Up @@ -320,7 +322,7 @@ const SERVER_CONFIG: StrictServerConfiguration = {
voterPercentage: 0.1,
waitUpstreamTx: false,
gossipCompleteData: false,
shareCompleteData: false, //turn off the neighbor sharing of complete data.
shareCompleteData: false, //turn off the neighbor sharing of complete data.
txStateMachineChanges: true,
canRequestFinalData: true,
numberOfReInjectNodes: 5,
Expand All @@ -342,7 +344,7 @@ const SERVER_CONFIG: StrictServerConfiguration = {
stuckTxMoveTime: 60000,
forceVoteForFailedPreApply: true,
collectedDataFix: true,
noRepairIfDataAttached: false, // this seems to be an optimization that we will leave off for now
noRepairIfDataAttached: false, // this seems to be an optimization that we will leave off for now
rejectSharedDataIfCovered: false,
requestAwaitedDataAllowed: false, // this has been disabled by accident for a long time so leaving it off
awaitingDataCanBailOnReceipt: true,
Expand Down
19 changes: 17 additions & 2 deletions src/debug/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ export function isServiceMode(): boolean {
}

export function getHashedDevKey(): string {
return config?.debug?.hashedDevAuth || '';
return config?.debug?.hashedDevAuth || ''
}

export function getDevPublicKeys(): DebugConfigurations['devPublicKeys'] {
return config?.debug?.devPublicKeys || {};
return config?.debug?.devPublicKeys || {}
}

export function ensureKeySecurity(pubKey: string, level: DevSecurityLevel): boolean {
Expand Down Expand Up @@ -66,3 +66,18 @@ export function getDevPublicKeyMaxLevel(clearance?: DevSecurityLevel): string |
}
return maxKey
}

export function getMultisigPublicKeys(): DebugConfigurations['multisigKeys'] {
return config?.debug?.multisigKeys || {}
}

export function getMultisigPublicKey(key: string): string | null {
// eslint-disable-next-line security/detect-object-injection
return getMultisigPublicKeys()[key] !== undefined ? key : null
}

export function ensureMultisigKeySecurity(pubKey: string, level: DevSecurityLevel): boolean {
// eslint-disable-next-line security/detect-object-injection
const pkClearance = getMultisigPublicKeys()[pubKey]
return pkClearance !== undefined && pkClearance >= level
}
81 changes: 22 additions & 59 deletions src/network/debugMiddleware.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { isDebugMode, getDevPublicKeys, ensureKeySecurity } from '../debug'
import { isDebugMode, getDevPublicKeys, ensureKeySecurity, getMultisigPublicKeys } from '../debug'
import * as Context from '../p2p/Context'
import * as crypto from '@shardus/crypto-utils'
import { DevSecurityLevel } from '../shardus/shardus-types'
Expand Down Expand Up @@ -103,11 +103,12 @@ function handleDebugAuth(_req, res, next, authLevel) {
})
}

function handleMultiDebugAuth(_req, res, next, authLevel: DevSecurityLevel) {
function handleDebugMultiSigAuth(_req, res, next, authLevel: DevSecurityLevel) {
nestedCountersInstance.countEvent('middleware', 'debug_multi_sig_auth')
try {
//auth with a signature
if (_req.query.sig != null && _req.query.sig_counter != null) {
const devPublicKeys = getDevPublicKeys() // This should return list of public keys
const devPublicKeys = getMultisigPublicKeys()

let parsedSignatures = Utils.safeJsonParse(_req.query.sig)

Expand Down Expand Up @@ -144,11 +145,7 @@ function handleMultiDebugAuth(_req, res, next, authLevel: DevSecurityLevel) {
// Remove duplicates from parsedSignatures
parsedSignatures = Array.from(new Set(parsedSignatures))

// Verify the signatures against the proposal
let allSignaturesValid = false
let signatureValid = false

const minApprovals = Math.max(2, SERVER_CONFIG.debug.minApprovalsMultiAuth)
const minApprovals = Math.max(1, SERVER_CONFIG.debug.minMultiSigRequiredForEndpoints)

if (parsedSignatures.length < minApprovals) {
return res.status(400).json({
Expand All @@ -157,65 +154,29 @@ function handleMultiDebugAuth(_req, res, next, authLevel: DevSecurityLevel) {
})
}

// when the debug call is signed it include the hash of baseurl and counter to prevent replay at later time and replay at different node or the same endpoint different query params
let payload: any = {
// when the debug call is signed it include the counter to prevent replay at later time and replay at different node or the same endpoint different query params
const payload: any = {
route: stripQueryParams(_req.originalUrl, ['sig', 'sig_counter', 'nodeIds']),
nodes: _req.query.nodeIds, // example 8f35,8b3a,85f1
count: _req.query.sig_counter,
networkId: CycleChain.newest.networkId,
}

payload = {
...payload,
requestHash: crypto.hash(Utils.safeStringify(payload)),
} as any

// Require a larger counter than before. This prevents replay attacks
if (parseInt(_req.query.sig_counter) > multiSigLstCounter && parsedSignatures.length >= minApprovals) {
let validSignaturesCount = 0
const seen = new Set()
for (let i = 0; i < parsedSignatures.length; i++) {
if (seen.has(parsedSignatures[i])) {
break
}
// Check each signature against all public keys
seen.add(parsedSignatures[i])
for (const publicKey of Object.keys(devPublicKeys)) {
payload = {
...payload,
sign: {
owner: publicKey,
sig: parsedSignatures[i],
},
} as SignedObject
signatureValid = crypto.verify(payload, parsedSignatures[i], publicKey)
if (signatureValid) {
const clearanceLevels = { low: 1, medium: 2, high: 3 } // Enum for security levels
const authorized = ensureKeySecurity(publicKey, authLevel) // Check if the approver is authorized to access the endpoint
if (authorized) {
validSignaturesCount++ // Increment only if signature is valid and authorized
break // Break if a valid and authorized signature is found
} else {
return res.status(401).json({
status: 401,
message: 'Unauthorized!',
})
}
}
}
if (!signatureValid) {
allSignaturesValid = false
console.log(`Invalid signature : ${parsedSignatures[i]}`)
break // Break the loop if an invalid signature is found
}
}
// Set allSignaturesValid to true only if all signatures are valid and authorized
allSignaturesValid = validSignaturesCount >= minApprovals
const signaturesValid = Context.stateManager.app.verifyMultiSigs(
payload,
parsedSignatures,
devPublicKeys,
minApprovals,
authLevel
)

// If all signatures are valid, proceed with the next middleware
if (allSignaturesValid) {
if (signaturesValid) {
multiSigLstCounter = parseInt(_req.query.sig_counter)
next()
return
} else {
return res.status(401).json({
status: 401,
Expand All @@ -227,8 +188,10 @@ function handleMultiDebugAuth(_req, res, next, authLevel: DevSecurityLevel) {
}
}
} catch (error) {
if (logFlags.verbose && logFlags.console) console.log('Error in handleDebugMultiSigAuth:', error)
console.log(error)
}
nestedCountersInstance.countEvent('middleware', 'debug_multi_sig_auth failure')
return res.status(403).json({
status: 403,
message: 'FORBIDDEN. Endpoint is only available in debug mode in addtion to signature verification.',
Expand Down Expand Up @@ -295,27 +258,27 @@ export const isDebugModeMiddlewareHigh = (_req, res, next) => {
export const isDebugModeMiddlewareMultiSig = (_req, res, next) => {
const isDebug = isDebugMode()
if (!isDebug) {
handleMultiDebugAuth(_req, res, next, DevSecurityLevel.High)
handleDebugMultiSigAuth(_req, res, next, DevSecurityLevel.High)
} else next()
}

export const isDebugModeMiddlewareMultiSigHigh = (_req, res, next) => {
const isDebug = isDebugMode()
if (!isDebug) {
handleMultiDebugAuth(_req, res, next, DevSecurityLevel.High)
handleDebugMultiSigAuth(_req, res, next, DevSecurityLevel.High)
} else next()
}

export const isDebugModeMiddlewareMultiSigMedium = (_req, res, next) => {
const isDebug = isDebugMode()
if (!isDebug) {
handleMultiDebugAuth(_req, res, next, DevSecurityLevel.Medium)
handleDebugMultiSigAuth(_req, res, next, DevSecurityLevel.Medium)
} else next()
}

export const isDebugModeMiddlewareMultiSigLow = (_req, res, next) => {
const isDebug = isDebugMode()
if (!isDebug) {
handleMultiDebugAuth(_req, res, next, DevSecurityLevel.Low)
handleDebugMultiSigAuth(_req, res, next, DevSecurityLevel.Low)
} else next()
}
Loading
Loading