Esempio n. 1
0
 def visitCircEncConstraint(self, stmt: CircEncConstraint):
     assert stmt.cipher.t.is_cipher()
     assert stmt.pk.t == TypeName.key_type()
     assert stmt.rnd.t == TypeName.rnd_type()
     if stmt.is_dec:
         return f'checkDec("{stmt.plain.name}", "{stmt.pk.name}", "{stmt.rnd.name}", "{stmt.cipher.name}");'
     else:
         return f'checkEnc("{stmt.plain.name}", "{stmt.pk.name}", "{stmt.rnd.name}", "{stmt.cipher.name}");'
Esempio n. 2
0
 def visitIfStatement(self, ast: IfStatement):
     b = ast.condition
     if not b.instanceof_data_type(TypeName.bool_type()):
         raise TypeMismatchException(TypeName.bool_type(),
                                     b.annotated_type.type_name, b)
     if ast.condition.annotated_type.is_private():
         expected = AnnotatedTypeName(TypeName.bool_type(),
                                      Expression.me_expr())
         if not b.instanceof(expected):
             raise TypeMismatchException(expected, b.annotated_type, b)
Esempio n. 3
0
 def handle_cast(self, expr: Expression, t: TypeName) -> AnnotatedTypeName:
     # because of the fake solidity check we already know that the cast is possible -> don't have to check if cast possible
     if expr.annotated_type.is_private():
         expected = AnnotatedTypeName(expr.annotated_type.type_name,
                                      Expression.me_expr())
         if not expr.instanceof(expected):
             raise TypeMismatchException(expected, expr.annotated_type,
                                         expr)
         return AnnotatedTypeName(t.clone(), Expression.me_expr())
     else:
         return AnnotatedTypeName(t.clone())
Esempio n. 4
0
 def implicitly_converted_to(expr: Expression, t: TypeName) -> Expression:
     assert expr.annotated_type.type_name.is_primitive_type()
     cast = PrimitiveCastExpr(t.clone(), expr, is_implicit=True).override(
         parent=expr.parent,
         statement=expr.statement,
         line=expr.line,
         column=expr.column)
     cast.elem_type.parent = cast
     expr.parent = cast
     cast.annotated_type = AnnotatedTypeName(
         t.clone(),
         expr.annotated_type.privacy_annotation.clone()).override(
             parent=cast)
     return cast
Esempio n. 5
0
 def _require_secret_key(self,
                         crypto_params: CryptoParams) -> HybridArgumentIdf:
     self._needed_secret_key[
         crypto_params] = None  # Add to _need_secret_key OrderedDict
     key_name = self.get_own_secret_key_name(crypto_params)
     return HybridArgumentIdf(key_name, TypeName.key_type(crypto_params),
                              HybridArgType.PRIV_CIRCUIT_VAL)
Esempio n. 6
0
    def visitFunctionCallExpr(self, ast: FunctionCallExpr):
        if isinstance(ast.func, BuiltinFunction) and (ast.func.is_arithmetic()
                                                      or ast.func.op == '~'):
            # For arithmetic operations, need to simulate finite integer semantics (since python has arbitrary precision ints)
            t = ast.annotated_type.type_name if ast.annotated_type is not None else TypeName.uint_type(
            )
            res = super().visitFunctionCallExpr(ast)
            if not t.is_literal and ast.func.homomorphism == Homomorphism.NON_HOMOMORPHIC:
                # Use cast for correct overflow behavior according to type
                res = self.handle_cast(res, t)
            return res
        elif isinstance(ast.func, BuiltinFunction) and ast.func.is_comp(
        ) and self.inside_circuit:
            # Inside circuit, only comparisons with values using less than 252 bits are valid
            # -> perform additional check
            args = [
                f'{api("range_checked")}({self.visit(a)})' for a in ast.args
            ]
            return ast.func.format_string().format(*args)
        elif ast.is_cast:
            return self.handle_cast(self.visit(ast.args[0]),
                                    ast.func.target.annotated_type.type_name)
        elif isinstance(
                ast.func, LocationExpr
        ) and ast.func.target is not None and ast.func.target.requires_verification:
            # Function calls to functions which require verification need to be treated differently
            # (called function has a different priv-value dictionary)
            f = self.visit(ast.func)
            a = self.visit_list(ast.args, ', ')
            return f'{api("call_fct")}({ast.sec_start_offset}, self.{f}, {a})'

        return super().visitFunctionCallExpr(ast)
Esempio n. 7
0
    def visitFunctionCallExpr(self, ast: FunctionCallExpr):
        if isinstance(ast.func, BuiltinFunction):
            assert ast.func.can_be_private()
            args = list(map(self.visit, ast.args))
            if ast.func.is_shiftop():
                assert ast.args[1].annotated_type.type_name.is_literal
                args[1] = ast.args[1].annotated_type.type_name.value

            op = ast.func.op
            op = '-' if op == 'sign-' else op

            if op == 'ite':
                fstr = "o_({}, '?', {}, ':', {})"
            elif op == 'parenthesis':
                fstr = '({})'
            elif op == 'sign+':
                raise NotImplementedError()
            else:
                o = f"'{op}'" if len(op) == 1 else f'"{op}"'
                if len(args) == 1:
                    fstr = f"o_({o}, {{}})"
                else:
                    assert len(args) == 2
                    fstr = f'o_({{}}, {o}, {{}})'

            return fstr.format(*args)
        elif ast.is_cast and isinstance(ast.func.target, EnumDefinition):
            assert ast.annotated_type.type_name.elem_bitwidth == 256
            return self.handle_cast(self.visit(ast.args[0]),
                                    TypeName.uint_type())

        raise ValueError(
            f'Unsupported function {ast.func.code()} inside circuit')
Esempio n. 8
0
 def request_private_key(self,
                         crypto_params: CryptoParams) -> List[Statement]:
     assert crypto_params in self._needed_secret_key or crypto_params in \
            [p.annotated_type.type_name.crypto_params for p in self.fct.parameters if p.annotated_type.is_cipher()]
     key_name = self.get_own_secret_key_name(crypto_params)
     self._secret_input_name_factory.add_idf(
         key_name, TypeName.key_type(crypto_params))
     return [EnterPrivateKeyStatement(crypto_params)]
