Skip to content

BinSub & Bits2Num vulnerability #136

@alexbabits

Description

@alexbabits

Preface

I checked GitHub repos that included both BinSub and Bits2Num in their circom files with GitHub's search: "BinSub" AND "Bits2Num" language:circom

I was surprised to find zero live projects affected. Because it appears no live projects are at risk, I'm publishing this bug report publicly.

Overview

BinSub template gracefully handles negative values obtained from subtraction by outputting it's binary out array in Twos Complement form.

For example, if we instantiate BinSub(4):

  • Perform 0 - 15 = -15. Where "in": [[0, 0, 0, 0], [1, 1, 1, 1]] and out = [1, 0, 0, 0] = 0001 = {-15, 1}
  • Perform 1 - 15 = -14. Where "in": [[1, 0, 0, 0], [1, 1, 1, 1]] and out = [0, 1, 0, 0] = 0010 = {-14, 2}
  • Perform 2 - 15 = -13. Where "in": [[0, 1, 0, 0], [1, 1, 1, 1]] and out = [1, 1, 0, 0] = 0011 = {-13, 3}
  • ...and so on

We see the out array represent the correct solution in Twos Complement, but also represents it's sister positive number.

Bits2Num template assumes incoming binary representations are always positive decimal numbers.

For example, if we instantiate Bits2Num(4):

  • 15 in binary = 1111 = "in": [1, 1, 1, 1]. Outputs out=15
  • 14 in binary = 1110 = "in": [0, 1, 1, 1]. Outputs out=14
  • 8 in binary = 1000 = "in": [0, 0, 0, 1]. Outputs out=8
  • ...and so on

We can see this is a problem anytime the developer wants a negative binary result from BinSub to be translated into it's negative decimal representation through Bits2Num.

Importantly, Bits2Num is silently outputting these incorrect positive numbers, rather than reverting.

Developer makes the incorrect natural assumption: "BinSub allows for negative outputs and handles stuff like 4-10 just fine. There is no negative-specific template for Bits2Num, so it'll handle all positive and negative results from BinSub fine too."

Notice there is already a Num2BitsNeg template which handles conversion of negative numbers into their binary representation. However, there is no Bits2NumNeg capable of handling Twos Complement negative binary values.

Tangential note: Num2BitsNeg seems to perform p - in, rather than outputting in Twos Complement? Not sure that's ideal/intended?

Proof of Concept

zkREPL

pragma circom 2.0.0;

template BinSub(n) {
    signal input in[2][n];
    signal output out[n];

    signal aux;

    var lin = 2**n;
    var lout = 0;

    var i;

    for (i=0; i<n; i++) {
        lin = lin + in[0][i]*(2**i);
        lin = lin - in[1][i]*(2**i);
    }

    for (i=0; i<n; i++) {
        out[i] <-- (lin >> i) & 1;

        // Ensure out is binary
        out[i] * (out[i] - 1) === 0;

        lout = lout + out[i]*(2**i);
    }

    aux <-- (lin >> n) & 1;
    aux*(aux-1) === 0;
    lout = lout + aux*(2**n);

    // Ensure the sum;
    lin === lout;
}


template Bits2Num(n) {
    signal input in[n];
    signal output out;
    var lc1=0;

    var e2 = 1;
    for (var i = 0; i<n; i++) {
        lc1 += in[i] * e2;
        e2 = e2 + e2;
    }

    lc1 ==> out;
}


template BadOutput(n) {
    signal input a[n]; // 6 in binary = 0110  = [0, 1, 1, 0]
    signal input b[n]; // 11 in binary = 1011 = [1, 1, 0, 1]
    signal output out;

    component binsub = BinSub(n); // 6 - 11 = -5
    binsub.in[0] <== a;
    binsub.in[1] <== b;

    component bits2num = Bits2Num(n);
    bits2num.in <== binsub.out; // -5 in twos complement binary

    out <== bits2num.out; // outputs 11 instead of -5
}

component main = BadOutput(4);

/* 
INPUT = {
    "a": [0, 1, 1, 0],
    "b": [1, 1, 0, 1]
}
*/

Recommended Fix

It probably doesn't make sense to stop BinSub from outputting negative results in Twos Complement.

You could add a bit flag attached to the output of BinSub. If b > a (negative result) then flag is 1, else flag is 0.

Then handle this flag accordingly in Bits2Num, allowing it to handle Twos Complement negative representations and normal positive representations.

The flag's Boolean state represents whether or not a binary array is a negative Twos Complement or not.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions