mirror of
https://github.com/selfxyz/self.git
synced 2026-04-27 03:01:15 -04:00
@@ -17,12 +17,12 @@
|
||||
"@react-native-async-storage/async-storage": "^1.23.1",
|
||||
"@react-native-community/cli": "^14.1.1",
|
||||
"@react-native-community/netinfo": "^11.3.1",
|
||||
"@tamagui/colors": "1.105.0",
|
||||
"@tamagui/config": "1.105.0",
|
||||
"@tamagui/core": "1.105.0",
|
||||
"@tamagui/lucide-icons": "1.105.0",
|
||||
"@tamagui/toast": "1.105.0",
|
||||
"@tamagui/types": "1.105.0",
|
||||
"@tamagui/colors": "1.110.0",
|
||||
"@tamagui/config": "1.110.0",
|
||||
"@tamagui/core": "1.110.0",
|
||||
"@tamagui/lucide-icons": "1.110.0",
|
||||
"@tamagui/toast": "1.110.0",
|
||||
"@tamagui/types": "1.110.0",
|
||||
"@types/msgpack-lite": "^0.1.11",
|
||||
"@types/pako": "^2.0.3",
|
||||
"@openpassport/zk-kit-imt": "^0.0.5",
|
||||
@@ -52,7 +52,7 @@
|
||||
"react-native-svg": "13.4.0",
|
||||
"react-native-zip-archive": "^6.1.0",
|
||||
"socket.io-client": "^4.7.5",
|
||||
"tamagui": "1.105.0",
|
||||
"tamagui": "1.110.0",
|
||||
"zustand": "^4.5.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { NativeEventEmitter, NativeModules, Linking, Modal, Platform, Pressable } from 'react-native';
|
||||
import { NativeEventEmitter, NativeModules, Linking, Modal, Platform, Pressable, TouchableOpacity, ScrollView } from 'react-native';
|
||||
import { YStack, XStack, Text, Button, Tabs, Sheet, Label, Fieldset, Input, Switch, H2, Image, useWindowDimensions, H4, H3, View, Separator } from 'tamagui'
|
||||
import { HelpCircle, IterationCw, VenetianMask, Cog, CheckCircle2, ChevronLeft, Share, Eraser, ArrowRight, UserPlus, CalendarSearch, X, ShieldCheck } from '@tamagui/lucide-icons';
|
||||
import Telegram from '../images/telegram.png'
|
||||
@@ -36,6 +36,9 @@ import ValidProofScreen from './ValidProofScreen';
|
||||
import WrongProofScreen from './WrongProofScreen';
|
||||
import MockDataScreen from './MockDataScreen';
|
||||
import OPENPASSPORT_LOGO from '../images/openpassport.png'
|
||||
import { countryCodes } from '../../../common/src/constants/constants';
|
||||
import getCountryISO2 from "country-iso-3-to-2";
|
||||
import { flag } from 'country-emoji';
|
||||
|
||||
const emitter = (Platform.OS === 'android')
|
||||
? new NativeEventEmitter(NativeModules.nativeModule)
|
||||
@@ -57,6 +60,10 @@ const MainScreen: React.FC = () => {
|
||||
const [dateOfBirthDatePickerIsOpen, setDateOfBirthDatePickerIsOpen] = useState(false)
|
||||
const [dateOfExpiryDatePickerIsOpen, setDateOfExpiryDatePickerIsOpen] = useState(false)
|
||||
const [isFormComplete, setIsFormComplete] = useState(false);
|
||||
const [countrySheetOpen, setCountrySheetOpen] = useState(false);
|
||||
const [algorithmSheetOpen, setAlgorithmSheetOpen] = useState(false);
|
||||
const [selectedCountry, setSelectedCountry] = useState('USA');
|
||||
const [selectedAlgorithm, setSelectedAlgorithm] = useState('rsa sha256');
|
||||
|
||||
const {
|
||||
passportNumber,
|
||||
@@ -179,7 +186,7 @@ const MainScreen: React.FC = () => {
|
||||
setSelectedTab("start");
|
||||
}
|
||||
else if (selectedTab === "next") {
|
||||
if (passportData.mockUser) {
|
||||
if (passportData?.mockUser) {
|
||||
setSelectedTab("mock");
|
||||
} else {
|
||||
setSelectedTab("nfc");
|
||||
@@ -206,6 +213,16 @@ const MainScreen: React.FC = () => {
|
||||
|
||||
const { height } = useWindowDimensions();
|
||||
|
||||
const handleCountrySelect = (countryCode: string) => {
|
||||
setSelectedCountry(countryCode);
|
||||
setCountrySheetOpen(false);
|
||||
};
|
||||
|
||||
const handleAlgorithmSelect = (algorithm: string) => {
|
||||
setSelectedAlgorithm(algorithm);
|
||||
setAlgorithmSheetOpen(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<YStack f={1}>
|
||||
<ToastViewport portalToRoot flexDirection="column-reverse" top={85} right={0} left={0} />
|
||||
@@ -773,6 +790,82 @@ const MainScreen: React.FC = () => {
|
||||
</Sheet.Frame>
|
||||
</Sheet>
|
||||
|
||||
<Sheet
|
||||
modal
|
||||
open={countrySheetOpen}
|
||||
onOpenChange={setCountrySheetOpen}
|
||||
snapPoints={[60]}
|
||||
animation="medium"
|
||||
disableDrag
|
||||
>
|
||||
<Sheet.Overlay />
|
||||
<Sheet.Frame bg={bgWhite} borderTopLeftRadius="$9" borderTopRightRadius="$9">
|
||||
<YStack p="$4">
|
||||
<XStack ai="center" jc="space-between" mb="$4">
|
||||
<Text fontSize="$8">Select a country</Text>
|
||||
<XStack onPress={() => setCountrySheetOpen(false)} p="$2">
|
||||
<X color={borderColor} size="$1.5" mr="$2" />
|
||||
</XStack>
|
||||
</XStack>
|
||||
<Separator borderColor={separatorColor} mb="$4" />
|
||||
<ScrollView showsVerticalScrollIndicator={false}>
|
||||
{Object.keys(countryCodes).map((countryCode) => (
|
||||
<TouchableOpacity
|
||||
key={countryCode}
|
||||
onPress={() => {
|
||||
handleCountrySelect(countryCode);
|
||||
setCountrySheetOpen(false);
|
||||
}}
|
||||
>
|
||||
<XStack py="$3" px="$2">
|
||||
<Text fontSize="$4">
|
||||
{countryCodes[countryCode as keyof typeof countryCodes]} {flag(getCountryISO2(countryCode))}
|
||||
</Text>
|
||||
</XStack>
|
||||
</TouchableOpacity>
|
||||
))}
|
||||
</ScrollView>
|
||||
</YStack>
|
||||
</Sheet.Frame>
|
||||
</Sheet>
|
||||
|
||||
<Sheet
|
||||
modal
|
||||
open={algorithmSheetOpen}
|
||||
onOpenChange={setAlgorithmSheetOpen}
|
||||
snapPoints={[40]}
|
||||
animation="medium"
|
||||
disableDrag
|
||||
>
|
||||
<Sheet.Overlay />
|
||||
<Sheet.Frame bg={bgWhite} borderTopLeftRadius="$9" borderTopRightRadius="$9">
|
||||
<YStack p="$4">
|
||||
<XStack ai="center" jc="space-between" mb="$4">
|
||||
<Text fontSize="$8">Select an algorithm</Text>
|
||||
<XStack onPress={() => setAlgorithmSheetOpen(false)} p="$2">
|
||||
<X color={borderColor} size="$1.5" mr="$2" />
|
||||
</XStack>
|
||||
</XStack>
|
||||
<Separator borderColor={separatorColor} mb="$4" />
|
||||
<ScrollView showsVerticalScrollIndicator={false}>
|
||||
{["rsa sha256", "rsa sha1", "rsapss sha256"].map((algorithm) => (
|
||||
<TouchableOpacity
|
||||
key={algorithm}
|
||||
onPress={() => {
|
||||
handleAlgorithmSelect(algorithm);
|
||||
setAlgorithmSheetOpen(false);
|
||||
}}
|
||||
>
|
||||
<XStack py="$3" px="$2">
|
||||
<Text fontSize="$4">{algorithm}</Text>
|
||||
</XStack>
|
||||
</TouchableOpacity>
|
||||
))}
|
||||
</ScrollView>
|
||||
</YStack>
|
||||
</Sheet.Frame>
|
||||
</Sheet>
|
||||
|
||||
</YStack>
|
||||
|
||||
<Tabs f={1} orientation="horizontal" flexDirection="column" defaultValue={"splash"}
|
||||
@@ -789,7 +882,12 @@ const MainScreen: React.FC = () => {
|
||||
/>
|
||||
</Tabs.Content>
|
||||
<Tabs.Content value="mock" f={1}>
|
||||
<MockDataScreen />
|
||||
<MockDataScreen
|
||||
onCountryPress={() => setCountrySheetOpen(true)}
|
||||
onAlgorithmPress={() => setAlgorithmSheetOpen(true)}
|
||||
selectedCountry={selectedCountry}
|
||||
selectedAlgorithm={selectedAlgorithm}
|
||||
/>
|
||||
</Tabs.Content>
|
||||
<Tabs.Content value="scan" f={1}>
|
||||
<CameraScreen
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { useState, useCallback, useMemo } from 'react';
|
||||
import React, { useState, useCallback, useMemo, useRef } from 'react';
|
||||
import { YStack, XStack, Text, Select, Adapt, Sheet, Fieldset, Button, Spinner, Switch } from 'tamagui';
|
||||
import { Check, ChevronDown, ChevronUp, Cpu, Minus, Plus } from '@tamagui/lucide-icons';
|
||||
import { Check, ChevronDown, ChevronUp, Cpu, Minus, Plus, X } from '@tamagui/lucide-icons';
|
||||
import { bgColor, bgGreen, bgGreen2, blueColor, borderColor, greenColorLight, redColorDark, textBlack } from '../utils/colors';
|
||||
import useUserStore from '../stores/userStore';
|
||||
import useNavigationStore from '../stores/navigationStore';
|
||||
@@ -9,14 +9,23 @@ import { genMockPassportData } from '../../../common/src/utils/genMockPassportDa
|
||||
import { countryCodes } from '../../../common/src/constants/constants';
|
||||
import getCountryISO2 from "country-iso-3-to-2";
|
||||
import { flag } from 'country-emoji';
|
||||
import { StyleSheet, TouchableOpacity, View } from 'react-native';
|
||||
|
||||
const MockDataScreen: React.FC = () => {
|
||||
const [signatureAlgorithm, setSignatureAlgorithm] = useState("rsa_sha256");
|
||||
const listOfSignatureAlgorithms = ["rsa_sha1", "rsa_sha256", "rsapss_sha256"];
|
||||
interface MockDataScreenProps {
|
||||
onCountryPress: () => void;
|
||||
onAlgorithmPress: () => void;
|
||||
selectedCountry: string;
|
||||
selectedAlgorithm: string;
|
||||
}
|
||||
|
||||
const MockDataScreen: React.FC<MockDataScreenProps> = ({
|
||||
onCountryPress,
|
||||
onAlgorithmPress,
|
||||
selectedCountry,
|
||||
selectedAlgorithm,
|
||||
}) => {
|
||||
const [age, setAge] = useState(24);
|
||||
const [expiryYears, setExpiryYears] = useState(5);
|
||||
const [nationality, setNationality] = useState("FRA");
|
||||
const [isGenerating, setIsGenerating] = useState(false);
|
||||
const [isInOfacList, setIsInOfacList] = useState(false);
|
||||
const castDate = (yearsOffset: number) => {
|
||||
@@ -26,6 +35,11 @@ const MockDataScreen: React.FC = () => {
|
||||
};
|
||||
|
||||
const { toast } = useNavigationStore();
|
||||
const signatureAlgorithmToStrictSignatureAlgorithm = {
|
||||
"rsa sha256": "rsa_sha256",
|
||||
"rsa sha1": "rsa_sha1",
|
||||
"rsapss sha256": "rsapss_sha256"
|
||||
} as const;
|
||||
|
||||
const handleGenerate = useCallback(async () => {
|
||||
setIsGenerating(true);
|
||||
@@ -34,18 +48,18 @@ const MockDataScreen: React.FC = () => {
|
||||
let mockPassportData;
|
||||
if (isInOfacList) {
|
||||
mockPassportData = genMockPassportData(
|
||||
signatureAlgorithm as "rsa_sha256" | "rsa_sha1" | "rsapss_sha256",
|
||||
nationality as keyof typeof countryCodes,
|
||||
signatureAlgorithmToStrictSignatureAlgorithm[selectedAlgorithm as keyof typeof signatureAlgorithmToStrictSignatureAlgorithm],
|
||||
selectedCountry as keyof typeof countryCodes,
|
||||
castDate(-age),
|
||||
castDate(expiryYears),
|
||||
randomPassportNumber,
|
||||
'HENAO MONTOYA',
|
||||
'HENAO MONTOYA', // this name is the OFAC list
|
||||
'ARCANGEL DE JESUS'
|
||||
);
|
||||
} else {
|
||||
mockPassportData = genMockPassportData(
|
||||
signatureAlgorithm as "rsa_sha256" | "rsa_sha1" | "rsapss_sha256",
|
||||
nationality as keyof typeof countryCodes,
|
||||
signatureAlgorithmToStrictSignatureAlgorithm[selectedAlgorithm as keyof typeof signatureAlgorithmToStrictSignatureAlgorithm],
|
||||
selectedCountry as keyof typeof countryCodes,
|
||||
castDate(-age),
|
||||
castDate(expiryYears),
|
||||
randomPassportNumber,
|
||||
@@ -65,149 +79,30 @@ const MockDataScreen: React.FC = () => {
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
useNavigationStore.getState().setSelectedTab("next");
|
||||
}, [signatureAlgorithm, nationality, age, expiryYears, isInOfacList]);
|
||||
const countryOptions = useMemo(() => {
|
||||
return Object.keys(countryCodes).map((countryCode, index) => ({
|
||||
countryCode,
|
||||
countryName: countryCodes[countryCode as keyof typeof countryCodes],
|
||||
flagEmoji: flag(getCountryISO2(countryCode)),
|
||||
index,
|
||||
}));
|
||||
}, []);
|
||||
}, [selectedAlgorithm, selectedCountry, age, expiryYears, isInOfacList]);
|
||||
|
||||
return (
|
||||
<YStack f={1} gap="$4" >
|
||||
<Text my="$9" textAlign="center" fontSize="$9" color={textBlack}>Generate passport data</Text>
|
||||
<XStack ai="center" >
|
||||
<Text f={1} fontSize="$5">
|
||||
Encryption
|
||||
</Text>
|
||||
<Select
|
||||
id="signature-algorithm"
|
||||
value={signatureAlgorithm}
|
||||
onValueChange={setSignatureAlgorithm}
|
||||
native
|
||||
>
|
||||
<Select.Trigger w="$16" iconAfter={ChevronDown}>
|
||||
<Select.Value placeholder="Select algorithm" />
|
||||
</Select.Trigger>
|
||||
|
||||
<Adapt when="sm" platform="touch">
|
||||
<Sheet
|
||||
modal
|
||||
dismissOnSnapToBottom
|
||||
animationConfig={{
|
||||
type: 'spring',
|
||||
damping: 20,
|
||||
mass: 1.2,
|
||||
stiffness: 250,
|
||||
}}
|
||||
>
|
||||
<Sheet.Frame>
|
||||
<Text fontSize="$8" p="$3">Encryption method</Text>
|
||||
<Sheet.ScrollView>
|
||||
<Adapt.Contents />
|
||||
</Sheet.ScrollView>
|
||||
</Sheet.Frame>
|
||||
<Sheet.Overlay
|
||||
animation="lazy"
|
||||
enterStyle={{ opacity: 0 }}
|
||||
exitStyle={{ opacity: 0 }}
|
||||
/>
|
||||
</Sheet>
|
||||
</Adapt>
|
||||
|
||||
<Select.Content zIndex={200000}>
|
||||
<Select.ScrollUpButton ai="center" jc="center" pos="relative" w="100%" h="$3">
|
||||
<ChevronUp size={20} />
|
||||
</Select.ScrollUpButton>
|
||||
|
||||
<Select.Viewport minWidth={200}>
|
||||
<Select.Group>
|
||||
{listOfSignatureAlgorithms.map((algorithm, index) => (
|
||||
<Select.Item key={algorithm} index={index} value={algorithm}>
|
||||
<Select.ItemText >{algorithm}</Select.ItemText>
|
||||
<Select.ItemIndicator marginLeft="auto">
|
||||
<Check size={16} />
|
||||
</Select.ItemIndicator>
|
||||
</Select.Item>
|
||||
))}
|
||||
</Select.Group>
|
||||
</Select.Viewport>
|
||||
|
||||
<Select.ScrollDownButton ai="center" jc="center" pos="relative" w="100%" h="$3">
|
||||
<ChevronDown size={20} />
|
||||
</Select.ScrollDownButton>
|
||||
</Select.Content>
|
||||
</Select>
|
||||
<XStack ai="center">
|
||||
<Text f={1} fontSize="$5">Encryption</Text>
|
||||
<Button onPress={onAlgorithmPress} p="$2" px="$3" bg="white" borderColor={borderColor} borderWidth={1} borderRadius="$4" >
|
||||
<XStack ai="center" gap="$2">
|
||||
<Text fontSize="$4">{selectedAlgorithm}</Text>
|
||||
<ChevronDown size={20} />
|
||||
</XStack>
|
||||
</Button>
|
||||
</XStack>
|
||||
|
||||
|
||||
<XStack ai="center" gap="$2">
|
||||
<Text f={1} fontSize="$5">
|
||||
Nationality
|
||||
</Text>
|
||||
<Select
|
||||
id="nationality"
|
||||
value={nationality}
|
||||
onValueChange={setNationality}
|
||||
native
|
||||
>
|
||||
<Select.Trigger width="$16" iconAfter={ChevronDown}>
|
||||
<Select.Value placeholder="Select algorithm" />
|
||||
</Select.Trigger>
|
||||
|
||||
<Adapt when="sm" platform="touch">
|
||||
<Sheet
|
||||
modal
|
||||
dismissOnSnapToBottom
|
||||
animationConfig={{
|
||||
type: 'spring',
|
||||
damping: 20,
|
||||
mass: 1.2,
|
||||
stiffness: 250,
|
||||
}}
|
||||
>
|
||||
<Sheet.Frame >
|
||||
<Text fontSize="$8" p="$3">Nationality</Text>
|
||||
<Sheet.ScrollView>
|
||||
<Adapt.Contents />
|
||||
</Sheet.ScrollView>
|
||||
</Sheet.Frame>
|
||||
<Sheet.Overlay
|
||||
animation="lazy"
|
||||
enterStyle={{ opacity: 0 }}
|
||||
exitStyle={{ opacity: 0 }}
|
||||
/>
|
||||
</Sheet>
|
||||
</Adapt>
|
||||
|
||||
<Select.Content zIndex={200000}>
|
||||
<Select.ScrollUpButton ai="center" jc="center" pos="relative" w="100%" h="$3">
|
||||
<ChevronUp size={20} />
|
||||
</Select.ScrollUpButton>
|
||||
|
||||
<Select.Viewport minWidth={200}>
|
||||
<Select.Group>
|
||||
{countryOptions.map(({ countryCode, countryName, flagEmoji, index }) => (
|
||||
<Select.Item key={countryCode} index={index} value={countryCode}>
|
||||
<Select.ItemText>{countryName} {flagEmoji}</Select.ItemText>
|
||||
<Select.ItemIndicator marginLeft="auto">
|
||||
<Check size={16} />
|
||||
</Select.ItemIndicator>
|
||||
</Select.Item>
|
||||
))}
|
||||
</Select.Group>
|
||||
</Select.Viewport>
|
||||
|
||||
<Select.ScrollDownButton ai="center" jc="center" pos="relative" w="100%" h="$3">
|
||||
<ChevronDown size={20} />
|
||||
</Select.ScrollDownButton>
|
||||
</Select.Content>
|
||||
</Select>
|
||||
<XStack ai="center">
|
||||
<Text f={1} fontSize="$5">Nationality</Text>
|
||||
<Button onPress={onCountryPress} p="$2" px="$3" bg="white" borderColor={borderColor} borderWidth={1} borderRadius="$4">
|
||||
<XStack ai="center" gap="$2">
|
||||
<Text fontSize="$4">{countryCodes[selectedCountry as keyof typeof countryCodes]} {flag(getCountryISO2(selectedCountry))}</Text>
|
||||
<ChevronDown size={20} />
|
||||
</XStack>
|
||||
</Button>
|
||||
</XStack>
|
||||
|
||||
|
||||
|
||||
<Fieldset mt="$2" gap="$2" horizontal>
|
||||
<Text color={textBlack} width={160} justifyContent="flex-end" fontSize="$5">
|
||||
Age (🎂)
|
||||
|
||||
@@ -245,7 +245,7 @@ const ProveScreen: React.FC<ProveScreenProps> = ({ setSheetRegisterIsOpen }) =>
|
||||
);
|
||||
|
||||
return (
|
||||
<YStack f={1} p="$3" pt="$8">
|
||||
<YStack f={1} pt="$8">
|
||||
{hasEnabledDisclosureOptions ? (
|
||||
<YStack mt="$4">
|
||||
<Text fontSize="$9">
|
||||
|
||||
@@ -8,7 +8,7 @@ import { QrCode } from '@tamagui/lucide-icons';
|
||||
|
||||
const SuccessScreen: React.FC = () => {
|
||||
return (
|
||||
<YStack f={1} p="$3">
|
||||
<YStack f={1} >
|
||||
<YStack f={1} mt="$8">
|
||||
<Text ml="$1" fontSize="$10" color={textBlack}><Text style={{ textDecorationLine: 'underline', textDecorationColor: bgGreen }}>Success</Text>, the proof has been verified</Text>
|
||||
<XStack f={1} />
|
||||
|
||||
@@ -31,7 +31,7 @@ const WrongProofScreen: React.FC = () => {
|
||||
console.log('Failed conditions:', JSON.stringify(failedConditions));
|
||||
|
||||
return (
|
||||
<YStack f={1} p="$3">
|
||||
<YStack f={1} >
|
||||
<YStack f={1} mt="$4" >
|
||||
<Text ml="$1" fontSize={34} color={textBlack}>
|
||||
<Text style={{ textDecorationLine: 'underline', textDecorationColor: bgGreen }}>Oops</Text>, the proof is not valid.
|
||||
|
||||
1696
app/yarn.lock
1696
app/yarn.lock
File diff suppressed because it is too large
Load Diff
5
circuits/circuits/tests/utils/customHasher_tester.circom
Normal file
5
circuits/circuits/tests/utils/customHasher_tester.circom
Normal file
@@ -0,0 +1,5 @@
|
||||
pragma circom 2.1.6;
|
||||
|
||||
include "../../utils/passport/customHashers.circom";
|
||||
|
||||
component main = CustomHasher(16);
|
||||
@@ -0,0 +1,438 @@
|
||||
pragma circom 2.1.6;
|
||||
|
||||
include "./circomPairing/curve.circom";
|
||||
include "./brainpoolFunc.circom";
|
||||
include "./brainpoolPows.circom";
|
||||
include "circomlib/circuits/multiplexer.circom";
|
||||
include "circomlib/circuits/bitify.circom";
|
||||
include "circomlib/circuits/comparators.circom";
|
||||
include "../utils/func.circom";
|
||||
|
||||
template BrainpoolScalarMult(CHUNK_SIZE, CHUNK_NUMBER){
|
||||
signal input scalar[CHUNK_NUMBER];
|
||||
signal input point[2][CHUNK_NUMBER];
|
||||
|
||||
signal output out[2][CHUNK_NUMBER];
|
||||
|
||||
component n2b[CHUNK_NUMBER];
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
n2b[i] = Num2Bits(CHUNK_SIZE);
|
||||
n2b[i].in <== scalar[i];
|
||||
}
|
||||
|
||||
// hasPrevNonZero[CHUNK_SIZE * i + j] == 1 if there is a nonzero bit in location [i][j] or higher order bit
|
||||
component hasPrevNonZero[CHUNK_NUMBER * CHUNK_SIZE];
|
||||
for (var i = CHUNK_NUMBER - 1; i >= 0; i--) {
|
||||
for (var j = CHUNK_SIZE - 1; j >= 0; j--) {
|
||||
hasPrevNonZero[CHUNK_SIZE * i + j] = OR();
|
||||
if (i == CHUNK_NUMBER - 1 && j == CHUNK_SIZE - 1) {
|
||||
hasPrevNonZero[CHUNK_SIZE * i + j].a <== 0;
|
||||
hasPrevNonZero[CHUNK_SIZE * i + j].b <== n2b[i].out[j];
|
||||
} else {
|
||||
hasPrevNonZero[CHUNK_SIZE * i + j].a <== hasPrevNonZero[CHUNK_SIZE * i + j + 1].out;
|
||||
hasPrevNonZero[CHUNK_SIZE * i + j].b <== n2b[i].out[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
signal partial[CHUNK_SIZE * CHUNK_NUMBER][2][CHUNK_NUMBER];
|
||||
signal intermed[CHUNK_SIZE * CHUNK_NUMBER - 1][2][CHUNK_NUMBER];
|
||||
component adders[CHUNK_SIZE * CHUNK_NUMBER - 1];
|
||||
component doublers[CHUNK_SIZE * CHUNK_NUMBER - 1];
|
||||
for (var i = CHUNK_NUMBER - 1; i >= 0; i--) {
|
||||
for (var j = CHUNK_SIZE - 1; j >= 0; j--) {
|
||||
if (i == CHUNK_NUMBER - 1 && j == CHUNK_SIZE - 1) {
|
||||
for (var idx = 0; idx < CHUNK_NUMBER; idx++) {
|
||||
partial[CHUNK_SIZE * i + j][0][idx] <== point[0][idx];
|
||||
partial[CHUNK_SIZE * i + j][1][idx] <== point[1][idx];
|
||||
}
|
||||
}
|
||||
if (i < CHUNK_NUMBER - 1 || j < CHUNK_SIZE - 1) {
|
||||
adders[CHUNK_SIZE * i + j] = BrainpoolAddUnequal(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
doublers[CHUNK_SIZE * i + j] = BrainpoolDouble(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
for (var idx = 0; idx < CHUNK_NUMBER; idx++) {
|
||||
doublers[CHUNK_SIZE * i + j].in[0][idx] <== partial[CHUNK_SIZE * i + j + 1][0][idx];
|
||||
doublers[CHUNK_SIZE * i + j].in[1][idx] <== partial[CHUNK_SIZE * i + j + 1][1][idx];
|
||||
}
|
||||
for (var idx = 0; idx < CHUNK_NUMBER; idx++) {
|
||||
adders[CHUNK_SIZE * i + j].point1[0][idx] <== doublers[CHUNK_SIZE * i + j].out[0][idx];
|
||||
adders[CHUNK_SIZE * i + j].point1[1][idx] <== doublers[CHUNK_SIZE * i + j].out[1][idx];
|
||||
adders[CHUNK_SIZE * i + j].point2[0][idx] <== point[0][idx];
|
||||
adders[CHUNK_SIZE * i + j].point2[1][idx] <== point[1][idx];
|
||||
}
|
||||
// partial[CHUNK_SIZE * i + j]
|
||||
// = hasPrevNonZero[CHUNK_SIZE * i + j + 1] * ((1 - n2b[i].out[j]) * doublers[CHUNK_SIZE * i + j] + n2b[i].out[j] * adders[CHUNK_SIZE * i + j])
|
||||
// + (1 - hasPrevNonZero[CHUNK_SIZE * i + j + 1]) * point
|
||||
for (var idx = 0; idx < CHUNK_NUMBER; idx++) {
|
||||
intermed[CHUNK_SIZE * i + j][0][idx] <== n2b[i].out[j] * (adders[CHUNK_SIZE * i + j].out[0][idx] - doublers[CHUNK_SIZE * i + j].out[0][idx]) + doublers[CHUNK_SIZE * i + j].out[0][idx];
|
||||
intermed[CHUNK_SIZE * i + j][1][idx] <== n2b[i].out[j] * (adders[CHUNK_SIZE * i + j].out[1][idx] - doublers[CHUNK_SIZE * i + j].out[1][idx]) + doublers[CHUNK_SIZE * i + j].out[1][idx];
|
||||
partial[CHUNK_SIZE * i + j][0][idx] <== hasPrevNonZero[CHUNK_SIZE * i + j + 1].out * (intermed[CHUNK_SIZE * i + j][0][idx] - point[0][idx]) + point[0][idx];
|
||||
partial[CHUNK_SIZE * i + j][1][idx] <== hasPrevNonZero[CHUNK_SIZE * i + j + 1].out * (intermed[CHUNK_SIZE * i + j][1][idx] - point[1][idx]) + point[1][idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var idx = 0; idx < CHUNK_NUMBER; idx++) {
|
||||
out[0][idx] <== partial[0][0][idx];
|
||||
out[1][idx] <== partial[0][1][idx];
|
||||
}
|
||||
}
|
||||
|
||||
template BrainpoolAddUnequal(CHUNK_SIZE, CHUNK_NUMBER){
|
||||
signal input point1[2][CHUNK_NUMBER];
|
||||
signal input point2[2][CHUNK_NUMBER];
|
||||
signal output out[2][CHUNK_NUMBER];
|
||||
|
||||
var PARAMS[3][CHUNK_NUMBER] = get_params(CHUNK_SIZE,CHUNK_NUMBER);
|
||||
|
||||
component add = EllipticCurveAddUnequal(CHUNK_SIZE, CHUNK_NUMBER, PARAMS[2]);
|
||||
add.a <== point1;
|
||||
add.b <== point2;
|
||||
add.out ==> out;
|
||||
}
|
||||
|
||||
template BrainpoolDouble(CHUNK_SIZE, CHUNK_NUMBER){
|
||||
signal input in[2][CHUNK_NUMBER];
|
||||
signal output out[2][CHUNK_NUMBER];
|
||||
|
||||
var PARAMS[3][CHUNK_NUMBER] = get_params(CHUNK_SIZE,CHUNK_NUMBER);
|
||||
|
||||
component doubling = EllipticCurveDouble(CHUNK_SIZE,CHUNK_NUMBER, PARAMS[0], PARAMS[1], PARAMS[2]);
|
||||
doubling.in <== in;
|
||||
doubling.out ==> out;
|
||||
}
|
||||
|
||||
template BrainpoolGetGenerator(CHUNK_SIZE, CHUNK_NUMBER){
|
||||
signal output gen[2][CHUNK_NUMBER];
|
||||
|
||||
gen[0][0] <== 4112880906850;
|
||||
gen[0][1] <== 8402973968522;
|
||||
gen[0][2] <== 7591230932878;
|
||||
gen[0][3] <== 4501096422359;
|
||||
gen[0][4] <== 8682220995764;
|
||||
gen[0][5] <== 1201070240662;
|
||||
|
||||
gen[1][0] <== 5253533821335;
|
||||
gen[1][1] <== 6261165491114;
|
||||
gen[1][2] <== 7738945195191;
|
||||
gen[1][3] <== 3354540412644;
|
||||
gen[1][4] <== 6237632167812;
|
||||
gen[1][5] <== 725814897543;
|
||||
}
|
||||
|
||||
template GetBrainpoolOrder(CHUNK_SIZE, CHUNK_NUMBER){
|
||||
signal output order[6];
|
||||
|
||||
order[0] <== 7157953615527;
|
||||
order[1] <== 4625125213121;
|
||||
order[2] <== 6807085551317;
|
||||
order[3] <== 5808117106360;
|
||||
order[4] <== 7604705420896;
|
||||
order[5] <== 1460132624195;
|
||||
}
|
||||
|
||||
template BrainpoolGeneratorMultiplication(CHUNK_SIZE,CHUNK_NUMBER){
|
||||
var STRIDE = 8;
|
||||
signal input scalar[CHUNK_NUMBER];
|
||||
signal output out[2][CHUNK_NUMBER];
|
||||
|
||||
component n2b[CHUNK_NUMBER];
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
n2b[i] = Num2Bits(CHUNK_SIZE);
|
||||
n2b[i].in <== scalar[i];
|
||||
}
|
||||
|
||||
var NUM_STRIDES = div_ceil(CHUNK_SIZE * CHUNK_NUMBER, STRIDE);
|
||||
// power[i][j] contains: [j * (1 << STRIDE * i) * G] for 1 <= j < (1 << STRIDE)
|
||||
var POWERS[NUM_STRIDES][2 ** STRIDE][2][CHUNK_NUMBER];
|
||||
POWERS = get_g_pow_stride8_table(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
|
||||
var DUMMY_HOLDER[2][CHUNK_NUMBER] = get_dummy_point(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
var DUMMY[2][CHUNK_NUMBER];
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) DUMMY[0][i] = DUMMY_HOLDER[0][i];
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) DUMMY[1][i] = DUMMY_HOLDER[1][i];
|
||||
|
||||
component selectors[NUM_STRIDES];
|
||||
for (var i = 0; i < NUM_STRIDES; i++) {
|
||||
selectors[i] = Bits2Num(STRIDE);
|
||||
for (var j = 0; j < STRIDE; j++) {
|
||||
var bit_idx1 = (i * STRIDE + j) \ CHUNK_SIZE;
|
||||
var bit_idx2 = (i * STRIDE + j) % CHUNK_SIZE;
|
||||
if (bit_idx1 < CHUNK_NUMBER) {
|
||||
selectors[i].in[j] <== n2b[bit_idx1].out[bit_idx2];
|
||||
} else {
|
||||
selectors[i].in[j] <== 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// multiplexers[i][l].out will be the coordinates of:
|
||||
// selectors[i].out * (2 ** (i * STRIDE)) * G if selectors[i].out is non-zero
|
||||
// (2 ** 255) * G if selectors[i].out is zero
|
||||
component multiplexers[NUM_STRIDES][2];
|
||||
// select from CHUNK_NUMBER-register outputs using a 2 ** STRIDE bit selector
|
||||
for (var i = 0; i < NUM_STRIDES; i++) {
|
||||
for (var l = 0; l < 2; l++) {
|
||||
multiplexers[i][l] = Multiplexer(CHUNK_NUMBER, (1 << STRIDE));
|
||||
multiplexers[i][l].sel <== selectors[i].out;
|
||||
for (var idx = 0; idx < CHUNK_NUMBER; idx++) {
|
||||
multiplexers[i][l].inp[0][idx] <== DUMMY[l][idx];
|
||||
for (var j = 1; j < (1 << STRIDE); j++) {
|
||||
multiplexers[i][l].inp[j][idx] <== POWERS[i][j][l][idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component isZero[NUM_STRIDES];
|
||||
for (var i = 0; i < NUM_STRIDES; i++) {
|
||||
isZero[i] = IsZero();
|
||||
isZero[i].in <== selectors[i].out;
|
||||
}
|
||||
|
||||
// hasPrevNonZero[i] = 1 if at least one of the selections in privkey up to STRIDE i is non-zero
|
||||
component hasPrevNonZero[NUM_STRIDES];
|
||||
hasPrevNonZero[0] = OR();
|
||||
hasPrevNonZero[0].a <== 0;
|
||||
hasPrevNonZero[0].b <== 1 - isZero[0].out;
|
||||
for (var i = 1; i < NUM_STRIDES; i++) {
|
||||
hasPrevNonZero[i] = OR();
|
||||
hasPrevNonZero[i].a <== hasPrevNonZero[i - 1].out;
|
||||
hasPrevNonZero[i].b <== 1 - isZero[i].out;
|
||||
}
|
||||
|
||||
signal partial[NUM_STRIDES][2][CHUNK_NUMBER];
|
||||
for (var idx = 0; idx < CHUNK_NUMBER; idx++) {
|
||||
for (var l = 0; l < 2; l++) {
|
||||
partial[0][l][idx] <== multiplexers[0][l].out[idx];
|
||||
}
|
||||
}
|
||||
|
||||
component adders[NUM_STRIDES - 1];
|
||||
signal intermed1[NUM_STRIDES - 1][2][CHUNK_NUMBER];
|
||||
signal intermed2[NUM_STRIDES - 1][2][CHUNK_NUMBER];
|
||||
for (var i = 1; i < NUM_STRIDES; i++) {
|
||||
adders[i - 1] = BrainpoolAddUnequal(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
for (var idx = 0; idx < CHUNK_NUMBER; idx++) {
|
||||
for (var l = 0; l < 2; l++) {
|
||||
adders[i - 1].point1[l][idx] <== partial[i - 1][l][idx];
|
||||
adders[i - 1].point2[l][idx] <== multiplexers[i][l].out[idx];
|
||||
}
|
||||
}
|
||||
|
||||
// partial[i] = hasPrevNonZero[i - 1] * ((1 - isZero[i]) * adders[i - 1].out + isZero[i] * partial[i - 1][0][idx])
|
||||
// + (1 - hasPrevNonZero[i - 1]) * (1 - isZero[i]) * multiplexers[i]
|
||||
for (var idx = 0; idx < CHUNK_NUMBER; idx++) {
|
||||
for (var l = 0; l < 2; l++) {
|
||||
intermed1[i - 1][l][idx] <== isZero[i].out * (partial[i - 1][l][idx] - adders[i - 1].out[l][idx]) + adders[i - 1].out[l][idx];
|
||||
intermed2[i - 1][l][idx] <== multiplexers[i][l].out[idx] - isZero[i].out * multiplexers[i][l].out[idx];
|
||||
partial[i][l][idx] <== hasPrevNonZero[i - 1].out * (intermed1[i - 1][l][idx] - intermed2[i - 1][l][idx]) + intermed2[i - 1][l][idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
for (var l = 0; l < 2; l++) {
|
||||
out[l][i] <== partial[NUM_STRIDES - 1][l][i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template BrainpoolPrecomputePipinger(CHUNK_SIZE, CHUNK_NUMBER, WINDOW_SIZE){
|
||||
signal input in[2][CHUNK_NUMBER];
|
||||
|
||||
var PRECOMPUTE_NUMBER = 2 ** WINDOW_SIZE;
|
||||
|
||||
signal output out[PRECOMPUTE_NUMBER][2][CHUNK_NUMBER];
|
||||
|
||||
for (var i = 0; i < 2; i++){
|
||||
for (var j = 0; j < CHUNK_NUMBER; j++){
|
||||
out[0][i][j] <== 0;
|
||||
}
|
||||
}
|
||||
|
||||
out[1] <== in;
|
||||
|
||||
component doublers[PRECOMPUTE_NUMBER\2 - 1];
|
||||
component adders [PRECOMPUTE_NUMBER\2 - 1];
|
||||
|
||||
for (var i = 2; i < PRECOMPUTE_NUMBER; i++){
|
||||
if (i % 2 == 0){
|
||||
doublers[i\2 - 1] = BrainpoolDouble(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
doublers[i\2 - 1].in <== out[i\2];
|
||||
doublers[i\2 - 1].out ==> out[i];
|
||||
}
|
||||
else
|
||||
{
|
||||
adders[i\2 - 1] = BrainpoolAddUnequal(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
adders[i\2 - 1].point1 <== out[1];
|
||||
adders[i\2 - 1].point2 <== out[i - 1];
|
||||
adders[i\2 - 1].out ==> out[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template BrainpoolPipingerMult(CHUNK_SIZE, CHUNK_NUMBER, WINDOW_SIZE){
|
||||
|
||||
assert(WINDOW_SIZE == 4);
|
||||
|
||||
signal input point[2][CHUNK_NUMBER];
|
||||
signal input scalar [CHUNK_NUMBER];
|
||||
|
||||
signal output out[2][CHUNK_NUMBER];
|
||||
|
||||
var PRECOMPUTE_NUMBER = 2 ** WINDOW_SIZE;
|
||||
|
||||
signal precomputed[PRECOMPUTE_NUMBER][2][CHUNK_NUMBER];
|
||||
|
||||
component precompute = BrainpoolPrecomputePipinger(CHUNK_SIZE, CHUNK_NUMBER, WINDOW_SIZE);
|
||||
precompute.in <== point;
|
||||
precompute.out ==> precomputed;
|
||||
|
||||
var DOUBLERS_NUMBER = 256 - WINDOW_SIZE;
|
||||
var ADDERS_NUMBER = 256 \ WINDOW_SIZE;
|
||||
|
||||
component doublers[DOUBLERS_NUMBER];
|
||||
component adders [ADDERS_NUMBER];
|
||||
component bits2Num[ADDERS_NUMBER];
|
||||
component num2Bits[CHUNK_NUMBER];
|
||||
|
||||
signal res [ADDERS_NUMBER + 1][2][CHUNK_NUMBER];
|
||||
|
||||
signal tmp [ADDERS_NUMBER][PRECOMPUTE_NUMBER][2][CHUNK_NUMBER];
|
||||
|
||||
signal tmp2[ADDERS_NUMBER] [2] [CHUNK_NUMBER];
|
||||
signal tmp3[ADDERS_NUMBER] [2][2][CHUNK_NUMBER];
|
||||
signal tmp4[ADDERS_NUMBER] [2] [CHUNK_NUMBER];
|
||||
signal tmp5[ADDERS_NUMBER] [2][2][CHUNK_NUMBER];
|
||||
signal tmp6[ADDERS_NUMBER - 1][2][2][CHUNK_NUMBER];
|
||||
signal tmp7[ADDERS_NUMBER - 1][2] [CHUNK_NUMBER];
|
||||
|
||||
component equals [ADDERS_NUMBER][PRECOMPUTE_NUMBER][2][CHUNK_NUMBER];
|
||||
component zeroEquals[ADDERS_NUMBER];
|
||||
component tmpEquals [ADDERS_NUMBER];
|
||||
|
||||
component g = BrainpoolGetGenerator(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
signal gen[2][CHUNK_NUMBER];
|
||||
gen <== g.gen;
|
||||
|
||||
signal scalarBits[256];
|
||||
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++){
|
||||
num2Bits[i] = Num2Bits(CHUNK_SIZE);
|
||||
num2Bits[i].in <== scalar[i];
|
||||
if (i != CHUNK_NUMBER - 1){
|
||||
for (var j = 0; j < CHUNK_SIZE; j++){
|
||||
scalarBits[256 - CHUNK_SIZE * (i + 1) + j] <== num2Bits[i].out[CHUNK_SIZE - 1 - j];
|
||||
}
|
||||
} else {
|
||||
for (var j = 0; j < CHUNK_SIZE - (CHUNK_SIZE*CHUNK_NUMBER - 256); j++){
|
||||
scalarBits[j] <== num2Bits[i].out[CHUNK_SIZE - 1 - (j + (CHUNK_SIZE * CHUNK_NUMBER - 256))];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
res[0] <== precomputed[0];
|
||||
|
||||
for (var i = 0; i < 256; i += WINDOW_SIZE){
|
||||
adders[i\WINDOW_SIZE] = BrainpoolAddUnequal(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
bits2Num[i\WINDOW_SIZE] = Bits2Num(WINDOW_SIZE);
|
||||
for (var j = 0; j < WINDOW_SIZE; j++){
|
||||
bits2Num[i\WINDOW_SIZE].in[j] <== scalarBits[i + (WINDOW_SIZE - 1) - j];
|
||||
}
|
||||
|
||||
tmpEquals[i\WINDOW_SIZE] = IsEqual();
|
||||
tmpEquals[i\WINDOW_SIZE].in[0] <== 0;
|
||||
tmpEquals[i\WINDOW_SIZE].in[1] <== res[i\WINDOW_SIZE][0][0];
|
||||
|
||||
if (i != 0){
|
||||
for (var j = 0; j < WINDOW_SIZE; j++){
|
||||
doublers[i + j - WINDOW_SIZE] = BrainpoolDouble(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
|
||||
if (j == 0){
|
||||
for (var axis_idx = 0; axis_idx < 2; axis_idx++){
|
||||
for (var coor_idx = 0; coor_idx < 6; coor_idx ++){
|
||||
tmp6[i\WINDOW_SIZE - 1][0][axis_idx][coor_idx] <== tmpEquals[i\WINDOW_SIZE].out * gen[axis_idx][coor_idx];
|
||||
tmp6[i\WINDOW_SIZE - 1][1][axis_idx][coor_idx] <== (1 - tmpEquals[i\WINDOW_SIZE].out) * res[i\WINDOW_SIZE][axis_idx][coor_idx];
|
||||
tmp7[i\WINDOW_SIZE - 1] [axis_idx][coor_idx] <== tmp6[i\WINDOW_SIZE - 1][0][axis_idx][coor_idx]
|
||||
+ tmp6[i\WINDOW_SIZE - 1][1][axis_idx][coor_idx];
|
||||
}
|
||||
}
|
||||
|
||||
doublers[i + j - WINDOW_SIZE].in <== tmp7[i\WINDOW_SIZE - 1];
|
||||
}
|
||||
else
|
||||
{
|
||||
doublers[i + j - WINDOW_SIZE].in <== doublers[i + j - 1 - WINDOW_SIZE].out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var point_idx = 0; point_idx < PRECOMPUTE_NUMBER; point_idx++){
|
||||
for (var axis_idx = 0; axis_idx < 2; axis_idx++){
|
||||
for (var coor_idx = 0; coor_idx < CHUNK_NUMBER; coor_idx++){
|
||||
equals[i\WINDOW_SIZE][point_idx][axis_idx][coor_idx] = IsEqual();
|
||||
equals[i\WINDOW_SIZE][point_idx][axis_idx][coor_idx].in[0] <== point_idx;
|
||||
equals[i\WINDOW_SIZE][point_idx][axis_idx][coor_idx].in[1] <== bits2Num[i\WINDOW_SIZE].out;
|
||||
tmp [i\WINDOW_SIZE][point_idx][axis_idx][coor_idx] <== precomputed[point_idx][axis_idx][coor_idx] *
|
||||
equals[i\WINDOW_SIZE][point_idx][axis_idx][coor_idx].out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var axis_idx = 0; axis_idx < 2; axis_idx++){
|
||||
for (var coor_idx = 0; coor_idx < CHUNK_NUMBER; coor_idx++){
|
||||
tmp2[i\WINDOW_SIZE] [axis_idx][coor_idx] <==
|
||||
tmp[i\WINDOW_SIZE][0] [axis_idx][coor_idx] +
|
||||
tmp[i\WINDOW_SIZE][1] [axis_idx][coor_idx] +
|
||||
tmp[i\WINDOW_SIZE][2] [axis_idx][coor_idx] +
|
||||
tmp[i\WINDOW_SIZE][3] [axis_idx][coor_idx] +
|
||||
tmp[i\WINDOW_SIZE][4] [axis_idx][coor_idx] +
|
||||
tmp[i\WINDOW_SIZE][5] [axis_idx][coor_idx] +
|
||||
tmp[i\WINDOW_SIZE][6] [axis_idx][coor_idx] +
|
||||
tmp[i\WINDOW_SIZE][7] [axis_idx][coor_idx] +
|
||||
tmp[i\WINDOW_SIZE][8] [axis_idx][coor_idx] +
|
||||
tmp[i\WINDOW_SIZE][9] [axis_idx][coor_idx] +
|
||||
tmp[i\WINDOW_SIZE][10][axis_idx][coor_idx] +
|
||||
tmp[i\WINDOW_SIZE][11][axis_idx][coor_idx] +
|
||||
tmp[i\WINDOW_SIZE][12][axis_idx][coor_idx] +
|
||||
tmp[i\WINDOW_SIZE][13][axis_idx][coor_idx] +
|
||||
tmp[i\WINDOW_SIZE][14][axis_idx][coor_idx] +
|
||||
tmp[i\WINDOW_SIZE][15][axis_idx][coor_idx];
|
||||
}
|
||||
}
|
||||
|
||||
if (i == 0){
|
||||
|
||||
adders[i\WINDOW_SIZE].point1 <== res [i\WINDOW_SIZE];
|
||||
adders[i\WINDOW_SIZE].point2 <== tmp2[i\WINDOW_SIZE];
|
||||
res[i\WINDOW_SIZE + 1] <== tmp2[i\WINDOW_SIZE];
|
||||
|
||||
} else {
|
||||
|
||||
adders[i\WINDOW_SIZE].point1 <== doublers[i - 1].out;
|
||||
adders[i\WINDOW_SIZE].point2 <== tmp2[i\WINDOW_SIZE];
|
||||
|
||||
zeroEquals[i\WINDOW_SIZE] = IsEqual();
|
||||
|
||||
zeroEquals[i\WINDOW_SIZE].in[0]<== 0;
|
||||
zeroEquals[i\WINDOW_SIZE].in[1]<== tmp2[i\WINDOW_SIZE][0][0];
|
||||
|
||||
for (var axis_idx = 0; axis_idx < 2; axis_idx++){
|
||||
for(var coor_idx = 0; coor_idx < CHUNK_NUMBER; coor_idx++){
|
||||
|
||||
tmp3[i\WINDOW_SIZE][0][axis_idx][coor_idx] <== adders [i\WINDOW_SIZE].out[axis_idx][coor_idx] * (1 - zeroEquals[i\WINDOW_SIZE].out);
|
||||
tmp3[i\WINDOW_SIZE][1][axis_idx][coor_idx] <== zeroEquals[i\WINDOW_SIZE].out * doublers[i-1].out[axis_idx][coor_idx];
|
||||
tmp4[i\WINDOW_SIZE] [axis_idx][coor_idx] <== tmp3[i\WINDOW_SIZE][0][axis_idx][coor_idx] + tmp3[i\WINDOW_SIZE][1][axis_idx][coor_idx];
|
||||
tmp5[i\WINDOW_SIZE][0][axis_idx][coor_idx] <== (1 - tmpEquals[i\WINDOW_SIZE].out) * tmp4[i\WINDOW_SIZE] [axis_idx][coor_idx];
|
||||
tmp5[i\WINDOW_SIZE][1][axis_idx][coor_idx] <== tmpEquals[i\WINDOW_SIZE].out * tmp2[i\WINDOW_SIZE] [axis_idx][coor_idx];
|
||||
|
||||
res[i\WINDOW_SIZE + 1][axis_idx][coor_idx] <== tmp5[i\WINDOW_SIZE][0][axis_idx][coor_idx] + tmp5[i\WINDOW_SIZE][1][axis_idx][coor_idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
out <== res[ADDERS_NUMBER];
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
pragma circom 2.1.6;
|
||||
|
||||
function get_order(CHUNK_SIZE,CHUNK_NUMBER){
|
||||
|
||||
assert ((CHUNK_SIZE == 43) && (CHUNK_NUMBER == 6));
|
||||
|
||||
var ORDER[6];
|
||||
|
||||
ORDER[0] = 7157953615527;
|
||||
ORDER[1] = 4625125213121;
|
||||
ORDER[2] = 6807085551317;
|
||||
ORDER[3] = 5808117106360;
|
||||
ORDER[4] = 7604705420896;
|
||||
ORDER[5] = 1460132624195;
|
||||
|
||||
return ORDER;
|
||||
}
|
||||
|
||||
function get_params(CHUNK_SIZE,CHUNK_NUMBER){
|
||||
|
||||
assert ((CHUNK_SIZE == 43) && (CHUNK_NUMBER == 6));
|
||||
|
||||
var A[6];
|
||||
var P[6];
|
||||
var B[6];
|
||||
|
||||
var PARAMS[3][6];
|
||||
|
||||
A[0] = 3594672715225;
|
||||
A[1] = 3897911224649;
|
||||
A[2] = 8718806090907;
|
||||
A[3] = 2852407574515;
|
||||
A[4] = 3036634083175;
|
||||
A[5] = 1076762962936;
|
||||
|
||||
B[0] = 4505413093302;
|
||||
B[1] = 7680371292571;
|
||||
B[2] = 6966711199091;
|
||||
B[3] = 3216358751839;
|
||||
B[4] = 5105446236939;
|
||||
B[5] = 333811603922;
|
||||
|
||||
|
||||
P[0] = 125081375607;
|
||||
P[1] = 5239944249961;
|
||||
P[2] = 1893809557332;
|
||||
P[3] = 5808117106361;
|
||||
P[4] = 7604705420896;
|
||||
P[5] = 1460132624195;
|
||||
|
||||
PARAMS[0] = A;
|
||||
PARAMS[1] = B;
|
||||
PARAMS[2] = P;
|
||||
|
||||
return PARAMS;
|
||||
}
|
||||
|
||||
function get_dummy_point(CHUNK_SIZE,CHUNK_NUMBER){
|
||||
|
||||
assert ((CHUNK_SIZE == 43) && (CHUNK_NUMBER == 6));
|
||||
|
||||
var G_X[6];
|
||||
G_X[0] = 3388304235908;
|
||||
G_X[1] = 4032733930425;
|
||||
G_X[2] = 7365882166150;
|
||||
G_X[3] = 7483795453108;
|
||||
G_X[4] = 4735531870343;
|
||||
G_X[5] = 20153295753;
|
||||
|
||||
var G_Y[6];
|
||||
G_Y[0] = 2121852786555;
|
||||
G_Y[1] = 2023660523771;
|
||||
G_Y[2] = 377054420119;
|
||||
G_Y[3] = 2147401067624;
|
||||
G_Y[4] = 1307469495555;
|
||||
G_Y[5] = 559867427305;
|
||||
|
||||
var DUMMY[2][6];
|
||||
DUMMY[0] = G_X;
|
||||
DUMMY[1] = G_Y;
|
||||
return DUMMY;
|
||||
}
|
||||
|
||||
|
||||
109833
circuits/circuits/utils/brainpool/brainpoolP256r1/brainpoolPows.circom
Normal file
109833
circuits/circuits/utils/brainpool/brainpoolP256r1/brainpoolPows.circom
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,901 @@
|
||||
pragma circom 2.0.3;
|
||||
|
||||
include "../../../bigInt/bigInt.circom";
|
||||
include "../../../bigInt/bigIntFunc.circom";
|
||||
include "fp.circom";
|
||||
include "fp2.circom";
|
||||
include "curve.circom";
|
||||
include "curveFp2.circom";
|
||||
include "bls12_381Func.circom";
|
||||
|
||||
/*
|
||||
implementation of optimized simplified SWU map to BLS12-381 G2
|
||||
following Wahby-Boneh: Section 4.2 of https://eprint.iacr.org/2019/403.pdf
|
||||
Python reference code: https://github.com/algorand/bls_sigs_ref/blob/master/python-impl/opt_swu_g2.py
|
||||
Additional exposition: https://hackmd.io/@benjaminion/bls12-381#Simplified-SWU-map
|
||||
|
||||
E2 is y^2 = x^3 + 4(1+u) over Fp2
|
||||
E2' is A curve of form y^2 = x^3 + A x + B that is 3-isogenous to E2
|
||||
Constants are A = 240 u, B = 1012 + 1012 u where u = sqrt( - 1)
|
||||
*/
|
||||
|
||||
// Simplified SWU map, optimized and adapted to E2'
|
||||
// in = t: 2 x CHUNK_NUMBER array, element of Fp2
|
||||
// out: 2 x 2 x CHUNK_NUMBER array, point (out[0], out[1]) on curve E2'
|
||||
//
|
||||
// This is osswu2_help(t) in Python reference code
|
||||
// See Section 4.2 of Wahby-Boneh: https://eprint.iacr.org/2019/403.pdf
|
||||
// circom implementation is slightly different since sqrt and inversion are cheap
|
||||
template OptSimpleSWU2(CHUNK_SIZE, CHUNK_NUMBER){
|
||||
signal input in[2][CHUNK_NUMBER];
|
||||
signal output out[2][2][CHUNK_NUMBER];
|
||||
//signal output isInfinity; optimized simple SWU should never return point at infinity, exceptional case still returns A normal point
|
||||
|
||||
var P[150] = get_BLS12_381_prime(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
|
||||
var A[2] = [0, 240];
|
||||
var B[2] = [1012, 1012];
|
||||
|
||||
// distinguished non-square in Fp2 for SWU map: XI = - 2 - u
|
||||
// this is Z in the suite 8.8.2 of https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-14#section-10
|
||||
var XI[2] = [ - 2, - 1];
|
||||
|
||||
var LOGK = log_ceil(CHUNK_NUMBER);
|
||||
// in = t, compute t^2, t^3
|
||||
component tSQ = SignedFp2MultiplyNoCarryCompress(CHUNK_SIZE, CHUNK_NUMBER, P, CHUNK_SIZE, 3 * CHUNK_SIZE + 2 * LOGK + 1);
|
||||
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
tSQ.a[i][idx] <== in[i][idx];
|
||||
tSQ.b[i][idx] <== in[i][idx];
|
||||
}
|
||||
}
|
||||
// compute XI * t^2
|
||||
component xiTSq = SignedFp2CarryModP(CHUNK_SIZE, CHUNK_NUMBER, 3 * CHUNK_SIZE + 2 * LOGK + 3, P);
|
||||
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
xiTSq.in[0][idx] <== XI[0] * tSQ.out[0][idx] - XI[1] * tSQ.out[1][idx];
|
||||
xiTSq.in[1][idx] <== XI[0] * tSQ.out[1][idx] + XI[1] * tSQ.out[0][idx];
|
||||
}
|
||||
|
||||
// XI^2 * t^4
|
||||
component xi2T4 = SignedFp2MultiplyNoCarryCompress(CHUNK_SIZE, CHUNK_NUMBER, P, CHUNK_SIZE, 3 * CHUNK_SIZE + 2 * LOGK + 1);
|
||||
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
xi2T4.a[i][idx] <== xiTSq.out[i][idx];
|
||||
xi2T4.b[i][idx] <== xiTSq.out[i][idx];
|
||||
}
|
||||
}
|
||||
// XI^2 * t^4 + XI * t^2
|
||||
var NUM_DEN_COMMON[2][CHUNK_NUMBER];
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
NUM_DEN_COMMON[i][idx] = xi2T4.out[i][idx] + xiTSq.out[i][idx];
|
||||
}
|
||||
}
|
||||
// x0(t) = B (XI^2 * t^4 + XI * t^2 + 1) / (-A * (XI^2 * t^4 + XI * t^2))
|
||||
component x0Den = SignedFp2CarryModP(CHUNK_SIZE, CHUNK_NUMBER, 3 * CHUNK_SIZE + 2 * LOGK + 2 + 9, P);
|
||||
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
x0Den.in[0][idx] <== -A[0] * NUM_DEN_COMMON[0][idx] + A[1] * NUM_DEN_COMMON[1][idx];
|
||||
x0Den.in[1][idx] <== -A[0] * NUM_DEN_COMMON[1][idx] - A[1] * NUM_DEN_COMMON[0][idx];
|
||||
}
|
||||
|
||||
// if x0Den = 0, replace with X1_DEN = A * XI; this way x1(t) = x0Num / X1_DEN = B / (XI * A)
|
||||
// X1_DEN = A * XI = 240 - 480 i
|
||||
assert(CHUNK_SIZE == 55 && CHUNK_NUMBER == 7);
|
||||
|
||||
var X1_DEN[2][CHUNK_NUMBER];
|
||||
if(CHUNK_SIZE == 55 && CHUNK_NUMBER == 7){
|
||||
X1_DEN = [
|
||||
[240,0,0,0,0,0,0],
|
||||
[35747322042230987,36025922209447795,1084959616957103,7925923977987733,16551456537884751,23443114579904617,1829881462546425]
|
||||
];
|
||||
}
|
||||
|
||||
// Exception if x0Den = 0:
|
||||
component exception = Fp2IsZero(CHUNK_SIZE, CHUNK_NUMBER, P);
|
||||
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
exception.in[i][idx] <== x0Den.out[i][idx];
|
||||
}
|
||||
}
|
||||
//isInfinity <== exception.out;
|
||||
|
||||
NUM_DEN_COMMON[0][0]++;
|
||||
component x0Num = SignedFp2CarryModP(CHUNK_SIZE, CHUNK_NUMBER, 3 * CHUNK_SIZE + 2 * LOGK + 2 + 11, P);
|
||||
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
x0Num.in[0][idx] <== B[0] * NUM_DEN_COMMON[0][idx] - B[1] * NUM_DEN_COMMON[1][idx];
|
||||
x0Num.in[1][idx] <== B[0] * NUM_DEN_COMMON[1][idx] + B[1] * NUM_DEN_COMMON[0][idx];
|
||||
}
|
||||
// division is same cost/constraints as multiplication, so we will compute x0 and avoid using projective coordinates
|
||||
component x0 = SignedFp2Divide(CHUNK_SIZE, CHUNK_NUMBER, CHUNK_SIZE, CHUNK_SIZE, P);
|
||||
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
x0.a[i][idx] <== x0Num.out[i][idx];
|
||||
x0.b[i][idx] <== x0Den.out[i][idx] + exception.out * (X1_DEN[i][idx] - x0Den.out[i][idx]);
|
||||
}
|
||||
}
|
||||
|
||||
// g(x) = x^3 + A x + B
|
||||
// Compute g(x0(t))
|
||||
component gX0 = EllipticCurveFunction(CHUNK_SIZE, CHUNK_NUMBER, A, B, P);
|
||||
// x1(t) = XI * t^2 * x0(t)
|
||||
component x1 = Fp2Multiply(CHUNK_SIZE, CHUNK_NUMBER, P);
|
||||
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
gX0.in[i][idx] <== x0.out[i][idx];
|
||||
x1.a[i][idx] <== xiTSq.out[i][idx];
|
||||
x1.b[i][idx] <== x0.out[i][idx];
|
||||
}
|
||||
}
|
||||
|
||||
component xi3T6 = Fp2MultiplyThree(CHUNK_SIZE, CHUNK_NUMBER, P); // shares A hidden component with xi2T4; I'll let compiler optimize that out for readability
|
||||
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
xi3T6.a[i][idx] <== xiTSq.out[i][idx];
|
||||
xi3T6.b[i][idx] <== xiTSq.out[i][idx];
|
||||
xi3T6.c[i][idx] <== xiTSq.out[i][idx];
|
||||
}
|
||||
}
|
||||
// g(x1(t)) = XI^3 * t^6 * g(x0(t))
|
||||
component gX1 = Fp2Multiply(CHUNK_SIZE, CHUNK_NUMBER, P);
|
||||
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
gX1.a[i][idx] <== xi3T6.out[i][idx];
|
||||
gX1.b[i][idx] <== gX0.out[i][idx];
|
||||
}
|
||||
}
|
||||
/*
|
||||
XI^3 is not A square, so one of gX0, gX1 must be A square
|
||||
isSquare = 1 if gX0 is A square, = 0 if gX1 is A square
|
||||
sqrt = sqrt(gX0) if isSquare = 1, sqrt = sqrt(gX1) if isSquare = 0
|
||||
|
||||
Implementation is special to P^2 = 9 mod 16
|
||||
References:
|
||||
P. 9 of https://eprint.iacr.org/2019/403.pdf
|
||||
F.2.1.1 for general version for any field: https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-14#appendix-F.2.1.1
|
||||
|
||||
I do not use the trick for combining division and sqrt from Section 5 of
|
||||
Bernstein, Duif, Lange, Schwabe, and Yang, "High-speed high-security signatures",
|
||||
since division is cheap in circom
|
||||
*/
|
||||
|
||||
signal isSquare;
|
||||
|
||||
// Precompute SQRT_CANDIDATE = gX0^{(P^2 + 7) / 16}
|
||||
// P^2 + 7
|
||||
var C1[150] = long_add_unequal(CHUNK_SIZE, 2 * CHUNK_NUMBER, 1, prod(CHUNK_SIZE, CHUNK_NUMBER, P, P), [7]);
|
||||
// (P^2 + 7) // 16
|
||||
var C2[2][150] = long_div2(CHUNK_SIZE, 1, 2 * CHUNK_NUMBER - 1, C1, [16]);
|
||||
|
||||
assert(C2[1][0] == 0); // assert P^2 + 7 is divisible by 16
|
||||
|
||||
var SQRT_CANDIDATE[2][150] = find_Fp2_exp(CHUNK_SIZE, CHUNK_NUMBER, gX0.out, P, C2[0]);
|
||||
// if gX0 is A square, square root must be SQRT_CANDIDATE * (8th-root of unity)
|
||||
// - 1 is A square in Fp2 (because P^2 - 1 is even) so we only need to check half of the 8th ROOTS of unity
|
||||
var ROOTS[4][2][150] = get_roots_of_unity(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
var SQRT_WITHNESS[2][2][150];
|
||||
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
SQRT_WITHNESS[i][j][idx] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var IS_SQUARE = 0;
|
||||
for(var i = 0; i < 4; i++){
|
||||
var SQRT_TMP[2][150] = find_Fp2_product(CHUNK_SIZE, CHUNK_NUMBER, SQRT_CANDIDATE, ROOTS[i], P);
|
||||
if(is_equal_Fp2(CHUNK_SIZE, CHUNK_NUMBER, find_Fp2_product(CHUNK_SIZE, CHUNK_NUMBER, SQRT_TMP, SQRT_TMP, P), gX0.out) == 1){
|
||||
IS_SQUARE = 1;
|
||||
SQRT_WITHNESS[0] = SQRT_TMP;
|
||||
}
|
||||
}
|
||||
isSquare <-- IS_SQUARE;
|
||||
isSquare * (1 - isSquare) === 0;
|
||||
|
||||
var IS_SQUARE1 = 0;
|
||||
var ETAS[4][2][150] = get_etas(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
// find square root of gX1
|
||||
// square root of gX1 must be = SQRT_CANDIDATE * t^3 * eta
|
||||
// for one of four precomputed values of eta
|
||||
// eta determined by eta^2 = XI^3 * ( - 1)^{ - 1/4}
|
||||
var T_CU[2][150] = find_Fp2_product(CHUNK_SIZE, CHUNK_NUMBER, find_Fp2_product(CHUNK_SIZE, CHUNK_NUMBER, in, in, P), in, P);
|
||||
SQRT_CANDIDATE = find_Fp2_product(CHUNK_SIZE, CHUNK_NUMBER, SQRT_CANDIDATE, T_CU, P);
|
||||
|
||||
for(var i = 0; i < 4; i++){
|
||||
var SQRT_TMP[2][150] = find_Fp2_product(CHUNK_SIZE, CHUNK_NUMBER, SQRT_CANDIDATE, ETAS[i], P);
|
||||
if(is_equal_Fp2(CHUNK_SIZE, CHUNK_NUMBER, find_Fp2_product(CHUNK_SIZE, CHUNK_NUMBER, SQRT_TMP, SQRT_TMP, P), gX1.out) == 1){
|
||||
IS_SQUARE1 = 1;
|
||||
SQRT_WITHNESS[1] = SQRT_TMP;
|
||||
}
|
||||
}
|
||||
assert(IS_SQUARE == 1 || IS_SQUARE1 == 1); // one of gX0 or gX1 must be A square!
|
||||
|
||||
|
||||
// X = out[0] = x0 if isSquare == 1, else X = x1
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
out[0][i][idx] <== isSquare * (x0.out[i][idx] - x1.out[i][idx]) + x1.out[i][idx];
|
||||
}
|
||||
}
|
||||
|
||||
// sgn0(t)
|
||||
component sgn_in = Fp2Sgn0(CHUNK_SIZE, CHUNK_NUMBER, P);
|
||||
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
sgn_in.in[i][idx] <== in[i][idx];
|
||||
}
|
||||
}
|
||||
|
||||
var Y[2][150];
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
Y[i][idx] = IS_SQUARE * SQRT_WITHNESS[0][i][idx] + (1-IS_SQUARE) * SQRT_WITHNESS[1][i][idx];
|
||||
}
|
||||
}
|
||||
// Y = out[1] = +- SQRT_WITHNESS; sign determined by sgn0(Y) = sgn0(t)
|
||||
|
||||
if(get_fp2_sgn0(CHUNK_NUMBER, Y) != sgn_in.out){
|
||||
Y[0] = long_sub(CHUNK_SIZE, CHUNK_NUMBER, P, Y[0]);
|
||||
Y[1] = long_sub(CHUNK_SIZE, CHUNK_NUMBER, P, Y[1]);
|
||||
}
|
||||
|
||||
component ySq = Fp2Multiply(CHUNK_SIZE, CHUNK_NUMBER, P);
|
||||
// Y^2 == g(X)
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
out[1][i][idx] <-- Y[i][idx];
|
||||
ySq.a[i][idx] <== out[1][i][idx];
|
||||
ySq.b[i][idx] <== out[1][i][idx];
|
||||
}
|
||||
}
|
||||
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
ySq.out[i][idx] === isSquare * (gX0.out[i][idx] - gX1.out[i][idx]) + gX1.out[i][idx];
|
||||
}
|
||||
}
|
||||
|
||||
// sgn0(Y) == sgn0(t)
|
||||
component sqnY = Fp2Sgn0(CHUNK_SIZE, CHUNK_NUMBER, P);
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
sqnY.in[i][idx] <== out[1][i][idx];
|
||||
}
|
||||
}
|
||||
sqnY.out === sgn_in.out;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
3-Isogeny from E2' to E2
|
||||
References:
|
||||
Appendix E.3 of https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-14#appendix-E.3
|
||||
Section 4.3 of Wahby-Boneh: https://eprint.iacr.org/2019/403.pdf
|
||||
iso3(P) in Python reference code: https://github.com/algorand/bls_sigs_ref/blob/master/python-impl/opt_swu_g2.py
|
||||
*/
|
||||
|
||||
// Input:
|
||||
// in = (x', y') point on E2' assumed to not be point at infinity
|
||||
// inIsInfinity = 1 if input is point at infinity on E2' (in which case x', y' are arbitrary)
|
||||
// Output:
|
||||
// out = (x, y) is point on E2 after applying 3-isogeny to in
|
||||
// isInfinity = 1 if one of exceptional cases occurs and output should be point at infinity
|
||||
// Exceptions:
|
||||
// inIsInfinity = 1
|
||||
// input is A pole of the isogeny, i.e., x_den or y_den = 0
|
||||
|
||||
template Iso3Map(CHUNK_SIZE, CHUNK_NUMBER){
|
||||
signal input in[2][2][CHUNK_NUMBER];
|
||||
//signal input inIsInfinity;
|
||||
signal output out[2][2][CHUNK_NUMBER];
|
||||
signal output isInfinity;
|
||||
|
||||
var P[150] = get_BLS12_381_prime(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
|
||||
// load coefficients of the isogeny (precomputed)
|
||||
var COEFFS[4][4][2][150] = get_iso3_coeffs(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
|
||||
// x = x_num / x_den
|
||||
// y = y' * y_num / y_den
|
||||
// x_num = sum_{i = 0}^3 COEFFS[0][i] * x'^i
|
||||
// x_den = x'^2 + COEFFS[1][1] * x' + COEFFS[1][0]
|
||||
// y_num = sum_{i = 0}^3 COEFFS[2][i] * x'^i
|
||||
// y_den = x'^3 + sum_{i = 0}^2 COEFFS[3][i] * x'^i
|
||||
|
||||
var LOGK = log_ceil(CHUNK_NUMBER);
|
||||
component xp2NoCarry = SignedFp2MultiplyNoCarry(CHUNK_SIZE, CHUNK_NUMBER, 2 * CHUNK_SIZE + LOGK + 1);
|
||||
component xp2 = SignedFp2CompressCarry(CHUNK_SIZE, CHUNK_NUMBER, CHUNK_NUMBER - 1, 2 * CHUNK_SIZE+LOGK+1, P);
|
||||
component xp3NoCarry = SignedFp2MultiplyNoCarryUnequal(CHUNK_SIZE, 2 * CHUNK_NUMBER - 1, CHUNK_NUMBER, 3 * CHUNK_SIZE + 2 * LOGK + 2);
|
||||
component xp3 = SignedFp2CompressCarry(CHUNK_SIZE, CHUNK_NUMBER, 2 * CHUNK_NUMBER - 2, 3 * CHUNK_SIZE + 2 * LOGK + 2, P);
|
||||
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
xp2NoCarry.a[i][idx] <== in[0][i][idx];
|
||||
xp2NoCarry.b[i][idx] <== in[0][i][idx];
|
||||
xp3NoCarry.b[i][idx] <== in[0][i][idx];
|
||||
}
|
||||
}
|
||||
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var idx = 0; idx < 2 * CHUNK_NUMBER - 1; idx++){
|
||||
xp3NoCarry.a[i][idx] <== xp2NoCarry.out[i][idx];
|
||||
xp2.in[i][idx] <== xp2NoCarry.out[i][idx];
|
||||
}
|
||||
}
|
||||
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var idx = 0; idx < 3 * CHUNK_NUMBER - 2; idx++){
|
||||
xp3.in[i][idx] <== xp3NoCarry.out[i][idx];
|
||||
}
|
||||
}
|
||||
|
||||
signal xpPow[3][2][CHUNK_NUMBER];
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
xpPow[0][i][idx] <== in[0][i][idx];
|
||||
xpPow[1][i][idx] <== xp2.out[i][idx];
|
||||
xpPow[2][i][idx] <== xp3.out[i][idx];
|
||||
}
|
||||
}
|
||||
|
||||
component COEFFS_XP[4][3];
|
||||
var deg[4] = [3, 1, 3, 2];
|
||||
for(var i = 0; i < 4; i++){
|
||||
for(var j = 0; j < deg[i]; j++){
|
||||
COEFFS_XP[i][j] = SignedFp2MultiplyNoCarry(CHUNK_SIZE, CHUNK_NUMBER, 2 * CHUNK_SIZE + LOGK + 1);
|
||||
for(var l = 0; l < 2; l++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
COEFFS_XP[i][j].a[l][idx] <== COEFFS[i][j + 1][l][idx];
|
||||
COEFFS_XP[i][j].b[l][idx] <== xpPow[j][l][idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
var x_frac[4][2][150];
|
||||
for(var i = 0; i < 4; i++){
|
||||
for(var l = 0; l < 2; l++){
|
||||
for(var idx = 0; idx < 2 * CHUNK_NUMBER - 1; idx++){
|
||||
if(idx < CHUNK_NUMBER)
|
||||
x_frac[i][l][idx] = COEFFS[i][0][l][idx];
|
||||
else
|
||||
x_frac[i][l][idx] = 0;
|
||||
}
|
||||
}
|
||||
for(var j = 0; j < deg[i]; j++){
|
||||
for(var l = 0; l < 2; l++){
|
||||
for(var idx = 0; idx < 2 * CHUNK_NUMBER - 1; idx++){
|
||||
x_frac[i][l][idx] += COEFFS_XP[i][j].out[l][idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for(var l = 0; l < 2; l++)for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
x_frac[1][l][idx] += xp2.out[l][idx];
|
||||
x_frac[3][l][idx] += xp3.out[l][idx];
|
||||
}
|
||||
|
||||
// carry the denominators since we need to check whether they are 0
|
||||
component den[2];
|
||||
component denIsZero[2];
|
||||
for(var i = 0; i < 2; i++){
|
||||
den[i] = SignedFp2CompressCarry(CHUNK_SIZE, CHUNK_NUMBER, CHUNK_NUMBER - 1, 2 * CHUNK_SIZE + LOGK + 3, P);
|
||||
for(var l = 0; l < 2; l++){
|
||||
for(var idx = 0; idx < 2 * CHUNK_NUMBER - 1; idx++){
|
||||
den[i].in[l][idx] <== x_frac[2 * i + 1][l][idx];
|
||||
}
|
||||
}
|
||||
|
||||
denIsZero[i] = Fp2IsZero(CHUNK_SIZE, CHUNK_NUMBER, P);
|
||||
for(var l = 0; l < 2; l++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
denIsZero[i].in[l][idx] <== den[i].out[l][idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//component exception = IsZero();
|
||||
//exception.in <== inIsInfinity + denIsZero[0].out + denIsZero[1].out;
|
||||
isInfinity <== denIsZero[0].out + denIsZero[1].out - denIsZero[0].out * denIsZero[1].out; // OR gate: if either denominator is 0, output point at infinity
|
||||
|
||||
component num[2];
|
||||
|
||||
for(var i = 0; i < 2; i++){
|
||||
num[i] = Fp2Compress(CHUNK_SIZE, CHUNK_NUMBER, CHUNK_NUMBER - 1, P, 3 * CHUNK_SIZE + 2 * LOGK + 3);
|
||||
for(var l = 0; l < 2; l++){
|
||||
for(var idx = 0; idx < 2 * CHUNK_NUMBER - 1; idx++){
|
||||
num[i].in[l][idx] <== x_frac[2 * i][l][idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component x[2];
|
||||
// num / den if den != 0, else num / 1
|
||||
for(var i = 0; i < 2; i++){
|
||||
x[i] = SignedFp2Divide(CHUNK_SIZE, CHUNK_NUMBER, 3 * CHUNK_SIZE + 2 * LOGK + 3, CHUNK_SIZE, P);
|
||||
for(var l = 0; l < 2; l++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
x[i].a[l][idx] <== num[i].out[l][idx];
|
||||
if(l == 0 && idx == 0)
|
||||
x[i].b[l][idx] <== isInfinity * (1 - den[i].out[l][idx]) + den[i].out[l][idx];
|
||||
else
|
||||
x[i].b[l][idx] <== - isInfinity * den[i].out[l][idx] + den[i].out[l][idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component y = Fp2Multiply(CHUNK_SIZE, CHUNK_NUMBER, P);
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
y.a[i][idx] <== in[1][i][idx];
|
||||
y.b[i][idx] <== x[1].out[i][idx];
|
||||
}
|
||||
}
|
||||
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
out[0][i][idx] <== x[0].out[i][idx];
|
||||
out[1][i][idx] <== y.out[i][idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Cofactor Clearing for BLS12-381 G2
|
||||
Implementation below follows Appendix G.3 of https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-14#appendix-G.3
|
||||
References:
|
||||
The endomorphism psi of Budroni-Pintore: https://eprint.iacr.org/2017/419.pdf
|
||||
BLS For the Rest of Us: https://hackmd.io/@benjaminion/bls12-381#Cofactor-clearing
|
||||
*/
|
||||
|
||||
// Input: in, A point on the curve E2 : y^2 = x^3 + 4(1+u)
|
||||
// coordinates of in are in "proper" representation
|
||||
// Output: out = psi(in), A point on the same curve.
|
||||
template EndomorphismPsi(CHUNK_SIZE, CHUNK_NUMBER, P){
|
||||
signal input in[2][2][CHUNK_NUMBER];
|
||||
signal output out[2][2][CHUNK_NUMBER];
|
||||
|
||||
var C[2][2][CHUNK_NUMBER];
|
||||
// Constants:
|
||||
// c0 = 1 / (1 + I)^((P - 1) / 3) # in GF(P^2)
|
||||
// C1 = 1 / (1 + I)^((P - 1) / 2) # in GF(P^2)
|
||||
|
||||
assert(CHUNK_SIZE == 55 && CHUNK_NUMBER == 7);
|
||||
if(CHUNK_SIZE == 55 && CHUNK_NUMBER == 7){
|
||||
C = [[[0, 0, 0, 0, 0, 0, 0],
|
||||
[35184372088875693,
|
||||
22472499736345367,
|
||||
5698637743850064,
|
||||
21300661132716363,
|
||||
21929049149954008,
|
||||
23430044241153146,
|
||||
1829881462546425]],
|
||||
[[31097504852074146,
|
||||
21847832108733923,
|
||||
11215546103677201,
|
||||
1564033941097252,
|
||||
9796175148277139,
|
||||
23041766052141807,
|
||||
1359550313685033],
|
||||
[4649817190157321,
|
||||
14178090100713872,
|
||||
25898210532243870,
|
||||
6361890036890480,
|
||||
6755281389607612,
|
||||
401348527762810,
|
||||
470331148861392]]];
|
||||
}
|
||||
component frob[2];
|
||||
component qx[2];
|
||||
for(var i = 0; i < 2; i++){
|
||||
frob[i] = Fp2FrobeniusMap(CHUNK_SIZE, CHUNK_NUMBER, 1, P);
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
frob[i].in[j][idx] <== in[i][j][idx];
|
||||
}
|
||||
}
|
||||
qx[i] = Fp2Multiply(CHUNK_SIZE, CHUNK_NUMBER, P);
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
qx[i].a[j][idx] <== C[i][j][idx];
|
||||
qx[i].b[j][idx] <== frob[i].out[j][idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
out[0][j][idx] <== qx[0].out[j][idx];
|
||||
out[1][j][idx] <== qx[1].out[j][idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Input: in, A point on the curve E2 : y^2 = x^3 + 4(1+u)
|
||||
// coordinates of in are in "proper" representation
|
||||
// Output: out = psi(psi(in)), A point on the same curve.
|
||||
template EndomorphismPsi2(CHUNK_SIZE, CHUNK_NUMBER, P){
|
||||
signal input in[2][2][CHUNK_NUMBER];
|
||||
signal output out[2][2][CHUNK_NUMBER];
|
||||
|
||||
var C[CHUNK_NUMBER];
|
||||
// Third root of unity:
|
||||
// C = 1 / 2^((P - 1) / 3) # in GF(P)
|
||||
|
||||
assert(CHUNK_SIZE == 55 && CHUNK_NUMBER == 7);
|
||||
if(CHUNK_SIZE == 55 && CHUNK_NUMBER == 7){
|
||||
C = [35184372088875692,
|
||||
22472499736345367,
|
||||
5698637743850064,
|
||||
21300661132716363,
|
||||
21929049149954008,
|
||||
23430044241153146,
|
||||
1829881462546425];
|
||||
}
|
||||
|
||||
component qx[2];
|
||||
component qy[2];
|
||||
for(var i = 0; i < 2; i++){
|
||||
qx[i] = FpMultiply(CHUNK_SIZE, CHUNK_NUMBER, P);
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
qx[i].a[idx] <== C[idx];
|
||||
qx[i].b[idx] <== in[0][i][idx];
|
||||
}
|
||||
|
||||
qy[i] = BigSub(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
qy[i].a[idx] <== P[idx];
|
||||
qy[i].b[idx] <== in[1][i][idx];
|
||||
}
|
||||
}
|
||||
for(var i = 0; i < 2; i++)for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
out[0][i][idx] <== qx[i].out[idx];
|
||||
out[1][i][idx] <== qy[i].out[idx];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// in = P, A point on curve E2
|
||||
// out = [x^2 - x - 1]P + [x - 1]*psi(P) + psi2(2 * P)
|
||||
// where x = -15132376222941642752 is the parameter for BLS12-381
|
||||
template ClearCofactorG2(CHUNK_SIZE, CHUNK_NUMBER){
|
||||
signal input in[2][2][CHUNK_NUMBER];
|
||||
signal input inIsInfinity;
|
||||
|
||||
signal output out[2][2][CHUNK_NUMBER];
|
||||
signal output isInfinity;
|
||||
|
||||
var P[150] = get_BLS12_381_prime(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
var X_ABS = get_BLS12_381_parameter(); // this is abs(x). remember x is negative!
|
||||
var A[2] = [0,0];
|
||||
var B[2] = [4,4];
|
||||
var DUMMY_POINT[2][2][150] = get_generator_G2(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
|
||||
// Output: [|x|^2 + |x| - 1]*P + [-|x| - 1]*psi(P) + psi2(2 * P)
|
||||
// = |x| * (|x|*P + P - psi(P)) - P -psi(P) + psi2(2 * P)
|
||||
|
||||
// replace `in` with DUMMY_POINT if inIsInfinity = 1 to ensure P is on the curve
|
||||
signal P[2][2][CHUNK_NUMBER];
|
||||
component xP = EllipticCurveScalarMultiplyFp2(CHUNK_SIZE, CHUNK_NUMBER, B, X_ABS, P);
|
||||
component psiP = EndomorphismPsi(CHUNK_SIZE, CHUNK_NUMBER, P);
|
||||
component negPy = Fp2Negate(CHUNK_SIZE, CHUNK_NUMBER, P);
|
||||
component negPsiPy = Fp2Negate(CHUNK_SIZE, CHUNK_NUMBER, P);
|
||||
component doubP = EllipticCurveDoubleFp2(CHUNK_SIZE, CHUNK_NUMBER, A, B, P);
|
||||
|
||||
xP.inIsInfinity <== inIsInfinity;
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
P[i][j][idx] <== in[i][j][idx] + inIsInfinity * (DUMMY_POINT[i][j][idx] - in[i][j][idx]);
|
||||
xP.in[i][j][idx] <== P[i][j][idx];
|
||||
psiP.in[i][j][idx] <== P[i][j][idx];
|
||||
doubP.in[i][j][idx] <== P[i][j][idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
negPy.in[j][idx] <== P[1][j][idx];
|
||||
negPsiPy.in[j][idx] <== psiP.out[1][j][idx];
|
||||
}
|
||||
}
|
||||
|
||||
component psi22P = EndomorphismPsi2(CHUNK_SIZE, CHUNK_NUMBER, P);
|
||||
component add[5];
|
||||
for(var i = 0; i < 5; i++)
|
||||
add[i] = EllipticCurveAddFp2(CHUNK_SIZE, CHUNK_NUMBER, A, B, P);
|
||||
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
psi22P.in[i][j][idx] <== doubP.out[i][j][idx];
|
||||
add[0].a[i][j][idx] <== xP.out[i][j][idx];
|
||||
add[0].b[i][j][idx] <== P[i][j][idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
add[0].aIsInfinity <== xP.isInfinity;
|
||||
add[0].bIsInfinity <== inIsInfinity;
|
||||
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
add[1].a[i][j][idx] <== add[0].out[i][j][idx];
|
||||
if(i == 0){
|
||||
add[1].b[i][j][idx] <== psiP.out[i][j][idx];
|
||||
} else {
|
||||
add[1].b[i][j][idx] <== negPsiPy.out[j][idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
add[1].aIsInfinity <== add[0].isInfinity;
|
||||
add[1].bIsInfinity <== inIsInfinity;
|
||||
|
||||
component xAdd1 = EllipticCurveScalarMultiplyFp2(CHUNK_SIZE, CHUNK_NUMBER, B, X_ABS, P);
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
xAdd1.in[i][j][idx] <== add[1].out[i][j][idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
xAdd1.inIsInfinity <== add[1].isInfinity;
|
||||
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
add[2].a[i][j][idx] <== xAdd1.out[i][j][idx];
|
||||
if(i == 0) {
|
||||
add[2].b[i][j][idx] <== P[i][j][idx];
|
||||
} else {
|
||||
add[2].b[i][j][idx] <== negPy.out[j][idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
add[2].aIsInfinity <== xAdd1.isInfinity;
|
||||
add[2].bIsInfinity <== inIsInfinity;
|
||||
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
add[3].a[i][j][idx] <== add[2].out[i][j][idx];
|
||||
if(i == 0){
|
||||
add[3].b[i][j][idx] <== psiP.out[i][j][idx];
|
||||
} else{
|
||||
add[3].b[i][j][idx] <== negPsiPy.out[j][idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
add[3].aIsInfinity <== add[2].isInfinity;
|
||||
add[3].bIsInfinity <== inIsInfinity;
|
||||
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
add[4].a[i][j][idx] <== add[3].out[i][j][idx];
|
||||
add[4].b[i][j][idx] <== psi22P.out[i][j][idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
add[4].aIsInfinity <== add[3].isInfinity;
|
||||
add[4].bIsInfinity <== inIsInfinity;
|
||||
|
||||
// isInfinity = add[4].isInfinity or inIsInfinity (if starting point was O, output must be O)
|
||||
isInfinity <== add[4].isInfinity + inIsInfinity - inIsInfinity * add[4].isInfinity;
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
out[i][j][idx] <== add[4].out[i][j][idx] + isInfinity * (DUMMY_POINT[i][j][idx] - add[4].out[i][j][idx]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// `in` is 2 x 2 x CHUNK_NUMBER representing two field elements in Fp2
|
||||
// `out` is 2 x 2 x CHUNK_NUMBER representing A point in subgroup G2 of E2(Fp2) twisted curve for BLS12-381
|
||||
// isInfinity = 1 if `out` is point at infinity
|
||||
// Implements steps 2-6 of hash_to_curve as specified in https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-14#section-3
|
||||
// In practice `in` = hash_to_field(msg, 2) for an arbitrary-length byte string, in which case `out` = hash_to_curve(msg)
|
||||
template MapToG2(CHUNK_SIZE, CHUNK_NUMBER){
|
||||
signal input in[2][2][CHUNK_NUMBER];
|
||||
signal output out[2][2][CHUNK_NUMBER];
|
||||
signal output isInfinity;
|
||||
|
||||
var P[150] = get_BLS12_381_prime(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
|
||||
component Qp[2];
|
||||
for(var i = 0; i < 2; i++){
|
||||
Qp[i] = OptSimpleSWU2(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
Qp[i].in[j][idx] <== in[i][j][idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// There is A small optimization we can do: Iso3Map is A group homomorphism, so we can add first and then apply isogeny. This uses EllipticCurveAdd on E2'
|
||||
component Rp = EllipticCurveAddFp2(CHUNK_SIZE, CHUNK_NUMBER, [0, 240], [1012, 1012], P);
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
Rp.a[i][j][idx] <== Qp[0].out[i][j][idx];
|
||||
Rp.b[i][j][idx] <== Qp[1].out[i][j][idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
Rp.aIsInfinity <== 0;
|
||||
Rp.bIsInfinity <== 0;
|
||||
|
||||
component R = Iso3Map(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
R.in[i][j][idx] <== Rp.out[i][j][idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component P = ClearCofactorG2(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
P.in[i][j][idx] <== R.out[i][j][idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
P.inIsInfinity <== R.isInfinity + Rp.isInfinity - R.isInfinity * Rp.isInfinity;
|
||||
|
||||
isInfinity <== P.isInfinity;
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
out[i][j][idx] <== P.out[i][j][idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Subgroup checks for G1, G2:
|
||||
use the latest methods by Scott: https://eprint.iacr.org/2021/1130.pdf
|
||||
Other references:
|
||||
Bowe: https://eprint.iacr.org/2019/814.pdf
|
||||
El Housni: https://hackmd.io/@yelhousni/bls12_subgroup_check
|
||||
*/
|
||||
|
||||
// `in` = P is 2 x 2 x CHUNK_NUMBER, pair of Fp2 elements
|
||||
// check P is on curve twist E2(Fp2)
|
||||
// check psi(P) = [x]P where x is parameter for BLS12-381
|
||||
template SubgroupCheckG2(CHUNK_SIZE, CHUNK_NUMBER){
|
||||
signal input in[2][2][CHUNK_NUMBER];
|
||||
|
||||
var P[150] = get_BLS12_381_prime(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
var X_ABS = get_BLS12_381_parameter();
|
||||
|
||||
component isOnCurve = PointOnCurveFp2(CHUNK_SIZE, CHUNK_NUMBER, [0,0], [4,4], P);
|
||||
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
isOnCurve.in[i][j][idx] <== in[i][j][idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component psiP = EndomorphismPsi(CHUNK_SIZE, CHUNK_NUMBER, P);
|
||||
component negP = Fp2Negate(CHUNK_SIZE, CHUNK_NUMBER, P);
|
||||
component xP = EllipticCurveScalarMultiplyUnequalFp2(CHUNK_SIZE, CHUNK_NUMBER, [4, 4], X_ABS, P);
|
||||
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
negP.in[j][idx] <== in[1][j][idx];
|
||||
}
|
||||
}
|
||||
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
psiP.in[0][j][idx] <== in[0][j][idx];
|
||||
psiP.in[1][j][idx] <== in[1][j][idx];
|
||||
xP.in[0][j][idx] <== in[0][j][idx];
|
||||
xP.in[1][j][idx] <== negP.out[j][idx];
|
||||
}
|
||||
}
|
||||
|
||||
// psi(P) == [x]P
|
||||
component isEq[2];
|
||||
for(var i = 0; i < 2; i++){
|
||||
isEq[i] = Fp2IsEqual(CHUNK_SIZE, CHUNK_NUMBER, P);
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
isEq[i].a[j][idx] <== psiP.out[i][j][idx];
|
||||
isEq[i].b[j][idx] <== xP.out[i][j][idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
isEq[0].out === 1;
|
||||
isEq[1].out === 1;
|
||||
}
|
||||
|
||||
// `in` = P is 2 x CHUNK_NUMBER, pair of Fp elements
|
||||
// check P is on curve E(Fp)
|
||||
// check phi(P) == [-x^2] P where phi(x,y) = (omega * x, y) where omega is A cube root of unity in Fp
|
||||
template SubgroupCheckG1(CHUNK_SIZE, CHUNK_NUMBER){
|
||||
signal input in[2][CHUNK_NUMBER];
|
||||
|
||||
var P[150] = get_BLS12_381_prime(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
var X_ABS = get_BLS12_381_parameter();
|
||||
var B = 4;
|
||||
|
||||
component isOnCurve = PointOnCurve(CHUNK_SIZE, CHUNK_NUMBER, 0, B, P);
|
||||
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
isOnCurve.in[i][idx] <== in[i][idx];
|
||||
}
|
||||
}
|
||||
|
||||
var omega[CHUNK_NUMBER];
|
||||
// Third root of unity:
|
||||
// omega = 2^((P - 1) / 3) # in GF(P)
|
||||
assert(CHUNK_SIZE == 55 && CHUNK_NUMBER == 7);
|
||||
if(CHUNK_SIZE == 55 && CHUNK_NUMBER == 7){
|
||||
omega = [562949953355774,
|
||||
13553422473102428,
|
||||
31415118892071007,
|
||||
22654059864235337,
|
||||
30651204406894710,
|
||||
13070338751470,
|
||||
0];
|
||||
}
|
||||
|
||||
component phiPx = FpMultiply(CHUNK_SIZE, CHUNK_NUMBER, P);
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
phiPx.a[idx] <== omega[idx];
|
||||
phiPx.b[idx] <== in[0][idx];
|
||||
}
|
||||
component phiPyNeg = BigSub(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
phiPyNeg.a[idx] <== P[idx];
|
||||
phiPyNeg.b[idx] <== in[1][idx];
|
||||
}
|
||||
|
||||
// x has hamming weight 6 while x^2 has hamming weight 17 so better to do double-and-add on x twice
|
||||
component xP = EllipticCurveScalarMultiplyUnequal(CHUNK_SIZE, CHUNK_NUMBER, B, X_ABS, P);
|
||||
component x2P = EllipticCurveScalarMultiplyUnequal(CHUNK_SIZE, CHUNK_NUMBER, B, X_ABS, P);
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
xP.in[i][idx] <== in[i][idx];
|
||||
}
|
||||
}
|
||||
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
x2P.in[i][idx] <== xP.out[i][idx];
|
||||
}
|
||||
}
|
||||
// check -phi(P) == [x^2]P
|
||||
component isEq = Fp2IsEqual(CHUNK_SIZE, CHUNK_NUMBER, P); // using Fp2IsEqual to check two Fp points are equal
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
isEq.a[0][idx] <== phiPx.out[idx];
|
||||
isEq.a[1][idx] <== phiPyNeg.out[idx];
|
||||
|
||||
isEq.b[0][idx] <== x2P.out[0][idx];
|
||||
isEq.b[1][idx] <== x2P.out[1][idx];
|
||||
}
|
||||
isEq.out === 1;
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
pragma circom 2.0.3;
|
||||
|
||||
include "finalExp.circom";
|
||||
include "pairing.circom";
|
||||
include "bls12_381Func.circom";
|
||||
include "bls12_381HashToG2.circom";
|
||||
|
||||
|
||||
// Input: pubkey in G_1
|
||||
// signature, H(m) in G_2
|
||||
// Output: out = 1 if valid signature, else = 0
|
||||
// Verifies that e(G1, signature) = e(pubkey, H(m)) by checking e(G1, signature)*e(pubkey, -H(m)) === 1 where e(,) is optimal Ate pairing
|
||||
template CoreVerifyPubkeyG1NoCheck(CHUNK_SIZE, CHUNK_NUMBER){
|
||||
signal input pubkey[2][CHUNK_NUMBER];
|
||||
signal input signature[2][2][CHUNK_NUMBER];
|
||||
signal input Hm[2][2][CHUNK_NUMBER];
|
||||
signal output out;
|
||||
|
||||
var Q[150] = get_BLS12_381_prime(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
var X = get_BLS12_381_parameter();
|
||||
var G1[2][150] = get_generator_G1(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
|
||||
signal negS[2][2][CHUNK_NUMBER];
|
||||
component neg[2];
|
||||
for(var j = 0; j < 2; j++){
|
||||
neg[j] = FpNegate(CHUNK_SIZE, CHUNK_NUMBER, Q);
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++)
|
||||
neg[j].in[idx] <== signature[1][j][idx];
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
negS[0][j][idx] <== signature[0][j][idx];
|
||||
negS[1][j][idx] <== neg[j].out[idx];
|
||||
}
|
||||
}
|
||||
|
||||
component miller = MillerLoopFp2Two(CHUNK_SIZE, CHUNK_NUMBER, [4,4], X, Q);
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
miller.P[0][i][j][idx] <== negS[i][j][idx];
|
||||
miller.P[1][i][j][idx] <== Hm[i][j][idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
miller.Q[0][i][idx] <== G1[i][idx];
|
||||
miller.Q[1][i][idx] <== pubkey[i][idx];
|
||||
}
|
||||
}
|
||||
|
||||
component finalExp = FinalExponentiate(CHUNK_SIZE, CHUNK_NUMBER, Q);
|
||||
for(var i = 0; i < 6; i++){
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
finalExp.in[i][j][idx] <== miller.out[i][j][idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component is_valid[6][2][CHUNK_NUMBER];
|
||||
var total = 12 * CHUNK_NUMBER;
|
||||
for(var i = 0; i < 6; i++){
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
is_valid[i][j][idx] = IsZero();
|
||||
if(i == 0 && j == 0 && idx == 0){
|
||||
is_valid[i][j][idx].in <== finalExp.out[i][j][idx] - 1;
|
||||
} else {
|
||||
is_valid[i][j][idx].in <== finalExp.out[i][j][idx];
|
||||
}
|
||||
total -= is_valid[i][j][idx].out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component valid = IsZero();
|
||||
valid.in <== total;
|
||||
out <== valid.out;
|
||||
}
|
||||
|
||||
// Inputs:
|
||||
// - pubkey as element of E(Fq)
|
||||
// - hash represents two field elements in Fp2, in practice hash = hash_to_field(msg,2).
|
||||
// - signature, as element of E2(Fq2)
|
||||
// Assume signature is not point at infinity
|
||||
template CoreVerifyPubkeyG1(CHUNK_SIZE, CHUNK_NUMBER){
|
||||
signal input pubkey[2][CHUNK_NUMBER];
|
||||
signal input signature[2][2][CHUNK_NUMBER];
|
||||
signal input hash[2][2][CHUNK_NUMBER];
|
||||
|
||||
var Q[150] = get_BLS12_381_prime(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
|
||||
component lt[10];
|
||||
// check all len CHUNK_NUMBER input arrays are correctly formatted bigints < Q (BigLessThan calls Num2Bits)
|
||||
for(var i = 0; i < 10; i++){
|
||||
lt[i] = BigLessThan(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++)
|
||||
lt[i].b[idx] <== Q[idx];
|
||||
}
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
lt[0].a[idx] <== pubkey[0][idx];
|
||||
lt[1].a[idx] <== pubkey[1][idx];
|
||||
lt[2].a[idx] <== signature[0][0][idx];
|
||||
lt[3].a[idx] <== signature[0][1][idx];
|
||||
lt[4].a[idx] <== signature[1][0][idx];
|
||||
lt[5].a[idx] <== signature[1][1][idx];
|
||||
lt[6].a[idx] <== hash[0][0][idx];
|
||||
lt[7].a[idx] <== hash[0][1][idx];
|
||||
lt[8].a[idx] <== hash[1][0][idx];
|
||||
lt[9].a[idx] <== hash[1][1][idx];
|
||||
}
|
||||
var r = 0;
|
||||
for(var i = 0; i < 10; i++){
|
||||
r += lt[i].out;
|
||||
}
|
||||
r === 10;
|
||||
// check all registers are in [0, 2^CHUNK_SIZE)
|
||||
component check[5];
|
||||
for(var i = 0; i < 5; i++){
|
||||
check[i] = RangeCheck2D(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
}
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
check[0].in[i][idx] <== pubkey[i][idx];
|
||||
check[1].in[i][idx] <== signature[0][i][idx];
|
||||
check[2].in[i][idx] <== signature[1][i][idx];
|
||||
check[3].in[i][idx] <== hash[0][i][idx];
|
||||
check[4].in[i][idx] <== hash[1][i][idx];
|
||||
}
|
||||
}
|
||||
|
||||
component pubkeyValid = SubgroupCheckG1(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
pubkeyValid.in[i][idx] <== pubkey[i][idx];
|
||||
}
|
||||
}
|
||||
|
||||
component signatureValid = SubgroupCheckG2(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
signatureValid.in[i][j][idx] <== signature[i][j][idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component Hm = MapToG2(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
Hm.in[i][j][idx] <== hash[i][j][idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Hm.isInfinity === 0;
|
||||
|
||||
component verify = CoreVerifyPubkeyG1NoCheck(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
verify.pubkey[i][idx] <== pubkey[i][idx];
|
||||
}
|
||||
}
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
verify.signature[i][j][idx] <== signature[i][j][idx];
|
||||
verify.Hm[i][j][idx] <== Hm.out[i][j][idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
verify.out === 1;
|
||||
}
|
||||
@@ -0,0 +1,554 @@
|
||||
pragma circom 2.0.3;
|
||||
|
||||
include "circomlib/circuits/bitify.circom";
|
||||
include "fp2.circom";
|
||||
|
||||
// in[i] = (x_i, y_i)
|
||||
// Implements constraint: (y_1 + y_3) * (x_2 - x_1) - (y_2 - y_1)*(x_1 - x_3) = 0 mod P
|
||||
// used to show (x1, y1), (x2, y2), (X3, -Y3) are co-linear
|
||||
template PointOnLine(CHUNK_SIZE, CHUNK_NUMBER, P) {
|
||||
signal input in[3][2][CHUNK_NUMBER];
|
||||
|
||||
var LOGK = log_ceil(CHUNK_NUMBER);
|
||||
var LOGK2 = log_ceil(3 * CHUNK_NUMBER*CHUNK_NUMBER);
|
||||
assert(3 * CHUNK_SIZE + LOGK2 < 251);
|
||||
|
||||
// AKA check point on line
|
||||
component left = BigMultShortLong(CHUNK_SIZE, CHUNK_NUMBER, 2 * CHUNK_SIZE + LOGK + 1); // 2k - 1 registers abs val < 2k*2^{2n}
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++){
|
||||
left.a[i] <== in[0][1][i] + in[2][1][i];
|
||||
left.b[i] <== in[1][0][i] - in[0][0][i];
|
||||
}
|
||||
|
||||
component right = BigMultShortLong(CHUNK_SIZE, CHUNK_NUMBER, 2 * CHUNK_SIZE + LOGK); // 2k - 1 registers abs val < CHUNK_NUMBER*2^{2n}
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++){
|
||||
right.a[i] <== in[1][1][i] - in[0][1][i];
|
||||
right.b[i] <== in[0][0][i] - in[2][0][i];
|
||||
}
|
||||
|
||||
component diffRed;
|
||||
diffRed = PrimeReduce(CHUNK_SIZE, CHUNK_NUMBER, CHUNK_NUMBER - 1, P, 3 * CHUNK_SIZE + LOGK2);
|
||||
for(var i = 0; i < 2 * CHUNK_NUMBER - 1; i++)
|
||||
diffRed.in[i] <== left.out[i] - right.out[i];
|
||||
|
||||
// diffRed has CHUNK_NUMBER registers abs val < 3 * CHUNK_NUMBER^2 * 2^{3n}
|
||||
component diffMod = SignedCheckCarryModToZero(CHUNK_SIZE, CHUNK_NUMBER, 3 * CHUNK_SIZE + LOGK2, P);
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++)
|
||||
diffMod.in[i] <== diffRed.out[i];
|
||||
}
|
||||
|
||||
// in = (x, y)
|
||||
// Implements:~
|
||||
// x^3 + aX + b - y^2 = 0 mod P
|
||||
// Assume: a, b in [0, 2^CHUNK_SIZE)
|
||||
template PointOnCurve(CHUNK_SIZE, CHUNK_NUMBER, a, b, P){
|
||||
signal input in[2][CHUNK_NUMBER];
|
||||
|
||||
var LOGK = log_ceil(CHUNK_NUMBER);
|
||||
var LOGK2 = log_ceil( (2 * CHUNK_NUMBER - 1)*(CHUNK_NUMBER*CHUNK_NUMBER + 1) );
|
||||
assert(4*CHUNK_SIZE + LOGK2 < 251);
|
||||
|
||||
// compute x^3, y^2
|
||||
component xSq = BigMultShortLong(CHUNK_SIZE, CHUNK_NUMBER, 2 * CHUNK_SIZE + LOGK); // 2k - 1 registers in [0, CHUNK_NUMBER*2^{2n})
|
||||
component ySq = BigMultShortLong(CHUNK_SIZE, CHUNK_NUMBER, 2 * CHUNK_SIZE + LOGK); // 2k - 1 registers in [0, CHUNK_NUMBER*2^{2n})
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++){
|
||||
xSq.a[i] <== in[0][i];
|
||||
xSq.b[i] <== in[0][i];
|
||||
|
||||
ySq.a[i] <== in[1][i];
|
||||
ySq.b[i] <== in[1][i];
|
||||
}
|
||||
component xCu = BigMultShortLongUnequal(CHUNK_SIZE, 2 * CHUNK_NUMBER - 1, CHUNK_NUMBER, 3 * CHUNK_SIZE + 2 * LOGK); // 3k-2 registers in [0, CHUNK_NUMBER^2 * 2^{3n})
|
||||
for(var i = 0; i < 2 * CHUNK_NUMBER - 1; i++)
|
||||
xCu.a[i] <== xSq.out[i];
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++)
|
||||
xCu.b[i] <== in[0][i];
|
||||
|
||||
component aX = BigMultShortLong(CHUNK_SIZE, CHUNK_NUMBER, 2 * CHUNK_SIZE + LOGK); // 2k - 1 registers in [0, CHUNK_NUMBER*2^{2n})
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
aX.a[i] <== a[i];
|
||||
aX.b[i] <== in[0][i];
|
||||
}
|
||||
|
||||
// xCu + a x + b has 3k-2 positive registers < CHUNK_NUMBER^2 * 2^{3n} + 2^{2n} + 2^CHUNK_SIZE < (CHUNK_NUMBER^2 + 1) * 2^{3n}
|
||||
component cuRed = PrimeReduce(CHUNK_SIZE, CHUNK_NUMBER, 2 * CHUNK_NUMBER-2, P, 4*CHUNK_SIZE + 3 * LOGK + 1);
|
||||
for(var i = 0; i<3 * CHUNK_NUMBER-2; i++){
|
||||
if (i < CHUNK_NUMBER) {
|
||||
cuRed.in[i] <== xCu.out[i] + aX.out[i] + b[i];
|
||||
} else {
|
||||
if (i < 2 * CHUNK_NUMBER - 1) {
|
||||
cuRed.in[i] <== xCu.out[i] + aX.out[i];
|
||||
} else {
|
||||
cuRed.in[i] <== xCu.out[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
// cuRed has CHUNK_NUMBER registers < (CHUNK_NUMBER^2 + 1)*(2k - 1)*2^{4n}
|
||||
|
||||
component ySqRed = PrimeReduce(CHUNK_SIZE, CHUNK_NUMBER, CHUNK_NUMBER - 1, P, 3 * CHUNK_SIZE + 2 * LOGK + 1);
|
||||
for(var i = 0; i < 2 * CHUNK_NUMBER - 1; i++)
|
||||
ySqRed.in[i] <== ySq.out[i];
|
||||
// ySqRed has positive registers, so when we subtract from cuRed it doesn't increase absolute value
|
||||
|
||||
component constraint = SignedCheckCarryModToZero(CHUNK_SIZE, CHUNK_NUMBER, 4*CHUNK_SIZE + LOGK2, P);
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++){
|
||||
constraint.in[i] <== cuRed.out[i] - ySqRed.out[i];
|
||||
}
|
||||
}
|
||||
|
||||
// in[0] = (x_1, y_1), in[1] = (x_3, y_3)
|
||||
// Checks that the line between (x_1, y_1) and (x_3, -y_3) is equal to the tangent line to the elliptic curve at the point (x_1, y_1)
|
||||
// Implements:
|
||||
// (y_1 + y_3) = LAMBDA * (x_1 - x_3)
|
||||
// where LAMBDA = (3 x_1^2 + a)/(2 y_1)
|
||||
// Actual constraint is 2y_1 (y_1 + y_3) = (3 x_1^2 + a ) ( x_1 - x_3 )
|
||||
template PointOnTangent(CHUNK_SIZE, CHUNK_NUMBER, a, P){
|
||||
signal input in[2][2][CHUNK_NUMBER];
|
||||
|
||||
var LOGK = log_ceil(CHUNK_NUMBER);
|
||||
var LOGK3 = log_ceil((3 * CHUNK_NUMBER)*(2 * CHUNK_NUMBER - 1) + 1);
|
||||
assert(4*CHUNK_SIZE + LOGK3 < 251);
|
||||
component xSq = BigMultShortLong(CHUNK_SIZE, CHUNK_NUMBER, 2 * CHUNK_SIZE + LOGK); // 2k - 1 registers < CHUNK_NUMBER*2^{2n})
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++){
|
||||
xSq.a[i] <== in[0][0][i];
|
||||
xSq.b[i] <== in[0][0][i];
|
||||
}
|
||||
component right = BigMultShortLongUnequal(CHUNK_SIZE, 2 * CHUNK_NUMBER - 1, CHUNK_NUMBER, 3 * CHUNK_SIZE + 2 * LOGK + 3); // 3k-2 registers < (3 * CHUNK_NUMBER + 1)*CHUNK_NUMBER*2^{3n}
|
||||
for(var i = 0; i < 2 * CHUNK_NUMBER - 1; i++){
|
||||
if (i < CHUNK_NUMBER) {
|
||||
right.a[i] <== 3 * xSq.out[i] + a[i]; // registers in [0, 3 * CHUNK_NUMBER*2^{2n} + 2^CHUNK_SIZE = (3k+2^{-CHUNK_SIZE})*2^{2n})
|
||||
} else {
|
||||
right.a[i] <== 3 * xSq.out[i];
|
||||
}
|
||||
}
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++){
|
||||
right.b[i] <== in[0][0][i] - in[1][0][i];
|
||||
}
|
||||
|
||||
component left = BigMultShortLong(CHUNK_SIZE, CHUNK_NUMBER, 2 * CHUNK_SIZE + 2 + LOGK); // 2k - 1 registers in [0, 4k * 2^{2n})
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++){
|
||||
left.a[i] <== 2 * in[0][1][i];
|
||||
left.b[i] <== in[0][1][i] + in[1][1][i];
|
||||
}
|
||||
|
||||
// prime reduce right - left
|
||||
component diffRed = PrimeReduce(CHUNK_SIZE, CHUNK_NUMBER, 2 * CHUNK_NUMBER-2, P, 4*CHUNK_SIZE + LOGK3);
|
||||
for(var i = 0; i<3 * CHUNK_NUMBER-2; i++){
|
||||
if(i < 2 * CHUNK_NUMBER - 1)
|
||||
diffRed.in[i] <== right.out[i] - left.out[i];
|
||||
else
|
||||
diffRed.in[i] <== right.out[i];
|
||||
}
|
||||
// inputs of diffRed has registers < (3k+2^{-CHUNK_SIZE})CHUNK_NUMBER*2^{3n} + 4k*2^{2n} < (3k^2 + 1)*2^{3n} assuming 5k <= 2^CHUNK_SIZE
|
||||
// diffRed.out has registers < (3k + 1)*(2k - 1) * 2^{4n}
|
||||
component constraint = SignedCheckCarryModToZero(CHUNK_SIZE, CHUNK_NUMBER, 4*CHUNK_SIZE + LOGK3, P);
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++)
|
||||
constraint.in[i] <== diffRed.out[i];
|
||||
}
|
||||
|
||||
// requires x_1 != x_2
|
||||
// assume P is size CHUNK_NUMBER array, the prime that curve lives over
|
||||
//
|
||||
// Implements:
|
||||
// Given a = (x_1, y_1) and b = (x_2, y_2),
|
||||
// assume x_1 != x_2 and a != -b,
|
||||
// Find a + b = (x_3, y_3)
|
||||
// By solving:
|
||||
// x_1 + x_2 + x_3 - LAMBDA^2 = 0 mod P
|
||||
// y_3 = LAMBDA (x_1 - x_3) - y_1 mod P
|
||||
// where LAMBDA = (y_2-y_1)/(x_2-x_1) is the slope of the line between (x_1, y_1) and (x_2, y_2)
|
||||
// these equations are equivalent to:
|
||||
// (x_1 + x_2 + x_3)*(x_2 - x_1)^2 = (y_2 - y_1)^2 mod P
|
||||
// (y_1 + y_3)*(x_2 - x_1) = (y_2 - y_1)*(x_1 - x_3) mod P
|
||||
template EllipticCurveAddUnequal(CHUNK_SIZE, CHUNK_NUMBER, P) {
|
||||
signal input a[2][CHUNK_NUMBER];
|
||||
signal input b[2][CHUNK_NUMBER];
|
||||
|
||||
signal output out[2][CHUNK_NUMBER];
|
||||
|
||||
var LOGK = log_ceil(CHUNK_NUMBER);
|
||||
var LOGK3 = log_ceil( (3 * CHUNK_NUMBER*CHUNK_NUMBER)*(2 * CHUNK_NUMBER - 1) + 1 );
|
||||
assert(4*CHUNK_SIZE + LOGK3 < 251);
|
||||
|
||||
// precompute LAMBDA and x_3 and then y_3
|
||||
var DY[150] = long_sub_mod(CHUNK_SIZE, CHUNK_NUMBER, b[1], a[1], P);
|
||||
var DX[150] = long_sub_mod(CHUNK_SIZE, CHUNK_NUMBER, b[0], a[0], P);
|
||||
var DX_INV[150] = mod_inv(CHUNK_SIZE, CHUNK_NUMBER, DX, P);
|
||||
var LAMBDA[150] = prod_mod(CHUNK_SIZE, CHUNK_NUMBER, DY, DX_INV, P);
|
||||
var LAMBDA_SQ[150] = prod_mod(CHUNK_SIZE, CHUNK_NUMBER, LAMBDA, LAMBDA, P);
|
||||
// out[0] = x_3 = LAMB^2 - a[0] - b[0] % P
|
||||
// out[1] = y_3 = LAMB * (a[0] - x_3) - a[1] % P
|
||||
var X3[150] = long_sub_mod(CHUNK_SIZE, CHUNK_NUMBER, long_sub_mod(CHUNK_SIZE, CHUNK_NUMBER, LAMBDA_SQ, a[0], P), b[0], P);
|
||||
var Y3[150] = long_sub_mod(CHUNK_SIZE, CHUNK_NUMBER, prod_mod(CHUNK_SIZE, CHUNK_NUMBER, LAMBDA, long_sub_mod(CHUNK_SIZE, CHUNK_NUMBER, a[0], X3, P), P), a[1], P);
|
||||
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++){
|
||||
out[0][i] <-- X3[i];
|
||||
out[1][i] <-- Y3[i];
|
||||
}
|
||||
|
||||
// constrain x_3 by CUBIC (x_1 + x_2 + x_3) * (x_2 - x_1)^2 - (y_2 - y_1)^2 = 0 mod P
|
||||
|
||||
component dxSq = BigMultShortLong(CHUNK_SIZE, CHUNK_NUMBER, 2 * CHUNK_SIZE+LOGK+2); // 2k - 1 registers abs val < CHUNK_NUMBER*2^{2n}
|
||||
component dySq = BigMultShortLong(CHUNK_SIZE, CHUNK_NUMBER, 2 * CHUNK_SIZE+LOGK+2); // 2k - 1 registers < CHUNK_NUMBER*2^{2n}
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++){
|
||||
dxSq.a[i] <== b[0][i] - a[0][i];
|
||||
dxSq.b[i] <== b[0][i] - a[0][i];
|
||||
|
||||
dySq.a[i] <== b[1][i] - a[1][i];
|
||||
dySq.b[i] <== b[1][i] - a[1][i];
|
||||
}
|
||||
|
||||
// x_1 + x_2 + x_3 has registers in [0, 3 * 2^CHUNK_SIZE)
|
||||
component cubic = BigMultShortLongUnequal(CHUNK_SIZE, CHUNK_NUMBER, 2 * CHUNK_NUMBER - 1, 3 * CHUNK_SIZE+4+2 * LOGK); // 3k-2 registers < 3 * CHUNK_NUMBER^2 * 2^{3n} )
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++)
|
||||
cubic.a[i] <== a[0][i] + b[0][i] + out[0][i];
|
||||
for(var i = 0; i < 2 * CHUNK_NUMBER - 1; i++){
|
||||
cubic.b[i] <== dxSq.out[i];
|
||||
}
|
||||
|
||||
component cubicRed = PrimeReduce(CHUNK_SIZE, CHUNK_NUMBER, 2 * CHUNK_NUMBER-2, P, 4*CHUNK_SIZE + LOGK3);
|
||||
for(var i = 0; i < 2 * CHUNK_NUMBER - 1; i++)
|
||||
cubicRed.in[i] <== cubic.out[i] - dySq.out[i]; // registers abs val < 3k^2 * 2^{3n} + CHUNK_NUMBER*2^{2n} < (3k^2 + 1)2^{3n}
|
||||
for(var i=2 * CHUNK_NUMBER - 1; i<3 * CHUNK_NUMBER-2; i++)
|
||||
cubicRed.in[i] <== cubic.out[i];
|
||||
// cubicRed has CHUNK_NUMBER registers < (3k^2 + 1)(2k - 1) * 2^{4n}
|
||||
|
||||
component cubicMod = SignedCheckCarryModToZero(CHUNK_SIZE, CHUNK_NUMBER, 4*CHUNK_SIZE + LOGK3, P);
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++)
|
||||
cubicMod.in[i] <== cubicRed.out[i];
|
||||
// END OF CONSTRAINING X3
|
||||
|
||||
// constrain y_3 by (y_1 + y_3) * (x_2 - x_1) = (y_2 - y_1)*(x_1 - x_3) mod P
|
||||
component yConstraint = PointOnLine(CHUNK_SIZE, CHUNK_NUMBER, P); // 2k - 1 registers in [0, CHUNK_NUMBER*2^{2n + 1})
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++)for(var j = 0; j < 2; j++){
|
||||
yConstraint.in[0][j][i] <== a[j][i];
|
||||
yConstraint.in[1][j][i] <== b[j][i];
|
||||
yConstraint.in[2][j][i] <== out[j][i];
|
||||
}
|
||||
// END OF CONSTRAINING Y3
|
||||
|
||||
// check if out[][] has registers in [0, 2^CHUNK_SIZE)
|
||||
component rangeCheck = RangeCheck2D(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
for(var j = 0; j < 2; j++)for(var i = 0; i < CHUNK_NUMBER; i++)
|
||||
rangeCheck.in[j][i] <== out[j][i];
|
||||
}
|
||||
|
||||
|
||||
// Elliptic curve is E : y**2 = x**3 + aX + b
|
||||
// assuming a < 2^CHUNK_SIZE for now
|
||||
// Note that for BLS12-381, a = 0, b = 4
|
||||
|
||||
// Implements:
|
||||
// computing 2P on elliptic curve E for P = (x_1, y_1)
|
||||
// formula from https://crypto.stanford.edu/pbc/notes/elliptic/explicit.html
|
||||
// x_1 = in[0], y_1 = in[1]
|
||||
// assume y_1 != 0 (otherwise 2P = O)
|
||||
|
||||
// LAMB = (3x_1^2 + a) / (2 y_1) % P
|
||||
// x_3 = out[0] = LAMBDA^2 - 2 x_1 % P
|
||||
// y_3 = out[1] = LAMBDA (x_1 - x_3) - y_1 % P
|
||||
|
||||
// We precompute (x_3, y_3) and then constrain by showing that:
|
||||
// * (x_3, y_3) is a valid point on the curve
|
||||
// * (x_3, y_3) is on the tangent line to E at (x_1, y_1)
|
||||
// * x_1 != x_3
|
||||
template EllipticCurveDouble(CHUNK_SIZE, CHUNK_NUMBER, a, b, p) {
|
||||
signal input in[2][CHUNK_NUMBER];
|
||||
signal output out[2][CHUNK_NUMBER];
|
||||
|
||||
var LONG3[CHUNK_NUMBER];
|
||||
LONG3[0] = 3;
|
||||
for (var i = 1; i < CHUNK_NUMBER; i++) {
|
||||
LONG3[i] = 0;
|
||||
}
|
||||
|
||||
// precompute lambda
|
||||
var lamb_num[150] = long_add_mod(CHUNK_SIZE, CHUNK_NUMBER, a, prod_mod(CHUNK_SIZE, CHUNK_NUMBER, LONG3, prod_mod(CHUNK_SIZE, CHUNK_NUMBER, in[0], in[0], p), p), p);
|
||||
var lamb_denom[150] = long_add_mod(CHUNK_SIZE, CHUNK_NUMBER, in[1], in[1], p);
|
||||
var lamb[150] = prod_mod(CHUNK_SIZE, CHUNK_NUMBER, lamb_num, mod_inv(CHUNK_SIZE, CHUNK_NUMBER, lamb_denom, p), p);
|
||||
|
||||
// precompute x_3, y_3
|
||||
var x3[150] = long_sub_mod(CHUNK_SIZE, CHUNK_NUMBER, prod_mod(CHUNK_SIZE, CHUNK_NUMBER, lamb, lamb, p), long_add_mod(CHUNK_SIZE, CHUNK_NUMBER, in[0], in[0], p), p);
|
||||
var y3[150] = long_sub_mod(CHUNK_SIZE, CHUNK_NUMBER, prod_mod(CHUNK_SIZE, CHUNK_NUMBER, lamb, long_sub_mod(CHUNK_SIZE, CHUNK_NUMBER, in[0], x3, p), p), in[1], p);
|
||||
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++){
|
||||
out[0][i] <-- x3[i];
|
||||
out[1][i] <-- y3[i];
|
||||
}
|
||||
// check if out[][] has registers in [0, 2^CHUNK_SIZE)
|
||||
component range_check = RangeCheck2D(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
for(var j = 0; j < 2; j++)for(var i = 0; i < CHUNK_NUMBER; i++)
|
||||
range_check.in[j][i] <== out[j][i];
|
||||
|
||||
component point_on_tangent = PointOnTangent(CHUNK_SIZE, CHUNK_NUMBER, a, p);
|
||||
for(var j = 0; j < 2; j++)for(var i = 0; i < CHUNK_NUMBER; i++){
|
||||
point_on_tangent.in[0][j][i] <== in[j][i];
|
||||
point_on_tangent.in[1][j][i] <== out[j][i];
|
||||
}
|
||||
|
||||
component point_on_curve = PointOnCurve(CHUNK_SIZE, CHUNK_NUMBER, a, b, p);
|
||||
for(var j = 0; j < 2; j++)for(var i = 0; i < CHUNK_NUMBER; i++)
|
||||
point_on_curve.in[j][i] <== out[j][i];
|
||||
|
||||
component x3_eq_x1 = FpIsEqual(CHUNK_SIZE, CHUNK_NUMBER, p);
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++){
|
||||
x3_eq_x1.in[0][i] <== out[0][i];
|
||||
x3_eq_x1.in[1][i] <== in[0][i];
|
||||
}
|
||||
x3_eq_x1.out === 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Fp curve y^2 = x^3 + A1*x + B1
|
||||
// Assume curve has no Fp points of order 2, i.e., x^3 + A1*x + B1 has no Fp roots
|
||||
// Fact: ^ this is the case for BLS12-381 and BN254
|
||||
// If isInfinity = 1, replace `out` with `a` so if `a` was on curve, so is output
|
||||
template EllipticCurveAdd(CHUNK_SIZE, CHUNK_NUMBER, A1, B1, P){
|
||||
signal input a[2][CHUNK_NUMBER];
|
||||
signal input aIsInfinity;
|
||||
signal input b[2][CHUNK_NUMBER];
|
||||
signal input bIsInfinity;
|
||||
|
||||
signal output out[2][CHUNK_NUMBER];
|
||||
signal output isInfinity;
|
||||
|
||||
component xEqual = FpIsEqual(CHUNK_SIZE, CHUNK_NUMBER, P);
|
||||
component yEqual = FpIsEqual(CHUNK_SIZE, CHUNK_NUMBER, P);
|
||||
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
xEqual.in[0][idx] <== a[0][idx];
|
||||
xEqual.in[1][idx] <== b[0][idx];
|
||||
|
||||
yEqual.in[0][idx] <== a[1][idx];
|
||||
yEqual.in[1][idx] <== b[1][idx];
|
||||
}
|
||||
// if a.x = b.x then a = +-b
|
||||
// if a = b then a + b = 2 * a so we need to do point doubling
|
||||
// if a = -a then out is infinity
|
||||
signal addIsDouble;
|
||||
addIsDouble <== xEqual.out * yEqual.out; // AND gate
|
||||
|
||||
// if a.x = b.x, need to replace b.x by a different number just so AddUnequal doesn't break
|
||||
// I will do this in a dumb way: replace b[0][0] by (b[0][0] == 0)
|
||||
component isZero = IsZero();
|
||||
isZero.in <== b[0][0];
|
||||
|
||||
component add = EllipticCurveAddUnequal(CHUNK_SIZE, CHUNK_NUMBER, P);
|
||||
component doub = EllipticCurveDouble(CHUNK_SIZE, CHUNK_NUMBER, A1, B1, P);
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
add.a[i][idx] <== a[i][idx];
|
||||
if( i == 0 && idx == 0 ){
|
||||
add.b[i][idx] <== b[i][idx] + xEqual.out * (isZero.out - b[i][idx]);
|
||||
} else {
|
||||
add.b[i][idx] <== b[i][idx];
|
||||
}
|
||||
doub.in[i][idx] <== a[i][idx];
|
||||
}
|
||||
}
|
||||
|
||||
// out = O iff ( a = O AND b = O ) OR (a != 0 AND b != 0) AND ( xEqual AND NOT yEqual )
|
||||
signal ab0;
|
||||
ab0 <== aIsInfinity * bIsInfinity;
|
||||
signal abNon0;
|
||||
abNon0 <== (1 - aIsInfinity) * (1 - bIsInfinity);
|
||||
signal aNegB;
|
||||
aNegB <== xEqual.out - xEqual.out * yEqual.out;
|
||||
signal inverse;
|
||||
inverse <== abNon0 * aNegB;
|
||||
isInfinity <== ab0 + inverse - ab0 * inverse; // OR gate
|
||||
|
||||
signal tmp[3][2][CHUNK_NUMBER];
|
||||
for(var i = 0; i < 2; i++) {
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
tmp[0][i][idx] <== add.out[i][idx] + addIsDouble * (doub.out[i][idx] - add.out[i][idx]);
|
||||
// if a = O, then a + b = b
|
||||
tmp[1][i][idx] <== tmp[0][i][idx] + aIsInfinity * (b[i][idx] - tmp[0][i][idx]);
|
||||
// if b = O, then a + b = a
|
||||
tmp[2][i][idx] <== tmp[1][i][idx] + bIsInfinity * (a[i][idx] - tmp[1][i][idx]);
|
||||
out[i][idx] <== tmp[2][i][idx] + isInfinity * (a[i][idx] - tmp[2][i][idx]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Curve E : y^2 = x^3 + b
|
||||
// Inputs:
|
||||
// in is 2 x CHUNK_NUMBER array where P = (x, y) is a point in E(Fp)
|
||||
// inIsInfinity = 1 if P = O, else = 0
|
||||
// Output:
|
||||
// out = [x]P is 2 x CHUNK_NUMBER array representing a point in E(Fp)
|
||||
// isInfinity = 1 if [x]P = O, else = 0
|
||||
// Assume:
|
||||
// x in [0, 2^250)
|
||||
// `in` is point in E even if inIsInfinity = 1 just so nothing goes wrong
|
||||
// E(Fp) has no points of order 2
|
||||
template EllipticCurveScalarMultiply(CHUNK_SIZE, CHUNK_NUMBER, b, x, p){
|
||||
signal input in[2][CHUNK_NUMBER];
|
||||
signal input inIsInfinity;
|
||||
|
||||
signal output out[2][CHUNK_NUMBER];
|
||||
signal output isInfinity;
|
||||
|
||||
var LOGK = log_ceil(CHUNK_NUMBER);
|
||||
|
||||
var BITS[250];
|
||||
var BITS_LENGTH;
|
||||
var SIG_BITS = 0;
|
||||
for (var i = 0; i < 250; i++) {
|
||||
BITS[i] = (x >> i) & 1;
|
||||
if(BITS[i] == 1){
|
||||
SIG_BITS++;
|
||||
BITS_LENGTH = i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
signal r[BITS_LENGTH][2][CHUNK_NUMBER];
|
||||
signal rIsO[BITS_LENGTH];
|
||||
component pDouble[BITS_LENGTH];
|
||||
component pAdd[SIG_BITS];
|
||||
var CUR_ID = 0;
|
||||
|
||||
// if in = O then [x]O = O so there's no point to any of this
|
||||
signal P[2][CHUNK_NUMBER];
|
||||
for(var j = 0; j < 2; j++)for(var idx = 0; idx < CHUNK_NUMBER; idx++)
|
||||
P[j][idx] <== in[j][idx];
|
||||
|
||||
for(var i = BITS_LENGTH - 1; i >= 0; i--){
|
||||
if( i == BITS_LENGTH - 1 ){
|
||||
for(var j = 0; j < 2; j++)for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
r[i][j][idx] <== P[j][idx];
|
||||
}
|
||||
rIsO[i] <== 0;
|
||||
} else {
|
||||
// E(Fp) has no points of order 2, so the only way 2 * r[i + 1] = O is if r[i + 1] = O
|
||||
pDouble[i] = EllipticCurveDouble(CHUNK_SIZE, CHUNK_NUMBER, 0, b, p);
|
||||
for(var j = 0; j < 2; j++)for(var idx = 0; idx < CHUNK_NUMBER; idx++)
|
||||
pDouble[i].in[j][idx] <== r[i + 1][j][idx];
|
||||
|
||||
if(BITS[i] == 0){
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
r[i][j][idx] <== pDouble[i].out[j][idx];
|
||||
}
|
||||
}
|
||||
rIsO[i] <== rIsO[i + 1];
|
||||
} else {
|
||||
// pAdd[CUR_ID] = pDouble[i] + P
|
||||
pAdd[CUR_ID] = EllipticCurveAdd(CHUNK_SIZE, CHUNK_NUMBER, 0, b, p);
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
pAdd[CUR_ID].a[j][idx] <== pDouble[i].out[j][idx];
|
||||
pAdd[CUR_ID].b[j][idx] <== P[j][idx];
|
||||
}
|
||||
}
|
||||
pAdd[CUR_ID].aIsInfinity <== rIsO[i + 1];
|
||||
pAdd[CUR_ID].bIsInfinity <== 0;
|
||||
|
||||
rIsO[i] <== pAdd[CUR_ID].isInfinity;
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
r[i][j][idx] <== pAdd[CUR_ID].out[j][idx];
|
||||
}
|
||||
}
|
||||
|
||||
CUR_ID++;
|
||||
}
|
||||
}
|
||||
}
|
||||
// output = O if input = O or r[0] = O
|
||||
isInfinity <== inIsInfinity + rIsO[0] - inIsInfinity * rIsO[0];
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
out[i][idx] <== r[0][i][idx] + isInfinity * (in[i][idx] - r[0][i][idx]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Curve E : y^2 = x^3 + b
|
||||
// Inputs:
|
||||
// in = P is 2 x CHUNK_NUMBER array where P = (x, y) is a point in E(Fp)
|
||||
// Output:
|
||||
// out = [x]P is 2 x CHUNK_NUMBER array representing a point in E(Fp)
|
||||
// Assume:
|
||||
// x in [0, 2^250)
|
||||
// E(Fp) has no points of order 2
|
||||
// P has order > x so never hit point at infinity, and can always use add unequal: constraint assertion fails if add unequal fails
|
||||
template EllipticCurveScalarMultiplyUnequal(CHUNK_SIZE, CHUNK_NUMBER, b, x, P){
|
||||
signal input in[2][CHUNK_NUMBER];
|
||||
signal output out[2][CHUNK_NUMBER];
|
||||
|
||||
var LOGK = log_ceil(CHUNK_NUMBER);
|
||||
|
||||
var BITS[250];
|
||||
var BITS_LENGTH;
|
||||
var SIG_BITS = 0;
|
||||
for (var i = 0; i < 250; i++) {
|
||||
BITS[i] = (x >> i) & 1;
|
||||
if(BITS[i] == 1){
|
||||
SIG_BITS++;
|
||||
BITS_LENGTH = i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
signal r[BITS_LENGTH][2][CHUNK_NUMBER];
|
||||
component pDouble[BITS_LENGTH];
|
||||
component pAdd[SIG_BITS];
|
||||
component add_exception[SIG_BITS];
|
||||
var CUR_ID = 0;
|
||||
|
||||
for(var i=BITS_LENGTH - 1; i >= 0; i--){
|
||||
if( i == BITS_LENGTH - 1 ){
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
r[i][j][idx] <== in[j][idx];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// E(Fp) has no points of order 2, so the only way 2 * r[i + 1] = O is if r[i + 1] = O
|
||||
pDouble[i] = EllipticCurveDouble(CHUNK_SIZE, CHUNK_NUMBER, 0, b, P);
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
pDouble[i].in[j][idx] <== r[i + 1][j][idx];
|
||||
}
|
||||
}
|
||||
if(BITS[i] == 0){
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
r[i][j][idx] <== pDouble[i].out[j][idx];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Constrain that pDouble[i].x != P.x
|
||||
add_exception[CUR_ID] = FpIsEqual(CHUNK_SIZE, CHUNK_NUMBER, P);
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
add_exception[CUR_ID].in[0][idx] <== pDouble[i].out[0][idx];
|
||||
add_exception[CUR_ID].in[1][idx] <== in[0][idx];
|
||||
}
|
||||
add_exception[CUR_ID].out === 0;
|
||||
|
||||
// pAdd[CUR_ID] = pDouble[i] + P
|
||||
pAdd[CUR_ID] = EllipticCurveAddUnequal(CHUNK_SIZE, CHUNK_NUMBER, P);
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
pAdd[CUR_ID].a[j][idx] <== pDouble[i].out[j][idx];
|
||||
pAdd[CUR_ID].b[j][idx] <== in[j][idx];
|
||||
}
|
||||
}
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
r[i][j][idx] <== pAdd[CUR_ID].out[j][idx];
|
||||
}
|
||||
}
|
||||
CUR_ID++;
|
||||
}
|
||||
}
|
||||
}
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
out[i][idx] <== r[0][i][idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,758 @@
|
||||
pragma circom 2.0.3;
|
||||
|
||||
include "circomlib/circuits/bitify.circom";
|
||||
|
||||
include "../../../bigInt/bigInt.circom";
|
||||
include "../../../bigInt/bigIntFunc.circom";
|
||||
include "./fp.circom";
|
||||
include "./fp2.circom";
|
||||
include "./fp12.circom";
|
||||
include "./curve.circom";
|
||||
include "./bls12_381Func.circom";
|
||||
|
||||
// in[i] = (x_i, y_i)
|
||||
// Implements constraint: (y_1 + y_3) * (x_2 - x_1) - (y_2 - y_1) * (x_1 - x_3) = 0 mod P
|
||||
// used to show (x1, y1), (x2, y2), (x3, -y3) are co-linear
|
||||
template PointOnLineFp2(CHUNK_SIZE, CHUNK_NUMBER, P) {
|
||||
signal input in[3][2][2][CHUNK_NUMBER];
|
||||
|
||||
var LOGK = log_ceil(CHUNK_NUMBER);
|
||||
var LOGK2 = log_ceil(6 * CHUNK_NUMBER*CHUNK_NUMBER);
|
||||
assert(3 * CHUNK_SIZE + LOGK2 < 251);
|
||||
|
||||
// AKA check point on line
|
||||
component left = BigMultShortLong2D(CHUNK_SIZE, CHUNK_NUMBER, 2); // 3 x 2k - 1 registers abs val < 4k*2^{2n}
|
||||
for(var i = 0; i < 2; i++) {
|
||||
for (var j = 0; j < CHUNK_NUMBER; j++) {
|
||||
left.a[i][j] <== in[0][1][i][j] + in[2][1][i][j];
|
||||
left.b[i][j] <== in[1][0][i][j] - in[0][0][i][j];
|
||||
}
|
||||
}
|
||||
|
||||
component right = BigMultShortLong2D(CHUNK_SIZE, CHUNK_NUMBER, 2); // 3 x 2k - 1 registers abs val < 2k*2^{2n}
|
||||
for(var i = 0; i < 2; i++) {
|
||||
for (var j = 0; j < CHUNK_NUMBER; j++) {
|
||||
right.a[i][j] <== in[1][1][i][j] - in[0][1][i][j];
|
||||
right.b[i][j] <== in[0][0][i][j] - in[2][0][i][j];
|
||||
}
|
||||
}
|
||||
|
||||
component diffRed[2];
|
||||
diffRed[0] = PrimeReduce(CHUNK_SIZE, CHUNK_NUMBER, CHUNK_NUMBER - 1, P, 3 * CHUNK_SIZE + 2 * LOGK + 2);
|
||||
diffRed[1] = PrimeReduce(CHUNK_SIZE, CHUNK_NUMBER, CHUNK_NUMBER - 1, P, 3 * CHUNK_SIZE + 2 * LOGK + 1);
|
||||
for(var i = 0; i < 2 * CHUNK_NUMBER - 1; i++) {
|
||||
diffRed[0].in[i] <== left.out[0][i] - left.out[2][i] - right.out[0][i] + right.out[2][i];
|
||||
diffRed[1].in[i] <== left.out[1][i] - right.out[1][i];
|
||||
}
|
||||
// diffRed has CHUNK_NUMBER registers abs val < 6 * CHUNK_NUMBER^2 * 2^{3n} -- to see this, easier to use SignedFp2MultiplyNoCarry instead of BigMultShortLong2D
|
||||
component diffMod[2];
|
||||
for (var j = 0; j < 2; j++) {
|
||||
diffMod[j] = SignedCheckCarryModToZero(CHUNK_SIZE, CHUNK_NUMBER, 3 * CHUNK_SIZE + LOGK2, P);
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
diffMod[j].in[i] <== diffRed[j].out[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// in = (x, y)
|
||||
// Implements:
|
||||
// x^3 + ax + B - y^2 = 0 mod P
|
||||
// Assume: A, B in [0, 2^CHUNK_SIZE). A, B are complex
|
||||
template PointOnCurveFp2(CHUNK_SIZE, CHUNK_NUMBER, A, B, P){
|
||||
signal input in[2][2][CHUNK_NUMBER];
|
||||
|
||||
var LOGK = log_ceil(CHUNK_NUMBER);
|
||||
var LOGK3 = log_ceil((2 * CHUNK_NUMBER - 1) * (4 * CHUNK_NUMBER*CHUNK_NUMBER) + 1);
|
||||
assert(4 * CHUNK_SIZE + LOGK3 < 251);
|
||||
|
||||
// compute x^3, y^2
|
||||
component xSq = SignedFp2MultiplyNoCarryUnequal(CHUNK_SIZE, CHUNK_NUMBER, CHUNK_NUMBER, 2 * CHUNK_SIZE + 1 + LOGK); // 2k - 1 registers in [0, 2 * CHUNK_NUMBER*2^{2n})
|
||||
component ySq = SignedFp2MultiplyNoCarryUnequal(CHUNK_SIZE, CHUNK_NUMBER, CHUNK_NUMBER, 2 * CHUNK_SIZE + 1 + LOGK); // 2k - 1 registers in [0, 2 * CHUNK_NUMBER*2^{2n})
|
||||
for (var i = 0; i < 2; i++) {
|
||||
for (var j = 0; j < CHUNK_NUMBER; j++) {
|
||||
xSq.a[i][j] <== in[0][i][j];
|
||||
xSq.b[i][j] <== in[0][i][j];
|
||||
ySq.a[i][j] <== in[1][i][j];
|
||||
ySq.b[i][j] <== in[1][i][j];
|
||||
}
|
||||
}
|
||||
component xCu = SignedFp2MultiplyNoCarryUnequal(CHUNK_SIZE, 2 * CHUNK_NUMBER - 1, CHUNK_NUMBER, 3 * CHUNK_SIZE + 2 * LOGK + 2); // 3k - 2 registers in [0, 4 * CHUNK_NUMBER^2 * 2^{3n})
|
||||
for (var i = 0; i < 2; i++) {
|
||||
for (var j = 0; j < 2 * CHUNK_NUMBER - 1; j++) {
|
||||
xCu.a[i][j] <== xSq.out[i][j];
|
||||
}
|
||||
for (var j = 0; j < CHUNK_NUMBER; j++) {
|
||||
xCu.b[i][j] <== in[0][i][j];
|
||||
}
|
||||
}
|
||||
|
||||
// xCu + A x + B has 3k - 2 registers < (4k^2 + 1/2^CHUNK_SIZE + 1/2^2n)2^{3n} <= (4 * CHUNK_NUMBER^2 + 2/2^CHUNK_SIZE)2^{3n}
|
||||
component cuRed[2];
|
||||
for (var j = 0; j < 2; j++) {
|
||||
cuRed[j] = PrimeReduce(CHUNK_SIZE, CHUNK_NUMBER, 2 * CHUNK_NUMBER - 2, P, 4 * CHUNK_SIZE + 3 * LOGK + 4);
|
||||
for(var i = 0; i < 3 * CHUNK_NUMBER - 2; i++){
|
||||
if(i == 0) {
|
||||
if(j == 0)
|
||||
cuRed[j].in[i] <== xCu.out[j][i] + A[0] * in[0][0][i] - A[1] * in[0][1][i] + B[j];
|
||||
else
|
||||
cuRed[j].in[i] <== xCu.out[j][i] + A[0] * in[0][1][i] + A[1] * in[0][0][i] + B[j];
|
||||
}
|
||||
else{
|
||||
if(i < CHUNK_NUMBER){
|
||||
if(j == 0)
|
||||
cuRed[j].in[i] <== xCu.out[j][i] + A[0] * in[0][0][i] - A[1] * in[0][1][i];
|
||||
else
|
||||
cuRed[j].in[i] <== xCu.out[j][i] + A[0] * in[0][1][i] + A[1] * in[0][0][i];
|
||||
}
|
||||
else
|
||||
cuRed[j].in[i] <== xCu.out[j][i];
|
||||
}
|
||||
}
|
||||
}
|
||||
// cuRed has CHUNK_NUMBER registers < (2k - 1) * (4 * CHUNK_NUMBER^2 + 2/2^CHUNK_SIZE)2^{4n} < 2^{4n + 3LOGK + 4}
|
||||
|
||||
component ySqRed[2]; // CHUNK_NUMBER registers < 2k^2 * 2^{3n}
|
||||
for (var i = 0; i < 2; i++) {
|
||||
ySqRed[i] = PrimeReduce(CHUNK_SIZE, CHUNK_NUMBER, CHUNK_NUMBER - 1, P, 3 * CHUNK_SIZE + 2 * LOGK + 1);
|
||||
for(var j = 0; j < 2 * CHUNK_NUMBER - 1; j++){
|
||||
ySqRed[i].in[j] <== ySq.out[i][j];
|
||||
}
|
||||
}
|
||||
|
||||
component constraint[2];
|
||||
constraint[0] = SignedCheckCarryModToZero(CHUNK_SIZE, CHUNK_NUMBER, 4 * CHUNK_SIZE + LOGK3, P);
|
||||
constraint[1] = SignedCheckCarryModToZero(CHUNK_SIZE, CHUNK_NUMBER, 4 * CHUNK_SIZE + LOGK3, P);
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++){
|
||||
constraint[0].in[i] <== cuRed[0].out[i] - ySqRed[0].out[i];
|
||||
constraint[1].in[i] <== cuRed[1].out[i] - ySqRed[1].out[i];
|
||||
}
|
||||
}
|
||||
|
||||
// in = x
|
||||
// Implements:
|
||||
// out = x^3 + ax + B mod P
|
||||
// Assume: A, B are in Fp2, coefficients in [0, 2^CHUNK_SIZE)
|
||||
template EllipticCurveFunction(CHUNK_SIZE, CHUNK_NUMBER, A, B, P){
|
||||
signal input in[2][CHUNK_NUMBER];
|
||||
signal output out[2][CHUNK_NUMBER];
|
||||
|
||||
var LOGK = log_ceil(CHUNK_NUMBER);
|
||||
var LOGK3 = log_ceil((2 * CHUNK_NUMBER - 1) * (4 * CHUNK_NUMBER*CHUNK_NUMBER) + 1);
|
||||
assert(4 * CHUNK_SIZE + LOGK3 < 251);
|
||||
|
||||
// compute x^3, y^2
|
||||
component xSq = SignedFp2MultiplyNoCarryUnequal(CHUNK_SIZE, CHUNK_NUMBER, CHUNK_NUMBER, 2 * CHUNK_SIZE + 1 + LOGK); // 2k - 1 registers in [0, 2 * CHUNK_NUMBER*2^{2n})
|
||||
for (var i = 0; i < 2; i++) {
|
||||
for (var j = 0; j < CHUNK_NUMBER; j++) {
|
||||
xSq.a[i][j] <== in[i][j];
|
||||
xSq.b[i][j] <== in[i][j];
|
||||
}
|
||||
}
|
||||
component xCu = SignedFp2MultiplyNoCarryUnequal(CHUNK_SIZE, 2 * CHUNK_NUMBER - 1, CHUNK_NUMBER, 3 * CHUNK_SIZE + 2 * LOGK + 2); // 3k - 2 registers in [0, 4 * CHUNK_NUMBER^2 * 2^{3n})
|
||||
for (var i = 0; i < 2; i++) {
|
||||
for (var j = 0; j < 2 * CHUNK_NUMBER - 1; j++) {
|
||||
xCu.a[i][j] <== xSq.out[i][j];
|
||||
}
|
||||
for (var j = 0; j < CHUNK_NUMBER; j++) {
|
||||
xCu.b[i][j] <== in[i][j];
|
||||
}
|
||||
}
|
||||
|
||||
// xCu + A x + B has 3k - 2 registers < (4 * CHUNK_NUMBER^2 + 1)2^{3n}
|
||||
component cuRed[2];
|
||||
for (var j = 0; j < 2; j++) {
|
||||
cuRed[j] = PrimeReduce(CHUNK_SIZE, CHUNK_NUMBER, 2 * CHUNK_NUMBER - 2, P, 4 * CHUNK_SIZE + 3 * LOGK + 4);
|
||||
for(var i = 0; i < 3 * CHUNK_NUMBER - 2; i++){
|
||||
if(i == 0) {
|
||||
if(j == 0)
|
||||
cuRed[j].in[i] <== xCu.out[j][i] + A[0] * in[0][i] - A[1] * in[1][i] + B[j];
|
||||
else
|
||||
cuRed[j].in[i] <== xCu.out[j][i] + A[0] * in[1][i] + A[1] * in[0][i] + B[j];
|
||||
}
|
||||
else{
|
||||
if(i < CHUNK_NUMBER){
|
||||
if(j == 0)
|
||||
cuRed[j].in[i] <== xCu.out[j][i] + A[0] * in[0][i] - A[1] * in[1][i];
|
||||
else
|
||||
cuRed[j].in[i] <== xCu.out[j][i] + A[0] * in[1][i] + A[1] * in[0][i];
|
||||
}
|
||||
else
|
||||
cuRed[j].in[i] <== xCu.out[j][i];
|
||||
}
|
||||
}
|
||||
}
|
||||
// cuRed has CHUNK_NUMBER registers < (2k - 1) * (4 * CHUNK_NUMBER^2 + 1)2^{4n} < 2^{4n + 3LOGK + 4}
|
||||
|
||||
component carry = SignedFp2CarryModP(CHUNK_SIZE, CHUNK_NUMBER, 4 * CHUNK_SIZE + LOGK3, P);
|
||||
for(var j = 0; j < 2; j++)for(var i = 0; i < CHUNK_NUMBER; i++)
|
||||
carry.in[j][i] <== cuRed[j].out[i];
|
||||
|
||||
for(var j = 0; j < 2; j++)for(var i = 0; i < CHUNK_NUMBER; i++)
|
||||
out[j][i] <== carry.out[j][i];
|
||||
}
|
||||
|
||||
|
||||
// in[0] = (x_1, y_1), in[1] = (x_3, y_3)
|
||||
// Checks that the line between (x_1, y_1) and (x_3, -y_3) is equal to the tangent line to the elliptic curve at the point (x_1, y_1)
|
||||
// Implements:
|
||||
// (y_1 + y_3) = lambda * (x_1 - x_3)
|
||||
// where lambda = (3 x_1^2 + A)/(2 y_1)
|
||||
// Actual constraint is 2y_1 (y_1 + y_3) = (3 x_1^2 + A) (x_1 - x_3)
|
||||
// A is complex
|
||||
template PointOnTangentFp2(CHUNK_SIZE, CHUNK_NUMBER, A, P){
|
||||
signal input in[2][2][2][CHUNK_NUMBER];
|
||||
|
||||
var LOGK = log_ceil(CHUNK_NUMBER);
|
||||
var LOGK3 = log_ceil((2 * CHUNK_NUMBER - 1) * (12*CHUNK_NUMBER*CHUNK_NUMBER) + 1);
|
||||
assert(4 * CHUNK_SIZE + LOGK3 < 251);
|
||||
component xSq = SignedFp2MultiplyNoCarryUnequal(CHUNK_SIZE, CHUNK_NUMBER, CHUNK_NUMBER, 2 * CHUNK_SIZE + 1 + LOGK); // 2k - 1 registers in [0, 2 * CHUNK_NUMBER*2^{2n})
|
||||
for (var i = 0; i < 2; i++) {
|
||||
for (var j = 0; j < CHUNK_NUMBER; j++) {
|
||||
xSq.a[i][j] <== in[0][0][i][j];
|
||||
xSq.b[i][j] <== in[0][0][i][j];
|
||||
}
|
||||
}
|
||||
component right = SignedFp2MultiplyNoCarryUnequal(CHUNK_SIZE, 2 * CHUNK_NUMBER - 1, CHUNK_NUMBER, 3 * CHUNK_SIZE + 2 * LOGK + 3); // 3k - 2 registers < 2(6 * CHUNK_NUMBER^2 + 2k/2^CHUNK_SIZE)*2^{3n}
|
||||
for(var i = 0; i < 2 * CHUNK_NUMBER - 1; i++){
|
||||
if(i == 0) {
|
||||
right.a[0][i] <== 3 * xSq.out[0][i] + A[0]; // registers in [0, 3 * CHUNK_NUMBER*2^{2n} + 2^CHUNK_SIZE)
|
||||
right.a[1][i] <== 3 * xSq.out[1][i] + A[1];
|
||||
}
|
||||
else {
|
||||
right.a[0][i] <== 3 * xSq.out[0][i];
|
||||
right.a[1][i] <== 3 * xSq.out[1][i];
|
||||
}
|
||||
}
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++){
|
||||
right.b[0][i] <== in[0][0][0][i] - in[1][0][0][i];
|
||||
right.b[1][i] <== in[0][0][1][i] - in[1][0][1][i];
|
||||
}
|
||||
|
||||
component left = SignedFp2MultiplyNoCarryUnequal(CHUNK_SIZE, CHUNK_NUMBER, CHUNK_NUMBER, 2 * CHUNK_SIZE + 3 + LOGK); // 2k - 1 registers in [0, 8k * 2^{2n})
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++){
|
||||
left.a[0][i] <== 2 * in[0][1][0][i];
|
||||
left.a[1][i] <== 2 * in[0][1][1][i];
|
||||
left.b[0][i] <== in[0][1][0][i] + in[1][1][0][i];
|
||||
left.b[1][i] <== in[0][1][1][i] + in[1][1][1][i];
|
||||
}
|
||||
|
||||
// prime reduce right - left
|
||||
component diffRed[2];
|
||||
for (var i = 0; i < 2; i++) {
|
||||
diffRed[i] = PrimeReduce(CHUNK_SIZE, CHUNK_NUMBER, 2 * CHUNK_NUMBER - 2, P, 4 * CHUNK_SIZE + LOGK3);
|
||||
for (var j = 0; j < 3 * CHUNK_NUMBER - 2; j++) {
|
||||
if (j < 2 * CHUNK_NUMBER - 1) {
|
||||
diffRed[i].in[j] <== right.out[i][j] - left.out[i][j];
|
||||
}
|
||||
else {
|
||||
diffRed[i].in[j] <== right.out[i][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
// inputs of diffRed has registers < (12k^2 + 12k/2^CHUNK_SIZE)*2^{3n}
|
||||
// diffRed.out has registers < ((2k - 1)*12k^2 + 1) * 2^{4n} assuming 12k(2k - 1) <= 2^CHUNK_SIZE
|
||||
component constraint[2];
|
||||
for (var i = 0; i < 2; i++) {
|
||||
constraint[i] = SignedCheckCarryModToZero(CHUNK_SIZE, CHUNK_NUMBER, 4 * CHUNK_SIZE + LOGK3, P);
|
||||
for (var j = 0; j < CHUNK_NUMBER; j++) {
|
||||
constraint[i].in[j] <== diffRed[i].out[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// requires x_1 != x_2
|
||||
// assume P is size CHUNK_NUMBER array, the prime that curve lives over
|
||||
//
|
||||
// Implements:
|
||||
// Given A = (x_1, y_1) and B = (x_2, y_2),
|
||||
// assume x_1 != x_2 and A != -B,
|
||||
// Find A + B = (x_3, y_3)
|
||||
// By solving:
|
||||
// x_1 + x_2 + x_3 - lambda^2 = 0 mod P
|
||||
// y_3 = lambda (x_1 - x_3) - y_1 mod P
|
||||
// where lambda = (y_2-y_1)/(x_2-x_1) is the slope of the line between (x_1, y_1) and (x_2, y_2)
|
||||
// these equations are equivalent to:
|
||||
// (x_1 + x_2 + x_3) * (x_2 - x_1)^2 = (y_2 - y_1)^2 mod P
|
||||
// (y_1 + y_3) * (x_2 - x_1) = (y_2 - y_1) * (x_1 - x_3) mod P
|
||||
template EllipticCurveAddUnequalFp2(CHUNK_SIZE, CHUNK_NUMBER, P) {
|
||||
signal input a[2][2][CHUNK_NUMBER];
|
||||
signal input b[2][2][CHUNK_NUMBER];
|
||||
|
||||
signal output out[2][2][CHUNK_NUMBER];
|
||||
|
||||
var LOGK = log_ceil(CHUNK_NUMBER);
|
||||
var LOGK3 = log_ceil((12*CHUNK_NUMBER*CHUNK_NUMBER) * (2 * CHUNK_NUMBER - 1) + 1);
|
||||
assert(4 * CHUNK_SIZE + LOGK3 + 2< 251);
|
||||
|
||||
// precompute lambda and x_3 and then y_3
|
||||
var dy[2][150] = find_Fp2_diff(CHUNK_SIZE, CHUNK_NUMBER, B[1], A[1], P);
|
||||
var dx[2][150] = find_Fp2_diff(CHUNK_SIZE, CHUNK_NUMBER, B[0], A[0], P);
|
||||
var dx_inv[2][150] = find_Fp2_inverse(CHUNK_SIZE, CHUNK_NUMBER, dx, P);
|
||||
var lambda[2][150] = find_Fp2_product(CHUNK_SIZE, CHUNK_NUMBER, dy, dx_inv, P);
|
||||
var lambda_sq[2][150] = find_Fp2_product(CHUNK_SIZE, CHUNK_NUMBER, lambda, lambda, P);
|
||||
// out[0] = x_3 = lamb^2 - A[0] - B[0] % P
|
||||
// out[1] = y_3 = lamb * (A[0] - x_3) - A[1] % P
|
||||
var x3[2][150] = find_Fp2_diff(CHUNK_SIZE, CHUNK_NUMBER, find_Fp2_diff(CHUNK_SIZE, CHUNK_NUMBER, lambda_sq, A[0], P), B[0], P);
|
||||
var y3[2][150] = find_Fp2_diff(CHUNK_SIZE, CHUNK_NUMBER, find_Fp2_product(CHUNK_SIZE, CHUNK_NUMBER, lambda, find_Fp2_diff(CHUNK_SIZE, CHUNK_NUMBER, A[0], x3, P), P), A[1], P);
|
||||
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++){
|
||||
out[0][0][i] <-- x3[0][i];
|
||||
out[0][1][i] <-- x3[1][i];
|
||||
out[1][0][i] <-- y3[0][i];
|
||||
out[1][1][i] <-- y3[1][i];
|
||||
}
|
||||
|
||||
// constrain x_3 by CUBIC (x_1 + x_2 + x_3) * (x_2 - x_1)^2 - (y_2 - y_1)^2 = 0 mod P
|
||||
|
||||
component dx_sq = BigMultShortLong2D(CHUNK_SIZE, CHUNK_NUMBER, 2); // 2k - 1 registers abs val < 2k*2^{2n}
|
||||
component dy_sq = BigMultShortLong2D(CHUNK_SIZE, CHUNK_NUMBER, 2); // 2k - 1 registers abs val < 2k*2^{2n}
|
||||
for (var i = 0; i < 2; i++) {
|
||||
for (var j = 0; j < CHUNK_NUMBER; j++) {
|
||||
dx_sq.a[i][j] <== B[0][i][j] - A[0][i][j];
|
||||
dx_sq.b[i][j] <== B[0][i][j] - A[0][i][j];
|
||||
dy_sq.a[i][j] <== B[1][i][j] - A[1][i][j];
|
||||
dy_sq.b[i][j] <== B[1][i][j] - A[1][i][j];
|
||||
}
|
||||
}
|
||||
|
||||
// x_1 + x_2 + x_3 has registers in [0, 3 * 2^CHUNK_SIZE)
|
||||
component cubic = BigMultShortLong2DUnequal(CHUNK_SIZE, CHUNK_NUMBER, 2 * CHUNK_NUMBER - 1, 2, 2); // 3k - 2 x 3 registers < 12 * CHUNK_NUMBER^2 * 2^{3n})
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
cubic.a[0][i] <== A[0][0][i] + B[0][0][i] + out[0][0][i];
|
||||
cubic.a[1][i] <== A[0][1][i] + B[0][1][i] + out[0][1][i];
|
||||
}
|
||||
for(var i = 0; i < 2 * CHUNK_NUMBER - 1; i++){
|
||||
cubic.b[0][i] <== dx_sq.out[0][i] - dx_sq.out[2][i];
|
||||
cubic.b[1][i] <== dx_sq.out[1][i];
|
||||
}
|
||||
|
||||
component cubic_red[2];
|
||||
cubic_red[0] = PrimeReduce(CHUNK_SIZE, CHUNK_NUMBER, 2 * CHUNK_NUMBER - 2, P, 4 * CHUNK_SIZE + LOGK3 + 2);
|
||||
cubic_red[1] = PrimeReduce(CHUNK_SIZE, CHUNK_NUMBER, 2 * CHUNK_NUMBER - 2, P, 4 * CHUNK_SIZE + LOGK3 + 2);
|
||||
for(var i = 0; i < 2 * CHUNK_NUMBER - 1; i++) {
|
||||
// get i^2 parts too!
|
||||
cubic_red[0].in[i] <== cubic.out[0][i] - cubic.out[2][i] - dy_sq.out[0][i] + dy_sq.out[2][i]; // registers abs val < 12*CHUNK_NUMBER^2 * 2^{3n} + 2k*2^{2n} <= (12k^2 + 2k/2^CHUNK_SIZE) * 2^{3n}
|
||||
cubic_red[1].in[i] <== cubic.out[1][i] - dy_sq.out[1][i]; // registers in < 12*CHUNK_NUMBER^2 * 2^{3n} + 4k*2^{2n} < (12k + 1)CHUNK_NUMBER * 2^{3n})
|
||||
}
|
||||
for(var i=2 * CHUNK_NUMBER - 1; i < 3 * CHUNK_NUMBER - 2; i++) {
|
||||
cubic_red[0].in[i] <== cubic.out[0][i] - cubic.out[2][i];
|
||||
cubic_red[1].in[i] <== cubic.out[1][i];
|
||||
}
|
||||
// cubic_red has CHUNK_NUMBER registers < ((2k - 1)*12k^2 + 1) * 2^{4n} assuming 2k(2k - 1) <= 2^CHUNK_SIZE
|
||||
|
||||
component cubic_mod[2];
|
||||
cubic_mod[0] = SignedCheckCarryModToZero(CHUNK_SIZE, CHUNK_NUMBER, 4 * CHUNK_SIZE + LOGK3 + 2, P);
|
||||
cubic_mod[1] = SignedCheckCarryModToZero(CHUNK_SIZE, CHUNK_NUMBER, 4 * CHUNK_SIZE + LOGK3 + 2, P);
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
cubic_mod[0].in[i] <== cubic_red[0].out[i];
|
||||
cubic_mod[1].in[i] <== cubic_red[1].out[i];
|
||||
}
|
||||
// END OF CONSTRAINING x3
|
||||
|
||||
// constrain y_3 by (y_1 + y_3) * (x_2 - x_1) = (y_2 - y_1) * (x_1 - x_3) mod P
|
||||
component y_constraint = PointOnLineFp2(CHUNK_SIZE, CHUNK_NUMBER, P);
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++){
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var ind = 0; ind < 2; ind++) {
|
||||
y_constraint.in[0][j][ind][i] <== A[j][ind][i];
|
||||
y_constraint.in[1][j][ind][i] <== B[j][ind][i];
|
||||
y_constraint.in[2][j][ind][i] <== out[j][ind][i];
|
||||
}
|
||||
}
|
||||
}
|
||||
// END OF CONSTRAINING y3
|
||||
|
||||
// check if out[][] has registers in [0, 2^CHUNK_SIZE)
|
||||
component range_check[2];
|
||||
range_check[0] = RangeCheck2D(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
range_check[1] = RangeCheck2D(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
range_check[0].in[j][i] <== out[0][j][i];
|
||||
range_check[1].in[j][i] <== out[1][j][i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Elliptic curve is E : y**2 = x**3 + ax + B
|
||||
// assuming A < 2^CHUNK_SIZE for now, B is complex
|
||||
// Note that for BLS12-381 twisted, A = 0, B = 4+4u
|
||||
|
||||
// Implements:
|
||||
// computing 2P on elliptic curve E for P = (x_1, y_1)
|
||||
// formula from https://crypto.stanford.edu/pbc/notes/elliptic/explicit.html
|
||||
// x_1 = in[0], y_1 = in[1]
|
||||
// assume y_1 != 0 (otherwise 2P = O)
|
||||
|
||||
// lamb = (3x_1^2 + A) / (2 y_1) % P
|
||||
// x_3 = out[0] = lambda^2 - 2 x_1 % P
|
||||
// y_3 = out[1] = lambda (x_1 - x_3) - y_1 % P
|
||||
|
||||
// We precompute (x_3, y_3) and then constrain by showing that:
|
||||
// * (x_3, y_3) is A valid point on the curve
|
||||
// * the slope (y_3 - y_1)/(x_3 - x_1) equals
|
||||
// * x_1 != x_3
|
||||
template EllipticCurveDoubleFp2(CHUNK_SIZE, CHUNK_NUMBER, A, B, P) {
|
||||
signal input in[2][2][CHUNK_NUMBER];
|
||||
signal output out[2][2][CHUNK_NUMBER];
|
||||
|
||||
var long_a[2][CHUNK_NUMBER];
|
||||
var long_3[2][CHUNK_NUMBER];
|
||||
long_a[0][0] = A[0];
|
||||
long_3[0][0] = 3;
|
||||
long_a[1][0] = A[1];
|
||||
long_3[1][0] = 0;
|
||||
for (var i = 1; i < CHUNK_NUMBER; i++) {
|
||||
long_a[0][i] = 0;
|
||||
long_3[0][i] = 0;
|
||||
long_a[1][i] = 0;
|
||||
long_3[1][i] = 0;
|
||||
}
|
||||
|
||||
// precompute lambda
|
||||
var lamb_num[2][150] = find_Fp2_sum(CHUNK_SIZE, CHUNK_NUMBER, long_a, find_Fp2_product(CHUNK_SIZE, CHUNK_NUMBER, long_3, find_Fp2_product(CHUNK_SIZE, CHUNK_NUMBER, in[0], in[0], P), P), P);
|
||||
var lamb_denom[2][150] = find_Fp2_sum(CHUNK_SIZE, CHUNK_NUMBER, in[1], in[1], P);
|
||||
var lamb[2][150] = find_Fp2_product(CHUNK_SIZE, CHUNK_NUMBER, lamb_num, find_Fp2_inverse(CHUNK_SIZE, CHUNK_NUMBER, lamb_denom, P), P);
|
||||
|
||||
// precompute x_3, y_3
|
||||
var x3[2][150] = find_Fp2_diff(CHUNK_SIZE, CHUNK_NUMBER, find_Fp2_product(CHUNK_SIZE, CHUNK_NUMBER, lamb, lamb, P), find_Fp2_sum(CHUNK_SIZE, CHUNK_NUMBER, in[0], in[0], P), P);
|
||||
var y3[2][150] = find_Fp2_diff(CHUNK_SIZE, CHUNK_NUMBER, find_Fp2_product(CHUNK_SIZE, CHUNK_NUMBER, lamb, find_Fp2_diff(CHUNK_SIZE, CHUNK_NUMBER, in[0], x3, P), P), in[1], P);
|
||||
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++){
|
||||
out[0][0][i] <-- x3[0][i];
|
||||
out[0][1][i] <-- x3[1][i];
|
||||
out[1][0][i] <-- y3[0][i];
|
||||
out[1][1][i] <-- y3[1][i];
|
||||
}
|
||||
// check if out[][] has registers in [0, 2^CHUNK_SIZE)
|
||||
component range_check[2];
|
||||
range_check[0] = RangeCheck2D(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
range_check[1] = RangeCheck2D(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
range_check[0].in[j][i] <== out[0][j][i];
|
||||
range_check[1].in[j][i] <== out[1][j][i];
|
||||
}
|
||||
}
|
||||
|
||||
component point_on_tangent = PointOnTangentFp2(CHUNK_SIZE, CHUNK_NUMBER, A, P);
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++){
|
||||
point_on_tangent.in[0][j][0][i] <== in[j][0][i];
|
||||
point_on_tangent.in[0][j][1][i] <== in[j][1][i];
|
||||
point_on_tangent.in[1][j][0][i] <== out[j][0][i];
|
||||
point_on_tangent.in[1][j][1][i] <== out[j][1][i];
|
||||
}
|
||||
}
|
||||
|
||||
component point_on_curve = PointOnCurveFp2(CHUNK_SIZE, CHUNK_NUMBER, A, B, P);
|
||||
for(var j = 0; j < 2; j++)for(var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
point_on_curve.in[j][0][i] <== out[j][0][i];
|
||||
point_on_curve.in[j][1][i] <== out[j][1][i];
|
||||
}
|
||||
|
||||
component x3_eq_x1 = Fp2IsEqual(CHUNK_SIZE, CHUNK_NUMBER, P);
|
||||
for(var j = 0; j < 2; j++)for(var i = 0; i < CHUNK_NUMBER; i++){
|
||||
x3_eq_x1.a[j][i] <== out[0][j][i];
|
||||
x3_eq_x1.b[j][i] <== in[0][j][i];
|
||||
}
|
||||
x3_eq_x1.out === 0;
|
||||
}
|
||||
|
||||
// Fp2 curve y^2 = x^3 + A2*x + B2 with B2 complex
|
||||
// Assume curve has no Fp2 points of order 2, i.e., x^3 + A2*x + B2 has no Fp2 roots
|
||||
// Fact: ^ this is the case for BLS12-381 twisted E2 and its 3-isogeny E2'
|
||||
// If isInfinity = 1, replace `out` with `A` so if `A` was on curve, so is output
|
||||
template EllipticCurveAddFp2(CHUNK_SIZE, CHUNK_NUMBER, A2, B2, P){
|
||||
signal input a[2][2][CHUNK_NUMBER];
|
||||
signal input aIsInfinity;
|
||||
signal input b[2][2][CHUNK_NUMBER];
|
||||
signal input bIsInfinity;
|
||||
|
||||
signal output out[2][2][CHUNK_NUMBER];
|
||||
signal output isInfinity;
|
||||
|
||||
component xEqual = Fp2IsEqual(CHUNK_SIZE, CHUNK_NUMBER, P);
|
||||
component yEqual = Fp2IsEqual(CHUNK_SIZE, CHUNK_NUMBER, P);
|
||||
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
xEqual.a[i][idx] <== A[0][i][idx];
|
||||
xEqual.b[i][idx] <== B[0][i][idx];
|
||||
|
||||
yEqual.a[i][idx] <== A[1][i][idx];
|
||||
yEqual.b[i][idx] <== B[1][i][idx];
|
||||
}
|
||||
}
|
||||
// if A.x = B.x then A = +-B
|
||||
// if A = B then A + B = 2 * A so we need to do point doubling
|
||||
// if A = -A then out is infinity
|
||||
signal addIsDouble;
|
||||
addIsDouble <== xEqual.out * yEqual.out; // AND gate
|
||||
|
||||
// if A.x = B.x, need to replace B.x by A different number just so AddUnequal doesn't break
|
||||
// I will do this in A dumb way: replace B[0][0][0] by (B[0][0][0] == 0)
|
||||
component isZero = IsZero();
|
||||
isZero.in <== B[0][0][0];
|
||||
|
||||
component add = EllipticCurveAddUnequalFp2(CHUNK_SIZE, CHUNK_NUMBER, P);
|
||||
component doub = EllipticCurveDoubleFp2(CHUNK_SIZE, CHUNK_NUMBER, A2, B2, P);
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
add.a[i][j][idx] <== A[i][j][idx];
|
||||
if(i == 0 && j == 0 && idx == 0){
|
||||
add.b[i][j][idx] <== B[i][j][idx] + xEqual.out * (isZero.out - B[i][j][idx]);
|
||||
} else {
|
||||
add.b[i][j][idx] <== B[i][j][idx];
|
||||
}
|
||||
doub.in[i][j][idx] <== A[i][j][idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// out = O iff (A = O AND B = O) OR (A != 0 AND B != 0) AND (xEqual AND NOT yEqual)
|
||||
signal ab0;
|
||||
ab0 <== aIsInfinity * bIsInfinity;
|
||||
signal abNon0;
|
||||
abNon0 <== (1 - aIsInfinity) * (1 - bIsInfinity);
|
||||
signal aNegB;
|
||||
aNegB <== xEqual.out - xEqual.out * yEqual.out;
|
||||
signal inverse;
|
||||
inverse <== abNon0 * aNegB;
|
||||
isInfinity <== ab0 + inverse - ab0 * inverse; // OR gate
|
||||
|
||||
|
||||
signal tmp[3][2][2][CHUNK_NUMBER];
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
tmp[0][i][j][idx] <== add.out[i][j][idx] + addIsDouble * (doub.out[i][j][idx] - add.out[i][j][idx]);
|
||||
// if A = O, then A + B = B
|
||||
tmp[1][i][j][idx] <== tmp[0][i][j][idx] + aIsInfinity * (B[i][j][idx] - tmp[0][i][j][idx]);
|
||||
// if B = O, then A + B = A
|
||||
tmp[2][i][j][idx] <== tmp[1][i][j][idx] + bIsInfinity * (A[i][j][idx] - tmp[1][i][j][idx]);
|
||||
out[i][j][idx] <== tmp[2][i][j][idx] + isInfinity * (A[i][j][idx] - tmp[2][i][j][idx]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Curve E2 : y^2 = x^3 + B
|
||||
// Inputs:
|
||||
// in = P is 2 x 2 x CHUNK_NUMBER array where P = (x, y) is A point in E2(Fp2)
|
||||
// inIsInfinity = 1 if P = O, else = 0
|
||||
// Output:
|
||||
// out = [x]P is 2 x 2 x CHUNK_NUMBER array representing A point in E2(Fp2)
|
||||
// isInfinity = 1 if [x]P = O, else = 0
|
||||
// Assume:
|
||||
// E2 has no Fp2 points of order 2
|
||||
// x in [0, 2^250)
|
||||
// `in` is point in E2 even if inIsInfinity = 1 just so nothing goes wrong
|
||||
// E2(Fp2) has no points of order 2
|
||||
template EllipticCurveScalarMultiplyFp2(CHUNK_SIZE, CHUNK_NUMBER, B, x, P){
|
||||
signal input in[2][2][CHUNK_NUMBER];
|
||||
signal input inIsInfinity;
|
||||
|
||||
signal output out[2][2][CHUNK_NUMBER];
|
||||
signal output isInfinity;
|
||||
|
||||
var LOGK = log_ceil(CHUNK_NUMBER);
|
||||
|
||||
var BITS[250];
|
||||
var BITS_LENGTH;
|
||||
var SIG_BITS = 0;
|
||||
for (var i = 0; i < 250; i++) {
|
||||
BITS[i] = (x >> i) & 1;
|
||||
if(BITS[i] == 1){
|
||||
SIG_BITS++;
|
||||
BITS_LENGTH = i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
signal r[BITS_LENGTH][2][2][CHUNK_NUMBER];
|
||||
signal rIsO[BITS_LENGTH];
|
||||
component pDouble[BITS_LENGTH];
|
||||
component pAdd[SIG_BITS];
|
||||
var CUR_ID = 0;
|
||||
|
||||
// if in = O then [x]O = O so there's no point to any of this
|
||||
signal P[2][2][CHUNK_NUMBER];
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
for(var l = 0; l < 2; l++){
|
||||
P[j][l][idx] <== in[j][l][idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(var i = BITS_LENGTH - 1; i >= 0; i--){
|
||||
if(i == BITS_LENGTH - 1){
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
for(var l = 0; l < 2; l++){
|
||||
r[i][j][l][idx] <== P[j][l][idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
rIsO[i] <== 0;
|
||||
} else {
|
||||
// E2(Fp2) has no points of order 2, so the only way 2 * r[i + 1] = O is if r[i + 1] = O
|
||||
pDouble[i] = EllipticCurveDoubleFp2(CHUNK_SIZE, CHUNK_NUMBER, [0,0], B, P);
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
for(var l = 0; l < 2; l++){
|
||||
pDouble[i].in[j][l][idx] <== r[i + 1][j][l][idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
if(BITS[i] == 0){
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
for(var l = 0; l < 2; l++){
|
||||
r[i][j][l][idx] <== pDouble[i].out[j][l][idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
rIsO[i] <== rIsO[i + 1];
|
||||
} else {
|
||||
// pAdd[CUR_ID] = pDouble[i] + P
|
||||
pAdd[CUR_ID] = EllipticCurveAddFp2(CHUNK_SIZE, CHUNK_NUMBER, [0,0], B, P);
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var l = 0; l < 2; l++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
pAdd[CUR_ID].a[j][l][idx] <== pDouble[i].out[j][l][idx];
|
||||
pAdd[CUR_ID].b[j][l][idx] <== P[j][l][idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
pAdd[CUR_ID].aIsInfinity <== rIsO[i + 1];
|
||||
pAdd[CUR_ID].bIsInfinity <== 0;
|
||||
|
||||
rIsO[i] <== pAdd[CUR_ID].isInfinity;
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var l = 0; l < 2; l++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
r[i][j][l][idx] <== pAdd[CUR_ID].out[j][l][idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
CUR_ID++;
|
||||
}
|
||||
}
|
||||
}
|
||||
// output = O if input = O or r[0] = O
|
||||
isInfinity <== inIsInfinity + rIsO[0] - inIsInfinity * rIsO[0];
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
out[i][j][idx] <== r[0][i][j][idx] + isInfinity * (in[i][j][idx] - r[0][i][j][idx]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Curve E2 : y^2 = x^3 + B
|
||||
// Inputs:
|
||||
// in = P is 2 x 2 x CHUNK_NUMBER array where P = (x, y) is A point in E2(Fp2)
|
||||
// Output:
|
||||
// out = [x]P is 2 x 2 x CHUNK_NUMBER array representing A point in E2(Fp2)
|
||||
// Assume:
|
||||
// E2 has no Fp2 points of order 2
|
||||
// x in [0, 2^250)
|
||||
// P has order > x, so in double-and-add loop we never hit point at infinity, and only add unequal is allowed: constraint will fail if add unequal fails
|
||||
template EllipticCurveScalarMultiplyUnequalFp2(CHUNK_SIZE, CHUNK_NUMBER, B, x, P){
|
||||
signal input in[2][2][CHUNK_NUMBER];
|
||||
signal output out[2][2][CHUNK_NUMBER];
|
||||
|
||||
var LOGK = log_ceil(CHUNK_NUMBER);
|
||||
|
||||
var BITS[250];
|
||||
var BITS_LENGTH;
|
||||
var SIG_BITS = 0;
|
||||
for (var i = 0; i < 250; i++) {
|
||||
BITS[i] = (x >> i) & 1;
|
||||
if(BITS[i] == 1){
|
||||
SIG_BITS++;
|
||||
BITS_LENGTH = i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
signal r[BITS_LENGTH][2][2][CHUNK_NUMBER];
|
||||
component pDouble[BITS_LENGTH];
|
||||
component pAdd[SIG_BITS];
|
||||
component addException[SIG_BITS];
|
||||
var CUR_ID = 0;
|
||||
|
||||
for(var i = BITS_LENGTH - 1; i >= 0; i--){
|
||||
if(i == BITS_LENGTH - 1){
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
for(var l = 0; l < 2; l++){
|
||||
r[i][j][l][idx] <== in[j][l][idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Assuming E2 has no points of order 2, so double never fails
|
||||
// To remove this assumption, just add A check that pDouble[i].y != 0
|
||||
pDouble[i] = EllipticCurveDoubleFp2(CHUNK_SIZE, CHUNK_NUMBER, [0,0], B, P);
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
for(var l = 0; l < 2; l++){
|
||||
pDouble[i].in[j][l][idx] <== r[i + 1][j][l][idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(BITS[i] == 0){
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
for(var l = 0; l < 2; l++){
|
||||
r[i][j][l][idx] <== pDouble[i].out[j][l][idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Constrain pDouble[i].x != P.x
|
||||
addException[CUR_ID] = Fp2IsEqual(CHUNK_SIZE, CHUNK_NUMBER, P);
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
addException[CUR_ID].a[j][idx] <== pDouble[i].out[0][j][idx];
|
||||
addException[CUR_ID].b[j][idx] <== in[0][j][idx];
|
||||
}
|
||||
}
|
||||
addException[CUR_ID].out === 0;
|
||||
|
||||
// pAdd[CUR_ID] = pDouble[i] + P
|
||||
pAdd[CUR_ID] = EllipticCurveAddUnequalFp2(CHUNK_SIZE, CHUNK_NUMBER, P);
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var l = 0; l < 2; l++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
pAdd[CUR_ID].a[j][l][idx] <== pDouble[i].out[j][l][idx];
|
||||
pAdd[CUR_ID].b[j][l][idx] <== in[j][l][idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var l = 0; l < 2; l++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
r[i][j][l][idx] <== pAdd[CUR_ID].out[j][l][idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CUR_ID++;
|
||||
}
|
||||
}
|
||||
}
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
out[i][j][idx] <== r[0][i][j][idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,825 @@
|
||||
pragma circom 2.0.2;
|
||||
|
||||
include "circomlib/circuits/bitify.circom";
|
||||
|
||||
include "../../../bigInt/bigInt.circom";
|
||||
include "../../../bigInt/bigIntFunc.circom";
|
||||
include "./fp.circom";
|
||||
include "./fp2.circom";
|
||||
include "./fp12.circom";
|
||||
include "./curve.circom";
|
||||
|
||||
// requires a[0] != b[0]
|
||||
//
|
||||
// Implements:
|
||||
// lamb = (b[1] - a[1]) / (b[0] - a[0]) % Q
|
||||
// out[0] = lamb ** 2 - a[0] - b[0] % Q
|
||||
// out[1] = lamb * (a[0] - out[0]) - a[1] % Q
|
||||
template EllipticCurveAddUnequal3Reg(CHUNK_SIZE, Q0, Q1, Q2) {
|
||||
var CHUNK_NUMBER = 3;
|
||||
signal input a[2][CHUNK_NUMBER];
|
||||
signal input b[2][CHUNK_NUMBER];
|
||||
|
||||
signal output out[2][CHUNK_NUMBER];
|
||||
|
||||
var Q[20];
|
||||
Q[0] = Q0;
|
||||
Q[1] = Q1;
|
||||
Q[2] = Q2;
|
||||
for (var idx = 3; idx < 20; idx++) {
|
||||
Q[idx] = 0;
|
||||
}
|
||||
|
||||
// b[1] - a[1]
|
||||
component sub1 = BigSubModP(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
sub1.a[i] <== b[1][i];
|
||||
sub1.b[i] <== a[1][i];
|
||||
sub1.p[i] <== Q[i];
|
||||
}
|
||||
|
||||
// b[0] - a[0]
|
||||
component sub0 = BigSubModP(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
sub0.a[i] <== b[0][i];
|
||||
sub0.b[i] <== a[0][i];
|
||||
sub0.p[i] <== Q[i];
|
||||
}
|
||||
|
||||
signal LAMBDA[CHUNK_NUMBER];
|
||||
var SUB_0_INV[20] = mod_inv(CHUNK_SIZE, CHUNK_NUMBER, sub0.out, Q);
|
||||
var SUB1_SUB_0_INV[20] = prod(CHUNK_SIZE, CHUNK_NUMBER, sub1.out, SUB_0_INV);
|
||||
var LAMB_ARR[2][20] = long_div(CHUNK_SIZE, CHUNK_NUMBER, SUB1_SUB_0_INV, Q);
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
LAMBDA[i] <-- LAMB_ARR[1][i];
|
||||
}
|
||||
component rangeChecks[CHUNK_NUMBER];
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
rangeChecks[i] = Num2Bits(CHUNK_SIZE);
|
||||
rangeChecks[i].in <== LAMBDA[i];
|
||||
}
|
||||
component lessThan = BigLessThan(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
lessThan.a[i] <== LAMBDA[i];
|
||||
lessThan.b[i] <== Q[i];
|
||||
}
|
||||
lessThan.out === 1;
|
||||
|
||||
component lambdaCheck = BigMultModP(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
lambdaCheck.a[i] <== sub0.out[i];
|
||||
lambdaCheck.b[i] <== LAMBDA[i];
|
||||
lambdaCheck.p[i] <== Q[i];
|
||||
}
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
lambdaCheck.out[i] === sub1.out[i];
|
||||
}
|
||||
|
||||
component lambdaSq = BigMultModP(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
lambdaSq.a[i] <== LAMBDA[i];
|
||||
lambdaSq.b[i] <== LAMBDA[i];
|
||||
lambdaSq.p[i] <== Q[i];
|
||||
}
|
||||
component out0Pre = BigSubModP(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
out0Pre.a[i] <== lambdaSq.out[i];
|
||||
out0Pre.b[i] <== a[0][i];
|
||||
out0Pre.p[i] <== Q[i];
|
||||
}
|
||||
component out0 = BigSubModP(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
out0.a[i] <== out0Pre.out[i];
|
||||
out0.b[i] <== b[0][i];
|
||||
out0.p[i] <== Q[i];
|
||||
}
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
out[0][i] <== out0.out[i];
|
||||
}
|
||||
|
||||
component out1_0 = BigSubModP(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
out1_0.a[i] <== a[0][i];
|
||||
out1_0.b[i] <== out[0][i];
|
||||
out1_0.p[i] <== Q[i];
|
||||
}
|
||||
component out1_1 = BigMultModP(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
out1_1.a[i] <== LAMBDA[i];
|
||||
out1_1.b[i] <== out1_0.out[i];
|
||||
out1_1.p[i] <== Q[i];
|
||||
}
|
||||
component out1 = BigSubModP(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
out1.a[i] <== out1_1.out[i];
|
||||
out1.b[i] <== a[1][i];
|
||||
out1.p[i] <== Q[i];
|
||||
}
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
out[1][i] <== out1.out[i];
|
||||
}
|
||||
}
|
||||
|
||||
// requires a[0] != b[0]
|
||||
//
|
||||
// Implements:
|
||||
// lamb = (b[1] - a[1]) / (b[0] - a[0]) % Q
|
||||
// out[0] = lamb ** 2 - a[0] - b[0] % Q
|
||||
// out[1] = lamb * (a[0] - out[0]) - a[1] % Q
|
||||
template EllipticCurveAddUnequal4Reg(CHUNK_SIZE, Q0, Q1, Q2, q3) {
|
||||
var CHUNK_NUMBER = 4;
|
||||
signal input a[2][CHUNK_NUMBER];
|
||||
signal input b[2][CHUNK_NUMBER];
|
||||
|
||||
signal output out[2][CHUNK_NUMBER];
|
||||
|
||||
var Q[20];
|
||||
Q[0] = Q0;
|
||||
Q[1] = Q1;
|
||||
Q[2] = Q2;
|
||||
Q[3] = q3;
|
||||
for (var idx = 4; idx < 20; idx++) {
|
||||
Q[idx] = 0;
|
||||
}
|
||||
|
||||
// b[1] - a[1]
|
||||
component sub1 = BigSubModP(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
sub1.a[i] <== b[1][i];
|
||||
sub1.b[i] <== a[1][i];
|
||||
sub1.p[i] <== Q[i];
|
||||
}
|
||||
|
||||
// b[0] - a[0]
|
||||
component sub0 = BigSubModP(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
sub0.a[i] <== b[0][i];
|
||||
sub0.b[i] <== a[0][i];
|
||||
sub0.p[i] <== Q[i];
|
||||
}
|
||||
|
||||
signal LAMBDA[CHUNK_NUMBER];
|
||||
var SUB_0_INV[20] = mod_inv(CHUNK_SIZE, CHUNK_NUMBER, sub0.out, Q);
|
||||
var SUB1_SUB_0_INV[20] = prod(CHUNK_SIZE, CHUNK_NUMBER, sub1.out, SUB_0_INV);
|
||||
var LAMB_ARR[2][20] = long_div(CHUNK_SIZE, CHUNK_NUMBER, SUB1_SUB_0_INV, Q);
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
LAMBDA[i] <-- LAMB_ARR[1][i];
|
||||
}
|
||||
component rangeChecks[CHUNK_NUMBER];
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
rangeChecks[i] = Num2Bits(CHUNK_SIZE);
|
||||
rangeChecks[i].in <== LAMBDA[i];
|
||||
}
|
||||
component lessThan = BigLessThan(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
lessThan.a[i] <== LAMBDA[i];
|
||||
lessThan.b[i] <== Q[i];
|
||||
}
|
||||
lessThan.out === 1;
|
||||
|
||||
component lambdaCheck = BigMultModP(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
lambdaCheck.a[i] <== sub0.out[i];
|
||||
lambdaCheck.b[i] <== LAMBDA[i];
|
||||
lambdaCheck.p[i] <== Q[i];
|
||||
}
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
lambdaCheck.out[i] === sub1.out[i];
|
||||
}
|
||||
|
||||
component lambdaSq = BigMultModP(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
lambdaSq.a[i] <== LAMBDA[i];
|
||||
lambdaSq.b[i] <== LAMBDA[i];
|
||||
lambdaSq.p[i] <== Q[i];
|
||||
}
|
||||
component out0Pre = BigSubModP(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
out0Pre.a[i] <== lambdaSq.out[i];
|
||||
out0Pre.b[i] <== a[0][i];
|
||||
out0Pre.p[i] <== Q[i];
|
||||
}
|
||||
component out0 = BigSubModP(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
out0.a[i] <== out0Pre.out[i];
|
||||
out0.b[i] <== b[0][i];
|
||||
out0.p[i] <== Q[i];
|
||||
}
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
out[0][i] <== out0.out[i];
|
||||
}
|
||||
|
||||
component out1_0 = BigSubModP(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
out1_0.a[i] <== a[0][i];
|
||||
out1_0.b[i] <== out[0][i];
|
||||
out1_0.p[i] <== Q[i];
|
||||
}
|
||||
component out1_1 = BigMultModP(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
out1_1.a[i] <== LAMBDA[i];
|
||||
out1_1.b[i] <== out1_0.out[i];
|
||||
out1_1.p[i] <== Q[i];
|
||||
}
|
||||
component out1 = BigSubModP(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
out1.a[i] <== out1_1.out[i];
|
||||
out1.b[i] <== a[1][i];
|
||||
out1.p[i] <== Q[i];
|
||||
}
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
out[1][i] <== out1.out[i];
|
||||
}
|
||||
}
|
||||
|
||||
// Elliptic curve is E : y**2 = x**3 + a x + b
|
||||
// Note that for BLS12-381, a = 0, b = 4
|
||||
|
||||
// Implements:
|
||||
// computing 2P on elliptic curve E for P = (in[0], in[1])
|
||||
// formula from https://crypto.stanford.edu/pbc/notes/elliptic/explicit.html
|
||||
|
||||
// lamb = (3 * in[0] ** 2 + a) / (2 * in[1]) % Q
|
||||
// out[0] = lamb ** 2 - 2 * in[0] % Q
|
||||
// out[1] = lamb * (in[0] - out[0]) - in[1] % Q
|
||||
template EllipticCurveDouble0(CHUNK_SIZE, CHUNK_NUMBER, a, Q0, Q1, Q2, q3) {
|
||||
signal input in[2][CHUNK_NUMBER];
|
||||
|
||||
signal output out[2][CHUNK_NUMBER];
|
||||
|
||||
// assuming Q < 2 * * (4n)
|
||||
// represent Q = Q0 + Q1 * 2 * *CHUNK_SIZE + Q2 * 2 * * (2n) + q3 * 2 * * (3n)
|
||||
// not sure how I feel about this convention...
|
||||
var Q[20];
|
||||
Q[0] = Q0;
|
||||
Q[1] = Q1;
|
||||
Q[2] = Q2;
|
||||
Q[3] = q3;
|
||||
for (var idx = 4; idx < 20; idx++) {
|
||||
Q[idx] = 0;
|
||||
}
|
||||
|
||||
// assuming a is small
|
||||
var LONG_A[20];
|
||||
LONG_A[0] = a;
|
||||
for (var i = 1; i < 20; i++) {
|
||||
LONG_A[i] = 0;
|
||||
}
|
||||
|
||||
component in0Sq = BigMultModP(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
in0Sq.a[i] <== in[0][i];
|
||||
in0Sq.b[i] <== in[0][i];
|
||||
in0Sq.p[i] <== Q[i];
|
||||
}
|
||||
|
||||
var LONG_2[20];
|
||||
var LONG_3[20];
|
||||
LONG_2[0] = 2;
|
||||
LONG_3[0] = 3;
|
||||
for (var i = 1; i < CHUNK_NUMBER; i++) {
|
||||
LONG_A[i] = 0;
|
||||
LONG_2[i] = 0;
|
||||
LONG_3[i] = 0;
|
||||
}
|
||||
var INV_2[20] = mod_inv(CHUNK_SIZE, CHUNK_NUMBER, LONG_2, Q);
|
||||
var LONG_3_DIV_2[20] = prod(CHUNK_SIZE, CHUNK_NUMBER, LONG_3, INV_2);
|
||||
var LONG_3_DIV_2_MOD_Q[2][20] = long_div(CHUNK_SIZE, CHUNK_NUMBER, LONG_3_DIV_2, Q);
|
||||
|
||||
// numerator = 3/2 * in[0]**2 + a
|
||||
// numer1 = 3/2 * in[0]**2
|
||||
component numer1 = BigMultModP(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
numer1.a[i] <== LONG_3_DIV_2_MOD_Q[1][i];
|
||||
numer1.b[i] <== in0Sq.out[i];
|
||||
numer1.p[i] <== Q[i];
|
||||
}
|
||||
component numer = BigAddModP(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
numer.a[i] <== numer1.out[i];
|
||||
numer.b[i] <== LONG_A[i];
|
||||
numer.p[i] <== Q[i];
|
||||
}
|
||||
|
||||
signal LAMBDA[CHUNK_NUMBER];
|
||||
var DENOM_INV[20] = mod_inv(CHUNK_SIZE, CHUNK_NUMBER, in[1], Q);
|
||||
var PRODUCT[20] = prod(CHUNK_SIZE, CHUNK_NUMBER, numer.out, DENOM_INV);
|
||||
var LAMB_ARR[2][20] = long_div(CHUNK_SIZE, CHUNK_NUMBER, PRODUCT, Q);
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
LAMBDA[i] <-- LAMB_ARR[1][i];
|
||||
}
|
||||
component lessThan = BigLessThan(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
lessThan.a[i] <== LAMBDA[i];
|
||||
lessThan.b[i] <== Q[i];
|
||||
}
|
||||
lessThan.out === 1;
|
||||
|
||||
component lambdaRangeChecks[CHUNK_NUMBER];
|
||||
component lambdaCheck = BigMultModP(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
lambdaRangeChecks[i] = Num2Bits(CHUNK_SIZE);
|
||||
lambdaRangeChecks[i].in <== LAMBDA[i];
|
||||
|
||||
lambdaCheck.a[i] <== in[1][i];
|
||||
lambdaCheck.b[i] <== LAMBDA[i];
|
||||
lambdaCheck.p[i] <== Q[i];
|
||||
}
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
lambdaCheck.out[i] === numer.out[i];
|
||||
}
|
||||
|
||||
component lambdaSq = BigMultModP(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
lambdaSq.a[i] <== LAMBDA[i];
|
||||
lambdaSq.b[i] <== LAMBDA[i];
|
||||
lambdaSq.p[i] <== Q[i];
|
||||
}
|
||||
component out0Pre = BigSubModP(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
out0Pre.a[i] <== lambdaSq.out[i];
|
||||
out0Pre.b[i] <== in[0][i];
|
||||
out0Pre.p[i] <== Q[i];
|
||||
}
|
||||
// out0 = LAMBDA**2 - 2 * in[0]
|
||||
component out0 = BigSubModP(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
out0.a[i] <== out0Pre.out[i];
|
||||
out0.b[i] <== in[0][i];
|
||||
out0.p[i] <== Q[i];
|
||||
}
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
out[0][i] <== out0.out[i];
|
||||
}
|
||||
|
||||
component out1_0 = BigSubModP(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
out1_0.a[i] <== in[0][i];
|
||||
out1_0.b[i] <== out[0][i];
|
||||
out1_0.p[i] <== Q[i];
|
||||
}
|
||||
component out1_1 = BigMultModP(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
out1_1.a[i] <== LAMBDA[i];
|
||||
out1_1.b[i] <== out1_0.out[i];
|
||||
out1_1.p[i] <== Q[i];
|
||||
}
|
||||
component out1 = BigSubModP(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
out1.a[i] <== out1_1.out[i];
|
||||
out1.b[i] <== in[1][i];
|
||||
out1.p[i] <== Q[i];
|
||||
}
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
out[1][i] <== out1.out[i];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
// P = (x, y)
|
||||
// a = - 3x^2
|
||||
// b = 2 y
|
||||
// c = 3 x^3 - 2 y^2
|
||||
// a, c registers in [0, 2^CHUNK_SIZE), b registers in [0, 2^{CHUNK_SIZE})
|
||||
// out = [a, b, c]
|
||||
template LineEqualCoefficients(CHUNK_SIZE, CHUNK_NUMBER, Q){
|
||||
signal input P[2][CHUNK_NUMBER];
|
||||
signal output out[3][CHUNK_NUMBER];
|
||||
|
||||
component xsq3 = BigMultShortLong(CHUNK_SIZE, CHUNK_NUMBER); // 2k - 1 registers [0, 3 * CHUNK_NUMBER* 2^{2n})
|
||||
component ysq = BigMultShortLong(CHUNK_SIZE, CHUNK_NUMBER); // 2k - 1 registers [0, CHUNK_NUMBER*2^{2n})
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++){
|
||||
xsq3.a[i] <== 3 * P[0][i];
|
||||
xsq3.b[i] <== P[0][i];
|
||||
|
||||
ysq.a[i] <== P[1][i];
|
||||
ysq.b[i] <== P[1][i];
|
||||
}
|
||||
|
||||
component xcube3 = BigMultShortLongUnequal(CHUNK_SIZE, 2 * CHUNK_NUMBER - 1, CHUNK_NUMBER); // 3k - 2 registers [0, 3 * CHUNK_NUMBER^2 * 2^{3n})
|
||||
for(var i = 0; i < 2 * CHUNK_NUMBER - 1; i++)
|
||||
xcube3.a[i] <== xsq3.out[i];
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++)
|
||||
xcube3.b[i] <== P[0][i];
|
||||
|
||||
|
||||
component xsq3red = PrimeReduce(CHUNK_SIZE, CHUNK_NUMBER, CHUNK_NUMBER - 1, Q); // CHUNK_NUMBER registers in [0, CHUNK_NUMBER^2 * 2^{3n})
|
||||
for(var i = 0; i < 2 * CHUNK_NUMBER - 1; i++)
|
||||
xsq3red.in[i] <== xsq3.out[i];
|
||||
|
||||
component a = FpCarryModP(CHUNK_SIZE, CHUNK_NUMBER, 3 * CHUNK_SIZE + log_ceil(CHUNK_NUMBER * CHUNK_NUMBER), Q);
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++){
|
||||
a.in[0][i] <== 0;
|
||||
a.in[1][i] <== xsq3red.out[i];
|
||||
}
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++)
|
||||
out[0][i] <== a.out[i];
|
||||
|
||||
// I think reducing registers of b to [0, 2^CHUNK_SIZE) is still useful for future multiplications
|
||||
component b = BigAddModP(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++){
|
||||
b.a[i] <== P[1][i];
|
||||
b.b[i] <== P[1][i];
|
||||
b.p[i] <== Q[i];
|
||||
}
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++)
|
||||
out[1][i] <== b.out[i];
|
||||
|
||||
component xcube3red = PrimeReduce(CHUNK_SIZE, CHUNK_NUMBER, 2 * CHUNK_NUMBER - 2, Q); // CHUNK_NUMBER registers in [0, 3 * CHUNK_NUMBER^2 * (2 * CHUNK_NUMBER - 2 ) * 2^{4n})
|
||||
for(var i = 0; i < 3 * CHUNK_NUMBER - 2; i++)
|
||||
xcube3red.in[i] <== xcube3.out[i];
|
||||
|
||||
component ysqred = PrimeReduce(CHUNK_SIZE, CHUNK_NUMBER, CHUNK_NUMBER - 1, Q);
|
||||
for(var i = 0; i < 2 * CHUNK_NUMBER - 1; i++)
|
||||
ysqred.in[i] <== ysq.out[i];
|
||||
|
||||
component c = FpCarryModP(CHUNK_SIZE, CHUNK_NUMBER, 4 * CHUNK_SIZE + log_ceil(3 * CHUNK_NUMBER * CHUNK_NUMBER * (2 * CHUNK_NUMBER - 2 )), Q);
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++){
|
||||
c.in[0][i] <== xcube3red.out[i];
|
||||
c.in[1][i] <== 2 * ysqred.out[i];
|
||||
}
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++)
|
||||
out[2][i] <== c.out[i];
|
||||
|
||||
}*/
|
||||
|
||||
|
||||
|
||||
// Assuming curve is of form Y^2 = X^3 + b for now (a = 0) for better register bounds
|
||||
// Inputs:
|
||||
// P is 2 x CHUNK_NUMBER array where P = (x, y) is a point in E[r](Fq)
|
||||
// Q is 2 x 6 x 2 x CHUNK_NUMBER array representing point (X, Y) in E(Fq12)
|
||||
// Output:
|
||||
// f_r(Q) where <f_r >= [r]P - [r]O is computed using Miller's algorithm
|
||||
// Assume:
|
||||
// r has CHUNK_NUMBER registers in [0, 2^CHUNK_SIZE)
|
||||
// Q has CHUNK_NUMBER registers in [0, 2^CHUNK_SIZE)
|
||||
// r is prime
|
||||
// P != O so the order of P in E(Fq) is r, so [i]P != [j]P for i != j in Z/r
|
||||
template MillerLoop1(CHUNK_SIZE, CHUNK_NUMBER, b, r, q){
|
||||
signal input P[2][CHUNK_NUMBER];
|
||||
signal input Q[2][6][2][CHUNK_NUMBER];
|
||||
signal output out[6][2][CHUNK_NUMBER];
|
||||
|
||||
var LOGK = log_ceil(CHUNK_NUMBER);
|
||||
var XI0 = 1;
|
||||
var LOGK2 = log_ceil(36 * (2 + XI0) * (2 + XI0) * CHUNK_NUMBER * CHUNK_NUMBER);
|
||||
var LOGK3 = log_ceil(36 * (2 + XI0) * (2 + XI0) * CHUNK_NUMBER * CHUNK_NUMBER * (2 * CHUNK_NUMBER - 1));
|
||||
assert(4 * CHUNK_SIZE + LOGK3 < 251);
|
||||
|
||||
|
||||
var BITS[513]; // length is CHUNK_NUMBER * CHUNK_SIZE
|
||||
var BIT_LENGTH;
|
||||
var SIG_BITS = 0;
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
for (var j = 0; j < CHUNK_SIZE; j++) {
|
||||
BITS[j + CHUNK_SIZE * i] = (r[i] >> j) & 1;
|
||||
if(BITS[j + CHUNK_SIZE * i] == 1){
|
||||
SIG_BITS++;
|
||||
BIT_LENGTH = j + CHUNK_SIZE * i + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
signal pInterMed[BIT_LENGTH][2][CHUNK_NUMBER];
|
||||
signal f[BIT_LENGTH][6][2][CHUNK_NUMBER];
|
||||
|
||||
component pDouble[BIT_LENGTH];
|
||||
component fDouble[BIT_LENGTH];
|
||||
component square[BIT_LENGTH];
|
||||
component line[BIT_LENGTH];
|
||||
component compress[BIT_LENGTH];
|
||||
component noCarry[BIT_LENGTH];
|
||||
component pAdd[SIG_BITS];
|
||||
component fAdd[SIG_BITS];
|
||||
component fAddPre[SIG_BITS];
|
||||
var CUR_ID = 0;
|
||||
|
||||
for(var i=BIT_LENGTH - 1; i >= 0; i--){
|
||||
if(i == BIT_LENGTH - 1){
|
||||
// f = 1
|
||||
for(var l = 0; l < 6; l++){
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
if(l == 0 && j == 0 && idx == 0){
|
||||
f[i][l][j][idx] <== 1;
|
||||
} else {
|
||||
f[i][l][j][idx] <== 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for(var j = 0; j < 2; j++)for(var idx = 0; idx < CHUNK_NUMBER; idx++)
|
||||
pInterMed[i][j][idx] <== P[j][idx];
|
||||
} else {
|
||||
// compute fDouble[i] = f[i+1]^2 * l_{pInterMed[i+1], pInterMed[i+1]}(Q)
|
||||
square[i] = SignedFp12MultiplyNoCarry(CHUNK_SIZE, CHUNK_NUMBER, 2 * CHUNK_SIZE + 4 + LOGK); // 6 x 2 x 2k - 1 registers in [0, 6 * CHUNK_NUMBER * (2 + XI0) * 2^{2n})
|
||||
for(var l = 0; l < 6; l++){
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
square[i].a[l][j][idx] <== f[i+1][l][j][idx];
|
||||
square[i].b[l][j][idx] <== f[i+1][l][j][idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
line[i] = LineFunctionEqual(CHUNK_SIZE, CHUNK_NUMBER, q); // 6 x 2 x CHUNK_NUMBER registers in [0, 2^CHUNK_SIZE)
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
line[i].P[j][idx] <== pInterMed[i+1][j][idx];
|
||||
}
|
||||
}
|
||||
for(var eps = 0; eps < 2; eps++){
|
||||
for(var l = 0; l < 6; l++){
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
line[i].Q[eps][l][j][idx] <== Q[eps][l][j][idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
noCarry[i] = SignedFp12MultiplyNoCarryUnequal(CHUNK_SIZE, 2 * CHUNK_NUMBER - 1, CHUNK_NUMBER, 3 * CHUNK_SIZE + LOGK2); // 6 x 2 x 3k - 2 registers < (6 * (2 + XI0))^2 * CHUNK_NUMBER^2 * 2^{3n})
|
||||
for(var l = 0; l < 6; l++){
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var idx = 0; idx < 2 * CHUNK_NUMBER - 1; idx++){
|
||||
noCarry[i].a[l][j][idx] <== square[i].out[l][j][idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(var l = 0; l < 6; l++){
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
noCarry[i].b[l][j][idx] <== line[i].out[l][j][idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
compress[i] = Fp12Compress(CHUNK_SIZE, CHUNK_NUMBER, 2 * CHUNK_NUMBER - 2, q, 4 * CHUNK_SIZE + LOGK3); // 6 x 2 x CHUNK_NUMBER registers < (6 * (2 + XI0))^2 * CHUNK_NUMBER^2 * (2k - 1) * 2^{4n})
|
||||
for(var l = 0; l < 6; l++){
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var idx = 0; idx < 3 * CHUNK_NUMBER - 2; idx++){
|
||||
compress[i].in[l][j][idx] <== noCarry[i].out[l][j][idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fDouble[i] = SignedFp12CarryModP(CHUNK_SIZE, CHUNK_NUMBER, 4 * CHUNK_SIZE + LOGK3, q);
|
||||
for(var l = 0; l < 6; l++){
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
fDouble[i].in[l][j][idx] <== compress[i].out[l][j][idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pDouble[i] = EllipticCurveDouble(CHUNK_SIZE, CHUNK_NUMBER, 0, b, q);
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
pDouble[i].in[j][idx] <== pInterMed[i+1][j][idx];
|
||||
}
|
||||
}
|
||||
|
||||
if(BITS[i] == 0){
|
||||
for(var l = 0; l < 6; l++){
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
f[i][l][j][idx] <== fDouble[i].out[l][j][idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
pInterMed[i][j][idx] <== pDouble[i].out[j][idx];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// fAdd[CUR_ID] = fDouble * l_{pDouble[i], P}(Q)
|
||||
fAdd[CUR_ID] = Fp12MultiplyWithLineUnequal(CHUNK_SIZE, CHUNK_NUMBER, CHUNK_NUMBER, CHUNK_SIZE, Q);
|
||||
for(var l = 0; l < 6; l++){
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
fAdd[CUR_ID].g[l][j][idx] <== fDouble[i].out[l][j][idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
fAdd[CUR_ID].P[0][j][idx] <== pDouble[i].out[j][idx];
|
||||
fAdd[CUR_ID].P[1][j][idx] <== P[j][idx];
|
||||
}
|
||||
}
|
||||
for(var eps = 0; eps < 2; eps++){
|
||||
for(var l = 0; l < 6; l++){
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
fAdd[CUR_ID].Q[eps][l][j][idx] <== Q[eps][l][j][idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(var l = 0; l < 6; l++){
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
f[i][l][j][idx] <== fAdd[CUR_ID].out[l][j][idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// pAdd[CUR_ID] = pDouble[i] + P
|
||||
pAdd[CUR_ID] = EllipticCurveAddUnequal(CHUNK_SIZE, CHUNK_NUMBER, q);
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
pAdd[CUR_ID].a[j][idx] <== pDouble[i].out[j][idx];
|
||||
pAdd[CUR_ID].b[j][idx] <== P[j][idx];
|
||||
}
|
||||
}
|
||||
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
pInterMed[i][j][idx] <== pAdd[CUR_ID].out[j][idx];
|
||||
}
|
||||
}
|
||||
CUR_ID++;
|
||||
}
|
||||
}
|
||||
}
|
||||
for(var l = 0; l < 6; l++){
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
out[l][j][idx] <== f[0][l][j][idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
// Input:
|
||||
// g is 6 x 4 x kg array representing element of Fp12, allowing overflow and negative
|
||||
// P, Q are as in inputs of LineFunctionEqualNoCarry
|
||||
// Assume:
|
||||
// all registers of g are in [0, 2^{overflowg})
|
||||
// all registers of P, Q are in [0, 2^CHUNK_SIZE)
|
||||
// Output:
|
||||
// out = g * l_{P, P}(Q) as element of Fp12 with carry
|
||||
// out is 6 x 2 x CHUNK_NUMBER
|
||||
template Fp12MultiplyWithLineEqual(CHUNK_SIZE, CHUNK_NUMBER, kg, overflowg, Q){
|
||||
signal input g[6][4][kg];
|
||||
signal input P[2][CHUNK_NUMBER];
|
||||
signal input Q[2][6][2][CHUNK_NUMBER];
|
||||
signal output out[6][2][CHUNK_NUMBER];
|
||||
|
||||
var LOGK = log_ceil(CHUNK_NUMBER);
|
||||
component line = LineFunctionEqualNoCarry(CHUNK_SIZE, CHUNK_NUMBER, 3 * CHUNK_SIZE + 2 * LOGK + 2); // 6 x 4 x (3k - 2) registers in [0, 2^{3n + 2log(CHUNK_NUMBER) + 2})
|
||||
for(var l = 0; l < 2; l++)for(var idx = 0; idx < CHUNK_NUMBER; idx++)
|
||||
line.P[l][idx] <== P[l][idx];
|
||||
for(var l = 0; l < 2; l++)for(var i = 0; i < 6; i++)for(var j = 0; j < 2; j++)for(var idx = 0; idx < CHUNK_NUMBER; idx++)
|
||||
line.Q[l][i][j][idx] <== Q[l][i][j][idx];
|
||||
|
||||
var mink;
|
||||
if(kg < 3 * CHUNK_NUMBER - 2)
|
||||
mink = kg;
|
||||
else
|
||||
mink = 3 * CHUNK_NUMBER - 2;
|
||||
var logc = log_ceil(12 * (2 * CHUNK_NUMBER + kg - 2) * mink * CHUNK_NUMBER * CHUNK_NUMBER);
|
||||
assert(overflowg + 4 * CHUNK_SIZE + logc + 2 < 252);
|
||||
var log2kkg = log_ceil (2 * CHUNK_NUMBER + kg - 2);
|
||||
component mult = Fp12MultiplyNoCarryUnequal(CHUNK_SIZE, kg, 3 * CHUNK_NUMBER - 2, overflowg + 3 * CHUNK_SIZE + logc + 2 - log2kkg); // 3k + kg - 3 registers in [0, 12 * min(kg, 3k - 2) * 2^{overflowg + 3n + 2log(CHUNK_NUMBER) + 2})
|
||||
|
||||
for(var i = 0; i < 6; i++)for(var j = 0; j<4; j++)for(var idx = 0; idx<kg; idx++)
|
||||
mult.a[i][j][idx] <== g[i][j][idx];
|
||||
for(var i = 0; i < 6; i++)for(var j = 0; j<4; j++)for(var idx = 0; idx < 3 * CHUNK_NUMBER - 2; idx++)
|
||||
mult.b[i][j][idx] <== line.out[i][j][idx];
|
||||
component reduce = Fp12Compress(CHUNK_SIZE, CHUNK_NUMBER, 2 * CHUNK_NUMBER + kg - 3, Q, overflowg + 4 * CHUNK_SIZE + logc + 2); // CHUNK_NUMBER registers in [0, 12 * (2k + kg - 2) * min(kg, 3k - 2) * 2^{overflowg + 4n + 2log(CHUNK_NUMBER) + 2})
|
||||
for(var i = 0; i < 6; i++)for(var j = 0; j<4; j++)for(var idx = 0; idx < 3 * CHUNK_NUMBER + kg - 3; idx++)
|
||||
reduce.in[i][j][idx] <== mult.out[i][j][idx];
|
||||
|
||||
component carry = Fp12CarryModP(CHUNK_SIZE, CHUNK_NUMBER, overflowg + 4 * CHUNK_SIZE + logc + 2, Q);
|
||||
|
||||
for(var i = 0; i < 6; i++)for(var j = 0; j<4; j++)for(var idx = 0; idx < CHUNK_NUMBER; idx++)
|
||||
carry.in[i][j][idx] <== reduce.out[i][j][idx];
|
||||
|
||||
for(var i = 0; i < 6; i++)for(var j = 0; j < 2; j++)for(var idx = 0; idx < CHUNK_NUMBER; idx++)
|
||||
out[i][j][idx] <== carry.out[i][j][idx];
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
// version with one less carry per loop that requires 6n + ... overflow
|
||||
// doesn't actually reduce constraints for some reason
|
||||
template MillerLoop2(CHUNK_SIZE, CHUNK_NUMBER, b, r, Q){
|
||||
signal input P[2][CHUNK_NUMBER];
|
||||
signal input Q[2][6][2][CHUNK_NUMBER];
|
||||
signal output out[6][2][CHUNK_NUMBER];
|
||||
|
||||
var rBits[513]; // length is CHUNK_NUMBER * CHUNK_SIZE
|
||||
var rBitLength;
|
||||
var rSigBits = 0;
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
for (var j = 0; j < CHUNK_SIZE; j++) {
|
||||
rBits[j + CHUNK_SIZE * i] = (r[i] >> j) & 1;
|
||||
if(rBits[j + CHUNK_SIZE * i] == 1){
|
||||
rSigBits++;
|
||||
rBitLength = j + CHUNK_SIZE * i + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
signal pInterMed[rBitLength][2][CHUNK_NUMBER];
|
||||
signal f[rBitLength][6][2][CHUNK_NUMBER];
|
||||
|
||||
component pDouble[rBitLength];
|
||||
component pAdd[rSigBits];
|
||||
component fDouble[rBitLength];
|
||||
component square[rBitLength];
|
||||
component fAdd[rSigBits];
|
||||
var CUR_ID = 0;
|
||||
|
||||
var LOGK = log_ceil(CHUNK_NUMBER);
|
||||
assert(6*CHUNK_SIZE + LOGK + 6 + log_ceil(12 * (4 * CHUNK_NUMBER-3) * (2 * CHUNK_NUMBER - 1) * CHUNK_NUMBER * CHUNK_NUMBER) < 252);
|
||||
|
||||
for(var i=rBitLength - 1; i >= 0; i--){
|
||||
if(i == rBitLength - 1){
|
||||
// f = 1
|
||||
for(var l = 0; l < 6; l++)for(var j = 0; j < 2; j++)for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
if(l == 0 && j == 0 && idx == 0)
|
||||
f[i][l][j][idx] <== 1;
|
||||
else
|
||||
f[i][l][j][idx] <== 0;
|
||||
}
|
||||
for(var j = 0; j < 2; j++)for(var idx = 0; idx < CHUNK_NUMBER; idx++)
|
||||
pInterMed[i][j][idx] <== P[j][idx];
|
||||
} else {
|
||||
// compute fDouble[i] = f[i+1]^2 * l_{pInterMed[i+1], pInterMed[i+1]}(Q)
|
||||
square[i] = Fp12MultiplyNoCarry(CHUNK_SIZE, CHUNK_NUMBER, 2 * CHUNK_SIZE + LOGK + 4); // 2k - 1 registers in [0, 12 * CHUNK_NUMBER * 2^{2n})
|
||||
for(var l = 0; l < 6; l++)for(var j = 0; j < 2; j++)for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
square[i].a[l][j][idx] <== f[i+1][l][j][idx];
|
||||
square[i].a[l][j+2][idx] <== 0;
|
||||
square[i].b[l][j][idx] <== f[i+1][l][j][idx];
|
||||
square[i].b[l][j+2][idx] <== 0;
|
||||
}
|
||||
|
||||
fDouble[i] = Fp12MultiplyWithLineEqual(CHUNK_SIZE, CHUNK_NUMBER, 2 * CHUNK_NUMBER - 1, 2 * CHUNK_SIZE + LOGK + 4, Q);
|
||||
// assert (6n + log(CHUNK_NUMBER) + 6 + log(12 * (4k-3) * (2k - 1) * CHUNK_NUMBER * CHUNK_NUMBER)) < 252
|
||||
for(var l = 0; l < 6; l++)for(var j = 0; j<4; j++)for(var idx = 0; idx < 2 * CHUNK_NUMBER - 1; idx++)
|
||||
fDouble[i].g[l][j][idx] <== square[i].out[l][j][idx];
|
||||
for(var j = 0; j < 2; j++)for(var idx = 0; idx < CHUNK_NUMBER; idx++)
|
||||
fDouble[i].P[j][idx] <== pInterMed[i+1][j][idx];
|
||||
for(var eps = 0; eps < 2; eps++)for(var l = 0; l < 6; l++)for(var j = 0; j < 2; j++)for(var idx = 0; idx < CHUNK_NUMBER; idx++)
|
||||
fDouble[i].Q[eps][l][j][idx] <== Q[eps][l][j][idx];
|
||||
|
||||
if(i != 0 || (i == 0 && rBits[i] == 1)){
|
||||
pDouble[i] = EllipticCurveDouble(CHUNK_SIZE, CHUNK_NUMBER, 0, b, Q);
|
||||
for(var j = 0; j < 2; j++)for(var idx = 0; idx < CHUNK_NUMBER; idx++)
|
||||
pDouble[i].in[j][idx] <== pInterMed[i+1][j][idx];
|
||||
}
|
||||
|
||||
if(rBits[i] == 0){
|
||||
for(var l = 0; l < 6; l++)for(var j = 0; j < 2; j++)for(var idx = 0; idx < CHUNK_NUMBER; idx++)
|
||||
f[i][l][j][idx] <== fDouble[i].out[l][j][idx];
|
||||
if(i != 0){
|
||||
for(var j = 0; j < 2; j++)for(var idx = 0; idx < CHUNK_NUMBER; idx++)
|
||||
pInterMed[i][j][idx] <== pDouble[i].out[j][idx];
|
||||
}
|
||||
} else {
|
||||
// fAdd[CUR_ID] = fDouble * l_{pDouble[i], P}(Q)
|
||||
fAdd[CUR_ID] = Fp12MultiplyWithLineUnequal(CHUNK_SIZE, CHUNK_NUMBER, CHUNK_NUMBER, CHUNK_SIZE, Q);
|
||||
for(var l = 0; l < 6; l++)for(var j = 0; j < 2; j++)for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
fAdd[CUR_ID].g[l][j][idx] <== fDouble[i].out[l][j][idx];
|
||||
fAdd[CUR_ID].g[l][j+2][idx] <== 0;
|
||||
}
|
||||
for(var j = 0; j < 2; j++)for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
fAdd[CUR_ID].P[0][j][idx] <== pDouble[i].out[j][idx];
|
||||
fAdd[CUR_ID].P[1][j][idx] <== P[j][idx];
|
||||
}
|
||||
for(var eps = 0; eps < 2; eps++)for(var l = 0; l < 6; l++)for(var j = 0; j < 2; j++)for(var idx = 0; idx < CHUNK_NUMBER; idx++)
|
||||
fAdd[CUR_ID].Q[eps][l][j][idx] <== Q[eps][l][j][idx];
|
||||
|
||||
for(var l = 0; l < 6; l++)for(var j = 0; j < 2; j++)for(var idx = 0; idx < CHUNK_NUMBER; idx++)
|
||||
f[i][l][j][idx] <== fAdd[CUR_ID].out[l][j][idx];
|
||||
|
||||
if(i != 0){
|
||||
// pAdd[CUR_ID] = pDouble[i] + P
|
||||
pAdd[CUR_ID] = EllipticCurveAddUnequal(CHUNK_SIZE, CHUNK_NUMBER, Q);
|
||||
for(var j = 0; j < 2; j++)for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
pAdd[CUR_ID].a[j][idx] <== pDouble[i].out[j][idx];
|
||||
pAdd[CUR_ID].b[j][idx] <== P[j][idx];
|
||||
}
|
||||
|
||||
for(var j = 0; j < 2; j++)for(var idx = 0; idx < CHUNK_NUMBER; idx++)
|
||||
pInterMed[i][j][idx] <== pAdd[CUR_ID].out[j][idx];
|
||||
}
|
||||
|
||||
CUR_ID++;
|
||||
}
|
||||
}
|
||||
}
|
||||
for(var l = 0; l < 6; l++)for(var j = 0; j < 2; j++)for(var idx = 0; idx < CHUNK_NUMBER; idx++)
|
||||
out[l][j][idx] <== f[0][l][j][idx];
|
||||
|
||||
}
|
||||
*/
|
||||
@@ -0,0 +1,718 @@
|
||||
pragma circom 2.0.2;
|
||||
|
||||
include "../../../bigInt/bigInt.circom";
|
||||
include "../../../bigInt/bigIntFunc.circom";
|
||||
include "fp2.circom";
|
||||
include "fp12.circom";
|
||||
|
||||
// Extra circuits with different approaches to field operations.
|
||||
// Kept here for reference; less efficient or specialized than the ones in field_elements.circom
|
||||
|
||||
|
||||
// a, b are elements of Fp^L
|
||||
// a[i] represents a[i][0] + a[i][1] * 2 * *CHUNK_SIZE + ... + a[i][L - 1] * 2 * *(CHUNK_SIZE*(CHUNK_NUMBER - 1))
|
||||
// compute a+b in Fp^L
|
||||
template FieldAdd2D(CHUNK_SIZE, CHUNK_NUMBER, L) {
|
||||
signal input a[L][CHUNK_NUMBER];
|
||||
signal input b[L][CHUNK_NUMBER];
|
||||
signal input p[CHUNK_NUMBER];
|
||||
signal output c[L][CHUNK_NUMBER];
|
||||
|
||||
component adders[L];
|
||||
for (var i = 0; i < L; i++) {
|
||||
adders[i] = BigAddModP(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
for (var j = 0; j < CHUNK_NUMBER; j++) {
|
||||
adders[i].a[j] <== a[i][j];
|
||||
adders[i].b[j] <== b[i][j];
|
||||
adders[i].p[j] <== p[j];
|
||||
}
|
||||
for (var j = 0; j < CHUNK_NUMBER; j++) {
|
||||
c[i][j] <== adders[i].out[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// take a polynomial expression a[0] + omega^1 a[1] + ... + omega^(2l - 2) a[2l - 2]
|
||||
// reduce it to degree L - 1 using omega^L + omega^(L - 1) POLY[L - 1] + ... + POLY[0] = 0
|
||||
// WARNING: can produce incorrectly handled negative coefficients. only here for reference; do not use
|
||||
template PolynomialReduce(L) {
|
||||
signal input a[2 * L - 1];
|
||||
signal input POLY[L];
|
||||
signal output out[L];
|
||||
|
||||
var residue[2 * L - 1];
|
||||
signal quotient[L - 1];
|
||||
for (var i = 0; i < 2 * L - 1; i++) {
|
||||
residue[i] = a[i];
|
||||
}
|
||||
for (var i = L - 2; i >= 0; i --) {
|
||||
for (var j = 0; j < L; j++) {
|
||||
residue[i + j] = residue[i + j] - residue[i + L] * POLY[j];
|
||||
}
|
||||
quotient[i] <-- residue[i + L];
|
||||
residue[i + L] = 0;
|
||||
}
|
||||
component mult = BigMultShortLong(1, L + 1);
|
||||
for (var i = 0; i < L - 1; i++) {
|
||||
mult.a[i] <== quotient[i];
|
||||
mult.b[i] <== POLY[i];
|
||||
}
|
||||
mult.a[L - 1] <== 0;
|
||||
mult.a[L] <== 0;
|
||||
mult.b[L - 1] <== POLY[L - 1];
|
||||
mult.b[L] <== 1;
|
||||
signal aOut[2 * L - 1];
|
||||
for (var i = 0; i < 2 * L - 1; i++) {
|
||||
aOut[i] <== mult.out[i];
|
||||
}
|
||||
for (var i = 0; i < L; i++ ) {
|
||||
out[i] <-- residue[i];
|
||||
}
|
||||
for (var i = 0; i < L; i++) {
|
||||
a[i] === aOut[i] + out[i];
|
||||
}
|
||||
for (var i = L; i < 2 * L - 1; i++) {
|
||||
a[i] === aOut[i];
|
||||
}
|
||||
}
|
||||
|
||||
template Fp2PolynomialReduce(CHUNK_SIZE, CHUNK_NUMBER, p) {
|
||||
var L = 2;
|
||||
signal input a[2 * L - 1][CHUNK_NUMBER];
|
||||
var POLY[2] = [1, 0]; // x^2 + 1 = 0
|
||||
signal output out[L][CHUNK_NUMBER];
|
||||
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
out[1][i] <== a[1][i];
|
||||
}
|
||||
component sub = FpSubtract(CHUNK_SIZE, CHUNK_NUMBER, p);
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
sub.a[i] <== a[0][i];
|
||||
sub.b[i] <== a[2][i];
|
||||
}
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
out[0][i] <== sub.out[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// very un-optimized version:
|
||||
|
||||
// outputs a*b in Fp2
|
||||
// (a0 + a1 u)*(b0 + b1 u) = (a0*b0 - a1*b1) + (a0*b1 + a1*b0)u
|
||||
// out[i] has CHUNK_NUMBER registers each in [0, 2^CHUNK_SIZE)
|
||||
// out[i] in [0, p)
|
||||
// A similar circuit can do multiplication in different fields.
|
||||
// The only difference is that Fp2PolynomialReduce (which reduces quadratics by x^2 + 1)
|
||||
// must be replaced with a different circuit specialized to the minimal polynomial
|
||||
template Fp2Multiply1(CHUNK_SIZE, CHUNK_NUMBER, p) {
|
||||
// L is always 2. POLY is always [1, 0]
|
||||
var L = 2;
|
||||
signal input a[L][CHUNK_NUMBER];
|
||||
signal input b[L][CHUNK_NUMBER];
|
||||
signal output out[L][CHUNK_NUMBER];
|
||||
|
||||
component mult = BigMultShortLong2D(CHUNK_SIZE, CHUNK_NUMBER, L);
|
||||
for (var i = 0; i < L; i++) {
|
||||
for (var j = 0; j < CHUNK_NUMBER; j++) {
|
||||
mult.a[i][j] <== a[i][j];
|
||||
mult.b[i][j] <== b[i][j];
|
||||
}
|
||||
} // out: 2l - 1 x 2k - 1 array of longs
|
||||
component longShorts[2 * L - 1];
|
||||
for (var i = 0; i < 2 * L - 1; i++) {
|
||||
longShorts[i] = LongToShortNoEndCarry(CHUNK_SIZE, 2 * CHUNK_NUMBER - 1);
|
||||
for (var j = 0; j < 2 * CHUNK_NUMBER - 1; j++) {
|
||||
longShorts[i].in[j] <== mult.out[i][j];
|
||||
}
|
||||
} // out: 2l - 1 x 2k array of shorts
|
||||
component bigmods[2 * L - 1];
|
||||
for (var i = 0; i < 2 * L - 1; i++) {
|
||||
bigmods[i] = BigMod(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
for (var j = 0; j < 2 * CHUNK_NUMBER; j++) {
|
||||
bigmods[i].a[j] <== longShorts[i].out[j];
|
||||
}
|
||||
for (var j = 0; j < CHUNK_NUMBER; j++) {
|
||||
bigmods[i].b[j] <== p[j];
|
||||
}
|
||||
} // out: 2l - 1 x CHUNK_NUMBER array of shorts
|
||||
component reduce = Fp2PolynomialReduce(CHUNK_SIZE, CHUNK_NUMBER, p);
|
||||
for (var i = 0; i < 2 * L - 1; i++) {
|
||||
for (var j = 0; j < CHUNK_NUMBER; j++) {
|
||||
reduce.a[i][j] <== bigmods[i].mod[j];
|
||||
}
|
||||
} // out: L x CHUNK_NUMBER array of shorts
|
||||
for (var i = 0; i < L; i++) {
|
||||
for (var j = 0; j < CHUNK_NUMBER; j++) {
|
||||
out[i][j] <== reduce.out[i][j];
|
||||
}
|
||||
} // out: L x CHUNK_NUMBER array of shorts
|
||||
}
|
||||
|
||||
|
||||
// squaring can be optimized to save 2 multiplication
|
||||
// (a**2-b**2) = (a+b)(a-b)
|
||||
// (a+b u)**2 = (a+b)(a-b) + (a*b+a*b)u
|
||||
template Fp2Square(CHUNK_SIZE, CHUNK_NUMBER){
|
||||
signal input in[2][CHUNK_NUMBER];
|
||||
signal input p[CHUNK_NUMBER];
|
||||
signal output out[2][CHUNK_NUMBER];
|
||||
|
||||
component SUM = BigAdd(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++){
|
||||
SUM.a[i] <== in[0][i];
|
||||
SUM.b[i] <== in[1][i];
|
||||
}
|
||||
component diff = BigSubModP(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++){
|
||||
diff.a[i] <== in[0][i];
|
||||
diff.b[i] <== in[1][i];
|
||||
diff.p[i] <== p[i];
|
||||
}
|
||||
component prod = BigMult(CHUNK_SIZE, CHUNK_NUMBER + 1);
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++){
|
||||
prod.a[i] <== SUM.out[i];
|
||||
prod.b[i] <== diff.out[i];
|
||||
}
|
||||
prod.a[CHUNK_NUMBER] <== SUM.out[CHUNK_NUMBER];
|
||||
prod.b[CHUNK_NUMBER] <== 0;
|
||||
|
||||
component prodMod = BigMod2(CHUNK_SIZE, CHUNK_NUMBER, 2 * CHUNK_NUMBER + 2);
|
||||
for(var i = 0; i < 2 * CHUNK_NUMBER + 2; i++){
|
||||
prodMod.a[i] <== prod.out[i];
|
||||
if(i < CHUNK_NUMBER){
|
||||
prodMod.b[i] <== p[i];
|
||||
}
|
||||
}
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++){
|
||||
out[0][i] <== prodMod.mod[i];
|
||||
}
|
||||
|
||||
component aB = BigMult(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++){
|
||||
aB.a[i] <== in[0][i];
|
||||
aB.b[i] <== in[1][i];
|
||||
}
|
||||
component twoAB = BigAdd(CHUNK_SIZE, 2 * CHUNK_NUMBER);
|
||||
for(var i = 0; i < 2 * CHUNK_NUMBER; i++){
|
||||
twoAB.a[i] <== aB.out[i];
|
||||
twoAB.b[i] <== aB.out[i];
|
||||
}
|
||||
component twoABMod = BigMod2(CHUNK_SIZE, CHUNK_NUMBER, 2 * CHUNK_NUMBER + 1);
|
||||
for(var i = 0; i < 2 * CHUNK_NUMBER + 1; i++){
|
||||
twoABMod.a[i] <== twoAB.out[i];
|
||||
if(i < CHUNK_NUMBER){
|
||||
twoABMod.b[i] <== p[i];
|
||||
}
|
||||
}
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++){
|
||||
out[1][i] <== twoABMod.mod[i];
|
||||
}
|
||||
}
|
||||
|
||||
// a[2][CHUNK_NUMBER] all registers in [0, 2 * *CHUNK_SIZE)
|
||||
// b[2][CHUNK_NUMBER] all registers in [0, 2 * *CHUNK_SIZE)
|
||||
// p[CHUNK_NUMBER]
|
||||
// consider a,b as elements of Fp2
|
||||
// out[2][2][CHUNK_NUMBER] solving
|
||||
// a0*b0 + (p-a1)*b1 = p * out[0][0] + out[0][1] with out[0][1] in [0,p)
|
||||
// a0*b1 + a1*b0 = p * out[1][0] + out[1][1] with out[1][1] in [0,p)
|
||||
// out[i][0] has CHUNK_NUMBER + 2 registers in short BigInt format [0, 2 * *CHUNK_SIZE)
|
||||
// out[i][1] has CHUNK_NUMBER registers in short BigInt format
|
||||
// a * b = out[0][1] + out[1][1] * u in Fp2
|
||||
function Fp2prod(CHUNK_SIZE, CHUNK_NUMBER, a, b, p){
|
||||
var out[2][2][20];
|
||||
// solve for x and Y such that a0*b0 + (p-a1)*b1 = p*x + Y with Y in [0,p)
|
||||
// -a1*b1 = (p-a1)*b1 mod p
|
||||
var A0B0_VAR[20] = prod(CHUNK_SIZE, CHUNK_NUMBER, a[0], b[0]);
|
||||
var A1_NEG[20] = long_sub(CHUNK_SIZE, CHUNK_NUMBER, p, a[1]);
|
||||
var A1B1_NEG[20] = prod(CHUNK_SIZE, CHUNK_NUMBER, A1_NEG, b[1]);
|
||||
var diff[20] = long_add(CHUNK_SIZE, 2 * CHUNK_NUMBER, A0B0_VAR, A1B1_NEG); // 2 * CHUNK_NUMBER + 1 registers
|
||||
out[0] = long_div2(CHUNK_SIZE, CHUNK_NUMBER, CHUNK_NUMBER + 1, diff, p);
|
||||
// x = out[0][0] has CHUNK_NUMBER + 2 registers, Y = out[0][1] has CHUNK_NUMBER registers
|
||||
|
||||
// solve for x and Y such that a0*b1 + a1*b0 = p*x + Y with Y in [0,p)
|
||||
var A0B1_VAR[20] = prod(CHUNK_SIZE, CHUNK_NUMBER, a[0], b[1]);
|
||||
var A1B0_VAR[20] = prod(CHUNK_SIZE, CHUNK_NUMBER, a[1], b[0]);
|
||||
var SUM[20] = long_add(CHUNK_SIZE, 2 * CHUNK_NUMBER, A0B1_VAR, A1B0_VAR); // output 2 * CHUNK_NUMBER + 1 registers
|
||||
out[1] = long_div2(CHUNK_SIZE, CHUNK_NUMBER, CHUNK_NUMBER + 1, SUM, p);
|
||||
// x = out[1][0] has CHUNK_NUMBER + 2 registers, Y = out[1][1] has CHUNK_NUMBER registers
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
// output (a0 + a1 u)*(b0 + b1 u) = (a0*b0 + (p-a1)*b1) + (a0*b1 + a1*b0)u
|
||||
// where no carries are performed
|
||||
// out[0], out[1] have 2 * CHUNK_NUMBER - 1 registers
|
||||
// out[0][i] in (-(CHUNK_NUMBER + 1)*2^{2n}, (CHUNK_NUMBER + 1)*2^{2n + 1})
|
||||
// out[1][i] in [0, (CHUNK_NUMBER + 1)*2^{2n + 1})
|
||||
template Fp2multiplyNoCarry(CHUNK_SIZE, CHUNK_NUMBER){
|
||||
signal input a[2][CHUNK_NUMBER];
|
||||
signal input b[2][CHUNK_NUMBER];
|
||||
signal input p[CHUNK_NUMBER];
|
||||
signal output out[2][2 * CHUNK_NUMBER - 1];
|
||||
|
||||
component a0b0 = BigMultShortLong(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
component a1b1 = BigMultShortLong(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
component pb1 = BigMultShortLong(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
component a0b1 = BigMultShortLong(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
component a1b0 = BigMultShortLong(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++){
|
||||
a0b0.a[i] <== a[0][i];
|
||||
a0b0.b[i] <== b[0][i];
|
||||
|
||||
a1b1.a[i] <== a[1][i];
|
||||
a1b1.b[i] <== b[1][i];
|
||||
|
||||
pb1.a[i] <== p[i];
|
||||
pb1.b[i] <== b[1][i];
|
||||
|
||||
a0b1.a[i] <== a[0][i];
|
||||
a0b1.b[i] <== b[1][i];
|
||||
|
||||
a1b0.a[i] <== a[1][i];
|
||||
a1b0.b[i] <== b[0][i];
|
||||
}
|
||||
|
||||
for(var i = 0; i < 2 * CHUNK_NUMBER - 1; i++){
|
||||
out[0][i] <== a0b0.out[i] + pb1.out[i] - a1b1.out[i];
|
||||
out[1][i] <== a0b1.out[i] + a1b0.out[i];
|
||||
}
|
||||
}
|
||||
|
||||
// multiplication specialized to Fp^2
|
||||
// (a0 + a1 u)*(b0 + b1 u) = (a0*b0 - a1*b1) + (a0*b1 + a1*b0)u
|
||||
template Fp2multiply(CHUNK_SIZE, CHUNK_NUMBER){
|
||||
signal input a[2][CHUNK_NUMBER];
|
||||
signal input b[2][CHUNK_NUMBER];
|
||||
signal input p[CHUNK_NUMBER];
|
||||
signal output out[2][CHUNK_NUMBER];
|
||||
|
||||
var LOGK = log_ceil(CHUNK_NUMBER);
|
||||
assert(2 * CHUNK_SIZE + 1 + LOGK < 254);
|
||||
|
||||
var X_VAR[2][2][20] = Fp2prod(CHUNK_SIZE, CHUNK_NUMBER, a, b, p);
|
||||
component rangeChecks[2][CHUNK_NUMBER];
|
||||
component lT[2];
|
||||
signal x[2][CHUNK_NUMBER + 2];
|
||||
component xRangeChecks[2][CHUNK_NUMBER + 2];
|
||||
|
||||
for(var eps = 0; eps < 2; eps++){
|
||||
lT[eps] = BigLessThan(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++){
|
||||
out[eps][i] <-- X_VAR[eps][1][i];
|
||||
rangeChecks[eps][i] = Num2Bits(CHUNK_SIZE);
|
||||
rangeChecks[eps][i].in <== out[eps][i];
|
||||
|
||||
lT[eps].a[i] <== out[eps][i];
|
||||
lT[eps].b[i] <== p[i];
|
||||
}
|
||||
lT[eps].out === 1;
|
||||
|
||||
for(var i = 0; i < CHUNK_NUMBER + 2; i++){
|
||||
x[eps][i] <-- X_VAR[eps][0][i];
|
||||
xRangeChecks[eps][i] = Num2Bits(CHUNK_SIZE);
|
||||
xRangeChecks[eps][i].in <== x[eps][i];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// out[0] constraint: x = x[0], Y = out[0]
|
||||
// constrain by Carry(a0 *' b0 +' p *' b1 -' a1 *' b1 - p *' x - Y ) = 0
|
||||
// where all operations are performed without CARRY
|
||||
// each register is an overflow representation in the range (-CHUNK_NUMBER*2^{2n + 1} - 2^CHUNK_SIZE, CHUNK_NUMBER*2^{2n + 1} )
|
||||
// which is inside (- 2^{2n + 1+LOGK}, 2^{2n + 1+LOGK})
|
||||
|
||||
// out[1] constraint: x = x[1], Y = out[1]
|
||||
// constrain by Carry(a0 *' b1 +' a1 *' b0 -' p *' x - Y) = 0
|
||||
// each register is an overflow representation in the range (-CHUNK_NUMBER*2^{2n} - 2^CHUNK_SIZE, CHUNK_NUMBER*2^{2n + 1} )
|
||||
// which is inside (- 2^{2n + 1+LOGK}, 2^{2n + 1+LOGK})
|
||||
|
||||
component aB = Fp2multiplyNoCarry(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++){
|
||||
aB.p[i] <== p[i];
|
||||
aB.a[0][i] <== a[0][i];
|
||||
aB.a[1][i] <== a[1][i];
|
||||
aB.b[0][i] <== b[0][i];
|
||||
aB.b[1][i] <== b[1][i];
|
||||
}
|
||||
component pX[2];
|
||||
component carryCheck[2];
|
||||
for(var eps = 0; eps < 2; eps++){
|
||||
pX[eps] = BigMultShortLong(CHUNK_SIZE, CHUNK_NUMBER + 2); // 2 * CHUNK_NUMBER + 3 registers
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++){
|
||||
pX[eps].a[i] <== p[i];
|
||||
pX[eps].b[i] <== x[eps][i];
|
||||
}
|
||||
for(var i = CHUNK_NUMBER; i < CHUNK_NUMBER + 2; i++){
|
||||
pX[eps].a[i] <== 0;
|
||||
pX[eps].b[i] <== x[eps][i];
|
||||
}
|
||||
|
||||
carryCheck[eps] = CheckCarryToZero(CHUNK_SIZE, 2 * CHUNK_SIZE + 2 + LOGK, 2 * CHUNK_NUMBER + 3);
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++)
|
||||
carryCheck[eps].in[i] <== aB.out[eps][i] - pX[eps].out[i] - out[eps][i];
|
||||
for(var i = CHUNK_NUMBER; i < 2 * CHUNK_NUMBER - 1; i++)
|
||||
carryCheck[eps].in[i] <== aB.out[eps][i] - pX[eps].out[i];
|
||||
for(var i = 2 * CHUNK_NUMBER - 1; i < 2 * CHUNK_NUMBER + 3; i++)
|
||||
carryCheck[eps].in[i] <== -pX[eps].out[i];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// adapted from BigMultShortLong2D and LongToShortNoEndCarry2 witness computation
|
||||
function prod3D(CHUNK_SIZE, CHUNK_NUMBER, L, a, b, c) {
|
||||
// first compute the intermediate values. taken from BigMulShortLong
|
||||
var PROD_VAL[20][20]; // length is 3l - 2 by 3k - 2
|
||||
for (var i = 0; i < 3 * CHUNK_NUMBER; i++) {
|
||||
for (var j = 0; j < 3 * L; j++) {
|
||||
PROD_VAL[j][i] = 0;
|
||||
}
|
||||
}
|
||||
for (var i1 = 0; i1 < CHUNK_NUMBER; i1++) {
|
||||
for (var i2 = 0; i2 < CHUNK_NUMBER; i2++) {
|
||||
for (var i3 = 0; i3 < CHUNK_NUMBER; i3++) {
|
||||
for (var j1 = 0; j1 < L; j1++) {
|
||||
for (var j2 = 0; j2 < L; j2++) {
|
||||
for (var j3 = 0; j3 < L; j3++) {
|
||||
PROD_VAL[j1 + j2 + j3][i1 + i2 + i3] = PROD_VAL[j1 + j2 + j3][i1 + i2 + i3] + a[j1][i1] * b[j2][i2] * c[j3][i3];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// now do a bunch of carrying to make sure registers not overflowed. taken from LongToShortNoEndCarry2
|
||||
var out[20][20]; // length is 3 * L by 3 * CHUNK_NUMBER
|
||||
|
||||
var SPLIT[20][20][3]; // second dimension has length 3 * CHUNK_NUMBER - 1
|
||||
for (var j = 0; j < 3 * L - 1; j++) {
|
||||
for (var i = 0; i < 3 * CHUNK_NUMBER - 1; i++) {
|
||||
SPLIT[j][i] = SplitThreeFn(PROD_VAL[j][i], CHUNK_SIZE, CHUNK_SIZE, CHUNK_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
var CARRY[20][20]; // length is 3l - 1 x 3k
|
||||
var SUM_AND_CARRY[20][2];
|
||||
for (var j = 0; j < 3 * L - 1; j++) {
|
||||
CARRY[j][0] = 0;
|
||||
out[j][0] = SPLIT[j][0][0];
|
||||
if (3 * CHUNK_NUMBER - 1 > 1) {
|
||||
SUM_AND_CARRY[j] = SplitFn(SPLIT[j][0][1] + SPLIT[j][1][0], CHUNK_SIZE, CHUNK_SIZE);
|
||||
out[j][1] = SUM_AND_CARRY[j][0];
|
||||
CARRY[j][1] = SUM_AND_CARRY[j][1];
|
||||
}
|
||||
if (3 * CHUNK_NUMBER - 1 > 2) {
|
||||
for (var i = 2; i < 3 * CHUNK_NUMBER - 1; i++) {
|
||||
SUM_AND_CARRY[j] = SplitFn(SPLIT[j][i][0] + SPLIT[j][i - 1][1] + SPLIT[j][i - 2][2] + CARRY[j][i - 1], CHUNK_SIZE, CHUNK_SIZE);
|
||||
out[j][i] = SUM_AND_CARRY[j][0];
|
||||
CARRY[j][i] = SUM_AND_CARRY[j][1];
|
||||
}
|
||||
out[j][3 * CHUNK_NUMBER - 1] = SPLIT[j][3 * CHUNK_NUMBER - 2][1] + SPLIT[j][3 * CHUNK_NUMBER-3][2] + CARRY[j][3 * CHUNK_NUMBER - 2];
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
// a = SUM w^i u^j a_ij for w^6=u + 1, u^2= - 1. similarly for b, c
|
||||
// we first write a = A + B u, b = C + D u, c = E + F u and compute
|
||||
// abc = (ACE - BDE - ADF - BCF) + (ADE + BCE + ACF - BCF) u, and then simplify the representation
|
||||
// assumes CHUNK_SIZE, CHUNK_NUMBER are chosen so that cubic carries are OK
|
||||
template Fp12MultiplyThree(CHUNK_SIZE, CHUNK_NUMBER, p) {
|
||||
var L = 6;
|
||||
signal input a[L][2][CHUNK_NUMBER];
|
||||
signal input b[L][2][CHUNK_NUMBER];
|
||||
signal input c[L][2][CHUNK_NUMBER];
|
||||
signal output out[L][2][CHUNK_NUMBER];
|
||||
|
||||
var LOGK = log_ceil(CHUNK_NUMBER);
|
||||
var LOGL = 4;
|
||||
assert(L < 15);
|
||||
assert(CHUNK_NUMBER < 7);
|
||||
assert(2 * CHUNK_SIZE + 1 + LOGK + LOGL < 254);
|
||||
|
||||
var a0[L][CHUNK_NUMBER];
|
||||
var a1[L][CHUNK_NUMBER];
|
||||
var b0[L][CHUNK_NUMBER];
|
||||
var b1[L][CHUNK_NUMBER];
|
||||
var c0[L][CHUNK_NUMBER];
|
||||
var c1[L][CHUNK_NUMBER];
|
||||
var NEG_A0[L][20];
|
||||
var NEG_A1[L][20];
|
||||
for (var i = 0; i < L; i++) {
|
||||
for (var j = 0; j < CHUNK_NUMBER; j++) {
|
||||
a0[i][j] = a[i][0][j];
|
||||
a1[i][j] = a[i][1][j];
|
||||
b0[i][j] = b[i][0][j];
|
||||
b1[i][j] = b[i][1][j];
|
||||
c0[i][j] = c[i][0][j];
|
||||
c1[i][j] = c[i][1][j];
|
||||
}
|
||||
}
|
||||
for (var i = 0; i < L; i++) {
|
||||
NEG_A0[i] = long_sub(CHUNK_SIZE, CHUNK_NUMBER, p, a0[i]);
|
||||
NEG_A1[i] = long_sub(CHUNK_SIZE, CHUNK_NUMBER, p, a1[i]);
|
||||
}
|
||||
|
||||
var REAL_INIT[3 * L - 1][20];
|
||||
var IMAG_INIT[3 * L - 1][20];
|
||||
var IMAG_INIT_NEG[3 * L - 1][20];
|
||||
// each product will be 3l - 1 x 3k
|
||||
var A0B0C0_VAR[20][20] = prod3D(CHUNK_SIZE, CHUNK_NUMBER, L, a0, b0, c0);
|
||||
var A1B1C0_NEG[20][20] = prod3D(CHUNK_SIZE, CHUNK_NUMBER, L, NEG_A1, b1, c0);
|
||||
var A1B0C1_NEG[20][20] = prod3D(CHUNK_SIZE, CHUNK_NUMBER, L, NEG_A1, b0, c1);
|
||||
var A0B1C1_NEG[20][20] = prod3D(CHUNK_SIZE, CHUNK_NUMBER, L, NEG_A0, b1, c1);
|
||||
|
||||
var A1B0C0_VAR[20][20] = prod3D(CHUNK_SIZE, CHUNK_NUMBER, L, a1, b0, c0);
|
||||
var A0B1C0_VAR[20][20] = prod3D(CHUNK_SIZE, CHUNK_NUMBER, L, a0, b1, c0);
|
||||
var A0B0C1_VAR[20][20] = prod3D(CHUNK_SIZE, CHUNK_NUMBER, L, a0, b0, c1);
|
||||
var A1B1C1_NEG[20][20] = prod3D(CHUNK_SIZE, CHUNK_NUMBER, L, NEG_A0, b1, c1);
|
||||
|
||||
var A1B0C0_NEG[20][20] = prod3D(CHUNK_SIZE, CHUNK_NUMBER, L, NEG_A1, b0, c0);
|
||||
var A0B1C0_NEG[20][20] = prod3D(CHUNK_SIZE, CHUNK_NUMBER, L, NEG_A0, b1, c0);
|
||||
var A0B0C1_NEG[20][20] = prod3D(CHUNK_SIZE, CHUNK_NUMBER, L, NEG_A0, b0, c1);
|
||||
var A1B1C1_VAR[20][20] = prod3D(CHUNK_SIZE, CHUNK_NUMBER, L, a0, b1, c1);
|
||||
|
||||
for (var i = 0; i < 3 * L - 1; i++) { // compute initial rep (deg w = 10)
|
||||
REAL_INIT[i] = long_add4(CHUNK_SIZE, 3 * CHUNK_NUMBER, A0B0C0_VAR[i], A1B1C0_NEG[i], A1B0C1_NEG[i], A0B1C1_NEG[i]); // 3 * CHUNK_NUMBER + 1 registers each
|
||||
IMAG_INIT[i] = long_add4(CHUNK_SIZE, 3 * CHUNK_NUMBER, A1B0C0_VAR[i], A0B1C0_VAR[i], A0B0C1_VAR[i], A1B1C1_NEG[i]);
|
||||
IMAG_INIT_NEG[i] = long_add4(CHUNK_SIZE, 3 * CHUNK_NUMBER, A1B0C0_NEG[i], A0B1C0_NEG[i], A0B0C1_NEG[i], A1B1C1_VAR[i]);
|
||||
}
|
||||
|
||||
// carries using w^6 = u + 1, w^12 = 2 u
|
||||
var REAL_CARRY[L][20];
|
||||
var IMAG_CARRY[L][20];
|
||||
var REAL_FINAL[L][20];
|
||||
var IMAG_FINAL[L][20];
|
||||
var ZEROS[20]; // to balance register sizes
|
||||
for (var i = 0; i < 20; i++) {
|
||||
ZEROS[i] = 0;
|
||||
}
|
||||
for (var i = 0; i < L; i++) {
|
||||
if (i == L - 1) {
|
||||
REAL_CARRY[i] = long_add4(CHUNK_SIZE, 3 * CHUNK_NUMBER + 1, ZEROS, ZEROS, REAL_INIT[i + L], IMAG_INIT_NEG[i + L]);
|
||||
IMAG_CARRY[i] = long_add4(CHUNK_SIZE, 3 * CHUNK_NUMBER + 1, ZEROS, ZEROS, REAL_INIT[i + L], IMAG_INIT[i + L]);
|
||||
} else {
|
||||
REAL_CARRY[i] = long_add4(CHUNK_SIZE, 3 * CHUNK_NUMBER + 1, REAL_INIT[i + L], IMAG_INIT_NEG[i + L], IMAG_INIT_NEG[i + 2 * L], IMAG_INIT_NEG[i + 2 * L]); // now 3 * CHUNK_NUMBER + 2 registers
|
||||
IMAG_CARRY[i] = long_add4(CHUNK_SIZE, 3 * CHUNK_NUMBER + 1, IMAG_INIT[i + L], REAL_INIT[i + L], REAL_INIT[i + 2 * L], REAL_INIT[i + 2 * L]);
|
||||
}
|
||||
}
|
||||
for (var i = 0; i < L; i++) {
|
||||
REAL_FINAL[i] = long_add_unequal(CHUNK_SIZE, 3 * CHUNK_NUMBER + 2, 3 * CHUNK_NUMBER + 1, REAL_CARRY[i], REAL_INIT[i]); // now 3 * CHUNK_NUMBER + 3 registers
|
||||
IMAG_FINAL[i] = long_add_unequal(CHUNK_SIZE, 3 * CHUNK_NUMBER + 2, 3 * CHUNK_NUMBER + 1, IMAG_CARRY[i], IMAG_INIT[i]);
|
||||
}
|
||||
|
||||
// reduction mod p
|
||||
var PROD_REAL_TEMP[L][2][20];
|
||||
var PROD_IMAG_TEMP[L][2][20];
|
||||
|
||||
// PROD_REAL[*][0][2 * CHUNK_NUMBER + 4] * p + PROD_REAL[*][1][CHUNK_NUMBER] = REAL_FINAL[*]
|
||||
// PROD_IMAG[*][0][2 * CHUNK_NUMBER + 4] * p + PROD_IMAG[*][1][CHUNK_NUMBER] = IMAG_FINAL[*]
|
||||
signal PROD_REAL[L][2][2 * CHUNK_NUMBER + 4];
|
||||
signal PROD_IMAG[L][2][2 * CHUNK_NUMBER + 4];
|
||||
for (var i = 0; i < L; i++) {
|
||||
PROD_REAL_TEMP[i] = long_div2(CHUNK_SIZE, CHUNK_NUMBER, 2 * CHUNK_NUMBER + 3, REAL_FINAL[i], p); // 2 * CHUNK_NUMBER + 4 register quotient, CHUNK_NUMBER register remainder
|
||||
PROD_IMAG_TEMP[i] = long_div2(CHUNK_SIZE, CHUNK_NUMBER, 2 * CHUNK_NUMBER + 3, IMAG_FINAL[i], p);
|
||||
}
|
||||
for (var i = 0; i < L; i++) {
|
||||
for (var j = 0; j < 2 * CHUNK_NUMBER + 4; j++) {
|
||||
PROD_REAL[i][0][j] <-- PROD_REAL_TEMP[i][0][j];
|
||||
PROD_IMAG[i][0][j] <-- PROD_IMAG_TEMP[i][0][j];
|
||||
if (j < CHUNK_NUMBER) {
|
||||
PROD_REAL[i][1][j] <-- PROD_REAL_TEMP[i][1][j];
|
||||
PROD_IMAG[i][1][j] <-- PROD_IMAG_TEMP[i][1][j];
|
||||
} else {
|
||||
PROD_REAL[i][1][j] <== 0;
|
||||
PROD_IMAG[i][1][j] <== 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (var i = 0; i < L; i++) {
|
||||
for (var j = 0; j < CHUNK_NUMBER; j++) {
|
||||
out[i][0][j] <== PROD_REAL[i][1][j];
|
||||
out[i][1][j] <== PROD_IMAG[i][1][j];
|
||||
}
|
||||
}
|
||||
|
||||
component outRangeChecks[L][2][CHUNK_NUMBER];
|
||||
for(var i = 0; i < L; i++){
|
||||
for (var j = 0; j < 2; j++) {
|
||||
for (var m = 0; m < CHUNK_NUMBER; m++) {
|
||||
outRangeChecks[i][j][m] = Num2Bits(CHUNK_SIZE);
|
||||
outRangeChecks[i][j][m].in <== out[i][j][m];
|
||||
}
|
||||
}
|
||||
}
|
||||
component lT[L][2];
|
||||
for (var i = 0; i < L; i++) {
|
||||
for (var j = 0; j < 2; j++) {
|
||||
lT[i][j] = BigLessThan(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
for (var m = 0; m < CHUNK_NUMBER; m++) {
|
||||
lT[i][j].a[m] <== out[i][j][m];
|
||||
lT[i][j].b[m] <== p[m];
|
||||
}
|
||||
lT[i][j].out === 1;
|
||||
}
|
||||
}
|
||||
|
||||
component divRangeChecks[L][2][2 * CHUNK_NUMBER + 4];
|
||||
for (var i = 0; i < L; i++) {
|
||||
for (var j = 0; j < 2 * CHUNK_NUMBER + 4; j++) {
|
||||
divRangeChecks[i][0][j] = Num2Bits(CHUNK_SIZE);
|
||||
divRangeChecks[i][1][j] = Num2Bits(CHUNK_SIZE);
|
||||
divRangeChecks[i][0][j].in <== PROD_REAL[i][0][j];
|
||||
divRangeChecks[i][1][j].in <== PROD_IMAG[i][0][j];
|
||||
}
|
||||
}
|
||||
|
||||
// constrain by:
|
||||
// x = a0 *' b0 *' c0 +' (p -' a1) *' b1 *' c0 +' (p -' a1) *' b0 *' c1 +' (p -' a0) *' b1 *' c1
|
||||
// Y = a1 *' b0 *' c0 +' a0 *' b1 *' c0 +' a0 *' b0 *' c1 +' (p -' a1) *' b1 *' c1
|
||||
// Carry(X_0 +' X_1 -' Y_1 -' Y_2 -' Y_2 -' p *' PROD_REAL[0] -' PROD_REAL[1] ) = 0
|
||||
// Carry(Y_0 +' X_1 +' Y_1 +' X_2 +' X_2 -' p *' PROD_IMAG[0] -' PROD_IMAG[1] ) = 0
|
||||
// where all operations are performed without CARRY
|
||||
// X_0 is the coeffs of w^0, ..., w^5
|
||||
// X_1 is the coeffs of w^6, ..., w^11
|
||||
// X_2 is the coeffs of w^12, ..., w^17
|
||||
// each register is an overflow representation in the range (-kl*2^{3n+4}, kl*2^{3n + 4} )
|
||||
component b0c0 = BigMultShortLong2D(CHUNK_SIZE, CHUNK_NUMBER, L);
|
||||
component b0c1 = BigMultShortLong2D(CHUNK_SIZE, CHUNK_NUMBER, L);
|
||||
component b1c0 = BigMultShortLong2D(CHUNK_SIZE, CHUNK_NUMBER, L);
|
||||
component b1c1 = BigMultShortLong2D(CHUNK_SIZE, CHUNK_NUMBER, L);
|
||||
for (var i = 0; i < L; i++) {
|
||||
for (var j = 0; j < CHUNK_NUMBER; j++) {
|
||||
b0c0.a[i][j] <== b[i][0][j];
|
||||
b0c0.b[i][j] <== c[i][0][j];
|
||||
b0c1.a[i][j] <== b[i][0][j];
|
||||
b0c1.b[i][j] <== c[i][1][j];
|
||||
b1c0.a[i][j] <== b[i][1][j];
|
||||
b1c0.b[i][j] <== c[i][0][j];
|
||||
b1c1.a[i][j] <== b[i][1][j];
|
||||
b1c1.b[i][j] <== c[i][1][j];
|
||||
}
|
||||
}
|
||||
|
||||
component a0b0c0 = BigMultShortLong2DUnequal(CHUNK_SIZE, CHUNK_NUMBER, 2 * CHUNK_NUMBER - 1, L, 2 * L - 1);
|
||||
component a1b0c0 = BigMultShortLong2DUnequal(CHUNK_SIZE, CHUNK_NUMBER, 2 * CHUNK_NUMBER - 1, L, 2 * L - 1);
|
||||
component a0b0c1 = BigMultShortLong2DUnequal(CHUNK_SIZE, CHUNK_NUMBER, 2 * CHUNK_NUMBER - 1, L, 2 * L - 1);
|
||||
component a1b0c1 = BigMultShortLong2DUnequal(CHUNK_SIZE, CHUNK_NUMBER, 2 * CHUNK_NUMBER - 1, L, 2 * L - 1);
|
||||
component a0b1c0 = BigMultShortLong2DUnequal(CHUNK_SIZE, CHUNK_NUMBER, 2 * CHUNK_NUMBER - 1, L, 2 * L - 1);
|
||||
component a1b1c0 = BigMultShortLong2DUnequal(CHUNK_SIZE, CHUNK_NUMBER, 2 * CHUNK_NUMBER - 1, L, 2 * L - 1);
|
||||
component a0b1c1 = BigMultShortLong2DUnequal(CHUNK_SIZE, CHUNK_NUMBER, 2 * CHUNK_NUMBER - 1, L, 2 * L - 1);
|
||||
component a1b1c1 = BigMultShortLong2DUnequal(CHUNK_SIZE, CHUNK_NUMBER, 2 * CHUNK_NUMBER - 1, L, 2 * L - 1);
|
||||
|
||||
component pb0c1 = BigMultShortLong2DUnequal(CHUNK_SIZE, CHUNK_NUMBER, 2 * CHUNK_NUMBER - 1, L, 2 * L - 1);
|
||||
component pb1c0 = BigMultShortLong2DUnequal(CHUNK_SIZE, CHUNK_NUMBER, 2 * CHUNK_NUMBER - 1, L, 2 * L - 1);
|
||||
component pb1c1 = BigMultShortLong2DUnequal(CHUNK_SIZE, CHUNK_NUMBER, 2 * CHUNK_NUMBER - 1, L, 2 * L - 1);
|
||||
for (var i = 0; i < L; i++) {
|
||||
for (var j = 0; j < CHUNK_NUMBER; j++) {
|
||||
a0b0c0.a[i][j] <== a[i][0][j];
|
||||
a0b0c1.a[i][j] <== a[i][0][j];
|
||||
a0b1c0.a[i][j] <== a[i][0][j];
|
||||
a0b1c1.a[i][j] <== a[i][0][j];
|
||||
a1b0c0.a[i][j] <== a[i][1][j];
|
||||
a1b0c1.a[i][j] <== a[i][1][j];
|
||||
a1b1c0.a[i][j] <== a[i][1][j];
|
||||
a1b1c1.a[i][j] <== a[i][1][j];
|
||||
|
||||
pb0c1.a[i][j] <== p[j];
|
||||
pb1c0.a[i][j] <== p[j];
|
||||
pb1c1.a[i][j] <== p[j];
|
||||
}
|
||||
}
|
||||
for (var i = 0; i < 2 * L - 1; i++) {
|
||||
for (var j = 0; j < 2 * CHUNK_NUMBER - 1; j++) {
|
||||
a0b0c0.b[i][j] <== b0c0.out[i][j];
|
||||
a1b0c0.b[i][j] <== b0c0.out[i][j];
|
||||
a0b0c1.b[i][j] <== b0c1.out[i][j];
|
||||
a1b0c1.b[i][j] <== b0c1.out[i][j];
|
||||
a0b1c0.b[i][j] <== b1c0.out[i][j];
|
||||
a1b1c0.b[i][j] <== b1c0.out[i][j];
|
||||
a0b1c1.b[i][j] <== b1c1.out[i][j];
|
||||
a1b1c1.b[i][j] <== b1c1.out[i][j];
|
||||
|
||||
pb0c1.b[i][j] <== b0c1.out[i][j];
|
||||
pb1c0.b[i][j] <== b1c0.out[i][j];
|
||||
pb1c1.b[i][j] <== b1c1.out[i][j];
|
||||
}
|
||||
}
|
||||
|
||||
component pProdReal0[L];
|
||||
component pProdImag0[L];
|
||||
for (var i = 0; i < L; i++) {
|
||||
pProdReal0[i] = BigMultShortLongUnequal(CHUNK_SIZE, CHUNK_NUMBER, 2 * CHUNK_NUMBER + 4);
|
||||
pProdImag0[i] = BigMultShortLongUnequal(CHUNK_SIZE, CHUNK_NUMBER, 2 * CHUNK_NUMBER + 4);
|
||||
|
||||
for (var j = 0; j < CHUNK_NUMBER; j++) {
|
||||
pProdReal0[i].a[j] <== p[j];
|
||||
pProdImag0[i].a[j] <== p[j];
|
||||
}
|
||||
for (var j = 0; j < 2 * CHUNK_NUMBER + 4; j++) {
|
||||
pProdReal0[i].b[j] <== PROD_REAL[i][0][j];
|
||||
pProdImag0[i].b[j] <== PROD_IMAG[i][0][j];
|
||||
}
|
||||
}
|
||||
|
||||
var X0[L][3 * CHUNK_NUMBER - 2];
|
||||
var X1[L][3 * CHUNK_NUMBER - 2];
|
||||
var X2[L][3 * CHUNK_NUMBER - 2];
|
||||
var Y0[L][3 * CHUNK_NUMBER - 2];
|
||||
var Y1[L][3 * CHUNK_NUMBER - 2];
|
||||
var Y2[L][3 * CHUNK_NUMBER - 2];
|
||||
for (var i = 0; i < L; i++) {
|
||||
for (var j = 0; j < 3 * CHUNK_NUMBER - 2; j++) {
|
||||
X0[i][j] = a0b0c0.out[i][j] + pb1c0.out[i][j] - a1b1c0.out[i][j] + pb0c1.out[i][j] - a1b0c1.out[i][j] + pb1c1.out[i][j] - a0b1c1.out[i][j];
|
||||
X1[i][j] = a0b0c0.out[i + L][j] + pb1c0.out[i + L][j] - a1b1c0.out[i + L][j] + pb0c1.out[i + L][j] - a1b0c1.out[i + L][j] + pb1c1.out[i + L][j] - a0b1c1.out[i + L][j];
|
||||
Y0[i][j] = a1b0c0.out[i][j] + a0b1c0.out[i][j] + a0b0c1.out[i][j] + pb1c1.out[i][j] - a1b1c1.out[i][j];
|
||||
Y1[i][j] = a1b0c0.out[i + L][j] + a0b1c0.out[i + L][j] + a0b0c1.out[i + L][j] + pb1c1.out[i + L][j] - a1b1c1.out[i + L][j];
|
||||
if (i < L - 2) {
|
||||
X2[i][j] = a0b0c0.out[i + 2 * L][j] + pb1c0.out[i + 2 * L][j] - a1b1c0.out[i + 2 * L][j] + pb0c1.out[i + 2 * L][j] - a1b0c1.out[i + 2 * L][j] + pb1c1.out[i + 2 * L][j] - a0b1c1.out[i + 2 * L][j];
|
||||
Y2[i][j] = a1b0c0.out[i + 2 * L][j] + a0b1c0.out[i + 2 * L][j] + a0b0c1.out[i + 2 * L][j] + pb1c1.out[i + 2 * L][j] - a1b1c1.out[i + 2 * L][j];
|
||||
} else {
|
||||
X2[i][j] = 0;
|
||||
Y2[i][j] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component carryCheck[L][2];
|
||||
for (var i = 0; i < L; i++) {
|
||||
if (3 * CHUNK_NUMBER - 2 < 2 * CHUNK_NUMBER + 4) {
|
||||
carryCheck[i][0] = CheckCarryToZero(CHUNK_SIZE, 3 * CHUNK_SIZE + 4 + LOGK + LOGL, 2 * CHUNK_NUMBER + 4);
|
||||
carryCheck[i][1] = CheckCarryToZero(CHUNK_SIZE, 3 * CHUNK_SIZE + 4 + LOGK + LOGL, 2 * CHUNK_NUMBER + 4);
|
||||
} else {
|
||||
carryCheck[i][0] = CheckCarryToZero(CHUNK_SIZE, 3 * CHUNK_SIZE + 4 + LOGK + LOGL, 3 * CHUNK_NUMBER - 2);
|
||||
carryCheck[i][1] = CheckCarryToZero(CHUNK_SIZE, 3 * CHUNK_SIZE + 4 + LOGK + LOGL, 3 * CHUNK_NUMBER - 2);
|
||||
}
|
||||
for (var j = 0; j < CHUNK_NUMBER; j++) {
|
||||
carryCheck[i][0].in[j] <== X0[i][j] + X1[i][j] - Y1[i][j] - Y2[i][j] - Y2[i][j] - pProdReal0[i].out[j] - PROD_REAL[i][1][j];
|
||||
carryCheck[i][1].in[j] <== Y0[i][j] + X1[i][j] + Y1[i][j] + X2[i][j] + X2[i][j] - pProdImag0[i].out[j] - PROD_IMAG[i][1][j];
|
||||
}
|
||||
if (3 * CHUNK_NUMBER - 2 < 2 * CHUNK_NUMBER + 4) {
|
||||
for (var j = CHUNK_NUMBER; j < 3 * CHUNK_NUMBER - 2; j++) {
|
||||
carryCheck[i][0].in[j] <== X0[i][j] + X1[i][j] - Y1[i][j] - Y2[i][j] - Y2[i][j] - pProdReal0[i].out[j] - PROD_REAL[i][1][j];
|
||||
carryCheck[i][1].in[j] <== Y0[i][j] + X1[i][j] + Y1[i][j] + X2[i][j] + X2[i][j] - pProdImag0[i].out[j] - PROD_IMAG[i][1][j];
|
||||
}
|
||||
for (var j = 3 * CHUNK_NUMBER - 2; j < 2 * CHUNK_NUMBER + 4; j++) {
|
||||
carryCheck[i][0].in[j] <== - PROD_REAL[i][1][j];
|
||||
carryCheck[i][1].in[j] <== - PROD_IMAG[i][1][j];
|
||||
}
|
||||
} else {
|
||||
for (var j = CHUNK_NUMBER; j < 2 * CHUNK_NUMBER + 4; j++) {
|
||||
carryCheck[i][0].in[j] <== X0[i][j] + X1[i][j] - Y1[i][j] - Y2[i][j] - Y2[i][j] - pProdReal0[i].out[j] - PROD_REAL[i][1][j];
|
||||
carryCheck[i][1].in[j] <== Y0[i][j] + X1[i][j] + Y1[i][j] + X2[i][j] + X2[i][j] - pProdImag0[i].out[j] - PROD_IMAG[i][1][j];
|
||||
}
|
||||
for (var j = 2 * CHUNK_NUMBER + 4; j < 3 * CHUNK_NUMBER - 2; j++) {
|
||||
carryCheck[i][0].in[j] <== X0[i][j] + X1[i][j] - Y1[i][j] - Y2[i][j] - Y2[i][j] - pProdReal0[i].out[j];
|
||||
carryCheck[i][1].in[j] <== Y0[i][j] + X1[i][j] + Y1[i][j] + X2[i][j] + X2[i][j] - pProdImag0[i].out[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,293 @@
|
||||
pragma circom 2.0.3;
|
||||
|
||||
function get_fp_sgn0(a){
|
||||
return a[0] % 2;
|
||||
}
|
||||
|
||||
// CHUNK_SIZE bits per register
|
||||
// num has CHUNK_NUMBER registers
|
||||
// p has CHUNK_NUMBER registers
|
||||
// CHUNK_NUMBER * CHUNK_SIZE <= 513
|
||||
// p is a prime
|
||||
// if num == 0 mod p, returns 0
|
||||
// else computes inv = num^{ - 1} mod p using extended euclidean algorithm
|
||||
// https://brilliant.org/wiki/extended-euclidean-algorithm/
|
||||
function find_Fp_inverse(CHUNK_SIZE, CHUNK_NUMBER, num, p) {
|
||||
var A_MOD_P[2][150] = long_div2(CHUNK_SIZE, CHUNK_NUMBER, 0, num, p);
|
||||
var a[150];
|
||||
var b[150];
|
||||
var x[150];
|
||||
var y[150];
|
||||
var u[150];
|
||||
var v[150];
|
||||
|
||||
var ret[150];
|
||||
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++){
|
||||
a[i] = A_MOD_P[1][i];
|
||||
b[i] = p[i];
|
||||
x[i] = 0;
|
||||
y[i] = 0;
|
||||
u[i] = 0;
|
||||
v[i] = 0;
|
||||
}
|
||||
y[0] = 1;
|
||||
u[0] = 1;
|
||||
// euclidean algorithm takes log_phi(min(a, p)) iterations, where phi is golden ratio
|
||||
// should be less than 1000 for our cases...
|
||||
for(var l = 0; l < 1000; l++){
|
||||
var k_a = 0;
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
if (a[i] != 0) {
|
||||
k_a = i + 1;
|
||||
}
|
||||
}
|
||||
if (k_a == 0) {
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
ret[i] = x[i];
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
var r[2][150] = long_div2(CHUNK_SIZE, k_a, CHUNK_NUMBER - k_a, b, a);
|
||||
var q[150];
|
||||
for(var i = 0; i < CHUNK_NUMBER - k_a + 1; i++)
|
||||
q[i] = r[0][i];
|
||||
for(var i = CHUNK_NUMBER - k_a + 1; i < CHUNK_NUMBER; i++)
|
||||
q[i] = 0;
|
||||
|
||||
var NEW_U[150] = long_sub_mod(CHUNK_SIZE, CHUNK_NUMBER, x, prod_mod(CHUNK_SIZE, CHUNK_NUMBER, u, q, p), p);
|
||||
var NEW_V[150] = long_sub_mod(CHUNK_SIZE, CHUNK_NUMBER, y, prod_mod(CHUNK_SIZE, CHUNK_NUMBER, v, q, p), p);
|
||||
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++){
|
||||
b[i] = a[i];
|
||||
if(i < k_a)
|
||||
a[i] = r[1][i];
|
||||
else
|
||||
a[i] = 0;
|
||||
x[i] = u[i];
|
||||
y[i] = v[i];
|
||||
u[i] = NEW_U[i];
|
||||
v[i] = NEW_V[i];
|
||||
}
|
||||
}
|
||||
// should never reach here (loop should always return before now)
|
||||
assert(0 == 1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// a[CHUNK_NUMBER] registers can overflow
|
||||
// assume actual value of a < 2^{CHUNK_SIZE*(CHUNK_NUMBER+m)}
|
||||
// p[CHUNK_NUMBER] registers in [0, 2^CHUNK_SIZE)
|
||||
// out[2][CHUNK_NUMBER] solving
|
||||
// a = p * out[0] + out[1] with out[1] in [0,p)
|
||||
// out[0] has m registers in range [-2^CHUNK_SIZE, 2^CHUNK_SIZE)
|
||||
// out[1] has CHUNK_NUMBER registers in range [0, 2^CHUNK_SIZE)
|
||||
function get_signed_Fp_carry_witness(CHUNK_SIZE, CHUNK_NUMBER, m, a, p){
|
||||
var out[2][150];
|
||||
var A_SHORT[151] = signed_long_to_short(CHUNK_SIZE, CHUNK_NUMBER, a);
|
||||
|
||||
/* // commenting out to improve speed
|
||||
// let me make sure everything is in <= CHUNK_NUMBER+m registers
|
||||
for(var j=CHUNK_NUMBER+m; j<150; j++)
|
||||
assert(A_SHORT[j] == 0);
|
||||
*/
|
||||
|
||||
if(A_SHORT[150] == 0){
|
||||
out = long_div2(CHUNK_SIZE, CHUNK_NUMBER, m, A_SHORT, p);
|
||||
} else {
|
||||
var A_POS[150];
|
||||
for(var i = 0; i < CHUNK_NUMBER+m; i++)
|
||||
A_POS[i] = -A_SHORT[i];
|
||||
|
||||
var X[2][150] = long_div2(CHUNK_SIZE, CHUNK_NUMBER, m, A_POS, p);
|
||||
// what if X[1] is 0?
|
||||
var Y_IS_ZERO = 1;
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++){
|
||||
if(X[1][i] != 0)
|
||||
Y_IS_ZERO = 0;
|
||||
}
|
||||
if(Y_IS_ZERO == 1){
|
||||
out[1] = X[1];
|
||||
} else {
|
||||
out[1] = long_sub(CHUNK_SIZE, CHUNK_NUMBER, p, X[1]);
|
||||
|
||||
X[0][0]++;
|
||||
if(X[0][0] >= (1 << CHUNK_SIZE)){
|
||||
for(var i = 0; i < m - 1; i++){
|
||||
var carry = X[0][i] \ (1 << CHUNK_SIZE);
|
||||
X[0][i + 1] += carry;
|
||||
X[0][i] -= carry * (1 << CHUNK_SIZE);
|
||||
}
|
||||
assert(X[0][m - 1] < (1 << CHUNK_SIZE));
|
||||
}
|
||||
}
|
||||
for(var i = 0; i < m; i++)
|
||||
out[0][i] = -X[0][i];
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
// Implements:
|
||||
// calls get_signed_Fp_carry_witness twice
|
||||
// a[2][CHUNK_NUMBER] registers can overflow
|
||||
// assume actual value of each a[i] < (2^CHUNK_SIZE)^{CHUNK_NUMBER+m}
|
||||
// p[CHUNK_NUMBER] registers in [0, 2^CHUNK_SIZE)
|
||||
// out[2][2][CHUNK_NUMBER] solving
|
||||
// a[0] = p * out[0][0] + out[0][1] with out[0][1] in [0,p)
|
||||
// a[1] = p * out[1][0] + out[1][1] with out[1][1] in [0,p)
|
||||
// out[i][0] has m registers in range [-2^CHUNK_SIZE, 2^CHUNK_SIZE)
|
||||
// out[i][1] has CHUNK_NUMBER registers in range [0, 2^CHUNK_SIZE)
|
||||
function get_signed_Fp2_carry_witness(CHUNK_SIZE, CHUNK_NUMBER, m, a, p){
|
||||
var out[2][2][150];
|
||||
|
||||
for(var i = 0; i < 2; i++)
|
||||
out[i] = get_signed_Fp_carry_witness(CHUNK_SIZE, CHUNK_NUMBER, m, a[i], p);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
function get_fp2_sgn0(CHUNK_NUMBER, a){
|
||||
var IS_ZERO = long_is_zero(CHUNK_NUMBER, a[0]);
|
||||
var SGN_0 = a[0][0] % 2;
|
||||
var SGN_1 = a[1][0] % 2;
|
||||
return SGN_0 | (IS_ZERO & SGN_1);
|
||||
}
|
||||
|
||||
// helper function to precompute the product of two elements a, b in Fp2
|
||||
// a[2][CHUNK_NUMBER], b[2][CHUNK_NUMBER] all registers in [0, 2^CHUNK_SIZE)
|
||||
// (a0 + a1 u)*(b0 + b1 u) = (a0*b0 - a1*b1) + (a0*b1 + a1*b0)u
|
||||
// this is a direct computation - totally distinct from the combo of Fp2multiplyNoCarry and get_Fp2_carry_witness
|
||||
function find_Fp2_product(CHUNK_SIZE, CHUNK_NUMBER, a, b, p){
|
||||
var out[2][150];
|
||||
var ab[2][2][150];
|
||||
for(var i = 0; i < 2; i++)for(var j = 0; j < 2; j++){
|
||||
ab[i][j] = prod_mod(CHUNK_SIZE,CHUNK_NUMBER,a[i],b[j],p);
|
||||
}
|
||||
out[0] = long_sub_mod(CHUNK_SIZE,CHUNK_NUMBER,ab[0][0],ab[1][1],p);
|
||||
out[1] = long_add_mod(CHUNK_SIZE,CHUNK_NUMBER,ab[0][1],ab[1][0],p);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
// helper function to precompute the sum of two elements a, b in Fp2
|
||||
// a[2][CHUNK_NUMBER], b[2][CHUNK_NUMBER] all registers in [0, 2^CHUNK_SIZE)
|
||||
// this is a direct computation
|
||||
function find_Fp2_sum(CHUNK_SIZE, CHUNK_NUMBER, a, b, p){
|
||||
var out[2][150];
|
||||
out[0] = long_add_mod(CHUNK_SIZE,CHUNK_NUMBER,a[0],b[0],p);
|
||||
out[1] = long_add_mod(CHUNK_SIZE,CHUNK_NUMBER,a[1],b[1],p);
|
||||
return out;
|
||||
}
|
||||
|
||||
// helper function to precompute the difference of two elements a, b in Fp2
|
||||
// a[2][CHUNK_NUMBER], b[2][CHUNK_NUMBER] all registers in [0, 2^CHUNK_SIZE)
|
||||
// this is a direct computation
|
||||
function find_Fp2_diff(CHUNK_SIZE, CHUNK_NUMBER, a, b, p){
|
||||
var out[2][150];
|
||||
out[0] = long_sub_mod(CHUNK_SIZE,CHUNK_NUMBER,a[0],b[0],p);
|
||||
out[1] = long_sub_mod(CHUNK_SIZE,CHUNK_NUMBER,a[1],b[1],p);
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
// CHUNK_SIZE bits per register
|
||||
// a has 2 x CHUNK_NUMBER registers, elt of Fp2
|
||||
// p has CHUNK_NUMBER registers
|
||||
// e has 2k registers
|
||||
// CHUNK_NUMBER * CHUNK_SIZE <= 400
|
||||
// p is a prime
|
||||
// computes a^e in Fp2
|
||||
function find_Fp2_exp(CHUNK_SIZE, CHUNK_NUMBER, a, p, e){
|
||||
var E_BITS[800]; // length is (2k - 1) * CHUNK_SIZE
|
||||
var BIT_LENGTH;
|
||||
for (var i = 0; i < 2 * CHUNK_NUMBER; i++) {
|
||||
for (var j = 0; j < CHUNK_SIZE; j++) {
|
||||
E_BITS[j + CHUNK_SIZE * i] = (e[i] >> j) & 1;
|
||||
if(E_BITS[j + CHUNK_SIZE * i] == 1)
|
||||
BIT_LENGTH = j + CHUNK_SIZE * i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
var out[2][150]; // length is CHUNK_NUMBER
|
||||
for(var i = 0; i < 150; i++) {
|
||||
out[0][i] = 0;
|
||||
out[1][i] = 0;
|
||||
}
|
||||
out[0][0] = 1;
|
||||
|
||||
// repeated squaring
|
||||
for(var i = BIT_LENGTH - 1; i >= 0; i--) {
|
||||
// multiply by a if bit is 0
|
||||
if (E_BITS[i] == 1)
|
||||
out = find_Fp2_product(CHUNK_SIZE, CHUNK_NUMBER, out, a, p);
|
||||
// square, unless we're at the end
|
||||
if (i > 0)
|
||||
out = find_Fp2_product(CHUNK_SIZE, CHUNK_NUMBER, out, out, p);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function is_equal_Fp2(CHUNK_SIZE, CHUNK_NUMBER, a, b){
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
if(a[i][idx] != b[i][idx]){
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
// a[2][CHUNK_NUMBER] elt in Fp2
|
||||
// output multiplies by XI0 +u
|
||||
// multiplies register bounds by (XI0 + 1)
|
||||
function signed_Fp2_mult_w6(CHUNK_NUMBER, a, XI0){
|
||||
var out[2][150];
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++){
|
||||
out[0][i] = a[0][i] * XI0 - a[1][i];
|
||||
out[1][i] = a[0][i] + a[1][i] * XI0;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
// a is 2 x CHUNK_NUMBER, represents element of Fp^2
|
||||
// out is the inverse of a in Fp^2 (2 x CHUNK_NUMBER array of shorts)
|
||||
|
||||
// Src: https://github.com/paulmillr/noble-bls12-381/blob/23823d664b1767fb20c9c19c5800c66993b576a5/math.ts#L444
|
||||
// We wish to find the multiplicative inverse of a nonzero
|
||||
// element a + bu in Fp2. We leverage an identity
|
||||
//
|
||||
// (a + bu)(a - bu) = a² + b²
|
||||
//
|
||||
// which holds because u² = - 1. This can be rewritten as
|
||||
//
|
||||
// (a + bu)(a - bu)/(a² + b²) = 1
|
||||
//
|
||||
// because a² + b² = 0 has no nonzero solutions for (a, b).
|
||||
// This gives that (a - bu)/(a² + b²) is the inverse
|
||||
// of (a + bu).
|
||||
function find_Fp2_inverse(CHUNK_SIZE, CHUNK_NUMBER, a, p) {
|
||||
var SQ0[150] = prod(CHUNK_SIZE, CHUNK_NUMBER, a[0], a[0]);
|
||||
var SQ1[150] = prod(CHUNK_SIZE, CHUNK_NUMBER, a[1], a[1]);
|
||||
var SQ_SUM[150] = long_add(CHUNK_SIZE, 2 * CHUNK_NUMBER, SQ0, SQ1);
|
||||
var SQ_SUM_DIV[2][150] = long_div2(CHUNK_SIZE, CHUNK_NUMBER, CHUNK_NUMBER + 1, SQ_SUM, p);
|
||||
// LAMBDA = 1/(SQ_SUM)%p
|
||||
var LAMBDA[150] = mod_inv(CHUNK_SIZE, CHUNK_NUMBER, SQ_SUM_DIV[1], p);
|
||||
var OUT0[150] = prod(CHUNK_SIZE, CHUNK_NUMBER, LAMBDA, a[0]);
|
||||
var OUT0_DIV[2][150] = long_div(CHUNK_SIZE, CHUNK_NUMBER, OUT0, p);
|
||||
var out[2][150];
|
||||
out[0] = OUT0_DIV[1];
|
||||
|
||||
var OUT1_PRE[150] = long_sub(CHUNK_SIZE, CHUNK_NUMBER, p, a[1]);
|
||||
var OUT1[150] = prod(CHUNK_SIZE, CHUNK_NUMBER, LAMBDA, OUT1_PRE);
|
||||
var OUT1_DIV[2][150] = long_div(CHUNK_SIZE, CHUNK_NUMBER, OUT1, p);
|
||||
out[1] = OUT1_DIV[1];
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,761 @@
|
||||
pragma circom 2.0.3;
|
||||
|
||||
include "../../../bigInt/bigInt.circom";
|
||||
include "../../../bigInt/bigIntFunc.circom";
|
||||
include "fieldElementsFunc.circom";
|
||||
include "fp2.circom";
|
||||
include "fp12.circom";
|
||||
|
||||
// assume input is an element of Fp12 in the cyclotomic subgroup GΦ₁₂
|
||||
// A cyclotomic group is a subgroup of Fp^CHUNK_SIZE defined by
|
||||
// GΦₙ(p) = {α ∈ Fpⁿ : α^{Φₙ(p)} = 1}
|
||||
|
||||
// below we implement compression and decompression for an element GΦ₁₂ following Theorem 3.1 of https://eprint.iacr.org/2010/542.pdf
|
||||
// Fp4 = Fp2(w^3) where (w^3)^2 = 1 + u
|
||||
// Fp12 = Fp4(w) where w^3 = w^3
|
||||
|
||||
// in = g0 + g2 w + g4 w^2 + g1 w^3 + g3 w^4 + g5 w^5 where g_i are elements of Fp2
|
||||
// out = Compress(in) = [ g2, g3, g4, g5 ]
|
||||
template Fp12CyclotomicCompress(CHUNK_SIZE, CHUNK_NUMBER) {
|
||||
signal input in[6][2][CHUNK_NUMBER];
|
||||
signal output out[4][2][CHUNK_NUMBER];
|
||||
|
||||
for(var eps = 0; eps < 2; eps++){
|
||||
for(var j = 0; j < CHUNK_NUMBER; j++){
|
||||
out[0][eps][j] <== in[1][eps][j];
|
||||
out[1][eps][j] <== in[4][eps][j];
|
||||
out[2][eps][j] <== in[2][eps][j];
|
||||
out[3][eps][j] <== in[5][eps][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// in = [g2, g3, g4, g5] where g_i are elements of Fp2
|
||||
// out[6][2][CHUNK_NUMBER] each register in [0, 2^CHUNK_SIZE)
|
||||
// out = Decompress(in) = g0 + g2 w + g4 w^2 + g1 w^3 + g3 w^4 + g5 w^5 where
|
||||
// if g2 != 0:
|
||||
// g1 = (g5^2 * (1 + u) + 3 g4^2 - 2 g3)/(4g2)
|
||||
// g0 = (2 g1^2 + g2 * g5 - 3 g3*g4) * (1 + u) + 1
|
||||
// if g2 = 0:
|
||||
// g1 = (2 g4 * g5)/g3
|
||||
// g0 = (2 g1^2 - 3 g3 * g4) * (1 + u) + 1 NOTE g0 IS QUARTIC IN THE INPUTS, so I don't think it's worth trying to compute a NoCarry version of g0
|
||||
// out0 = g0, out1 = g2, out2 = g4, out3 = g1, out4 = g3, out5 = g5
|
||||
template Fp12CyclotomicDecompress(CHUNK_SIZE, CHUNK_NUMBER, p) {
|
||||
signal input in[4][2][CHUNK_NUMBER];
|
||||
signal output out[6][2][CHUNK_NUMBER];
|
||||
|
||||
var XI0 = 1;
|
||||
var LOGK = log_ceil(CHUNK_NUMBER);
|
||||
var LOGK2 = log_ceil(((1 + XI0) * 2 * CHUNK_NUMBER + 6 * CHUNK_NUMBER) * CHUNK_NUMBER + 1);
|
||||
var LOGK3 = log_ceil((1 + XI0) * 6 * CHUNK_NUMBER * CHUNK_NUMBER + 1);
|
||||
assert(3 * CHUNK_SIZE + LOGK2 < 250);
|
||||
|
||||
var len = 2 * CHUNK_NUMBER - 1; // number of registers in output of Fp2MultiplyNoCarry
|
||||
// len = CHUNK_NUMBER if using Fp2MultiplyNoCarryCompress
|
||||
|
||||
// g2 = in[0], g3 = in[1], g4 = in[2], g5 = in[3]
|
||||
for(var eps = 0; eps < 2; eps++){
|
||||
for(var j = 0; j < CHUNK_NUMBER; j++){
|
||||
out[1][eps][j] <== in[0][eps][j];
|
||||
out[2][eps][j] <== in[2][eps][j];
|
||||
out[4][eps][j] <== in[1][eps][j];
|
||||
out[5][eps][j] <== in[3][eps][j];
|
||||
}
|
||||
}
|
||||
|
||||
// detect if g2 is 0
|
||||
component g2IsZero = Fp2IsZero(CHUNK_SIZE, CHUNK_NUMBER, p);
|
||||
for(var eps = 0; eps < 2; eps++){
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++){
|
||||
g2IsZero.in[eps][i] <== in[0][eps][i];
|
||||
}
|
||||
}
|
||||
|
||||
// COMPUTATION OF g1 when g2 != 0:
|
||||
component g5SQ = SignedFp2MultiplyNoCarry(CHUNK_SIZE, CHUNK_NUMBER, 2 * CHUNK_SIZE + 1 + LOGK); // overflow < 2k * 2^{2n}
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++){
|
||||
for(var eps = 0; eps < 2; eps++){
|
||||
g5SQ.a[eps][i] <== in[3][eps][i];
|
||||
g5SQ.b[eps][i] <== in[3][eps][i];
|
||||
}
|
||||
}
|
||||
// c = XI0 + u
|
||||
// g5^2 * c
|
||||
var G_5_SQC[4][150] = signed_Fp2_mult_w6(len, g5SQ.out, XI0); // overflow < (1 + XI0)*2k * 2^{2n}
|
||||
// 3 g4^2
|
||||
component g4Sq3 = SignedFp2MultiplyNoCarry(CHUNK_SIZE, CHUNK_NUMBER, 2 * CHUNK_SIZE + 3 + LOGK); // overflow 6k * 2^{2n}
|
||||
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++){
|
||||
for(var eps = 0; eps < 2; eps++){
|
||||
g4Sq3.a[eps][i] <== 3 * in[2][eps][i];
|
||||
g4Sq3.b[eps][i] <== in[2][eps][i];
|
||||
}
|
||||
}
|
||||
signal g1Num[2][len]; // g5^2 * (XI0 + u) + 3 g4^2 - 2 g3
|
||||
// overflow ((1 + XI0)*2k + 6k + 2/2^CHUNK_SIZE) * 2^{2n}
|
||||
for(var i = 0; i < len; i++){
|
||||
for(var eps = 0; eps < 2; eps++){
|
||||
if(i < CHUNK_NUMBER){
|
||||
g1Num[eps][i] <== G_5_SQC[eps][i] + g4Sq3.out[eps][i] - 2 * in[1][eps][i];
|
||||
} else {
|
||||
g1Num[eps][i] <== G_5_SQC[eps][i] + g4Sq3.out[eps][i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component g1NumRed = Fp2Compress(CHUNK_SIZE, CHUNK_NUMBER, CHUNK_NUMBER - 1, p, 3 * CHUNK_SIZE + LOGK2);
|
||||
// overflow < ((1 + XI0)*2k + 6k + 2/2^CHUNK_SIZE) * CHUNK_NUMBER * 2^{3n}
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var j = 0; j < 2 * CHUNK_NUMBER - 1; j++){
|
||||
g1NumRed.in[i][j] <== g1Num[i][j];
|
||||
}
|
||||
}
|
||||
// compute g1NumRed / 4g2
|
||||
component g1_1 = SignedFp2Divide(CHUNK_SIZE, CHUNK_NUMBER, 3 * CHUNK_SIZE + LOGK2, CHUNK_SIZE + 2, p);
|
||||
for(var j = 0; j < CHUNK_NUMBER; j++){
|
||||
for(var i = 0; i < 2; i++) {
|
||||
g1_1.a[i][j] <== g1NumRed.out[i][j];
|
||||
}
|
||||
for(var i = 0; i < 2; i++){
|
||||
g1_1.b[i][j] <== 4*in[0][i][j];
|
||||
}
|
||||
}
|
||||
|
||||
// END OF COMPUTATION OF g1 when g2 != 0:
|
||||
|
||||
|
||||
|
||||
// COMPUTATION OF g1 when g2 = 0:
|
||||
// g1 = 2 * g4*g5 / g3
|
||||
component twoG4G5 = SignedFp2MultiplyNoCarryCompress(CHUNK_SIZE, CHUNK_NUMBER, p, CHUNK_SIZE + 1,3 * CHUNK_SIZE + 2 + 2 * LOGK); // overflow 4k^2 * 2^{3n}
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++){
|
||||
for(var eps = 0; eps < 2; eps++){
|
||||
twoG4G5.a[eps][i] <== 2 * in[2][eps][i];
|
||||
twoG4G5.b[eps][i] <== in[3][eps][i];
|
||||
}
|
||||
}
|
||||
component g1_0 = SignedFp2Divide(CHUNK_SIZE, CHUNK_NUMBER, 3 * CHUNK_SIZE + 2 + 2 * LOGK, CHUNK_SIZE, p);
|
||||
for(var j = 0; j < CHUNK_NUMBER; j++){
|
||||
for(var i = 0; i < 2; i++)
|
||||
g1_0.a[i][j] <== twoG4G5.out[i][j];
|
||||
for(var i = 0; i < 2; i++){
|
||||
g1_0.b[i][j] <== in[1][i][j];
|
||||
}
|
||||
}
|
||||
// END OF COMPUTATION OF g1 when g2 = 0.
|
||||
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++){
|
||||
for(var eps = 0; eps < 2; eps++){
|
||||
out[3][eps][i] <== g1_1.out[eps][i] + g2IsZero.out * (g1_0.out[eps][i] - g1_1.out[eps][i]);
|
||||
}
|
||||
}
|
||||
|
||||
// COMPUTATION OF g0 when g2 != 0:
|
||||
// g0 = (2 g1^2 + g2 g5 - 3 g3 g4)(1 + u) + 1
|
||||
component twoG1Sq= SignedFp2MultiplyNoCarry(CHUNK_SIZE, CHUNK_NUMBER, 2 * CHUNK_SIZE + 2 + LOGK); // overflow 4 * CHUNK_NUMBER * 2^{2n}
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++){
|
||||
for(var eps = 0; eps < 2; eps++){
|
||||
twoG1Sq.a[eps][i] <== 2 * g1_1.out[eps][i];
|
||||
twoG1Sq.b[eps][i] <== g1_1.out[eps][i];
|
||||
}
|
||||
}
|
||||
|
||||
component g2G5 = SignedFp2MultiplyNoCarry(CHUNK_SIZE, CHUNK_NUMBER, 2 * CHUNK_SIZE + 1 + LOGK); // overflow 2k * 2^{2n}
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++){
|
||||
for(var eps = 0; eps < 2; eps++){
|
||||
g2G5.a[eps][i] <== in[0][eps][i];
|
||||
g2G5.b[eps][i] <== in[3][eps][i];
|
||||
}
|
||||
}
|
||||
|
||||
component threeG3G4 = SignedFp2MultiplyNoCarry(CHUNK_SIZE, CHUNK_NUMBER, 2 * CHUNK_SIZE + 3 + LOGK); // overflow 6 * CHUNK_NUMBER * 2^{2n}
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++){
|
||||
for(var eps = 0; eps < 2; eps++){
|
||||
threeG3G4.a[eps][i] <== 3 * in[1][eps][i];
|
||||
threeG3G4.b[eps][i] <== in[2][eps][i];
|
||||
}
|
||||
}
|
||||
// 2 g1^2 + g2 g5 - 3 g3 g4
|
||||
var TEMP[2][len];
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var j = 0; j < len; j++){
|
||||
TEMP[i][j] = twoG1Sq.out[i][j] + g2G5.out[i][j] - threeG3G4.out[i][j];
|
||||
}
|
||||
}
|
||||
|
||||
// (2 g1^2 + g2 g5 - 3 g3 g4)(1 + u)
|
||||
var TEMP_C[2][150] = signed_Fp2_mult_w6(len, TEMP, XI0); // overflow abs val < (1 + XI0)*6k * 2^{2n}
|
||||
// (2 g1^2 + g2 g5 - 3 g3 g4)(1 + u) + 1
|
||||
TEMP_C[0][0]++;
|
||||
component compress01 = Fp2Compress(CHUNK_SIZE, CHUNK_NUMBER, CHUNK_NUMBER - 1, p, 3 * CHUNK_SIZE + LOGK3);
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var j = 0; j < 2 * CHUNK_NUMBER - 1; j++){
|
||||
compress01.in[i][j] <== TEMP_C[i][j];
|
||||
}
|
||||
}
|
||||
component carryMod01 = SignedFp2CarryModP(CHUNK_SIZE, CHUNK_NUMBER, 3 * CHUNK_SIZE + LOGK3, p);
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var j = 0; j < CHUNK_NUMBER; j++){
|
||||
carryMod01.in[i][j] <== compress01.out[i][j];
|
||||
}
|
||||
}
|
||||
signal g0_1[2][CHUNK_NUMBER];
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var j = 0; j < CHUNK_NUMBER; j++){
|
||||
g0_1[i][j] <== carryMod01.out[i][j];
|
||||
}
|
||||
}
|
||||
// END OF COMPUTATION OF g0 when g2 != 0.
|
||||
|
||||
// COMPUTATION OF g0 when g2 = 0:
|
||||
// g0 = (2g1^2 - 3g3g4)(1 + u) + 1
|
||||
component twoG1_0Sq= SignedFp2MultiplyNoCarry(CHUNK_SIZE, CHUNK_NUMBER, 2 * CHUNK_SIZE + 2 + LOGK); // overflow 4k * 2^{2n}
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++){
|
||||
for(var eps = 0; eps < 2; eps++){
|
||||
twoG1_0Sq.a[eps][i] <== 2 * g1_0.out[eps][i];
|
||||
twoG1_0Sq.b[eps][i] <== g1_0.out[eps][i];
|
||||
}
|
||||
}
|
||||
// can reuse threeG3G4 !
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var j = 0; j < len; j++){
|
||||
TEMP[i][j] = twoG1_0Sq.out[i][j] - threeG3G4.out[i][j];
|
||||
}
|
||||
}
|
||||
// overflow 6 * CHUNK_NUMBER * 2^{2n}
|
||||
TEMP_C = signed_Fp2_mult_w6(len, TEMP, XI0);
|
||||
TEMP_C[0][0]++;
|
||||
component compress0_0 = Fp2Compress(CHUNK_SIZE, CHUNK_NUMBER, CHUNK_NUMBER - 1, p, 3 * CHUNK_SIZE + LOGK3);
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var j = 0; j < 2 * CHUNK_NUMBER - 1; j++){
|
||||
compress0_0.in[i][j] <== TEMP_C[i][j];
|
||||
}
|
||||
}
|
||||
component carryMod0_0 = SignedFp2CarryModP(CHUNK_SIZE, CHUNK_NUMBER, 3 * CHUNK_SIZE + LOGK3, p);
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var j = 0; j < CHUNK_NUMBER; j++){
|
||||
carryMod0_0.in[i][j] <== compress0_0.out[i][j];
|
||||
}
|
||||
}
|
||||
signal g0_0[2][CHUNK_NUMBER];
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var j = 0; j < CHUNK_NUMBER; j++){
|
||||
g0_0[i][j] <== carryMod0_0.out[i][j];
|
||||
}
|
||||
}
|
||||
// END OF COMPUTATION OF g0 when g2 = 0.
|
||||
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++){
|
||||
for(var eps = 0; eps < 2; eps++){
|
||||
out[0][eps][i] <== g0_1[eps][i] + g2IsZero.out * (g0_0[eps][i] - g0_1[eps][i]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// input is [g2, g3, g4, g5] = C(g) in compressed format of Fp12cyclotomicCompress
|
||||
// input[i][2] keeps track of positive and negatives
|
||||
// don't assume anything about overflow in registers in case we want to square twice before carry
|
||||
// output is C(g^2) = [h2, h3, h4, h5] computed using Theorem 3.2 of https://eprint.iacr.org/2010/542.pdf
|
||||
// c = XI0 + u
|
||||
// h2 = 2(g2 + 3 * c*B_45)
|
||||
// h3 = 3(A_45 - (c + 1)B_45) - 2g3
|
||||
// h4 = 3(A_23 - (c + 1)B_23) - 2g4
|
||||
// h5 = 2(g5 + 3B_23)
|
||||
// A_ij = (g_i + g_j)(g_i + c g_j)
|
||||
// B_ij = g_i g_j
|
||||
|
||||
// everything computed with no carries
|
||||
// If registers of in[] are in [0, B), then registers of out[] have abs val < (18(XI0 + 2)CHUNK_NUMBER + 2/B)* B^2
|
||||
template SignedFp12CyclotomicSquareNoCarry(CHUNK_SIZE, CHUNK_NUMBER) {
|
||||
signal input in[4][2][CHUNK_NUMBER];
|
||||
signal output out[4][2][2 * CHUNK_NUMBER - 1];
|
||||
|
||||
var XI0 = 1;
|
||||
var LOGK = log_ceil(CHUNK_NUMBER);
|
||||
|
||||
component B23 = SignedFp2MultiplyNoCarry(CHUNK_SIZE, CHUNK_NUMBER, 2 * CHUNK_SIZE + 1 + LOGK); // overflow in 2 * CHUNK_NUMBER * 2^{2N}
|
||||
component B45 = SignedFp2MultiplyNoCarry(CHUNK_SIZE, CHUNK_NUMBER, 2 * CHUNK_SIZE + 1 + LOGK);
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var j = 0; j < CHUNK_NUMBER; j++){
|
||||
B23.a[i][j] <== in[0][i][j];
|
||||
B23.b[i][j] <== in[1][i][j];
|
||||
|
||||
B45.a[i][j] <== in[2][i][j];
|
||||
B45.b[i][j] <== in[3][i][j];
|
||||
}
|
||||
}
|
||||
|
||||
component A23 = SignedFp2MultiplyNoCarry(CHUNK_SIZE, CHUNK_NUMBER, 2 * CHUNK_SIZE + 5 + LOGK); // overflow in 4*(2 + XI0) * CHUNK_NUMBER * 2^{2N}
|
||||
component A45 = SignedFp2MultiplyNoCarry(CHUNK_SIZE, CHUNK_NUMBER, 2 * CHUNK_SIZE + 5 + LOGK);
|
||||
// c*g3 = (1 + u)*g3
|
||||
var cg3[2][150] = signed_Fp2_mult_w6(CHUNK_NUMBER, in[1], XI0);
|
||||
var cg5[2][150] = signed_Fp2_mult_w6(CHUNK_NUMBER, in[3], XI0);
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var j = 0; j < CHUNK_NUMBER; j++){
|
||||
A23.a[i][j] <== in[0][i][j] + in[1][i][j]; // 2 * 2^{N}
|
||||
A23.b[i][j] <== in[0][i][j] + cg3[i][j]; // (2 + XI0) * 2^{N}
|
||||
|
||||
A45.a[i][j] <== in[2][i][j] + in[3][i][j];
|
||||
A45.b[i][j] <== in[2][i][j] + cg5[i][j];
|
||||
}
|
||||
}
|
||||
|
||||
var cB45[2][150] = signed_Fp2_mult_w6(2 * CHUNK_NUMBER - 1, B45.out, XI0); // (1 + XI0)*2k * 2^{2N}
|
||||
var cB23[2][150] = signed_Fp2_mult_w6(2 * CHUNK_NUMBER - 1, B23.out, XI0);
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var j = 0; j < 2 * CHUNK_NUMBER - 1; j++){
|
||||
if(j < CHUNK_NUMBER){
|
||||
out[0][i][j] <== 2 * (in[0][i][j] + 3 * cB45[i][j]); // (2 + 6 * (1 + XI0)CHUNK_NUMBER) * 2^{2N}
|
||||
out[3][i][j] <== 2 * (in[3][i][j] + 3 * B23.out[i][j]); // (2 + 6 * (1 + XI0)CHUNK_NUMBER) * 2^{2N}
|
||||
out[1][i][j] <== 3 * (A45.out[i][j] - cB45[i][j] - B45.out[i][j]) - 2 * in[1][i][j];
|
||||
// 3 * 4*(2 + XI0) * CHUNK_NUMBER * 2^{2N}
|
||||
out[2][i][j] <== 3 * (A23.out[i][j] - cB23[i][j] - B23.out[i][j]) - 2 * in[2][i][j];
|
||||
} else {
|
||||
out[0][i][j] <== 2 * (3 * cB45[i][j]);
|
||||
out[3][i][j] <== 2 * (3 * B23.out[i][j]);
|
||||
out[1][i][j] <== 3 * (A45.out[i][j] - cB45[i][j] - B45.out[i][j]);
|
||||
out[2][i][j] <== 3 * (A23.out[i][j] - cB23[i][j] - B23.out[i][j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// input is [g2, g3, g4, g5] = C(g) is output of Fp12cyclotomicCompress
|
||||
// assume in[4][2][CHUNK_NUMBER] has registers in [0, 2^CHUNK_SIZE) with in[i][j] in [0, p)
|
||||
// output is C(g^2)
|
||||
// out[4][2][CHUNK_NUMBER] has registers in [0, 2^CHUNK_SIZE) with out[i][j] in [0, p)
|
||||
template Fp12CyclotomicSquare(CHUNK_SIZE, CHUNK_NUMBER, p) {
|
||||
signal input in[4][2][CHUNK_NUMBER];
|
||||
signal output out[4][2][CHUNK_NUMBER];
|
||||
|
||||
var XI0 = 1;
|
||||
var LOGK2 = log_ceil(18 * (2 + XI0) * CHUNK_NUMBER * CHUNK_NUMBER + 1);
|
||||
assert(3 * CHUNK_SIZE + LOGK2 < 251);
|
||||
|
||||
component sq = SignedFp12CyclotomicSquareNoCarry(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
for(var i = 0; i < 4; i++){
|
||||
for(var eps = 0; eps < 2; eps++){
|
||||
for(var j = 0; j < CHUNK_NUMBER; j++){
|
||||
sq.in[i][eps][j] <== in[i][eps][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
component sqRed[4];
|
||||
component sqMod[4];
|
||||
for(var i = 0; i < 4; i++){
|
||||
sqRed[i] = Fp2Compress(CHUNK_SIZE, CHUNK_NUMBER, CHUNK_NUMBER - 1, p, 3 * CHUNK_SIZE + LOGK2);
|
||||
for(var eps = 0; eps < 2; eps++)
|
||||
{
|
||||
for(var j = 0; j < 2 * CHUNK_NUMBER - 1; j++){
|
||||
sqRed[i].in[eps][j] <== sq.out[i][eps][j];
|
||||
}
|
||||
}
|
||||
sqMod[i] = SignedFp2CarryModP(CHUNK_SIZE, CHUNK_NUMBER, 3 * CHUNK_SIZE + LOGK2, p);
|
||||
for(var eps = 0; eps < 2; eps++){
|
||||
for(var j = 0; j < CHUNK_NUMBER; j++){
|
||||
sqMod[i].in[eps][j] <== sqRed[i].out[eps][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
for(var i = 0; i < 4; i++){
|
||||
for(var eps = 0; eps < 2; eps++){
|
||||
for(var j = 0; j < CHUNK_NUMBER; j++){
|
||||
out[i][eps][j] <== sqMod[i].out[eps][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// assume input is an element of Fp12 in the cyclotomic subgroup GΦ12
|
||||
// output is input raised to the e-th power
|
||||
// use the square and multiply method
|
||||
// assume 0 < e < 2^254
|
||||
template Fp12CyclotomicExp(CHUNK_SIZE, CHUNK_NUMBER, e, p) {
|
||||
assert(e > 0);
|
||||
|
||||
signal input in[6][2][CHUNK_NUMBER];
|
||||
signal output out[6][2][CHUNK_NUMBER];
|
||||
|
||||
var TEMP = e;
|
||||
// get bitlength of e
|
||||
var BITLENGTH;
|
||||
for(var i = 0; i < 254; i++){
|
||||
if(TEMP != 0){
|
||||
BITLENGTH = i;
|
||||
}
|
||||
TEMP = TEMP>>1;
|
||||
}
|
||||
BITLENGTH++;
|
||||
|
||||
// Compress in[]
|
||||
component cIn = Fp12CyclotomicCompress(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
for(var i = 0; i < 6; i++){
|
||||
for(var eps = 0; eps < 2; eps++){
|
||||
for(var j = 0; j < CHUNK_NUMBER; j++){
|
||||
cIn.in[i][eps][j] <== in[i][eps][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component pow2[BITLENGTH]; // pow2[i] = C(in^{2^i})
|
||||
component dPow2[BITLENGTH];
|
||||
component mult[BITLENGTH];
|
||||
|
||||
signal first[6][2][CHUNK_NUMBER];
|
||||
var CUR_ID = 0; // tracks current index in mult[]
|
||||
|
||||
for(var i = 0; i < BITLENGTH; i++){
|
||||
// compute pow2[i] = pow2[i - 1]* * 2
|
||||
if(i > 0){ // pow2[0] is never defined since there is no squaring involved
|
||||
pow2[i] = Fp12CyclotomicSquare(CHUNK_SIZE, CHUNK_NUMBER, p);
|
||||
if(i == 1){
|
||||
for(var id = 0; id < 4; id++){
|
||||
for(var eps = 0; eps < 2; eps++){
|
||||
for(var j = 0; j < CHUNK_NUMBER; j++){
|
||||
pow2[i].in[id][eps][j] <== cIn.out[id][eps][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for(var id = 0; id < 4; id++){
|
||||
for(var eps = 0; eps < 2; eps++){
|
||||
for(var j = 0; j < CHUNK_NUMBER; j++){
|
||||
pow2[i].in[id][eps][j] <== pow2[i - 1].out[id][eps][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(((e >> i) & 1) == 1){
|
||||
// decompress pow2[i] so we can use it
|
||||
if(i > 0){
|
||||
dPow2[CUR_ID] = Fp12CyclotomicDecompress(CHUNK_SIZE, CHUNK_NUMBER, p);
|
||||
for(var id = 0; id < 4; id++){
|
||||
for(var eps = 0; eps < 2; eps++){
|
||||
for(var j = 0; j < CHUNK_NUMBER; j++){
|
||||
dPow2[CUR_ID].in[id][eps][j] <== pow2[i].out[id][eps][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(CUR_ID == 0){ // this is the least significant bit
|
||||
if(i == 0){
|
||||
for(var id = 0; id < 6; id++){
|
||||
for(var eps = 0; eps < 2; eps++){
|
||||
for(var j = 0; j < CHUNK_NUMBER; j++){
|
||||
first[id][eps][j] <== in[id][eps][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for(var id = 0; id < 6; id++){
|
||||
for(var eps = 0; eps < 2; eps++){
|
||||
for(var j = 0; j < CHUNK_NUMBER; j++){
|
||||
first[id][eps][j] <== dPow2[CUR_ID].out[id][eps][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// multiply what we already have with pow2[i]
|
||||
mult[CUR_ID] = Fp12Multiply(CHUNK_SIZE, CHUNK_NUMBER, p);
|
||||
for(var id = 0; id < 6; id++){
|
||||
for(var eps = 0; eps < 2; eps++){
|
||||
for(var j = 0; j < CHUNK_NUMBER; j++){
|
||||
mult[CUR_ID].a[id][eps][j] <== dPow2[CUR_ID].out[id][eps][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
if(CUR_ID == 1){
|
||||
for(var id = 0; id < 6; id++){
|
||||
for(var eps = 0; eps < 2; eps++){
|
||||
for(var j = 0; j < CHUNK_NUMBER; j++){
|
||||
mult[CUR_ID].b[id][eps][j] <== first[id][eps][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for(var id = 0; id < 6; id++){
|
||||
for(var eps = 0; eps < 2; eps++){
|
||||
for(var j = 0; j < CHUNK_NUMBER; j++){
|
||||
mult[CUR_ID].b[id][eps][j] <== mult[CUR_ID - 1].out[id][eps][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
CUR_ID++;
|
||||
}
|
||||
}
|
||||
CUR_ID--;
|
||||
if(CUR_ID == 0){
|
||||
for(var id = 0; id < 6; id++){
|
||||
for(var eps = 0; eps < 2; eps++){
|
||||
for(var j = 0; j < CHUNK_NUMBER; j++){
|
||||
out[id][eps][j] <== first[id][eps][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for(var id = 0; id < 6; id++){
|
||||
for(var eps = 0; eps < 2; eps++){
|
||||
for(var j = 0; j < CHUNK_NUMBER; j++){
|
||||
out[id][eps][j] <== mult[CUR_ID].out[id][eps][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// hard part of final exponentiation
|
||||
// use equation at top of p.14 from https://eprint.iacr.org/2020/875.pdf
|
||||
template FinalExpHardPart(CHUNK_SIZE, CHUNK_NUMBER, p){
|
||||
signal input in[6][2][CHUNK_NUMBER];
|
||||
signal output out[6][2][CHUNK_NUMBER];
|
||||
|
||||
var x = get_BLS12_381_parameter(); // absolute value of parameter for BLS12-381
|
||||
|
||||
// in^{(x + 1)/3}
|
||||
component pow1 = Fp12CyclotomicExp(CHUNK_SIZE, CHUNK_NUMBER, (x + 1) \ 3, p);
|
||||
for(var id = 0; id < 6; id++){
|
||||
for(var eps = 0; eps < 2; eps++){
|
||||
for(var j = 0; j < CHUNK_NUMBER; j++){
|
||||
pow1.in[id][eps][j] <== in[id][eps][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// in^{(x + 1)/3 * (x + 1)}
|
||||
component pow2 = Fp12CyclotomicExp(CHUNK_SIZE, CHUNK_NUMBER, x + 1, p);
|
||||
for(var id = 0; id < 6; id++){
|
||||
for(var eps = 0; eps < 2; eps++){
|
||||
for(var j = 0; j < CHUNK_NUMBER; j++){
|
||||
pow2.in[id][eps][j] <== pow1.out[id][eps][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// in^{(x + 1)^2/3 * - 1} = pow2^ - 1 inverse = frob(6) in cyclotomic subgroup
|
||||
component pow3 = Fp12FrobeniusMap(CHUNK_SIZE, CHUNK_NUMBER, 6);
|
||||
for(var id = 0; id < 6; id++){
|
||||
for(var eps = 0; eps < 2; eps++){
|
||||
for(var j = 0; j < CHUNK_NUMBER; j++){
|
||||
pow3.in[id][eps][j] <== pow2.out[id][eps][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// in^{(x + 1)^2/3 * -x} = pow3^x
|
||||
component pow4 = Fp12CyclotomicExp(CHUNK_SIZE, CHUNK_NUMBER, x, p);
|
||||
for(var id = 0; id < 6; id++){
|
||||
for(var eps = 0; eps < 2; eps++){
|
||||
for(var j = 0; j < CHUNK_NUMBER; j++){
|
||||
pow4.in[id][eps][j] <== pow3.out[id][eps][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// in^{(x + 1)^2/3 * p} = pow2^p
|
||||
component pow5 = Fp12FrobeniusMap(CHUNK_SIZE, CHUNK_NUMBER, 1);
|
||||
for(var id = 0; id < 6; id++){
|
||||
for(var eps = 0; eps < 2; eps++){
|
||||
for(var j = 0; j < CHUNK_NUMBER; j++){
|
||||
pow5.in[id][eps][j] <== pow2.out[id][eps][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// in^{(x + 1)^2/3 * (-x+p)} = pow4 * pow5
|
||||
component pow6 = Fp12Multiply(CHUNK_SIZE, CHUNK_NUMBER, p);
|
||||
for(var id = 0; id < 6; id++){
|
||||
for(var eps = 0; eps < 2; eps++){
|
||||
for(var j = 0; j < CHUNK_NUMBER; j++){
|
||||
pow6.a[id][eps][j] <== pow4.out[id][eps][j];
|
||||
pow6.b[id][eps][j] <== pow5.out[id][eps][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// in^{(x + 1)^2/3 * (-x+p) * x} = pow6^x
|
||||
component pow7 = Fp12CyclotomicExp(CHUNK_SIZE, CHUNK_NUMBER, x, p);
|
||||
for(var id = 0; id < 6; id++){
|
||||
for(var eps = 0; eps < 2; eps++){
|
||||
for(var j = 0; j < CHUNK_NUMBER; j++){
|
||||
pow7.in[id][eps][j] <== pow6.out[id][eps][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// in^{(x + 1)^2/3 * (-x+p) * x^2} = pow7^x
|
||||
component pow8 = Fp12CyclotomicExp(CHUNK_SIZE, CHUNK_NUMBER, x, p);
|
||||
for(var id = 0; id < 6; id++){
|
||||
for(var eps = 0; eps < 2; eps++){
|
||||
for(var j = 0; j < CHUNK_NUMBER; j++){
|
||||
pow8.in[id][eps][j] <== pow7.out[id][eps][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// in^{(x + 1)^2/3 * (-x+p) * q^2} = pow6^{q^2}
|
||||
component pow9 = Fp12FrobeniusMap(CHUNK_SIZE, CHUNK_NUMBER, 2);
|
||||
for(var id = 0; id < 6; id++){
|
||||
for(var eps = 0; eps < 2; eps++){
|
||||
for(var j = 0; j < CHUNK_NUMBER; j++){
|
||||
pow9.in[id][eps][j] <== pow6.out[id][eps][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// in^{(x + 1)^2/3 * (-x+p) * - 1} = pow6^{ - 1} = pow6^{q^6}
|
||||
component pow10 = Fp12FrobeniusMap(CHUNK_SIZE, CHUNK_NUMBER, 6);
|
||||
for(var id = 0; id < 6; id++){
|
||||
for(var eps = 0; eps < 2; eps++){
|
||||
for(var j = 0; j < CHUNK_NUMBER; j++){
|
||||
pow10.in[id][eps][j] <== pow6.out[id][eps][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// in^{(x + 1)^2/3 * (-x+p) * (x^2 + q^2)} = pow8 * pow9
|
||||
component pow11 = Fp12Multiply(CHUNK_SIZE, CHUNK_NUMBER, p);
|
||||
for(var id = 0; id < 6; id++){
|
||||
for(var eps = 0; eps < 2; eps++){
|
||||
for(var j = 0; j < CHUNK_NUMBER; j++){
|
||||
pow11.a[id][eps][j] <== pow8.out[id][eps][j];
|
||||
pow11.b[id][eps][j] <== pow9.out[id][eps][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// in^{(x + 1)^2/3 * (-x+p) * (x^2 + q^2 - 1)} = pow10 * pow11
|
||||
component pow12 = Fp12Multiply(CHUNK_SIZE, CHUNK_NUMBER, p);
|
||||
for(var id = 0; id < 6; id++){
|
||||
for(var eps = 0; eps < 2; eps++){
|
||||
for(var j = 0; j < CHUNK_NUMBER; j++){
|
||||
pow12.a[id][eps][j] <== pow10.out[id][eps][j];
|
||||
pow12.b[id][eps][j] <== pow11.out[id][eps][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// final answer
|
||||
// in^{(x + 1)^2/3 * (-x+p) * (x^2 + q^2 - 1) + 1} = pow12 * in
|
||||
component pow13 = Fp12Multiply(CHUNK_SIZE, CHUNK_NUMBER, p);
|
||||
for(var id = 0; id < 6; id++){
|
||||
for(var eps = 0; eps < 2; eps++){
|
||||
for(var j = 0; j < CHUNK_NUMBER; j++){
|
||||
pow13.a[id][eps][j] <== pow12.out[id][eps][j];
|
||||
pow13.b[id][eps][j] <== in[id][eps][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(var id = 0; id < 6; id++){
|
||||
for(var eps = 0; eps < 2; eps++){
|
||||
for(var j = 0; j < CHUNK_NUMBER; j++){
|
||||
out[id][eps][j] <== pow13.out[id][eps][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// easy part of final exponentiation
|
||||
// out = in^{ (q^6 - 1)*(q^2 + 1) }
|
||||
template FinalExpEasyPart(CHUNK_SIZE, CHUNK_NUMBER, p){
|
||||
signal input in[6][2][CHUNK_NUMBER];
|
||||
signal output out[6][2][CHUNK_NUMBER];
|
||||
|
||||
// in^{q^6}
|
||||
component f1 = Fp12FrobeniusMap(CHUNK_SIZE, CHUNK_NUMBER, 6);
|
||||
for(var id = 0; id < 6; id++){
|
||||
for(var eps = 0; eps < 2; eps++){
|
||||
for(var j = 0; j < CHUNK_NUMBER; j++){
|
||||
f1.in[id][eps][j] <== in[id][eps][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// in^{ - 1}
|
||||
component f2 = Fp12Invert(CHUNK_SIZE, CHUNK_NUMBER, p);
|
||||
for(var id = 0; id < 6; id++){
|
||||
for(var eps = 0; eps < 2; eps++){
|
||||
for(var j = 0; j < CHUNK_NUMBER; j++){
|
||||
f2.in[id][eps][j] <== in[id][eps][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// in^{q^6 - 1}
|
||||
component f3 = Fp12Multiply(CHUNK_SIZE, CHUNK_NUMBER, p);
|
||||
for(var id = 0; id < 6; id++){
|
||||
for(var eps = 0; eps < 2; eps++){
|
||||
for(var j = 0; j < CHUNK_NUMBER; j++){
|
||||
f3.a[id][eps][j] <== f1.out[id][eps][j];
|
||||
f3.b[id][eps][j] <== f2.out[id][eps][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// in^{(q^6 - 1)*q^2} = f3^{q^2}
|
||||
component f4 = Fp12FrobeniusMap(CHUNK_SIZE, CHUNK_NUMBER, 2);
|
||||
for(var id = 0; id < 6; id++){
|
||||
for(var eps = 0; eps < 2; eps++){
|
||||
for(var j = 0; j < CHUNK_NUMBER; j++){
|
||||
f4.in[id][eps][j] <== f3.out[id][eps][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// in^{(q^6 - 1)(q^2 + 1)} = f4 * f3
|
||||
component f5 = Fp12Multiply(CHUNK_SIZE, CHUNK_NUMBER, p);
|
||||
for(var id = 0; id < 6; id++){
|
||||
for(var eps = 0; eps < 2; eps++){
|
||||
for(var j = 0; j < CHUNK_NUMBER; j++){
|
||||
f5.a[id][eps][j] <== f3.out[id][eps][j];
|
||||
f5.b[id][eps][j] <== f4.out[id][eps][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(var id = 0; id < 6; id++){
|
||||
for(var eps = 0; eps < 2; eps++){
|
||||
for(var j = 0; j < CHUNK_NUMBER; j++){
|
||||
out[id][eps][j] <== f5.out[id][eps][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// out = in^{(q^12 - 1)/r} = FinalExpHardPart(FinalExpEasyPart(in))
|
||||
template FinalExponentiate(CHUNK_SIZE, CHUNK_NUMBER, p){
|
||||
signal input in[6][2][CHUNK_NUMBER];
|
||||
signal output out[6][2][CHUNK_NUMBER];
|
||||
|
||||
component f1 = FinalExpEasyPart(CHUNK_SIZE, CHUNK_NUMBER, p);
|
||||
for(var id = 0; id < 6; id++){
|
||||
for(var eps = 0; eps < 2; eps++){
|
||||
for(var j = 0; j < CHUNK_NUMBER; j++){
|
||||
f1.in[id][eps][j] <== in[id][eps][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component f = FinalExpHardPart(CHUNK_SIZE, CHUNK_NUMBER, p);
|
||||
for(var id = 0; id < 6; id++){
|
||||
for(var eps = 0; eps < 2; eps++){
|
||||
for(var j = 0; j < CHUNK_NUMBER; j++){
|
||||
f.in[id][eps][j] <== f1.out[id][eps][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(var id = 0; id < 6; id++){
|
||||
for(var eps = 0; eps < 2; eps++){
|
||||
for(var j = 0; j < CHUNK_NUMBER; j++){
|
||||
out[id][eps][j] <== f.out[id][eps][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,296 @@
|
||||
pragma circom 2.0.3;
|
||||
|
||||
include "../../../bigInt/bigInt.circom";
|
||||
include "../../../bigInt/bigIntFunc.circom";
|
||||
// a[i], b[i] in 0... 2 * *CHUNK_SIZE - 1
|
||||
// represent a = a[0] + a[1] * 2 * *CHUNK_SIZE + .. + a[CHUNK_NUMBER - 1] * 2 * *(CHUNK_SIZE * CHUNK_NUMBER)
|
||||
// calculates (a+b)%p, where 0<= a,b < p
|
||||
template FpAdd(CHUNK_SIZE, CHUNK_NUMBER, p){
|
||||
assert(CHUNK_SIZE <= 252);
|
||||
signal input a[CHUNK_NUMBER];
|
||||
signal input b[CHUNK_NUMBER];
|
||||
signal output out[CHUNK_NUMBER];
|
||||
|
||||
component add = BigAdd(CHUNK_SIZE,CHUNK_NUMBER);
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
add.a[i] <== a[i];
|
||||
add.b[i] <== b[i];
|
||||
}
|
||||
component lessThan = BigLessThan(CHUNK_SIZE, CHUNK_NUMBER + 1);
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
lessThan.a[i] <== add.out[i];
|
||||
lessThan.b[i] <== p[i];
|
||||
}
|
||||
lessThan.a[CHUNK_NUMBER] <== add.out[CHUNK_NUMBER];
|
||||
lessThan.b[CHUNK_NUMBER] <== 0;
|
||||
|
||||
component sub = BigSub(CHUNK_SIZE,CHUNK_NUMBER + 1);
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
sub.a[i] <== add.out[i];
|
||||
sub.b[i] <== p[i] - lessThan.out * p[i];
|
||||
}
|
||||
sub.a[CHUNK_NUMBER] <== add.out[CHUNK_NUMBER];
|
||||
sub.b[CHUNK_NUMBER] <== 0;
|
||||
|
||||
sub.out[CHUNK_NUMBER] === 0;
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
out[i] <== sub.out[i];
|
||||
}
|
||||
}
|
||||
|
||||
// calculates (a - b) % p, where a, b < p
|
||||
// note: does not assume a >= b
|
||||
template FpSubtract(CHUNK_SIZE, CHUNK_NUMBER, p){
|
||||
assert(CHUNK_SIZE <= 252);
|
||||
signal input a[CHUNK_NUMBER];
|
||||
signal input b[CHUNK_NUMBER];
|
||||
signal output out[CHUNK_NUMBER];
|
||||
component sub = BigSub(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++){
|
||||
sub.a[i] <== a[i];
|
||||
sub.b[i] <== b[i];
|
||||
}
|
||||
signal flag;
|
||||
flag <== sub.underflow;
|
||||
component add = BigAdd(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++){
|
||||
add.a[i] <== sub.out[i];
|
||||
add.b[i] <== p[i];
|
||||
}
|
||||
signal tmp[CHUNK_NUMBER];
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++){
|
||||
tmp[i] <== (1 - flag) * sub.out[i];
|
||||
out[i] <== tmp[i] + flag * add.out[i];
|
||||
}
|
||||
}
|
||||
|
||||
// Input: in <= p
|
||||
// Output: -in (mod p) = p - in if in != 0, else 0
|
||||
// Constrains in <= p
|
||||
template FpNegate(CHUNK_SIZE, CHUNK_NUMBER, p){
|
||||
signal input in[CHUNK_NUMBER];
|
||||
signal output out[CHUNK_NUMBER];
|
||||
|
||||
component neg = BigSub(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
component isZero = BigIsZero(CHUNK_NUMBER);
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
neg.a[idx] <== p[idx];
|
||||
neg.b[idx] <== in[idx];
|
||||
|
||||
isZero.in[idx] <== in[idx];
|
||||
}
|
||||
neg.underflow === 0; // constrain in <= p
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++)
|
||||
out[idx] <== (1 - isZero.out)*neg.out[idx];
|
||||
}
|
||||
|
||||
template FpMultiply(CHUNK_SIZE, CHUNK_NUMBER, p) {
|
||||
assert(CHUNK_SIZE <= 252);
|
||||
signal input a[CHUNK_NUMBER];
|
||||
signal input b[CHUNK_NUMBER];
|
||||
signal output out[CHUNK_NUMBER];
|
||||
|
||||
var LOGK = log_ceil(CHUNK_NUMBER);
|
||||
|
||||
component noCarry = BigMultShortLong(CHUNK_SIZE, CHUNK_NUMBER, 2 * CHUNK_SIZE + LOGK);
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
noCarry.a[i] <== a[i];
|
||||
noCarry.b[i] <== b[i];
|
||||
}
|
||||
component red = PrimeReduce(CHUNK_SIZE, CHUNK_NUMBER, CHUNK_NUMBER - 1, p, 3 * CHUNK_SIZE + 2 * LOGK);
|
||||
for(var i = 0; i < 2 * CHUNK_NUMBER - 1; i++)
|
||||
red.in[i] <== noCarry.out[i];
|
||||
|
||||
component bigMod = SignedFpCarryModP(CHUNK_SIZE, CHUNK_NUMBER, 3 * CHUNK_SIZE + 2 * LOGK, p);
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++)
|
||||
bigMod.in[i] <== red.out[i];
|
||||
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++)
|
||||
out[i] <== bigMod.out[i];
|
||||
}
|
||||
|
||||
// constrain in = p * X + Y
|
||||
// in[i] in (-2^OVERFLOW, 2^OVERFLOW)
|
||||
// assume registers of X have abs value < 2^{OVERFLOW - CHUNK_SIZE - log(min(CHUNK_NUMBER,m)) - 1}
|
||||
// assume OVERFLOW - 1 >= CHUNK_SIZE
|
||||
template CheckCarryModP(CHUNK_SIZE, CHUNK_NUMBER, m, OVERFLOW, p){
|
||||
signal input in[CHUNK_NUMBER];
|
||||
signal input X[m];
|
||||
signal input Y[CHUNK_NUMBER];
|
||||
|
||||
assert(OVERFLOW < 251);
|
||||
assert(CHUNK_SIZE <= OVERFLOW - 1);
|
||||
component pX;
|
||||
component carryCheck;
|
||||
|
||||
pX = BigMultShortLongUnequal(CHUNK_SIZE, CHUNK_NUMBER, m, OVERFLOW); // p has CHUNK_NUMBER registers, X has m registers, so output really has CHUNK_NUMBER + m - 1 registers
|
||||
// OVERFLOW register in (-2^{OVERFLOW - 1} , 2^{OVERFLOW - 1})
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++){
|
||||
pX.a[i] <== p[i];
|
||||
}
|
||||
for(var i = 0; i < m; i++){
|
||||
pX.b[i] <== X[i];
|
||||
}
|
||||
|
||||
// in - p*X - Y has registers in (-2^{OVERFLOW + 1}, 2^{OVERFLOW + 1})
|
||||
carryCheck = CheckCarryToZero(CHUNK_SIZE, OVERFLOW + 1, CHUNK_NUMBER + m - 1);
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++){
|
||||
carryCheck.in[i] <== in[i] - pX.out[i] - Y[i];
|
||||
}
|
||||
for(var i=CHUNK_NUMBER; i < CHUNK_NUMBER + m - 1; i++){
|
||||
carryCheck.in[i] <== -pX.out[i];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// solve for in = p * X + out
|
||||
// assume in has registers in (-2^OVERFLOW, 2^OVERFLOW)
|
||||
// X has registers lying in [-2^CHUNK_SIZE, 2^CHUNK_SIZE)
|
||||
// X has at most Ceil(OVERFLOW / CHUNK_SIZE) registers
|
||||
|
||||
// out has registers in [0, 2^CHUNK_SIZE) but don't constrain out < p
|
||||
template SignedFpCarryModP(CHUNK_SIZE, CHUNK_NUMBER, OVERFLOW, p){
|
||||
signal input in[CHUNK_NUMBER];
|
||||
var m = (OVERFLOW + CHUNK_SIZE - 1) \ CHUNK_SIZE;
|
||||
signal output X[m];
|
||||
signal output out[CHUNK_NUMBER];
|
||||
|
||||
assert(OVERFLOW < 251);
|
||||
|
||||
var Xvar[2][150] = get_signed_Fp_carry_witness(CHUNK_SIZE, CHUNK_NUMBER, m, in, p);
|
||||
component xRangeChecks[m];
|
||||
component rangeChecks[CHUNK_NUMBER];
|
||||
//component lessThan = BigLessThan(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++){
|
||||
out[i] <-- Xvar[1][i];
|
||||
rangeChecks[i] = Num2Bits(CHUNK_SIZE);
|
||||
rangeChecks[i].in <== out[i];
|
||||
//lessThan.a[i] <== out[i];
|
||||
//lessThan.b[i] <== p[i];
|
||||
}
|
||||
//lessThan.out === 1;
|
||||
|
||||
for(var i = 0; i < m; i++){
|
||||
X[i] <-- Xvar[0][i];
|
||||
xRangeChecks[i] = Num2Bits(CHUNK_SIZE + 1);
|
||||
xRangeChecks[i].in <== X[i] + (1 << CHUNK_SIZE); // X[i] should be between [-2^CHUNK_SIZE, 2^CHUNK_SIZE)
|
||||
}
|
||||
|
||||
component modCheck = CheckCarryModP(CHUNK_SIZE, CHUNK_NUMBER, m, OVERFLOW, p);
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++){
|
||||
modCheck.in[i] <== in[i];
|
||||
modCheck.Y[i] <== out[i];
|
||||
}
|
||||
for(var i = 0; i < m; i++){
|
||||
modCheck.X[i] <== X[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Constrain in = 0 mod p by solving for in = p * X
|
||||
// assume in has registers in (-2^OVERFLOW, 2^OVERFLOW)
|
||||
// X has registers lying in [-2^CHUNK_SIZE, 2^CHUNK_SIZE)
|
||||
// X has at most Ceil(OVERFLOW / CHUNK_SIZE) registers
|
||||
|
||||
// save range check on Y compared to SignedFpCarryModP
|
||||
template SignedCheckCarryModToZero(CHUNK_SIZE, CHUNK_NUMBER, OVERFLOW, p){
|
||||
signal input in[CHUNK_NUMBER];
|
||||
var m = (OVERFLOW + CHUNK_SIZE - 1) \ CHUNK_SIZE;
|
||||
signal output X[m];
|
||||
|
||||
assert(OVERFLOW < 251);
|
||||
|
||||
var Xvar[2][150] = get_signed_Fp_carry_witness(CHUNK_SIZE, CHUNK_NUMBER, m, in, p);
|
||||
component xRangeChecks[m];
|
||||
|
||||
for(var i = 0; i < m; i++){
|
||||
X[i] <-- Xvar[0][i];
|
||||
xRangeChecks[i] = Num2Bits(CHUNK_SIZE + 1);
|
||||
xRangeChecks[i].in <== X[i] + (1 << CHUNK_SIZE); // X[i] should be between [-2^CHUNK_SIZE, 2^CHUNK_SIZE)
|
||||
}
|
||||
|
||||
component modCheck = CheckCarryModP(CHUNK_SIZE, CHUNK_NUMBER, m, OVERFLOW, p);
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++){
|
||||
modCheck.in[i] <== in[i];
|
||||
modCheck.Y[i] <== 0;
|
||||
}
|
||||
for(var i = 0; i < m; i++){
|
||||
modCheck.X[i] <== X[i];
|
||||
}
|
||||
}
|
||||
|
||||
// in has CHUNK_NUMBER registers, elt of Fp
|
||||
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-4.1
|
||||
// return in % 2
|
||||
// This requires `in` to be the unique element < p.
|
||||
// NOTE: different from Wahby-Boneh paper https://eprint.iacr.org/2019/403.pdf and python reference code: https://github.com/algorand/bls_sigs_ref/blob/master/python-impl/opt_swu_g2.py
|
||||
template FpSgn0(CHUNK_SIZE, CHUNK_NUMBER, p){
|
||||
signal input in[CHUNK_NUMBER];
|
||||
signal output out;
|
||||
|
||||
// constrain in < p
|
||||
component lessThan = BigLessThan(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++){
|
||||
lessThan.a[i] <== in[i];
|
||||
lessThan.b[i] <== p[i];
|
||||
}
|
||||
lessThan.out === 1;
|
||||
|
||||
// note we only need in[0] !
|
||||
var r = in[0] % 2;
|
||||
var q = (in[0] - r) / 2;
|
||||
out <-- r;
|
||||
signal div;
|
||||
div <-- q;
|
||||
out * (1 - out) === 0;
|
||||
in[0] === 2 * div + out;
|
||||
}
|
||||
|
||||
template FpIsZero(CHUNK_SIZE, CHUNK_NUMBER, p){
|
||||
signal input in[CHUNK_NUMBER];
|
||||
signal output out;
|
||||
|
||||
// check that in < p
|
||||
component lessThan = BigLessThan(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
component isZero = BigIsZero(CHUNK_NUMBER);
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
lessThan.a[i] <== in[i];
|
||||
lessThan.b[i] <== p[i];
|
||||
|
||||
isZero.in[i] <== in[i];
|
||||
}
|
||||
lessThan.out === 1;
|
||||
out <== isZero.out;
|
||||
}
|
||||
|
||||
template FpIsEqual(CHUNK_SIZE, CHUNK_NUMBER, p){
|
||||
signal input in[2][CHUNK_NUMBER];
|
||||
signal output out;
|
||||
|
||||
// check in[i] < p
|
||||
component lessThan[2];
|
||||
for(var i = 0; i < 2; i++){
|
||||
lessThan[i] = BigLessThan(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
lessThan[i].a[idx] <== in[i][idx];
|
||||
lessThan[i].b[idx] <== p[idx];
|
||||
}
|
||||
lessThan[i].out === 1;
|
||||
}
|
||||
|
||||
component isEqual[CHUNK_NUMBER + 1];
|
||||
var sum = 0;
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++){
|
||||
isEqual[i] = IsEqual();
|
||||
isEqual[i].in[0] <== in[0][i];
|
||||
isEqual[i].in[1] <== in[1][i];
|
||||
sum = sum + isEqual[i].out;
|
||||
}
|
||||
|
||||
isEqual[CHUNK_NUMBER] = IsEqual();
|
||||
isEqual[CHUNK_NUMBER].in[0] <== sum;
|
||||
isEqual[CHUNK_NUMBER].in[1] <== CHUNK_NUMBER;
|
||||
out <== isEqual[CHUNK_NUMBER].out;
|
||||
}
|
||||
@@ -0,0 +1,654 @@
|
||||
pragma circom 2.0.3;
|
||||
|
||||
include "../../../bigInt/bigInt.circom";
|
||||
include "../../../bigInt/bigIntFunc.circom";
|
||||
include "fieldElementsFunc.circom";
|
||||
include "fp.circom";
|
||||
include "fp2.circom";
|
||||
include "fp12Func.circom";
|
||||
include "bls12_381Func.circom";
|
||||
|
||||
template Fp12FrobeniusMap(CHUNK_SIZE, CHUNK_NUMBER, POWER){
|
||||
signal input in[6][2][CHUNK_NUMBER];
|
||||
signal output out[6][2][CHUNK_NUMBER];
|
||||
|
||||
var P[150] = get_BLS12_381_prime(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
var FP12_FROBENIUS_COEFFICIENTS[12][6][2][20] = get_Fp12_frobenius(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
var POW = POWER % 12;
|
||||
|
||||
component inFrob[6];
|
||||
|
||||
// multiply inFrob[i] by FP12_FROBENIUS_COEFFICIENTS[POW][i]
|
||||
// if POW is even, then FP12_FROBENIUS_COEFFICIENTS[POW][i] is in Fp instead of Fp2, so can optimize
|
||||
component multOdd[6];
|
||||
component multEven[6][2];
|
||||
if((POW % 2) == 0){
|
||||
for(var j = 0; j < CHUNK_NUMBER; j++){
|
||||
out[0][0][j] <== in[0][0][j];
|
||||
out[0][1][j] <== in[0][1][j];
|
||||
}
|
||||
for(var i = 1; i < 6; i++){
|
||||
multEven[i][0] = FpMultiply(CHUNK_SIZE, CHUNK_NUMBER, P);
|
||||
multEven[i][1] = FpMultiply(CHUNK_SIZE, CHUNK_NUMBER, P);
|
||||
for(var j = 0; j < CHUNK_NUMBER; j++){
|
||||
multEven[i][0].a[j] <== in[i][0][j];
|
||||
multEven[i][1].a[j] <== in[i][1][j];
|
||||
|
||||
multEven[i][0].b[j] <== FP12_FROBENIUS_COEFFICIENTS[POW][i][0][j];
|
||||
multEven[i][1].b[j] <== FP12_FROBENIUS_COEFFICIENTS[POW][i][0][j];
|
||||
}
|
||||
for(var j = 0; j < CHUNK_NUMBER; j++){
|
||||
out[i][0][j] <== multEven[i][0].out[j];
|
||||
out[i][1][j] <== multEven[i][1].out[j];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// apply Frob to coefficients first
|
||||
for(var i = 0; i < 6; i++){
|
||||
inFrob[i] = Fp2FrobeniusMap(CHUNK_SIZE, CHUNK_NUMBER, POW, P);
|
||||
for(var j = 0; j < CHUNK_NUMBER; j++){
|
||||
inFrob[i].in[0][j] <== in[i][0][j];
|
||||
inFrob[i].in[1][j] <== in[i][1][j];
|
||||
}
|
||||
}
|
||||
for(var j = 0; j < CHUNK_NUMBER; j++){
|
||||
out[0][0][j] <== inFrob[0].out[0][j];
|
||||
out[0][1][j] <== inFrob[0].out[1][j];
|
||||
}
|
||||
for(var i = 1; i < 6; i++){
|
||||
multOdd[i] = Fp2Multiply(CHUNK_SIZE, CHUNK_NUMBER, P);
|
||||
for(var j = 0; j < CHUNK_NUMBER; j++){
|
||||
for(var eps = 0; eps < 2; eps++){
|
||||
multOdd[i].a[eps][j] <== inFrob[i].out[eps][j];
|
||||
multOdd[i].b[eps][j] <== FP12_FROBENIUS_COEFFICIENTS[POW][i][eps][j];
|
||||
}
|
||||
}
|
||||
for(var j = 0; j < CHUNK_NUMBER; j++){
|
||||
out[i][0][j] <== multOdd[i].out[0][j];
|
||||
out[i][1][j] <== multOdd[i].out[1][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template Fp12Add(CHUNK_SIZE, CHUNK_NUMBER, P) {
|
||||
signal input a[6][2][CHUNK_NUMBER];
|
||||
signal input b[6][2][CHUNK_NUMBER];
|
||||
signal output out[6][2][CHUNK_NUMBER];
|
||||
component adders[6][2];
|
||||
for (var i = 0; i < 6; i ++) {
|
||||
for (var j = 0; j < 2; j ++) {
|
||||
adders[i][j] = FpAdd(CHUNK_SIZE,CHUNK_NUMBER, P);
|
||||
for (var M = 0; M < CHUNK_NUMBER; M ++) {
|
||||
adders[i][j].a[M] <== a[i][j][M];
|
||||
adders[i][j].b[M] <== b[i][j][M];
|
||||
adders[i][j].P[M] <== P[M];
|
||||
}
|
||||
for (var M = 0; M < CHUNK_NUMBER; M ++) {
|
||||
out[i][j][M] <== adders[i][j].out[M];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// a is CHUNK_NUMBER array representing element a of Fp allowing negative registers
|
||||
// b is 6 x 2 x CHUNK_NUMBER array representing element b0 + b1 u of Fp12 allowing negative registers
|
||||
// where b_i = b[][i][] is 6 x CHUNK_NUMBER array
|
||||
// out is a*b in Fp12 as 6 x 2 x (2k - 1) array
|
||||
// M_OUT is the expected max number of bits in the output registers
|
||||
template SignedFp12ScalarMultiplyNoCarry(CHUNK_SIZE, CHUNK_NUMBER, M_OUT){
|
||||
signal input a[CHUNK_NUMBER];
|
||||
signal input b[6][2][CHUNK_NUMBER];
|
||||
signal output out[6][2][2 * CHUNK_NUMBER - 1];
|
||||
|
||||
component ab[6][2];
|
||||
for(var i = 0; i < 6; i++){
|
||||
for(var j = 0; j < 2; j++){
|
||||
ab[i][j] = BigMultShortLong(CHUNK_SIZE, CHUNK_NUMBER, M_OUT); // 2k - 1 registers
|
||||
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
ab[i][j].a[idx] <== a[idx];
|
||||
ab[i][j].b[idx] <== b[i][j][idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(var i = 0; i < 6; i++){
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var idx = 0; idx < 2 * CHUNK_NUMBER - 1; idx++){
|
||||
out[i][j][idx] <== ab[i][j].out[idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// M_OUT is the expected max number of bits in the output registers
|
||||
template SignedFp12ScalarMultiplyNoCarryUnequal(CHUNK_SIZE, ka, kb, M_OUT){
|
||||
signal input a[ka];
|
||||
signal input b[6][2][kb];
|
||||
signal output out[6][2][ka + kb - 1];
|
||||
|
||||
component ab[6][2];
|
||||
for(var i = 0; i < 6; i++){
|
||||
for(var j = 0; j < 2; j++){
|
||||
ab[i][j] = BigMultShortLongUnequal(CHUNK_SIZE, ka, kb, M_OUT); // 2k - 1 registers
|
||||
|
||||
for(var idx = 0; idx < ka; idx++){
|
||||
ab[i][j].a[idx] <== a[idx];
|
||||
}
|
||||
for(var idx = 0; idx < kb; idx++){
|
||||
ab[i][j].b[idx] <== b[i][j][idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(var i = 0; i < 6; i++){
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var idx = 0; idx < ka + kb - 1; idx++){
|
||||
out[i][j][idx] <== ab[i][j].out[idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// a is 2 x CHUNK_NUMBER array representing element a of Fp2 allowing negative registers
|
||||
// b is 6 x 2 x CHUNK_NUMBER array representing element b0 + b1 u of Fp12 allowing negative registers
|
||||
// where b_i = b[][i][] is 6 x CHUNK_NUMBER array
|
||||
// out is a*b in Fp12 as 6 x 2 x (2k - 1) array
|
||||
// M_OUT is the expected max number of bits in the output registers
|
||||
template SignedFp12Fp2MultiplyNoCarry(CHUNK_SIZE, CHUNK_NUMBER, M_OUT){
|
||||
signal input a[2][CHUNK_NUMBER];
|
||||
signal input b[6][2][CHUNK_NUMBER];
|
||||
signal output out[6][2][2 * CHUNK_NUMBER - 1];
|
||||
|
||||
component ab[6][2];
|
||||
component abi[6][2];
|
||||
for(var i = 0; i < 6; i++){
|
||||
for(var j = 0; j < 2; j++){
|
||||
ab[i][j] = BigMultShortLong(CHUNK_SIZE, CHUNK_NUMBER, M_OUT); // 2k - 1 registers
|
||||
abi[i][j] = BigMultShortLong(CHUNK_SIZE, CHUNK_NUMBER, M_OUT); // 2k - 1 registers
|
||||
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
ab[i][j].a[idx] <== a[0][idx];
|
||||
ab[i][j].b[idx] <== b[i][j][idx];
|
||||
}
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
abi[i][j].a[idx] <== a[1][idx];
|
||||
abi[i][j].b[idx] <== b[i][j][idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(var i = 0; i < 6; i++){
|
||||
for(var idx = 0; idx < 2 * CHUNK_NUMBER - 1; idx++) {
|
||||
out[i][0][idx] <== ab[i][0].out[idx] - abi[i][1].out[idx];
|
||||
out[i][1][idx] <== abi[i][0].out[idx] + ab[i][1].out[idx];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// M_OUT is the expected max number of bits in the output registers
|
||||
template SignedFp12Fp2MultiplyNoCarryUnequal(CHUNK_SIZE, ka, kb, M_OUT){
|
||||
signal input a[2][ka];
|
||||
signal input b[6][2][kb];
|
||||
signal output out[6][2][ka + kb - 1];
|
||||
|
||||
component ab[6][2];
|
||||
component abi[6][2];
|
||||
for(var i = 0; i < 6; i++){
|
||||
for(var j = 0; j < 2; j++){
|
||||
ab[i][j] = BigMultShortLongUnequal(CHUNK_SIZE, ka, kb, M_OUT); // 2k - 1 registers
|
||||
abi[i][j] = BigMultShortLongUnequal(CHUNK_SIZE, ka, kb, M_OUT); // 2k - 1 registers
|
||||
|
||||
for(var idx = 0; idx < ka; idx++){
|
||||
ab[i][j].a[idx] <== a[0][idx];
|
||||
abi[i][j].a[idx] <== a[1][idx];
|
||||
}
|
||||
for(var idx = 0; idx < kb; idx++){
|
||||
ab[i][j].b[idx] <== b[i][j][idx];
|
||||
abi[i][j].b[idx] <== b[i][j][idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(var i = 0; i < 6; i++){
|
||||
for(var idx = 0; idx < ka + kb - 1; idx++){
|
||||
out[i][0][idx] <== ab[i][0].out[idx] - abi[i][1].out[idx];
|
||||
out[i][1][idx] <== abi[i][0].out[idx] + ab[i][1].out[idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// we first write a = a0 + a1 u, b = b0 + b1 u for ai, bi being:
|
||||
// * length 6 vectors with ka, kb registers in (-B_a, B_a) and (-B_b, B_b)
|
||||
// ab = (a0 b0 - a1 b1) + (a0 b1 + a1 b0) u
|
||||
// a_i b_j is degree 10 polynomial in w
|
||||
// Assume w^6 = XI0 + u and convert ab into degree 5 polynomials in w by substitution
|
||||
// The real and imaginary parts are
|
||||
// * length 6 vectors with ka + kb - 1 registers abs val < B_a * B_b * 6 * min(ka, kb) * (2 + XI0)
|
||||
// M_OUT is the expected max number of bits in the output registers
|
||||
template SignedFp12MultiplyNoCarryUnequal(CHUNK_SIZE, ka, kb, M_OUT){
|
||||
var L = 6;
|
||||
var XI0 = 1;
|
||||
signal input a[L][2][ka];
|
||||
signal input b[L][2][kb];
|
||||
signal output out[L][2][ka + kb - 1];
|
||||
|
||||
component a0b0 = BigMultShortLong2DUnequal(CHUNK_SIZE, ka, kb, L, L);
|
||||
component a0b1 = BigMultShortLong2DUnequal(CHUNK_SIZE, ka, kb, L, L);
|
||||
component a1b0 = BigMultShortLong2DUnequal(CHUNK_SIZE, ka, kb, L, L);
|
||||
component a1b1 = BigMultShortLong2DUnequal(CHUNK_SIZE, ka, kb, L, L);
|
||||
|
||||
for (var i = 0; i < L; i ++) {
|
||||
for (var j = 0; j < ka; j ++) {
|
||||
a0b0.a[i][j] <== a[i][0][j];
|
||||
a0b1.a[i][j] <== a[i][0][j];
|
||||
|
||||
a1b0.a[i][j] <== a[i][1][j];
|
||||
a1b1.a[i][j] <== a[i][1][j];
|
||||
}
|
||||
for (var j = 0; j < kb; j ++) {
|
||||
a0b0.b[i][j] <== b[i][0][j];
|
||||
a1b0.b[i][j] <== b[i][0][j];
|
||||
|
||||
a0b1.b[i][j] <== b[i][1][j];
|
||||
a1b1.b[i][j] <== b[i][1][j];
|
||||
}
|
||||
}
|
||||
|
||||
// X[][0] = a0 b0 - a1 b1
|
||||
// X[][1] = a0 b1 + a1 b0
|
||||
// X[][0] = sum_{i = 0}^10 X[i][0] * w^i
|
||||
signal X[2 * L - 1][2][ka + kb - 1];
|
||||
for (var i = 0; i < 2 * L - 1; i++) {
|
||||
for (var j = 0; j < ka + kb - 1; j++) {
|
||||
X[i][0][j] <== a0b0.out[i][j] - a1b1.out[i][j];
|
||||
X[i][1][j] <== a0b1.out[i][j] + a1b0.out[i][j];
|
||||
}
|
||||
}
|
||||
|
||||
// X[i+6][0] w^{i+6} = X[i+6][0] * XI0 * w^i + X[i+6][0] * w^i * u
|
||||
// X[i+6][1] w^{i+6} = - X[i+6][1] * w^i X[i+6][1] * XI0 * w^i * u
|
||||
for (var i = 0; i < L; i++){
|
||||
for (var j = 0; j < ka + kb - 1; j++) {
|
||||
if (i < L - 1) {
|
||||
out[i][0][j] <== X[i][0][j] + X[L + i][0][j] * XI0 - X[L + i][1][j];
|
||||
out[i][1][j] <== X[i][1][j] + X[L + i][0][j] + X[L + i][1][j];
|
||||
} else {
|
||||
out[i][0][j] <== X[i][0][j];
|
||||
out[i][1][j] <== X[i][1][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template SignedFp12MultiplyNoCarry(CHUNK_SIZE, CHUNK_NUMBER, M_OUT){
|
||||
var L = 6;
|
||||
signal input a[L][2][CHUNK_NUMBER];
|
||||
signal input b[L][2][CHUNK_NUMBER];
|
||||
signal output out[L][2][2 * CHUNK_NUMBER - 1];
|
||||
|
||||
component mult = SignedFp12MultiplyNoCarryUnequal(CHUNK_SIZE, CHUNK_NUMBER, CHUNK_NUMBER, M_OUT);
|
||||
for(var i = 0; i < L; i++){
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
mult.a[i][j][idx] <== a[i][j][idx];
|
||||
mult.b[i][j][idx] <== b[i][j][idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(var i = 0; i < L; i++){
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var idx = 0; idx < 2 * CHUNK_NUMBER - 1; idx++){
|
||||
out[i][j][idx] <== mult.out[i][j][idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// M_OUT is the expected max number of bits in the output registers
|
||||
template Fp12Compress(CHUNK_SIZE, CHUNK_NUMBER, M, P, M_OUT){
|
||||
var L = 6;
|
||||
signal input in[L][2][CHUNK_NUMBER+M];
|
||||
signal output out[L][2][CHUNK_NUMBER];
|
||||
|
||||
component reduce[L][2];
|
||||
for (var i = 0; i < L; i++){
|
||||
for (var j = 0; j < 2; j++){
|
||||
reduce[i][j] = PrimeReduce(CHUNK_SIZE, CHUNK_NUMBER, M, P, M_OUT);
|
||||
for (var idx = 0; idx < CHUNK_NUMBER + M; idx++){
|
||||
reduce[i][j].in[idx] <== in[i][j][idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < L; i++){
|
||||
for (var j = 0; j < 2; j++){
|
||||
for (var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
out[i][j][idx] <== reduce[i][j].out[idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Input is same as for Fp12MultiplyNoCarry
|
||||
// Our answer is the prime reduction of output of Fp12MultiplyNoCarry to
|
||||
// * length 6 vectors with CHUNK_NUMBER registers in [0, B_a * B_b * 2^CHUNK_SIZE * 6 * (2 + XI0) * CHUNK_NUMBER^2)
|
||||
// P is length CHUNK_NUMBER
|
||||
// m_in is the expected max number of bits in the input registers (necessary for some intermediate overflow validation)
|
||||
// M_OUT is the expected max number of bits in the output registers
|
||||
template SignedFp12MultiplyNoCarryCompress(CHUNK_SIZE, CHUNK_NUMBER, P, m_in, M_OUT) {
|
||||
var L = 6;
|
||||
var XI0 = 1;
|
||||
signal input a[L][2][CHUNK_NUMBER];
|
||||
signal input b[L][2][CHUNK_NUMBER];
|
||||
signal output out[L][2][CHUNK_NUMBER];
|
||||
|
||||
var LOGK1 = log_ceil(6 * CHUNK_NUMBER * (2 + XI0));
|
||||
component noCarry = SignedFp12MultiplyNoCarry(CHUNK_SIZE, CHUNK_NUMBER, 2 * m_in + LOGK1);
|
||||
for (var i = 0; i < L; i ++){
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
noCarry.a[i][j][idx] <== a[i][j][idx];
|
||||
noCarry.b[i][j][idx] <== b[i][j][idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component reduce = Fp12Compress(CHUNK_SIZE, CHUNK_NUMBER, CHUNK_NUMBER - 1, P, M_OUT);
|
||||
for (var i = 0; i < L; i++){
|
||||
for (var j = 0; j < 2; j++){
|
||||
for (var idx = 0; idx < 2 * CHUNK_NUMBER - 1; idx++){
|
||||
reduce.in[i][j][idx] <== noCarry.out[i][j][idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < L; i++){
|
||||
for (var j = 0; j < 2; j++){
|
||||
for (var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
out[i][j][idx] <== reduce.out[i][j][idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// solve for: in = X * P + out
|
||||
// X has Ceil(overflow / CHUNK_SIZE) registers, lying in [-2^CHUNK_SIZE, 2^CHUNK_SIZE)
|
||||
// assume in has registers in [0, 2^overflow)
|
||||
template SignedFp12CarryModP(CHUNK_SIZE, CHUNK_NUMBER, overflow, P) {
|
||||
var L = 6;
|
||||
var M = (overflow + CHUNK_SIZE - 1) \ CHUNK_SIZE;
|
||||
signal input in[L][2][CHUNK_NUMBER];
|
||||
signal output X[L][2][M];
|
||||
signal output out[L][2][CHUNK_NUMBER];
|
||||
|
||||
assert(overflow < 251);
|
||||
|
||||
component carry[L][2];
|
||||
for(var i = 0; i < L; i++){
|
||||
for(var j = 0; j < 2; j++){
|
||||
carry[i][j] = SignedFpCarryModP(CHUNK_SIZE, CHUNK_NUMBER, overflow, P);
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++)
|
||||
carry[i][j].in[idx] <== in[i][j][idx];
|
||||
for(var idx = 0; idx<M; idx++)
|
||||
X[i][j][idx] <== carry[i][j].X[idx];
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++)
|
||||
out[i][j][idx] <== carry[i][j].out[idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// version of Fp12Multiply that uses the prime reduction trick
|
||||
// takes longer to compile
|
||||
// assumes P has CHUNK_NUMBER registers with kth register nonzero
|
||||
template Fp12Multiply(CHUNK_SIZE, CHUNK_NUMBER, P) {
|
||||
var L = 6;
|
||||
var XI0 = 1;
|
||||
signal input a[L][2][CHUNK_NUMBER];
|
||||
signal input b[L][2][CHUNK_NUMBER];
|
||||
|
||||
signal output out[L][2][CHUNK_NUMBER];
|
||||
|
||||
var LOGK2 = log_ceil(6 * CHUNK_NUMBER * CHUNK_NUMBER * (2 + XI0));
|
||||
component noCarry = SignedFp12MultiplyNoCarryCompress(CHUNK_SIZE, CHUNK_NUMBER, P, CHUNK_SIZE, 3 * CHUNK_SIZE + LOGK2);
|
||||
// registers abs val < 2^{3n} * 6 * (2 + XI0) * CHUNK_NUMBER^2)
|
||||
for (var i = 0; i < L; i++){
|
||||
for(var j = 0; j < 2; j++){
|
||||
for (var idx = 0; idx < CHUNK_NUMBER; idx++) {
|
||||
noCarry.a[i][j][idx] <== a[i][j][idx];
|
||||
noCarry.b[i][j][idx] <== b[i][j][idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
component carryMod;
|
||||
carryMod = SignedFp12CarryModP(CHUNK_SIZE, CHUNK_NUMBER, 3 * CHUNK_SIZE + LOGK2, P);
|
||||
for (var i = 0; i < L; i++){
|
||||
for (var j = 0; j < 2; j++){
|
||||
for (var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
carryMod.in[i][j][idx] <== noCarry.out[i][j][idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < L; i++){
|
||||
for (var j = 0; j < 2; j++){
|
||||
for (var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
out[i][j][idx] <== carryMod.out[i][j][idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// unoptimized squaring, just takes two elements of Fp12 and multiplies them
|
||||
template Fp12Square(CHUNK_SIZE, CHUNK_NUMBER, P) {
|
||||
signal input in[6][2][CHUNK_NUMBER];
|
||||
signal output out[6][2][CHUNK_NUMBER];
|
||||
|
||||
// for now just use plain multiplication, this can be optimized later
|
||||
component square = Fp12Multiply(CHUNK_SIZE, CHUNK_NUMBER, P);
|
||||
for(var i = 0; i < 6; i++){
|
||||
for(var j = 0; j < CHUNK_NUMBER; j++){
|
||||
square.a[i][0][j] <== in[i][0][j];
|
||||
square.a[i][1][j] <== in[i][1][j];
|
||||
|
||||
square.b[i][0][j] <== in[i][0][j];
|
||||
square.b[i][1][j] <== in[i][1][j];
|
||||
}
|
||||
}
|
||||
|
||||
for(var i = 0; i < 6; i++){
|
||||
for(var j = 0; j < CHUNK_NUMBER; j++){
|
||||
out[i][0][j] <== square.out[i][0][j];
|
||||
out[i][1][j] <== square.out[i][1][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// not actually a relevant circuit - this only exists to test find_Fp6_inverse
|
||||
template Fp6Invert(CHUNK_SIZE, CHUNK_NUMBER, P) {
|
||||
signal input a0[2][CHUNK_NUMBER];
|
||||
signal input a1[2][CHUNK_NUMBER];
|
||||
signal input a2[2][CHUNK_NUMBER];
|
||||
var out[6][2][150] = find_Fp6_inverse(CHUNK_SIZE, CHUNK_NUMBER, P, a0, a1, a2);
|
||||
signal output real_out[6][2][CHUNK_NUMBER];
|
||||
for (var i = 0; i < 6; i++) {
|
||||
for (var j = 0; j < 2; j ++) {
|
||||
for (var idx = 0; idx < CHUNK_NUMBER; idx++) {
|
||||
real_out[i][j][idx] <-- out[i][j][idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Call find_Fp12_inverse to compute INVERSE
|
||||
// Then check out * in = 1, out is an array of shorts
|
||||
template Fp12Invert(CHUNK_SIZE, CHUNK_NUMBER, P){
|
||||
signal input in[6][2][CHUNK_NUMBER];
|
||||
signal output out[6][2][CHUNK_NUMBER];
|
||||
|
||||
var INVERSE[6][2][150] = find_Fp12_inverse(CHUNK_SIZE, CHUNK_NUMBER, P, in); // 6 x 2 x 150, only 6 x 2 x CHUNK_NUMBER relevant
|
||||
for (var i = 0; i < 6; i ++) {
|
||||
for (var j = 0; j < 2; j ++) {
|
||||
for (var M = 0; M < CHUNK_NUMBER; M ++) {
|
||||
out[i][j][M] <-- INVERSE[i][j][M];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component outRangeChecks[6][2][CHUNK_NUMBER];
|
||||
for(var i = 0; i < 6; i++) {
|
||||
for(var j = 0; j < 2; j++) {
|
||||
for(var M = 0; M < CHUNK_NUMBER; M++) {
|
||||
outRangeChecks[i][j][M] = Num2Bits(CHUNK_SIZE);
|
||||
outRangeChecks[i][j][M].in <== out[i][j][M];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component inOut = Fp12Multiply(CHUNK_SIZE, CHUNK_NUMBER, P);
|
||||
for(var i = 0; i < 6; i++) {
|
||||
for(var j = 0; j < 2; j++) {
|
||||
for(var M = 0; M < CHUNK_NUMBER; M++) {
|
||||
inOut.a[i][j][M] <== in[i][j][M];
|
||||
inOut.b[i][j][M] <== out[i][j][M];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(var i = 0; i < 6; i++){
|
||||
for(var j = 0; j < 2; j++){
|
||||
for(var M = 0; M < CHUNK_NUMBER; M ++) {
|
||||
if(i == 0 && j == 0 && M == 0){
|
||||
inOut.out[i][j][M] === 1;
|
||||
} else {
|
||||
inOut.out[i][j][M] === 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// input is an element of Fp12
|
||||
// output is input raised to the e-th POWER
|
||||
// use the square and multiply method
|
||||
// assume 0 < e < 2^254
|
||||
template Fp12Exp(CHUNK_SIZE, CHUNK_NUMBER, e, P) {
|
||||
assert(e > 0);
|
||||
|
||||
signal input in[6][2][CHUNK_NUMBER];
|
||||
signal output out[6][2][CHUNK_NUMBER];
|
||||
|
||||
var temp = e;
|
||||
var BITLENGTH;
|
||||
for(var i = 0; i < 254; i++){
|
||||
if(temp != 0){
|
||||
BITLENGTH = i;
|
||||
}
|
||||
temp = temp >> 1;
|
||||
}
|
||||
BITLENGTH++;
|
||||
component pow2[BITLENGTH]; // pow2[i] = in^{2^i}
|
||||
component mult[BITLENGTH];
|
||||
|
||||
signal first[6][2][CHUNK_NUMBER];
|
||||
var CUR_ID = 0;
|
||||
|
||||
for(var i = 0; i < BITLENGTH; i++){
|
||||
// compute pow2[i] = pow2[i - 1]**2
|
||||
if(i > 0){ // pow2[0] is never defined since there is no squaring involved
|
||||
pow2[i] = Fp12Square(CHUNK_SIZE, CHUNK_NUMBER, P);
|
||||
for(var j = 0; j < CHUNK_NUMBER; j++) {
|
||||
pow2[i].P[j] <== P[j];
|
||||
}
|
||||
if(i == 1){
|
||||
for(var id = 0; id < 6; id++){
|
||||
for(var eps = 0; eps < 2; eps++){
|
||||
for(var j = 0; j < CHUNK_NUMBER; j++){
|
||||
pow2[i].in[id][eps][j] <== in[id][eps][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for(var id = 0; id < 6; id++){
|
||||
for(var eps = 0; eps < 2; eps++){
|
||||
for(var j = 0; j < CHUNK_NUMBER; j++){
|
||||
pow2[i].in[id][eps][j] <== pow2[i - 1].out[id][eps][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(((e >> i) & 1) == 1){
|
||||
if(CUR_ID == 0){ // this is the least significant bit
|
||||
if(i == 0){
|
||||
for(var id = 0; id < 6; id++){
|
||||
for(var eps = 0; eps < 2; eps++){
|
||||
for(var j = 0; j < CHUNK_NUMBER; j++)
|
||||
first[id][eps][j] <== in[id][eps][j];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for(var id = 0; id < 6; id++){
|
||||
for(var eps = 0; eps < 2; eps++){
|
||||
for(var j = 0; j < CHUNK_NUMBER; j++){
|
||||
first[id][eps][j] <== pow2[i].out[id][eps][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// multiply what we already have with pow2[i]
|
||||
mult[CUR_ID] = Fp12Multiply(CHUNK_SIZE, CHUNK_NUMBER, P);
|
||||
for(var id = 0; id < 6; id++){
|
||||
for(var eps = 0; eps < 2; eps++){
|
||||
for(var j = 0; j < CHUNK_NUMBER; j++){
|
||||
mult[CUR_ID].a[id][eps][j] <== pow2[i].out[id][eps][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
if(CUR_ID == 1){
|
||||
for(var id = 0; id < 6; id++){
|
||||
for(var eps = 0; eps < 2; eps++){
|
||||
for(var j = 0; j < CHUNK_NUMBER; j++){
|
||||
mult[CUR_ID].b[id][eps][j] <== first[id][eps][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for(var id = 0; id < 6; id++){
|
||||
for(var eps = 0; eps < 2; eps++){
|
||||
for(var j = 0; j < CHUNK_NUMBER; j++){
|
||||
mult[CUR_ID].b[id][eps][j] <== mult[CUR_ID - 1].out[id][eps][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
CUR_ID++;
|
||||
}
|
||||
}
|
||||
CUR_ID--;
|
||||
if(CUR_ID == 0){
|
||||
for(var id = 0; id < 6; id++){
|
||||
for(var eps = 0; eps < 2; eps++){
|
||||
for(var j = 0; j < CHUNK_NUMBER; j++){
|
||||
out[id][eps][j] <== first[id][eps][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for(var id = 0; id < 6; id++){
|
||||
for(var eps = 0; eps < 2; eps++){
|
||||
for(var j = 0; j < CHUNK_NUMBER; j++){
|
||||
out[id][eps][j] <== mult[CUR_ID].out[id][eps][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,231 @@
|
||||
pragma circom 2.0.3;
|
||||
|
||||
function find_Fp12_sum(CHUNK_SIZE, CHUNK_NUMBER, a, b, p) {
|
||||
var out[6][2][150];
|
||||
for(var i = 0; i < 6; i++){
|
||||
out[i] = find_Fp2_sum(CHUNK_SIZE, CHUNK_NUMBER, a[i], b[i], p);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function find_Fp12_diff(CHUNK_SIZE, CHUNK_NUMBER, a, b, p) {
|
||||
var out[6][2][150];
|
||||
for(var i = 0; i < 6; i++){
|
||||
out[i] = find_Fp2_diff(CHUNK_SIZE, CHUNK_NUMBER, a[i], b[i], p);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function find_Fp12_product(CHUNK_SIZE, CHUNK_NUMBER, a, b, p) {
|
||||
var l = 6;
|
||||
var A0[l][150];
|
||||
var A1[l][150];
|
||||
var B0[l][150];
|
||||
var B1[l][150];
|
||||
var NEG_B0[l][150];
|
||||
var NEG_B1[l][150];
|
||||
var out[l][2][150];
|
||||
for (var i = 0; i < l; i++) {
|
||||
for (var j = 0; j < CHUNK_NUMBER; j++) {
|
||||
A0[i][j] = a[i][0][j];
|
||||
A1[i][j] = a[i][1][j];
|
||||
B0[i][j] = b[i][0][j];
|
||||
B1[i][j] = b[i][1][j];
|
||||
}
|
||||
}
|
||||
for (var i = 0; i < l; i++) {
|
||||
NEG_B0[i] = long_sub(CHUNK_SIZE, CHUNK_NUMBER, p, B0[i]);
|
||||
NEG_B1[i] = long_sub(CHUNK_SIZE, CHUNK_NUMBER, p, B1[i]);
|
||||
}
|
||||
|
||||
var REAL_INIT[20][150];
|
||||
var INAG_INIT[20][150];
|
||||
var INAG_INIT_NEG[20][150];
|
||||
// var real[l][2][150];
|
||||
// var imag[l][2][150];
|
||||
// each product will be 2l-1 x 2k
|
||||
var A0B0_VAR[20][150] = prod2D(CHUNK_SIZE, CHUNK_NUMBER, l, A0, B0);
|
||||
var A1B1_NEG[20][150] = prod2D(CHUNK_SIZE, CHUNK_NUMBER, l, A1, NEG_B1);
|
||||
var A0B1_VAR[20][150] = prod2D(CHUNK_SIZE, CHUNK_NUMBER, l, A0, B1);
|
||||
var A1B0_VAR[20][150] = prod2D(CHUNK_SIZE, CHUNK_NUMBER, l, A1, B0);
|
||||
var A0B1_NEG[20][150] = prod2D(CHUNK_SIZE, CHUNK_NUMBER, l, A0, NEG_B1);
|
||||
var A1B0_NEG[20][150] = prod2D(CHUNK_SIZE, CHUNK_NUMBER, l, A1, NEG_B0);
|
||||
for (var i = 0; i < 2 * l - 1; i++) { // compute initial rep (deg w = 10)
|
||||
REAL_INIT[i] = long_add(CHUNK_SIZE, 2 * CHUNK_NUMBER, A0B0_VAR[i], A1B1_NEG[i]); // 2 * CHUNK_NUMBER + 1 registers each
|
||||
INAG_INIT[i] = long_add(CHUNK_SIZE, 2 * CHUNK_NUMBER, A0B1_VAR[i], A1B0_VAR[i]);
|
||||
INAG_INIT_NEG[i] = long_add(CHUNK_SIZE, 2 * CHUNK_NUMBER, A0B1_NEG[i], A1B0_NEG[i]);
|
||||
}
|
||||
var REAL_CARRY[l][150];
|
||||
var IMAG_CARRY[l][150];
|
||||
var REAL_FINAL[l][150];
|
||||
var IMAG_FINAL[l][150];
|
||||
var zeros[150]; // to balance register sizes
|
||||
for (var i = 0; i < 150; i++) {
|
||||
zeros[i] = 0;
|
||||
}
|
||||
for (var i = 0; i < l; i++) {
|
||||
if (i == l - 1) {
|
||||
REAL_CARRY[i] = long_add(CHUNK_SIZE, 2 * CHUNK_NUMBER + 1, zeros, zeros);
|
||||
IMAG_CARRY[i] = long_add(CHUNK_SIZE, 2 * CHUNK_NUMBER + 1, zeros, zeros);
|
||||
} else {
|
||||
REAL_CARRY[i] = long_add(CHUNK_SIZE, 2 * CHUNK_NUMBER + 1, REAL_INIT[i + l], INAG_INIT_NEG[i + l]); // now 2 * CHUNK_NUMBER + 2 registers
|
||||
IMAG_CARRY[i] = long_add(CHUNK_SIZE, 2 * CHUNK_NUMBER + 1, INAG_INIT[i + l], REAL_INIT[i + l]);
|
||||
}
|
||||
}
|
||||
for (var i = 0; i < l; i++) {
|
||||
REAL_FINAL[i] = long_add_unequal(CHUNK_SIZE, 2 * CHUNK_NUMBER + 2, 2 * CHUNK_NUMBER + 1, REAL_CARRY[i], REAL_INIT[i]); // now 2 * CHUNK_NUMBER + 3 registers
|
||||
IMAG_FINAL[i] = long_add_unequal(CHUNK_SIZE, 2 * CHUNK_NUMBER + 2, 2 * CHUNK_NUMBER + 1, IMAG_CARRY[i], INAG_INIT[i]);
|
||||
}
|
||||
var X_Y_REAL_TEMP[l][2][150];
|
||||
var X_Y_IMAG_TEMP[l][2][150];
|
||||
for (var i = 0; i < l; i++) {
|
||||
X_Y_REAL_TEMP[i] = long_div2(CHUNK_SIZE, CHUNK_NUMBER, CHUNK_NUMBER + 3, REAL_FINAL[i], p); // CHUNK_NUMBER+4 register quotient, CHUNK_NUMBER register remainder
|
||||
X_Y_IMAG_TEMP[i] = long_div2(CHUNK_SIZE, CHUNK_NUMBER, CHUNK_NUMBER + 3, IMAG_FINAL[i], p);
|
||||
}
|
||||
for (var i = 0; i < l; i++) {
|
||||
for (var j = 0; j < CHUNK_NUMBER; j++) {
|
||||
out[i][0][j] = X_Y_REAL_TEMP[i][1][j];
|
||||
out[i][1][j] = X_Y_IMAG_TEMP[i][1][j];
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
// a is 6 x 2 x CHUNK_NUMBER element of Fp^12
|
||||
// compute inverse. first multiply by conjugate a + bw (a,b in Fp^6, w^6=1+u, u^2=-1)
|
||||
// then reduce to inverting in Fp^6
|
||||
function find_Fp12_inverse(CHUNK_SIZE, CHUNK_NUMBER, p, a) {
|
||||
var A[6][2][150];
|
||||
var B[6][2][150];
|
||||
var BW[6][2][150];
|
||||
for (var i = 0; i < 3; i++) {
|
||||
for (var j = 0; j < 2; j++) {
|
||||
for (var m = 0; m < CHUNK_NUMBER; m++) {
|
||||
A[2 * i + 1][j][m] = 0;
|
||||
B[2 * i + 1][j][m] = 0;
|
||||
A[2 * i][j][m] = a[2 * i][j][m];
|
||||
B[2 * i][j][m] = a[2 * i + 1][j][m];
|
||||
BW[2 * i][j][m] = 0;
|
||||
BW[2 * i + 1][j][m] = a[2 * i + 1][j][m];
|
||||
}
|
||||
}
|
||||
}
|
||||
var A2[6][2][150] = find_Fp12_product(CHUNK_SIZE, CHUNK_NUMBER, A, A, p);
|
||||
var B2[6][2][150] = find_Fp12_product(CHUNK_SIZE, CHUNK_NUMBER, B, B, p);
|
||||
var CONJ[6][2][150] = find_Fp12_diff(CHUNK_SIZE, CHUNK_NUMBER, A, BW, p);
|
||||
var W2[6][2][150];
|
||||
for (var i = 0; i < 6; i++) {
|
||||
for (var j = 0; j < 2; j++) {
|
||||
for (var m = 0; m < CHUNK_NUMBER; m++) {
|
||||
if (i == 2 && j == 0 && m == 0) {
|
||||
W2[i][j][m] = 1;
|
||||
} else {
|
||||
W2[i][j][m] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
var B2_W2[6][2][150] = find_Fp12_product(CHUNK_SIZE, CHUNK_NUMBER, B2, W2, p);
|
||||
var CONJ_PROD[6][2][150] = find_Fp12_diff(CHUNK_SIZE, CHUNK_NUMBER, A2, B2_W2, p);
|
||||
var A0[2][150];
|
||||
var A1[2][150];
|
||||
var A2[2][150];
|
||||
for (var i = 0; i < 2; i++) {
|
||||
for (var m = 0; m < CHUNK_NUMBER; m++) {
|
||||
A0[i][m] = CONJ_PROD[0][i][m];
|
||||
A1[i][m] = CONJ_PROD[2][i][m];
|
||||
A2[i][m] = CONJ_PROD[4][i][m];
|
||||
}
|
||||
}
|
||||
var conjProdInv[6][2][150] = find_Fp6_inverse(CHUNK_SIZE, CHUNK_NUMBER, p, A0, A1, A2);
|
||||
var out[6][2][150] = find_Fp12_product(CHUNK_SIZE, CHUNK_NUMBER, CONJ, conjProdInv, p);
|
||||
return out;
|
||||
}
|
||||
|
||||
// compute the inverse of A0 + a1v + a2v^2 in Fp6, where
|
||||
// v^3 = 1+u, u^2 = -1, A0 A1 A2 in Fp2 (2 x CHUNK_NUMBER)
|
||||
// returns an element in standard Fp12 representation (6 x 2 x CHUNK_NUMBER)
|
||||
function find_Fp6_inverse(CHUNK_SIZE, CHUNK_NUMBER, p, A0, A1, A2) {
|
||||
var out[6][2][150];
|
||||
|
||||
var A0_SQUARED[2][150] = find_Fp2_product(CHUNK_SIZE, CHUNK_NUMBER, A0, A0, p);
|
||||
var A1_SQUARED[2][150] = find_Fp2_product(CHUNK_SIZE, CHUNK_NUMBER, A1, A1, p);
|
||||
var A2_SQUARED[2][150] = find_Fp2_product(CHUNK_SIZE, CHUNK_NUMBER, A2, A2, p);
|
||||
var A0_A1[2][150] = find_Fp2_product(CHUNK_SIZE, CHUNK_NUMBER, A0, A1, p);
|
||||
var A0_A2[2][150] = find_Fp2_product(CHUNK_SIZE, CHUNK_NUMBER, A0, A2, p);
|
||||
var A1_A2[2][150] = find_Fp2_product(CHUNK_SIZE, CHUNK_NUMBER, A1, A2, p);
|
||||
var A0_A1_A2[2][150] = find_Fp2_product(CHUNK_SIZE, CHUNK_NUMBER, A0_A1, A2, p);
|
||||
|
||||
var V3[2][150]; // v^3 = 1 + u
|
||||
for (var i = 0; i < 2; i++) {
|
||||
for (var j = 0; j < CHUNK_NUMBER; j++) {
|
||||
if (j == 0) {
|
||||
V3[i][j] = 1;
|
||||
} else {
|
||||
V3[i][j] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var THREE_V3[2][150]; // 3v^3 = 3 + 3u
|
||||
for (var i = 0; i < 2; i++) {
|
||||
for (var j = 0; j < CHUNK_NUMBER; j++) {
|
||||
if (j == 0) {
|
||||
THREE_V3[i][j] = 3;
|
||||
} else {
|
||||
THREE_V3[i][j] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var V6[2][150]; // v^6 = 2u
|
||||
for (var i = 0; i < 2; i++) {
|
||||
for (var j = 0; j < CHUNK_NUMBER; j++) {
|
||||
if (i == 1 && j == 0) {
|
||||
V6[i][j] = 2;
|
||||
} else {
|
||||
V6[i][j] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var V0_1[2][150] = find_Fp2_product(CHUNK_SIZE, CHUNK_NUMBER, A1_A2, V3, p);
|
||||
var V0_TEMP[2][150] = find_Fp2_diff(CHUNK_SIZE, CHUNK_NUMBER, A0_SQUARED, V0_1, p); // A0^2 - a1a2v^3
|
||||
var V1_1[2][150] = find_Fp2_product(CHUNK_SIZE, CHUNK_NUMBER, A2_SQUARED, V3, p);
|
||||
var V1_TEMP[2][150] = find_Fp2_diff(CHUNK_SIZE, CHUNK_NUMBER, V1_1, A0_A1, p); // v^3a2^2 - A0_A1
|
||||
var V2_TEMP[2][150] = find_Fp2_diff(CHUNK_SIZE, CHUNK_NUMBER, A1_SQUARED, A0_A2, p); // A1^2 - A0_A2
|
||||
|
||||
var A0_CUBED[2][150] = find_Fp2_product(CHUNK_SIZE, CHUNK_NUMBER, A0, A0_SQUARED, p);
|
||||
var A1_CUBED[2][150] = find_Fp2_product(CHUNK_SIZE, CHUNK_NUMBER, A1, A1_SQUARED, p);
|
||||
var A2_CUBED[2][150] = find_Fp2_product(CHUNK_SIZE, CHUNK_NUMBER, A2, A2_SQUARED, p);
|
||||
var A1_3V3[2][150] = find_Fp2_product(CHUNK_SIZE, CHUNK_NUMBER, A1_CUBED, V3, p);
|
||||
var A2_3V6[2][150] = find_Fp2_product(CHUNK_SIZE, CHUNK_NUMBER, A2_CUBED, V6, p);
|
||||
var A0_A1_A2_3V3[2][150] = find_Fp2_product(CHUNK_SIZE, CHUNK_NUMBER, A0_A1_A2, THREE_V3, p);
|
||||
|
||||
var DENOM_1[2][150] = find_Fp2_sum(CHUNK_SIZE, CHUNK_NUMBER, A0_CUBED, A1_3V3, p);
|
||||
var DENOM_2[2][150] = find_Fp2_diff(CHUNK_SIZE, CHUNK_NUMBER, A2_3V6, A0_A1_A2_3V3, p);
|
||||
var DENOM[2][150] = find_Fp2_sum(CHUNK_SIZE, CHUNK_NUMBER, DENOM_1, DENOM_2, p); // A0^3 + A1^3v^3 + A2^3v^6 - 3a0a1a2v^3
|
||||
|
||||
var DENOM_INV[2][150] = find_Fp2_inverse(CHUNK_SIZE, CHUNK_NUMBER, DENOM, p);
|
||||
|
||||
var V0_FINAL[2][150] = find_Fp2_product(CHUNK_SIZE, CHUNK_NUMBER, V0_TEMP, DENOM_INV, p);
|
||||
var V1_FINAL[2][150] = find_Fp2_product(CHUNK_SIZE, CHUNK_NUMBER, V1_TEMP, DENOM_INV, p);
|
||||
var V2_FINAL[2][150] = find_Fp2_product(CHUNK_SIZE, CHUNK_NUMBER, V2_TEMP, DENOM_INV, p);
|
||||
|
||||
for (var i = 1; i < 6; i = i + 2) {
|
||||
for (var j = 0; j < 2; j++) {
|
||||
for (var m = 0; m < 150; m++) {
|
||||
if (i > 1){
|
||||
out[i][j][m] = 0;
|
||||
} else {
|
||||
out[i][j][m] = 0;//V3[j][m];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
out[0] = V0_FINAL;
|
||||
out[2] = V1_FINAL;
|
||||
out[4] = V2_FINAL;
|
||||
return out;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,605 @@
|
||||
pragma circom 2.0.3;
|
||||
|
||||
include "../../../bigInt/bigInt.circom";
|
||||
include "../../../bigInt/bigIntFunc.circom";
|
||||
include "fieldElementsFunc.circom";
|
||||
include "fp.circom";
|
||||
|
||||
// add two elements in Fp2
|
||||
template Fp2Add(CHUNK_SIZE, CHUNK_NUMBER, p) {
|
||||
signal input a[2][CHUNK_NUMBER];
|
||||
signal input b[2][CHUNK_NUMBER];
|
||||
signal output out[2][CHUNK_NUMBER];
|
||||
|
||||
component adders[2];
|
||||
for (var i = 0; i < 2; i++) {
|
||||
adders[i] = FpAdd(CHUNK_SIZE, CHUNK_NUMBER, p);
|
||||
for (var j = 0; j < CHUNK_NUMBER; j++) {
|
||||
adders[i].a[j] <== a[i][j];
|
||||
adders[i].b[j] <== b[i][j];
|
||||
}
|
||||
for (var j = 0; j < CHUNK_NUMBER; j ++) {
|
||||
out[i][j] <== adders[i].out[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
p has CHUNK_NUMBER registers
|
||||
Inputs:
|
||||
- a[2][ka] allow signed overflow
|
||||
- b[2][kb]
|
||||
Outputs:
|
||||
- out[2][ka + kb - 1] such that
|
||||
(a0 + a1 u)*(b0 + b1 u) = out[0] + out[1] u
|
||||
Notes:
|
||||
- if each a[i][j], b[i][j] has abs value < B then out[i][j] has abs val < 2 * CHUNK_NUMBER*B^2
|
||||
- out[i] has ka + kb - 1 registers since that's output of BigMultShortLong
|
||||
M_OUT is the expected max number of bits in the output registers
|
||||
*/
|
||||
template SignedFp2MultiplyNoCarryUnequal(CHUNK_SIZE, ka, kb, M_OUT){
|
||||
signal input a[2][ka];
|
||||
signal input b[2][kb];
|
||||
signal output out[2][ka + kb - 1];
|
||||
|
||||
component ab[2][2];
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var j = 0; j < 2; j++){
|
||||
ab[i][j] = BigMultShortLongUnequal(CHUNK_SIZE, ka, kb, M_OUT); // output has ka + kb - 1 registers
|
||||
for(var l = 0; l < ka; l++){
|
||||
ab[i][j].a[l] <== a[i][l];
|
||||
}
|
||||
for(var l = 0; l < kb; l++){
|
||||
ab[i][j].b[l] <== b[j][l];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(var j = 0; j < ka + kb - 1; j++){
|
||||
out[0][j] <== ab[0][0].out[j] - ab[1][1].out[j];
|
||||
out[1][j] <== ab[0][1].out[j] + ab[1][0].out[j];
|
||||
}
|
||||
}
|
||||
|
||||
template SignedFp2MultiplyNoCarry(CHUNK_SIZE, CHUNK_NUMBER, M_OUT){
|
||||
signal input a[2][CHUNK_NUMBER];
|
||||
signal input b[2][CHUNK_NUMBER];
|
||||
signal output out[2][2 * CHUNK_NUMBER - 1];
|
||||
|
||||
component mult = SignedFp2MultiplyNoCarryUnequal(CHUNK_SIZE, CHUNK_NUMBER, CHUNK_NUMBER, M_OUT);
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var j = 0; j < CHUNK_NUMBER; j++){
|
||||
mult.a[i][j] <== a[i][j];
|
||||
mult.b[i][j] <== b[i][j];
|
||||
}
|
||||
}
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var j = 0; j < 2 * CHUNK_NUMBER - 1; j++){
|
||||
out[i][j] <== mult.out[i][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// input is 2 x (CHUNK_NUMBER+m) with registers in (-B,B)
|
||||
// in[0] + in[1] u
|
||||
// output is congruent to input (mod p) and represented as 2 x CHUNK_NUMBER where registers have abs val < (m + 1)*2^CHUNK_SIZE*B
|
||||
// M_OUT is the expected max number of bits in the output registers
|
||||
template Fp2Compress(CHUNK_SIZE, CHUNK_NUMBER, m, p, M_OUT){
|
||||
signal input in[2][CHUNK_NUMBER+m];
|
||||
signal output out[2][CHUNK_NUMBER];
|
||||
|
||||
component c[2];
|
||||
for(var i = 0; i < 2; i++){
|
||||
c[i] = PrimeReduce(CHUNK_SIZE, CHUNK_NUMBER, m, p, M_OUT);
|
||||
for(var j = 0; j < CHUNK_NUMBER+m; j++)
|
||||
c[i].in[j] <== in[i][j];
|
||||
}
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var j = 0; j < CHUNK_NUMBER; j++){
|
||||
out[i][j] <== c[i].out[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
// same input as above
|
||||
// outputs:
|
||||
// out[2][CHUNK_NUMBER] such that
|
||||
// out[i] has CHUNK_NUMBER registers because we use the "prime trick" to compress from 2 * CHUNK_NUMBER - 1 to CHUNK_NUMBER registers
|
||||
// if each a[i][j] is in (-B, B) then out[i][j] has abs val < 2k^2 * 2^CHUNK_SIZE*B^2
|
||||
// 2k*B^2 from SignedFp2MultiplyNoCarry
|
||||
// *CHUNK_NUMBER*2^CHUNK_SIZE from prime trick
|
||||
// m_in is the expected max number of bits in the input registers (necessary for some intermediate overflow validation)
|
||||
// M_OUT is the expected max number of bits in the output registers
|
||||
template SignedFp2MultiplyNoCarryCompress(CHUNK_SIZE, CHUNK_NUMBER, p, m_in, M_OUT){
|
||||
signal input a[2][CHUNK_NUMBER];
|
||||
signal input b[2][CHUNK_NUMBER];
|
||||
signal output out[2][CHUNK_NUMBER];
|
||||
|
||||
var LOGK1 = log_ceil(2 * CHUNK_NUMBER);
|
||||
component ab = SignedFp2MultiplyNoCarry(CHUNK_SIZE, CHUNK_NUMBER, 2 * m_in + LOGK1);
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
ab.a[i][idx] <== a[i][idx];
|
||||
ab.b[i][idx] <== b[i][idx];
|
||||
}
|
||||
}
|
||||
|
||||
var LOGK2 = log_ceil(2 * CHUNK_NUMBER*CHUNK_NUMBER);
|
||||
component compress = Fp2Compress(CHUNK_SIZE, CHUNK_NUMBER, CHUNK_NUMBER - 1, p, 2 * m_in + CHUNK_SIZE + LOGK2);
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var j = 0; j < 2 * CHUNK_NUMBER - 1; j++){
|
||||
compress.in[i][j] <== ab.out[i][j];
|
||||
}
|
||||
}
|
||||
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var j = 0; j < CHUNK_NUMBER; j++){
|
||||
out[i][j] <== compress.out[i][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template SignedFp2MultiplyNoCarryCompressThree(CHUNK_SIZE, CHUNK_NUMBER, p, m_in, M_OUT){
|
||||
signal input a[2][CHUNK_NUMBER];
|
||||
signal input b[2][CHUNK_NUMBER];
|
||||
signal input c[2][CHUNK_NUMBER];
|
||||
signal output out[2][CHUNK_NUMBER];
|
||||
|
||||
var LOGK = log_ceil(CHUNK_NUMBER);
|
||||
component ab = SignedFp2MultiplyNoCarry(CHUNK_SIZE, CHUNK_NUMBER, 2 * m_in + LOGK + 1);
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
ab.a[i][idx] <== a[i][idx];
|
||||
ab.b[i][idx] <== b[i][idx];
|
||||
}
|
||||
}
|
||||
|
||||
component abc = SignedFp2MultiplyNoCarryUnequal(CHUNK_SIZE, 2 * CHUNK_NUMBER - 1, CHUNK_NUMBER, 3 * m_in + 2 * LOGK + 2);
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var idx = 0; idx < 2 * CHUNK_NUMBER - 1; idx++){
|
||||
abc.a[i][idx] <== ab.out[i][idx];
|
||||
}
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
abc.b[i][idx] <== c[i][idx];
|
||||
}
|
||||
}
|
||||
|
||||
component compress = Fp2Compress(CHUNK_SIZE, CHUNK_NUMBER, 2 * CHUNK_NUMBER - 2, p, 3 * m_in + CHUNK_SIZE + 3 * LOGK + 3);
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var j = 0; j < 3 * CHUNK_NUMBER - 2; j++){
|
||||
compress.in[i][j] <== abc.out[i][j];
|
||||
}
|
||||
}
|
||||
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var j = 0; j < CHUNK_NUMBER; j++){
|
||||
out[i][j] <== compress.out[i][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check if in[0], in[1] both have CHUNK_NUMBER registers in [0,2^CHUNK_SIZE)
|
||||
// to save constraints, DO NOT CONSTRAIN in[i] < p
|
||||
template RangeCheck2D(CHUNK_SIZE, CHUNK_NUMBER){
|
||||
signal input in[2][CHUNK_NUMBER];
|
||||
component rangeChecks[2][CHUNK_NUMBER];
|
||||
//component lessThan[2];
|
||||
|
||||
for(var eps = 0; eps < 2; eps++){
|
||||
//lessThan[eps] = BigLessThan(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++){
|
||||
rangeChecks[eps][i] = Num2Bits(CHUNK_SIZE);
|
||||
rangeChecks[eps][i].in <== in[eps][i];
|
||||
//lessThan[eps].a[i] <== in[eps][i];
|
||||
//lessThan[eps].b[i] <== p[i];
|
||||
}
|
||||
//lessThan[eps].out === 1;
|
||||
}
|
||||
}
|
||||
|
||||
// solve for in = p * X + out
|
||||
// X has registers lying in [ - 2^CHUNK_SIZE, 2^CHUNK_SIZE)
|
||||
// X has at most Ceil(overflow / CHUNK_SIZE) registers
|
||||
// assume in has registers in (- 2^overflow, 2^overflow)
|
||||
template SignedFp2CarryModP(CHUNK_SIZE, CHUNK_NUMBER, overflow, p){
|
||||
signal input in[2][CHUNK_NUMBER];
|
||||
var m = (overflow + CHUNK_SIZE - 1) \ CHUNK_SIZE;
|
||||
signal output X[2][m];
|
||||
signal output out[2][CHUNK_NUMBER];
|
||||
|
||||
assert(overflow < 251);
|
||||
|
||||
component carry[2];
|
||||
for(var i = 0; i < 2; i++){
|
||||
carry[i] = SignedFpCarryModP(CHUNK_SIZE, CHUNK_NUMBER, overflow, p);
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
carry[i].in[idx] <== in[i][idx];
|
||||
}
|
||||
for(var idx = 0; idx<m; idx++){
|
||||
X[i][idx] <== carry[i].X[idx];
|
||||
}
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
out[i][idx] <== carry[i].out[idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// input is 2 x (CHUNK_NUMBER+m) with registers in (- 2^overflow, 2^overflow)
|
||||
// in[0] + in[1] u
|
||||
// calls Fp2Compress and SignedFp2CarryModP
|
||||
template SignedFp2CompressCarry(CHUNK_SIZE, CHUNK_NUMBER, m, overflow, p){
|
||||
signal input in[2][CHUNK_NUMBER+m];
|
||||
signal output out[2][CHUNK_NUMBER];
|
||||
|
||||
var LOGM = log_ceil(m + 1);
|
||||
component compress = Fp2Compress(CHUNK_SIZE, CHUNK_NUMBER, m, p, overflow + CHUNK_SIZE + LOGM);
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER+m; idx++){
|
||||
compress.in[i][idx] <== in[i][idx];
|
||||
}
|
||||
}
|
||||
|
||||
component carry = SignedFp2CarryModP(CHUNK_SIZE, CHUNK_NUMBER, overflow + CHUNK_SIZE + LOGM, p);
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
carry.in[i][idx] <== compress.out[i][idx];
|
||||
}
|
||||
}
|
||||
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
out[i][idx] <== carry.out[i][idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// outputs a*b in Fp2
|
||||
// (a0 + a1 u)*(b0 + b1 u) = (a0*b0 - a1*b1) + (a0*b1 + a1*b0)u
|
||||
// out[i] has CHUNK_NUMBER registers each in [0, 2^CHUNK_SIZE)
|
||||
// out[i] in [0, p)
|
||||
template Fp2Multiply(CHUNK_SIZE, CHUNK_NUMBER, p){
|
||||
signal input a[2][CHUNK_NUMBER];
|
||||
signal input b[2][CHUNK_NUMBER];
|
||||
signal output out[2][CHUNK_NUMBER];
|
||||
|
||||
var LOGK2 = log_ceil(2 * CHUNK_NUMBER*CHUNK_NUMBER);
|
||||
assert(3 * CHUNK_SIZE + LOGK2 < 251);
|
||||
|
||||
component c = SignedFp2MultiplyNoCarryCompress(CHUNK_SIZE, CHUNK_NUMBER, p, CHUNK_SIZE, 3 * CHUNK_SIZE + LOGK2);
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++){
|
||||
c.a[0][i] <== a[0][i];
|
||||
c.a[1][i] <== a[1][i];
|
||||
c.b[0][i] <== b[0][i];
|
||||
c.b[1][i] <== b[1][i];
|
||||
}
|
||||
|
||||
component carryMod = SignedFp2CarryModP(CHUNK_SIZE, CHUNK_NUMBER, 3 * CHUNK_SIZE + LOGK2, p);
|
||||
for(var i = 0; i < 2; i++)for(var j = 0; j < CHUNK_NUMBER; j++){
|
||||
carryMod.in[i][j] <== c.out[i][j];
|
||||
}
|
||||
for(var i = 0; i < 2; i++)for(var j = 0; j < CHUNK_NUMBER; j++){
|
||||
out[i][j] <== carryMod.out[i][j];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template Fp2MultiplyThree(CHUNK_SIZE, CHUNK_NUMBER, p){
|
||||
signal input a[2][CHUNK_NUMBER];
|
||||
signal input b[2][CHUNK_NUMBER];
|
||||
signal input c[2][CHUNK_NUMBER];
|
||||
signal output out[2][CHUNK_NUMBER];
|
||||
|
||||
var LOGK3 = log_ceil(4 * CHUNK_NUMBER*CHUNK_NUMBER*(2 * CHUNK_NUMBER - 1));
|
||||
assert(4 * CHUNK_SIZE + LOGK3 < 251);
|
||||
|
||||
component compress = SignedFp2MultiplyNoCarryCompressThree(CHUNK_SIZE, CHUNK_NUMBER, p, CHUNK_SIZE, 4 * CHUNK_SIZE + LOGK3);
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
compress.a[i][idx] <== a[i][idx];
|
||||
compress.b[i][idx] <== b[i][idx];
|
||||
compress.c[i][idx] <== c[i][idx];
|
||||
}
|
||||
}
|
||||
|
||||
component carryMod = SignedFp2CarryModP(CHUNK_SIZE, CHUNK_NUMBER, 4 * CHUNK_SIZE + LOGK3, p);
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var j = 0; j < CHUNK_NUMBER; j++){
|
||||
carryMod.in[i][j] <== compress.out[i][j];
|
||||
}
|
||||
}
|
||||
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var j = 0; j < CHUNK_NUMBER; j++){
|
||||
out[i][j] <== carryMod.out[i][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// input: in[0] + in[1] u
|
||||
// output: (p-in[0]) + (p-in[1]) u
|
||||
// assume 0 <= in < p
|
||||
template Fp2Negate(CHUNK_SIZE, CHUNK_NUMBER, p){
|
||||
signal input in[2][CHUNK_NUMBER];
|
||||
signal output out[2][CHUNK_NUMBER];
|
||||
|
||||
component neg[2];
|
||||
for(var j = 0; j < 2; j++){
|
||||
neg[j] = FpNegate(CHUNK_SIZE, CHUNK_NUMBER, p);
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++){
|
||||
neg[j].in[i] <== in[j][i];
|
||||
}
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++){
|
||||
out[j][i] <== neg[j].out[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// input: a0 + a1 u, b0 + b1 u
|
||||
// output: (a0-b0) + (a1-b1)u
|
||||
template Fp2Subtract(CHUNK_SIZE, CHUNK_NUMBER, p){
|
||||
signal input a[2][CHUNK_NUMBER];
|
||||
signal input b[2][CHUNK_NUMBER];
|
||||
signal output out[2][CHUNK_NUMBER];
|
||||
|
||||
component sub0 = FpSubtract(CHUNK_SIZE, CHUNK_NUMBER, p);
|
||||
component sub1 = FpSubtract(CHUNK_SIZE, CHUNK_NUMBER, p);
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++){
|
||||
sub0.a[i] <== a[0][i];
|
||||
sub0.b[i] <== b[0][i];
|
||||
sub1.a[i] <== a[1][i];
|
||||
sub1.b[i] <== b[1][i];
|
||||
}
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++){
|
||||
out[0][i] <== sub0.out[i];
|
||||
out[1][i] <== sub1.out[i];
|
||||
}
|
||||
}
|
||||
|
||||
// Call find_Fp2_inverse to compute inverse
|
||||
// Then check out * in = 1, out is an array of shorts
|
||||
template Fp2Invert(CHUNK_SIZE, CHUNK_NUMBER, p){
|
||||
signal input in[2][CHUNK_NUMBER];
|
||||
signal output out[2][CHUNK_NUMBER];
|
||||
|
||||
var inverse[2][150] = find_Fp2_inverse(CHUNK_SIZE, CHUNK_NUMBER, in, p); // 2 x 150, only 2 x CHUNK_NUMBER relevant
|
||||
for (var i = 0; i < 2; i ++) {
|
||||
for (var j = 0; j < CHUNK_NUMBER; j ++) {
|
||||
out[i][j] <-- inverse[i][j];
|
||||
}
|
||||
}
|
||||
|
||||
//range checks
|
||||
component outRangeChecks[2][CHUNK_NUMBER];
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var j = 0; j < CHUNK_NUMBER; j++){
|
||||
outRangeChecks[i][j] = Num2Bits(CHUNK_SIZE);
|
||||
outRangeChecks[i][j].in <== out[i][j];
|
||||
}
|
||||
}
|
||||
|
||||
component inOut = Fp2Multiply(CHUNK_SIZE, CHUNK_NUMBER, p);
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var j = 0; j < CHUNK_NUMBER; j++){
|
||||
inOut.a[i][j] <== in[i][j];
|
||||
inOut.b[i][j] <== out[i][j];
|
||||
}
|
||||
}
|
||||
|
||||
for(var i = 0; i < 2; i++){
|
||||
for(var j = 0; j < CHUNK_NUMBER; j++){
|
||||
if(i == 0 && j == 0){
|
||||
inOut.out[i][j] === 1;
|
||||
} else {
|
||||
inOut.out[i][j] === 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// a, b are two elements of Fp2 where we use the 2 x CHUNK_NUMBER format
|
||||
// solve for out * b - a = p * X
|
||||
// assume X has at most m registers, lying in [ - 2^{CHUNK_SIZE + 1}, 2^{CHUNK_SIZE + 1}) NOTE CHUNK_SIZE + 1 not CHUNK_SIZE DIFFERENT FROM Fp2CarryModP
|
||||
// out has registers in [0, 2^CHUNK_SIZE)
|
||||
template SignedFp2Divide(CHUNK_SIZE, CHUNK_NUMBER, OVERFLOW_A, OVERFLOW_B, p){
|
||||
signal input a[2][CHUNK_NUMBER];
|
||||
signal input b[2][CHUNK_NUMBER];
|
||||
signal output out[2][CHUNK_NUMBER];
|
||||
|
||||
var ma = OVERFLOW_A \ CHUNK_SIZE;
|
||||
var mb = OVERFLOW_B \ CHUNK_SIZE;
|
||||
// first precompute a, b mod p as shorts
|
||||
var A_MOD[2][150];
|
||||
var B_MOD[2][150];
|
||||
for(var eps = 0; eps < 2; eps++){
|
||||
// 2^{overflow} <= 2^{CHUNK_SIZE*ceil(overflow/CHUNK_SIZE)}
|
||||
var temp[2][150] = get_signed_Fp_carry_witness(CHUNK_SIZE, CHUNK_NUMBER, ma, a[eps], p);
|
||||
A_MOD[eps] = temp[1];
|
||||
temp = get_signed_Fp_carry_witness(CHUNK_SIZE, CHUNK_NUMBER, mb, b[eps], p);
|
||||
B_MOD[eps] = temp[1];
|
||||
}
|
||||
|
||||
// precompute 1/b
|
||||
var B_INV[2][150] = find_Fp2_inverse(CHUNK_SIZE, CHUNK_NUMBER, B_MOD, p);
|
||||
// precompute a/b
|
||||
var OUT_VAR[2][150] = find_Fp2_product(CHUNK_SIZE, CHUNK_NUMBER, A_MOD, B_INV, p);
|
||||
|
||||
for(var eps = 0; eps < 2; eps++)for(var i = 0; i < CHUNK_NUMBER; i++){
|
||||
out[eps][i] <-- OUT_VAR[eps][i];
|
||||
}
|
||||
|
||||
component check = RangeCheck2D(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
for(var eps = 0; eps < 2; eps++)for(var i = 0; i < CHUNK_NUMBER; i++){
|
||||
check.in[eps][i] <== out[eps][i];
|
||||
}
|
||||
|
||||
// constraint is out * b = a + p * X
|
||||
// precompute out * b = p * X' + Y' and a = p * X'' + Y''
|
||||
// should have Y' = Y'' so X = X' - X''
|
||||
|
||||
var LOGK2 = log_ceil(2 * CHUNK_NUMBER*CHUNK_NUMBER);
|
||||
// out * b, registers overflow in 2 * CHUNK_NUMBER*CHUNK_NUMBER * 2^{2n + OVERFLOW_B}
|
||||
component mult = SignedFp2MultiplyNoCarryCompress(CHUNK_SIZE, CHUNK_NUMBER, p, max(CHUNK_SIZE, OVERFLOW_B), 2 * CHUNK_SIZE + OVERFLOW_B + LOGK2);
|
||||
for(var eps = 0; eps < 2; eps++)for(var i = 0; i < CHUNK_NUMBER; i++){
|
||||
mult.a[eps][i] <== out[eps][i];
|
||||
mult.b[eps][i] <== b[eps][i];
|
||||
}
|
||||
|
||||
var m = max(mb + CHUNK_NUMBER, ma);
|
||||
// get mult = out * b = p*X' + Y'
|
||||
var XY[2][2][150] = get_signed_Fp2_carry_witness(CHUNK_SIZE, CHUNK_NUMBER, m, mult.out, p); // total value is < 2^{nk} * 2^{CHUNK_SIZE*CHUNK_NUMBER + OVERFLOW_B - CHUNK_SIZE + 1}
|
||||
// get a = p*X' + Y'
|
||||
var XY1[2][2][150] = get_signed_Fp2_carry_witness(CHUNK_SIZE, CHUNK_NUMBER, m, a, p); // same as above, m extra registers enough
|
||||
|
||||
signal x[2][m];
|
||||
component xRangeChecks[2][m];
|
||||
for(var eps = 0; eps < 2; eps++){
|
||||
for(var i = 0; i<m; i++){
|
||||
// x'' = x-x'
|
||||
x[eps][i] <-- XY[eps][0][i] - XY1[eps][0][i]; // each XY[eps][0] is in [ - 2^CHUNK_SIZE, 2^CHUNK_SIZE) so difference is in [ - 2^{CHUNK_SIZE + 1}, 2^{CHUNK_SIZE + 1})
|
||||
xRangeChecks[eps][i] = Num2Bits(CHUNK_SIZE+2);
|
||||
xRangeChecks[eps][i].in <== x[eps][i] + (1<<(CHUNK_SIZE + 1)); // x[eps][i] should be between [ - 2^{CHUNK_SIZE + 1}, 2^{CHUNK_SIZE + 1})
|
||||
}
|
||||
}
|
||||
|
||||
var overflow = max(2 * CHUNK_SIZE + OVERFLOW_B + LOGK2, OVERFLOW_A);
|
||||
// finally constrain out * b - a = p * x
|
||||
// out * b - a has overflow in (- 2^{overflow + 1}, 2^{overflow + 1})
|
||||
component modCheck[2];
|
||||
for(var eps = 0; eps < 2; eps++){
|
||||
modCheck[eps] = CheckCarryModP(CHUNK_SIZE, CHUNK_NUMBER, m, overflow + 1, p);
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++){
|
||||
modCheck[eps].in[i] <== mult.out[eps][i] - a[eps][i];
|
||||
modCheck[eps].Y[i] <== 0;
|
||||
}
|
||||
for(var i = 0; i<m; i++){
|
||||
modCheck[eps].X[i] <== x[eps][i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// input: a+b u
|
||||
// output: a-b u
|
||||
// IF p = 3 mod 4 THEN a - b u = (a+b u)^p <-- Frobenius map
|
||||
// aka Fp2FrobeniusMap(CHUNK_SIZE, CHUNK_NUMBER)
|
||||
template Fp2Conjugate(CHUNK_SIZE, CHUNK_NUMBER, p){
|
||||
signal input in[2][CHUNK_NUMBER];
|
||||
signal output out[2][CHUNK_NUMBER];
|
||||
|
||||
component neg1 = FpNegate(CHUNK_SIZE, CHUNK_NUMBER, p);
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++){
|
||||
neg1.in[i] <== in[1][i];
|
||||
}
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++){
|
||||
out[0][i] <== in[0][i];
|
||||
out[1][i] <== neg1.out[i];
|
||||
}
|
||||
}
|
||||
|
||||
// raises to q^power-th power
|
||||
template Fp2FrobeniusMap(CHUNK_SIZE, CHUNK_NUMBER, power, p){
|
||||
signal input in[2][CHUNK_NUMBER];
|
||||
signal output out[2][CHUNK_NUMBER];
|
||||
|
||||
var pow = power % 2;
|
||||
component neg1 = FpNegate(CHUNK_SIZE,CHUNK_NUMBER,p);
|
||||
if(pow == 0){
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++){
|
||||
out[0][i] <== in[0][i];
|
||||
out[1][i] <== in[1][i];
|
||||
}
|
||||
}else{
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++){
|
||||
neg1.in[i] <== in[1][i];
|
||||
}
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++){
|
||||
out[0][i] <== in[0][i];
|
||||
out[1][i] <== neg1.out[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// in = in0 + in1 * u, elt of Fp2
|
||||
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-4.1
|
||||
// NOTE: different from Wahby-Boneh paper https://eprint.iacr.org/2019/403.pdf and python reference code: https://github.com/algorand/bls_sigs_ref/blob/master/python-impl/opt_swu_g2.py
|
||||
template Fp2Sgn0(CHUNK_SIZE, CHUNK_NUMBER, p){
|
||||
signal input in[2][CHUNK_NUMBER];
|
||||
signal output out;
|
||||
|
||||
component sgn[2];
|
||||
for(var i = 0; i < 2; i++){
|
||||
sgn[i] = FpSgn0(CHUNK_SIZE, CHUNK_NUMBER, p);
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
sgn[i].in[idx] <== in[i][idx];
|
||||
}
|
||||
}
|
||||
component isZero = BigIsZero(CHUNK_NUMBER);
|
||||
for(var idx = 0; idx < CHUNK_NUMBER; idx++){
|
||||
isZero.in[idx] <== in[0][idx];
|
||||
}
|
||||
|
||||
signal sgn1; // (in0 == 0) && (sgn[1])
|
||||
sgn1 <== isZero.out * sgn[1].out;
|
||||
out <== sgn[0].out + sgn1 - sgn[0].out * sgn1; // sgn[0] || ((in0 == 0 && sgn[1]))
|
||||
}
|
||||
|
||||
template Fp2IsZero(CHUNK_SIZE, CHUNK_NUMBER, p){
|
||||
signal input in[2][CHUNK_NUMBER];
|
||||
signal output out;
|
||||
|
||||
// check that in[i] < p
|
||||
component lessThan[2];
|
||||
|
||||
component isZeros[2][CHUNK_NUMBER];
|
||||
var total = 2 * CHUNK_NUMBER;
|
||||
for(var j = 0; j < 2; j++){
|
||||
lessThan[j] = BigLessThan(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
for(var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
lessThan[j].a[i] <== in[j][i];
|
||||
lessThan[j].b[i] <== p[i];
|
||||
|
||||
isZeros[j][i] = IsZero();
|
||||
isZeros[j][i].in <== in[j][i];
|
||||
total -= isZeros[j][i].out;
|
||||
}
|
||||
lessThan[j].out === 1;
|
||||
}
|
||||
component checkZero = IsZero();
|
||||
checkZero.in <== total;
|
||||
out <== checkZero.out;
|
||||
}
|
||||
|
||||
template Fp2IsEqual(CHUNK_SIZE, CHUNK_NUMBER, p){
|
||||
signal input a[2][CHUNK_NUMBER];
|
||||
signal input b[2][CHUNK_NUMBER];
|
||||
signal output out;
|
||||
|
||||
// check that a[i], b[i] < p
|
||||
component lessThanA[2];
|
||||
component lessThanB[2];
|
||||
component isEquals[2][CHUNK_NUMBER];
|
||||
var total = 2 * CHUNK_NUMBER;
|
||||
for(var j = 0; j < 2; j++){
|
||||
lessThanA[j] = BigLessThan(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
lessThanB[j] = BigLessThan(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
for (var i = 0; i < CHUNK_NUMBER; i ++) {
|
||||
lessThanA[j].a[i] <== a[j][i];
|
||||
lessThanA[j].b[i] <== p[i];
|
||||
|
||||
lessThanB[j].a[i] <== b[j][i];
|
||||
lessThanB[j].b[i] <== p[i];
|
||||
|
||||
isEquals[j][i] = IsEqual();
|
||||
isEquals[j][i].in[0] <== a[j][i];
|
||||
isEquals[j][i].in[1] <== b[j][i];
|
||||
total -= isEquals[j][i].out;
|
||||
}
|
||||
lessThanA[j].out === 1;
|
||||
lessThanB[j].out === 1;
|
||||
}
|
||||
component checkZero = IsZero();
|
||||
checkZero.in <== total;
|
||||
out <== checkZero.out;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,127 @@
|
||||
pragma circom 2.1.6;
|
||||
|
||||
include "./circomPairing/curve.circom";
|
||||
include "circomlib/circuits/bitify.circom";
|
||||
include "./brainpool.circom";
|
||||
include "./brainpoolFunc.circom";
|
||||
|
||||
|
||||
template verifyBrainpool(CHUNK_SIZE,CHUNK_NUMBER, ALGO){
|
||||
signal input pubkey[2 * CHUNK_SIZE * CHUNK_NUMBER];
|
||||
signal input signature[2 * CHUNK_SIZE * CHUNK_NUMBER];
|
||||
signal input hashed[ALGO];
|
||||
|
||||
signal pubkeyChunked[2][6];
|
||||
signal signatureChunked[2][6];
|
||||
|
||||
signal pubkeyBits[2][258];
|
||||
signal signatureBits[2][258];
|
||||
pubkeyBits[0][0] <== 0;
|
||||
pubkeyBits[0][1] <== 0;
|
||||
pubkeyBits[1][0] <== 0;
|
||||
pubkeyBits[1][1] <== 0;
|
||||
signatureBits[0][0] <== 0;
|
||||
signatureBits[0][1] <== 0;
|
||||
signatureBits[1][0] <== 0;
|
||||
signatureBits[1][1] <== 0;
|
||||
|
||||
for (var i = 0; i < 2; i++){
|
||||
for (var j = 0; j < 256; j++){
|
||||
pubkeyBits[i][j+2] <== pubkey[i*256 + j];
|
||||
signatureBits[i][j+2] <== signature[i*256 +j];
|
||||
}
|
||||
}
|
||||
|
||||
component bits2NumInput[24];
|
||||
|
||||
for (var i = 0; i < 2; i++){
|
||||
for (var j = 0; j < 6; j++){
|
||||
bits2NumInput[i*6+j] = Bits2Num(43);
|
||||
bits2NumInput[(i+2)*6+j] = Bits2Num(43);
|
||||
|
||||
for (var z = 0; z < 43; z++){
|
||||
bits2NumInput[i*6+j].in[z] <== pubkeyBits[i][43 * j + 42 - z];
|
||||
bits2NumInput[(i+2)*6+j].in[z] <== signatureBits[i][43 * j + 42 - z];
|
||||
}
|
||||
bits2NumInput[i*6+j].out ==> pubkeyChunked[i][5-j];
|
||||
bits2NumInput[(i+2)*6+j].out ==> signatureChunked[i][5-j];
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
signal hashedMessageBits[258];
|
||||
hashedMessageBits[0] <== 0;
|
||||
hashedMessageBits[1] <== 0;
|
||||
for (var i = 0; i < ALGO; i++){
|
||||
hashedMessageBits[i+2] <== hashed[i];
|
||||
}
|
||||
|
||||
|
||||
signal hashedMessageChunked[6];
|
||||
|
||||
component bits2Num[6];
|
||||
for (var i = 0; i < 6; i++) {
|
||||
bits2Num[i] = Bits2Num(43);
|
||||
for (var j = 0; j < 43; j++) {
|
||||
bits2Num[i].in[43-1-j] <== hashedMessageBits[i*43+j];
|
||||
}
|
||||
hashedMessageChunked[6-1-i] <== bits2Num[i].out;
|
||||
}
|
||||
|
||||
component getOrder = GetBrainpoolOrder(43,6);
|
||||
signal order[6];
|
||||
order <== getOrder.order;
|
||||
|
||||
signal sinv[6];
|
||||
|
||||
component modInv = BigModInv(43,6);
|
||||
|
||||
modInv.in <== signatureChunked[1];
|
||||
modInv.p <== order;
|
||||
modInv.out ==> sinv;
|
||||
|
||||
signal sh[6];
|
||||
|
||||
component mult = BigMultModP(43, 6);
|
||||
|
||||
mult.a <== sinv;
|
||||
mult.b <== hashedMessageChunked;
|
||||
mult.p <== order;
|
||||
sh <== mult.out;
|
||||
|
||||
signal sr[6];
|
||||
|
||||
component mult2 = BigMultModP(43, 6);
|
||||
|
||||
mult2.a <== sinv;
|
||||
mult2.b <== signatureChunked[0];
|
||||
mult2.p <== order;
|
||||
sr <== mult2.out;
|
||||
|
||||
|
||||
signal tmpPoint1[2][6];
|
||||
signal tmpPoint2[2][6];
|
||||
|
||||
component scalarMult1 = BrainpoolGeneratorMultiplication(43,6);
|
||||
component scalarMult2 = BrainpoolPipingerMult(43,6,4);
|
||||
|
||||
scalarMult1.scalar <== sh;
|
||||
|
||||
tmpPoint1 <== scalarMult1.out;
|
||||
|
||||
scalarMult2.scalar <== sr;
|
||||
scalarMult2.point <== pubkeyChunked;
|
||||
|
||||
tmpPoint2 <== scalarMult2.out;
|
||||
|
||||
signal verifyX[6];
|
||||
|
||||
component sumPoints = BrainpoolAddUnequal(43,6);
|
||||
|
||||
sumPoints.point1 <== tmpPoint1;
|
||||
sumPoints.point2 <== tmpPoint2;
|
||||
verifyX <== sumPoints.out[0];
|
||||
|
||||
verifyX === signatureChunked[0];
|
||||
}
|
||||
@@ -0,0 +1,548 @@
|
||||
pragma circom 2.1.6;
|
||||
|
||||
include "../brainpoolP256r1/circomPairing/curve.circom";
|
||||
include "./brainpoolFunc.circom";
|
||||
include "./brainpoolPows.circom";
|
||||
include "circomlib/circuits/multiplexer.circom";
|
||||
include "circomlib/circuits/bitify.circom";
|
||||
include "circomlib/circuits/comparators.circom";
|
||||
include "../utils/func.circom";
|
||||
|
||||
template Brainpool320ScalarMult(CHUNK_SIZE, CHUNK_NUMBER){
|
||||
signal input scalar[CHUNK_NUMBER];
|
||||
signal input point[2][CHUNK_NUMBER];
|
||||
|
||||
signal output out[2][CHUNK_NUMBER];
|
||||
|
||||
component n2b[CHUNK_NUMBER];
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
n2b[i] = Num2Bits(CHUNK_SIZE);
|
||||
n2b[i].in <== scalar[i];
|
||||
}
|
||||
|
||||
// hasPrevNonZero[CHUNK_SIZE * i + j] == 1 if there is a nonzero bit in location [i][j] or higher order bit
|
||||
component hasPrevNonZero[CHUNK_NUMBER * CHUNK_SIZE];
|
||||
for (var i = CHUNK_NUMBER - 1; i >= 0; i--) {
|
||||
for (var j = CHUNK_SIZE - 1; j >= 0; j--) {
|
||||
hasPrevNonZero[CHUNK_SIZE * i + j] = OR();
|
||||
if (i == CHUNK_NUMBER - 1 && j == CHUNK_SIZE - 1) {
|
||||
hasPrevNonZero[CHUNK_SIZE * i + j].a <== 0;
|
||||
hasPrevNonZero[CHUNK_SIZE * i + j].b <== n2b[i].out[j];
|
||||
} else {
|
||||
hasPrevNonZero[CHUNK_SIZE * i + j].a <== hasPrevNonZero[CHUNK_SIZE * i + j + 1].out;
|
||||
hasPrevNonZero[CHUNK_SIZE * i + j].b <== n2b[i].out[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
signal partial[CHUNK_SIZE * CHUNK_NUMBER][2][CHUNK_NUMBER];
|
||||
signal intermed[CHUNK_SIZE * CHUNK_NUMBER - 1][2][CHUNK_NUMBER];
|
||||
component adders[CHUNK_SIZE * CHUNK_NUMBER - 1];
|
||||
component doublers[CHUNK_SIZE * CHUNK_NUMBER - 1];
|
||||
for (var i = CHUNK_NUMBER - 1; i >= 0; i--) {
|
||||
for (var j = CHUNK_SIZE - 1; j >= 0; j--) {
|
||||
if (i == CHUNK_NUMBER - 1 && j == CHUNK_SIZE - 1) {
|
||||
for (var idx = 0; idx < CHUNK_NUMBER; idx++) {
|
||||
partial[CHUNK_SIZE * i + j][0][idx] <== point[0][idx];
|
||||
partial[CHUNK_SIZE * i + j][1][idx] <== point[1][idx];
|
||||
}
|
||||
}
|
||||
if (i < CHUNK_NUMBER - 1 || j < CHUNK_SIZE - 1) {
|
||||
adders[CHUNK_SIZE * i + j] = Brainpool320AddUnequal(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
doublers[CHUNK_SIZE * i + j] = Brainpool320Double(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
for (var idx = 0; idx < CHUNK_NUMBER; idx++) {
|
||||
doublers[CHUNK_SIZE * i + j].in[0][idx] <== partial[CHUNK_SIZE * i + j + 1][0][idx];
|
||||
doublers[CHUNK_SIZE * i + j].in[1][idx] <== partial[CHUNK_SIZE * i + j + 1][1][idx];
|
||||
}
|
||||
for (var idx = 0; idx < CHUNK_NUMBER; idx++) {
|
||||
adders[CHUNK_SIZE * i + j].point1[0][idx] <== doublers[CHUNK_SIZE * i + j].out[0][idx];
|
||||
adders[CHUNK_SIZE * i + j].point1[1][idx] <== doublers[CHUNK_SIZE * i + j].out[1][idx];
|
||||
adders[CHUNK_SIZE * i + j].point2[0][idx] <== point[0][idx];
|
||||
adders[CHUNK_SIZE * i + j].point2[1][idx] <== point[1][idx];
|
||||
}
|
||||
// partial[CHUNK_SIZE * i + j]
|
||||
// = hasPrevNonZero[CHUNK_SIZE * i + j + 1] * ((1 - n2b[i].out[j]) * doublers[CHUNK_SIZE * i + j] + n2b[i].out[j] * adders[CHUNK_SIZE * i + j])
|
||||
// + (1 - hasPrevNonZero[CHUNK_SIZE * i + j + 1]) * point
|
||||
for (var idx = 0; idx < CHUNK_NUMBER; idx++) {
|
||||
intermed[CHUNK_SIZE * i + j][0][idx] <== n2b[i].out[j] * (adders[CHUNK_SIZE * i + j].out[0][idx] - doublers[CHUNK_SIZE * i + j].out[0][idx]) + doublers[CHUNK_SIZE * i + j].out[0][idx];
|
||||
intermed[CHUNK_SIZE * i + j][1][idx] <== n2b[i].out[j] * (adders[CHUNK_SIZE * i + j].out[1][idx] - doublers[CHUNK_SIZE * i + j].out[1][idx]) + doublers[CHUNK_SIZE * i + j].out[1][idx];
|
||||
partial[CHUNK_SIZE * i + j][0][idx] <== hasPrevNonZero[CHUNK_SIZE * i + j + 1].out * (intermed[CHUNK_SIZE * i + j][0][idx] - point[0][idx]) + point[0][idx];
|
||||
partial[CHUNK_SIZE * i + j][1][idx] <== hasPrevNonZero[CHUNK_SIZE * i + j + 1].out * (intermed[CHUNK_SIZE * i + j][1][idx] - point[1][idx]) + point[1][idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var idx = 0; idx < CHUNK_NUMBER; idx++) {
|
||||
out[0][idx] <== partial[0][0][idx];
|
||||
out[1][idx] <== partial[0][1][idx];
|
||||
}
|
||||
}
|
||||
|
||||
template Brainpool320AddUnequal(CHUNK_SIZE, CHUNK_NUMBER){
|
||||
signal input point1[2][CHUNK_NUMBER];
|
||||
signal input point2[2][CHUNK_NUMBER];
|
||||
signal output out[2][CHUNK_NUMBER];
|
||||
|
||||
var PARAMS[3][CHUNK_NUMBER] = get_320_params(CHUNK_SIZE,CHUNK_NUMBER);
|
||||
|
||||
component add = EllipticCurveAddUnequal(CHUNK_SIZE, CHUNK_NUMBER, PARAMS[2]);
|
||||
add.a <== point1;
|
||||
add.b <== point2;
|
||||
add.out ==> out;
|
||||
}
|
||||
|
||||
template Brainpool320Double(CHUNK_SIZE, CHUNK_NUMBER){
|
||||
signal input in[2][CHUNK_NUMBER];
|
||||
signal output out[2][CHUNK_NUMBER];
|
||||
|
||||
var PARAMS[3][CHUNK_NUMBER] = get_320_params(CHUNK_SIZE,CHUNK_NUMBER);
|
||||
|
||||
component doubling = EllipticCurveDouble(CHUNK_SIZE,CHUNK_NUMBER, PARAMS[0], PARAMS[1], PARAMS[2]);
|
||||
doubling.in <== in;
|
||||
doubling.out ==> out;
|
||||
}
|
||||
|
||||
template Brainpool320GetGenerator(CHUNK_SIZE, CHUNK_NUMBER){
|
||||
|
||||
assert((CHUNK_SIZE == 32 && CHUNK_NUMBER == 10) || (CHUNK_SIZE == 40 && CHUNK_NUMBER == 8) || (CHUNK_SIZE == 16 && CHUNK_NUMBER == 20));
|
||||
|
||||
|
||||
signal output gen[2][CHUNK_NUMBER];
|
||||
|
||||
if ((CHUNK_SIZE == 32) && (CHUNK_NUMBER == 10)){
|
||||
gen[0][0] <== 971114001;
|
||||
gen[0][1] <== 279940365;
|
||||
gen[0][2] <== 279288263;
|
||||
gen[0][3] <== 3884391978;
|
||||
gen[0][4] <== 168328886;
|
||||
gen[0][5] <== 4060166097;
|
||||
gen[0][6] <== 2397421542;
|
||||
gen[0][7] <== 1384758468;
|
||||
gen[0][8] <== 4216576184;
|
||||
gen[0][9] <== 1136492186;
|
||||
gen[1][0] <== 1764658913;
|
||||
gen[1][1] <== 3545384401;
|
||||
gen[1][2] <== 2863426247;
|
||||
gen[1][3] <== 2848422007;
|
||||
gen[1][4] <== 292651754;
|
||||
gen[1][5] <== 121896941;
|
||||
gen[1][6] <== 2138515294;
|
||||
gen[1][7] <== 2873135908;
|
||||
gen[1][8] <== 1173101768;
|
||||
gen[1][9] <== 352178261;
|
||||
}
|
||||
if ((CHUNK_SIZE == 40) && (CHUNK_NUMBER == 8)){
|
||||
gen[0][0] <== 56805688849;
|
||||
gen[0][1] <== 660469755789;
|
||||
gen[0][2] <== 580326658213;
|
||||
gen[0][3] <== 43092195047;
|
||||
gen[0][4] <== 991902644177;
|
||||
gen[0][5] <== 810751550911;
|
||||
gen[0][6] <== 360118243977;
|
||||
gen[0][7] <== 290941999867;
|
||||
gen[1][0] <== 899412823777;
|
||||
gen[1][1] <== 458619048517;
|
||||
gen[1][2] <== 856719600300;
|
||||
gen[1][3] <== 74918849193;
|
||||
gen[1][4] <== 403848822765;
|
||||
gen[1][5] <== 631972525863;
|
||||
gen[1][6] <== 1014095194944;
|
||||
gen[1][7] <== 90157634885;
|
||||
}
|
||||
if ((CHUNK_SIZE == 16) && (CHUNK_NUMBER == 20)){
|
||||
gen[0][0] <== 1553;
|
||||
gen[0][1] <== 14818;
|
||||
gen[0][2] <== 36109;
|
||||
gen[0][3] <== 4271;
|
||||
gen[0][4] <== 39367;
|
||||
gen[0][5] <== 4261;
|
||||
gen[0][6] <== 7722;
|
||||
gen[0][7] <== 59271;
|
||||
gen[0][8] <== 32438;
|
||||
gen[0][9] <== 2568;
|
||||
gen[0][10] <== 14289;
|
||||
gen[0][11] <== 61953;
|
||||
gen[0][12] <== 49126;
|
||||
gen[0][13] <== 36581;
|
||||
gen[0][14] <== 48324;
|
||||
gen[0][15] <== 21129;
|
||||
gen[0][16] <== 55480;
|
||||
gen[0][17] <== 64339;
|
||||
gen[0][18] <== 32410;
|
||||
gen[0][19] <== 17341;
|
||||
gen[1][0] <== 36577;
|
||||
gen[1][1] <== 26926;
|
||||
gen[1][2] <== 17873;
|
||||
gen[1][3] <== 54098;
|
||||
gen[1][4] <== 27335;
|
||||
gen[1][5] <== 43692;
|
||||
gen[1][6] <== 30839;
|
||||
gen[1][7] <== 43463;
|
||||
gen[1][8] <== 33514;
|
||||
gen[1][9] <== 4465;
|
||||
gen[1][10] <== 65517;
|
||||
gen[1][11] <== 1859;
|
||||
gen[1][12] <== 10078;
|
||||
gen[1][13] <== 32631;
|
||||
gen[1][14] <== 37668;
|
||||
gen[1][15] <== 43840;
|
||||
gen[1][16] <== 7368;
|
||||
gen[1][17] <== 17900;
|
||||
gen[1][18] <== 53333;
|
||||
gen[1][19] <== 5373;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
template GetBrainpool320Order(CHUNK_SIZE, CHUNK_NUMBER){
|
||||
|
||||
assert((CHUNK_SIZE == 32 && CHUNK_NUMBER == 10) || (CHUNK_SIZE == 40 && CHUNK_NUMBER == 8) || (CHUNK_SIZE == 16 && CHUNK_NUMBER == 20));
|
||||
|
||||
signal output order[CHUNK_NUMBER];
|
||||
if ((CHUNK_SIZE == 32) && (CHUNK_NUMBER == 10)){
|
||||
order[0] <== 1153798929;
|
||||
order[1] <== 2257671515;
|
||||
order[2] <== 4001781993;
|
||||
order[3] <== 759705287;
|
||||
order[4] <== 3062829731;
|
||||
order[5] <== 4186951589;
|
||||
order[6] <== 3523338341;
|
||||
order[7] <== 3778836574;
|
||||
order[8] <== 918310839;
|
||||
order[9] <== 3546171168;
|
||||
}
|
||||
if ((CHUNK_SIZE == 40) && (CHUNK_NUMBER == 8)){
|
||||
order[0] <== 391995822865;
|
||||
order[1] <== 381875032405;
|
||||
order[2] <== 310022499974;
|
||||
order[3] <== 784084411181;
|
||||
order[4] <== 437978648485;
|
||||
order[5] <== 516986896864;
|
||||
order[6] <== 808791302460;
|
||||
order[7] <== 907819819062;
|
||||
}
|
||||
if (CHUNK_SIZE == 16 && CHUNK_NUMBER == 20){
|
||||
order[0] <== 37649;
|
||||
order[1] <== 17605;
|
||||
order[2] <== 21851;
|
||||
order[3] <== 34449;
|
||||
order[4] <== 22761;
|
||||
order[5] <== 61062;
|
||||
order[6] <== 11975;
|
||||
order[7] <== 11592;
|
||||
order[8] <== 4771;
|
||||
order[9] <== 46735;
|
||||
order[10] <== 53157;
|
||||
order[11] <== 63887;
|
||||
order[12] <== 57445;
|
||||
order[13] <== 53761;
|
||||
order[14] <== 30814;
|
||||
order[15] <== 57660;
|
||||
order[16] <== 20407;
|
||||
order[17] <== 14012;
|
||||
order[18] <== 18208;
|
||||
order[19] <== 54110;
|
||||
}
|
||||
}
|
||||
|
||||
template Brainpool320GeneratorMultiplication(CHUNK_SIZE, CHUNK_NUMBER){
|
||||
var STRIDE = 8;
|
||||
signal input scalar[CHUNK_NUMBER];
|
||||
signal output out[2][CHUNK_NUMBER];
|
||||
|
||||
component n2b[CHUNK_NUMBER];
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
n2b[i] = Num2Bits(CHUNK_SIZE);
|
||||
n2b[i].in <== scalar[i];
|
||||
}
|
||||
|
||||
var NUM_STRIDES = div_ceil(CHUNK_SIZE * CHUNK_NUMBER, STRIDE);
|
||||
// power[i][j] contains: [j * (1 << STRIDE * i) * G] for 1 <= j < (1 << STRIDE)
|
||||
var POWERS[NUM_STRIDES][2 ** STRIDE][2][CHUNK_NUMBER];
|
||||
POWERS = get_g_pow_stride8_table_brainpool320(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
|
||||
var DUMMY_HOLDER[2][CHUNK_NUMBER] = get_320_dummy_point(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
var DUMMY[2][CHUNK_NUMBER];
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) DUMMY[0][i] = DUMMY_HOLDER[0][i];
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) DUMMY[1][i] = DUMMY_HOLDER[1][i];
|
||||
|
||||
component selectors[NUM_STRIDES];
|
||||
for (var i = 0; i < NUM_STRIDES; i++) {
|
||||
selectors[i] = Bits2Num(STRIDE);
|
||||
for (var j = 0; j < STRIDE; j++) {
|
||||
var bit_idx1 = (i * STRIDE + j) \ CHUNK_SIZE;
|
||||
var bit_idx2 = (i * STRIDE + j) % CHUNK_SIZE;
|
||||
if (bit_idx1 < CHUNK_NUMBER) {
|
||||
selectors[i].in[j] <== n2b[bit_idx1].out[bit_idx2];
|
||||
} else {
|
||||
selectors[i].in[j] <== 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// multiplexers[i][l].out will be the coordinates of:
|
||||
// selectors[i].out * (2 ** (i * STRIDE)) * G if selectors[i].out is non-zero
|
||||
// (2 ** 255) * G if selectors[i].out is zero
|
||||
component multiplexers[NUM_STRIDES][2];
|
||||
// select from CHUNK_NUMBER-register outputs using a 2 ** STRIDE bit selector
|
||||
for (var i = 0; i < NUM_STRIDES; i++) {
|
||||
for (var l = 0; l < 2; l++) {
|
||||
multiplexers[i][l] = Multiplexer(CHUNK_NUMBER, (1 << STRIDE));
|
||||
multiplexers[i][l].sel <== selectors[i].out;
|
||||
for (var idx = 0; idx < CHUNK_NUMBER; idx++) {
|
||||
multiplexers[i][l].inp[0][idx] <== DUMMY[l][idx];
|
||||
for (var j = 1; j < (1 << STRIDE); j++) {
|
||||
multiplexers[i][l].inp[j][idx] <== POWERS[i][j][l][idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component isZero[NUM_STRIDES];
|
||||
for (var i = 0; i < NUM_STRIDES; i++) {
|
||||
isZero[i] = IsZero();
|
||||
isZero[i].in <== selectors[i].out;
|
||||
}
|
||||
|
||||
// hasPrevNonZero[i] = 1 if at least one of the selections in privkey up to STRIDE i is non-zero
|
||||
component hasPrevNonZero[NUM_STRIDES];
|
||||
hasPrevNonZero[0] = OR();
|
||||
hasPrevNonZero[0].a <== 0;
|
||||
hasPrevNonZero[0].b <== 1 - isZero[0].out;
|
||||
for (var i = 1; i < NUM_STRIDES; i++) {
|
||||
hasPrevNonZero[i] = OR();
|
||||
hasPrevNonZero[i].a <== hasPrevNonZero[i - 1].out;
|
||||
hasPrevNonZero[i].b <== 1 - isZero[i].out;
|
||||
}
|
||||
|
||||
signal partial[NUM_STRIDES][2][CHUNK_NUMBER];
|
||||
for (var idx = 0; idx < CHUNK_NUMBER; idx++) {
|
||||
for (var l = 0; l < 2; l++) {
|
||||
partial[0][l][idx] <== multiplexers[0][l].out[idx];
|
||||
}
|
||||
}
|
||||
|
||||
component adders[NUM_STRIDES - 1];
|
||||
signal intermed1[NUM_STRIDES - 1][2][CHUNK_NUMBER];
|
||||
signal intermed2[NUM_STRIDES - 1][2][CHUNK_NUMBER];
|
||||
for (var i = 1; i < NUM_STRIDES; i++) {
|
||||
adders[i - 1] = Brainpool320AddUnequal(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
for (var idx = 0; idx < CHUNK_NUMBER; idx++) {
|
||||
for (var l = 0; l < 2; l++) {
|
||||
adders[i - 1].point1[l][idx] <== partial[i - 1][l][idx];
|
||||
adders[i - 1].point2[l][idx] <== multiplexers[i][l].out[idx];
|
||||
}
|
||||
}
|
||||
|
||||
// partial[i] = hasPrevNonZero[i - 1] * ((1 - isZero[i]) * adders[i - 1].out + isZero[i] * partial[i - 1][0][idx])
|
||||
// + (1 - hasPrevNonZero[i - 1]) * (1 - isZero[i]) * multiplexers[i]
|
||||
for (var idx = 0; idx < CHUNK_NUMBER; idx++) {
|
||||
for (var l = 0; l < 2; l++) {
|
||||
intermed1[i - 1][l][idx] <== isZero[i].out * (partial[i - 1][l][idx] - adders[i - 1].out[l][idx]) + adders[i - 1].out[l][idx];
|
||||
intermed2[i - 1][l][idx] <== multiplexers[i][l].out[idx] - isZero[i].out * multiplexers[i][l].out[idx];
|
||||
partial[i][l][idx] <== hasPrevNonZero[i - 1].out * (intermed1[i - 1][l][idx] - intermed2[i - 1][l][idx]) + intermed2[i - 1][l][idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
for (var l = 0; l < 2; l++) {
|
||||
out[l][i] <== partial[NUM_STRIDES - 1][l][i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template Brainpool320PrecomputePipinger(CHUNK_SIZE, CHUNK_NUMBER, WINDOW_SIZE){
|
||||
signal input in[2][CHUNK_NUMBER];
|
||||
|
||||
var PRECOMPUTE_NUMBER = 2 ** WINDOW_SIZE;
|
||||
|
||||
signal output out[PRECOMPUTE_NUMBER][2][CHUNK_NUMBER];
|
||||
|
||||
for (var i = 0; i < 2; i++){
|
||||
for (var j = 0; j < CHUNK_NUMBER; j++){
|
||||
out[0][i][j] <== 0;
|
||||
}
|
||||
}
|
||||
|
||||
out[1] <== in;
|
||||
|
||||
component doublers[PRECOMPUTE_NUMBER\2 - 1];
|
||||
component adders [PRECOMPUTE_NUMBER\2 - 1];
|
||||
|
||||
for (var i = 2; i < PRECOMPUTE_NUMBER; i++){
|
||||
if (i % 2 == 0){
|
||||
doublers[i\2 - 1] = Brainpool320Double(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
doublers[i\2 - 1].in <== out[i\2];
|
||||
doublers[i\2 - 1].out ==> out[i];
|
||||
}
|
||||
else
|
||||
{
|
||||
adders[i\2 - 1] = Brainpool320AddUnequal(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
adders[i\2 - 1].point1 <== out[1];
|
||||
adders[i\2 - 1].point2 <== out[i - 1];
|
||||
adders[i\2 - 1].out ==> out[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template Brainpool320PipingerMult(CHUNK_SIZE, CHUNK_NUMBER, WINDOW_SIZE){
|
||||
|
||||
assert(WINDOW_SIZE == 4);
|
||||
|
||||
signal input point[2][CHUNK_NUMBER];
|
||||
signal input scalar [CHUNK_NUMBER];
|
||||
|
||||
signal output out[2][CHUNK_NUMBER];
|
||||
|
||||
var PRECOMPUTE_NUMBER = 2 ** WINDOW_SIZE;
|
||||
|
||||
signal precomputed[PRECOMPUTE_NUMBER][2][CHUNK_NUMBER];
|
||||
|
||||
component precompute = Brainpool320PrecomputePipinger(CHUNK_SIZE, CHUNK_NUMBER, WINDOW_SIZE);
|
||||
precompute.in <== point;
|
||||
precompute.out ==> precomputed;
|
||||
|
||||
var DOUBLERS_NUMBER = CHUNK_SIZE*CHUNK_NUMBER - WINDOW_SIZE;
|
||||
var ADDERS_NUMBER = CHUNK_SIZE*CHUNK_NUMBER \ WINDOW_SIZE; //80
|
||||
|
||||
component doublers[DOUBLERS_NUMBER];
|
||||
component adders [ADDERS_NUMBER];
|
||||
component bits2Num[ADDERS_NUMBER];
|
||||
component num2Bits[CHUNK_NUMBER];
|
||||
|
||||
signal res [ADDERS_NUMBER + 1][2][CHUNK_NUMBER];
|
||||
|
||||
signal tmp [ADDERS_NUMBER][PRECOMPUTE_NUMBER][2][CHUNK_NUMBER];
|
||||
|
||||
signal tmp2[ADDERS_NUMBER] [2] [CHUNK_NUMBER];
|
||||
signal tmp3[ADDERS_NUMBER] [2][2][CHUNK_NUMBER];
|
||||
signal tmp4[ADDERS_NUMBER] [2] [CHUNK_NUMBER];
|
||||
signal tmp5[ADDERS_NUMBER] [2][2][CHUNK_NUMBER];
|
||||
signal tmp6[ADDERS_NUMBER - 1][2][2][CHUNK_NUMBER];
|
||||
signal tmp7[ADDERS_NUMBER - 1][2] [CHUNK_NUMBER]; //79
|
||||
|
||||
component equals [ADDERS_NUMBER][PRECOMPUTE_NUMBER][2][CHUNK_NUMBER];
|
||||
component zeroEquals[ADDERS_NUMBER];
|
||||
component tmpEquals [ADDERS_NUMBER];
|
||||
|
||||
component g = Brainpool320GetGenerator(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
signal gen[2][CHUNK_NUMBER];
|
||||
gen <== g.gen;
|
||||
|
||||
signal scalarBits[CHUNK_NUMBER*CHUNK_SIZE];
|
||||
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++){
|
||||
num2Bits[i] = Num2Bits(CHUNK_SIZE);
|
||||
num2Bits[i].in <== scalar[i];
|
||||
|
||||
for (var j = 0; j < CHUNK_SIZE; j++){
|
||||
scalarBits[CHUNK_NUMBER*CHUNK_SIZE - CHUNK_SIZE * (i + 1) + j] <== num2Bits[i].out[CHUNK_SIZE - 1 - j];
|
||||
}
|
||||
}
|
||||
|
||||
res[0] <== precomputed[0];
|
||||
|
||||
for (var i = 0; i < CHUNK_NUMBER*CHUNK_SIZE; i += WINDOW_SIZE){
|
||||
adders[i\WINDOW_SIZE] = Brainpool320AddUnequal(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
bits2Num[i\WINDOW_SIZE] = Bits2Num(WINDOW_SIZE);
|
||||
for (var j = 0; j < WINDOW_SIZE; j++){
|
||||
bits2Num[i\WINDOW_SIZE].in[j] <== scalarBits[i + (WINDOW_SIZE - 1) - j];
|
||||
}
|
||||
|
||||
tmpEquals[i\WINDOW_SIZE] = IsEqual();
|
||||
tmpEquals[i\WINDOW_SIZE].in[0] <== 0;
|
||||
tmpEquals[i\WINDOW_SIZE].in[1] <== res[i\WINDOW_SIZE][0][0];
|
||||
|
||||
if (i != 0){
|
||||
for (var j = 0; j < WINDOW_SIZE; j++){
|
||||
doublers[i + j - WINDOW_SIZE] = Brainpool320Double(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
|
||||
if (j == 0){
|
||||
for (var axis_idx = 0; axis_idx < 2; axis_idx++){
|
||||
for (var coor_idx = 0; coor_idx < CHUNK_NUMBER; coor_idx ++){
|
||||
tmp6[i\WINDOW_SIZE - 1][0][axis_idx][coor_idx] <== tmpEquals[i\WINDOW_SIZE].out * gen[axis_idx][coor_idx];
|
||||
tmp6[i\WINDOW_SIZE - 1][1][axis_idx][coor_idx] <== (1 - tmpEquals[i\WINDOW_SIZE].out) * res[i\WINDOW_SIZE][axis_idx][coor_idx];
|
||||
tmp7[i\WINDOW_SIZE - 1] [axis_idx][coor_idx] <== tmp6[i\WINDOW_SIZE - 1][0][axis_idx][coor_idx]
|
||||
+ tmp6[i\WINDOW_SIZE - 1][1][axis_idx][coor_idx];
|
||||
}
|
||||
}
|
||||
|
||||
doublers[i + j - WINDOW_SIZE].in <== tmp7[i\WINDOW_SIZE - 1];
|
||||
}
|
||||
else
|
||||
{
|
||||
doublers[i + j - WINDOW_SIZE].in <== doublers[i + j - 1 - WINDOW_SIZE].out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var point_idx = 0; point_idx < PRECOMPUTE_NUMBER; point_idx++){
|
||||
for (var axis_idx = 0; axis_idx < 2; axis_idx++){
|
||||
for (var coor_idx = 0; coor_idx < CHUNK_NUMBER; coor_idx++){
|
||||
equals[i\WINDOW_SIZE][point_idx][axis_idx][coor_idx] = IsEqual();
|
||||
equals[i\WINDOW_SIZE][point_idx][axis_idx][coor_idx].in[0] <== point_idx;
|
||||
equals[i\WINDOW_SIZE][point_idx][axis_idx][coor_idx].in[1] <== bits2Num[i\WINDOW_SIZE].out;
|
||||
tmp [i\WINDOW_SIZE][point_idx][axis_idx][coor_idx] <== precomputed[point_idx][axis_idx][coor_idx] *
|
||||
equals[i\WINDOW_SIZE][point_idx][axis_idx][coor_idx].out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var axis_idx = 0; axis_idx < 2; axis_idx++){
|
||||
for (var coor_idx = 0; coor_idx < CHUNK_NUMBER; coor_idx++){
|
||||
tmp2[i\WINDOW_SIZE] [axis_idx][coor_idx] <==
|
||||
tmp[i\WINDOW_SIZE][0] [axis_idx][coor_idx] +
|
||||
tmp[i\WINDOW_SIZE][1] [axis_idx][coor_idx] +
|
||||
tmp[i\WINDOW_SIZE][2] [axis_idx][coor_idx] +
|
||||
tmp[i\WINDOW_SIZE][3] [axis_idx][coor_idx] +
|
||||
tmp[i\WINDOW_SIZE][4] [axis_idx][coor_idx] +
|
||||
tmp[i\WINDOW_SIZE][5] [axis_idx][coor_idx] +
|
||||
tmp[i\WINDOW_SIZE][6] [axis_idx][coor_idx] +
|
||||
tmp[i\WINDOW_SIZE][7] [axis_idx][coor_idx] +
|
||||
tmp[i\WINDOW_SIZE][8] [axis_idx][coor_idx] +
|
||||
tmp[i\WINDOW_SIZE][9] [axis_idx][coor_idx] +
|
||||
tmp[i\WINDOW_SIZE][10][axis_idx][coor_idx] +
|
||||
tmp[i\WINDOW_SIZE][11][axis_idx][coor_idx] +
|
||||
tmp[i\WINDOW_SIZE][12][axis_idx][coor_idx] +
|
||||
tmp[i\WINDOW_SIZE][13][axis_idx][coor_idx] +
|
||||
tmp[i\WINDOW_SIZE][14][axis_idx][coor_idx] +
|
||||
tmp[i\WINDOW_SIZE][15][axis_idx][coor_idx];
|
||||
}
|
||||
}
|
||||
|
||||
if (i == 0){
|
||||
|
||||
adders[i\WINDOW_SIZE].point1 <== res [i\WINDOW_SIZE];
|
||||
adders[i\WINDOW_SIZE].point2 <== tmp2[i\WINDOW_SIZE];
|
||||
res[i\WINDOW_SIZE + 1] <== tmp2[i\WINDOW_SIZE];
|
||||
|
||||
} else {
|
||||
|
||||
adders[i\WINDOW_SIZE].point1 <== doublers[i - 1].out;
|
||||
adders[i\WINDOW_SIZE].point2 <== tmp2[i\WINDOW_SIZE];
|
||||
|
||||
zeroEquals[i\WINDOW_SIZE] = IsEqual();
|
||||
|
||||
zeroEquals[i\WINDOW_SIZE].in[0]<== 0;
|
||||
zeroEquals[i\WINDOW_SIZE].in[1]<== tmp2[i\WINDOW_SIZE][0][0];
|
||||
|
||||
for (var axis_idx = 0; axis_idx < 2; axis_idx++){
|
||||
for(var coor_idx = 0; coor_idx < CHUNK_NUMBER; coor_idx++){
|
||||
|
||||
tmp3[i\WINDOW_SIZE][0][axis_idx][coor_idx] <== adders [i\WINDOW_SIZE].out[axis_idx][coor_idx] * (1 - zeroEquals[i\WINDOW_SIZE].out);
|
||||
tmp3[i\WINDOW_SIZE][1][axis_idx][coor_idx] <== zeroEquals[i\WINDOW_SIZE].out * doublers[i-1].out[axis_idx][coor_idx];
|
||||
tmp4[i\WINDOW_SIZE] [axis_idx][coor_idx] <== tmp3[i\WINDOW_SIZE][0][axis_idx][coor_idx] + tmp3[i\WINDOW_SIZE][1][axis_idx][coor_idx];
|
||||
tmp5[i\WINDOW_SIZE][0][axis_idx][coor_idx] <== (1 - tmpEquals[i\WINDOW_SIZE].out) * tmp4[i\WINDOW_SIZE] [axis_idx][coor_idx];
|
||||
tmp5[i\WINDOW_SIZE][1][axis_idx][coor_idx] <== tmpEquals[i\WINDOW_SIZE].out * tmp2[i\WINDOW_SIZE] [axis_idx][coor_idx];
|
||||
|
||||
res[i\WINDOW_SIZE + 1][axis_idx][coor_idx] <== tmp5[i\WINDOW_SIZE][0][axis_idx][coor_idx] + tmp5[i\WINDOW_SIZE][1][axis_idx][coor_idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
out <== res[ADDERS_NUMBER];
|
||||
}
|
||||
@@ -0,0 +1,342 @@
|
||||
pragma circom 2.1.6;
|
||||
|
||||
function get_320_order(CHUNK_SIZE,CHUNK_NUMBER){
|
||||
|
||||
assert((CHUNK_SIZE == 32 && CHUNK_NUMBER == 10) || (CHUNK_SIZE == 40 && CHUNK_NUMBER == 8) || (CHUNK_SIZE == 16 && CHUNK_NUMBER == 20));
|
||||
|
||||
if ((CHUNK_SIZE == 32) && (CHUNK_NUMBER == 10)){
|
||||
var ORDER[10];
|
||||
|
||||
ORDER[0] = 1153798929;
|
||||
ORDER[1] = 2257671515;
|
||||
ORDER[2] = 4001781993;
|
||||
ORDER[3] = 759705287;
|
||||
ORDER[4] = 3062829731;
|
||||
ORDER[5] = 4186951589;
|
||||
ORDER[6] = 3523338341;
|
||||
ORDER[7] = 3778836574;
|
||||
ORDER[8] = 918310839;
|
||||
ORDER[9] = 3546171168;
|
||||
|
||||
return ORDER;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((CHUNK_SIZE == 40) && (CHUNK_NUMBER == 8))
|
||||
{
|
||||
var ORDER[8];
|
||||
|
||||
ORDER[0] = 391995822865;
|
||||
ORDER[1] = 381875032405;
|
||||
ORDER[2] = 310022499974;
|
||||
ORDER[3] = 784084411181;
|
||||
ORDER[4] = 437978648485;
|
||||
ORDER[5] = 516986896864;
|
||||
ORDER[6] = 808791302460;
|
||||
ORDER[7] = 907819819062;
|
||||
|
||||
return ORDER;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
var ORDER[20];
|
||||
|
||||
ORDER[0] = 37649;
|
||||
ORDER[1] = 17605;
|
||||
ORDER[2] = 21851;
|
||||
ORDER[3] = 34449;
|
||||
ORDER[4] = 22761;
|
||||
ORDER[5] = 61062;
|
||||
ORDER[6] = 11975;
|
||||
ORDER[7] = 11592;
|
||||
ORDER[8] = 4771;
|
||||
ORDER[9] = 46735;
|
||||
ORDER[10] = 53157;
|
||||
ORDER[11] = 63887;
|
||||
ORDER[12] = 57445;
|
||||
ORDER[13] = 53761;
|
||||
ORDER[14] = 30814;
|
||||
ORDER[15] = 57660;
|
||||
ORDER[16] = 20407;
|
||||
ORDER[17] = 14012;
|
||||
ORDER[18] = 18208;
|
||||
ORDER[19] = 54110;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
function get_320_params(CHUNK_SIZE,CHUNK_NUMBER){
|
||||
|
||||
assert((CHUNK_SIZE == 32 && CHUNK_NUMBER == 10) || (CHUNK_SIZE == 40 && CHUNK_NUMBER == 8) || (CHUNK_SIZE == 16 && CHUNK_NUMBER == 20));
|
||||
|
||||
if ((CHUNK_SIZE == 32) && (CHUNK_NUMBER == 10)) {
|
||||
|
||||
var A[10];
|
||||
var P[10];
|
||||
var B[10];
|
||||
|
||||
var PARAMS[3][10];
|
||||
|
||||
A[0] = 2105937588;
|
||||
A[1] = 2465428905;
|
||||
A[2] = 2248124916;
|
||||
A[3] = 1712918192;
|
||||
A[4] = 4125850074;
|
||||
A[5] = 2728867091;
|
||||
A[6] = 1832860600;
|
||||
A[7] = 2211245012;
|
||||
A[8] = 2411376888;
|
||||
A[9] = 1055066966;
|
||||
B[0] = 2410803622;
|
||||
B[1] = 1868477612;
|
||||
B[2] = 2286238081;
|
||||
B[3] = 3425819853;
|
||||
B[4] = 2505356442;
|
||||
B[5] = 3779019060;
|
||||
B[6] = 1080593007;
|
||||
B[7] = 3551336838;
|
||||
B[8] = 2650651714;
|
||||
B[9] = 1376289684;
|
||||
P[0] = 4055051815;
|
||||
P[1] = 4241756849;
|
||||
P[2] = 2022960168;
|
||||
P[3] = 1335015916;
|
||||
P[4] = 4143189487;
|
||||
P[5] = 4186951590;
|
||||
P[6] = 3523338341;
|
||||
P[7] = 3778836574;
|
||||
P[8] = 918310839;
|
||||
P[9] = 3546171168;
|
||||
|
||||
PARAMS[0] = A;
|
||||
PARAMS[1] = B;
|
||||
PARAMS[2] = P;
|
||||
|
||||
return PARAMS;
|
||||
}
|
||||
else {
|
||||
if ((CHUNK_SIZE == 40) && (CHUNK_NUMBER == 8))
|
||||
{
|
||||
|
||||
var A[8];
|
||||
var P[8];
|
||||
var B[8];
|
||||
|
||||
var PARAMS[3][8];
|
||||
|
||||
A[0] = 727955410612;
|
||||
A[1] = 729952744309;
|
||||
A[2] = 107620632063;
|
||||
A[3] = 1056217619046;
|
||||
A[4] = 793002849555;
|
||||
A[5] = 1012881243963;
|
||||
A[6] = 801832993740;
|
||||
A[7] = 270097143439;
|
||||
B[0] = 741145178534;
|
||||
B[1] = 246984695476;
|
||||
B[2] = 214157854789;
|
||||
B[3] = 641371249356;
|
||||
B[4] = 480520388916;
|
||||
B[5] = 109626550410;
|
||||
B[6] = 1089785222061;
|
||||
B[7] = 352330159261;
|
||||
P[0] = 764264263207;
|
||||
P[1] = 1014299939858;
|
||||
P[2] = 630184507539;
|
||||
P[3] = 1060656508751;
|
||||
P[4] = 437978648486;
|
||||
P[5] = 516986896864;
|
||||
P[6] = 808791302460;
|
||||
P[7] = 907819819062;
|
||||
|
||||
|
||||
PARAMS[0] = A;
|
||||
PARAMS[1] = B;
|
||||
PARAMS[2] = P;
|
||||
|
||||
return PARAMS;
|
||||
}
|
||||
else
|
||||
{
|
||||
var A[20];
|
||||
var P[20];
|
||||
var B[20];
|
||||
|
||||
var PARAMS[3][20];
|
||||
A[0] = 3764;
|
||||
A[1] = 32134;
|
||||
A[2] = 30121;
|
||||
A[3] = 37619;
|
||||
A[4] = 43508;
|
||||
A[5] = 34303;
|
||||
A[6] = 3760;
|
||||
A[7] = 26137;
|
||||
A[8] = 31194;
|
||||
A[9] = 62955;
|
||||
A[10] = 13587;
|
||||
A[11] = 41639;
|
||||
A[12] = 15288;
|
||||
A[13] = 27967;
|
||||
A[14] = 60372;
|
||||
A[15] = 33740;
|
||||
A[16] = 45304;
|
||||
A[17] = 36794;
|
||||
A[18] = 2902;
|
||||
A[19] = 16099;
|
||||
B[0] = 61862;
|
||||
B[1] = 36785;
|
||||
B[2] = 46252;
|
||||
B[3] = 28510;
|
||||
B[4] = 14721;
|
||||
B[5] = 34885;
|
||||
B[6] = 56525;
|
||||
B[7] = 52273;
|
||||
B[8] = 46234;
|
||||
B[9] = 38228;
|
||||
B[10] = 16692;
|
||||
B[11] = 57663;
|
||||
B[12] = 35439;
|
||||
B[13] = 16488;
|
||||
B[14] = 6534;
|
||||
B[15] = 54189;
|
||||
B[16] = 48194;
|
||||
B[17] = 40445;
|
||||
B[18] = 33684;
|
||||
B[19] = 21000;
|
||||
P[0] = 11815;
|
||||
P[1] = 61875;
|
||||
P[2] = 4785;
|
||||
P[3] = 64724;
|
||||
P[4] = 60456;
|
||||
P[5] = 30867;
|
||||
P[6] = 47596;
|
||||
P[7] = 20370;
|
||||
P[8] = 3567;
|
||||
P[9] = 63220;
|
||||
P[10] = 53158;
|
||||
P[11] = 63887;
|
||||
P[12] = 57445;
|
||||
P[13] = 53761;
|
||||
P[14] = 30814;
|
||||
P[15] = 57660;
|
||||
P[16] = 20407;
|
||||
P[17] = 14012;
|
||||
P[18] = 18208;
|
||||
P[19] = 54110;
|
||||
return PARAMS;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function get_320_dummy_point(CHUNK_SIZE,CHUNK_NUMBER){
|
||||
|
||||
assert((CHUNK_SIZE == 32 && CHUNK_NUMBER == 10) || (CHUNK_SIZE == 40 && CHUNK_NUMBER == 8) || (CHUNK_SIZE == 16 && CHUNK_NUMBER == 20));
|
||||
|
||||
if ((CHUNK_SIZE == 32) && (CHUNK_NUMBER == 10)){
|
||||
var DUMMY[2][10];
|
||||
|
||||
DUMMY[0][0] = 3618987067;
|
||||
DUMMY[0][1] = 1246284267;
|
||||
DUMMY[0][2] = 1443155418;
|
||||
DUMMY[0][3] = 814100841;
|
||||
DUMMY[0][4] = 2623697010;
|
||||
DUMMY[0][5] = 4004732673;
|
||||
DUMMY[0][6] = 3535093894;
|
||||
DUMMY[0][7] = 2197257376;
|
||||
DUMMY[0][8] = 2043287943;
|
||||
DUMMY[0][9] = 2382825036;
|
||||
DUMMY[1][0] = 2499851110;
|
||||
DUMMY[1][1] = 822647969;
|
||||
DUMMY[1][2] = 2980671764;
|
||||
DUMMY[1][3] = 1347766415;
|
||||
DUMMY[1][4] = 2998204557;
|
||||
DUMMY[1][5] = 510652630;
|
||||
DUMMY[1][6] = 2505684036;
|
||||
DUMMY[1][7] = 3635842112;
|
||||
DUMMY[1][8] = 4197847501;
|
||||
DUMMY[1][9] = 833113252;
|
||||
|
||||
return DUMMY;
|
||||
}
|
||||
else{
|
||||
if ((CHUNK_SIZE == 40) && (CHUNK_NUMBER == 8))
|
||||
{
|
||||
var DUMMY[2][8];
|
||||
|
||||
DUMMY[0][0] = 1012936301627;
|
||||
DUMMY[0][1] = 884130597065;
|
||||
DUMMY[0][2] = 576354604548;
|
||||
DUMMY[0][3] = 671666434608;
|
||||
DUMMY[0][4] = 579530350337;
|
||||
DUMMY[0][5] = 535274108224;
|
||||
DUMMY[0][6] = 868011705079;
|
||||
DUMMY[0][7] = 610003209337;
|
||||
DUMMY[1][0] = 693989585766;
|
||||
DUMMY[1][1] = 485670062236;
|
||||
DUMMY[1][2] = 366289596841;
|
||||
DUMMY[1][3] = 767540366672;
|
||||
DUMMY[1][4] = 292568428758;
|
||||
DUMMY[1][5] = 602378951092;
|
||||
DUMMY[1][6] = 232226937014;
|
||||
DUMMY[1][7] = 213276992762;
|
||||
|
||||
return DUMMY;
|
||||
}
|
||||
else
|
||||
{
|
||||
var DUMMY[2][20];
|
||||
|
||||
DUMMY[0][0] = 23611;
|
||||
DUMMY[0][1] = 55221;
|
||||
DUMMY[0][2] = 51691;
|
||||
DUMMY[0][3] = 19016;
|
||||
DUMMY[0][4] = 52698;
|
||||
DUMMY[0][5] = 22020;
|
||||
DUMMY[0][6] = 12649;
|
||||
DUMMY[0][7] = 12422;
|
||||
DUMMY[0][8] = 28786;
|
||||
DUMMY[0][9] = 40034;
|
||||
DUMMY[0][10] = 24321;
|
||||
DUMMY[0][11] = 61107;
|
||||
DUMMY[0][12] = 16518;
|
||||
DUMMY[0][13] = 53941;
|
||||
DUMMY[0][14] = 31904;
|
||||
DUMMY[0][15] = 33527;
|
||||
DUMMY[0][16] = 6535;
|
||||
DUMMY[0][17] = 31178;
|
||||
DUMMY[0][18] = 1612;
|
||||
DUMMY[0][19] = 36359;
|
||||
DUMMY[1][0] = 45926;
|
||||
DUMMY[1][1] = 38144;
|
||||
DUMMY[1][2] = 40097;
|
||||
DUMMY[1][3] = 12552;
|
||||
DUMMY[1][4] = 28948;
|
||||
DUMMY[1][5] = 45481;
|
||||
DUMMY[1][6] = 18575;
|
||||
DUMMY[1][7] = 20565;
|
||||
DUMMY[1][8] = 63629;
|
||||
DUMMY[1][9] = 45748;
|
||||
DUMMY[1][10] = 61654;
|
||||
DUMMY[1][11] = 7791;
|
||||
DUMMY[1][12] = 46148;
|
||||
DUMMY[1][13] = 38233;
|
||||
DUMMY[1][14] = 35904;
|
||||
DUMMY[1][15] = 55478;
|
||||
DUMMY[1][16] = 4557;
|
||||
DUMMY[1][17] = 64054;
|
||||
DUMMY[1][18] = 19620;
|
||||
DUMMY[1][19] = 12712;
|
||||
|
||||
|
||||
return DUMMY;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
808984
circuits/circuits/utils/brainpool/brainpoolP320r1/brainpoolPows.circom
Normal file
808984
circuits/circuits/utils/brainpool/brainpoolP320r1/brainpoolPows.circom
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,113 @@
|
||||
pragma circom 2.1.6;
|
||||
|
||||
include "../brainpoolP256r1/circomPairing/curve.circom";
|
||||
include "circomlib/circuits/bitify.circom";
|
||||
include "./brainpool.circom";
|
||||
include "./brainpoolFunc.circom";
|
||||
|
||||
|
||||
template verifyBrainpool320(CHUNK_SIZE,CHUNK_NUMBER, ALGO){
|
||||
signal input pubkey[2 * CHUNK_SIZE * CHUNK_NUMBER];
|
||||
signal input signature[2 * CHUNK_SIZE * CHUNK_NUMBER];
|
||||
signal input hashed[ALGO];
|
||||
|
||||
signal pubkeyChunked[2][CHUNK_NUMBER];
|
||||
signal signatureChunked[2][CHUNK_NUMBER];
|
||||
|
||||
component bits2NumInput[40];
|
||||
|
||||
for (var i = 0; i < 2; i++){
|
||||
for (var j = 0; j < CHUNK_NUMBER; j++){
|
||||
bits2NumInput[i*CHUNK_NUMBER+j] = Bits2Num(CHUNK_SIZE);
|
||||
bits2NumInput[(i+2)*CHUNK_NUMBER+j] = Bits2Num(CHUNK_SIZE);
|
||||
|
||||
for (var z = 0; z < CHUNK_SIZE; z++){
|
||||
bits2NumInput[i*CHUNK_NUMBER+j].in[z] <== pubkey[i*CHUNK_NUMBER*CHUNK_SIZE + CHUNK_SIZE * j + (CHUNK_SIZE - 1) - z];
|
||||
bits2NumInput[(i+2)*CHUNK_NUMBER+j].in[z] <== signature[i*CHUNK_NUMBER*CHUNK_SIZE + CHUNK_SIZE * j + (CHUNK_SIZE - 1) - z];
|
||||
}
|
||||
bits2NumInput[i*CHUNK_NUMBER+j].out ==> pubkeyChunked[i][(CHUNK_NUMBER - 1) - j];
|
||||
bits2NumInput[(i+2)*CHUNK_NUMBER+j].out ==> signatureChunked[i][(CHUNK_NUMBER - 1) - j];
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
signal hashedMessageBits[CHUNK_SIZE*CHUNK_NUMBER];
|
||||
var SHIFT = CHUNK_SIZE*CHUNK_NUMBER - ALGO;
|
||||
for (var i = 0; i < SHIFT; i++){
|
||||
hashedMessageBits[i] <== 0;
|
||||
}
|
||||
for (var i = 0; i < ALGO; i++){
|
||||
hashedMessageBits[i+SHIFT] <== hashed[i];
|
||||
}
|
||||
|
||||
|
||||
signal hashedMessageChunked[CHUNK_NUMBER];
|
||||
|
||||
component bits2Num[CHUNK_NUMBER];
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
bits2Num[i] = Bits2Num(CHUNK_SIZE);
|
||||
for (var j = 0; j < CHUNK_SIZE; j++) {
|
||||
bits2Num[i].in[CHUNK_SIZE-1-j] <== hashedMessageBits[i*CHUNK_SIZE+j];
|
||||
}
|
||||
hashedMessageChunked[CHUNK_NUMBER-1-i] <== bits2Num[i].out;
|
||||
}
|
||||
|
||||
component getOrder = GetBrainpool320Order(CHUNK_SIZE,CHUNK_NUMBER);
|
||||
signal order[CHUNK_NUMBER];
|
||||
order <== getOrder.order;
|
||||
|
||||
signal sinv[CHUNK_NUMBER];
|
||||
|
||||
component modInv = BigModInv(CHUNK_SIZE,CHUNK_NUMBER);
|
||||
|
||||
modInv.in <== signatureChunked[1];
|
||||
modInv.p <== order;
|
||||
modInv.out ==> sinv;
|
||||
|
||||
|
||||
signal sh[CHUNK_NUMBER];
|
||||
|
||||
component mult = BigMultModP(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
|
||||
mult.a <== sinv;
|
||||
mult.b <== hashedMessageChunked;
|
||||
mult.p <== order;
|
||||
sh <== mult.out;
|
||||
|
||||
signal sr[CHUNK_NUMBER];
|
||||
|
||||
component mult2 = BigMultModP(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
|
||||
mult2.a <== sinv;
|
||||
mult2.b <== signatureChunked[0];
|
||||
mult2.p <== order;
|
||||
sr <== mult2.out;
|
||||
|
||||
|
||||
signal tmpPoint1[2][CHUNK_NUMBER];
|
||||
signal tmpPoint2[2][CHUNK_NUMBER];
|
||||
|
||||
component scalarMult1 = Brainpool320GeneratorMultiplication(CHUNK_SIZE,CHUNK_NUMBER);
|
||||
component scalarMult2 = Brainpool320PipingerMult(CHUNK_SIZE,CHUNK_NUMBER, 4);
|
||||
|
||||
scalarMult1.scalar <== sh;
|
||||
|
||||
tmpPoint1 <== scalarMult1.out;
|
||||
|
||||
|
||||
scalarMult2.scalar <== sr;
|
||||
scalarMult2.point <== pubkeyChunked;
|
||||
|
||||
tmpPoint2 <== scalarMult2.out;
|
||||
|
||||
|
||||
signal verifyX[CHUNK_NUMBER];
|
||||
|
||||
component sumPoints = Brainpool320AddUnequal(CHUNK_SIZE,CHUNK_NUMBER);
|
||||
|
||||
sumPoints.point1 <== tmpPoint1;
|
||||
sumPoints.point2 <== tmpPoint2;
|
||||
|
||||
verifyX <== sumPoints.out[0];
|
||||
verifyX === signatureChunked[0];
|
||||
}
|
||||
@@ -0,0 +1,468 @@
|
||||
pragma circom 2.1.6;
|
||||
|
||||
include "../brainpoolP256r1/circomPairing/curve.circom";
|
||||
include "./brainpoolFunc.circom";
|
||||
include "./brainpoolPows.circom";
|
||||
include "circomlib/circuits/multiplexer.circom";
|
||||
include "circomlib/circuits/bitify.circom";
|
||||
include "circomlib/circuits/comparators.circom";
|
||||
include "../utils/func.circom";
|
||||
|
||||
template BrainpoolScalarMult(CHUNK_SIZE, CHUNK_NUMBER){
|
||||
signal input scalar[CHUNK_NUMBER];
|
||||
signal input point[2][CHUNK_NUMBER];
|
||||
|
||||
signal output out[2][CHUNK_NUMBER];
|
||||
|
||||
component n2b[CHUNK_NUMBER];
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
n2b[i] = Num2Bits(CHUNK_SIZE);
|
||||
n2b[i].in <== scalar[i];
|
||||
}
|
||||
|
||||
// hasPrevNonZero[CHUNK_SIZE * i + j] == 1 if there is a nonzero bit in location [i][j] or higher order bit
|
||||
component hasPrevNonZero[CHUNK_NUMBER * CHUNK_SIZE];
|
||||
for (var i = CHUNK_NUMBER - 1; i >= 0; i--) {
|
||||
for (var j = CHUNK_SIZE - 1; j >= 0; j--) {
|
||||
hasPrevNonZero[CHUNK_SIZE * i + j] = OR();
|
||||
if (i == CHUNK_NUMBER - 1 && j == CHUNK_SIZE - 1) {
|
||||
hasPrevNonZero[CHUNK_SIZE * i + j].a <== 0;
|
||||
hasPrevNonZero[CHUNK_SIZE * i + j].b <== n2b[i].out[j];
|
||||
} else {
|
||||
hasPrevNonZero[CHUNK_SIZE * i + j].a <== hasPrevNonZero[CHUNK_SIZE * i + j + 1].out;
|
||||
hasPrevNonZero[CHUNK_SIZE * i + j].b <== n2b[i].out[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
signal partial[CHUNK_SIZE * CHUNK_NUMBER][2][CHUNK_NUMBER];
|
||||
signal intermed[CHUNK_SIZE * CHUNK_NUMBER - 1][2][CHUNK_NUMBER];
|
||||
component adders[CHUNK_SIZE * CHUNK_NUMBER - 1];
|
||||
component doublers[CHUNK_SIZE * CHUNK_NUMBER - 1];
|
||||
for (var i = CHUNK_NUMBER - 1; i >= 0; i--) {
|
||||
for (var j = CHUNK_SIZE - 1; j >= 0; j--) {
|
||||
if (i == CHUNK_NUMBER - 1 && j == CHUNK_SIZE - 1) {
|
||||
for (var idx = 0; idx < CHUNK_NUMBER; idx++) {
|
||||
partial[CHUNK_SIZE * i + j][0][idx] <== point[0][idx];
|
||||
partial[CHUNK_SIZE * i + j][1][idx] <== point[1][idx];
|
||||
}
|
||||
}
|
||||
if (i < CHUNK_NUMBER - 1 || j < CHUNK_SIZE - 1) {
|
||||
adders[CHUNK_SIZE * i + j] = BrainpoolAddUnequal(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
doublers[CHUNK_SIZE * i + j] = BrainpoolDouble(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
for (var idx = 0; idx < CHUNK_NUMBER; idx++) {
|
||||
doublers[CHUNK_SIZE * i + j].in[0][idx] <== partial[CHUNK_SIZE * i + j + 1][0][idx];
|
||||
doublers[CHUNK_SIZE * i + j].in[1][idx] <== partial[CHUNK_SIZE * i + j + 1][1][idx];
|
||||
}
|
||||
for (var idx = 0; idx < CHUNK_NUMBER; idx++) {
|
||||
adders[CHUNK_SIZE * i + j].point1[0][idx] <== doublers[CHUNK_SIZE * i + j].out[0][idx];
|
||||
adders[CHUNK_SIZE * i + j].point1[1][idx] <== doublers[CHUNK_SIZE * i + j].out[1][idx];
|
||||
adders[CHUNK_SIZE * i + j].point2[0][idx] <== point[0][idx];
|
||||
adders[CHUNK_SIZE * i + j].point2[1][idx] <== point[1][idx];
|
||||
}
|
||||
// partial[CHUNK_SIZE * i + j]
|
||||
// = hasPrevNonZero[CHUNK_SIZE * i + j + 1] * ((1 - n2b[i].out[j]) * doublers[CHUNK_SIZE * i + j] + n2b[i].out[j] * adders[CHUNK_SIZE * i + j])
|
||||
// + (1 - hasPrevNonZero[CHUNK_SIZE * i + j + 1]) * point
|
||||
for (var idx = 0; idx < CHUNK_NUMBER; idx++) {
|
||||
intermed[CHUNK_SIZE * i + j][0][idx] <== n2b[i].out[j] * (adders[CHUNK_SIZE * i + j].out[0][idx] - doublers[CHUNK_SIZE * i + j].out[0][idx]) + doublers[CHUNK_SIZE * i + j].out[0][idx];
|
||||
intermed[CHUNK_SIZE * i + j][1][idx] <== n2b[i].out[j] * (adders[CHUNK_SIZE * i + j].out[1][idx] - doublers[CHUNK_SIZE * i + j].out[1][idx]) + doublers[CHUNK_SIZE * i + j].out[1][idx];
|
||||
partial[CHUNK_SIZE * i + j][0][idx] <== hasPrevNonZero[CHUNK_SIZE * i + j + 1].out * (intermed[CHUNK_SIZE * i + j][0][idx] - point[0][idx]) + point[0][idx];
|
||||
partial[CHUNK_SIZE * i + j][1][idx] <== hasPrevNonZero[CHUNK_SIZE * i + j + 1].out * (intermed[CHUNK_SIZE * i + j][1][idx] - point[1][idx]) + point[1][idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var idx = 0; idx < CHUNK_NUMBER; idx++) {
|
||||
out[0][idx] <== partial[0][0][idx];
|
||||
out[1][idx] <== partial[0][1][idx];
|
||||
}
|
||||
}
|
||||
|
||||
template BrainpoolAddUnequal(CHUNK_SIZE, CHUNK_NUMBER){
|
||||
signal input point1[2][CHUNK_NUMBER];
|
||||
signal input point2[2][CHUNK_NUMBER];
|
||||
signal output out[2][CHUNK_NUMBER];
|
||||
|
||||
var PARAMS[3][CHUNK_NUMBER] = get_params(CHUNK_SIZE,CHUNK_NUMBER);
|
||||
|
||||
component add = EllipticCurveAddUnequal(CHUNK_SIZE, CHUNK_NUMBER, PARAMS[2]);
|
||||
add.a <== point1;
|
||||
add.b <== point2;
|
||||
add.out ==> out;
|
||||
}
|
||||
|
||||
template BrainpoolDouble(CHUNK_SIZE, CHUNK_NUMBER){
|
||||
signal input in[2][CHUNK_NUMBER];
|
||||
signal output out[2][CHUNK_NUMBER];
|
||||
|
||||
var PARAMS[3][CHUNK_NUMBER] = get_params(CHUNK_SIZE,CHUNK_NUMBER);
|
||||
|
||||
component doubling = EllipticCurveDouble(CHUNK_SIZE,CHUNK_NUMBER, PARAMS[0], PARAMS[1], PARAMS[2]);
|
||||
doubling.in <== in;
|
||||
doubling.out ==> out;
|
||||
}
|
||||
|
||||
template BrainpoolGetGenerator(CHUNK_SIZE, CHUNK_NUMBER){
|
||||
|
||||
assert ((CHUNK_SIZE == 32) && (CHUNK_NUMBER == 16));
|
||||
signal output gen[2][CHUNK_NUMBER];
|
||||
|
||||
gen[0][0] <== 3166304290;
|
||||
gen[0][1] <== 2335515145;
|
||||
gen[0][2] <== 1080712808;
|
||||
gen[0][3] <== 2087538759;
|
||||
gen[0][4] <== 2478407007;
|
||||
gen[0][5] <== 1355901051;
|
||||
gen[0][6] <== 3805336717;
|
||||
gen[0][7] <== 4282064760;
|
||||
gen[0][8] <== 4107274638;
|
||||
gen[0][9] <== 3023790830;
|
||||
gen[0][10] <== 3050903233;
|
||||
gen[0][11] <== 2246942576;
|
||||
gen[0][12] <== 2622253715;
|
||||
gen[0][13] <== 1512124974;
|
||||
gen[0][14] <== 3626948964;
|
||||
gen[0][15] <== 2175722685;
|
||||
gen[1][0] <== 987236498;
|
||||
gen[1][1] <== 2026708495;
|
||||
gen[1][2] <== 2834322438;
|
||||
gen[1][3] <== 3519687471;
|
||||
gen[1][4] <== 2317837230;
|
||||
gen[1][5] <== 1539984344;
|
||||
gen[1][6] <== 1247758430;
|
||||
gen[1][7] <== 3000819273;
|
||||
gen[1][8] <== 2283766033;
|
||||
gen[1][9] <== 2684405083;
|
||||
gen[1][10] <== 614824730;
|
||||
gen[1][11] <== 4060739328;
|
||||
gen[1][12] <== 3480756989;
|
||||
gen[1][13] <== 3236609961;
|
||||
gen[1][14] <== 1449341676;
|
||||
gen[1][15] <== 2111715421;
|
||||
|
||||
}
|
||||
|
||||
template GetBrainpoolOrder(CHUNK_SIZE, CHUNK_NUMBER){
|
||||
|
||||
assert((CHUNK_SIZE == 32 && CHUNK_NUMBER == 16));
|
||||
|
||||
signal output order[CHUNK_NUMBER];
|
||||
|
||||
order[0] <== 2628321385;
|
||||
order[1] <== 3045561986;
|
||||
order[2] <== 140368605;
|
||||
order[3] <== 498193281;
|
||||
order[4] <== 2141982791;
|
||||
order[5] <== 1099325721;
|
||||
order[6] <== 1286153753;
|
||||
order[7] <== 1430150209;
|
||||
order[8] <== 1882392688;
|
||||
order[9] <== 3596852426;
|
||||
order[10] <== 3016348174;
|
||||
order[11] <== 3408956851;
|
||||
order[12] <== 868875271;
|
||||
order[13] <== 1070917294;
|
||||
order[14] <== 3689530507;
|
||||
order[15] <== 2866650552;
|
||||
}
|
||||
|
||||
template BrainpoolGeneratorMultiplication(CHUNK_SIZE, CHUNK_NUMBER){
|
||||
var STRIDE = 8;
|
||||
signal input scalar[CHUNK_NUMBER];
|
||||
signal output out[2][CHUNK_NUMBER];
|
||||
|
||||
component n2b[CHUNK_NUMBER];
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
n2b[i] = Num2Bits(CHUNK_SIZE);
|
||||
n2b[i].in <== scalar[i];
|
||||
}
|
||||
|
||||
var NUM_STRIDES = div_ceil(CHUNK_SIZE * CHUNK_NUMBER, STRIDE);
|
||||
// power[i][j] contains: [j * (1 << STRIDE * i) * G] for 1 <= j < (1 << STRIDE)
|
||||
var POWERS[NUM_STRIDES][2 ** STRIDE][2][CHUNK_NUMBER];
|
||||
POWERS = get_g_pow_stride8_table_brainpool512(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
|
||||
var DUMMY_HOLDER[2][CHUNK_NUMBER] = get_dummy_point(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
var DUMMY[2][CHUNK_NUMBER];
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) DUMMY[0][i] = DUMMY_HOLDER[0][i];
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) DUMMY[1][i] = DUMMY_HOLDER[1][i];
|
||||
|
||||
component selectors[NUM_STRIDES];
|
||||
for (var i = 0; i < NUM_STRIDES; i++) {
|
||||
selectors[i] = Bits2Num(STRIDE);
|
||||
for (var j = 0; j < STRIDE; j++) {
|
||||
var bit_idx1 = (i * STRIDE + j) \ CHUNK_SIZE;
|
||||
var bit_idx2 = (i * STRIDE + j) % CHUNK_SIZE;
|
||||
if (bit_idx1 < CHUNK_NUMBER) {
|
||||
selectors[i].in[j] <== n2b[bit_idx1].out[bit_idx2];
|
||||
} else {
|
||||
selectors[i].in[j] <== 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// multiplexers[i][l].out will be the coordinates of:
|
||||
// selectors[i].out * (2 ** (i * STRIDE)) * G if selectors[i].out is non-zero
|
||||
// (2 ** 255) * G if selectors[i].out is zero
|
||||
component multiplexers[NUM_STRIDES][2];
|
||||
// select from CHUNK_NUMBER-register outputs using a 2 ** STRIDE bit selector
|
||||
for (var i = 0; i < NUM_STRIDES; i++) {
|
||||
for (var l = 0; l < 2; l++) {
|
||||
multiplexers[i][l] = Multiplexer(CHUNK_NUMBER, (1 << STRIDE));
|
||||
multiplexers[i][l].sel <== selectors[i].out;
|
||||
for (var idx = 0; idx < CHUNK_NUMBER; idx++) {
|
||||
multiplexers[i][l].inp[0][idx] <== DUMMY[l][idx];
|
||||
for (var j = 1; j < (1 << STRIDE); j++) {
|
||||
multiplexers[i][l].inp[j][idx] <== POWERS[i][j][l][idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component isZero[NUM_STRIDES];
|
||||
for (var i = 0; i < NUM_STRIDES; i++) {
|
||||
isZero[i] = IsZero();
|
||||
isZero[i].in <== selectors[i].out;
|
||||
}
|
||||
|
||||
// hasPrevNonZero[i] = 1 if at least one of the selections in privkey up to STRIDE i is non-zero
|
||||
component hasPrevNonZero[NUM_STRIDES];
|
||||
hasPrevNonZero[0] = OR();
|
||||
hasPrevNonZero[0].a <== 0;
|
||||
hasPrevNonZero[0].b <== 1 - isZero[0].out;
|
||||
for (var i = 1; i < NUM_STRIDES; i++) {
|
||||
hasPrevNonZero[i] = OR();
|
||||
hasPrevNonZero[i].a <== hasPrevNonZero[i - 1].out;
|
||||
hasPrevNonZero[i].b <== 1 - isZero[i].out;
|
||||
}
|
||||
|
||||
signal partial[NUM_STRIDES][2][CHUNK_NUMBER];
|
||||
for (var idx = 0; idx < CHUNK_NUMBER; idx++) {
|
||||
for (var l = 0; l < 2; l++) {
|
||||
partial[0][l][idx] <== multiplexers[0][l].out[idx];
|
||||
}
|
||||
}
|
||||
|
||||
component adders[NUM_STRIDES - 1];
|
||||
signal intermed1[NUM_STRIDES - 1][2][CHUNK_NUMBER];
|
||||
signal intermed2[NUM_STRIDES - 1][2][CHUNK_NUMBER];
|
||||
for (var i = 1; i < NUM_STRIDES; i++) {
|
||||
adders[i - 1] = BrainpoolAddUnequal(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
for (var idx = 0; idx < CHUNK_NUMBER; idx++) {
|
||||
for (var l = 0; l < 2; l++) {
|
||||
adders[i - 1].point1[l][idx] <== partial[i - 1][l][idx];
|
||||
adders[i - 1].point2[l][idx] <== multiplexers[i][l].out[idx];
|
||||
}
|
||||
}
|
||||
|
||||
// partial[i] = hasPrevNonZero[i - 1] * ((1 - isZero[i]) * adders[i - 1].out + isZero[i] * partial[i - 1][0][idx])
|
||||
// + (1 - hasPrevNonZero[i - 1]) * (1 - isZero[i]) * multiplexers[i]
|
||||
for (var idx = 0; idx < CHUNK_NUMBER; idx++) {
|
||||
for (var l = 0; l < 2; l++) {
|
||||
intermed1[i - 1][l][idx] <== isZero[i].out * (partial[i - 1][l][idx] - adders[i - 1].out[l][idx]) + adders[i - 1].out[l][idx];
|
||||
intermed2[i - 1][l][idx] <== multiplexers[i][l].out[idx] - isZero[i].out * multiplexers[i][l].out[idx];
|
||||
partial[i][l][idx] <== hasPrevNonZero[i - 1].out * (intermed1[i - 1][l][idx] - intermed2[i - 1][l][idx]) + intermed2[i - 1][l][idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
for (var l = 0; l < 2; l++) {
|
||||
out[l][i] <== partial[NUM_STRIDES - 1][l][i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template BrainpoolPrecomputePipinger(CHUNK_SIZE, CHUNK_NUMBER, WINDOW_SIZE){
|
||||
signal input in[2][CHUNK_NUMBER];
|
||||
|
||||
var PRECOMPUTE_NUMBER = 2 ** WINDOW_SIZE;
|
||||
|
||||
signal output out[PRECOMPUTE_NUMBER][2][CHUNK_NUMBER];
|
||||
|
||||
for (var i = 0; i < 2; i++){
|
||||
for (var j = 0; j < CHUNK_NUMBER; j++){
|
||||
out[0][i][j] <== 0;
|
||||
}
|
||||
}
|
||||
|
||||
out[1] <== in;
|
||||
|
||||
component doublers[PRECOMPUTE_NUMBER\2 - 1];
|
||||
component adders [PRECOMPUTE_NUMBER\2 - 1];
|
||||
|
||||
for (var i = 2; i < PRECOMPUTE_NUMBER; i++){
|
||||
if (i % 2 == 0){
|
||||
doublers[i\2 - 1] = BrainpoolDouble(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
doublers[i\2 - 1].in <== out[i\2];
|
||||
doublers[i\2 - 1].out ==> out[i];
|
||||
}
|
||||
else
|
||||
{
|
||||
adders[i\2 - 1] = BrainpoolAddUnequal(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
adders[i\2 - 1].point1 <== out[1];
|
||||
adders[i\2 - 1].point2 <== out[i - 1];
|
||||
adders[i\2 - 1].out ==> out[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template BrainpoolPipingerMult(CHUNK_SIZE, CHUNK_NUMBER, WINDOW_SIZE){
|
||||
|
||||
assert(WINDOW_SIZE == 4);
|
||||
|
||||
signal input point[2][CHUNK_NUMBER];
|
||||
signal input scalar [CHUNK_NUMBER];
|
||||
|
||||
signal output out[2][CHUNK_NUMBER];
|
||||
|
||||
var PRECOMPUTE_NUMBER = 2 ** WINDOW_SIZE;
|
||||
|
||||
signal precomputed[PRECOMPUTE_NUMBER][2][CHUNK_NUMBER];
|
||||
|
||||
component precompute = BrainpoolPrecomputePipinger(CHUNK_SIZE, CHUNK_NUMBER, WINDOW_SIZE);
|
||||
precompute.in <== point;
|
||||
precompute.out ==> precomputed;
|
||||
|
||||
var DOUBLERS_NUMBER = CHUNK_SIZE*CHUNK_NUMBER - WINDOW_SIZE;
|
||||
var ADDERS_NUMBER = CHUNK_SIZE*CHUNK_NUMBER \ WINDOW_SIZE;
|
||||
|
||||
component doublers[DOUBLERS_NUMBER];
|
||||
component adders [ADDERS_NUMBER];
|
||||
component bits2Num[ADDERS_NUMBER];
|
||||
component num2Bits[CHUNK_NUMBER];
|
||||
|
||||
signal res [ADDERS_NUMBER + 1][2][CHUNK_NUMBER];
|
||||
|
||||
signal tmp [ADDERS_NUMBER][PRECOMPUTE_NUMBER][2][CHUNK_NUMBER];
|
||||
|
||||
signal tmp2[ADDERS_NUMBER] [2] [CHUNK_NUMBER];
|
||||
signal tmp3[ADDERS_NUMBER] [2][2][CHUNK_NUMBER];
|
||||
signal tmp4[ADDERS_NUMBER] [2] [CHUNK_NUMBER];
|
||||
signal tmp5[ADDERS_NUMBER] [2][2][CHUNK_NUMBER];
|
||||
signal tmp6[ADDERS_NUMBER - 1][2][2][CHUNK_NUMBER];
|
||||
signal tmp7[ADDERS_NUMBER - 1][2] [CHUNK_NUMBER];
|
||||
|
||||
component equals [ADDERS_NUMBER][PRECOMPUTE_NUMBER][2][CHUNK_NUMBER];
|
||||
component zeroEquals[ADDERS_NUMBER];
|
||||
component tmpEquals [ADDERS_NUMBER];
|
||||
|
||||
component g = BrainpoolGetGenerator(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
signal gen[2][CHUNK_NUMBER];
|
||||
gen <== g.gen;
|
||||
|
||||
signal scalarBits[CHUNK_NUMBER*CHUNK_SIZE];
|
||||
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++){
|
||||
num2Bits[i] = Num2Bits(CHUNK_SIZE);
|
||||
num2Bits[i].in <== scalar[i];
|
||||
|
||||
for (var j = 0; j < CHUNK_SIZE; j++){
|
||||
scalarBits[CHUNK_NUMBER*CHUNK_SIZE - CHUNK_SIZE * (i + 1) + j] <== num2Bits[i].out[CHUNK_SIZE - 1 - j];
|
||||
}
|
||||
}
|
||||
|
||||
res[0] <== precomputed[0];
|
||||
|
||||
for (var i = 0; i < CHUNK_NUMBER*CHUNK_SIZE; i += WINDOW_SIZE){
|
||||
adders[i\WINDOW_SIZE] = BrainpoolAddUnequal(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
bits2Num[i\WINDOW_SIZE] = Bits2Num(WINDOW_SIZE);
|
||||
for (var j = 0; j < WINDOW_SIZE; j++){
|
||||
bits2Num[i\WINDOW_SIZE].in[j] <== scalarBits[i + (WINDOW_SIZE - 1) - j];
|
||||
}
|
||||
|
||||
tmpEquals[i\WINDOW_SIZE] = IsEqual();
|
||||
tmpEquals[i\WINDOW_SIZE].in[0] <== 0;
|
||||
tmpEquals[i\WINDOW_SIZE].in[1] <== res[i\WINDOW_SIZE][0][0];
|
||||
|
||||
if (i != 0){
|
||||
for (var j = 0; j < WINDOW_SIZE; j++){
|
||||
doublers[i + j - WINDOW_SIZE] = BrainpoolDouble(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
|
||||
if (j == 0){
|
||||
for (var axis_idx = 0; axis_idx < 2; axis_idx++){
|
||||
for (var coor_idx = 0; coor_idx < CHUNK_NUMBER; coor_idx ++){
|
||||
tmp6[i\WINDOW_SIZE - 1][0][axis_idx][coor_idx] <== tmpEquals[i\WINDOW_SIZE].out * gen[axis_idx][coor_idx];
|
||||
tmp6[i\WINDOW_SIZE - 1][1][axis_idx][coor_idx] <== (1 - tmpEquals[i\WINDOW_SIZE].out) * res[i\WINDOW_SIZE][axis_idx][coor_idx];
|
||||
tmp7[i\WINDOW_SIZE - 1] [axis_idx][coor_idx] <== tmp6[i\WINDOW_SIZE - 1][0][axis_idx][coor_idx]
|
||||
+ tmp6[i\WINDOW_SIZE - 1][1][axis_idx][coor_idx];
|
||||
}
|
||||
}
|
||||
|
||||
doublers[i + j - WINDOW_SIZE].in <== tmp7[i\WINDOW_SIZE - 1];
|
||||
}
|
||||
else
|
||||
{
|
||||
doublers[i + j - WINDOW_SIZE].in <== doublers[i + j - 1 - WINDOW_SIZE].out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var point_idx = 0; point_idx < PRECOMPUTE_NUMBER; point_idx++){
|
||||
for (var axis_idx = 0; axis_idx < 2; axis_idx++){
|
||||
for (var coor_idx = 0; coor_idx < CHUNK_NUMBER; coor_idx++){
|
||||
equals[i\WINDOW_SIZE][point_idx][axis_idx][coor_idx] = IsEqual();
|
||||
equals[i\WINDOW_SIZE][point_idx][axis_idx][coor_idx].in[0] <== point_idx;
|
||||
equals[i\WINDOW_SIZE][point_idx][axis_idx][coor_idx].in[1] <== bits2Num[i\WINDOW_SIZE].out;
|
||||
tmp [i\WINDOW_SIZE][point_idx][axis_idx][coor_idx] <== precomputed[point_idx][axis_idx][coor_idx] *
|
||||
equals[i\WINDOW_SIZE][point_idx][axis_idx][coor_idx].out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var axis_idx = 0; axis_idx < 2; axis_idx++){
|
||||
for (var coor_idx = 0; coor_idx < CHUNK_NUMBER; coor_idx++){
|
||||
tmp2[i\WINDOW_SIZE] [axis_idx][coor_idx] <==
|
||||
tmp[i\WINDOW_SIZE][0] [axis_idx][coor_idx] +
|
||||
tmp[i\WINDOW_SIZE][1] [axis_idx][coor_idx] +
|
||||
tmp[i\WINDOW_SIZE][2] [axis_idx][coor_idx] +
|
||||
tmp[i\WINDOW_SIZE][3] [axis_idx][coor_idx] +
|
||||
tmp[i\WINDOW_SIZE][4] [axis_idx][coor_idx] +
|
||||
tmp[i\WINDOW_SIZE][5] [axis_idx][coor_idx] +
|
||||
tmp[i\WINDOW_SIZE][6] [axis_idx][coor_idx] +
|
||||
tmp[i\WINDOW_SIZE][7] [axis_idx][coor_idx] +
|
||||
tmp[i\WINDOW_SIZE][8] [axis_idx][coor_idx] +
|
||||
tmp[i\WINDOW_SIZE][9] [axis_idx][coor_idx] +
|
||||
tmp[i\WINDOW_SIZE][10][axis_idx][coor_idx] +
|
||||
tmp[i\WINDOW_SIZE][11][axis_idx][coor_idx] +
|
||||
tmp[i\WINDOW_SIZE][12][axis_idx][coor_idx] +
|
||||
tmp[i\WINDOW_SIZE][13][axis_idx][coor_idx] +
|
||||
tmp[i\WINDOW_SIZE][14][axis_idx][coor_idx] +
|
||||
tmp[i\WINDOW_SIZE][15][axis_idx][coor_idx];
|
||||
}
|
||||
}
|
||||
|
||||
if (i == 0){
|
||||
|
||||
adders[i\WINDOW_SIZE].point1 <== res [i\WINDOW_SIZE];
|
||||
adders[i\WINDOW_SIZE].point2 <== tmp2[i\WINDOW_SIZE];
|
||||
res[i\WINDOW_SIZE + 1] <== tmp2[i\WINDOW_SIZE];
|
||||
|
||||
} else {
|
||||
|
||||
adders[i\WINDOW_SIZE].point1 <== doublers[i - 1].out;
|
||||
adders[i\WINDOW_SIZE].point2 <== tmp2[i\WINDOW_SIZE];
|
||||
|
||||
zeroEquals[i\WINDOW_SIZE] = IsEqual();
|
||||
|
||||
zeroEquals[i\WINDOW_SIZE].in[0]<== 0;
|
||||
zeroEquals[i\WINDOW_SIZE].in[1]<== tmp2[i\WINDOW_SIZE][0][0];
|
||||
|
||||
for (var axis_idx = 0; axis_idx < 2; axis_idx++){
|
||||
for(var coor_idx = 0; coor_idx < CHUNK_NUMBER; coor_idx++){
|
||||
|
||||
tmp3[i\WINDOW_SIZE][0][axis_idx][coor_idx] <== adders [i\WINDOW_SIZE].out[axis_idx][coor_idx] * (1 - zeroEquals[i\WINDOW_SIZE].out);
|
||||
tmp3[i\WINDOW_SIZE][1][axis_idx][coor_idx] <== zeroEquals[i\WINDOW_SIZE].out * doublers[i-1].out[axis_idx][coor_idx];
|
||||
tmp4[i\WINDOW_SIZE] [axis_idx][coor_idx] <== tmp3[i\WINDOW_SIZE][0][axis_idx][coor_idx] + tmp3[i\WINDOW_SIZE][1][axis_idx][coor_idx];
|
||||
tmp5[i\WINDOW_SIZE][0][axis_idx][coor_idx] <== (1 - tmpEquals[i\WINDOW_SIZE].out) * tmp4[i\WINDOW_SIZE] [axis_idx][coor_idx];
|
||||
tmp5[i\WINDOW_SIZE][1][axis_idx][coor_idx] <== tmpEquals[i\WINDOW_SIZE].out * tmp2[i\WINDOW_SIZE] [axis_idx][coor_idx];
|
||||
|
||||
res[i\WINDOW_SIZE + 1][axis_idx][coor_idx] <== tmp5[i\WINDOW_SIZE][0][axis_idx][coor_idx] + tmp5[i\WINDOW_SIZE][1][axis_idx][coor_idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
out <== res[ADDERS_NUMBER];
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
pragma circom 2.1.6;
|
||||
|
||||
function get_order(CHUNK_SIZE,CHUNK_NUMBER){
|
||||
|
||||
assert ((CHUNK_SIZE == 32) && (CHUNK_NUMBER == 16));
|
||||
|
||||
var ORDER[16];
|
||||
|
||||
ORDER[0] = 2628321385;
|
||||
ORDER[1] = 3045561986;
|
||||
ORDER[2] = 140368605;
|
||||
ORDER[3] = 498193281;
|
||||
ORDER[4] = 2141982791;
|
||||
ORDER[5] = 1099325721;
|
||||
ORDER[6] = 1286153753;
|
||||
ORDER[7] = 1430150209;
|
||||
ORDER[8] = 1882392688;
|
||||
ORDER[9] = 3596852426;
|
||||
ORDER[10] = 3016348174;
|
||||
ORDER[11] = 3408956851;
|
||||
ORDER[12] = 868875271;
|
||||
ORDER[13] = 1070917294;
|
||||
ORDER[14] = 3689530507;
|
||||
ORDER[15] = 2866650552;
|
||||
|
||||
return ORDER;
|
||||
}
|
||||
|
||||
function get_params(CHUNK_SIZE,CHUNK_NUMBER){
|
||||
|
||||
assert ((CHUNK_SIZE == 32) && (CHUNK_NUMBER == 16));
|
||||
|
||||
var A[16];
|
||||
var P[16];
|
||||
var B[16];
|
||||
|
||||
var PARAMS[3][16];
|
||||
|
||||
A[0] = 2013041866;
|
||||
A[1] = 3888229453;
|
||||
A[2] = 737331129;
|
||||
A[3] = 2131826599;
|
||||
A[4] = 2342176949;
|
||||
A[5] = 170848713;
|
||||
A[6] = 2821012129;
|
||||
A[7] = 770530650;
|
||||
A[8] = 3935855548;
|
||||
A[9] = 2821997002;
|
||||
A[10] = 1039734288;
|
||||
A[11] = 2496388493;
|
||||
A[12] = 2887994565;
|
||||
A[13] = 3794956613;
|
||||
A[14] = 2338339721;
|
||||
A[15] = 2016453425;
|
||||
B[0] = 2148988707;
|
||||
B[1] = 671726947;
|
||||
B[2] = 1589306845;
|
||||
B[3] = 2554351799;
|
||||
B[4] = 3691527783;
|
||||
B[5] = 2013041866;
|
||||
B[6] = 3888229453;
|
||||
B[7] = 737331129;
|
||||
B[8] = 2131826599;
|
||||
B[9] = 2342176949;
|
||||
B[10] = 170848713;
|
||||
B[11] = 2821012129;
|
||||
B[12] = 770530650;
|
||||
B[13] = 3935855548;
|
||||
B[14] = 2821997002;
|
||||
B[15] = 1039734288;
|
||||
P[0] = 1480214771;
|
||||
P[1] = 682254422;
|
||||
P[2] = 763545221;
|
||||
P[3] = 679608111;
|
||||
P[4] = 3869475046;
|
||||
P[5] = 2932711722;
|
||||
P[6] = 2613471298;
|
||||
P[7] = 2102237952;
|
||||
P[8] = 1882392689;
|
||||
P[9] = 3596852426;
|
||||
P[10] = 3016348174;
|
||||
P[11] = 3408956851;
|
||||
P[12] = 868875271;
|
||||
P[13] = 1070917294;
|
||||
P[14] = 3689530507;
|
||||
P[15] = 2866650552;
|
||||
|
||||
PARAMS[0] = A;
|
||||
PARAMS[1] = B;
|
||||
PARAMS[2] = P;
|
||||
|
||||
return PARAMS;
|
||||
}
|
||||
|
||||
function get_dummy_point(CHUNK_SIZE,CHUNK_NUMBER){
|
||||
|
||||
assert ((CHUNK_SIZE == 32) && (CHUNK_NUMBER == 16));
|
||||
|
||||
var DUMMY[2][16];
|
||||
|
||||
DUMMY[0][0] = 2991692514;
|
||||
DUMMY[0][1] = 2420662644;
|
||||
DUMMY[0][2] = 3740366838;
|
||||
DUMMY[0][3] = 4192643162;
|
||||
DUMMY[0][4] = 2665451377;
|
||||
DUMMY[0][5] = 1405048891;
|
||||
DUMMY[0][6] = 330027888;
|
||||
DUMMY[0][7] = 3991377152;
|
||||
DUMMY[0][8] = 1602371801;
|
||||
DUMMY[0][9] = 1938229223;
|
||||
DUMMY[0][10] = 3356273039;
|
||||
DUMMY[0][11] = 2948957363;
|
||||
DUMMY[0][12] = 8932367;
|
||||
DUMMY[0][13] = 2671247543;
|
||||
DUMMY[0][14] = 1806633290;
|
||||
DUMMY[0][15] = 1215488118;
|
||||
DUMMY[1][0] = 4195201912;
|
||||
DUMMY[1][1] = 2534266230;
|
||||
DUMMY[1][2] = 1636447374;
|
||||
DUMMY[1][3] = 601230195;
|
||||
DUMMY[1][4] = 3115853024;
|
||||
DUMMY[1][5] = 1634595982;
|
||||
DUMMY[1][6] = 830636451;
|
||||
DUMMY[1][7] = 3935918568;
|
||||
DUMMY[1][8] = 158510016;
|
||||
DUMMY[1][9] = 2816471555;
|
||||
DUMMY[1][10] = 2764490350;
|
||||
DUMMY[1][11] = 2232664525;
|
||||
DUMMY[1][12] = 1222395589;
|
||||
DUMMY[1][13] = 1573560398;
|
||||
DUMMY[1][14] = 1496673685;
|
||||
DUMMY[1][15] = 2262337618;
|
||||
|
||||
return DUMMY;
|
||||
}
|
||||
|
||||
|
||||
540681
circuits/circuits/utils/brainpool/brainpoolP512r1/brainpoolPows.circom
Normal file
540681
circuits/circuits/utils/brainpool/brainpoolP512r1/brainpoolPows.circom
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,113 @@
|
||||
pragma circom 2.1.6;
|
||||
|
||||
include "../brainpoolP256r1/circomPairing/curve.circom";
|
||||
include "circomlib/circuits/bitify.circom";
|
||||
include "./brainpool.circom";
|
||||
include "./brainpoolFunc.circom";
|
||||
|
||||
|
||||
template verifyBrainpool(CHUNK_SIZE,CHUNK_NUMBER, ALGO){
|
||||
signal input pubkey[2 * CHUNK_SIZE * CHUNK_NUMBER];
|
||||
signal input signature[2 * CHUNK_SIZE * CHUNK_NUMBER];
|
||||
signal input hashed[ALGO];
|
||||
|
||||
signal pubkeyChunked[2][CHUNK_NUMBER];
|
||||
signal signatureChunked[2][CHUNK_NUMBER];
|
||||
|
||||
component bits2NumInput[2*2*CHUNK_NUMBER];
|
||||
|
||||
for (var i = 0; i < 2; i++){
|
||||
for (var j = 0; j < CHUNK_NUMBER; j++){
|
||||
bits2NumInput[i*CHUNK_NUMBER+j] = Bits2Num(CHUNK_SIZE);
|
||||
bits2NumInput[(i+2)*CHUNK_NUMBER+j] = Bits2Num(CHUNK_SIZE);
|
||||
|
||||
for (var z = 0; z < CHUNK_SIZE; z++){
|
||||
bits2NumInput[i*CHUNK_NUMBER+j].in[z] <== pubkey[i*CHUNK_NUMBER*CHUNK_SIZE + CHUNK_SIZE * j + (CHUNK_SIZE - 1) - z];
|
||||
bits2NumInput[(i+2)*CHUNK_NUMBER+j].in[z] <== signature[i*CHUNK_NUMBER*CHUNK_SIZE + CHUNK_SIZE * j + (CHUNK_SIZE - 1) - z];
|
||||
}
|
||||
bits2NumInput[i*CHUNK_NUMBER+j].out ==> pubkeyChunked[i][(CHUNK_NUMBER - 1) - j];
|
||||
bits2NumInput[(i+2)*CHUNK_NUMBER+j].out ==> signatureChunked[i][(CHUNK_NUMBER - 1) - j];
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
signal hashedMessageBits[CHUNK_SIZE*CHUNK_NUMBER];
|
||||
var SHIFT = CHUNK_SIZE*CHUNK_NUMBER - ALGO;
|
||||
for (var i = 0; i < SHIFT; i++){
|
||||
hashedMessageBits[i] <== 0;
|
||||
}
|
||||
for (var i = 0; i < ALGO; i++){
|
||||
hashedMessageBits[i+SHIFT] <== hashed[i];
|
||||
}
|
||||
|
||||
|
||||
signal hashedMessageChunked[CHUNK_NUMBER];
|
||||
|
||||
component bits2Num[CHUNK_NUMBER];
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
bits2Num[i] = Bits2Num(CHUNK_SIZE);
|
||||
for (var j = 0; j < CHUNK_SIZE; j++) {
|
||||
bits2Num[i].in[CHUNK_SIZE - 1 - j] <== hashedMessageBits[i*CHUNK_SIZE+j];
|
||||
}
|
||||
hashedMessageChunked[CHUNK_NUMBER - 1 - i] <== bits2Num[i].out;
|
||||
}
|
||||
|
||||
component getOrder = GetBrainpoolOrder(CHUNK_SIZE,CHUNK_NUMBER);
|
||||
signal order[CHUNK_NUMBER];
|
||||
order <== getOrder.order;
|
||||
|
||||
signal sinv[CHUNK_NUMBER];
|
||||
|
||||
component modInv = BigModInv(CHUNK_SIZE,CHUNK_NUMBER);
|
||||
|
||||
modInv.in <== signatureChunked[1];
|
||||
modInv.p <== order;
|
||||
modInv.out ==> sinv;
|
||||
|
||||
|
||||
signal sh[CHUNK_NUMBER];
|
||||
|
||||
component mult = BigMultModP(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
|
||||
mult.a <== sinv;
|
||||
mult.b <== hashedMessageChunked;
|
||||
mult.p <== order;
|
||||
sh <== mult.out;
|
||||
|
||||
signal sr[CHUNK_NUMBER];
|
||||
|
||||
component mult2 = BigMultModP(CHUNK_SIZE, CHUNK_NUMBER);
|
||||
|
||||
mult2.a <== sinv;
|
||||
mult2.b <== signatureChunked[0];
|
||||
mult2.p <== order;
|
||||
sr <== mult2.out;
|
||||
|
||||
|
||||
signal tmpPoint1[2][CHUNK_NUMBER];
|
||||
signal tmpPoint2[2][CHUNK_NUMBER];
|
||||
|
||||
component scalarMult1 = BrainpoolGeneratorMultiplication(CHUNK_SIZE,CHUNK_NUMBER);
|
||||
component scalarMult2 = BrainpoolPipingerMult(CHUNK_SIZE,CHUNK_NUMBER, 4);
|
||||
|
||||
scalarMult1.scalar <== sh;
|
||||
|
||||
tmpPoint1 <== scalarMult1.out;
|
||||
|
||||
|
||||
scalarMult2.scalar <== sr;
|
||||
scalarMult2.point <== pubkeyChunked;
|
||||
|
||||
tmpPoint2 <== scalarMult2.out;
|
||||
|
||||
|
||||
signal verifyX[CHUNK_NUMBER];
|
||||
|
||||
component sumPoints = BrainpoolAddUnequal(CHUNK_SIZE,CHUNK_NUMBER);
|
||||
|
||||
sumPoints.point1 <== tmpPoint1;
|
||||
sumPoints.point2 <== tmpPoint2;
|
||||
|
||||
verifyX <== sumPoints.out[0];
|
||||
verifyX === signatureChunked[0];
|
||||
}
|
||||
59
circuits/tests/other_circuits/commitment.test.ts
Normal file
59
circuits/tests/other_circuits/commitment.test.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import { expect } from 'chai';
|
||||
import path from 'path';
|
||||
import { wasm as wasm_tester } from 'circom_tester';
|
||||
|
||||
import { formatDg2Hash, formatMrz, packBytes } from '../../../common/src/utils/utils';
|
||||
import { getLeaf, generateCommitment } from '../../../common/src/utils/pubkeyTree';
|
||||
|
||||
import { genMockPassportData } from '../../../common/src/utils/genMockPassportData';
|
||||
import { formatInput } from '../../../common/src/utils/generateInputs';
|
||||
|
||||
describe('commitment hasher', function () {
|
||||
this.timeout(0);
|
||||
let circuit;
|
||||
|
||||
this.beforeAll(async () => {
|
||||
const circuitPath = path.resolve(
|
||||
__dirname,
|
||||
'../../circuits/tests/utils/commitment_tester.circom'
|
||||
);
|
||||
circuit = await wasm_tester(circuitPath, {
|
||||
include: [
|
||||
'node_modules',
|
||||
'./node_modules/@zk-kit/binary-merkle-root.circom/src',
|
||||
'./node_modules/circomlib/circuits',
|
||||
],
|
||||
});
|
||||
});
|
||||
describe('generate commitment', async () => {
|
||||
const passportData = genMockPassportData('rsa_sha256', 'FRA', '000101', '300101');
|
||||
const formattedMrz = formatMrz(passportData.mrz);
|
||||
const dg2HashFormatted = formatDg2Hash(passportData.dg2Hash);
|
||||
const secret = 0;
|
||||
const attestation_id = 1;
|
||||
const leaf = getLeaf(passportData.dsc);
|
||||
const inputs = {
|
||||
secret: formatInput(secret),
|
||||
attestation_id: formatInput(attestation_id),
|
||||
leaf: formatInput(leaf),
|
||||
dg1: formatInput(formattedMrz),
|
||||
dg2_hash: dg2HashFormatted,
|
||||
};
|
||||
|
||||
it('commitment from circuits should be equal to commitment from js', async () => {
|
||||
const witness = await circuit.calculateWitness(inputs, true);
|
||||
const leafValueCircom = (await circuit.getOutput(witness, ['out'])).out;
|
||||
console.log('\x1b[34m', 'hashValueCircom: ', leafValueCircom, '\x1b[0m');
|
||||
const mrz_bytes_packed = packBytes(formattedMrz);
|
||||
const commitment = generateCommitment(
|
||||
BigInt(secret).toString(),
|
||||
BigInt(attestation_id).toString(),
|
||||
BigInt(leaf).toString(),
|
||||
mrz_bytes_packed,
|
||||
dg2HashFormatted
|
||||
);
|
||||
console.log('\x1b[34m', 'commitment in js : ', commitment, '\x1b[0m');
|
||||
expect(BigInt(leafValueCircom).toString()).to.equal(BigInt(commitment).toString());
|
||||
});
|
||||
});
|
||||
});
|
||||
46
circuits/tests/other_circuits/custom_hasher.test.ts
Normal file
46
circuits/tests/other_circuits/custom_hasher.test.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { expect } from 'chai';
|
||||
import path from 'path';
|
||||
import { wasm as wasm_tester } from 'circom_tester';
|
||||
import { customHasher } from '../../../common/src/utils/pubkeyTree';
|
||||
import { formatInput } from '../../../common/src/utils/generateInputs';
|
||||
|
||||
describe('CustomHasher', function () {
|
||||
this.timeout(0);
|
||||
let circuit;
|
||||
|
||||
this.beforeAll(async () => {
|
||||
const circuitPath = path.resolve(
|
||||
__dirname,
|
||||
'../../circuits/tests/utils/customHasher_tester.circom'
|
||||
);
|
||||
circuit = await wasm_tester(circuitPath, {
|
||||
include: [
|
||||
'node_modules',
|
||||
'./node_modules/@zk-kit/binary-merkle-root.circom/src',
|
||||
'./node_modules/circomlib/circuits',
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
describe('custom hasher', async () => {
|
||||
const randomNumbers = Array(16)
|
||||
.fill(0)
|
||||
.map(() => {
|
||||
const maxVal = BigInt(2) ** BigInt(250);
|
||||
const randomVal =
|
||||
BigInt(Math.floor(Math.random() * Number.MAX_SAFE_INTEGER)) *
|
||||
BigInt(Math.floor(Math.random() * Number.MAX_SAFE_INTEGER));
|
||||
return (randomVal % maxVal).toString();
|
||||
});
|
||||
const inputs = { in: formatInput(randomNumbers) };
|
||||
|
||||
it('customHasher output should be the same between circom and js implementation', async () => {
|
||||
const witness = await circuit.calculateWitness(inputs, true);
|
||||
const hashValueCircom = (await circuit.getOutput(witness, ['out'])).out;
|
||||
console.log('\x1b[34m', 'hashValueCircom: ', hashValueCircom, '\x1b[0m');
|
||||
const hashValueJs = customHasher(randomNumbers);
|
||||
console.log('\x1b[34m', 'hashValueJs: ', hashValueJs, '\x1b[0m');
|
||||
expect(BigInt(hashValueCircom).toString()).to.equal(BigInt(hashValueJs).toString());
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,157 +0,0 @@
|
||||
import { expect } from 'chai';
|
||||
import { X509Certificate } from 'crypto';
|
||||
import path from 'path';
|
||||
import { wasm as wasm_tester } from 'circom_tester';
|
||||
import forge from 'node-forge';
|
||||
|
||||
import {
|
||||
mock_dsc_sha256_rsa_2048,
|
||||
mock_csca_sha256_rsa_2048,
|
||||
mock_dsc_sha1_rsa_2048,
|
||||
mock_csca_sha1_rsa_2048,
|
||||
mock_dsc_sha256_ecdsa,
|
||||
} from '../../../common/src/constants/mockCertificates';
|
||||
import {
|
||||
formatDg2Hash,
|
||||
formatMrz,
|
||||
hexToDecimal,
|
||||
packBytes,
|
||||
splitToWords,
|
||||
toUnsignedByte,
|
||||
} from '../../../common/src/utils/utils';
|
||||
import { getLeaf, customHasher, generateCommitment } from '../../../common/src/utils/pubkeyTree';
|
||||
import {
|
||||
k_dsc,
|
||||
k_dsc_ecdsa,
|
||||
n_dsc,
|
||||
n_dsc_ecdsa,
|
||||
PASSPORT_ATTESTATION_ID,
|
||||
SignatureAlgorithmIndex,
|
||||
} from '../../../common/src/constants/constants';
|
||||
import {
|
||||
parseCertificate,
|
||||
parseDSC,
|
||||
} from '../../../common/src/utils/certificates/handleCertificate';
|
||||
import { genMockPassportData } from '../../../common/src/utils/genMockPassportData';
|
||||
import { generateCircuitInputsInCircuits } from '../utils/generateMockInputsInCircuits';
|
||||
import { formatInput, generateCircuitInputsProve } from '../../../common/src/utils/generateInputs';
|
||||
|
||||
function loadCertificates(dscCertContent: string, cscaCertContent: string) {
|
||||
const dscCert = new X509Certificate(dscCertContent);
|
||||
const cscaCert = new X509Certificate(cscaCertContent);
|
||||
const dscCert_forge = forge.pki.certificateFromPem(dscCertContent);
|
||||
const cscaCert_forge = forge.pki.certificateFromPem(cscaCertContent);
|
||||
|
||||
return { dscCert, cscaCert, dscCert_forge, cscaCert_forge };
|
||||
}
|
||||
|
||||
describe('LeafHasher Light', function () {
|
||||
this.timeout(0);
|
||||
let circuit;
|
||||
|
||||
this.beforeAll(async () => {
|
||||
const circuitPath = path.resolve(
|
||||
__dirname,
|
||||
'../../circuits/tests/utils/leafHasher_tester.circom'
|
||||
);
|
||||
circuit = await wasm_tester(circuitPath, {
|
||||
include: [
|
||||
'node_modules',
|
||||
'./node_modules/@zk-kit/binary-merkle-root.circom/src',
|
||||
'./node_modules/circomlib/circuits',
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
// describe('CustomHasher - getLeaf ECDSA', async () => {
|
||||
// const cert = mock_dsc_sha256_ecdsa;
|
||||
// const { signatureAlgorithm, hashFunction, x, y, bits, curve, exponent } = parseCertificate(cert);
|
||||
// console.log(parseCertificate(cert));
|
||||
// const leaf_light = getLeaf(cert, n_dsc_ecdsa, k_dsc_ecdsa);
|
||||
// console.log('\x1b[34m', 'customHasher output: ', leaf_light, '\x1b[0m');
|
||||
|
||||
// const passportData = genMockPassportData('ecdsa_sha256', 'FRA', '000101', '300101');
|
||||
// const mock_inputs = generateCircuitInputsInCircuits(passportData, 'register');
|
||||
|
||||
// const signatureAlgorithmIndex = SignatureAlgorithmIndex[`${signatureAlgorithm}_${curve || exponent}_${hashFunction}_${bits}`];
|
||||
// console.log('\x1b[34m', 'signatureAlgorithmIndex: ', signatureAlgorithmIndex, '\x1b[0m');
|
||||
// it('should extract and log certificate information', async () => {
|
||||
// const inputs = {
|
||||
// in: mock_inputs.pubKey,
|
||||
// sigAlg: signatureAlgorithmIndex,
|
||||
// };
|
||||
// const witness = await circuit.calculateWitness(inputs, true);
|
||||
// const leafValueCircom = (await circuit.getOutput(witness, ['out'])).out;
|
||||
// console.log('\x1b[34m', 'leafValueCircom: ', leafValueCircom, '\x1b[0m');
|
||||
// expect(leafValueCircom).to.equal(leaf_light);
|
||||
// });
|
||||
// });
|
||||
|
||||
// describe('CustomHasher - customHasher', async () => {
|
||||
// const passportData = genMockPassportData('rsa_sha256', 'FRA', '000101', '300101');
|
||||
// it('should extract and log certificate information', async () => {
|
||||
// const inputs = {
|
||||
// in: passportData.dg2Hash.map((x) => toUnsignedByte(x).toString()),
|
||||
// };
|
||||
// const witness = await circuit.calculateWitness(inputs, true);
|
||||
// const leafValueCircom = (await circuit.getOutput(witness, ['out'])).out;
|
||||
// console.log('\x1b[34m', 'hashValueCircom: ', leafValueCircom, '\x1b[0m');
|
||||
|
||||
// const hashValue = customHasher(passportData.dg2Hash.map((x) => toUnsignedByte(x).toString()));
|
||||
// console.log('\x1b[34m', 'hashValue: ', hashValue, '\x1b[0m');
|
||||
// });
|
||||
// });
|
||||
describe('GenerateCommitment - computeCommitment', async () => {
|
||||
const passportData = genMockPassportData('rsa_sha256', 'FRA', '000101', '300101');
|
||||
const formattedMrz = formatMrz(passportData.mrz);
|
||||
const dg2HashFormatted = formatDg2Hash(passportData.dg2Hash);
|
||||
const secret = 0;
|
||||
const attestation_id = 1;
|
||||
const leaf = getLeaf(passportData.dsc);
|
||||
const inputs = {
|
||||
secret: formatInput(secret),
|
||||
attestation_id: formatInput(attestation_id),
|
||||
leaf: formatInput(leaf),
|
||||
dg1: formatInput(formattedMrz),
|
||||
dg2_hash: dg2HashFormatted,
|
||||
};
|
||||
// console.log(inputs);
|
||||
|
||||
it('should generate the commitment', async () => {
|
||||
const witness = await circuit.calculateWitness(inputs, true);
|
||||
const leafValueCircom = (await circuit.getOutput(witness, ['out'])).out;
|
||||
console.log('\x1b[34m', 'hashValueCircom: ', leafValueCircom, '\x1b[0m');
|
||||
const mrz_bytes_packed = packBytes(formattedMrz);
|
||||
const commitment = generateCommitment(
|
||||
BigInt(secret).toString(),
|
||||
BigInt(attestation_id).toString(),
|
||||
BigInt(leaf).toString(),
|
||||
mrz_bytes_packed,
|
||||
dg2HashFormatted
|
||||
);
|
||||
console.log('\x1b[34m', 'commitment in js : ', commitment, '\x1b[0m');
|
||||
expect(BigInt(leafValueCircom).toString()).to.equal(BigInt(commitment).toString());
|
||||
});
|
||||
});
|
||||
|
||||
// describe('CustomHasher - getLeaf RSA', async () => {
|
||||
// const cert = mock_dsc_sha1_rsa_2048;
|
||||
// const { signatureAlgorithm, hashFunction, modulus, x, y, bits, curve, exponent } =
|
||||
// parseCertificate(cert);
|
||||
// console.log(parseCertificate(cert));
|
||||
// const leaf_light = getLeaf(cert, n_dsc, k_dsc);
|
||||
// console.log('\x1b[34m', 'customHasher: ', leaf_light, '\x1b[0m');
|
||||
// it('should extract and log certificate information', async () => {
|
||||
// const inputs = {
|
||||
// in: splitToWords(BigInt(hexToDecimal(modulus)), n_dsc, k_dsc),
|
||||
// sigAlg:
|
||||
// SignatureAlgorithmIndex[
|
||||
// `${signatureAlgorithm}_${curve || exponent}_${hashFunction}_${bits}`
|
||||
// ],
|
||||
// };
|
||||
// const witness = await circuit.calculateWitness(inputs, true);
|
||||
// const leafValueCircom = (await circuit.getOutput(witness, ['out'])).out;
|
||||
// expect(leafValueCircom).to.equal(leaf_light);
|
||||
// });
|
||||
// });
|
||||
});
|
||||
@@ -18,6 +18,8 @@
|
||||
"axios": "^1.7.2",
|
||||
"buffer": "^6.0.3",
|
||||
"chai": "^4.3.8",
|
||||
"country-emoji": "^1.5.6",
|
||||
"country-iso-3-to-2": "^1.1.1",
|
||||
"elliptic": "^6.5.5",
|
||||
"fs": "^0.0.1-security",
|
||||
"js-sha1": "^0.7.0",
|
||||
|
||||
24
common/src/scripts/generateCountryOptions.ts
Normal file
24
common/src/scripts/generateCountryOptions.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { countryCodes } from '../constants/constants';
|
||||
import getCountryISO2 from "country-iso-3-to-2";
|
||||
import { flag } from 'country-emoji';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
try {
|
||||
console.log('Generating country options...');
|
||||
|
||||
const countryOptions = Object.keys(countryCodes).map((countryCode, index) => ({
|
||||
countryCode,
|
||||
countryName: countryCodes[countryCode as keyof typeof countryCodes],
|
||||
flagEmoji: flag(getCountryISO2(countryCode)),
|
||||
index,
|
||||
}));
|
||||
|
||||
const outputPath = path.join(__dirname, './countryOptions.json');
|
||||
fs.writeFileSync(outputPath, JSON.stringify(countryOptions, null, 2));
|
||||
|
||||
console.log(`Generated country options at ${outputPath}`);
|
||||
} catch (error) {
|
||||
console.error('Error generating country options:', error);
|
||||
process.exit(1);
|
||||
}
|
||||
@@ -65,15 +65,6 @@ export function getLeafCSCA(dsc: string): string {
|
||||
return customHasher([sigAlgIndex, ...pubkeyChunked]);
|
||||
}
|
||||
}
|
||||
export async function getTreeFromTracker(): Promise<LeanIMT> {
|
||||
const response = await axios.get(COMMITMENT_TREE_TRACKER_URL)
|
||||
const imt = new LeanIMT(
|
||||
(a: bigint, b: bigint) => poseidon2([a, b]),
|
||||
[]
|
||||
);
|
||||
imt.import(response.data)
|
||||
return imt
|
||||
}
|
||||
|
||||
export function generateCommitment(secret: string, attestation_id: string, pubkey_leaf: string, mrz_bytes: any[], dg2Hash: any[]) {
|
||||
const dg2Hash2 = customHasher(formatDg2Hash(dg2Hash).map(x => x.toString()));
|
||||
@@ -97,7 +88,6 @@ export async function fetchTreeFromUrl(url: string): Promise<LeanIMT> {
|
||||
}
|
||||
const commitmentMerkleTree = await response.json();
|
||||
console.log("\x1b[90m%s\x1b[0m", "commitment merkle tree: ", commitmentMerkleTree);
|
||||
const tree = new LeanIMT((a, b) => poseidon2([a, b]));
|
||||
tree.import(commitmentMerkleTree);
|
||||
const tree = LeanIMT.import((a, b) => poseidon2([a, b]), commitmentMerkleTree);
|
||||
return tree;
|
||||
}
|
||||
|
||||
@@ -335,6 +335,16 @@ combined-stream@^1.0.8:
|
||||
dependencies:
|
||||
delayed-stream "~1.0.0"
|
||||
|
||||
country-emoji@^1.5.6:
|
||||
version "1.5.6"
|
||||
resolved "https://registry.yarnpkg.com/country-emoji/-/country-emoji-1.5.6.tgz#cca1e637f3eac8cd2c3f2f910213d9a521b1307e"
|
||||
integrity sha512-pSB8OOROfimFc2bcN+H41DuzXYIod/JQ6SgF4pYXkRCm9f8uF1JAJ0vXPhenug6xkpt3Gv33mdypMXB49CJWRA==
|
||||
|
||||
country-iso-3-to-2@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/country-iso-3-to-2/-/country-iso-3-to-2-1.1.1.tgz#9c62d02290f3872d93d40debcdd5ecb1cc468881"
|
||||
integrity sha512-nirmbzPq5vt40WT9YWrWEvruxNt3h0g/h/k5umfo1ctq0ncw4Kwjd3+MKVL1vp5W5npb/fc3gv4E62xa/PZDiA==
|
||||
|
||||
data-view-buffer@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/data-view-buffer/-/data-view-buffer-1.0.1.tgz#8ea6326efec17a2e42620696e671d7d5a8bc66b2"
|
||||
|
||||
7
noir/Nargo.toml
Normal file
7
noir/Nargo.toml
Normal file
@@ -0,0 +1,7 @@
|
||||
[package]
|
||||
name = "noir"
|
||||
type = "bin"
|
||||
authors = [""]
|
||||
compiler_version = ">=0.36.0"
|
||||
|
||||
[dependencies]
|
||||
748
noir/Prover.toml
Normal file
748
noir/Prover.toml
Normal file
@@ -0,0 +1,748 @@
|
||||
|
||||
# inputs from { sigAlg: 'rsa', hashFunction: 'sha256'}
|
||||
|
||||
dg1 = [
|
||||
"97",
|
||||
"91",
|
||||
"95",
|
||||
"31",
|
||||
"88",
|
||||
"80",
|
||||
"60",
|
||||
"70",
|
||||
"82",
|
||||
"65",
|
||||
"68",
|
||||
"85",
|
||||
"80",
|
||||
"79",
|
||||
"78",
|
||||
"84",
|
||||
"60",
|
||||
"60",
|
||||
"65",
|
||||
"76",
|
||||
"80",
|
||||
"72",
|
||||
"79",
|
||||
"78",
|
||||
"83",
|
||||
"69",
|
||||
"60",
|
||||
"72",
|
||||
"85",
|
||||
"71",
|
||||
"72",
|
||||
"85",
|
||||
"69",
|
||||
"83",
|
||||
"60",
|
||||
"65",
|
||||
"76",
|
||||
"66",
|
||||
"69",
|
||||
"82",
|
||||
"84",
|
||||
"60",
|
||||
"60",
|
||||
"60",
|
||||
"60",
|
||||
"60",
|
||||
"60",
|
||||
"60",
|
||||
"60",
|
||||
"49",
|
||||
"53",
|
||||
"65",
|
||||
"65",
|
||||
"56",
|
||||
"49",
|
||||
"50",
|
||||
"51",
|
||||
"52",
|
||||
"52",
|
||||
"70",
|
||||
"82",
|
||||
"65",
|
||||
"48",
|
||||
"48",
|
||||
"48",
|
||||
"49",
|
||||
"48",
|
||||
"49",
|
||||
"49",
|
||||
"77",
|
||||
"51",
|
||||
"48",
|
||||
"48",
|
||||
"49",
|
||||
"48",
|
||||
"49",
|
||||
"53",
|
||||
"60",
|
||||
"60",
|
||||
"60",
|
||||
"60",
|
||||
"60",
|
||||
"60",
|
||||
"60",
|
||||
"60",
|
||||
"60",
|
||||
"60",
|
||||
"60",
|
||||
"60",
|
||||
"60",
|
||||
"60",
|
||||
"48",
|
||||
"50"
|
||||
]
|
||||
dg1_hash_offset = "37"
|
||||
dg2_hash = [
|
||||
"190",
|
||||
"82",
|
||||
"180",
|
||||
"235",
|
||||
"222",
|
||||
"33",
|
||||
"79",
|
||||
"50",
|
||||
"152",
|
||||
"136",
|
||||
"142",
|
||||
"35",
|
||||
"116",
|
||||
"224",
|
||||
"6",
|
||||
"242",
|
||||
"156",
|
||||
"141",
|
||||
"128",
|
||||
"248",
|
||||
"10",
|
||||
"61",
|
||||
"98",
|
||||
"86",
|
||||
"248",
|
||||
"45",
|
||||
"207",
|
||||
"210",
|
||||
"90",
|
||||
"232",
|
||||
"175",
|
||||
"38",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0"
|
||||
]
|
||||
eContent = [
|
||||
"224",
|
||||
"160",
|
||||
"251",
|
||||
"32",
|
||||
"217",
|
||||
"3",
|
||||
"37",
|
||||
"146",
|
||||
"50",
|
||||
"222",
|
||||
"84",
|
||||
"2",
|
||||
"10",
|
||||
"77",
|
||||
"79",
|
||||
"174",
|
||||
"55",
|
||||
"40",
|
||||
"132",
|
||||
"139",
|
||||
"206",
|
||||
"77",
|
||||
"40",
|
||||
"116",
|
||||
"244",
|
||||
"59",
|
||||
"49",
|
||||
"73",
|
||||
"132",
|
||||
"133",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"155",
|
||||
"69",
|
||||
"163",
|
||||
"24",
|
||||
"248",
|
||||
"167",
|
||||
"161",
|
||||
"54",
|
||||
"179",
|
||||
"18",
|
||||
"174",
|
||||
"196",
|
||||
"232",
|
||||
"60",
|
||||
"51",
|
||||
"198",
|
||||
"140",
|
||||
"219",
|
||||
"196",
|
||||
"229",
|
||||
"145",
|
||||
"248",
|
||||
"154",
|
||||
"203",
|
||||
"178",
|
||||
"114",
|
||||
"17",
|
||||
"33",
|
||||
"54",
|
||||
"80",
|
||||
"127",
|
||||
"173",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"190",
|
||||
"82",
|
||||
"180",
|
||||
"235",
|
||||
"222",
|
||||
"33",
|
||||
"79",
|
||||
"50",
|
||||
"152",
|
||||
"136",
|
||||
"142",
|
||||
"35",
|
||||
"116",
|
||||
"224",
|
||||
"6",
|
||||
"242",
|
||||
"156",
|
||||
"141",
|
||||
"128",
|
||||
"248",
|
||||
"10",
|
||||
"61",
|
||||
"98",
|
||||
"86",
|
||||
"248",
|
||||
"45",
|
||||
"207",
|
||||
"210",
|
||||
"90",
|
||||
"232",
|
||||
"175",
|
||||
"38",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"194",
|
||||
"104",
|
||||
"108",
|
||||
"237",
|
||||
"246",
|
||||
"97",
|
||||
"230",
|
||||
"116",
|
||||
"198",
|
||||
"69",
|
||||
"110",
|
||||
"26",
|
||||
"87",
|
||||
"17",
|
||||
"89",
|
||||
"110",
|
||||
"199",
|
||||
"108",
|
||||
"250",
|
||||
"36",
|
||||
"21",
|
||||
"39",
|
||||
"87",
|
||||
"110",
|
||||
"102",
|
||||
"250",
|
||||
"213",
|
||||
"174",
|
||||
"131",
|
||||
"171",
|
||||
"174",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"136",
|
||||
"155",
|
||||
"87",
|
||||
"144",
|
||||
"111",
|
||||
"15",
|
||||
"152",
|
||||
"127",
|
||||
"85",
|
||||
"25",
|
||||
"154",
|
||||
"81",
|
||||
"20",
|
||||
"58",
|
||||
"51",
|
||||
"75",
|
||||
"193",
|
||||
"116",
|
||||
"234",
|
||||
"0",
|
||||
"60",
|
||||
"30",
|
||||
"29",
|
||||
"30",
|
||||
"183",
|
||||
"141",
|
||||
"72",
|
||||
"247",
|
||||
"255",
|
||||
"203",
|
||||
"100",
|
||||
"124",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"41",
|
||||
"234",
|
||||
"106",
|
||||
"78",
|
||||
"31",
|
||||
"11",
|
||||
"114",
|
||||
"137",
|
||||
"237",
|
||||
"17",
|
||||
"92",
|
||||
"71",
|
||||
"134",
|
||||
"47",
|
||||
"62",
|
||||
"78",
|
||||
"189",
|
||||
"233",
|
||||
"201",
|
||||
"214",
|
||||
"53",
|
||||
"4",
|
||||
"47",
|
||||
"189",
|
||||
"201",
|
||||
"133",
|
||||
"6",
|
||||
"121",
|
||||
"34",
|
||||
"131",
|
||||
"64",
|
||||
"142",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"91",
|
||||
"222",
|
||||
"210",
|
||||
"193",
|
||||
"62",
|
||||
"222",
|
||||
"104",
|
||||
"82",
|
||||
"36",
|
||||
"41",
|
||||
"138",
|
||||
"253",
|
||||
"70",
|
||||
"15",
|
||||
"148",
|
||||
"208",
|
||||
"156",
|
||||
"45",
|
||||
"105",
|
||||
"171",
|
||||
"241",
|
||||
"195",
|
||||
"185",
|
||||
"43",
|
||||
"217",
|
||||
"162",
|
||||
"146",
|
||||
"201",
|
||||
"222",
|
||||
"89",
|
||||
"238",
|
||||
"38",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"76",
|
||||
"123",
|
||||
"216",
|
||||
"13",
|
||||
"51",
|
||||
"227",
|
||||
"72",
|
||||
"245",
|
||||
"59",
|
||||
"193",
|
||||
"238",
|
||||
"166",
|
||||
"103",
|
||||
"49",
|
||||
"23",
|
||||
"164",
|
||||
"171",
|
||||
"188",
|
||||
"194",
|
||||
"197",
|
||||
"156",
|
||||
"187",
|
||||
"249",
|
||||
"28",
|
||||
"198",
|
||||
"95",
|
||||
"69",
|
||||
"15",
|
||||
"182",
|
||||
"56",
|
||||
"54",
|
||||
"38",
|
||||
"128",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"9",
|
||||
"120",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0"
|
||||
]
|
||||
signed_attr = [
|
||||
"49",
|
||||
"102",
|
||||
"48",
|
||||
"21",
|
||||
"6",
|
||||
"9",
|
||||
"42",
|
||||
"134",
|
||||
"72",
|
||||
"134",
|
||||
"247",
|
||||
"13",
|
||||
"1",
|
||||
"9",
|
||||
"3",
|
||||
"49",
|
||||
"8",
|
||||
"6",
|
||||
"6",
|
||||
"103",
|
||||
"129",
|
||||
"8",
|
||||
"1",
|
||||
"1",
|
||||
"1",
|
||||
"48",
|
||||
"28",
|
||||
"6",
|
||||
"9",
|
||||
"42",
|
||||
"134",
|
||||
"72",
|
||||
"134",
|
||||
"247",
|
||||
"13",
|
||||
"1",
|
||||
"9",
|
||||
"5",
|
||||
"49",
|
||||
"15",
|
||||
"23",
|
||||
"13",
|
||||
"49",
|
||||
"57",
|
||||
"49",
|
||||
"50",
|
||||
"49",
|
||||
"54",
|
||||
"49",
|
||||
"55",
|
||||
"50",
|
||||
"50",
|
||||
"51",
|
||||
"56",
|
||||
"90",
|
||||
"48",
|
||||
"47",
|
||||
"6",
|
||||
"9",
|
||||
"42",
|
||||
"134",
|
||||
"72",
|
||||
"134",
|
||||
"247",
|
||||
"13",
|
||||
"1",
|
||||
"9",
|
||||
"4",
|
||||
"49",
|
||||
"34",
|
||||
"4",
|
||||
"32",
|
||||
"177",
|
||||
"155",
|
||||
"49",
|
||||
"181",
|
||||
"14",
|
||||
"247",
|
||||
"85",
|
||||
"87",
|
||||
"152",
|
||||
"110",
|
||||
"27",
|
||||
"53",
|
||||
"143",
|
||||
"167",
|
||||
"42",
|
||||
"52",
|
||||
"212",
|
||||
"157",
|
||||
"229",
|
||||
"85",
|
||||
"250",
|
||||
"130",
|
||||
"71",
|
||||
"58",
|
||||
"47",
|
||||
"23",
|
||||
"4",
|
||||
"237",
|
||||
"154",
|
||||
"139",
|
||||
"205",
|
||||
"116",
|
||||
"128",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"3",
|
||||
"64",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0",
|
||||
"0"
|
||||
]
|
||||
signed_attr_econtent_hash_offset = "72"
|
||||
eContent_padded_length = "320"
|
||||
signed_attr_padded_length = "128"
|
||||
1
noir/README.md
Normal file
1
noir/README.md
Normal file
@@ -0,0 +1 @@
|
||||
# Openpassport Noir Circuits
|
||||
1273
noir/rsa-inputs.json
Normal file
1273
noir/rsa-inputs.json
Normal file
File diff suppressed because it is too large
Load Diff
30
noir/src/dg1.nr
Normal file
30
noir/src/dg1.nr
Normal file
@@ -0,0 +1,30 @@
|
||||
use crate::utils::{MAX_ECONTENT_LEN, HASH_LEN_BYTES, DG_PADDING_BYTES_LEN, MAX_SIGNED_ATTR_LEN};
|
||||
use std::hash::sha256;
|
||||
|
||||
pub fn verify_dg1_and_dg2(
|
||||
dg1: [u8; 93],
|
||||
dg1_hash_offset: u8,
|
||||
dg2_hash: [u8; 64],
|
||||
eContent: [u8; MAX_ECONTENT_LEN],
|
||||
) {
|
||||
let computed_dg1_hash = sha256(dg1);
|
||||
|
||||
// Calc position where DG2 hash should start
|
||||
let dg2_hash_start = dg1_hash_offset as u32 + HASH_LEN_BYTES + DG_PADDING_BYTES_LEN;
|
||||
|
||||
// Verify DG1,DG2 hash matches in eContent
|
||||
for i in 0..HASH_LEN_BYTES {
|
||||
assert_eq(computed_dg1_hash[i], eContent[dg1_hash_offset as u32 + i as u32]);
|
||||
assert_eq(dg2_hash[i], eContent[dg2_hash_start + i as u32]);
|
||||
}
|
||||
}
|
||||
|
||||
// todo
|
||||
pub fn verify_econtent(
|
||||
eContent: [u8; MAX_ECONTENT_LEN],
|
||||
signed_attr: [u8; MAX_SIGNED_ATTR_LEN],
|
||||
signed_attr_econtent_hash_offset: u8,
|
||||
eContent_padded_length: u16,
|
||||
signed_attr_padded_length: u8,
|
||||
) {}
|
||||
|
||||
27
noir/src/main.nr
Normal file
27
noir/src/main.nr
Normal file
@@ -0,0 +1,27 @@
|
||||
use crate::utils::{MAX_ECONTENT_LEN, MAX_SIGNED_ATTR_LEN};
|
||||
use crate::dg1::{verify_dg1_and_dg2, verify_econtent};
|
||||
|
||||
pub mod dg1;
|
||||
pub mod utils;
|
||||
|
||||
fn main(
|
||||
dg1: [u8; 93],
|
||||
dg1_hash_offset: u8,
|
||||
dg2_hash: [u8; 64],
|
||||
eContent: [u8; MAX_ECONTENT_LEN],
|
||||
signed_attr: [u8; MAX_SIGNED_ATTR_LEN],
|
||||
signed_attr_econtent_hash_offset: u8,
|
||||
eContent_padded_length: u16,
|
||||
signed_attr_padded_length: u8,
|
||||
) {
|
||||
verify_dg1_and_dg2(dg1, dg1_hash_offset, dg2_hash, eContent);
|
||||
|
||||
verify_econtent(
|
||||
eContent,
|
||||
signed_attr,
|
||||
signed_attr_econtent_hash_offset,
|
||||
eContent_padded_length,
|
||||
signed_attr_padded_length,
|
||||
);
|
||||
}
|
||||
|
||||
6
noir/src/utils.nr
Normal file
6
noir/src/utils.nr
Normal file
@@ -0,0 +1,6 @@
|
||||
// todo: check with circom values
|
||||
|
||||
pub global MAX_ECONTENT_LEN = 384;
|
||||
pub global HASH_LEN_BYTES = 32;
|
||||
pub global DG_PADDING_BYTES_LEN = 7;
|
||||
pub global MAX_SIGNED_ATTR_LEN = 192;
|
||||
BIN
noir/target/noir.gz
Normal file
BIN
noir/target/noir.gz
Normal file
Binary file not shown.
1
noir/target/noir.json
Normal file
1
noir/target/noir.json
Normal file
File diff suppressed because one or more lines are too long
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openpassport/core",
|
||||
"version": "0.0.4",
|
||||
"version": "0.0.7",
|
||||
"main": "dist/sdk/core/index.js",
|
||||
"types": "dist/sdk/core/index.d.ts",
|
||||
"license": "MIT",
|
||||
@@ -18,19 +18,18 @@
|
||||
"js-sha1": "^0.7.0",
|
||||
"js-sha256": "^0.11.0",
|
||||
"js-sha512": "^0.9.0",
|
||||
"lottie-react": "^2.4.0",
|
||||
"msgpack-lite": "^0.1.26",
|
||||
"next": "^14.2.8",
|
||||
"node-forge": "https://github.com/remicolin/forge",
|
||||
"pako": "^2.1.0",
|
||||
"pkijs": "^3.2.4",
|
||||
"poseidon-lite": "^0.2.0",
|
||||
"react": "^18.0.0",
|
||||
"react-dom": "^18.0.0",
|
||||
"snarkjs": "^0.7.4",
|
||||
"uuid": "^10.0.0",
|
||||
"zlib": "^1.0.5",
|
||||
"@zk-kit/imt": "https://gitpkg.now.sh/0xturboblitz/zk-kit/packages/imt?6d417675"
|
||||
"@openpassport/zk-kit-imt": "^0.0.5",
|
||||
"@openpassport/zk-kit-lean-imt": "^0.0.6",
|
||||
"@openpassport/zk-kit-smt": "^0.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/chai": "^4.3.6",
|
||||
@@ -72,7 +71,6 @@
|
||||
"access": "public"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"lottie-react": "^2.4.0",
|
||||
"react": "^18.0.0",
|
||||
"react-dom": "^18.0.0"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openpassport/qrcode",
|
||||
"version": "0.0.5",
|
||||
"version": "0.0.8",
|
||||
"main": "dist/sdk/qrcode/index.js",
|
||||
"types": "dist/sdk/qrcode/index.d.ts",
|
||||
"license": "MIT",
|
||||
@@ -10,7 +10,7 @@
|
||||
},
|
||||
"author": "turnoffthiscomputer",
|
||||
"dependencies": {
|
||||
"@openpassport/core": "0.0.4",
|
||||
"@openpassport/core": "0.0.7",
|
||||
"@types/react": "^18.3.4",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"@types/uuid": "^10.0.0",
|
||||
@@ -45,8 +45,8 @@
|
||||
"@types/node-forge": "^1.3.5",
|
||||
"@types/pako": "^2.0.3",
|
||||
"@types/snarkjs": "^0.7.8",
|
||||
"@openpassport/zk-kit-imt": "^0.0.4",
|
||||
"@openpassport/zk-kit-lean-imt": "^0.0.4",
|
||||
"@openpassport/zk-kit-imt": "^0.0.5",
|
||||
"@openpassport/zk-kit-lean-imt": "^0.0.6",
|
||||
"@openpassport/zk-kit-smt": "^0.0.1",
|
||||
"asn1js": "^3.0.5",
|
||||
"axios": "^1.7.2",
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
"@emotion/react": "^11.13.3",
|
||||
"@emotion/styled": "^11.13.0",
|
||||
"@mui/material": "^6.0.2",
|
||||
"@openpassport/core": "^0.0.7",
|
||||
"@openpassport/qrcode": "^0.0.8",
|
||||
"axios": "^1.7.7",
|
||||
"next": "14.2.8",
|
||||
"react": "^18",
|
||||
@@ -27,4 +29,4 @@
|
||||
"tailwindcss": "^3.4.1",
|
||||
"typescript": "^5"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
'use client';
|
||||
|
||||
import { OpenPassportQRcode } from '../../../../../src/QRcode/OpenPassportQRcode';
|
||||
import OpenPassportQRcode from '../../../../../qrcode/OpenPassportQRcode';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { OpenPassportVerifier } from '../../../../../src/OpenPassportVerifier';
|
||||
import { OpenPassportVerifier } from '@openpassport/core';
|
||||
import { COMMITMENT_TREE_TRACKER_URL } from '../../../../../../common/src/constants/constants';
|
||||
export default function Prove() {
|
||||
const userId = uuidv4();
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
'use client';
|
||||
|
||||
import { OpenPassportQRcode } from '../../../../../src/QRcode/OpenPassportQRcode';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { OpenPassportVerifier } from '../../../../../src/OpenPassportVerifier';
|
||||
import { COMMITMENT_TREE_TRACKER_URL } from '../../../../../../common/src/constants/constants';
|
||||
import { OpenPassportVerifier } from '@openpassport/core';
|
||||
import OpenPassportQRcode from '../../../../../qrcode/OpenPassportQRcode';
|
||||
import axios from 'axios';
|
||||
export default function Prove() {
|
||||
const userId = uuidv4();
|
||||
|
||||
Reference in New Issue
Block a user