Esempio n. 9
0
    def get_new_name(self, t: TypeName, inc=False) -> str:
        """
        Generate a fresh name for a value of type t.

        :param t: transformed type
        :param inc: if True, the internal counter, which is used as part of fresh ids, is incremented
        """
        if t == TypeName.key_type():
            postfix = 'key'
        elif t.is_cipher():
            postfix = 'cipher'
        else:
            postfix = 'plain'
        name = f'{self.base_name}{self.count}_{postfix}'
        if inc:
            self.count += 1
        return name
Esempio n. 10
0
    def implicitly_converted_to(expr: Expression, t: TypeName) -> Expression:
        if isinstance(expr, ReclassifyExpr) and not expr.privacy.is_all_expr():
            # Cast the argument of the ReclassifyExpr instead
            expr.expr = TypeCheckVisitor.implicitly_converted_to(expr.expr, t)
            expr.annotated_type.type_name = expr.expr.annotated_type.type_name
            return expr

        assert expr.annotated_type.type_name.is_primitive_type()
        cast = PrimitiveCastExpr(t.clone(), expr, is_implicit=True).override(
            parent=expr.parent,
            statement=expr.statement,
            line=expr.line,
            column=expr.column)
        cast.elem_type.parent = cast
        expr.parent = cast
        cast.annotated_type = AnnotatedTypeName(
            t.clone(), expr.annotated_type.privacy_annotation.clone(),
            expr.annotated_type.homomorphism).override(parent=cast)
        return cast
Esempio n. 11
0
    def _ensure_encryption(self, stmt: Statement, plain: HybridArgumentIdf,
                           new_privacy: PrivacyLabelExpr,
                           crypto_params: CryptoParams,
                           cipher: HybridArgumentIdf, is_param: bool,
                           is_dec: bool):
        """
        Make sure that cipher = enc(plain, getPk(new_privacy), priv_user_provided_rnd).

        This automatically requests necessary keys and adds a circuit input for the randomness.

        Note: This function adds pre-statements to stmt

        :param stmt [SIDE EFFECT]: the statement which contains the expression which requires this encryption
        :param plain: circuit variable referencing the plaintext value
        :param new_privacy: privacy label corresponding to the destination key address
        :param cipher: circuit variable referencing the encrypted value
        :param is_param: whether cipher is a function parameter
        :param is_dec: whether this is a decryption operation (user supplied plain) as opposed to an encryption operation (user supplied cipher)
        """
        if crypto_params.is_symmetric_cipher():
            # Need a different set of keys for hybrid-encryption (ecdh-based) backends
            self._require_secret_key(crypto_params)
            my_pk = self._require_public_key_for_label_at(
                stmt, Expression.me_expr(), crypto_params)
            if is_dec:
                other_pk = self._get_public_key_in_sender_field(
                    stmt, cipher, crypto_params)
            else:
                if new_privacy == Expression.me_expr():
                    other_pk = my_pk
                else:
                    other_pk = self._require_public_key_for_label_at(
                        stmt, new_privacy, crypto_params)

                self.phi.append(
                    CircComment(
                        f'{cipher.name} = enc({plain.name}, ecdh({other_pk.name}, my_sk))'
                    ))
            self._phi.append(
                CircSymmEncConstraint(plain, other_pk, cipher, is_dec))
        else:
            rnd = self._secret_input_name_factory.add_idf(
                f'{plain.name if is_param else cipher.name}_R',
                TypeName.rnd_type(crypto_params))
            pk = self._require_public_key_for_label_at(stmt, new_privacy,
                                                       crypto_params)
            if not is_dec:
                self.phi.append(
                    CircComment(
                        f'{cipher.name} = enc({plain.name}, {pk.name})'))
            self._phi.append(CircEncConstraint(plain, rnd, pk, cipher, is_dec))
Esempio n. 12
0
    def visitFunctionCallExpr(self, ast: FunctionCallExpr):
        if isinstance(ast.func, BuiltinFunction):
            assert ast.func.can_be_private()
            args = list(map(self.visit, ast.args))
            if ast.func.is_shiftop():
                assert ast.args[1].annotated_type.type_name.is_literal
                args[1] = ast.args[1].annotated_type.type_name.value

            op = ast.func.op
            op = '-' if op == 'sign-' else op

            homomorphism = ast.func.homomorphism
            if homomorphism == Homomorphism.NON_HOMOMORPHIC:
                f_start = 'o_('
            else:
                crypto_backend = cfg.get_crypto_params(
                    homomorphism).crypto_name
                public_key_name = ast.public_key.name
                f_start = f'o_hom("{crypto_backend}", "{public_key_name}", '
                args = [f'HomomorphicInput.of({arg})' for arg in args]

            if op == 'ite':
                fstr = f"{f_start}{{}}, '?', {{}}, ':', {{}})"
            elif op == 'parenthesis':
                fstr = '({})'
            elif op == 'sign+':
                raise NotImplementedError()
            else:
                o = f"'{op}'" if len(op) == 1 else f'"{op}"'
                if len(args) == 1:
                    fstr = f"{f_start}{o}, {{}})"
                else:
                    assert len(args) == 2
                    fstr = f'{f_start}{{}}, {o}, {{}})'
                    if op == "*" and ast.func.rerand_using is not None:
                        # re-randomize homomorphic scalar multiplication
                        rnd = self.visit(ast.func.rerand_using)
                        fstr = f'o_rerand({fstr}, "{crypto_backend}", "{public_key_name}", {rnd})'

            return fstr.format(*args)
        elif ast.is_cast and isinstance(ast.func.target, EnumDefinition):
            assert ast.annotated_type.type_name.elem_bitwidth == 256
            return self.handle_cast(self.visit(ast.args[0]),
                                    TypeName.uint_type())

        raise ValueError(
            f'Unsupported function {ast.func.code()} inside circuit')
Esempio n. 13
0
    def request_public_key(self, crypto_params: CryptoParams,
                           plabel: Union[MeExpr, Identifier], name: str):
        """
        Request key for the address corresponding to plabel from pki infrastructure and add it to the public circuit inputs.

        :param plabel: privacy label for which to request key
        :param name: name to use for the HybridArgumentIdf holding the key
        :return: HybridArgumentIdf containing the requested key and an AssignmentStatement which assigns the key request to the idf location
        """
        idf = self._in_name_factory.add_idf(name,
                                            TypeName.key_type(crypto_params))
        pki = IdentifierExpr(
            cfg.get_contract_var_name(
                cfg.get_pki_contract_name(crypto_params)))
        privacy_label_expr = get_privacy_expr_from_label(plabel)
        return idf, idf.get_loc_expr().assign(
            pki.call('getPk', [self._expr_trafo.visit(privacy_label_expr)]))
Esempio n. 14
0
    def _require_public_key_for_label_at(
            self, stmt: Optional[Statement], privacy: PrivacyLabelExpr,
            crypto_params: CryptoParams) -> HybridArgumentIdf:
        """
        Make circuit helper aware, that the key corresponding to privacy is required at stmt.

        If privacy is not a statically known label, the key is requested on spot.
        Otherwise the label is added to the global key set.
        The keys in that set are requested only once at the start of the external wrapper function, to improve efficiency.

        Note: This function has side effects on stmt (adds a pre_statement)

        :return: HybridArgumentIdf which references the key
        """
        if privacy in self._static_owner_labels:
            # Statically known privacy -> keep track (all global keys will be requested only once)
            self._global_keys[(privacy, crypto_params)] = None
            return HybridArgumentIdf(
                self.get_glob_key_name(privacy, crypto_params),
                TypeName.key_type(crypto_params),
                HybridArgType.PUB_CIRCUIT_ARG)

        if stmt is None:
            raise ValueError(
                'stmt cannot be None if privacy is not guaranteed to be statically known'
            )

        # privacy cannot be MeExpr (is in _static_owner_labels) or AllExpr (has no public key)
        assert isinstance(privacy, Identifier)

        if stmt not in self._requested_dynamic_pks:
            self._requested_dynamic_pks[stmt] = {}
        requested_dynamic_pks = self._requested_dynamic_pks[stmt]
        if privacy in requested_dynamic_pks:
            return requested_dynamic_pks[privacy]

        # Dynamic privacy -> always request key on spot and add to local in args
        name = f'{self._in_name_factory.get_new_name(TypeName.key_type(crypto_params))}_{privacy.name}'
        idf, get_key_stmt = self.request_public_key(crypto_params, privacy,
                                                    name)
        stmt.pre_statements.append(get_key_stmt)
        requested_dynamic_pks[privacy] = idf
        return idf
Esempio n. 15
0
    def visitIndexExpr(self, ast: IndexExpr):
        arr = ast.arr
        index = ast.key

        map_t = arr.annotated_type
        # should have already been checked
        assert (map_t.privacy_annotation.is_all_expr())

        # do actual type checking
        if isinstance(map_t.type_name, Mapping):
            key_type = map_t.type_name.key_type
            expected = AnnotatedTypeName(key_type, Expression.all_expr())
            instance = index.instanceof(expected)
            if not instance:
                raise TypeMismatchException(expected, index.annotated_type,
                                            ast)

            # record indexing information
            if map_t.type_name.key_label is not None:  # TODO modification correct?
                if index.privacy_annotation_label():
                    map_t.type_name.instantiated_key = index
                else:
                    raise TypeException(
                        f'Index cannot be used as a privacy type for array of type {map_t}',
                        ast)

            # determine value type
            ast.annotated_type = map_t.type_name.value_type

            if not self.is_accessible_by_invoker(ast):
                raise TypeException(
                    "Tried to read value which cannot be proven to be owned by the transaction invoker",
                    ast)
        elif isinstance(map_t.type_name, Array):
            if ast.key.annotated_type.is_private():
                raise TypeException('No private array index', ast)
            if not ast.key.instanceof_data_type(TypeName.number_type()):
                raise TypeException('Array index must be numeric', ast)
            ast.annotated_type = map_t.type_name.value_type
        else:
            raise TypeException('Indexing into non-mapping', ast)
Esempio n. 16
0
    def _get_public_key_in_sender_field(
            self, stmt: Statement, cipher: HybridArgumentIdf,
            crypto_params: CryptoParams) -> HybridArgumentIdf:
        """
        Ensure the circuit has access to the public key stored in cipher's sender field.

        Note: This function has side effects on stmt [adds a pre-statement]

        :param stmt [SIDE EFFECT]: statement in which this private expression occurs
        :param cipher: HybridArgumentIdf which references the cipher value
        :return: HybridArgumentIdf which references the key in cipher's sender field (or 0 if none)
        """
        key_t = TypeName.key_type(crypto_params)
        name = f'{self._in_name_factory.get_new_name(key_t)}_sender'
        key_idf = self._in_name_factory.add_idf(name, key_t)
        cipher_payload_len = crypto_params.cipher_payload_len
        key_expr = KeyLiteralExpr(
            [cipher.get_loc_expr(stmt).index(cipher_payload_len)],
            crypto_params).as_type(key_t)
        stmt.pre_statements.append(
            AssignmentStatement(key_idf.get_loc_expr(), key_expr))
        return key_idf
Esempio n. 17
0
def add_function_circuit_arguments(circuit: CircuitHelper):
    """Generate java code which adds circuit IO as described by circuit"""

    input_init_stmts = []
    for sec_input in circuit.sec_idfs:
        input_init_stmts.append(
            f'addS("{sec_input.name}", {sec_input.t.size_in_uints}, {_get_t(sec_input.t)});'
        )

    for pub_input in circuit.input_idfs:
        if pub_input.t == TypeName.key_type():
            input_init_stmts.append(
                f'addK("{pub_input.name}", {pub_input.t.size_in_uints});')
        else:
            input_init_stmts.append(
                f'addIn("{pub_input.name}", {pub_input.t.size_in_uints}, {_get_t(pub_input.t)});'
            )

    for pub_output in circuit.output_idfs:
        input_init_stmts.append(
            f'addOut("{pub_output.name}", {pub_output.t.size_in_uints}, {_get_t(pub_output.t)});'
        )

    return input_init_stmts
Esempio n. 18
0
    def handle_builtin_function_call(self, ast: FunctionCallExpr,
                                     func: BuiltinFunction):
        # handle special cases
        if func.is_ite():
            cond_t = ast.args[0].annotated_type

            # Ensure that condition is boolean
            if not cond_t.type_name.implicitly_convertible_to(
                    TypeName.bool_type()):
                raise TypeMismatchException(TypeName.bool_type(),
                                            cond_t.type_name, ast.args[0])

            res_t = ast.args[1].annotated_type.type_name.combined_type(
                ast.args[2].annotated_type.type_name, True)

            if cond_t.is_private():
                # Everything is turned private
                func.is_private = True
                a = res_t.annotate(Expression.me_expr())
            else:
                p = ast.args[1].annotated_type.combined_privacy(
                    ast.analysis, ast.args[2].annotated_type)
                a = res_t.annotate(p)
            ast.args[1] = self.get_rhs(ast.args[1], a)
            ast.args[2] = self.get_rhs(ast.args[2], a)

            ast.annotated_type = a
            return
        elif func.is_parenthesis():
            ast.annotated_type = ast.args[0].annotated_type
            return

        # Check that argument types conform to op signature
        parameter_types = func.input_types()
        if not func.is_eq():
            for arg, t in zip(ast.args, parameter_types):
                if not arg.instanceof_data_type(t):
                    raise TypeMismatchException(t,
                                                arg.annotated_type.type_name,
                                                arg)

        t1 = ast.args[0].annotated_type.type_name
        t2 = None if len(
            ast.args) == 1 else ast.args[1].annotated_type.type_name

        if len(ast.args) == 1:
            arg_t = 'lit' if ast.args[
                0].annotated_type.type_name.is_literal else t1
        else:
            assert len(ast.args) == 2
            is_eq_with_tuples = func.is_eq() and isinstance(t1, TupleType)
            arg_t = t1.combined_type(t2, convert_literals=is_eq_with_tuples)

        # Infer argument and output types
        if arg_t == 'lit':
            res = func.op_func(
                *[arg.annotated_type.type_name.value for arg in ast.args])
            if isinstance(res, bool):
                assert func.output_type() == TypeName.bool_type()
                out_t = BooleanLiteralType(res)
            else:
                assert func.output_type() == TypeName.number_type()
                out_t = NumberLiteralType(res)
            if func.is_eq():
                arg_t = t1.to_abstract_type().combined_type(
                    t2.to_abstract_type(), True)
        elif func.output_type() == TypeName.bool_type():
            out_t = TypeName.bool_type()
        else:
            out_t = arg_t

        assert arg_t is not None and (arg_t != 'lit' or not func.is_eq())

        private_args = any(map(self.has_private_type, ast.args))
        if private_args:
            assert arg_t != 'lit'
            if func.can_be_private():
                if func.is_shiftop():
                    if not ast.args[1].annotated_type.type_name.is_literal:
                        raise TypeException(
                            'Private shift expressions must use a constant (literal) shift amount',
                            ast.args[1])
                    if ast.args[1].annotated_type.type_name.value < 0:
                        raise TypeException('Cannot shift by negative amount',
                                            ast.args[1])
                if func.is_bitop() or func.is_shiftop():
                    for arg in ast.args:
                        if arg.annotated_type.type_name.elem_bitwidth == 256:
                            raise TypeException(
                                'Private bitwise and shift operations are only supported for integer types < 256 bit, '
                                'please use a smaller type', arg)

                if func.is_arithmetic():
                    for a in ast.args:
                        if a.annotated_type.type_name.elem_bitwidth == 256:
                            issue_compiler_warning(
                                func, 'Possible field prime overflow',
                                'Private arithmetic 256bit operations overflow at FIELD_PRIME.\n'
                                'If you need correct overflow behavior, use a smaller integer type.'
                            )
                            break
                elif func.is_comp():
                    for a in ast.args:
                        if a.annotated_type.type_name.elem_bitwidth == 256:
                            issue_compiler_warning(
                                func, 'Possible private comparison failure',
                                'Private 256bit comparison operations will fail for values >= 2^252.\n'
                                'If you cannot guarantee that the value stays in range, you must use '
                                'a smaller integer type to ensure correctness.'
                            )
                            break

                func.is_private = True
                p = Expression.me_expr()
            else:
                raise TypeException(
                    f'Operation \'{func.op}\' does not support private operands',
                    ast)
        else:
            p = None

        if arg_t != 'lit':
            # Add implicit casts for arguments
            arg_pt = arg_t.annotate(p)
            if func.is_shiftop() and p is not None:
                ast.args[0] = self.get_rhs(ast.args[0], arg_pt)
            else:
                ast.args[:] = map(
                    lambda argument: self.get_rhs(argument, arg_pt), ast.args)

        ast.annotated_type = out_t.annotate(p)
Esempio n. 19
0
class GlobalDefs:
    # gasleft: FunctionDefinition = FunctionDefinition(
    #     idf=Identifier('gasleft'),
    #     parameters=[],
    #     modifiers=[],
    #     return_parameters=[Parameter([], annotated_type=AnnotatedTypeName.uint_all(), idf=Identifier(''))],
    #     body=Block([])
    # )
    # gasleft.idf.parent = gasleft

    address_struct: StructDefinition = StructDefinition(
        Identifier('<address>'), [
            VariableDeclaration([], AnnotatedTypeName.uint_all(),
                                Identifier('balance'))
        ])
    set_parents(address_struct)

    address_payable_struct: StructDefinition = StructDefinition(
        Identifier('<address_payable>'), [
            VariableDeclaration([], AnnotatedTypeName.uint_all(),
                                Identifier('balance')),
            ConstructorOrFunctionDefinition(
                Identifier('send'),
                [Parameter([], AnnotatedTypeName.uint_all(), Identifier(''))],
                ['public'],
                [Parameter([], AnnotatedTypeName.bool_all(), Identifier(''))],
                Block([])),
            ConstructorOrFunctionDefinition(
                Identifier('transfer'),
                [Parameter([], AnnotatedTypeName.uint_all(), Identifier(''))],
                ['public'], [], Block([])),
        ])
    address_payable_struct.members[1].can_be_private = False
    address_payable_struct.members[2].can_be_private = False
    set_parents(address_payable_struct)

    msg_struct: StructDefinition = StructDefinition(Identifier('<msg>'), [
        VariableDeclaration([],
                            AnnotatedTypeName(TypeName.address_payable_type()),
                            Identifier('sender')),
        VariableDeclaration([], AnnotatedTypeName.uint_all(),
                            Identifier('value')),
    ])
    set_parents(msg_struct)

    block_struct: StructDefinition = StructDefinition(Identifier('<block>'), [
        VariableDeclaration([],
                            AnnotatedTypeName(TypeName.address_payable_type()),
                            Identifier('coinbase')),
        VariableDeclaration([], AnnotatedTypeName.uint_all(),
                            Identifier('difficulty')),
        VariableDeclaration([], AnnotatedTypeName.uint_all(),
                            Identifier('gaslimit')),
        VariableDeclaration([], AnnotatedTypeName.uint_all(),
                            Identifier('number')),
        VariableDeclaration([], AnnotatedTypeName.uint_all(),
                            Identifier('timestamp')),
    ])
    set_parents(block_struct)

    tx_struct: StructDefinition = StructDefinition(Identifier('<tx>'), [
        VariableDeclaration([], AnnotatedTypeName.uint_all(),
                            Identifier('gasprice')),
        VariableDeclaration([],
                            AnnotatedTypeName(TypeName.address_payable_type()),
                            Identifier('origin')),
    ])
    set_parents(tx_struct)
Esempio n. 20
0
 def visitMapping(self, ast: Mapping):
     if ast.key_label is not None:
         if ast.key_type != TypeName.address_type():
             raise TypeException(f'Only addresses can be annotated', ast)
Esempio n. 21
0
    def _get_circuit_output_for_private_expression(
            self, expr: Expression, new_privacy: PrivacyLabelExpr,
            homomorphism: Homomorphism) -> LocationExpr:
        """
        Add evaluation of expr to the circuit and return the output HybridArgumentIdf corresponding to the evaluation result.

        Note: has side effects on expr.statement (adds pre_statement)

        :param expr: [SIDE EFFECT] expression to evaluate
        :param new_privacy: result owner (determines encryption key)
        :return: HybridArgumentIdf which references the circuit output containing the result of expr
        """
        is_circ_val = isinstance(expr, IdentifierExpr) and isinstance(
            expr.idf, HybridArgumentIdf
        ) and expr.idf.arg_type != HybridArgType.PUB_CONTRACT_VAL
        is_hom_comp = isinstance(expr, FunctionCallExpr) and isinstance(
            expr.func, BuiltinFunction
        ) and expr.func.homomorphism != Homomorphism.NON_HOMOMORPHIC
        if is_hom_comp:
            # Treat a homomorphic operation as a privately evaluated operation on (public) ciphertexts
            expr.annotated_type = AnnotatedTypeName.cipher_type(
                expr.annotated_type, homomorphism)

        if is_circ_val or expr.annotated_type.is_private(
        ) or expr.evaluate_privately:
            priv_result_idf = self._evaluate_private_expression(expr)
        else:
            # For public expressions which should not be evaluated in private, only the result is moved into the circuit
            priv_result_idf = self.add_to_circuit_inputs(expr)
        private_expr = priv_result_idf.get_idf_expr()

        t_suffix = ''
        if isinstance(expr, IdentifierExpr) and not is_circ_val:
            t_suffix += f'_{expr.idf.name}'

        if isinstance(new_privacy,
                      AllExpr) or expr.annotated_type.type_name.is_cipher():
            # If the result is public, add an equality constraint to ensure that the user supplied public output
            # is equal to the circuit evaluation result
            tname = f'{self._out_name_factory.get_new_name(expr.annotated_type.type_name)}{t_suffix}'
            new_out_param = self._out_name_factory.add_idf(
                tname, expr.annotated_type.type_name, private_expr)
            self._phi.append(CircEqConstraint(priv_result_idf, new_out_param))
            out_var = new_out_param.get_loc_expr().explicitly_converted(
                expr.annotated_type.type_name)
        else:
            # If the result is encrypted, add an encryption constraint to ensure that the user supplied encrypted output
            # is equal to the correctly encrypted circuit evaluation result
            new_privacy = self._get_canonical_privacy_label(
                expr.analysis, new_privacy)
            privacy_label_expr = get_privacy_expr_from_label(new_privacy)
            cipher_t = TypeName.cipher_type(expr.annotated_type, homomorphism)
            tname = f'{self._out_name_factory.get_new_name(cipher_t)}{t_suffix}'
            enc_expr = EncryptionExpression(private_expr, privacy_label_expr,
                                            homomorphism)
            new_out_param = self._out_name_factory.add_idf(
                tname, cipher_t, enc_expr)
            crypto_params = cfg.get_crypto_params(homomorphism)
            self._ensure_encryption(expr.statement, priv_result_idf,
                                    new_privacy, crypto_params, new_out_param,
                                    False, False)
            out_var = new_out_param.get_loc_expr()

        # Add an invisible CircuitComputationStatement to the solidity code, which signals the offchain simulator,
        # that the value the contained out variable must be computed at this point by simulating expression evaluation
        expr.statement.pre_statements.append(
            CircuitComputationStatement(new_out_param))
        return out_var
Esempio n. 22
0
    def add_to_circuit_inputs(self, expr: Expression) -> HybridArgumentIdf:
        """
        Add the provided expression to the public circuit inputs.

        Roughly corresponds to in() from paper

        If expr is encrypted (privacy != @all), this function also automatically ensures that the circuit has access to
        the correctly decrypted expression value in the form of a new private circuit input.

        If expr is an IdentifierExpr, its value will be cached
        (i.e. when the same identifier is needed again as a circuit input, its value will be retrieved from cache rather \
         than adding an expensive redundant input. The cache is invalidated as soon as the identifier is overwritten in public code)

        Note: This function has side effects on expr.statement (adds a pre_statement)

        :param expr: [SIDE EFFECT] expression which should be made available inside the circuit as an argument
        :return: HybridArgumentIdf which references the plaintext value of the newly added input
        """
        privacy = expr.annotated_type.privacy_annotation.privacy_annotation_label(
        ) if expr.annotated_type.is_private() else Expression.all_expr()
        is_public = privacy == Expression.all_expr()

        expr_text = expr.code()
        input_expr = self._expr_trafo.visit(expr)
        t = input_expr.annotated_type.type_name
        locally_decrypted_idf = None

        # If expression has literal type -> evaluate it inside the circuit (constant folding will be used)
        # rather than introducing an unnecessary public circuit input (expensive)
        if isinstance(t, BooleanLiteralType):
            return self._evaluate_private_expression(input_expr, str(t.value))
        elif isinstance(t, NumberLiteralType):
            return self._evaluate_private_expression(input_expr, str(t.value))

        t_suffix = ''
        if isinstance(expr, IdentifierExpr):
            # Look in cache before doing expensive move-in
            if self._remapper.is_remapped(expr.target.idf):
                remapped_idf = self._remapper.get_current(expr.target.idf)
                return remapped_idf

            t_suffix = f'_{expr.idf.name}'

        # Generate circuit inputs
        if is_public:
            tname = f'{self._in_name_factory.get_new_name(expr.annotated_type.type_name)}{t_suffix}'
            return_idf = input_idf = self._in_name_factory.add_idf(
                tname, expr.annotated_type.type_name)
            self._phi.append(CircComment(f'{input_idf.name} = {expr_text}'))
        else:
            # Encrypted inputs need to be decrypted inside the circuit (i.e. add plain as private input and prove encryption)
            tname = f'{self._secret_input_name_factory.get_new_name(expr.annotated_type.type_name)}{t_suffix}'
            return_idf = locally_decrypted_idf = self._secret_input_name_factory.add_idf(
                tname, expr.annotated_type.type_name)
            cipher_t = TypeName.cipher_type(input_expr.annotated_type,
                                            expr.annotated_type.homomorphism)
            tname = f'{self._in_name_factory.get_new_name(cipher_t)}{t_suffix}'
            input_idf = self._in_name_factory.add_idf(
                tname, cipher_t, IdentifierExpr(locally_decrypted_idf))

        # Add a CircuitInputStatement to the solidity code, which looks like a normal assignment statement,
        # but also signals the offchain simulator to perform decryption if necessary
        expr.statement.pre_statements.append(
            CircuitInputStatement(input_idf.get_loc_expr(), input_expr))

        if not is_public:
            # Check if the secret plain input corresponds to the decrypted cipher value
            crypto_params = cfg.get_crypto_params(
                expr.annotated_type.homomorphism)
            self._phi.append(
                CircComment(
                    f'{locally_decrypted_idf} = dec({expr_text}) [{input_idf.name}]'
                ))
            self._ensure_encryption(expr.statement, locally_decrypted_idf,
                                    Expression.me_expr(), crypto_params,
                                    input_idf, False, True)

        # Cache circuit input for later reuse if possible
        if cfg.opt_cache_circuit_inputs and isinstance(expr, IdentifierExpr):
            # TODO: What if a homomorphic variable gets used as both a plain variable and as a ciphertext?
            #       This works for now because we never perform homomorphic operations on variables we can decrypt.
            self._remapper.remap(expr.target.idf, return_idf)

        return return_idf
Esempio n. 23
0
    def create_external_wrapper_body(int_fct: ConstructorOrFunctionDefinition,
                                     ext_circuit: CircuitHelper,
                                     original_params: List[Parameter],
                                     requires_proof: bool) -> Block:
        """
        Return Block with external wrapper function body.

        :param int_fct: corresponding internal function
        :param ext_circuit: [SIDE EFFECT] circuit helper of the external wrapper function
        :param original_params: list of transformed function parameters without additional parameters added due to transformation
        :return: body with wrapper code
        """
        has_priv_args = any(
            [p.annotated_type.is_cipher() for p in original_params])
        stmts = []

        if has_priv_args:
            ext_circuit._require_public_key_for_label_at(
                None, Expression.me_expr())
        if cfg.is_symmetric_cipher():
            # Make sure msg.sender's key pair is available in the circuit
            assert any(isinstance(k, MeExpr) for k in ext_circuit.requested_global_keys) \
                   or has_priv_args, "requires verification => both sender keys required"
            stmts += ext_circuit.request_private_key()

        # Verify that out parameter has correct size
        stmts += [
            RequireStatement(
                IdentifierExpr(cfg.zk_out_name).dot('length').binop(
                    '==', NumberLiteralExpr(ext_circuit.out_size_trans)))
        ]

        # IdentifierExpr for array var holding serialized public circuit inputs
        in_arr_var = IdentifierExpr(cfg.zk_in_name).as_type(
            Array(AnnotatedTypeName.uint_all()))

        # Find index of me's public key in requested_global_keys
        glob_me_key_index = -1
        for idx, e in enumerate(ext_circuit.requested_global_keys):
            if isinstance(e, MeExpr):
                glob_me_key_index = idx
                break

        # Request static public keys
        offset = 0
        key_req_stmts = []
        if ext_circuit.requested_global_keys:
            # Ensure that me public key is stored starting at in[0]
            keys = [key for key in ext_circuit.requested_global_keys]
            if glob_me_key_index != -1:
                (keys[0], keys[glob_me_key_index]) = (keys[glob_me_key_index],
                                                      keys[0])

            tmp_key_var = Identifier('_tmp_key')
            key_req_stmts.append(
                tmp_key_var.decl_var(AnnotatedTypeName.key_type()))
            for key_owner in keys:
                idf, assignment = ext_circuit.request_public_key(
                    key_owner, ext_circuit.get_glob_key_name(key_owner))
                assignment.lhs = IdentifierExpr(tmp_key_var.clone())
                key_req_stmts.append(assignment)

                # Manually add to circuit inputs
                key_req_stmts.append(
                    in_arr_var.slice(offset, cfg.key_len).assign(
                        IdentifierExpr(tmp_key_var.clone()).slice(
                            0, cfg.key_len)))
                offset += cfg.key_len
                assert offset == ext_circuit.in_size

        # Check encrypted parameters
        param_stmts = []
        for p in original_params:
            """ * of T_e rule 8 """
            if p.annotated_type.is_cipher():
                assign_stmt = in_arr_var.slice(
                    offset, cfg.cipher_payload_len).assign(
                        IdentifierExpr(p.idf.clone()).slice(
                            0, cfg.cipher_payload_len))
                ext_circuit.ensure_parameter_encryption(assign_stmt, p)

                # Manually add to circuit inputs
                param_stmts.append(assign_stmt)
                offset += cfg.cipher_payload_len

        if cfg.is_symmetric_cipher():
            # Populate sender field of encrypted parameters
            copy_stmts = []
            for p in original_params:
                if p.annotated_type.is_cipher():
                    sender_key = in_arr_var.index(0)
                    idf = IdentifierExpr(p.idf.clone()).as_type(
                        p.annotated_type.clone())
                    lit = ArrayLiteralExpr([
                        idf.clone().index(i)
                        for i in range(cfg.cipher_payload_len)
                    ] + [sender_key])
                    copy_stmts.append(
                        VariableDeclarationStatement(
                            VariableDeclaration([], p.annotated_type.clone(),
                                                p.idf.clone(), 'memory'), lit))
            if copy_stmts:
                param_stmts += [
                    Comment(),
                    Comment(
                        'Copy from calldata to memory and set sender field')
                ] + copy_stmts

            assert glob_me_key_index != -1, "Symmetric cipher but did not request me key"

        # Declare in array
        new_in_array_expr = NewExpr(
            AnnotatedTypeName(TypeName.dyn_uint_array()),
            [NumberLiteralExpr(ext_circuit.in_size_trans)])
        in_var_decl = in_arr_var.idf.decl_var(TypeName.dyn_uint_array(),
                                              new_in_array_expr)
        stmts.append(in_var_decl)
        stmts.append(Comment())
        stmts += Comment.comment_wrap_block('Request static public keys',
                                            key_req_stmts)
        stmts += Comment.comment_wrap_block(
            'Backup private arguments for verification', param_stmts)

        # Call internal function
        args = [IdentifierExpr(param.idf.clone()) for param in original_params]
        internal_call = FunctionCallExpr(
            IdentifierExpr(int_fct.idf.clone()).override(target=int_fct), args)
        internal_call.sec_start_offset = ext_circuit.priv_in_size

        if int_fct.requires_verification:
            ext_circuit.call_function(internal_call)
            args += [
                in_arr_var.clone(),
                NumberLiteralExpr(ext_circuit.in_size),
                IdentifierExpr(cfg.zk_out_name),
                NumberLiteralExpr(ext_circuit.out_size)
            ]

        if int_fct.return_parameters:
            stmts += Comment.comment_list("Declare return variables", [
                VariableDeclarationStatement(deep_copy(vd))
                for vd in int_fct.return_var_decls
            ])
            in_call = TupleExpr([
                IdentifierExpr(vd.idf.clone())
                for vd in int_fct.return_var_decls
            ]).assign(internal_call)
        else:
            in_call = ExpressionStatement(internal_call)
        stmts.append(Comment("Call internal function"))
        stmts.append(in_call)
        stmts.append(Comment())

        # Call verifier
        if requires_proof:
            verifier = IdentifierExpr(
                cfg.get_contract_var_name(
                    ext_circuit.verifier_contract_type.code()))
            verifier_args = [
                IdentifierExpr(cfg.proof_param_name),
                IdentifierExpr(cfg.zk_in_name),
                IdentifierExpr(cfg.zk_out_name)
            ]
            verify = ExpressionStatement(
                verifier.call(cfg.verification_function_name, verifier_args))
            stmts.append(
                StatementList(
                    [Comment('Verify zk proof of execution'), verify],
                    excluded_from_simulation=True))

        # Add return statement at the end if necessary
        if int_fct.return_parameters:
            stmts.append(
                ReturnStatement(
                    TupleExpr([
                        IdentifierExpr(vd.idf.clone())
                        for vd in int_fct.return_var_decls
                    ])))

        return Block(stmts)
