Skip to content

Commit

Permalink
build-logic: Support m out of n signature verification
Browse files Browse the repository at this point in the history
In Electrum all developers sign the binaries, but in Bitcoin Core a
subset of its developers sign the binaries. Our existing signature
verification code fails if not all developers have signed the
binaries.

This change enforces that all provided signatures are signed by its
developers, i.e., if we have 20 hard-coded public keys and a binary
has 10 signatures we ensure that the public keys of those 10 signatures
belong to the hard-coded developers and all signatures are valid.
  • Loading branch information
alvasw committed Jul 22, 2024
1 parent 8b32afd commit 0966349
Showing 1 changed file with 15 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,27 @@ class SignatureVerifier(
): Boolean {
Security.addProvider(BouncyCastleProvider())

var isSuccess = true
val signatureFileInBytes = readSignatureFromFile(signatureFile)
val pgpObjectFactory = JcaPGPObjectFactory(signatureFileInBytes)
val signatureList: PGPSignatureList = pgpObjectFactory.nextObject() as PGPSignatureList

val signatureVerificationResult = mutableListOf<Boolean>()
pgpFingerprintToKeyUrl.forEach { (fingerprint, keyUrl) ->
val ppgPublicKeyParser = PpgPublicKeyParser(fingerprint, keyUrl)
ppgPublicKeyParser.parse()

val isSignedByAnyKey = verifyDetachedSignature(
potentialSigningKeys = ppgPublicKeyParser.keyById,
pgpSignatureByteArray = readSignatureFromFile(signatureFile),
signatureList = signatureList,
data = fileToVerify.readBytes()
)

isSuccess = isSuccess && isSignedByAnyKey
signatureVerificationResult.add(isSignedByAnyKey)
}

return isSuccess
val numberOfSuccessfulVerifications = signatureVerificationResult.filter { isSuccess -> isSuccess }
.count()
return numberOfSuccessfulVerifications == signatureList.size()
}

private fun readSignatureFromFile(signatureFile: File): ByteArray {
Expand All @@ -48,12 +54,9 @@ class SignatureVerifier(

private fun verifyDetachedSignature(
potentialSigningKeys: Map<Long, PGPPublicKey>,
pgpSignatureByteArray: ByteArray,
signatureList: PGPSignatureList,
data: ByteArray
): Boolean {
val pgpObjectFactory = JcaPGPObjectFactory(pgpSignatureByteArray)
val signatureList: PGPSignatureList = pgpObjectFactory.nextObject() as PGPSignatureList

var pgpSignature: PGPSignature? = null
var signingKey: PGPPublicKey? = null

Expand All @@ -65,7 +68,10 @@ class SignatureVerifier(
}
}

checkNotNull(signingKey) { "signingKey not found" }
if (signingKey == null) {
return false
}

checkNotNull(pgpSignature) { "signature for key not found" }

pgpSignature.init(
Expand Down

0 comments on commit 0966349

Please sign in to comment.