Validator public key is not checked
Description
To unlock funds from the bridge, a number of signatures by validators need to be submitted. On the Mina side, validation of the validator's public keys is performed by the validateValidator
function, which is implemented as follows:
public async validateValidator(
useSig1: Bool,
validator1: PublicKey,
useSig2: Bool,
validator2: PublicKey,
useSig3: Bool,
validator3: PublicKey,
) {
let count = UInt64.from(0);
const zero = Field.from(0);
const falseB = Bool(false);
const trueB = Bool(true);
const validatorManager = new ValidatorManager(this.validatorManager.getAndRequireEquals());
const validateIndex = async (validator: PublicKey, useSig: Bool) => {
const index = await validatorManager.getValidatorIndex(validator);
const isGreaterThanZero = index.greaterThan(zero);
let isOk = Provable.if(useSig, Provable.if(isGreaterThanZero, trueB, falseB), trueB);
isOk.assertTrue("Public key not found in validators");
};
const notDupValidator12 = Provable.if(useSig1.and(useSig2), Provable.if(validator1.equals(validator2), falseB, trueB), trueB);
const notDupValidator13 = Provable.if(useSig1.and(useSig3), Provable.if(validator1.equals(validator3), falseB, trueB),trueB);
const notDupValidator23 = Provable.if(useSig2.and(useSig3), Provable.if(validator2.equals(validator3), falseB, trueB), trueB);
const isDuplicate = Provable.if(
notDupValidator12.and(notDupValidator13).and(notDupValidator23),
falseB,
trueB,
);
isDuplicate.assertFalse("Duplicate validator keys");
count = Provable.if(useSig1, count.add(1), count);
count = Provable.if(useSig2, count.add(1), count);
count = Provable.if(useSig3, count.add(1), count);
count.assertGreaterThanOrEqual(this.threshold.getAndRequireEquals(), "Not reached threshold");
}
The second half of this function performs two checks: it checks that the validators whose signatures are used are all different, and it checks that the total number of used signatures is high enough.
The validateValidator
function also defines a function validateIndex
, which uses the ValidatorManager contract to check that the public keys are indeed the public keys of approved validators. However, validateIndex
is never called on the three public keys. This check is hence not performed by validateValidator
.
Impact
Any key may be used to sign unlocking messages. The additional security of using a quorum of validators for unlocking over fully trusting the minter is thus lost, as the minter can drain the contract by creating three different keys and signing the unlocking message with those three keys.
Recommendations
We recommend calling validateIndex
on the three pairs of the public key and Boolean.
Remediation
This issue has been acknowledged by Sotatek, and a fix was implemented in commit.