Esempio n. 24
0
    def create_external_wrapper_body(int_fct: ConstructorOrFunctionDefinition, ext_circuit: CircuitHelper,
                                     original_params: List[Parameter], requires_proof: bool) -> Block:
        """
        Return Block with external wrapper function body.

        :param int_fct: corresponding internal function
        :param ext_circuit: [SIDE EFFECT] circuit helper of the external wrapper function
        :param original_params: list of transformed function parameters without additional parameters added due to transformation
        :return: body with wrapper code
        """
        priv_args = [p for p in original_params if p.annotated_type.is_cipher()]
        args_backends = OrderedDict.fromkeys([p.annotated_type.type_name.crypto_params for p in priv_args])
        stmts = []

        for crypto_params in args_backends:
            assert crypto_params in int_fct.used_crypto_backends
            # If there are any private arguments with homomorphism 'hom', we need the public key for that crypto backend
            ext_circuit._require_public_key_for_label_at(None, Expression.me_expr(), crypto_params)
        for crypto_params in cfg.all_crypto_params():
            if crypto_params.is_symmetric_cipher():
                if (MeExpr(), crypto_params) in ext_circuit.requested_global_keys or crypto_params in args_backends:
                    # Make sure msg.sender's key pair is available in the circuit
                    stmts += ext_circuit.request_private_key(crypto_params)

        # Verify that out parameter has correct size
        stmts += [RequireStatement(IdentifierExpr(cfg.zk_out_name).dot('length').binop('==', NumberLiteralExpr(ext_circuit.out_size_trans)))]

        # IdentifierExpr for array var holding serialized public circuit inputs
        in_arr_var = IdentifierExpr(cfg.zk_in_name).as_type(Array(AnnotatedTypeName.uint_all()))

        # Request static public keys
        offset = 0
        key_req_stmts = []
        me_key_idx: Dict[CryptoParams, int] = {}
        if ext_circuit.requested_global_keys:
            # Ensure that me public key is stored starting at in[0]
            keys = [key for key in ext_circuit.requested_global_keys]

            tmp_keys = {}
            for crypto_params in int_fct.used_crypto_backends:
                tmp_key_var = Identifier(f'_tmp_key_{crypto_params.identifier_name}')
                key_req_stmts.append(tmp_key_var.decl_var(AnnotatedTypeName.key_type(crypto_params)))
                tmp_keys[crypto_params] = tmp_key_var
            for (key_owner, crypto_params) in keys:
                tmp_key_var = tmp_keys[crypto_params]
                idf, assignment = ext_circuit.request_public_key(crypto_params, key_owner, ext_circuit.get_glob_key_name(key_owner, crypto_params))
                assignment.lhs = IdentifierExpr(tmp_key_var.clone())
                key_req_stmts.append(assignment)

                # Remember me-keys for later use in symmetrically encrypted keys
                if key_owner == MeExpr():
                    assert crypto_params not in me_key_idx
                    me_key_idx[crypto_params] = offset

                # Manually add to circuit inputs
                key_len = crypto_params.key_len
                key_req_stmts.append(in_arr_var.slice(offset, key_len).assign(IdentifierExpr(tmp_key_var.clone()).slice(0, key_len)))
                offset += key_len
                assert offset == ext_circuit.in_size

        # Check encrypted parameters
        param_stmts = []
        for p in original_params:
            """ * of T_e rule 8 """
            if p.annotated_type.is_cipher():
                cipher_payload_len = p.annotated_type.type_name.crypto_params.cipher_payload_len
                assign_stmt = in_arr_var.slice(offset, cipher_payload_len).assign(IdentifierExpr(p.idf.clone()).slice(0, cipher_payload_len))
                ext_circuit.ensure_parameter_encryption(assign_stmt, p)

                # Manually add to circuit inputs
                param_stmts.append(assign_stmt)
                offset += cipher_payload_len

        # Populate sender field of parameters encrypted with a symmetric cipher
        copy_stmts = []
        for p in original_params:
            if p.annotated_type.is_cipher():
                c = p.annotated_type.type_name
                assert isinstance(c, CipherText)
                if c.crypto_params.is_symmetric_cipher():
                    sender_key = in_arr_var.index(me_key_idx[c.crypto_params])
                    idf = IdentifierExpr(p.idf.clone()).as_type(p.annotated_type.clone())
                    cipher_payload_len = cfg.get_crypto_params(p.annotated_type.homomorphism).cipher_payload_len
                    lit = ArrayLiteralExpr([idf.clone().index(i) for i in range(cipher_payload_len)] + [sender_key])
                    copy_stmts.append(VariableDeclarationStatement(VariableDeclaration([], p.annotated_type.clone(), p.idf.clone(), 'memory'), lit))
        if copy_stmts:
            param_stmts += [Comment(), Comment('Copy from calldata to memory and set sender field')] + copy_stmts

        # Declare in array
        new_in_array_expr = NewExpr(AnnotatedTypeName(TypeName.dyn_uint_array()), [NumberLiteralExpr(ext_circuit.in_size_trans)])
        in_var_decl = in_arr_var.idf.decl_var(TypeName.dyn_uint_array(), new_in_array_expr)
        stmts.append(in_var_decl)
        stmts.append(Comment())
        stmts += Comment.comment_wrap_block('Request static public keys', key_req_stmts)
        stmts += Comment.comment_wrap_block('Backup private arguments for verification', param_stmts)

        # Call internal function
        args = [IdentifierExpr(param.idf.clone()) for param in original_params]
        internal_call = FunctionCallExpr(IdentifierExpr(int_fct.idf.clone()).override(target=int_fct), args)
        internal_call.sec_start_offset = ext_circuit.priv_in_size

        if int_fct.requires_verification:
            ext_circuit.call_function(internal_call)
            args += [in_arr_var.clone(), NumberLiteralExpr(ext_circuit.in_size),
                     IdentifierExpr(cfg.zk_out_name), NumberLiteralExpr(ext_circuit.out_size)]

        if int_fct.return_parameters:
            stmts += Comment.comment_list("Declare return variables", [VariableDeclarationStatement(deep_copy(vd)) for vd in int_fct.return_var_decls])
            in_call = TupleExpr([IdentifierExpr(vd.idf.clone()) for vd in int_fct.return_var_decls]).assign(internal_call)
        else:
            in_call = ExpressionStatement(internal_call)
        stmts.append(Comment("Call internal function"))
        stmts.append(in_call)
        stmts.append(Comment())

        # Call verifier
        if requires_proof and not cfg.disable_verification:
            verifier = IdentifierExpr(cfg.get_contract_var_name(ext_circuit.verifier_contract_type.code()))
            verifier_args = [IdentifierExpr(cfg.proof_param_name), IdentifierExpr(cfg.zk_in_name), IdentifierExpr(cfg.zk_out_name)]
            verify = ExpressionStatement(verifier.call(cfg.verification_function_name, verifier_args))
            stmts.append(StatementList([Comment('Verify zk proof of execution'), verify], excluded_from_simulation=True))

        # Add return statement at the end if necessary
        if int_fct.return_parameters:
            stmts.append(ReturnStatement(TupleExpr([IdentifierExpr(vd.idf.clone()) for vd in int_fct.return_var_decls])))

        return Block(stmts)
Esempio n. 25
0
 def get_randomness_for_rerand(self, expr: Expression) -> IdentifierExpr:
     idf = self._secret_input_name_factory.get_new_idf(
         TypeName.rnd_type(expr.annotated_type.type_name.crypto_params))
     return IdentifierExpr(idf)
Esempio n. 26
0
 def visitAnnotatedTypeName(self, ast: AnnotatedTypeName):
     if ast.is_private():
         t = TypeName.cipher_type(ast)
     else:
         t = self.visit(ast.type_name.clone())
     return AnnotatedTypeName(t)