├── sources ├── modules │ ├── tests │ │ └── __init__.py │ ├── enum.py │ ├── Array.js │ ├── __init__.py │ ├── dataclasses.py │ ├── Hash.js │ ├── SecureRandom.js │ ├── logger.js │ ├── Nonces.js │ ├── jsons.js │ ├── _secure_random.js │ └── BigInteger.js ├── decidim-electionguard-tests.txt ├── decidim-electionguard.txt └── electionguard-python.txt ├── .gitignore ├── bin ├── apply-patches ├── create-package ├── run-transpile ├── create-patches └── create-build ├── Makefile.envs ├── demo └── index.htm ├── .gitmodules ├── webpack.config.js ├── Makefile ├── package.json └── patches.patch /sources/modules/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | orig 2 | build 3 | transpile 4 | node_modules 5 | dist -------------------------------------------------------------------------------- /sources/decidim-electionguard-tests.txt: -------------------------------------------------------------------------------- 1 | utils.py 2 | test_voter.py -------------------------------------------------------------------------------- /sources/modules/enum.py: -------------------------------------------------------------------------------- 1 | class Enum: 2 | pass 3 | 4 | def unique(func): 5 | return func -------------------------------------------------------------------------------- /sources/modules/Array.js: -------------------------------------------------------------------------------- 1 | export const isArray = (data) => { 2 | return Array.isArray(data) 3 | } -------------------------------------------------------------------------------- /bin/apply-patches: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [[ -e $PATCHES_FILE ]] 4 | then 5 | patch -s -p0 < $PATCHES_FILE 6 | fi 7 | -------------------------------------------------------------------------------- /sources/modules/__init__.py: -------------------------------------------------------------------------------- 1 | from decidim.electionguard.voter import Voter 2 | from tests.test_voter import TestVoter 3 | import BigInteger as bigInt 4 | -------------------------------------------------------------------------------- /sources/decidim-electionguard.txt: -------------------------------------------------------------------------------- 1 | decidim/__init__.py 2 | decidim/electionguard/__init__.py 3 | decidim/electionguard/common.py 4 | decidim/electionguard/voter.py 5 | decidim/electionguard/utils.py -------------------------------------------------------------------------------- /Makefile.envs: -------------------------------------------------------------------------------- 1 | 2 | export SOURCES_DIR = sources 3 | export ORIG_DIR = orig 4 | export BUILD_DIR = build 5 | export PATCHES_FILE = patches.patch 6 | export TRANSPILE_DIR = transpile 7 | export DIST_DIR = dist 8 | -------------------------------------------------------------------------------- /bin/create-package: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | rm -rf $DIST_DIR 4 | mkdir -p $DIST_DIR 5 | 6 | # Use this for production builds 7 | # npm run build -- --env production 8 | 9 | # Use this for development builds 10 | npm run build -- --env development -------------------------------------------------------------------------------- /bin/run-transpile: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | TEMP_DIR=temp/ 4 | 5 | rm -rf $TRANSPILE_DIR $TEMP_DIR 6 | cp -rf $BUILD_DIR $TEMP_DIR 7 | 8 | cd $TEMP_DIR 9 | transcrypt -n __init__.py 10 | cd .. 11 | 12 | mv $TEMP_DIR/__target__/ $TRANSPILE_DIR 13 | rm -rf $TEMP_DIR -------------------------------------------------------------------------------- /demo/index.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 11 | 12 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "sources/decidim-electionguard"] 2 | path = sources/decidim-electionguard 3 | url = https://github.com/codegram/decidim-electionguard 4 | [submodule "sources/electionguard-python"] 5 | path = sources/electionguard-python 6 | url = https://github.com/microsoft/electionguard-python 7 | -------------------------------------------------------------------------------- /sources/modules/dataclasses.py: -------------------------------------------------------------------------------- 1 | def dataclass(func): 2 | return func 3 | 4 | def field(default = None, default_factory = None, init = True): 5 | if default_factory: 6 | return default_factory() 7 | else: 8 | return default 9 | 10 | def InitVar(func): 11 | return func 12 | 13 | def replace(func): 14 | return func -------------------------------------------------------------------------------- /bin/create-patches: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [[ -e $PATCHES_FILE ]] 4 | then 5 | mv $PATCHES_FILE $PATCHES_FILE.orig 6 | fi 7 | 8 | diff -ruwN $ORIG_DIR $BUILD_DIR > $PATCHES_FILE 9 | 10 | if [[ -e $PATCHES_FILE ]] 11 | then 12 | rm -f $PATCHES_FILE.orig 13 | exit 0 14 | fi 15 | 16 | if [[ -e $PATCHES_FILE.orig ]] 17 | then 18 | mv $PATCHES_FILE.orig $PATCHES_FILE 19 | exit 1 20 | fi 21 | -------------------------------------------------------------------------------- /sources/modules/Hash.js: -------------------------------------------------------------------------------- 1 | import jsSHA from 'jssha'; 2 | 3 | class Sha256 { 4 | constructor() { 5 | this.sha = new jsSHA("SHA-256", "TEXT", { encoding: "UTF8" }); 6 | } 7 | 8 | py_update(str) { 9 | this.sha.update(str); 10 | } 11 | 12 | digest() { 13 | return this.sha.getHash("HEX"); 14 | } 15 | } 16 | 17 | export const sha256 = () => { 18 | return new Sha256(); 19 | } -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | const TRANSPILE_DIR = process.env.TRANSPILE_DIR; 4 | const DIST_DIR = process.env.DIST_DIR; 5 | 6 | module.exports = env => ({ 7 | entry: `./${TRANSPILE_DIR}/__init__.js`, 8 | mode: env.production ? 'production' : 'development', 9 | output: { 10 | path: path.resolve(__dirname, DIST_DIR), 11 | filename: 'voter.js', 12 | library: "voter", 13 | libraryTarget: "umd" 14 | }, 15 | devtool: 'inline-source-map' 16 | }) -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all install transpile package 2 | 3 | include Makefile.envs 4 | 5 | all: build package 6 | 7 | install: 8 | npm install 9 | pip install transcrypt strip-hints 10 | 11 | build: 12 | bin/create-build ${BUILD_DIR} 13 | bin/apply-patches 14 | 15 | transpile: 16 | bin/run-transpile 17 | 18 | package: transpile 19 | bin/create-package 20 | 21 | dev: 22 | bin/create-build ${ORIG_DIR} 23 | 24 | patches: 25 | bin/create-patches 26 | 27 | clean: 28 | rm -rf ${BUILD_DIR} ${ORIG_DIR} ${TRANSPILE_DIR} 29 | -------------------------------------------------------------------------------- /sources/modules/SecureRandom.js: -------------------------------------------------------------------------------- 1 | import * as bigInt from './BigInteger.js'; 2 | 3 | // Returns a secure random number below n 4 | export const randomBelow = (n, bytes_number = 32) => { 5 | const randomNumbers = Array.from(crypto.getRandomValues(new Uint8Array(bytes_number))); 6 | const resultStr = randomNumbers.map(elem => elem.toString(16).padStart(2, "0")).join("") 7 | const result = bigInt(resultStr, 16); 8 | 9 | if (result.lesser(n)) { 10 | return result; 11 | } 12 | 13 | return randomBelow(n, bits); 14 | } -------------------------------------------------------------------------------- /sources/electionguard-python.txt: -------------------------------------------------------------------------------- 1 | electionguard/__init__.py 2 | electionguard/ballot.py 3 | electionguard/chaum_pedersen.py 4 | electionguard/dlog.py 5 | electionguard/election.py 6 | electionguard/election_builder.py 7 | electionguard/election_object_base.py 8 | electionguard/elgamal.py 9 | electionguard/encrypt.py 10 | electionguard/group.py 11 | electionguard/hash.py 12 | electionguard/logs.py 13 | electionguard/nonces.py 14 | electionguard/proof.py 15 | electionguard/serializable.py 16 | electionguard/singleton.py 17 | electionguard/tracker.py 18 | electionguard/utils.py 19 | electionguard/words.py -------------------------------------------------------------------------------- /sources/modules/logger.js: -------------------------------------------------------------------------------- 1 | class ConsoleLogger { 2 | constructor() { 3 | } 4 | 5 | debug(message) { 6 | this.log("debug", message) 7 | } 8 | 9 | warning(message) { 10 | this.log("warning", message) 11 | } 12 | 13 | info(message) { 14 | this.log("info", message) 15 | } 16 | 17 | error(message) { 18 | this.log("error", message) 19 | } 20 | 21 | critical(message) { 22 | this.log("critical", message) 23 | } 24 | 25 | log(type, message) { 26 | console.log(type + ": " + message) 27 | } 28 | } 29 | 30 | export var Logger = function() { return new ConsoleLogger(); } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "electionguard-javascript", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "", 6 | "dependencies": { 7 | "big-integer": "^1.6.48", 8 | "jssha": "^3.1.2" 9 | }, 10 | "devDependencies": { 11 | "webpack": "^5.3.0", 12 | "webpack-cli": "^4.1.0" 13 | }, 14 | "scripts": { 15 | "build": "webpack" 16 | }, 17 | "repository": { 18 | "type": "git", 19 | "url": "git+https://github.com/codegram/electionguard-javascript.git" 20 | }, 21 | "author": "", 22 | "license": "ISC", 23 | "bugs": { 24 | "url": "https://github.com/codegram/electionguard-javascript/issues" 25 | }, 26 | "homepage": "https://github.com/codegram/electionguard-javascript#readme" 27 | } 28 | -------------------------------------------------------------------------------- /sources/modules/Nonces.js: -------------------------------------------------------------------------------- 1 | import { hash_elems } from "./electionguard.hash.js"; 2 | 3 | class NoncesIterator { 4 | constructor(seed, ...headers) { 5 | if (headers.length > 0) { 6 | this.seed = hash_elems(seed, ...headers); 7 | } else { 8 | this.seed = seed; 9 | } 10 | this.index = 0; 11 | } 12 | 13 | next() { 14 | return { 15 | value: this.py_get(this.index++), 16 | done: false 17 | }; 18 | } 19 | 20 | py_get(index) { 21 | return this.getWithHeaders(index); 22 | } 23 | 24 | getWithHeaders(item, ...headers) { 25 | if(item < 0) { 26 | throw new Error("Nonces do not support negative indices."); 27 | } 28 | return hash_elems(this.seed, item, ...headers); 29 | } 30 | 31 | [Symbol.iterator]() { 32 | return this; 33 | } 34 | } 35 | 36 | export const Nonces = (seed, ...headers) => { 37 | return new NoncesIterator(seed, ...headers); 38 | } -------------------------------------------------------------------------------- /bin/create-build: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | DIR=$1 4 | 5 | function fix_relative_imports { 6 | for file in $(ls $DIR/$1/*.py) 7 | do 8 | sed -i'.bak' "s/^\s*from \./from $2./g" $file && rm $file.bak 9 | sed -i'.bak' "s/^\s*import \./import $2./g" $file && rm $file.bak 10 | done 11 | } 12 | 13 | rm -rf $DIR 14 | mkdir -p $DIR 15 | 16 | for source in electionguard-python decidim-electionguard 17 | do 18 | for file in $(cat $SOURCES_DIR/$source.txt) 19 | do 20 | mkdir -p $DIR/$(dirname $file) 21 | strip-hints --to-empty $SOURCES_DIR/$source/src/$file > $DIR/$file 22 | done 23 | done 24 | 25 | cp -rf $SOURCES_DIR/modules/* $DIR/ 26 | 27 | fix_relative_imports electionguard electionguard 28 | fix_relative_imports decidim/electionguard decidim.electionguard 29 | 30 | 31 | ### TESTS 32 | mkdir -p $DIR/tests 33 | for file in $(cat $SOURCES_DIR/decidim-electionguard-tests.txt) 34 | do 35 | strip-hints --to-empty $SOURCES_DIR/decidim-electionguard/tests/$file > $DIR/tests/$file 36 | done 37 | 38 | fix_relative_imports tests tests -------------------------------------------------------------------------------- /sources/modules/jsons.js: -------------------------------------------------------------------------------- 1 | import * as bigInt from './BigInteger.js'; 2 | 3 | function urlBase64ToBase64(str) { 4 | var r = str % 4; 5 | if (2 === r) { 6 | str += '=='; 7 | } else if (3 === r) { 8 | str += '='; 9 | } 10 | return str.replace(/-/g, '+').replace(/_/g, '/'); 11 | } 12 | 13 | function b64ToBn(b64) { 14 | var bin = atob(b64); 15 | var hex = []; 16 | 17 | bin.split('').forEach(function (ch) { 18 | var h = ch.charCodeAt(0).toString(16); 19 | if (h.length % 2) { h = '0' + h; } 20 | hex.push(h); 21 | }); 22 | 23 | return BigInt('0x' + hex.join('')); 24 | } 25 | 26 | 27 | export var dump_json = function(instance, strip_privates = true) { 28 | // TO-DO: strip_privates? 29 | return JSON.stringify(instance); 30 | } 31 | 32 | export var dump_json_object = function(instance, strip_privates = true) { 33 | // TO-DO: strip_privates? 34 | return instance; 35 | } 36 | 37 | export var parse_json = function(data, class_out) { 38 | return parse_json_object(JSON.parse(data), class_out); 39 | } 40 | 41 | export var parse_json_object = function(data, class_out) { 42 | if (class_out === 'Optional') 43 | return data || null; 44 | 45 | if (Array.isArray(class_out)) { 46 | switch(class_out[0].__name__) { 47 | case 'str': 48 | case 'int': 49 | case 'bool': 50 | return data; 51 | default: 52 | return data.map(function(currentValue) { 53 | return parse_json_object(currentValue, class_out[0]); 54 | }); 55 | } 56 | } 57 | 58 | switch(class_out.__name__) { 59 | case 'datetime': 60 | return class_out.fromtimestamp((new Date(data)).valueOf()/1000); 61 | 62 | case 'ElementModP': 63 | case 'ElementModQ': 64 | return new class_out(new bigInt(b64ToBn(urlBase64ToBase64(data)))); 65 | 66 | case 'str': 67 | case 'int': 68 | case 'bool': 69 | return data; 70 | } 71 | 72 | var instance = new class_out(); 73 | var serializedObject = data; 74 | Object.assign(instance, serializedObject); 75 | 76 | if (instance._types) { 77 | instance._types.forEach(function(pair) { 78 | instance[pair[0]] = parse_json_object(instance[pair[0]], pair[1]); 79 | }); 80 | } 81 | 82 | return instance; 83 | } 84 | -------------------------------------------------------------------------------- /sources/modules/_secure_random.js: -------------------------------------------------------------------------------- 1 | !function(globals){ 2 | 'use strict' 3 | 4 | //*** UMD BEGIN 5 | if (typeof define !== 'undefined' && define.amd) { //require.js / AMD 6 | define([], function() { 7 | return secureRandom 8 | }) 9 | } else if (typeof module !== 'undefined' && module.exports) { //CommonJS 10 | module.exports = secureRandom 11 | } else { //script / browser 12 | globals.secureRandom = secureRandom 13 | } 14 | //*** UMD END 15 | 16 | //options.type is the only valid option 17 | function secureRandom(count, options) { 18 | options = options || {type: 'Array'} 19 | //we check for process.pid to prevent browserify from tricking us 20 | if ( 21 | typeof process != 'undefined' 22 | && typeof process.pid == 'number' 23 | && process.versions 24 | && process.versions.node 25 | ) { 26 | return nodeRandom(count, options) 27 | } else { 28 | var crypto = window.crypto || window.msCrypto 29 | if (!crypto) throw new Error("Your browser does not support window.crypto.") 30 | return browserRandom(count, options) 31 | } 32 | } 33 | 34 | function nodeRandom(count, options) { 35 | var crypto = require('crypto') 36 | var buf = crypto.randomBytes(count) 37 | 38 | switch (options.type) { 39 | case 'Array': 40 | return [].slice.call(buf) 41 | case 'Buffer': 42 | return buf 43 | case 'Uint8Array': 44 | var arr = new Uint8Array(count) 45 | for (var i = 0; i < count; ++i) { arr[i] = buf.readUInt8(i) } 46 | return arr 47 | default: 48 | throw new Error(options.type + " is unsupported.") 49 | } 50 | } 51 | 52 | function browserRandom(count, options) { 53 | var nativeArr = new Uint8Array(count) 54 | var crypto = window.crypto || window.msCrypto 55 | crypto.getRandomValues(nativeArr) 56 | 57 | switch (options.type) { 58 | case 'Array': 59 | return [].slice.call(nativeArr) 60 | case 'Buffer': 61 | try { var b = new Buffer(1) } catch(e) { throw new Error('Buffer not supported in this environment. Use Node.js or Browserify for browser support.')} 62 | return new Buffer(nativeArr) 63 | case 'Uint8Array': 64 | return nativeArr 65 | default: 66 | throw new Error(options.type + " is unsupported.") 67 | } 68 | } 69 | 70 | secureRandom.randomArray = function(byteCount) { 71 | return secureRandom(byteCount, {type: 'Array'}) 72 | } 73 | 74 | secureRandom.randomUint8Array = function(byteCount) { 75 | return secureRandom(byteCount, {type: 'Uint8Array'}) 76 | } 77 | 78 | secureRandom.randomBuffer = function(byteCount) { 79 | return secureRandom(byteCount, {type: 'Buffer'}) 80 | } 81 | 82 | 83 | }(this); 84 | -------------------------------------------------------------------------------- /sources/modules/BigInteger.js: -------------------------------------------------------------------------------- 1 | var bigInt = (function (undefined) { 2 | "use strict"; 3 | 4 | var BASE = 1e7, 5 | LOG_BASE = 7, 6 | MAX_INT = 9007199254740992, 7 | MAX_INT_ARR = smallToArray(MAX_INT), 8 | DEFAULT_ALPHABET = "0123456789abcdefghijklmnopqrstuvwxyz"; 9 | 10 | var supportsNativeBigInt = typeof BigInt === "function"; 11 | 12 | function Integer(v, radix, alphabet, caseSensitive) { 13 | if (typeof v === "undefined") return Integer[0]; 14 | if (typeof radix !== "undefined") return +radix === 10 && !alphabet ? parseValue(v) : parseBase(v, radix, alphabet, caseSensitive); 15 | return parseValue(v); 16 | } 17 | 18 | function BigInteger(value, sign) { 19 | this.value = value; 20 | this.sign = sign; 21 | this.isSmall = false; 22 | } 23 | BigInteger.prototype = Object.create(Integer.prototype); 24 | 25 | function SmallInteger(value) { 26 | this.value = value; 27 | this.sign = value < 0; 28 | this.isSmall = true; 29 | } 30 | SmallInteger.prototype = Object.create(Integer.prototype); 31 | 32 | function NativeBigInt(value) { 33 | this.value = value; 34 | } 35 | NativeBigInt.prototype = Object.create(Integer.prototype); 36 | 37 | function isPrecise(n) { 38 | return -MAX_INT < n && n < MAX_INT; 39 | } 40 | 41 | function smallToArray(n) { // For performance reasons doesn't reference BASE, need to change this function if BASE changes 42 | if (n < 1e7) 43 | return [n]; 44 | if (n < 1e14) 45 | return [n % 1e7, Math.floor(n / 1e7)]; 46 | return [n % 1e7, Math.floor(n / 1e7) % 1e7, Math.floor(n / 1e14)]; 47 | } 48 | 49 | function arrayToSmall(arr) { // If BASE changes this function may need to change 50 | trim(arr); 51 | var length = arr.length; 52 | if (length < 4 && compareAbs(arr, MAX_INT_ARR) < 0) { 53 | switch (length) { 54 | case 0: return 0; 55 | case 1: return arr[0]; 56 | case 2: return arr[0] + arr[1] * BASE; 57 | default: return arr[0] + (arr[1] + arr[2] * BASE) * BASE; 58 | } 59 | } 60 | return arr; 61 | } 62 | 63 | function trim(v) { 64 | var i = v.length; 65 | while (v[--i] === 0); 66 | v.length = i + 1; 67 | } 68 | 69 | function createArray(length) { // function shamelessly stolen from Yaffle's library https://github.com/Yaffle/BigInteger 70 | var x = new Array(length); 71 | var i = -1; 72 | while (++i < length) { 73 | x[i] = 0; 74 | } 75 | return x; 76 | } 77 | 78 | function truncate(n) { 79 | if (n > 0) return Math.floor(n); 80 | return Math.ceil(n); 81 | } 82 | 83 | function add(a, b) { // assumes a and b are arrays with a.length >= b.length 84 | var l_a = a.length, 85 | l_b = b.length, 86 | r = new Array(l_a), 87 | carry = 0, 88 | base = BASE, 89 | sum, i; 90 | for (i = 0; i < l_b; i++) { 91 | sum = a[i] + b[i] + carry; 92 | carry = sum >= base ? 1 : 0; 93 | r[i] = sum - carry * base; 94 | } 95 | while (i < l_a) { 96 | sum = a[i] + carry; 97 | carry = sum === base ? 1 : 0; 98 | r[i++] = sum - carry * base; 99 | } 100 | if (carry > 0) r.push(carry); 101 | return r; 102 | } 103 | 104 | function addAny(a, b) { 105 | if (a.length >= b.length) return add(a, b); 106 | return add(b, a); 107 | } 108 | 109 | function addSmall(a, carry) { // assumes a is array, carry is number with 0 <= carry < MAX_INT 110 | var l = a.length, 111 | r = new Array(l), 112 | base = BASE, 113 | sum, i; 114 | for (i = 0; i < l; i++) { 115 | sum = a[i] - base + carry; 116 | carry = Math.floor(sum / base); 117 | r[i] = sum - carry * base; 118 | carry += 1; 119 | } 120 | while (carry > 0) { 121 | r[i++] = carry % base; 122 | carry = Math.floor(carry / base); 123 | } 124 | return r; 125 | } 126 | 127 | BigInteger.prototype.add = function (v) { 128 | var n = parseValue(v); 129 | if (this.sign !== n.sign) { 130 | return this.subtract(n.negate()); 131 | } 132 | var a = this.value, b = n.value; 133 | if (n.isSmall) { 134 | return new BigInteger(addSmall(a, Math.abs(b)), this.sign); 135 | } 136 | return new BigInteger(addAny(a, b), this.sign); 137 | }; 138 | BigInteger.prototype.plus = BigInteger.prototype.add; 139 | 140 | SmallInteger.prototype.add = function (v) { 141 | var n = parseValue(v); 142 | var a = this.value; 143 | if (a < 0 !== n.sign) { 144 | return this.subtract(n.negate()); 145 | } 146 | var b = n.value; 147 | if (n.isSmall) { 148 | if (isPrecise(a + b)) return new SmallInteger(a + b); 149 | b = smallToArray(Math.abs(b)); 150 | } 151 | return new BigInteger(addSmall(b, Math.abs(a)), a < 0); 152 | }; 153 | SmallInteger.prototype.plus = SmallInteger.prototype.add; 154 | 155 | NativeBigInt.prototype.add = function (v) { 156 | return new NativeBigInt(this.value + parseValue(v).value); 157 | } 158 | NativeBigInt.prototype.plus = NativeBigInt.prototype.add; 159 | 160 | function subtract(a, b) { // assumes a and b are arrays with a >= b 161 | var a_l = a.length, 162 | b_l = b.length, 163 | r = new Array(a_l), 164 | borrow = 0, 165 | base = BASE, 166 | i, difference; 167 | for (i = 0; i < b_l; i++) { 168 | difference = a[i] - borrow - b[i]; 169 | if (difference < 0) { 170 | difference += base; 171 | borrow = 1; 172 | } else borrow = 0; 173 | r[i] = difference; 174 | } 175 | for (i = b_l; i < a_l; i++) { 176 | difference = a[i] - borrow; 177 | if (difference < 0) difference += base; 178 | else { 179 | r[i++] = difference; 180 | break; 181 | } 182 | r[i] = difference; 183 | } 184 | for (; i < a_l; i++) { 185 | r[i] = a[i]; 186 | } 187 | trim(r); 188 | return r; 189 | } 190 | 191 | function subtractAny(a, b, sign) { 192 | var value; 193 | if (compareAbs(a, b) >= 0) { 194 | value = subtract(a, b); 195 | } else { 196 | value = subtract(b, a); 197 | sign = !sign; 198 | } 199 | value = arrayToSmall(value); 200 | if (typeof value === "number") { 201 | if (sign) value = -value; 202 | return new SmallInteger(value); 203 | } 204 | return new BigInteger(value, sign); 205 | } 206 | 207 | function subtractSmall(a, b, sign) { // assumes a is array, b is number with 0 <= b < MAX_INT 208 | var l = a.length, 209 | r = new Array(l), 210 | carry = -b, 211 | base = BASE, 212 | i, difference; 213 | for (i = 0; i < l; i++) { 214 | difference = a[i] + carry; 215 | carry = Math.floor(difference / base); 216 | difference %= base; 217 | r[i] = difference < 0 ? difference + base : difference; 218 | } 219 | r = arrayToSmall(r); 220 | if (typeof r === "number") { 221 | if (sign) r = -r; 222 | return new SmallInteger(r); 223 | } return new BigInteger(r, sign); 224 | } 225 | 226 | BigInteger.prototype.subtract = function (v) { 227 | var n = parseValue(v); 228 | if (this.sign !== n.sign) { 229 | return this.add(n.negate()); 230 | } 231 | var a = this.value, b = n.value; 232 | if (n.isSmall) 233 | return subtractSmall(a, Math.abs(b), this.sign); 234 | return subtractAny(a, b, this.sign); 235 | }; 236 | BigInteger.prototype.minus = BigInteger.prototype.subtract; 237 | 238 | SmallInteger.prototype.subtract = function (v) { 239 | var n = parseValue(v); 240 | var a = this.value; 241 | if (a < 0 !== n.sign) { 242 | return this.add(n.negate()); 243 | } 244 | var b = n.value; 245 | if (n.isSmall) { 246 | return new SmallInteger(a - b); 247 | } 248 | return subtractSmall(b, Math.abs(a), a >= 0); 249 | }; 250 | SmallInteger.prototype.minus = SmallInteger.prototype.subtract; 251 | 252 | NativeBigInt.prototype.subtract = function (v) { 253 | return new NativeBigInt(this.value - parseValue(v).value); 254 | } 255 | NativeBigInt.prototype.minus = NativeBigInt.prototype.subtract; 256 | 257 | BigInteger.prototype.negate = function () { 258 | return new BigInteger(this.value, !this.sign); 259 | }; 260 | SmallInteger.prototype.negate = function () { 261 | var sign = this.sign; 262 | var small = new SmallInteger(-this.value); 263 | small.sign = !sign; 264 | return small; 265 | }; 266 | NativeBigInt.prototype.negate = function () { 267 | return new NativeBigInt(-this.value); 268 | } 269 | 270 | BigInteger.prototype.abs = function () { 271 | return new BigInteger(this.value, false); 272 | }; 273 | SmallInteger.prototype.abs = function () { 274 | return new SmallInteger(Math.abs(this.value)); 275 | }; 276 | NativeBigInt.prototype.abs = function () { 277 | return new NativeBigInt(this.value >= 0 ? this.value : -this.value); 278 | } 279 | 280 | 281 | function multiplyLong(a, b) { 282 | var a_l = a.length, 283 | b_l = b.length, 284 | l = a_l + b_l, 285 | r = createArray(l), 286 | base = BASE, 287 | product, carry, i, a_i, b_j; 288 | for (i = 0; i < a_l; ++i) { 289 | a_i = a[i]; 290 | for (var j = 0; j < b_l; ++j) { 291 | b_j = b[j]; 292 | product = a_i * b_j + r[i + j]; 293 | carry = Math.floor(product / base); 294 | r[i + j] = product - carry * base; 295 | r[i + j + 1] += carry; 296 | } 297 | } 298 | trim(r); 299 | return r; 300 | } 301 | 302 | function multiplySmall(a, b) { // assumes a is array, b is number with |b| < BASE 303 | var l = a.length, 304 | r = new Array(l), 305 | base = BASE, 306 | carry = 0, 307 | product, i; 308 | for (i = 0; i < l; i++) { 309 | product = a[i] * b + carry; 310 | carry = Math.floor(product / base); 311 | r[i] = product - carry * base; 312 | } 313 | while (carry > 0) { 314 | r[i++] = carry % base; 315 | carry = Math.floor(carry / base); 316 | } 317 | return r; 318 | } 319 | 320 | function shiftLeft(x, n) { 321 | var r = []; 322 | while (n-- > 0) r.push(0); 323 | return r.concat(x); 324 | } 325 | 326 | function multiplyKaratsuba(x, y) { 327 | var n = Math.max(x.length, y.length); 328 | 329 | if (n <= 30) return multiplyLong(x, y); 330 | n = Math.ceil(n / 2); 331 | 332 | var b = x.slice(n), 333 | a = x.slice(0, n), 334 | d = y.slice(n), 335 | c = y.slice(0, n); 336 | 337 | var ac = multiplyKaratsuba(a, c), 338 | bd = multiplyKaratsuba(b, d), 339 | abcd = multiplyKaratsuba(addAny(a, b), addAny(c, d)); 340 | 341 | var product = addAny(addAny(ac, shiftLeft(subtract(subtract(abcd, ac), bd), n)), shiftLeft(bd, 2 * n)); 342 | trim(product); 343 | return product; 344 | } 345 | 346 | // The following function is derived from a surface fit of a graph plotting the performance difference 347 | // between long multiplication and karatsuba multiplication versus the lengths of the two arrays. 348 | function useKaratsuba(l1, l2) { 349 | return -0.012 * l1 - 0.012 * l2 + 0.000015 * l1 * l2 > 0; 350 | } 351 | 352 | BigInteger.prototype.multiply = function (v) { 353 | var n = parseValue(v), 354 | a = this.value, b = n.value, 355 | sign = this.sign !== n.sign, 356 | abs; 357 | if (n.isSmall) { 358 | if (b === 0) return Integer[0]; 359 | if (b === 1) return this; 360 | if (b === -1) return this.negate(); 361 | abs = Math.abs(b); 362 | if (abs < BASE) { 363 | return new BigInteger(multiplySmall(a, abs), sign); 364 | } 365 | b = smallToArray(abs); 366 | } 367 | if (useKaratsuba(a.length, b.length)) // Karatsuba is only faster for certain array sizes 368 | return new BigInteger(multiplyKaratsuba(a, b), sign); 369 | return new BigInteger(multiplyLong(a, b), sign); 370 | }; 371 | 372 | BigInteger.prototype.times = BigInteger.prototype.multiply; 373 | 374 | function multiplySmallAndArray(a, b, sign) { // a >= 0 375 | if (a < BASE) { 376 | return new BigInteger(multiplySmall(b, a), sign); 377 | } 378 | return new BigInteger(multiplyLong(b, smallToArray(a)), sign); 379 | } 380 | SmallInteger.prototype._multiplyBySmall = function (a) { 381 | if (isPrecise(a.value * this.value)) { 382 | return new SmallInteger(a.value * this.value); 383 | } 384 | return multiplySmallAndArray(Math.abs(a.value), smallToArray(Math.abs(this.value)), this.sign !== a.sign); 385 | }; 386 | BigInteger.prototype._multiplyBySmall = function (a) { 387 | if (a.value === 0) return Integer[0]; 388 | if (a.value === 1) return this; 389 | if (a.value === -1) return this.negate(); 390 | return multiplySmallAndArray(Math.abs(a.value), this.value, this.sign !== a.sign); 391 | }; 392 | SmallInteger.prototype.multiply = function (v) { 393 | return parseValue(v)._multiplyBySmall(this); 394 | }; 395 | SmallInteger.prototype.times = SmallInteger.prototype.multiply; 396 | 397 | NativeBigInt.prototype.multiply = function (v) { 398 | return new NativeBigInt(this.value * parseValue(v).value); 399 | } 400 | NativeBigInt.prototype.times = NativeBigInt.prototype.multiply; 401 | 402 | function square(a) { 403 | //console.assert(2 * BASE * BASE < MAX_INT); 404 | var l = a.length, 405 | r = createArray(l + l), 406 | base = BASE, 407 | product, carry, i, a_i, a_j; 408 | for (i = 0; i < l; i++) { 409 | a_i = a[i]; 410 | carry = 0 - a_i * a_i; 411 | for (var j = i; j < l; j++) { 412 | a_j = a[j]; 413 | product = 2 * (a_i * a_j) + r[i + j] + carry; 414 | carry = Math.floor(product / base); 415 | r[i + j] = product - carry * base; 416 | } 417 | r[i + l] = carry; 418 | } 419 | trim(r); 420 | return r; 421 | } 422 | 423 | BigInteger.prototype.square = function () { 424 | return new BigInteger(square(this.value), false); 425 | }; 426 | 427 | SmallInteger.prototype.square = function () { 428 | var value = this.value * this.value; 429 | if (isPrecise(value)) return new SmallInteger(value); 430 | return new BigInteger(square(smallToArray(Math.abs(this.value))), false); 431 | }; 432 | 433 | NativeBigInt.prototype.square = function (v) { 434 | return new NativeBigInt(this.value * this.value); 435 | } 436 | 437 | function divMod1(a, b) { // Left over from previous version. Performs faster than divMod2 on smaller input sizes. 438 | var a_l = a.length, 439 | b_l = b.length, 440 | base = BASE, 441 | result = createArray(b.length), 442 | divisorMostSignificantDigit = b[b_l - 1], 443 | // normalization 444 | lambda = Math.ceil(base / (2 * divisorMostSignificantDigit)), 445 | remainder = multiplySmall(a, lambda), 446 | divisor = multiplySmall(b, lambda), 447 | quotientDigit, shift, carry, borrow, i, l, q; 448 | if (remainder.length <= a_l) remainder.push(0); 449 | divisor.push(0); 450 | divisorMostSignificantDigit = divisor[b_l - 1]; 451 | for (shift = a_l - b_l; shift >= 0; shift--) { 452 | quotientDigit = base - 1; 453 | if (remainder[shift + b_l] !== divisorMostSignificantDigit) { 454 | quotientDigit = Math.floor((remainder[shift + b_l] * base + remainder[shift + b_l - 1]) / divisorMostSignificantDigit); 455 | } 456 | // quotientDigit <= base - 1 457 | carry = 0; 458 | borrow = 0; 459 | l = divisor.length; 460 | for (i = 0; i < l; i++) { 461 | carry += quotientDigit * divisor[i]; 462 | q = Math.floor(carry / base); 463 | borrow += remainder[shift + i] - (carry - q * base); 464 | carry = q; 465 | if (borrow < 0) { 466 | remainder[shift + i] = borrow + base; 467 | borrow = -1; 468 | } else { 469 | remainder[shift + i] = borrow; 470 | borrow = 0; 471 | } 472 | } 473 | while (borrow !== 0) { 474 | quotientDigit -= 1; 475 | carry = 0; 476 | for (i = 0; i < l; i++) { 477 | carry += remainder[shift + i] - base + divisor[i]; 478 | if (carry < 0) { 479 | remainder[shift + i] = carry + base; 480 | carry = 0; 481 | } else { 482 | remainder[shift + i] = carry; 483 | carry = 1; 484 | } 485 | } 486 | borrow += carry; 487 | } 488 | result[shift] = quotientDigit; 489 | } 490 | // denormalization 491 | remainder = divModSmall(remainder, lambda)[0]; 492 | return [arrayToSmall(result), arrayToSmall(remainder)]; 493 | } 494 | 495 | function divMod2(a, b) { // Implementation idea shamelessly stolen from Silent Matt's library http://silentmatt.com/biginteger/ 496 | // Performs faster than divMod1 on larger input sizes. 497 | var a_l = a.length, 498 | b_l = b.length, 499 | result = [], 500 | part = [], 501 | base = BASE, 502 | guess, xlen, highx, highy, check; 503 | while (a_l) { 504 | part.unshift(a[--a_l]); 505 | trim(part); 506 | if (compareAbs(part, b) < 0) { 507 | result.push(0); 508 | continue; 509 | } 510 | xlen = part.length; 511 | highx = part[xlen - 1] * base + part[xlen - 2]; 512 | highy = b[b_l - 1] * base + b[b_l - 2]; 513 | if (xlen > b_l) { 514 | highx = (highx + 1) * base; 515 | } 516 | guess = Math.ceil(highx / highy); 517 | do { 518 | check = multiplySmall(b, guess); 519 | if (compareAbs(check, part) <= 0) break; 520 | guess--; 521 | } while (guess); 522 | result.push(guess); 523 | part = subtract(part, check); 524 | } 525 | result.reverse(); 526 | return [arrayToSmall(result), arrayToSmall(part)]; 527 | } 528 | 529 | function divModSmall(value, lambda) { 530 | var length = value.length, 531 | quotient = createArray(length), 532 | base = BASE, 533 | i, q, remainder, divisor; 534 | remainder = 0; 535 | for (i = length - 1; i >= 0; --i) { 536 | divisor = remainder * base + value[i]; 537 | q = truncate(divisor / lambda); 538 | remainder = divisor - q * lambda; 539 | quotient[i] = q | 0; 540 | } 541 | return [quotient, remainder | 0]; 542 | } 543 | 544 | function divModAny(self, v) { 545 | var value, n = parseValue(v); 546 | if (supportsNativeBigInt) { 547 | return [new NativeBigInt(self.value / n.value), new NativeBigInt(self.value % n.value)]; 548 | } 549 | var a = self.value, b = n.value; 550 | var quotient; 551 | if (b === 0) throw new Error("Cannot divide by zero"); 552 | if (self.isSmall) { 553 | if (n.isSmall) { 554 | return [new SmallInteger(truncate(a / b)), new SmallInteger(a % b)]; 555 | } 556 | return [Integer[0], self]; 557 | } 558 | if (n.isSmall) { 559 | if (b === 1) return [self, Integer[0]]; 560 | if (b == -1) return [self.negate(), Integer[0]]; 561 | var abs = Math.abs(b); 562 | if (abs < BASE) { 563 | value = divModSmall(a, abs); 564 | quotient = arrayToSmall(value[0]); 565 | var remainder = value[1]; 566 | if (self.sign) remainder = -remainder; 567 | if (typeof quotient === "number") { 568 | if (self.sign !== n.sign) quotient = -quotient; 569 | return [new SmallInteger(quotient), new SmallInteger(remainder)]; 570 | } 571 | return [new BigInteger(quotient, self.sign !== n.sign), new SmallInteger(remainder)]; 572 | } 573 | b = smallToArray(abs); 574 | } 575 | var comparison = compareAbs(a, b); 576 | if (comparison === -1) return [Integer[0], self]; 577 | if (comparison === 0) return [Integer[self.sign === n.sign ? 1 : -1], Integer[0]]; 578 | 579 | // divMod1 is faster on smaller input sizes 580 | if (a.length + b.length <= 200) 581 | value = divMod1(a, b); 582 | else value = divMod2(a, b); 583 | 584 | quotient = value[0]; 585 | var qSign = self.sign !== n.sign, 586 | mod = value[1], 587 | mSign = self.sign; 588 | if (typeof quotient === "number") { 589 | if (qSign) quotient = -quotient; 590 | quotient = new SmallInteger(quotient); 591 | } else quotient = new BigInteger(quotient, qSign); 592 | if (typeof mod === "number") { 593 | if (mSign) mod = -mod; 594 | mod = new SmallInteger(mod); 595 | } else mod = new BigInteger(mod, mSign); 596 | return [quotient, mod]; 597 | } 598 | 599 | BigInteger.prototype.divmod = function (v) { 600 | var result = divModAny(this, v); 601 | return { 602 | quotient: result[0], 603 | remainder: result[1] 604 | }; 605 | }; 606 | NativeBigInt.prototype.divmod = SmallInteger.prototype.divmod = BigInteger.prototype.divmod; 607 | 608 | 609 | BigInteger.prototype.divide = function (v) { 610 | return divModAny(this, v)[0]; 611 | }; 612 | NativeBigInt.prototype.over = NativeBigInt.prototype.divide = function (v) { 613 | return new NativeBigInt(this.value / parseValue(v).value); 614 | }; 615 | SmallInteger.prototype.over = SmallInteger.prototype.divide = BigInteger.prototype.over = BigInteger.prototype.divide; 616 | 617 | BigInteger.prototype.mod = function (v) { 618 | return divModAny(this, v)[1]; 619 | }; 620 | NativeBigInt.prototype.mod = NativeBigInt.prototype.remainder = function (v) { 621 | return new NativeBigInt(this.value % parseValue(v).value); 622 | }; 623 | SmallInteger.prototype.remainder = SmallInteger.prototype.mod = BigInteger.prototype.remainder = BigInteger.prototype.mod; 624 | 625 | BigInteger.prototype.pow = function (v) { 626 | var n = parseValue(v), 627 | a = this.value, 628 | b = n.value, 629 | value, x, y; 630 | if (b === 0) return Integer[1]; 631 | if (a === 0) return Integer[0]; 632 | if (a === 1) return Integer[1]; 633 | if (a === -1) return n.isEven() ? Integer[1] : Integer[-1]; 634 | if (n.sign) { 635 | return Integer[0]; 636 | } 637 | if (!n.isSmall) throw new Error("The exponent " + n.toString() + " is too large."); 638 | if (this.isSmall) { 639 | if (isPrecise(value = Math.pow(a, b))) 640 | return new SmallInteger(truncate(value)); 641 | } 642 | x = this; 643 | y = Integer[1]; 644 | while (true) { 645 | if (b & 1 === 1) { 646 | y = y.times(x); 647 | --b; 648 | } 649 | if (b === 0) break; 650 | b /= 2; 651 | x = x.square(); 652 | } 653 | return y; 654 | }; 655 | SmallInteger.prototype.pow = BigInteger.prototype.pow; 656 | 657 | NativeBigInt.prototype.pow = function (v) { 658 | var n = parseValue(v); 659 | var a = this.value, b = n.value; 660 | var _0 = BigInt(0), _1 = BigInt(1), _2 = BigInt(2); 661 | if (b === _0) return Integer[1]; 662 | if (a === _0) return Integer[0]; 663 | if (a === _1) return Integer[1]; 664 | if (a === BigInt(-1)) return n.isEven() ? Integer[1] : Integer[-1]; 665 | if (n.isNegative()) return new NativeBigInt(_0); 666 | var x = this; 667 | var y = Integer[1]; 668 | while (true) { 669 | if ((b & _1) === _1) { 670 | y = y.times(x); 671 | --b; 672 | } 673 | if (b === _0) break; 674 | b /= _2; 675 | x = x.square(); 676 | } 677 | return y; 678 | } 679 | 680 | BigInteger.prototype.modPow = function (exp, mod) { 681 | exp = parseValue(exp); 682 | mod = parseValue(mod); 683 | if (mod.isZero()) throw new Error("Cannot take modPow with modulus 0"); 684 | var r = Integer[1], 685 | base = this.mod(mod); 686 | if (exp.isNegative()) { 687 | exp = exp.multiply(Integer[-1]); 688 | base = base.modInv(mod); 689 | } 690 | while (exp.isPositive()) { 691 | if (base.isZero()) return Integer[0]; 692 | if (exp.isOdd()) r = r.multiply(base).mod(mod); 693 | exp = exp.divide(2); 694 | base = base.square().mod(mod); 695 | } 696 | return r; 697 | }; 698 | NativeBigInt.prototype.modPow = SmallInteger.prototype.modPow = BigInteger.prototype.modPow; 699 | 700 | function compareAbs(a, b) { 701 | if (a.length !== b.length) { 702 | return a.length > b.length ? 1 : -1; 703 | } 704 | for (var i = a.length - 1; i >= 0; i--) { 705 | if (a[i] !== b[i]) return a[i] > b[i] ? 1 : -1; 706 | } 707 | return 0; 708 | } 709 | 710 | BigInteger.prototype.compareAbs = function (v) { 711 | var n = parseValue(v), 712 | a = this.value, 713 | b = n.value; 714 | if (n.isSmall) return 1; 715 | return compareAbs(a, b); 716 | }; 717 | SmallInteger.prototype.compareAbs = function (v) { 718 | var n = parseValue(v), 719 | a = Math.abs(this.value), 720 | b = n.value; 721 | if (n.isSmall) { 722 | b = Math.abs(b); 723 | return a === b ? 0 : a > b ? 1 : -1; 724 | } 725 | return -1; 726 | }; 727 | NativeBigInt.prototype.compareAbs = function (v) { 728 | var a = this.value; 729 | var b = parseValue(v).value; 730 | a = a >= 0 ? a : -a; 731 | b = b >= 0 ? b : -b; 732 | return a === b ? 0 : a > b ? 1 : -1; 733 | } 734 | 735 | BigInteger.prototype.compare = function (v) { 736 | // See discussion about comparison with Infinity: 737 | // https://github.com/peterolson/BigInteger.js/issues/61 738 | if (v === Infinity) { 739 | return -1; 740 | } 741 | if (v === -Infinity) { 742 | return 1; 743 | } 744 | 745 | var n = parseValue(v), 746 | a = this.value, 747 | b = n.value; 748 | if (this.sign !== n.sign) { 749 | return n.sign ? 1 : -1; 750 | } 751 | if (n.isSmall) { 752 | return this.sign ? -1 : 1; 753 | } 754 | return compareAbs(a, b) * (this.sign ? -1 : 1); 755 | }; 756 | BigInteger.prototype.compareTo = BigInteger.prototype.compare; 757 | 758 | SmallInteger.prototype.compare = function (v) { 759 | if (v === Infinity) { 760 | return -1; 761 | } 762 | if (v === -Infinity) { 763 | return 1; 764 | } 765 | 766 | var n = parseValue(v), 767 | a = this.value, 768 | b = n.value; 769 | if (n.isSmall) { 770 | return a == b ? 0 : a > b ? 1 : -1; 771 | } 772 | if (a < 0 !== n.sign) { 773 | return a < 0 ? -1 : 1; 774 | } 775 | return a < 0 ? 1 : -1; 776 | }; 777 | SmallInteger.prototype.compareTo = SmallInteger.prototype.compare; 778 | 779 | NativeBigInt.prototype.compare = function (v) { 780 | if (v === Infinity) { 781 | return -1; 782 | } 783 | if (v === -Infinity) { 784 | return 1; 785 | } 786 | var a = this.value; 787 | var b = parseValue(v).value; 788 | return a === b ? 0 : a > b ? 1 : -1; 789 | } 790 | NativeBigInt.prototype.compareTo = NativeBigInt.prototype.compare; 791 | 792 | BigInteger.prototype.equals = function (v) { 793 | return this.compare(v) === 0; 794 | }; 795 | NativeBigInt.prototype.eq = NativeBigInt.prototype.equals = SmallInteger.prototype.eq = SmallInteger.prototype.equals = BigInteger.prototype.eq = BigInteger.prototype.equals; 796 | 797 | BigInteger.prototype.notEquals = function (v) { 798 | return this.compare(v) !== 0; 799 | }; 800 | NativeBigInt.prototype.neq = NativeBigInt.prototype.notEquals = SmallInteger.prototype.neq = SmallInteger.prototype.notEquals = BigInteger.prototype.neq = BigInteger.prototype.notEquals; 801 | 802 | BigInteger.prototype.greater = function (v) { 803 | return this.compare(v) > 0; 804 | }; 805 | NativeBigInt.prototype.gt = NativeBigInt.prototype.greater = SmallInteger.prototype.gt = SmallInteger.prototype.greater = BigInteger.prototype.gt = BigInteger.prototype.greater; 806 | 807 | BigInteger.prototype.lesser = function (v) { 808 | return this.compare(v) < 0; 809 | }; 810 | NativeBigInt.prototype.lt = NativeBigInt.prototype.lesser = SmallInteger.prototype.lt = SmallInteger.prototype.lesser = BigInteger.prototype.lt = BigInteger.prototype.lesser; 811 | 812 | BigInteger.prototype.greaterOrEquals = function (v) { 813 | return this.compare(v) >= 0; 814 | }; 815 | NativeBigInt.prototype.geq = NativeBigInt.prototype.greaterOrEquals = SmallInteger.prototype.geq = SmallInteger.prototype.greaterOrEquals = BigInteger.prototype.geq = BigInteger.prototype.greaterOrEquals; 816 | 817 | BigInteger.prototype.lesserOrEquals = function (v) { 818 | return this.compare(v) <= 0; 819 | }; 820 | NativeBigInt.prototype.leq = NativeBigInt.prototype.lesserOrEquals = SmallInteger.prototype.leq = SmallInteger.prototype.lesserOrEquals = BigInteger.prototype.leq = BigInteger.prototype.lesserOrEquals; 821 | 822 | BigInteger.prototype.isEven = function () { 823 | return (this.value[0] & 1) === 0; 824 | }; 825 | SmallInteger.prototype.isEven = function () { 826 | return (this.value & 1) === 0; 827 | }; 828 | NativeBigInt.prototype.isEven = function () { 829 | return (this.value & BigInt(1)) === BigInt(0); 830 | } 831 | 832 | BigInteger.prototype.isOdd = function () { 833 | return (this.value[0] & 1) === 1; 834 | }; 835 | SmallInteger.prototype.isOdd = function () { 836 | return (this.value & 1) === 1; 837 | }; 838 | NativeBigInt.prototype.isOdd = function () { 839 | return (this.value & BigInt(1)) === BigInt(1); 840 | } 841 | 842 | BigInteger.prototype.isPositive = function () { 843 | return !this.sign; 844 | }; 845 | SmallInteger.prototype.isPositive = function () { 846 | return this.value > 0; 847 | }; 848 | NativeBigInt.prototype.isPositive = SmallInteger.prototype.isPositive; 849 | 850 | BigInteger.prototype.isNegative = function () { 851 | return this.sign; 852 | }; 853 | SmallInteger.prototype.isNegative = function () { 854 | return this.value < 0; 855 | }; 856 | NativeBigInt.prototype.isNegative = SmallInteger.prototype.isNegative; 857 | 858 | BigInteger.prototype.isUnit = function () { 859 | return false; 860 | }; 861 | SmallInteger.prototype.isUnit = function () { 862 | return Math.abs(this.value) === 1; 863 | }; 864 | NativeBigInt.prototype.isUnit = function () { 865 | return this.abs().value === BigInt(1); 866 | } 867 | 868 | BigInteger.prototype.isZero = function () { 869 | return false; 870 | }; 871 | SmallInteger.prototype.isZero = function () { 872 | return this.value === 0; 873 | }; 874 | NativeBigInt.prototype.isZero = function () { 875 | return this.value === BigInt(0); 876 | } 877 | 878 | BigInteger.prototype.isDivisibleBy = function (v) { 879 | var n = parseValue(v); 880 | if (n.isZero()) return false; 881 | if (n.isUnit()) return true; 882 | if (n.compareAbs(2) === 0) return this.isEven(); 883 | return this.mod(n).isZero(); 884 | }; 885 | NativeBigInt.prototype.isDivisibleBy = SmallInteger.prototype.isDivisibleBy = BigInteger.prototype.isDivisibleBy; 886 | 887 | function isBasicPrime(v) { 888 | var n = v.abs(); 889 | if (n.isUnit()) return false; 890 | if (n.equals(2) || n.equals(3) || n.equals(5)) return true; 891 | if (n.isEven() || n.isDivisibleBy(3) || n.isDivisibleBy(5)) return false; 892 | if (n.lesser(49)) return true; 893 | // we don't know if it's prime: let the other functions figure it out 894 | } 895 | 896 | function millerRabinTest(n, a) { 897 | var nPrev = n.prev(), 898 | b = nPrev, 899 | r = 0, 900 | d, t, i, x; 901 | while (b.isEven()) b = b.divide(2), r++; 902 | next: for (i = 0; i < a.length; i++) { 903 | if (n.lesser(a[i])) continue; 904 | x = bigInt(a[i]).modPow(b, n); 905 | if (x.isUnit() || x.equals(nPrev)) continue; 906 | for (d = r - 1; d != 0; d--) { 907 | x = x.square().mod(n); 908 | if (x.isUnit()) return false; 909 | if (x.equals(nPrev)) continue next; 910 | } 911 | return false; 912 | } 913 | return true; 914 | } 915 | 916 | // Set "strict" to true to force GRH-supported lower bound of 2*log(N)^2 917 | BigInteger.prototype.isPrime = function (strict) { 918 | var isPrime = isBasicPrime(this); 919 | if (isPrime !== undefined) return isPrime; 920 | var n = this.abs(); 921 | var bits = n.bitLength(); 922 | if (bits <= 64) 923 | return millerRabinTest(n, [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37]); 924 | var logN = Math.log(2) * bits.toJSNumber(); 925 | var t = Math.ceil((strict === true) ? (2 * Math.pow(logN, 2)) : logN); 926 | for (var a = [], i = 0; i < t; i++) { 927 | a.push(bigInt(i + 2)); 928 | } 929 | return millerRabinTest(n, a); 930 | }; 931 | NativeBigInt.prototype.isPrime = SmallInteger.prototype.isPrime = BigInteger.prototype.isPrime; 932 | 933 | BigInteger.prototype.isProbablePrime = function (iterations, rng) { 934 | var isPrime = isBasicPrime(this); 935 | if (isPrime !== undefined) return isPrime; 936 | var n = this.abs(); 937 | var t = iterations === undefined ? 5 : iterations; 938 | for (var a = [], i = 0; i < t; i++) { 939 | a.push(bigInt.randBetween(2, n.minus(2), rng)); 940 | } 941 | return millerRabinTest(n, a); 942 | }; 943 | NativeBigInt.prototype.isProbablePrime = SmallInteger.prototype.isProbablePrime = BigInteger.prototype.isProbablePrime; 944 | 945 | BigInteger.prototype.modInv = function (n) { 946 | var t = bigInt.zero, newT = bigInt.one, r = parseValue(n), newR = this.abs(), q, lastT, lastR; 947 | while (!newR.isZero()) { 948 | q = r.divide(newR); 949 | lastT = t; 950 | lastR = r; 951 | t = newT; 952 | r = newR; 953 | newT = lastT.subtract(q.multiply(newT)); 954 | newR = lastR.subtract(q.multiply(newR)); 955 | } 956 | if (!r.isUnit()) throw new Error(this.toString() + " and " + n.toString() + " are not co-prime"); 957 | if (t.compare(0) === -1) { 958 | t = t.add(n); 959 | } 960 | if (this.isNegative()) { 961 | return t.negate(); 962 | } 963 | return t; 964 | }; 965 | 966 | NativeBigInt.prototype.modInv = SmallInteger.prototype.modInv = BigInteger.prototype.modInv; 967 | 968 | BigInteger.prototype.next = function () { 969 | var value = this.value; 970 | if (this.sign) { 971 | return subtractSmall(value, 1, this.sign); 972 | } 973 | return new BigInteger(addSmall(value, 1), this.sign); 974 | }; 975 | SmallInteger.prototype.next = function () { 976 | var value = this.value; 977 | if (value + 1 < MAX_INT) return new SmallInteger(value + 1); 978 | return new BigInteger(MAX_INT_ARR, false); 979 | }; 980 | NativeBigInt.prototype.next = function () { 981 | return new NativeBigInt(this.value + BigInt(1)); 982 | } 983 | 984 | BigInteger.prototype.prev = function () { 985 | var value = this.value; 986 | if (this.sign) { 987 | return new BigInteger(addSmall(value, 1), true); 988 | } 989 | return subtractSmall(value, 1, this.sign); 990 | }; 991 | SmallInteger.prototype.prev = function () { 992 | var value = this.value; 993 | if (value - 1 > -MAX_INT) return new SmallInteger(value - 1); 994 | return new BigInteger(MAX_INT_ARR, true); 995 | }; 996 | NativeBigInt.prototype.prev = function () { 997 | return new NativeBigInt(this.value - BigInt(1)); 998 | } 999 | 1000 | var powersOfTwo = [1]; 1001 | while (2 * powersOfTwo[powersOfTwo.length - 1] <= BASE) powersOfTwo.push(2 * powersOfTwo[powersOfTwo.length - 1]); 1002 | var powers2Length = powersOfTwo.length, highestPower2 = powersOfTwo[powers2Length - 1]; 1003 | 1004 | function shift_isSmall(n) { 1005 | return Math.abs(n) <= BASE; 1006 | } 1007 | 1008 | BigInteger.prototype.shiftLeft = function (v) { 1009 | var n = parseValue(v).toJSNumber(); 1010 | if (!shift_isSmall(n)) { 1011 | throw new Error(String(n) + " is too large for shifting."); 1012 | } 1013 | if (n < 0) return this.shiftRight(-n); 1014 | var result = this; 1015 | if (result.isZero()) return result; 1016 | while (n >= powers2Length) { 1017 | result = result.multiply(highestPower2); 1018 | n -= powers2Length - 1; 1019 | } 1020 | return result.multiply(powersOfTwo[n]); 1021 | }; 1022 | NativeBigInt.prototype.shiftLeft = SmallInteger.prototype.shiftLeft = BigInteger.prototype.shiftLeft; 1023 | 1024 | BigInteger.prototype.shiftRight = function (v) { 1025 | var remQuo; 1026 | var n = parseValue(v).toJSNumber(); 1027 | if (!shift_isSmall(n)) { 1028 | throw new Error(String(n) + " is too large for shifting."); 1029 | } 1030 | if (n < 0) return this.shiftLeft(-n); 1031 | var result = this; 1032 | while (n >= powers2Length) { 1033 | if (result.isZero() || (result.isNegative() && result.isUnit())) return result; 1034 | remQuo = divModAny(result, highestPower2); 1035 | result = remQuo[1].isNegative() ? remQuo[0].prev() : remQuo[0]; 1036 | n -= powers2Length - 1; 1037 | } 1038 | remQuo = divModAny(result, powersOfTwo[n]); 1039 | return remQuo[1].isNegative() ? remQuo[0].prev() : remQuo[0]; 1040 | }; 1041 | NativeBigInt.prototype.shiftRight = SmallInteger.prototype.shiftRight = BigInteger.prototype.shiftRight; 1042 | 1043 | function bitwise(x, y, fn) { 1044 | y = parseValue(y); 1045 | var xSign = x.isNegative(), ySign = y.isNegative(); 1046 | var xRem = xSign ? x.not() : x, 1047 | yRem = ySign ? y.not() : y; 1048 | var xDigit = 0, yDigit = 0; 1049 | var xDivMod = null, yDivMod = null; 1050 | var result = []; 1051 | while (!xRem.isZero() || !yRem.isZero()) { 1052 | xDivMod = divModAny(xRem, highestPower2); 1053 | xDigit = xDivMod[1].toJSNumber(); 1054 | if (xSign) { 1055 | xDigit = highestPower2 - 1 - xDigit; // two's complement for negative numbers 1056 | } 1057 | 1058 | yDivMod = divModAny(yRem, highestPower2); 1059 | yDigit = yDivMod[1].toJSNumber(); 1060 | if (ySign) { 1061 | yDigit = highestPower2 - 1 - yDigit; // two's complement for negative numbers 1062 | } 1063 | 1064 | xRem = xDivMod[0]; 1065 | yRem = yDivMod[0]; 1066 | result.push(fn(xDigit, yDigit)); 1067 | } 1068 | var sum = fn(xSign ? 1 : 0, ySign ? 1 : 0) !== 0 ? bigInt(-1) : bigInt(0); 1069 | for (var i = result.length - 1; i >= 0; i -= 1) { 1070 | sum = sum.multiply(highestPower2).add(bigInt(result[i])); 1071 | } 1072 | return sum; 1073 | } 1074 | 1075 | BigInteger.prototype.not = function () { 1076 | return this.negate().prev(); 1077 | }; 1078 | NativeBigInt.prototype.not = SmallInteger.prototype.not = BigInteger.prototype.not; 1079 | 1080 | BigInteger.prototype.and = function (n) { 1081 | return bitwise(this, n, function (a, b) { return a & b; }); 1082 | }; 1083 | NativeBigInt.prototype.and = SmallInteger.prototype.and = BigInteger.prototype.and; 1084 | 1085 | BigInteger.prototype.or = function (n) { 1086 | return bitwise(this, n, function (a, b) { return a | b; }); 1087 | }; 1088 | NativeBigInt.prototype.or = SmallInteger.prototype.or = BigInteger.prototype.or; 1089 | 1090 | BigInteger.prototype.xor = function (n) { 1091 | return bitwise(this, n, function (a, b) { return a ^ b; }); 1092 | }; 1093 | NativeBigInt.prototype.xor = SmallInteger.prototype.xor = BigInteger.prototype.xor; 1094 | 1095 | var LOBMASK_I = 1 << 30, LOBMASK_BI = (BASE & -BASE) * (BASE & -BASE) | LOBMASK_I; 1096 | function roughLOB(n) { // get lowestOneBit (rough) 1097 | // SmallInteger: return Min(lowestOneBit(n), 1 << 30) 1098 | // BigInteger: return Min(lowestOneBit(n), 1 << 14) [BASE=1e7] 1099 | var v = n.value, 1100 | x = typeof v === "number" ? v | LOBMASK_I : 1101 | typeof v === "bigint" ? v | BigInt(LOBMASK_I) : 1102 | v[0] + v[1] * BASE | LOBMASK_BI; 1103 | return x & -x; 1104 | } 1105 | 1106 | function integerLogarithm(value, base) { 1107 | if (base.compareTo(value) <= 0) { 1108 | var tmp = integerLogarithm(value, base.square(base)); 1109 | var p = tmp.p; 1110 | var e = tmp.e; 1111 | var t = p.multiply(base); 1112 | return t.compareTo(value) <= 0 ? { p: t, e: e * 2 + 1 } : { p: p, e: e * 2 }; 1113 | } 1114 | return { p: bigInt(1), e: 0 }; 1115 | } 1116 | 1117 | BigInteger.prototype.bitLength = function () { 1118 | var n = this; 1119 | if (n.compareTo(bigInt(0)) < 0) { 1120 | n = n.negate().subtract(bigInt(1)); 1121 | } 1122 | if (n.compareTo(bigInt(0)) === 0) { 1123 | return bigInt(0); 1124 | } 1125 | return bigInt(integerLogarithm(n, bigInt(2)).e).add(bigInt(1)); 1126 | } 1127 | NativeBigInt.prototype.bitLength = SmallInteger.prototype.bitLength = BigInteger.prototype.bitLength; 1128 | 1129 | function max(a, b) { 1130 | a = parseValue(a); 1131 | b = parseValue(b); 1132 | return a.greater(b) ? a : b; 1133 | } 1134 | function min(a, b) { 1135 | a = parseValue(a); 1136 | b = parseValue(b); 1137 | return a.lesser(b) ? a : b; 1138 | } 1139 | function gcd(a, b) { 1140 | a = parseValue(a).abs(); 1141 | b = parseValue(b).abs(); 1142 | if (a.equals(b)) return a; 1143 | if (a.isZero()) return b; 1144 | if (b.isZero()) return a; 1145 | var c = Integer[1], d, t; 1146 | while (a.isEven() && b.isEven()) { 1147 | d = min(roughLOB(a), roughLOB(b)); 1148 | a = a.divide(d); 1149 | b = b.divide(d); 1150 | c = c.multiply(d); 1151 | } 1152 | while (a.isEven()) { 1153 | a = a.divide(roughLOB(a)); 1154 | } 1155 | do { 1156 | while (b.isEven()) { 1157 | b = b.divide(roughLOB(b)); 1158 | } 1159 | if (a.greater(b)) { 1160 | t = b; b = a; a = t; 1161 | } 1162 | b = b.subtract(a); 1163 | } while (!b.isZero()); 1164 | return c.isUnit() ? a : a.multiply(c); 1165 | } 1166 | function lcm(a, b) { 1167 | a = parseValue(a).abs(); 1168 | b = parseValue(b).abs(); 1169 | return a.divide(gcd(a, b)).multiply(b); 1170 | } 1171 | function randBetween(a, b, rng) { 1172 | a = parseValue(a); 1173 | b = parseValue(b); 1174 | var usedRNG = rng || Math.random; 1175 | var low = min(a, b), high = max(a, b); 1176 | var range = high.subtract(low).add(1); 1177 | if (range.isSmall) return low.add(Math.floor(usedRNG() * range)); 1178 | var digits = toBase(range, BASE).value; 1179 | var result = [], restricted = true; 1180 | for (var i = 0; i < digits.length; i++) { 1181 | var top = restricted ? digits[i] : BASE; 1182 | var digit = truncate(usedRNG() * top); 1183 | result.push(digit); 1184 | if (digit < top) restricted = false; 1185 | } 1186 | return low.add(Integer.fromArray(result, BASE, false)); 1187 | } 1188 | 1189 | var parseBase = function (text, base, alphabet, caseSensitive) { 1190 | alphabet = alphabet || DEFAULT_ALPHABET; 1191 | text = String(text); 1192 | if (!caseSensitive) { 1193 | text = text.toLowerCase(); 1194 | alphabet = alphabet.toLowerCase(); 1195 | } 1196 | var length = text.length; 1197 | var i; 1198 | var absBase = Math.abs(base); 1199 | var alphabetValues = {}; 1200 | for (i = 0; i < alphabet.length; i++) { 1201 | alphabetValues[alphabet[i]] = i; 1202 | } 1203 | for (i = 0; i < length; i++) { 1204 | var c = text[i]; 1205 | if (c === "-") continue; 1206 | if (c in alphabetValues) { 1207 | if (alphabetValues[c] >= absBase) { 1208 | if (c === "1" && absBase === 1) continue; 1209 | throw new Error(c + " is not a valid digit in base " + base + "."); 1210 | } 1211 | } 1212 | } 1213 | base = parseValue(base); 1214 | var digits = []; 1215 | var isNegative = text[0] === "-"; 1216 | for (i = isNegative ? 1 : 0; i < text.length; i++) { 1217 | var c = text[i]; 1218 | if (c in alphabetValues) digits.push(parseValue(alphabetValues[c])); 1219 | else if (c === "<") { 1220 | var start = i; 1221 | do { i++; } while (text[i] !== ">" && i < text.length); 1222 | digits.push(parseValue(text.slice(start + 1, i))); 1223 | } 1224 | else throw new Error(c + " is not a valid character"); 1225 | } 1226 | return parseBaseFromArray(digits, base, isNegative); 1227 | }; 1228 | 1229 | function parseBaseFromArray(digits, base, isNegative) { 1230 | var val = Integer[0], pow = Integer[1], i; 1231 | for (i = digits.length - 1; i >= 0; i--) { 1232 | val = val.add(digits[i].times(pow)); 1233 | pow = pow.times(base); 1234 | } 1235 | return isNegative ? val.negate() : val; 1236 | } 1237 | 1238 | function stringify(digit, alphabet) { 1239 | alphabet = alphabet || DEFAULT_ALPHABET; 1240 | if (digit < alphabet.length) { 1241 | return alphabet[digit]; 1242 | } 1243 | return "<" + digit + ">"; 1244 | } 1245 | 1246 | function toBase(n, base) { 1247 | base = bigInt(base); 1248 | if (base.isZero()) { 1249 | if (n.isZero()) return { value: [0], isNegative: false }; 1250 | throw new Error("Cannot convert nonzero numbers to base 0."); 1251 | } 1252 | if (base.equals(-1)) { 1253 | if (n.isZero()) return { value: [0], isNegative: false }; 1254 | if (n.isNegative()) 1255 | return { 1256 | value: [].concat.apply([], Array.apply(null, Array(-n.toJSNumber())) 1257 | .map(Array.prototype.valueOf, [1, 0]) 1258 | ), 1259 | isNegative: false 1260 | }; 1261 | 1262 | var arr = Array.apply(null, Array(n.toJSNumber() - 1)) 1263 | .map(Array.prototype.valueOf, [0, 1]); 1264 | arr.unshift([1]); 1265 | return { 1266 | value: [].concat.apply([], arr), 1267 | isNegative: false 1268 | }; 1269 | } 1270 | 1271 | var neg = false; 1272 | if (n.isNegative() && base.isPositive()) { 1273 | neg = true; 1274 | n = n.abs(); 1275 | } 1276 | if (base.isUnit()) { 1277 | if (n.isZero()) return { value: [0], isNegative: false }; 1278 | 1279 | return { 1280 | value: Array.apply(null, Array(n.toJSNumber())) 1281 | .map(Number.prototype.valueOf, 1), 1282 | isNegative: neg 1283 | }; 1284 | } 1285 | var out = []; 1286 | var left = n, divmod; 1287 | while (left.isNegative() || left.compareAbs(base) >= 0) { 1288 | divmod = left.divmod(base); 1289 | left = divmod.quotient; 1290 | var digit = divmod.remainder; 1291 | if (digit.isNegative()) { 1292 | digit = base.minus(digit).abs(); 1293 | left = left.next(); 1294 | } 1295 | out.push(digit.toJSNumber()); 1296 | } 1297 | out.push(left.toJSNumber()); 1298 | return { value: out.reverse(), isNegative: neg }; 1299 | } 1300 | 1301 | function toBaseString(n, base, alphabet) { 1302 | var arr = toBase(n, base); 1303 | return (arr.isNegative ? "-" : "") + arr.value.map(function (x) { 1304 | return stringify(x, alphabet); 1305 | }).join(''); 1306 | } 1307 | 1308 | BigInteger.prototype.toArray = function (radix) { 1309 | return toBase(this, radix); 1310 | }; 1311 | 1312 | SmallInteger.prototype.toArray = function (radix) { 1313 | return toBase(this, radix); 1314 | }; 1315 | 1316 | NativeBigInt.prototype.toArray = function (radix) { 1317 | return toBase(this, radix); 1318 | }; 1319 | 1320 | BigInteger.prototype.toString = function (radix, alphabet) { 1321 | if (radix === undefined) radix = 10; 1322 | if (radix !== 10) return toBaseString(this, radix, alphabet); 1323 | var v = this.value, l = v.length, str = String(v[--l]), zeros = "0000000", digit; 1324 | while (--l >= 0) { 1325 | digit = String(v[l]); 1326 | str += zeros.slice(digit.length) + digit; 1327 | } 1328 | var sign = this.sign ? "-" : ""; 1329 | return sign + str; 1330 | }; 1331 | 1332 | SmallInteger.prototype.toString = function (radix, alphabet) { 1333 | if (radix === undefined) radix = 10; 1334 | if (radix != 10) return toBaseString(this, radix, alphabet); 1335 | return String(this.value); 1336 | }; 1337 | 1338 | NativeBigInt.prototype.toString = SmallInteger.prototype.toString; 1339 | 1340 | NativeBigInt.prototype.toJSON = BigInteger.prototype.toJSON = SmallInteger.prototype.toJSON = function () { return this.toString(); } 1341 | 1342 | BigInteger.prototype.valueOf = function () { 1343 | return parseInt(this.toString(), 10); 1344 | }; 1345 | BigInteger.prototype.toJSNumber = BigInteger.prototype.valueOf; 1346 | 1347 | SmallInteger.prototype.valueOf = function () { 1348 | return this.value; 1349 | }; 1350 | SmallInteger.prototype.toJSNumber = SmallInteger.prototype.valueOf; 1351 | NativeBigInt.prototype.valueOf = NativeBigInt.prototype.toJSNumber = function () { 1352 | return parseInt(this.toString(), 10); 1353 | } 1354 | 1355 | function parseStringValue(v) { 1356 | if (isPrecise(+v)) { 1357 | var x = +v; 1358 | if (x === truncate(x)) 1359 | return supportsNativeBigInt ? new NativeBigInt(BigInt(x)) : new SmallInteger(x); 1360 | throw new Error("Invalid integer: " + v); 1361 | } 1362 | var sign = v[0] === "-"; 1363 | if (sign) v = v.slice(1); 1364 | var split = v.split(/e/i); 1365 | if (split.length > 2) throw new Error("Invalid integer: " + split.join("e")); 1366 | if (split.length === 2) { 1367 | var exp = split[1]; 1368 | if (exp[0] === "+") exp = exp.slice(1); 1369 | exp = +exp; 1370 | if (exp !== truncate(exp) || !isPrecise(exp)) throw new Error("Invalid integer: " + exp + " is not a valid exponent."); 1371 | var text = split[0]; 1372 | var decimalPlace = text.indexOf("."); 1373 | if (decimalPlace >= 0) { 1374 | exp -= text.length - decimalPlace - 1; 1375 | text = text.slice(0, decimalPlace) + text.slice(decimalPlace + 1); 1376 | } 1377 | if (exp < 0) throw new Error("Cannot include negative exponent part for integers"); 1378 | text += (new Array(exp + 1)).join("0"); 1379 | v = text; 1380 | } 1381 | var isValid = /^([0-9][0-9]*)$/.test(v); 1382 | if (!isValid) throw new Error("Invalid integer: " + v); 1383 | if (supportsNativeBigInt) { 1384 | return new NativeBigInt(BigInt(sign ? "-" + v : v)); 1385 | } 1386 | var r = [], max = v.length, l = LOG_BASE, min = max - l; 1387 | while (max > 0) { 1388 | r.push(+v.slice(min, max)); 1389 | min -= l; 1390 | if (min < 0) min = 0; 1391 | max -= l; 1392 | } 1393 | trim(r); 1394 | return new BigInteger(r, sign); 1395 | } 1396 | 1397 | function parseNumberValue(v) { 1398 | if (supportsNativeBigInt) { 1399 | return new NativeBigInt(BigInt(v)); 1400 | } 1401 | if (isPrecise(v)) { 1402 | if (v !== truncate(v)) throw new Error(v + " is not an integer."); 1403 | return new SmallInteger(v); 1404 | } 1405 | return parseStringValue(v.toString()); 1406 | } 1407 | 1408 | function parseValue(v) { 1409 | if (typeof v === "number") { 1410 | return parseNumberValue(v); 1411 | } 1412 | if (typeof v === "string") { 1413 | return parseStringValue(v); 1414 | } 1415 | if (typeof v === "bigint") { 1416 | return new NativeBigInt(v); 1417 | } 1418 | return v; 1419 | } 1420 | // Pre-define numbers in range [-999,999] 1421 | for (var i = 0; i < 1000; i++) { 1422 | Integer[i] = parseValue(i); 1423 | if (i > 0) Integer[-i] = parseValue(-i); 1424 | } 1425 | // Backwards compatibility 1426 | Integer.one = Integer[1]; 1427 | Integer.zero = Integer[0]; 1428 | Integer.minusOne = Integer[-1]; 1429 | Integer.max = max; 1430 | Integer.min = min; 1431 | Integer.gcd = gcd; 1432 | Integer.lcm = lcm; 1433 | Integer.isInstance = function (x) { return x instanceof BigInteger || x instanceof SmallInteger || x instanceof NativeBigInt; }; 1434 | Integer.randBetween = randBetween; 1435 | 1436 | Integer.fromArray = function (digits, base, isNegative) { 1437 | return parseBaseFromArray(digits.map(parseValue), parseValue(base || 10), isNegative); 1438 | }; 1439 | 1440 | return Integer; 1441 | })(); 1442 | 1443 | // Node.js check 1444 | if (typeof module !== "undefined" && module.hasOwnProperty("exports")) { 1445 | module.exports = bigInt; 1446 | } 1447 | 1448 | //amd check 1449 | if (typeof define === "function" && define.amd) { 1450 | define( function () { 1451 | return bigInt; 1452 | }); 1453 | } 1454 | -------------------------------------------------------------------------------- /patches.patch: -------------------------------------------------------------------------------- 1 | diff -ruwN orig/decidim/electionguard/common.py build/decidim/electionguard/common.py 2 | --- orig/decidim/electionguard/common.py 2020-11-03 10:10:47.000000000 +0100 3 | +++ build/decidim/electionguard/common.py 2020-10-28 15:25:36.000000000 +0100 4 | @@ -12,7 +12,7 @@ 5 | #quorum: int 6 | 7 | def build_election(self, election_creation ): 8 | - self.election = ElectionDescription.from_json_object(complete_election_description(election_creation['description'])) 9 | + self.election = ElectionDescription.from_json_object(complete_election_description(election_creation['description']), ElectionDescription) 10 | 11 | if not self.election.is_valid(): 12 | raise InvalidElectionDescription() 13 | diff -ruwN orig/decidim/electionguard/utils.py build/decidim/electionguard/utils.py 14 | --- orig/decidim/electionguard/utils.py 2020-11-03 10:10:47.000000000 +0100 15 | +++ build/decidim/electionguard/utils.py 2020-11-03 10:28:42.000000000 +0100 16 | @@ -1,46 +1,7 @@ 17 | -from base64 import b64encode, b64decode 18 | from electionguard.group import ElementModP, ElementModQ 19 | import electionguard.serializable 20 | -from gmpy2 import mpz 21 | 22 | -# -- TEMPORARY MONKEYPATCH JSONS SERIALIZATION -- 23 | -old_set_serializers = electionguard.serializable.set_serializers 24 | -old_set_deserializers = electionguard.serializable.set_deserializers 25 | -electionguard.serializable.KEYS_TO_REMOVE += ['nonce'] # Remove nonces when serializing to JSON 26 | - 27 | - 28 | -def set_serializers(): 29 | - old_set_serializers() 30 | - electionguard.serializable.set_serializer(serialize_big_number, ElementModP) 31 | - electionguard.serializable.set_serializer(serialize_big_number, ElementModQ) 32 | - 33 | - 34 | -def set_deserializers(): 35 | - old_set_serializers() 36 | - electionguard.serializable.set_deserializer(deserialize_big_number, ElementModP) 37 | - electionguard.serializable.set_deserializer(deserialize_big_number, ElementModQ) 38 | - 39 | - 40 | -electionguard.serializable.set_serializers = set_serializers 41 | -electionguard.serializable.set_deserializers = set_deserializers 42 | -# ----------------------------------------------- 43 | - 44 | - 45 | -def serialize_big_number(obj , **_): 46 | - number = int(obj.to_int()) 47 | - return b64encode( 48 | - number.to_bytes( 49 | - (number.bit_length() + 7) // 8, 50 | - byteorder='big' 51 | - ) 52 | - ).decode('utf-8') 53 | - 54 | - 55 | -def deserialize_big_number(obj, cls, **_): 56 | - return cls(mpz(int.from_bytes(b64decode(obj), byteorder='big'))) 57 | - 58 | - 59 | -def serialize(obj, include_private = False): 60 | +def serialize(obj, include_private: bool = False): 61 | return electionguard.serializable.write_json_object(obj, not include_private) 62 | 63 | 64 | @@ -49,7 +10,7 @@ 65 | 66 | 67 | def deserialize_key(obj): 68 | - return deserialize_big_number(obj, ElementModP) 69 | + return electionguard.serializable.read_json_object(obj, ElementModP) 70 | 71 | 72 | class InvalidElectionDescription(Exception): 73 | @@ -67,24 +28,28 @@ 74 | 75 | 76 | def complete_election_description(election_description ) : 77 | - complete_description = { 78 | - **election_description, 79 | + complete_description = dict(election_description) 80 | + complete_description.update({ 81 | 'contact_information': { 82 | 'address_line': [], 83 | - 'name': 'Organization name', 84 | + '_name': 'Organization name', 85 | 'email': [{'annotation': 'contact', 'value': 'contact@example.org'}], 86 | 'phone': [] 87 | }, 88 | 'election_scope_id': 'test-election', 89 | - 'type': 'special', 90 | + '_type': { 91 | + '_name': 'special' 92 | + }, 93 | 'geopolitical_units': [ 94 | { 95 | 'object_id': 'a-place', 96 | - 'name': 'A place', 97 | - 'type': 'county', 98 | + '_name': 'A place', 99 | + '_type': { 100 | + '_name': 'county', 101 | + }, 102 | 'contact_information': { 103 | 'address_line': [], 104 | - 'name': 'Organization name', 105 | + '_name': 'Organization name', 106 | 'email': [{'annotation': 'contact', 'value': 'contact@example.org'}] 107 | }, 108 | 'phone': [] 109 | @@ -97,7 +62,7 @@ 110 | 'geopolitical_unit_ids': ['a-place'] 111 | } 112 | ] 113 | - } 114 | + }) 115 | 116 | for contest in complete_description['contests']: 117 | contest['electoral_district_id'] = 'a-place' 118 | diff -ruwN orig/decidim/electionguard/voter.py build/decidim/electionguard/voter.py 119 | --- orig/decidim/electionguard/voter.py 2020-11-03 10:10:47.000000000 +0100 120 | +++ build/decidim/electionguard/voter.py 2020-11-02 16:30:32.000000000 +0100 121 | @@ -5,7 +5,9 @@ 122 | from typing import List 123 | from decidim.electionguard.common import Context, ElectionStep, Wrapper 124 | from decidim.electionguard.utils import MissingJointKey, serialize, deserialize_key 125 | +import BigInteger as bigInt 126 | 127 | +mpz = bigInt 128 | 129 | class VoterContext(Context): 130 | has_joint_key = False 131 | @@ -24,7 +26,6 @@ 132 | 133 | def process_message(self, message_type , message , context ): 134 | joint_key = deserialize_key(message['joint_election_key']) 135 | - 136 | context.election_builder.set_public_key(get_optional(joint_key)) 137 | context.election_metadata, context.election_context = get_optional(context.election_builder.build()) 138 | context.has_joint_key = True 139 | @@ -50,20 +51,12 @@ 140 | for selection in contest.ballot_selections 141 | ] 142 | 143 | - contests.append(PlaintextBallotContest(contest.object_id, selections)) 144 | + contests.append(PlaintextBallotContest(object_id = contest.object_id, ballot_selections = selections)) 145 | 146 | - plaintext_ballot = PlaintextBallot(self.ballot_id, ballot_style, contests) 147 | + plaintext_ballot = PlaintextBallot(object_id = self.ballot_id, ballot_style = ballot_style, contests = contests) 148 | 149 | # TODO: store the audit information somewhere 150 | 151 | - # return serialize(encrypt_ballot( 152 | - # plaintext_ballot, 153 | - # self.context.election_metadata, 154 | - # self.context.election_context, 155 | - # ElementModQ(0), 156 | - # None, 157 | - # True 158 | - # )) 159 | return encrypt_ballot( 160 | plaintext_ballot, 161 | self.context.election_metadata, 162 | diff -ruwN orig/distutils.py build/distutils.py 163 | --- orig/distutils.py 1970-01-01 01:00:00.000000000 +0100 164 | +++ build/distutils.py 2020-10-28 15:25:36.000000000 +0100 165 | @@ -0,0 +1,5 @@ 166 | +class Util: 167 | + def strtobool(str): 168 | + return int(str) == 1 169 | + 170 | +util = Util() 171 | \ No newline at end of file 172 | diff -ruwN orig/electionguard/ballot.py build/electionguard/ballot.py 173 | --- orig/electionguard/ballot.py 2020-11-03 10:10:47.000000000 +0100 174 | +++ build/electionguard/ballot.py 2020-11-02 16:55:52.000000000 +0100 175 | @@ -1,8 +1,6 @@ 176 | from dataclasses import dataclass, field, replace 177 | from datetime import datetime 178 | -from distutils import util 179 | from enum import Enum 180 | -from typing import Any, List, Optional, Protocol, runtime_checkable, Sequence 181 | 182 | from electionguard.chaum_pedersen import ( 183 | ConstantChaumPedersenProof, 184 | @@ -15,9 +13,12 @@ 185 | from electionguard.group import add_q, ElementModP, ElementModQ, ZERO_MOD_Q 186 | from electionguard.hash import CryptoHashCheckable, hash_elems 187 | from electionguard.logs import log_warning 188 | +from electionguard.nonces import Nonces 189 | +from electionguard.proof import Proof 190 | from electionguard.tracker import get_rotating_tracker_hash, tracker_hash_to_words 191 | from electionguard.utils import to_ticks, flatmap_optional 192 | 193 | +__pragma__ ('kwargs') 194 | 195 | def _list_eq( 196 | list1 , list2 197 | @@ -63,7 +64,11 @@ 198 | discarded when encrypting. 199 | """ 200 | 201 | - #vote: str 202 | + _types = [ 203 | + ["vote", str], 204 | + ["is_placeholder_selection", bool], 205 | + ["extended_data", ExtendedData] 206 | + ] 207 | 208 | is_placeholder_selection = field(default=False) 209 | """Determines if this is a placeholder selection""" 210 | @@ -101,17 +106,21 @@ 211 | :return: an integer 0 or 1 for valid data, or 0 if the data is malformed 212 | """ 213 | 214 | - as_bool = False 215 | - try: 216 | - as_bool = util.strtobool(self.vote.lower()) 217 | - except ValueError: 218 | + vote_lower = str(self.vote).lower() 219 | + as_int = { 220 | + "y": 1, "yes": 1, "true": 1, "on": 1, "1": 1, 221 | + "n": 0, "no": 0, "f": 0, "false": 0, "off": 0, "0": 0 222 | + }.get(vote_lower) 223 | + 224 | + if as_int is None: 225 | log_warning( 226 | - f"to_int could not convert plaintext: {self.vote.lower()} to bool" 227 | + f"to_int could not convert plaintext: {vote_lower} to bool" 228 | ) 229 | + as_int = 0 230 | 231 | # TODO: ISSUE #33: If the boolean coercion above fails, support integer votes 232 | # greater than 1 for cases such as cumulative voting 233 | - as_int = int(as_bool) 234 | + # as_int = int(as_bool) 235 | return as_int 236 | 237 | def __eq__(self, other ) : 238 | @@ -127,8 +136,7 @@ 239 | return not self.__eq__(other) 240 | 241 | 242 | -@runtime_checkable 243 | -class CiphertextSelection(Protocol): 244 | +class CiphertextSelection: 245 | """ 246 | Encrypted selection 247 | """ 248 | @@ -170,14 +178,21 @@ 249 | By keeping the `proof` the nonce is not required fotor verify the encrypted selection. 250 | """ 251 | 252 | - #description_hash: ElementModQ 253 | - """The SelectionDescription hash""" 254 | - 255 | - #ciphertext: ElGamalCiphertext 256 | - """The encrypted representation of the vote field""" 257 | - 258 | - #crypto_hash: ElementModQ 259 | - """The hash of the encrypted values""" 260 | + _types = [ 261 | + ["description_hash", ElementModQ], 262 | + # """The SelectionDescription hash""" 263 | + 264 | + ["ciphertext", ElGamalCiphertext], 265 | + # """The encrypted representation of the vote field""" 266 | + 267 | + ["crypto_hash", ElementModQ], 268 | + # """The hash of the encrypted values""" 269 | + 270 | + ["is_placeholder_selection", bool], 271 | + ["nonce", Nonces], 272 | + ["proof", Proof], 273 | + ["extended_data", ExtendedData] 274 | + ] 275 | 276 | is_placeholder_selection = field(default=False) 277 | """Determines if this is a placeholder selection""" 278 | @@ -216,7 +231,7 @@ 279 | return False 280 | 281 | recalculated_crypto_hash = self.crypto_hash_with(seed_hash) 282 | - if self.crypto_hash != recalculated_crypto_hash: 283 | + if self.crypto_hash.__ne__(recalculated_crypto_hash): 284 | log_warning( 285 | f"mismatching crypto hash: {self.object_id} expected({str(recalculated_crypto_hash)}), actual({str(self.crypto_hash)})" 286 | ) 287 | @@ -317,6 +332,10 @@ 288 | while complete contests are passed into ElectionGuard when running encryption on an existing dataset. 289 | """ 290 | 291 | + _types = [ 292 | + ["ballot_selections", [PlaintextBallotSelection]] 293 | + ] 294 | + 295 | ballot_selections = field( 296 | default_factory=lambda: [] 297 | ) 298 | @@ -349,6 +368,7 @@ 299 | 300 | number_elected = 0 301 | votes = 0 302 | + 303 | # Verify the selections are well-formed 304 | for selection in self.ballot_selections: 305 | selection_count = selection.to_int() 306 | @@ -394,14 +414,19 @@ 307 | then it is required in order to regenerate the proof. 308 | """ 309 | 310 | - #description_hash: ElementModQ 311 | - """Hash from contestDescription""" 312 | + _types = [ 313 | + ["description_hash", ElementModQ], 314 | + #"""Hash from contestDescription""" 315 | 316 | - #ballot_selections: List[CiphertextBallotSelection] 317 | - """Collection of ballot selections""" 318 | + ["ballot_selections", [CiphertextBallotSelection]], 319 | + #"""Collection of ballot selections""" 320 | 321 | - #crypto_hash: ElementModQ 322 | - """Hash of the encrypted values""" 323 | + ["crypto_hash", ElementModQ], 324 | + #"""Hash of the encrypted values""" 325 | + 326 | + ["nonce", Nonces], 327 | + ["proof", Proof] 328 | + ] 329 | 330 | nonce = None 331 | """The nonce used to generate the encryption. Sensitive & should be treated as a secret""" 332 | @@ -471,6 +496,7 @@ 333 | Specifically, the seed hash in this context is the hash of the ContestDescription, 334 | or whatever `ElementModQ` was used to populate the `description_hash` field. 335 | """ 336 | + 337 | if seed_hash != self.description_hash: 338 | log_warning( 339 | f"mismatching contest hash: {self.object_id} expected({str(seed_hash)}), actual({str(self.description_hash)})" 340 | @@ -478,7 +504,7 @@ 341 | return False 342 | 343 | recalculated_crypto_hash = self.crypto_hash_with(seed_hash) 344 | - if self.crypto_hash != recalculated_crypto_hash: 345 | + if self.crypto_hash.__ne__(recalculated_crypto_hash): 346 | log_warning( 347 | f"mismatching crypto hash: {self.object_id} expected({str(recalculated_crypto_hash)}), actual({str(self.crypto_hash)})" 348 | ) 349 | @@ -490,6 +516,8 @@ 350 | log_warning(f"no proof exists for: {self.object_id}") 351 | return False 352 | 353 | + 354 | + 355 | # Verify the sum of the selections matches the proof 356 | elgamal_accumulation = self.elgamal_accumulate() 357 | return self.proof.is_valid( 358 | @@ -588,11 +616,13 @@ 359 | :field object_id: A unique Ballot ID that is relevant to the external system 360 | """ 361 | 362 | - #ballot_style: str 363 | - """The `object_id` of the `BallotStyle` in the `Election` Manifest""" 364 | + _types = [ 365 | + ["ballot_style", str], 366 | + # """The `object_id` of the `BallotStyle` in the `Election` Manifest""" 367 | 368 | - #contests: List[PlaintextBallotContest] 369 | - """The list of contests for this ballot""" 370 | + ["contests", [PlaintextBallotContest]] 371 | + # """The list of contests for this ballot""" 372 | + ] 373 | 374 | def is_valid(self, expected_ballot_style_id ) : 375 | """ 376 | @@ -632,29 +662,31 @@ 377 | :field object_id: A unique Ballot ID that is relevant to the external system 378 | """ 379 | 380 | - #ballot_style: str 381 | - """The `object_id` of the `BallotStyle` in the `Election` Manifest""" 382 | + _types = [ 383 | + ["ballot_style", str], 384 | + #"""The `object_id` of the `BallotStyle` in the `Election` Manifest""" 385 | 386 | - #description_hash: ElementModQ 387 | - """Hash of the election metadata""" 388 | + ["description_hash", ElementModQ], 389 | + #"""Hash of the election metadata""" 390 | 391 | - #previous_tracking_hash: ElementModQ 392 | - """Previous tracking hash or seed hash""" 393 | + ["previous_tracking_hash", ElementModQ], 394 | + #"""Previous tracking hash or seed hash""" 395 | 396 | - #contests: List[CiphertextBallotContest] 397 | - """List of contests for this ballot""" 398 | + ["contests", [CiphertextBallotContest]], 399 | + #"""List of contests for this ballot""" 400 | 401 | - #tracking_hash: Optional[ElementModQ] 402 | - """Unique ballot tracking hash for this ballot""" 403 | + ["tracking_hash", [ElementModQ]], 404 | + #"""Unique ballot tracking hash for this ballot""" 405 | 406 | - #timestamp: int 407 | - """Timestamp at which the ballot encryption is generated in tick""" 408 | + ["timestamp", int], 409 | + #"""Timestamp at which the ballot encryption is generated in tick""" 410 | 411 | - #crypto_hash: ElementModQ 412 | - """The hash of the encrypted ballot representation""" 413 | + ["crypto_hash", ElementModQ], 414 | + #"""The hash of the encrypted ballot representation""" 415 | 416 | - #nonce: Optional[ElementModQ] 417 | - """The nonce used to encrypt this ballot. Sensitive & should be treated as a secret""" 418 | + ["nonce", [ElementModQ]] 419 | + #"""The nonce used to encrypt this ballot. Sensitive & should be treated as a secret""" 420 | + ] 421 | 422 | def __eq__(self, other ) : 423 | return ( 424 | @@ -747,7 +779,7 @@ 425 | return False 426 | 427 | recalculated_crypto_hash = self.crypto_hash_with(seed_hash) 428 | - if self.crypto_hash != recalculated_crypto_hash: 429 | + if self.crypto_hash.__ne__(recalculated_crypto_hash): 430 | log_warning( 431 | f"mismatching crypto hash: {self.object_id} expected({str(recalculated_crypto_hash)}), actual({str(self.crypto_hash)})" 432 | ) 433 | @@ -812,7 +844,7 @@ 434 | def __eq__(self, other ) : 435 | return ( 436 | isinstance(other, CiphertextAcceptedBallot) 437 | - and super.__eq__(self, other) 438 | + and super().__eq__(self, other) 439 | and self.state == other.state 440 | ) 441 | 442 | diff -ruwN orig/electionguard/chaum_pedersen.py build/electionguard/chaum_pedersen.py 443 | --- orig/electionguard/chaum_pedersen.py 2020-11-03 10:10:47.000000000 +0100 444 | +++ build/electionguard/chaum_pedersen.py 2020-11-03 09:50:06.000000000 +0100 445 | @@ -19,6 +19,8 @@ 446 | from electionguard.nonces import Nonces 447 | from electionguard.proof import Proof, ProofUsage 448 | 449 | +__pragma__ ('kwargs') 450 | + 451 | 452 | @dataclass(frozen=True) 453 | class DisjunctiveChaumPedersenProof(Proof): 454 | @@ -47,8 +49,16 @@ 455 | usage = ProofUsage.SelectionValue 456 | """a description of how to use this proof""" 457 | 458 | - def __post_init__(self) : 459 | - super().__init__() 460 | + def __init__(self, proof_zero_pad, proof_zero_data, proof_one_pad, proof_one_data, proof_zero_challenge, proof_one_challenge, challenge, proof_zero_response, proof_one_response) : 461 | + self.proof_zero_pad = proof_zero_pad 462 | + self.proof_zero_data = proof_zero_data 463 | + self.proof_one_pad = proof_one_pad 464 | + self.proof_one_data = proof_one_data 465 | + self.proof_zero_challenge = proof_zero_challenge 466 | + self.proof_one_challenge = proof_one_challenge 467 | + self.challenge = challenge 468 | + self.proof_zero_response = proof_zero_response 469 | + self.proof_one_response = proof_one_response 470 | 471 | def is_valid( 472 | self, message , k , q 473 | @@ -62,7 +72,7 @@ 474 | :return: True if everything is consistent. False otherwise. 475 | """ 476 | 477 | - (alpha, beta) = message 478 | + (alpha, beta) = message.tuple() 479 | a0 = self.proof_zero_pad 480 | b0 = self.proof_zero_data 481 | a1 = self.proof_one_pad 482 | @@ -82,13 +92,13 @@ 483 | in_bounds_c1 = c1.is_in_bounds() 484 | in_bounds_v0 = v0.is_in_bounds() 485 | in_bounds_v1 = v1.is_in_bounds() 486 | - consistent_c = add_q(c0, c1) == c == hash_elems(q, alpha, beta, a0, b0, a1, b1) 487 | - consistent_gv0 = g_pow_p(v0) == mult_p(a0, pow_p(alpha, c0)) 488 | - consistent_gv1 = g_pow_p(v1) == mult_p(a1, pow_p(alpha, c1)) 489 | - consistent_kv0 = pow_p(k, v0) == mult_p(b0, pow_p(beta, c0)) 490 | - consistent_gc1kv1 = mult_p(g_pow_p(c1), pow_p(k, v1)) == mult_p( 491 | + consistent_c = add_q(c0, c1).__eq__(c) and c.__eq__(hash_elems(q, alpha, beta, a0, b0, a1, b1)) 492 | + consistent_gv0 = g_pow_p(v0).__eq__(mult_p(a0, pow_p(alpha, c0))) 493 | + consistent_gv1 = g_pow_p(v1).__eq__(mult_p(a1, pow_p(alpha, c1))) 494 | + consistent_kv0 = pow_p(k, v0).__eq__(mult_p(b0, pow_p(beta, c0))) 495 | + consistent_gc1kv1 = mult_p(g_pow_p(c1), pow_p(k, v1)).__eq__(mult_p( 496 | b1, pow_p(beta, c1) 497 | - ) 498 | + )) 499 | 500 | success = ( 501 | in_bounds_alpha 502 | @@ -153,8 +163,11 @@ 503 | usage = ProofUsage.SecretValue 504 | """a description of how to use this proof""" 505 | 506 | - def __post_init__(self) : 507 | - super().__init__() 508 | + def __init__(self, pad, data, challenge, response) : 509 | + self.pad = pad 510 | + self.data = data 511 | + self.challenge = challenge 512 | + self.response = response 513 | 514 | def is_valid( 515 | self, 516 | @@ -178,7 +191,7 @@ 517 | :param q: The extended base hash of the election 518 | :return: True if everything is consistent. False otherwise. 519 | """ 520 | - (alpha, beta) = message 521 | + (alpha, beta) = message.tuple() 522 | a = self.pad 523 | b = self.data 524 | c = self.challenge 525 | @@ -193,14 +206,14 @@ 526 | in_bounds_v = v.is_in_bounds() 527 | in_bounds_q = q.is_in_bounds() 528 | 529 | - same_c = c == hash_elems(q, alpha, beta, a, b, m) 530 | + same_c = c.__eq__(hash_elems(q, alpha, beta, a, b, m)) 531 | consistent_gv = ( 532 | in_bounds_v 533 | and in_bounds_a 534 | and in_bounds_c 535 | and in_bounds_v 536 | # The equation 𝑔^𝑣𝑖 = 𝑎𝑖𝐾^𝑐𝑖 537 | - and g_pow_p(v) == mult_p(a, pow_p(k, c)) 538 | + and g_pow_p(v).__eq__(mult_p(a, pow_p(k, c))) 539 | ) 540 | 541 | # The equation 𝐴^𝑣𝑖 = 𝑏𝑖𝑀𝑖^𝑐𝑖 mod 𝑝 542 | @@ -209,7 +222,7 @@ 543 | and in_bounds_b 544 | and in_bounds_c 545 | and in_bounds_v 546 | - and pow_p(alpha, v) == mult_p(b, pow_p(m, c)) 547 | + and pow_p(alpha, v).__eq__(mult_p(b, pow_p(m, c))) 548 | ) 549 | 550 | success = ( 551 | @@ -272,8 +285,12 @@ 552 | usage = ProofUsage.SelectionLimit 553 | """a description of how to use this proof""" 554 | 555 | - def __post_init__(self) : 556 | - super().__init__() 557 | + def __init__(self, pad, data, challenge, response, constant) : 558 | + self.pad = pad 559 | + self.data = data 560 | + self.challenge = challenge 561 | + self.response = response 562 | + self.constant = constant 563 | 564 | def is_valid( 565 | self, message , k , q 566 | @@ -288,7 +305,7 @@ 567 | :return: True if everything is consistent. False otherwise. 568 | """ 569 | 570 | - (alpha, beta) = message 571 | + (alpha, beta) = message.tuple() 572 | a = self.pad 573 | b = self.data 574 | c = self.challenge 575 | @@ -311,20 +328,20 @@ 576 | # this is an arbitrary constant check to verify that decryption will be performant 577 | # in some use cases this value may need to be increased 578 | sane_constant = 0 <= constant < 1_000_000_000 579 | - same_c = c == hash_elems(q, alpha, beta, a, b) 580 | + same_c = c.__eq__(hash_elems(q, alpha, beta, a, b)) 581 | consistent_gv = ( 582 | in_bounds_v 583 | and in_bounds_a 584 | and in_bounds_alpha 585 | and in_bounds_c 586 | # The equation 𝑔^𝑉 = 𝑎𝐴^𝐶 mod 𝑝 587 | - and g_pow_p(v) == mult_p(a, pow_p(alpha, c)) 588 | + and g_pow_p(v).__eq__(mult_p(a, pow_p(alpha, c))) 589 | ) 590 | 591 | # The equation 𝑔^𝐿𝐾^𝑣 = 𝑏𝐵^𝐶 mod 𝑝 592 | consistent_kv = in_bounds_constant and mult_p( 593 | g_pow_p(mult_p(c, constant_q)), pow_p(k, v) 594 | - ) == mult_p(b, pow_p(beta, c)) 595 | + ).__eq__(mult_p(b, pow_p(beta, c))) 596 | 597 | success = ( 598 | in_bounds_alpha 599 | @@ -385,7 +402,6 @@ 600 | :param seed: Used to generate other random values here 601 | :param plaintext: Zero or one 602 | """ 603 | - 604 | assert ( 605 | 0 <= plaintext <= 1 606 | ), "make_disjunctive_chaum_pedersen only supports plaintexts of 0 or 1" 607 | @@ -412,10 +428,10 @@ 608 | usually the election extended base hash (𝑄') 609 | :param seed: Used to generate other random values here 610 | """ 611 | - (alpha, beta) = message 612 | + (alpha, beta) = message.tuple() 613 | 614 | # Pick three random numbers in Q. 615 | - c1, v1, u0 = Nonces(seed, "disjoint-chaum-pedersen-proof")[0:3] 616 | + c1, v1, u0 = Nonces(seed, "disjoint-chaum-pedersen-proof").get_slice(0, 3) 617 | 618 | # Compute the NIZKP 619 | a0 = g_pow_p(u0) 620 | @@ -447,10 +463,10 @@ 621 | usually the election extended base hash (𝑄') 622 | :param seed: Used to generate other random values here 623 | """ 624 | - (alpha, beta) = message 625 | + (alpha, beta) = message.tuple() 626 | 627 | # Pick three random numbers in Q. 628 | - c0, v0, u1 = Nonces(seed, "disjoint-chaum-pedersen-proof")[0:3] 629 | + c0, v0, u1 = Nonces(seed, "disjoint-chaum-pedersen-proof").get_slice(0, 3) 630 | 631 | # Compute the NIZKP 632 | q_minus_c0 = negate_q(c0) 633 | @@ -483,10 +499,10 @@ 634 | :param hash_header: A value used when generating the challenge, 635 | usually the election extended base hash (𝑄') 636 | """ 637 | - (alpha, beta) = message 638 | + (alpha, beta) = message.tuple() 639 | 640 | # Pick one random number in Q. 641 | - u = Nonces(seed, "constant-chaum-pedersen-proof")[0] 642 | + u = Nonces(seed, "constant-chaum-pedersen-proof").get(0) 643 | a = g_pow_p(u) # 𝑔^𝑢𝑖 mod 𝑝 644 | b = pow_p(alpha, u) # 𝐴^𝑢𝑖 mod 𝑝 645 | c = hash_elems(hash_header, alpha, beta, a, b, m) # sha256(𝑄', A, B, a𝑖, b𝑖, 𝑀𝑖) 646 | @@ -514,10 +530,10 @@ 647 | :param hash_header: A value used when generating the challenge, 648 | usually the election extended base hash (𝑄') 649 | """ 650 | - (alpha, beta) = message 651 | + (alpha, beta) = message.tuple() 652 | 653 | # Pick one random number in Q. 654 | - u = Nonces(seed, "constant-chaum-pedersen-proof")[0] 655 | + u = Nonces(seed, "constant-chaum-pedersen-proof").get(0) 656 | a = g_pow_p(u) # 𝑔^𝑢𝑖 mod 𝑝 657 | b = pow_p(k, u) # 𝐴^𝑢𝑖 mod 𝑝 658 | c = hash_elems(hash_header, alpha, beta, a, b) # sha256(𝑄', A, B, a, b) 659 | diff -ruwN orig/electionguard/dlog.py build/electionguard/dlog.py 660 | --- orig/electionguard/dlog.py 2020-11-03 10:10:47.000000000 +0100 661 | +++ build/electionguard/dlog.py 2020-10-28 15:25:51.000000000 +0100 662 | @@ -1,8 +1,5 @@ 663 | # support for computing discrete logs, with a cache so they're never recomputed 664 | 665 | -import asyncio 666 | -from typing import Dict, Optional 667 | - 668 | from electionguard.group import G, ElementModP, ONE_MOD_P, mult_p, int_to_p_unchecked 669 | 670 | __dlog_cache = {ONE_MOD_P: 0} 671 | @@ -30,20 +27,14 @@ 672 | if e in __dlog_cache: 673 | return __dlog_cache[e] 674 | else: 675 | - return asyncio.run(__discrete_log_internal(e)) 676 | + return __discrete_log_internal(e) 677 | 678 | 679 | async def __discrete_log_internal(e ) : 680 | global __dlog_cache 681 | global __dlog_max_elem 682 | global __dlog_max_exp 683 | - global __dlog_lock 684 | - 685 | - if __dlog_lock is None: 686 | - # Initialize the lock on on first function call per process 687 | - __dlog_lock = asyncio.Lock() 688 | 689 | - async with __dlog_lock: 690 | g = int_to_p_unchecked(G) 691 | while e != __dlog_max_elem: 692 | __dlog_max_exp = __dlog_max_exp + 1 693 | diff -ruwN orig/electionguard/election.py build/electionguard/election.py 694 | --- orig/electionguard/election.py 2020-11-03 10:10:47.000000000 +0100 695 | +++ build/electionguard/election.py 2020-10-30 10:15:50.000000000 +0100 696 | @@ -1,7 +1,6 @@ 697 | from dataclasses import dataclass, field, InitVar 698 | from datetime import datetime 699 | from enum import Enum, unique 700 | -from typing import cast, List, Optional, Set, Any 701 | 702 | from electionguard.ballot import _list_eq 703 | from electionguard.election_object_base import ElectionObjectBase 704 | @@ -11,6 +10,7 @@ 705 | from electionguard.serializable import Serializable 706 | from electionguard.utils import get_optional, to_ticks 707 | 708 | +__pragma__ ('kwargs') 709 | 710 | @unique 711 | class ElectionType(Enum): 712 | @@ -129,6 +129,9 @@ 713 | Data entity used to represent multi-national text. Use when text on a ballot contains multi-national text. 714 | See: https://developers.google.com/elections-data/reference/internationalized-text 715 | """ 716 | + _types = [ 717 | + ["text", [Language]] 718 | + ] 719 | 720 | text = field(default_factory=lambda: []) 721 | 722 | @@ -145,17 +148,23 @@ 723 | For defining contact information about objects such as persons, boards of authorities, and organizations. 724 | See: https://developers.google.com/elections-data/reference/contact-information 725 | """ 726 | + _types = [ 727 | + ["address_line", [str]], 728 | + ["email", [AnnotatedString]], 729 | + ["phone", [AnnotatedString]], 730 | + ["_name", str] 731 | + ] 732 | 733 | address_line = field(default=None) 734 | email = field(default=None) 735 | phone = field(default=None) 736 | - name = field(default=None) 737 | + _name = field(default=None) 738 | 739 | def crypto_hash(self) : 740 | """ 741 | A hash representation of the object 742 | """ 743 | - return hash_elems(self.name, self.address_line, self.email, self.phone) 744 | + return hash_elems(self._name, self.address_line, self.email, self.phone) 745 | 746 | 747 | @dataclass(eq=True, unsafe_hash=True) 748 | @@ -166,8 +175,12 @@ 749 | See: https://developers.google.com/elections-data/reference/gp-unit 750 | """ 751 | 752 | - #name: str 753 | - #type: ReportingUnitType 754 | + _types = [ 755 | + ["_name", str], 756 | + ["_type", ReportingUnitType], 757 | + ["contact_information", ContactInformation] 758 | + ] 759 | + 760 | contact_information = field(default=None) 761 | 762 | def crypto_hash(self) : 763 | @@ -175,7 +188,7 @@ 764 | A hash representation of the object 765 | """ 766 | return hash_elems( 767 | - self.object_id, self.name, str(self.type.name), self.contact_information 768 | + self.object_id, self._name, str(self._type._name), self.contact_information 769 | ) 770 | 771 | 772 | @@ -185,6 +198,12 @@ 773 | A BallotStyle works as a key to uniquely specify a set of contests. See also `ContestDescription`. 774 | """ 775 | 776 | + _types = [ 777 | + ["geopolitical_unit_ids", [str]], 778 | + ["party_ids", "Optional"], 779 | + ["image_uri", str] 780 | + ] 781 | + 782 | geopolitical_unit_ids = field(default=None) 783 | party_ids = field(default=None) 784 | image_uri = field(default=None) 785 | @@ -205,6 +224,13 @@ 786 | See: https://developers.google.com/elections-data/reference/party 787 | """ 788 | 789 | + _types = [ 790 | + ["ballot_name", InternationalizedText], 791 | + ["abbreviation", str], 792 | + ["color", str], 793 | + ["logo_uri", str] 794 | + ] 795 | + 796 | ballot_name = field(default=InternationalizedText()) 797 | abbreviation = field(default=None) 798 | color = field(default=None) 799 | @@ -241,6 +267,13 @@ 800 | selections for the contest. See the wiki, readme's, and tests in this repo for more info 801 | """ 802 | 803 | + _types = [ 804 | + ["ballot_name", InternationalizedText], 805 | + ["party_id", str], 806 | + ["image_uri", str], 807 | + ["is_write_in", bool], 808 | + ] 809 | + 810 | ballot_name = field(default=InternationalizedText()) 811 | party_id = field(default=None) 812 | image_uri = field(default=None) 813 | @@ -276,8 +309,11 @@ 814 | however that information is not captured by default when encrypting a specific ballot. 815 | """ 816 | 817 | - #candidate_id: str 818 | - #sequence_order: int 819 | + _types = [ 820 | + ["candidate_id", str], 821 | + ["sequence_order", int] 822 | + ] 823 | + 824 | """ 825 | Used for ordering selections in a contest to ensure various encryption primitives are deterministic. 826 | The sequence order must be unique and should be representative of how the contests are represnted 827 | @@ -305,28 +341,33 @@ 828 | however that information is not captured by default when encrypting a specific ballot. 829 | """ 830 | 831 | - #electoral_district_id: str 832 | - #sequence_order: int 833 | - """ 834 | - Used for ordering contests in a ballot to ensure various encryption primitives are deterministic. 835 | - The sequence order must be unique and should be representative of how the contests are represnted 836 | - on a "master" ballot in an external system. The sequence order is not required to be in the order 837 | - in which they are displayed to a voter. Any acceptable range of integer values may be provided. 838 | - """ 839 | + _types = [ 840 | + ["electoral_district_id", str], 841 | + ["sequence_order", int], 842 | + 843 | + # Used for ordering contests in a ballot to ensure various encryption primitives are deterministic. 844 | + # The sequence order must be unique and should be representative of how the contests are represnted 845 | + # on a "master" ballot in an external system. The sequence order is not required to be in the order 846 | + # in which they are displayed to a voter. Any acceptable range of integer values may be provided. 847 | 848 | - #vote_variation: VoteVariationType 849 | + ["vote_variation", VoteVariationType], 850 | 851 | # Number of candidates that are elected in the contest ("n" of n-of-m). 852 | # Note: a referendum is considered a specific case of 1-of-m in ElectionGuard 853 | - #number_elected: int 854 | + ["number_elected", int], 855 | 856 | # Maximum number of votes/write-ins per voter in this contest. Used in cumulative voting 857 | # to indicate how many total votes a voter can spread around. In n-of-m elections, this will 858 | # be None. 859 | - #votes_allowed: Optional[int] 860 | + ["votes_allowed", "Optional"], # TODO: not the correct type 861 | 862 | # Name of the contest, not necessarily as it appears on the ballot. 863 | - #name: str 864 | + ["_name", str], 865 | + 866 | + ["ballot_selections", [SelectionDescription]], 867 | + ["ballot_title", InternationalizedText], 868 | + ["ballot_subtitle", InternationalizedText] 869 | + ] 870 | 871 | # For associating a ballot selection for the contest, i.e., a candidate, a ballot measure. 872 | ballot_selections = field(default_factory=lambda: []) 873 | @@ -345,7 +386,7 @@ 874 | and self.votes_allowed == other.votes_allowed 875 | and self.number_elected == other.number_elected 876 | and self.votes_allowed == other.votes_allowed 877 | - and self.name == other.name 878 | + and self._name == other._name 879 | and _list_eq(self.ballot_selections, other.ballot_selections) 880 | and self.ballot_title == other.ballot_title 881 | and self.ballot_subtitle == other.ballot_subtitle 882 | @@ -363,10 +404,10 @@ 883 | self.object_id, 884 | self.sequence_order, 885 | self.electoral_district_id, 886 | - str(self.vote_variation.name), 887 | + str(self.vote_variation._name), 888 | self.ballot_title, 889 | self.ballot_subtitle, 890 | - self.name, 891 | + self._name, 892 | self.number_elected, 893 | self.votes_allowed, 894 | self.ballot_selections, 895 | @@ -531,23 +572,28 @@ 896 | See: https://developers.google.com/elections-data/reference/election 897 | """ 898 | 899 | - #election_scope_id: str 900 | - #type: ElectionType 901 | - #start_date: datetime 902 | - #end_date: datetime 903 | - #geopolitical_units: List[GeopoliticalUnit] 904 | - #parties: List[Party] 905 | - #candidates: List[Candidate] 906 | - #contests: List[ContestDescription] 907 | - #ballot_styles: List[BallotStyle] 908 | - name = field(default=None) 909 | + _types = [ 910 | + ["election_scope_id", str], 911 | + ["_type", ElectionType], 912 | + ["start_date", datetime], 913 | + ["end_date", datetime], 914 | + ["geopolitical_units", [GeopoliticalUnit]], 915 | + ["parties", [Party]], 916 | + ["candidates", [Candidate]], 917 | + ["contests", [ContestDescription]], 918 | + ["ballot_styles", [BallotStyle]], 919 | + ["_name", InternationalizedText], 920 | + ["contact_information", ContactInformation] 921 | + ] 922 | + 923 | + _name = field(default=None) 924 | contact_information = field(default=None) 925 | 926 | def __eq__(self, other ) : 927 | return ( 928 | isinstance(other, ElectionDescription) 929 | and self.election_scope_id == other.election_scope_id 930 | - and self.type == other.type 931 | + and self._type == other._type 932 | and self.start_date == other.start_date 933 | and self.end_date == other.end_date 934 | and _list_eq(self.geopolitical_units, other.geopolitical_units) 935 | @@ -555,7 +601,7 @@ 936 | and _list_eq(self.candidates, other.candidates) 937 | and _list_eq(self.contests, other.contests) 938 | and _list_eq(self.ballot_styles, other.ballot_styles) 939 | - and self.name == other.name 940 | + and self._name == other._name 941 | and self.contact_information == other.contact_information 942 | ) 943 | 944 | @@ -563,13 +609,12 @@ 945 | """ 946 | Returns a hash of the metadata components of the election 947 | """ 948 | - 949 | return hash_elems( 950 | self.election_scope_id, 951 | - str(self.type.name), 952 | + str(self._type._name), 953 | to_ticks(self.start_date), 954 | to_ticks(self.end_date), 955 | - self.name, 956 | + self._name, 957 | self.contact_information, 958 | self.geopolitical_units, 959 | self.parties, 960 | @@ -739,13 +784,12 @@ 961 | 962 | description_hash = field(init=False) 963 | 964 | - def __post_init__(self, description ) : 965 | - object.__setattr__(self, "description_hash", description.crypto_hash()) 966 | - object.__setattr__(self, "geopolitical_units", description.geopolitical_units) 967 | - object.__setattr__(self, "ballot_styles", description.ballot_styles) 968 | - object.__setattr__( 969 | - self, "contests", self._generate_contests_with_placeholders(description) 970 | - ) 971 | + def __init__(self, description ) : 972 | + # TO-DO: why the post_init and the __setattr__ thing? 973 | + self.description_hash = description.crypto_hash() 974 | + self.geopolitical_units = description.geopolitical_units 975 | + self.ballot_styles = description.ballot_styles 976 | + self.contests = self._generate_contests_with_placeholders(description) 977 | 978 | def contest_for( 979 | self, contest_id 980 | @@ -844,26 +888,25 @@ 981 | `make_ciphertext_election_context` instead. 982 | """ 983 | 984 | - #number_of_guardians: int 985 | - """ 986 | - The number of guardians necessary to generate the public key 987 | - """ 988 | - #quorum: int 989 | - """ 990 | - The quorum of guardians necessary to decrypt an election. Must be less than `number_of_guardians` 991 | - """ 992 | + _types = [ 993 | + ["number_of_guardians", int], 994 | + #The number of guardians necessary to generate the public key 995 | + 996 | + ["quorum", int], 997 | + #The quorum of guardians necessary to decrypt an election. Must be less than `number_of_guardians` 998 | 999 | # the `joint public key (K)` in the [ElectionGuard Spec](https://github.com/microsoft/electionguard/wiki) 1000 | - #elgamal_public_key: ElementModP 1001 | + ["elgamal_public_key", ElementModP], 1002 | 1003 | # The hash of the election metadata 1004 | - #description_hash: ElementModQ 1005 | + ["description_hash", ElementModQ], 1006 | 1007 | # the `base hash code (𝑄)` in the [ElectionGuard Spec](https://github.com/microsoft/electionguard/wiki) 1008 | - #crypto_base_hash: ElementModQ 1009 | + ["crypto_base_hash", ElementModQ], 1010 | 1011 | # the `extended base hash code (𝑄')` in the [ElectionGuard Spec](https://github.com/microsoft/electionguard/wiki) 1012 | - #crypto_extended_base_hash: ElementModQ 1013 | + ["crypto_extended_base_hash", ElementModQ] 1014 | + ] 1015 | 1016 | 1017 | def make_ciphertext_election_context( 1018 | @@ -900,6 +943,7 @@ 1019 | crypto_base_hash = hash_elems( 1020 | P, Q, G, number_of_guardians, quorum, description_hash 1021 | ) 1022 | + 1023 | crypto_extended_base_hash = hash_elems(crypto_base_hash, elgamal_public_key) 1024 | return CiphertextElectionContext( 1025 | number_of_guardians=number_of_guardians, 1026 | @@ -927,7 +971,7 @@ 1027 | vote_variation=description.vote_variation, 1028 | number_elected=description.number_elected, 1029 | votes_allowed=description.votes_allowed, 1030 | - name=description.name, 1031 | + _name=description._name, 1032 | ballot_selections=description.ballot_selections, 1033 | ballot_title=description.ballot_title, 1034 | ballot_subtitle=description.ballot_subtitle, 1035 | @@ -954,11 +998,11 @@ 1036 | ) 1037 | return None 1038 | 1039 | - placeholder_object_id = f"{contest.object_id}-{use_sequence_id}" 1040 | + placeholder_object_id = str(contest.object_id) + "-" + str(use_sequence_id) 1041 | return SelectionDescription( 1042 | - f"{placeholder_object_id}-placeholder", 1043 | - f"{placeholder_object_id}-candidate", 1044 | - use_sequence_id, 1045 | + object_id = str(placeholder_object_id) + "-placeholder", 1046 | + candidate_id = str(placeholder_object_id) + "-candidate", 1047 | + sequence_order = use_sequence_id, 1048 | ) 1049 | 1050 | 1051 | diff -ruwN orig/electionguard/election_builder.py build/electionguard/election_builder.py 1052 | --- orig/electionguard/election_builder.py 2020-11-03 10:10:47.000000000 +0100 1053 | +++ build/electionguard/election_builder.py 2020-10-29 12:52:03.000000000 +0100 1054 | @@ -1,7 +1,4 @@ 1055 | -from __future__ import annotations 1056 | - 1057 | from dataclasses import dataclass, field 1058 | -from typing import Optional, Tuple 1059 | 1060 | from electionguard.election import ( 1061 | CiphertextElectionContext, 1062 | @@ -35,7 +32,10 @@ 1063 | 1064 | elgamal_public_key = field(default=None) 1065 | 1066 | - def __post_init__(self) : 1067 | + def __init__(self, number_of_guardians, quorum, description): 1068 | + self.number_of_guardians = number_of_guardians 1069 | + self.quorum = quorum 1070 | + self.description = description 1071 | self.internal_description = InternalElectionDescription(self.description) 1072 | 1073 | def set_public_key(self, elgamal_public_key ) : 1074 | diff -ruwN orig/electionguard/election_object_base.py build/electionguard/election_object_base.py 1075 | --- orig/electionguard/election_object_base.py 2020-11-03 10:10:47.000000000 +0100 1076 | +++ build/electionguard/election_object_base.py 2020-10-28 15:25:36.000000000 +0100 1077 | @@ -2,6 +2,7 @@ 1078 | 1079 | from electionguard.serializable import Serializable 1080 | 1081 | +__pragma__ ('kwargs') 1082 | 1083 | @dataclass 1084 | class ElectionObjectBase(Serializable): 1085 | diff -ruwN orig/electionguard/elgamal.py build/electionguard/elgamal.py 1086 | --- orig/electionguard/elgamal.py 2020-11-03 10:10:47.000000000 +0100 1087 | +++ build/electionguard/elgamal.py 2020-10-28 17:52:09.000000000 +0100 1088 | @@ -1,5 +1,3 @@ 1089 | -from typing import Iterable, NamedTuple, Optional 1090 | - 1091 | from electionguard.dlog import discrete_log 1092 | from electionguard.group import ( 1093 | ElementModQ, 1094 | @@ -18,14 +16,18 @@ 1095 | from electionguard.utils import flatmap_optional, get_optional 1096 | 1097 | 1098 | -class ElGamalKeyPair(NamedTuple): 1099 | +class ElGamalKeyPair: 1100 | """A tuple of an ElGamal secret key and public key.""" 1101 | 1102 | #secret_key: ElementModQ 1103 | #public_key: ElementModP 1104 | 1105 | + def __init__(self, secret_key, public_key): 1106 | + self.secret_key = secret_key 1107 | + self.public_key = public_key 1108 | + 1109 | 1110 | -class ElGamalCiphertext(NamedTuple): 1111 | +class ElGamalCiphertext: 1112 | """ 1113 | An "exponential ElGamal ciphertext" (i.e., with the plaintext in the exponent to allow for 1114 | homomorphic addition). Create one with `elgamal_encrypt`. Add them with `elgamal_add`. 1115 | @@ -38,6 +40,13 @@ 1116 | #data: ElementModP 1117 | """encrypted data or beta""" 1118 | 1119 | + def __init__(self, pad, data): 1120 | + self.pad = pad 1121 | + self.data = data 1122 | + 1123 | + def tuple(self): 1124 | + return [self.pad, self.data] 1125 | + 1126 | def decrypt_known_product(self, product ) : 1127 | """ 1128 | Decrypts an ElGamal ciphertext with a "known product" (the blinding factor used in the encryption). 1129 | diff -ruwN orig/electionguard/encrypt.py build/electionguard/encrypt.py 1130 | --- orig/electionguard/encrypt.py 2020-11-03 10:10:47.000000000 +0100 1131 | +++ build/electionguard/encrypt.py 2020-11-03 10:39:58.000000000 +0100 1132 | @@ -1,6 +1,3 @@ 1133 | -from typing import List, Optional 1134 | -from uuid import getnode 1135 | - 1136 | from electionguard.ballot import ( 1137 | CiphertextBallot, 1138 | CiphertextBallotContest, 1139 | @@ -23,71 +20,11 @@ 1140 | from electionguard.elgamal import elgamal_encrypt 1141 | from electionguard.group import ElementModP, ElementModQ, rand_q 1142 | from electionguard.logs import log_warning 1143 | -from electionguard.nonces import Nonces 1144 | +from Nonces import Nonces 1145 | from electionguard.serializable import Serializable 1146 | -from electionguard.tracker import get_hash_for_device 1147 | from electionguard.utils import get_optional, get_or_else_optional_func 1148 | 1149 | - 1150 | -class EncryptionDevice(Serializable): 1151 | - """ 1152 | - Metadata for encryption device 1153 | - """ 1154 | - 1155 | - #uuid: int 1156 | - #location: str 1157 | - 1158 | - def __init__(self, location ) : 1159 | - self.uuid = generate_device_uuid() 1160 | - self.location = location 1161 | - 1162 | - def get_hash(self) : 1163 | - """ 1164 | - Get hash for encryption device 1165 | - :return: Starting hash 1166 | - """ 1167 | - return get_hash_for_device(self.uuid, self.location) 1168 | - 1169 | - 1170 | -class EncryptionMediator(object): 1171 | - """ 1172 | - An object for caching election and encryption state. 1173 | - 1174 | - It composes Elections and Ballots. 1175 | - """ 1176 | - 1177 | - #_metadata: InternalElectionDescription 1178 | - #_encryption: CiphertextElectionContext 1179 | - #_seed_hash: ElementModQ 1180 | - 1181 | - def __init__( 1182 | - self, 1183 | - election_metadata , 1184 | - context , 1185 | - encryption_device , 1186 | - ): 1187 | - self._metadata = election_metadata 1188 | - self._encryption = context 1189 | - self._seed_hash = encryption_device.get_hash() 1190 | - 1191 | - def encrypt(self, ballot ) : 1192 | - """ 1193 | - Encrypt the specified ballot using the cached election context. 1194 | - """ 1195 | - encrypted_ballot = encrypt_ballot( 1196 | - ballot, self._metadata, self._encryption, self._seed_hash 1197 | - ) 1198 | - if encrypted_ballot is not None and encrypted_ballot.tracking_hash is not None: 1199 | - self._seed_hash = encrypted_ballot.tracking_hash 1200 | - return encrypted_ballot 1201 | - 1202 | - 1203 | -def generate_device_uuid() : 1204 | - """ 1205 | - Get unique identifier for device 1206 | - :return: Unique identifier 1207 | - """ 1208 | - return getnode() 1209 | +__pragma__ ('kwargs') 1210 | 1211 | 1212 | def selection_from( 1213 | @@ -107,7 +44,7 @@ 1214 | """ 1215 | 1216 | return PlaintextBallotSelection( 1217 | - description.object_id, 1218 | + object_id=description.object_id, 1219 | vote=str(is_affirmative), 1220 | is_placeholder_selection=is_placeholder, 1221 | ) 1222 | @@ -127,7 +64,7 @@ 1223 | for selection_description in description.ballot_selections: 1224 | selections.append(selection_from(selection_description)) 1225 | 1226 | - return PlaintextBallotContest(description.object_id, selections) 1227 | + return PlaintextBallotContest(object_id=description.object_id, ballot_selections=selections) 1228 | 1229 | 1230 | def encrypt_selection( 1231 | @@ -159,7 +96,7 @@ 1232 | 1233 | selection_description_hash = selection_description.crypto_hash() 1234 | nonce_sequence = Nonces(selection_description_hash, nonce_seed) 1235 | - selection_nonce = nonce_sequence[selection_description.sequence_order] 1236 | + selection_nonce = nonce_sequence.get(selection_description.sequence_order) 1237 | disjunctive_chaum_pedersen_nonce = next(iter(nonce_sequence)) 1238 | 1239 | selection_representation = selection.to_int() 1240 | @@ -174,7 +111,6 @@ 1241 | return None 1242 | 1243 | # TODO: ISSUE #35: encrypt/decrypt: encrypt the extended_data field 1244 | - 1245 | # Create the return object 1246 | encrypted_selection = make_ciphertext_ballot_selection( 1247 | object_id=selection.object_id, 1248 | @@ -249,7 +185,7 @@ 1249 | # account for sequence id 1250 | contest_description_hash = contest_description.crypto_hash() 1251 | nonce_sequence = Nonces(contest_description_hash, nonce_seed) 1252 | - contest_nonce = nonce_sequence[contest_description.sequence_order] 1253 | + contest_nonce = nonce_sequence.get(contest_description.sequence_order) 1254 | chaum_pedersen_nonce = next(iter(nonce_sequence)) 1255 | 1256 | encrypted_selections = list() 1257 | @@ -418,7 +354,6 @@ 1258 | nonce_seed = CiphertextBallot.nonce_seed( 1259 | election_metadata.description_hash, ballot.object_id, random_master_nonce, 1260 | ) 1261 | - 1262 | encrypted_contests = list() 1263 | 1264 | # only iterate on contests for this specific ballot style 1265 | diff -ruwN orig/electionguard/group.py build/electionguard/group.py 1266 | --- orig/electionguard/group.py 2020-11-03 10:10:47.000000000 +0100 1267 | +++ build/electionguard/group.py 2020-11-03 09:49:54.000000000 +0100 1268 | @@ -2,41 +2,35 @@ 1269 | # in the sense that performance may be less than hand-optimized C code, and no guarantees are 1270 | # made about timing or other side-channels. 1271 | 1272 | -from typing import Any, Final, NamedTuple, Optional, Union 1273 | -from base64 import b16decode 1274 | -from secrets import randbelow 1275 | -from gmpy2 import mpz, powmod, invert, to_binary, from_binary 1276 | +import BigInteger as bigInt 1277 | +from SecureRandom import randomBelow 1278 | 1279 | -# Constants used by ElectionGuard 1280 | -Q = pow(2, 256) - 189 1281 | -P = 1044388881413152506691752710716624382579964249047383780384233483283953907971553643537729993126875883902173634017777416360502926082946377942955704498542097614841825246773580689398386320439747911160897731551074903967243883427132918813748016269754522343505285898816777211761912392772914485521155521641049273446207578961939840619466145806859275053476560973295158703823395710210329314709715239251736552384080845836048778667318931418338422443891025911884723433084701207771901944593286624979917391350564662632723703007964229849154756196890615252286533089643184902706926081744149289517418249153634178342075381874131646013444796894582106870531535803666254579602632453103741452569793905551901541856173251385047414840392753585581909950158046256810542678368121278509960520957624737942914600310646609792665012858397381435755902851312071248102599442308951327039250818892493767423329663783709190716162023529669217300939783171415808233146823000766917789286154006042281423733706462905243774854543127239500245873582012663666430583862778167369547603016344242729592244544608279405999759391099775667746401633668308698186721172238255007962658564443858927634850415775348839052026675785694826386930175303143450046575460843879941791946313299322976993405829119 1282 | +mpz = bigInt 1283 | 1284 | +# Constants used by ElectionGuard 1285 | +Q = mpz(2).pow(256).minus(189) 1286 | +P = mpz("1044388881413152506691752710716624382579964249047383780384233483283953907971553643537729993126875883902173634017777416360502926082946377942955704498542097614841825246773580689398386320439747911160897731551074903967243883427132918813748016269754522343505285898816777211761912392772914485521155521641049273446207578961939840619466145806859275053476560973295158703823395710210329314709715239251736552384080845836048778667318931418338422443891025911884723433084701207771901944593286624979917391350564662632723703007964229849154756196890615252286533089643184902706926081744149289517418249153634178342075381874131646013444796894582106870531535803666254579602632453103741452569793905551901541856173251385047414840392753585581909950158046256810542678368121278509960520957624737942914600310646609792665012858397381435755902851312071248102599442308951327039250818892493767423329663783709190716162023529669217300939783171415808233146823000766917789286154006042281423733706462905243774854543127239500245873582012663666430583862778167369547603016344242729592244544608279405999759391099775667746401633668308698186721172238255007962658564443858927634850415775348839052026675785694826386930175303143450046575460843879941791946313299322976993405829119") 1287 | 1288 | -R = ((P - 1) * pow(Q, -1, P)) % P 1289 | -G = 14245109091294741386751154342323521003543059865261911603340669522218159898070093327838595045175067897363301047764229640327930333001123401070596314469603183633790452807428416775717923182949583875381833912370889874572112086966300498607364501764494811956017881198827400327403252039184448888877644781610594801053753235453382508543906993571248387749420874609737451803650021788641249940534081464232937193671929586747339353451021712752406225276255010281004857233043241332527821911604413582442915993833774890228705495787357234006932755876972632840760599399514028393542345035433135159511099877773857622699742816228063106927776147867040336649025152771036361273329385354927395836330206311072577683892664475070720408447257635606891920123791602538518516524873664205034698194561673019535564273204744076336022130453963648114321050173994259620611015189498335966173440411967562175734606706258335095991140827763942280037063180207172918769921712003400007923888084296685269233298371143630883011213745082207405479978418089917768242592557172834921185990876960527013386693909961093302289646193295725135238595082039133488721800071459503353417574248679728577942863659802016004283193163470835709405666994892499382890912238098413819320185166580019604608311466 1290 | 1291 | +R = P.minus(1).multiply(Q.modInv(P)).mod(P) 1292 | +G = mpz("14245109091294741386751154342323521003543059865261911603340669522218159898070093327838595045175067897363301047764229640327930333001123401070596314469603183633790452807428416775717923182949583875381833912370889874572112086966300498607364501764494811956017881198827400327403252039184448888877644781610594801053753235453382508543906993571248387749420874609737451803650021788641249940534081464232937193671929586747339353451021712752406225276255010281004857233043241332527821911604413582442915993833774890228705495787357234006932755876972632840760599399514028393542345035433135159511099877773857622699742816228063106927776147867040336649025152771036361273329385354927395836330206311072577683892664475070720408447257635606891920123791602538518516524873664205034698194561673019535564273204744076336022130453963648114321050173994259620611015189498335966173440411967562175734606706258335095991140827763942280037063180207172918769921712003400007923888084296685269233298371143630883011213745082207405479978418089917768242592557172834921185990876960527013386693909961093302289646193295725135238595082039133488721800071459503353417574248679728577942863659802016004283193163470835709405666994892499382890912238098413819320185166580019604608311466") 1293 | 1294 | Q_MINUS_ONE = Q - 1 1295 | 1296 | 1297 | -class ElementModQ(NamedTuple): 1298 | +class ElementModQ: 1299 | """An element of the smaller `mod q` space, i.e., in [0, Q), where Q is a 256-bit prime.""" 1300 | 1301 | #elem: mpz 1302 | - 1303 | - def to_bytes(self) : 1304 | - """ 1305 | - Converts from the element to the representation of bytes by first going through hex. 1306 | - This is preferable to directly accessing `elem`, whose representation might change. 1307 | - """ 1308 | - return b16decode(self.to_hex()) 1309 | + def __init__(self, elem): 1310 | + self.elem = elem 1311 | 1312 | def to_hex(self) : 1313 | """ 1314 | Converts from the element to the hex representation of bytes. This is preferable to directly 1315 | accessing `elem`, whose representation might change. 1316 | """ 1317 | - h = format(self.elem, "02X") 1318 | + h = self.elem.toString(16).upper() 1319 | if len(h) % 2: 1320 | h = "0" + h 1321 | return h 1322 | @@ -53,14 +47,14 @@ 1323 | Validates that the element is actually within the bounds of [0,Q). 1324 | Returns true if all is good, false if something's wrong. 1325 | """ 1326 | - return 0 <= self.elem < Q 1327 | + return 0 <= self.elem and self.elem.lesser(Q) 1328 | 1329 | def is_in_bounds_no_zero(self) : 1330 | """ 1331 | Validates that the element is actually within the bounds of [1,Q). 1332 | Returns true if all is good, false if something's wrong. 1333 | """ 1334 | - return 0 < self.elem < Q 1335 | + return 0 < self.elem and self.elem.lesser(Q) 1336 | 1337 | # overload != (not equal to) operator 1338 | def __ne__(self, other ) : 1339 | @@ -75,20 +69,25 @@ 1340 | ) and eq_elems(self, other) 1341 | 1342 | def __str__(self) : 1343 | - return self.elem.digits() 1344 | + return self.elem.toString() 1345 | 1346 | + def toString(self) : 1347 | + return str(self) 1348 | 1349 | -class ElementModP(NamedTuple): 1350 | + 1351 | +class ElementModP: 1352 | """An element of the larger `mod p` space, i.e., in [0, P), where P is a 4096-bit prime.""" 1353 | 1354 | #elem: mpz 1355 | + def __init__(self, elem): 1356 | + self.elem = elem 1357 | 1358 | def to_hex(self) : 1359 | """ 1360 | Converts from the element to the hex representation of bytes. This is preferable to directly 1361 | accessing `elem`, whose representation might change. 1362 | """ 1363 | - h = format(self.elem, "02X") 1364 | + h = self.elem.toString(16).upper() 1365 | if len(h) % 2: 1366 | h = "0" + h 1367 | return h 1368 | @@ -105,21 +104,21 @@ 1369 | Validates that the element is actually within the bounds of [0,P). 1370 | Returns true if all is good, false if something's wrong. 1371 | """ 1372 | - return 0 <= self.elem < P 1373 | + return 0 <= self.elem and self.elem.lesser(P) 1374 | 1375 | def is_in_bounds_no_zero(self) : 1376 | """ 1377 | Validates that the element is actually within the bounds of [1,P). 1378 | Returns true if all is good, false if something's wrong. 1379 | """ 1380 | - return 0 < self.elem < P 1381 | + return 0 < self.elem and self.elem.lesser(P) 1382 | 1383 | def is_valid_residue(self) : 1384 | """ 1385 | Validates that this element is in Z^r_p. 1386 | Returns true if all is good, false if something's wrong. 1387 | """ 1388 | - residue = pow_p(self, ElementModQ(mpz(Q))) == ONE_MOD_P 1389 | + residue = pow_p(self, ElementModQ(Q)).__eq__(ONE_MOD_P) 1390 | return self.is_in_bounds() and residue 1391 | 1392 | # overload != (not equal to) operator 1393 | @@ -135,7 +134,10 @@ 1394 | ) and eq_elems(self, other) 1395 | 1396 | def __str__(self) : 1397 | - return self.elem.digits() 1398 | + return self.elem.toString() 1399 | + 1400 | + def toString(self) : 1401 | + return str(self) 1402 | 1403 | 1404 | # Common constants 1405 | @@ -147,11 +149,6 @@ 1406 | ONE_MOD_P = ElementModP(mpz(1)) 1407 | TWO_MOD_P = ElementModP(mpz(2)) 1408 | 1409 | -ElementModPOrQ = Union[ElementModP, ElementModQ] 1410 | -ElementModPOrQorInt = Union[ElementModP, ElementModQ, int] 1411 | -ElementModQorInt = Union[ElementModQ, int] 1412 | -ElementModPorInt = Union[ElementModP, int] 1413 | - 1414 | 1415 | def hex_to_q(input ) : 1416 | """ 1417 | @@ -159,9 +156,9 @@ 1418 | Returns `None` if the number is out of the allowed 1419 | [0,Q) range. 1420 | """ 1421 | - i = int(input, 16) 1422 | + i = mpz(input, 16) 1423 | if 0 <= i < Q: 1424 | - return ElementModQ(mpz(i)) 1425 | + return ElementModQ(i) 1426 | else: 1427 | return None 1428 | 1429 | @@ -172,9 +169,9 @@ 1430 | Returns `None` if the number is out of the allowed 1431 | [0,Q) range. 1432 | """ 1433 | - i = int(input) 1434 | + i = mpz(input) 1435 | if 0 <= i < Q: 1436 | - return ElementModQ(mpz(i)) 1437 | + return ElementModQ(i) 1438 | else: 1439 | return None 1440 | 1441 | @@ -186,8 +183,7 @@ 1442 | element (i.e., outside of [0,Q)). Useful for tests of it 1443 | you're absolutely, positively, certain the input is in-bounds. 1444 | """ 1445 | - 1446 | - m = mpz(int(i)) 1447 | + m = mpz(i) 1448 | return ElementModQ(m) 1449 | 1450 | 1451 | @@ -197,9 +193,9 @@ 1452 | Returns `None` if the number is out of the allowed 1453 | [0,P) range. 1454 | """ 1455 | - i = int(input) 1456 | + i = mpz(input) 1457 | if 0 <= i < P: 1458 | - return ElementModP(mpz(i)) 1459 | + return ElementModP(i) 1460 | else: 1461 | return None 1462 | 1463 | @@ -211,24 +207,9 @@ 1464 | element (i.e., outside of [0,P)). Useful for tests or if 1465 | you're absolutely, positively, certain the input is in-bounds. 1466 | """ 1467 | - m = mpz(int(i)) 1468 | + m = mpz(i) 1469 | return ElementModP(m) 1470 | 1471 | - 1472 | -def q_to_bytes(e ) : 1473 | - """ 1474 | - Returns a byte sequence from the element. 1475 | - """ 1476 | - return to_binary(e.elem) 1477 | - 1478 | - 1479 | -def bytes_to_q(b ) : 1480 | - """ 1481 | - Returns an element from a byte sequence. 1482 | - """ 1483 | - return ElementModQ(mpz(from_binary(b))) 1484 | - 1485 | - 1486 | def add_q(*elems ) : 1487 | """ 1488 | Adds together one or more elements in Q, returns the sum mod Q. 1489 | @@ -237,7 +218,7 @@ 1490 | for e in elems: 1491 | if isinstance(e, int): 1492 | e = int_to_q_unchecked(e) 1493 | - t = (t + e.elem) % Q 1494 | + t = t.add(e.elem).mod(Q) 1495 | 1496 | return ElementModQ(t) 1497 | 1498 | @@ -251,7 +232,11 @@ 1499 | if isinstance(b, int): 1500 | b = int_to_q_unchecked(b) 1501 | 1502 | - return ElementModQ((a.elem - b.elem) % Q) 1503 | + result = a.elem.minus(b.elem) 1504 | + if result < 0: 1505 | + return ElementModQ(result.mod(Q).plus(Q)) 1506 | + 1507 | + return ElementModQ(result.mod(Q)) 1508 | 1509 | 1510 | def div_p(a , b ) : 1511 | @@ -263,7 +248,7 @@ 1512 | if isinstance(b, int): 1513 | b = int_to_p_unchecked(b) 1514 | 1515 | - inverse = invert(b.elem, mpz(P)) 1516 | + inverse = b.elem.modInv(P) 1517 | return mult_p(a, int_to_p_unchecked(inverse)) 1518 | 1519 | 1520 | @@ -276,7 +261,7 @@ 1521 | if isinstance(b, int): 1522 | b = int_to_p_unchecked(b) 1523 | 1524 | - inverse = invert(b.elem, mpz(Q)) 1525 | + inverse = b.elem.modInv(Q) 1526 | return mult_q(a, int_to_q_unchecked(inverse)) 1527 | 1528 | 1529 | @@ -286,7 +271,7 @@ 1530 | """ 1531 | if isinstance(a, int): 1532 | a = int_to_q_unchecked(a) 1533 | - return ElementModQ(Q - a.elem) 1534 | + return ElementModQ(Q.minus(a.elem)) 1535 | 1536 | 1537 | def a_plus_bc_q( 1538 | @@ -302,7 +287,7 @@ 1539 | if isinstance(c, int): 1540 | c = int_to_q_unchecked(c) 1541 | 1542 | - return ElementModQ((a.elem + b.elem * c.elem) % Q) 1543 | + return ElementModQ(a.elem.add(b.elem.multiply(c.elem)).mod(Q)) 1544 | 1545 | 1546 | def mult_inv_p(e ) : 1547 | @@ -315,7 +300,7 @@ 1548 | e = int_to_p_unchecked(e) 1549 | 1550 | assert e.elem != 0, "No multiplicative inverse for zero" 1551 | - return ElementModP(powmod(e.elem, -1, P)) 1552 | + return ElementModP(e.elem.modInv(P)) 1553 | 1554 | 1555 | def pow_p(b , e ) : 1556 | @@ -331,7 +316,7 @@ 1557 | if isinstance(e, int): 1558 | e = int_to_p_unchecked(e) 1559 | 1560 | - return ElementModP(powmod(b.elem, e.elem, P)) 1561 | + return ElementModP(b.elem.modPow(e.elem, P)) 1562 | 1563 | 1564 | def pow_q(b , e ) : 1565 | @@ -347,7 +332,7 @@ 1566 | if isinstance(e, int): 1567 | e = int_to_q_unchecked(e) 1568 | 1569 | - return ElementModQ(powmod(b.elem, e.elem, Q)) 1570 | + return ElementModQ(b.elem.modPow(e.elem, Q)) 1571 | 1572 | 1573 | def mult_p(*elems ) : 1574 | @@ -360,7 +345,7 @@ 1575 | for x in elems: 1576 | if isinstance(x, int): 1577 | x = int_to_p_unchecked(x) 1578 | - product = (product * x.elem) % P 1579 | + product = product.multiply(x.elem).mod(P) 1580 | return ElementModP(product) 1581 | 1582 | 1583 | @@ -374,7 +359,7 @@ 1584 | for x in elems: 1585 | if isinstance(x, int): 1586 | x = int_to_p_unchecked(x) 1587 | - product = (product * x.elem) % Q 1588 | + product = product.multiply(x.elem).mod(Q) 1589 | return ElementModQ(product) 1590 | 1591 | 1592 | @@ -393,7 +378,7 @@ 1593 | 1594 | :return: Random value between 0 and Q 1595 | """ 1596 | - return int_to_q_unchecked(randbelow(Q)) 1597 | + return int_to_q_unchecked(randomBelow(Q)) 1598 | 1599 | 1600 | def rand_range_q(start ) : 1601 | @@ -408,7 +393,7 @@ 1602 | 1603 | random = 0 1604 | while random < start: 1605 | - random = randbelow(Q) 1606 | + random = randomBelow(Q) 1607 | return int_to_q_unchecked(random) 1608 | 1609 | 1610 | @@ -416,4 +401,4 @@ 1611 | """ 1612 | Returns whether the two elements hold the same value. 1613 | """ 1614 | - return a.elem == b.elem 1615 | + return a.elem.eq(b.elem) 1616 | diff -ruwN orig/electionguard/hash.py build/electionguard/hash.py 1617 | --- orig/electionguard/hash.py 2020-11-03 10:10:47.000000000 +0100 1618 | +++ build/electionguard/hash.py 2020-10-30 09:48:02.000000000 +0100 1619 | @@ -1,11 +1,3 @@ 1620 | -from abc import abstractmethod 1621 | -from hashlib import sha256 1622 | -from typing import ( 1623 | - Union, 1624 | - Protocol, 1625 | - runtime_checkable, 1626 | - Sequence, 1627 | -) 1628 | 1629 | from electionguard.group import ( 1630 | ElementModPOrQ, 1631 | @@ -14,15 +6,17 @@ 1632 | int_to_q_unchecked, 1633 | ElementModP, 1634 | ) 1635 | +import BigInteger as bigInt 1636 | +from Hash import sha256 1637 | +from Array import isArray 1638 | 1639 | +mpz = bigInt 1640 | 1641 | -@runtime_checkable 1642 | -class CryptoHashable(Protocol): 1643 | +class CryptoHashable: 1644 | """ 1645 | Denotes hashable 1646 | """ 1647 | 1648 | - @abstractmethod 1649 | def crypto_hash(self) : 1650 | """ 1651 | Generates a hash given the fields on the implementing instance. 1652 | @@ -30,13 +24,11 @@ 1653 | ... 1654 | 1655 | 1656 | -@runtime_checkable 1657 | -class CryptoHashCheckable(Protocol): 1658 | +class CryptoHashCheckable: 1659 | """ 1660 | Checkable version of crypto hash 1661 | """ 1662 | 1663 | - @abstractmethod 1664 | def crypto_hash_with(self, seed_hash ) : 1665 | """ 1666 | Generates a hash with a given seed that can be checked later against the seed and class metadata. 1667 | @@ -44,16 +36,7 @@ 1668 | ... 1669 | 1670 | 1671 | -# All the "atomic" types that we know how to hash. 1672 | -CRYPTO_HASHABLE_T = Union[CryptoHashable, ElementModPOrQ, str, int, None] 1673 | - 1674 | -# "Compound" types that we know how to hash. Note that we're using Sequence, rather than List, 1675 | -# because Sequences are read-only, and thus safely covariant. All this really means is that 1676 | -# we promise never to mutate any list that you pass to hash_elems. 1677 | -CRYPTO_HASHABLE_ALL = Union[ 1678 | - Sequence[CRYPTO_HASHABLE_T], CRYPTO_HASHABLE_T, 1679 | -] 1680 | - 1681 | +from random import randint 1682 | 1683 | def hash_elems(*a ) : 1684 | """ 1685 | @@ -66,12 +49,11 @@ 1686 | :return: A cryptographic hash of these elements, concatenated. 1687 | """ 1688 | h = sha256() 1689 | - h.update("|".encode("utf-8")) 1690 | + h.update("|") 1691 | for x in a: 1692 | # We could just use str(x) for everything, but then we'd have a resulting string 1693 | # that's a bit Python-specific, and we'd rather make it easier for other languages 1694 | # to exactly match this hash function. 1695 | - 1696 | if not x: 1697 | # This case captures empty lists and None, nicely guaranteeing that we don't 1698 | # need to do a recursive call if the list is empty. So we need a string to 1699 | @@ -86,13 +68,16 @@ 1700 | elif isinstance(x, str): 1701 | # strings are iterable, so it's important to handle them before the following check 1702 | hash_me = x 1703 | - elif isinstance(x, Sequence): 1704 | + elif isArray(x): 1705 | + if x.length == 0: 1706 | + hash_me = "null" 1707 | + else: 1708 | # The simplest way to deal with lists, tuples, and such are to crunch them recursively. 1709 | hash_me = hash_elems(*x).to_hex() 1710 | else: 1711 | hash_me = str(x) 1712 | - h.update((hash_me + "|").encode("utf-8")) 1713 | - 1714 | + h.update((hash_me + "|")) 1715 | # We don't need the checked version of int_to_q, because the 1716 | # modulo operation here guarantees that we're in bounds. 1717 | - return int_to_q_unchecked(int.from_bytes(h.digest(), byteorder="big") % Q_MINUS_ONE) 1718 | + return int_to_q_unchecked(mpz(h.digest(), 16).mod(Q_MINUS_ONE)) 1719 | + 1720 | diff -ruwN orig/electionguard/logs.py build/electionguard/logs.py 1721 | --- orig/electionguard/logs.py 2020-11-03 10:10:47.000000000 +0100 1722 | +++ build/electionguard/logs.py 2020-10-29 11:24:41.000000000 +0100 1723 | @@ -1,14 +1,11 @@ 1724 | -import inspect 1725 | -import logging 1726 | -import os.path 1727 | -import sys 1728 | -from typing import Any, Tuple 1729 | -from logging.handlers import RotatingFileHandler 1730 | +from logger import Logger 1731 | 1732 | from electionguard.singleton import Singleton 1733 | 1734 | FORMAT = "[%(process)d:%(asctime)s]:%(levelname)s:%(message)s" 1735 | 1736 | +__pragma__ ('kwargs') 1737 | + 1738 | 1739 | class ElectionGuardLog(Singleton): 1740 | """ 1741 | @@ -18,87 +15,42 @@ 1742 | #__logger: logging.Logger 1743 | 1744 | def __init__(self) : 1745 | - super(ElectionGuardLog, self).__init__() 1746 | - 1747 | - self.__logger = logging.getLogger("electionguard") 1748 | - self.__logger.addHandler(self._get_file_handler()) 1749 | - self.__logger.addHandler(self._get_stream_handler()) 1750 | - 1751 | - @staticmethod 1752 | - def __get_call_info() : 1753 | - stack = inspect.stack() 1754 | - 1755 | - # stack[0]: __get_call_info 1756 | - # stack[1]: __formatted_message 1757 | - # stack[2]: (log method, e.g. "warn") 1758 | - # stack[3]: Singleton 1759 | - # stack[4]: caller <-- we want this 1760 | - 1761 | - filename = stack[4][1] 1762 | - line = stack[4][2] 1763 | - funcname = stack[4][3] 1764 | + super().__init__() 1765 | 1766 | - return filename, funcname, line 1767 | + self.__logger = Logger() 1768 | 1769 | def __formatted_message(self, message ) : 1770 | - filename, funcname, line = self.__get_call_info() 1771 | - message = f"{os.path.basename(filename)}.{funcname}:#L{line}: {message}" 1772 | return message 1773 | 1774 | - def _get_stream_handler(self) : 1775 | - """ 1776 | - Get a Stream Handler, sends only warnings and errors to stdout. 1777 | - """ 1778 | - stream_handler = logging.StreamHandler(sys.stdout) 1779 | - stream_handler.setLevel(logging.WARNING) 1780 | - stream_handler.setFormatter(logging.Formatter(FORMAT)) 1781 | - return stream_handler 1782 | - 1783 | - def _get_file_handler(self) : 1784 | - """ 1785 | - Get a File System Handler, sends verbose logging to a file, `electionguard.log`. 1786 | - When that file gets too large, the logs will rotate, creating files with names 1787 | - like `electionguard.log.1`. 1788 | - """ 1789 | - 1790 | - # TODO: add file compression, save a bunch of space. 1791 | - # https://medium.com/@rahulraghu94/overriding-pythons-timedrotatingfilehandler-to-compress-your-log-files-iot-c766a4ace240 1792 | - file_handler = RotatingFileHandler( 1793 | - "electionguard.log", "a", maxBytes=10_000_000, backupCount=10 1794 | - ) 1795 | - file_handler.setLevel(logging.DEBUG) 1796 | - file_handler.setFormatter(logging.Formatter(FORMAT)) 1797 | - return file_handler 1798 | - 1799 | - def debug(self, message , *args , **kwargs ) : 1800 | + def debug(self, message) : 1801 | """ 1802 | Logs a debug message 1803 | """ 1804 | - self.__logger.debug(self.__formatted_message(message), *args, **kwargs) 1805 | + self.__logger.debug(self.__formatted_message(message)) 1806 | 1807 | - def info(self, message , *args , **kwargs ) : 1808 | + def info(self, message) : 1809 | """ 1810 | Logs a info message 1811 | """ 1812 | - self.__logger.info(self.__formatted_message(message), *args, **kwargs) 1813 | + self.__logger.info(self.__formatted_message(message)) 1814 | 1815 | - def warn(self, message , *args , **kwargs ) : 1816 | + def warn(self, message) : 1817 | """ 1818 | Logs a warning message 1819 | """ 1820 | - self.__logger.warning(self.__formatted_message(message), *args, **kwargs) 1821 | + self.__logger.warning(self.__formatted_message(message)) 1822 | 1823 | - def error(self, message , *args , **kwargs ) : 1824 | + def error(self, message) : 1825 | """ 1826 | Logs a error message 1827 | """ 1828 | - self.__logger.error(self.__formatted_message(message), *args, **kwargs) 1829 | + self.__logger.error(self.__formatted_message(message)) 1830 | 1831 | - def critical(self, message , *args , **kwargs ) : 1832 | + def critical(self, message) : 1833 | """ 1834 | Logs a critical message 1835 | """ 1836 | - self.__logger.critical(self.__formatted_message(message), *args, **kwargs) 1837 | + self.__logger.critical(self.__formatted_message(message)) 1838 | 1839 | 1840 | LOG = ElectionGuardLog() 1841 | @@ -108,32 +60,32 @@ 1842 | """ 1843 | Logs a debug message to the console and the file log. 1844 | """ 1845 | - LOG.debug(msg, *args, **kwargs) 1846 | + LOG.debug(msg) 1847 | 1848 | 1849 | def log_info(msg , *args , **kwargs ) : 1850 | """ 1851 | Logs an information message to the console and the file log. 1852 | """ 1853 | - LOG.info(msg, *args, **kwargs) 1854 | + LOG.info(msg) 1855 | 1856 | 1857 | def log_warning(msg , *args , **kwargs ) : 1858 | """ 1859 | Logs a warning message to the console and the file log. 1860 | """ 1861 | - LOG.warn(msg, *args, **kwargs) 1862 | + LOG.warn(msg) 1863 | 1864 | 1865 | def log_error(msg , *args , **kwargs ) : 1866 | """ 1867 | Logs an error message to the console and the file log. 1868 | """ 1869 | - LOG.error(msg, *args, **kwargs) 1870 | + LOG.error(msg) 1871 | 1872 | 1873 | def log_critical(msg , *args , **kwargs ) : 1874 | """ 1875 | Logs a critical message to the console and the file log. 1876 | """ 1877 | - LOG.critical(msg, *args, **kwargs) 1878 | + LOG.critical(msg) 1879 | diff -ruwN orig/electionguard/nonces.py build/electionguard/nonces.py 1880 | --- orig/electionguard/nonces.py 2020-11-03 10:10:47.000000000 +0100 1881 | +++ build/electionguard/nonces.py 2020-10-28 16:03:13.000000000 +0100 1882 | @@ -1,10 +1,8 @@ 1883 | -from typing import Union, Sequence, List, overload 1884 | - 1885 | from electionguard.group import ElementModQ, ElementModPOrQ 1886 | from electionguard.hash import hash_elems 1887 | 1888 | 1889 | -class Nonces(Sequence[ElementModQ]): 1890 | +class Nonces: 1891 | """ 1892 | Creates a sequence of random elements in [0,Q), seeded from an initial element in [0,Q). 1893 | If you start with the same seed, you'll get exactly the same sequence. Optional string 1894 | @@ -21,27 +19,14 @@ 1895 | self.__seed = hash_elems(seed, *headers) 1896 | else: 1897 | self.__seed = seed 1898 | - # https://github.com/python/mypy/issues/4108 1899 | - @overload 1900 | - def __getitem__(self, index ) : 1901 | - pass 1902 | - 1903 | - @overload 1904 | - def __getitem__(self, index ) : 1905 | - pass 1906 | 1907 | - def __getitem__( 1908 | + def get( 1909 | self, index 1910 | ) : 1911 | - if isinstance(index, int): 1912 | return self.get_with_headers(index) 1913 | - else: 1914 | - if isinstance(index.stop, int): 1915 | - # Handling slices is a pain: https://stackoverflow.com/a/42731787 1916 | - indices = range(index.start or 0, index.stop, index.step or 1) 1917 | - return [self[i] for i in indices] 1918 | - else: 1919 | - raise TypeError("Cannot take unbounded slice of Nonces") 1920 | + 1921 | + def get_slice(self, start, stop): 1922 | + return [self.get(i) for i in range(start, stop)] 1923 | 1924 | def __len__(self) : 1925 | raise TypeError("Nonces does not have finite length") 1926 | diff -ruwN orig/electionguard/proof.py build/electionguard/proof.py 1927 | --- orig/electionguard/proof.py 2020-11-03 10:10:47.000000000 +0100 1928 | +++ build/electionguard/proof.py 2020-10-28 15:25:36.000000000 +0100 1929 | @@ -16,10 +16,10 @@ 1930 | class Proof(Serializable): 1931 | """Base class for proofs with name and usage case""" 1932 | 1933 | - name = "Proof" 1934 | + _name = "Proof" 1935 | usage = ProofUsage.Unknown 1936 | 1937 | def __init__(self) : 1938 | object.__setattr__( 1939 | - self, "name", space_between_capitals(self.__class__.__name__) 1940 | + self, "_name", space_between_capitals(self.__class__.__name__) 1941 | ) 1942 | diff -ruwN orig/electionguard/serializable.py build/electionguard/serializable.py 1943 | --- orig/electionguard/serializable.py 2020-11-03 10:10:47.000000000 +0100 1944 | +++ build/electionguard/serializable.py 2020-10-29 12:48:10.000000000 +0100 1945 | @@ -1,29 +1,14 @@ 1946 | from dataclasses import dataclass 1947 | from datetime import datetime 1948 | import re 1949 | -from os import path 1950 | -from typing import Any, cast, Type, TypeVar 1951 | 1952 | from jsons import ( 1953 | - dump, 1954 | - dumps, 1955 | - NoneType, 1956 | - load, 1957 | - loads, 1958 | - JsonsError, 1959 | - set_deserializer, 1960 | - set_serializer, 1961 | - set_validator, 1962 | - suppress_warnings, 1963 | - default_nonetype_deserializer, 1964 | + dump_json, 1965 | + dump_json_object, 1966 | + parse_json, 1967 | + parse_json_object 1968 | ) 1969 | 1970 | -S = TypeVar("S", bound="Serializable") 1971 | -T = TypeVar("T") 1972 | - 1973 | -JSON_FILE_EXTENSION = ".json" 1974 | -WRITE = "w" 1975 | -READ = "r" 1976 | JSON_PARSE_ERROR = '{"error": "Object could not be parsed due to json issue"}' 1977 | # TODO Issue #??: Jsons library incorrectly dumps class method 1978 | KEYS_TO_REMOVE = ["from_json", "from_json_file", "from_json_object"] 1979 | @@ -51,17 +36,6 @@ 1980 | """ 1981 | return write_json_object(self, strip_privates) 1982 | 1983 | - def to_json_file( 1984 | - self, file_name , file_path = "", strip_privates = True 1985 | - ) : 1986 | - """ 1987 | - Serialize an object to a json file 1988 | - :param file_name: File name 1989 | - :param file_path: File path 1990 | - :param strip_privates: Strip private variables 1991 | - """ 1992 | - write_json_file(self, file_name, file_path, strip_privates) 1993 | - 1994 | @classmethod 1995 | def from_json(cls , data ) : 1996 | """ 1997 | @@ -78,35 +52,6 @@ 1998 | """ 1999 | return read_json_object(data, cls) 2000 | 2001 | - @classmethod 2002 | - def from_json_file(cls , file_name , file_path = "") : 2003 | - """ 2004 | - Deserialize the provided file into the specified instance 2005 | - :param file_name: File name 2006 | - :param file_path: File path 2007 | - """ 2008 | - return read_json_file(cls, file_name, file_path) 2009 | - 2010 | - 2011 | -def _remove_key(obj , key_to_remove ) : 2012 | - """ 2013 | - Remove key from object recursively 2014 | - :param obj: Any object 2015 | - :param key_to_remove: key to remove 2016 | - """ 2017 | - if isinstance(obj, dict): 2018 | - for key in list(obj.keys()): 2019 | - if key == key_to_remove: 2020 | - del obj[key] 2021 | - else: 2022 | - _remove_key(obj[key], key_to_remove) 2023 | - elif isinstance(obj, list): 2024 | - for i in reversed(range(len(obj))): 2025 | - if obj[i] == key_to_remove: 2026 | - del obj[i] 2027 | - else: 2028 | - _remove_key(obj[i], key_to_remove) 2029 | - 2030 | 2031 | def write_json(object_to_write , strip_privates = True) : 2032 | """ 2033 | @@ -115,16 +60,7 @@ 2034 | :param strip_privates: strip private variables 2035 | :return: the json string representation of this object 2036 | """ 2037 | - set_serializers() 2038 | - suppress_warnings() 2039 | - try: 2040 | - json_object = write_json_object(object_to_write, strip_privates) 2041 | - json_string = cast( 2042 | - str, dumps(json_object, strip_privates=strip_privates, strip_nulls=True) 2043 | - ) 2044 | - return json_string 2045 | - except JsonsError: 2046 | - return JSON_PARSE_ERROR 2047 | + return dump_json(object_to_write, strip_privates) 2048 | 2049 | 2050 | def write_json_object(object_to_write , strip_privates = True) : 2051 | @@ -134,35 +70,7 @@ 2052 | :param strip_privates: strip private variables 2053 | :return: the json representation of this object 2054 | """ 2055 | - set_serializers() 2056 | - suppress_warnings() 2057 | - try: 2058 | - json_object = dump( 2059 | - object_to_write, strip_privates=strip_privates, strip_nulls=True 2060 | - ) 2061 | - for key in KEYS_TO_REMOVE: 2062 | - _remove_key(json_object, key) 2063 | - return json_object 2064 | - except JsonsError: 2065 | - return JSON_PARSE_ERROR 2066 | - 2067 | - 2068 | -def write_json_file( 2069 | - object_to_write , 2070 | - file_name , 2071 | - file_path = "", 2072 | - strip_privates = True, 2073 | -) : 2074 | - """ 2075 | - Serialize json data string to json file 2076 | - :param object_to_write: object to write to json 2077 | - :param file_name: File name 2078 | - :param file_path: File path 2079 | - :param strip_privates: strip private variables 2080 | - """ 2081 | - json_file_path = path.join(file_path, file_name + JSON_FILE_EXTENSION) 2082 | - with open(json_file_path, WRITE) as json_file: 2083 | - json_file.write(write_json(object_to_write, strip_privates)) 2084 | + return dump_json_object(object_to_write, strip_privates) 2085 | 2086 | 2087 | def read_json(data , class_out ) : 2088 | @@ -172,79 +80,14 @@ 2089 | :param class_out: Object type 2090 | :return: Deserialized object 2091 | """ 2092 | - set_deserializers() 2093 | - return cast(T, loads(data, class_out)) 2094 | + return parse_json(data, class_out) 2095 | 2096 | 2097 | -def read_json_object(data , class_out ) : 2098 | +def read_json_object(data , class_out, endian = 'big') : 2099 | """ 2100 | Deserialize json file to object 2101 | :param data: Json file data 2102 | :param class_out: Object type 2103 | :return: Deserialized object 2104 | """ 2105 | - set_deserializers() 2106 | - return cast(T, load(data, class_out)) 2107 | - 2108 | - 2109 | -def read_json_file(class_out , file_name , file_path = "") : 2110 | - """ 2111 | - Deserialize json file to object 2112 | - :param class_out: Object type 2113 | - :param file_name: File name 2114 | - :param file_path: File path 2115 | - :return: Deserialized object 2116 | - """ 2117 | - set_deserializers() 2118 | - json_file_path = path.join(file_path, file_name + JSON_FILE_EXTENSION) 2119 | - with open(json_file_path, READ) as json_file: 2120 | - data = json_file.read() 2121 | - target = read_json(data, class_out) 2122 | - return target 2123 | - 2124 | - 2125 | -def set_serializers() : 2126 | - """Set serializers for jsons to use to cast specific classes""" 2127 | - 2128 | - # Local import to minimize jsons usage across files 2129 | - from .group import ElementModP, ElementModQ 2130 | - 2131 | - set_serializer(lambda p, **_: str(p), ElementModP) 2132 | - set_serializer(lambda q, **_: str(q), ElementModQ) 2133 | - set_serializer(lambda dt, **_: dt.isoformat(), datetime) 2134 | - 2135 | - 2136 | -def set_deserializers() : 2137 | - """Set deserializers and validators for json to use to cast specific classes""" 2138 | - 2139 | - # Local import to minimize jsons usage across files 2140 | - from .group import ElementModP, ElementModQ, int_to_p_unchecked, int_to_q_unchecked 2141 | - 2142 | - set_deserializer( 2143 | - lambda p_as_int, cls, **_: int_to_p_unchecked(p_as_int), ElementModP 2144 | - ) 2145 | - set_validator(lambda p: p.is_in_bounds(), ElementModP) 2146 | - 2147 | - set_deserializer( 2148 | - lambda q_as_int, cls, **_: int_to_q_unchecked(q_as_int), ElementModQ 2149 | - ) 2150 | - set_validator(lambda q: q.is_in_bounds(), ElementModQ) 2151 | - 2152 | - set_deserializer( 2153 | - lambda none, cls, **_: None 2154 | - if none == "None" 2155 | - else default_nonetype_deserializer(none), 2156 | - NoneType, 2157 | - ) 2158 | - 2159 | - set_deserializer(lambda dt, cls, **_: _deserialize_datetime(dt), datetime) 2160 | - 2161 | - 2162 | -def _deserialize_datetime(value ) : 2163 | - """ 2164 | - The `fromisoformat` function doesn't recognize the Z (Zulu) suffix 2165 | - to indicate UTC. For compatibility with more external clients, we 2166 | - should allow it. 2167 | - """ 2168 | - tz_corrected = re.sub("Z$", "+00:00", value) 2169 | - return datetime.fromisoformat(tz_corrected) 2170 | + return parse_json_object(data, class_out, endian) 2171 | diff -ruwN orig/electionguard/singleton.py build/electionguard/singleton.py 2172 | --- orig/electionguard/singleton.py 2020-11-03 10:10:47.000000000 +0100 2173 | +++ build/electionguard/singleton.py 2020-10-28 15:25:36.000000000 +0100 2174 | @@ -1,4 +1,3 @@ 2175 | -from typing import Any 2176 | 2177 | 2178 | class Singleton: 2179 | diff -ruwN orig/electionguard/tracker.py build/electionguard/tracker.py 2180 | --- orig/electionguard/tracker.py 2020-11-03 10:10:47.000000000 +0100 2181 | +++ build/electionguard/tracker.py 2020-10-28 15:25:36.000000000 +0100 2182 | @@ -1,4 +1,3 @@ 2183 | -from typing import List, Optional 2184 | from electionguard.hash import hash_elems 2185 | from electionguard.group import ElementModQ 2186 | from electionguard.words import get_word 2187 | diff -ruwN orig/electionguard/utils.py build/electionguard/utils.py 2188 | --- orig/electionguard/utils.py 2020-11-03 10:10:47.000000000 +0100 2189 | +++ build/electionguard/utils.py 2020-10-28 15:25:36.000000000 +0100 2190 | @@ -1,10 +1,5 @@ 2191 | from datetime import datetime, timezone 2192 | -from os import mkdir, path 2193 | from re import sub 2194 | -from typing import Callable, Optional, TypeVar 2195 | - 2196 | -T = TypeVar("T") 2197 | -U = TypeVar("U") 2198 | 2199 | 2200 | def get_optional(optional ) : 2201 | @@ -74,8 +69,9 @@ 2202 | 2203 | ticks = ( 2204 | date_time.timestamp() 2205 | - if date_time.tzinfo 2206 | - else date_time.astimezone(timezone.utc).timestamp() 2207 | + # TO-DO: timezone check needed 2208 | + # if date_time.tzinfo 2209 | + # else date_time.astimezone(timezone.utc).timestamp() 2210 | ) 2211 | return int(ticks) 2212 | 2213 | diff -ruwN orig/electionguard/words.py build/electionguard/words.py 2214 | --- orig/electionguard/words.py 2020-11-03 10:10:47.000000000 +0100 2215 | +++ build/electionguard/words.py 2020-10-28 15:25:36.000000000 +0100 2216 | @@ -1,5 +1,3 @@ 2217 | -from typing import Optional 2218 | - 2219 | MIN_INDEX = 0 2220 | MAX_INDEX = 4095 2221 | 2222 | diff -ruwN orig/jsons.js build/jsons.js 2223 | --- orig/jsons.js 2020-11-03 10:10:47.000000000 +0100 2224 | +++ build/jsons.js 2020-10-30 10:27:36.000000000 +0100 2225 | @@ -10,7 +10,7 @@ 2226 | return str.replace(/-/g, '+').replace(/_/g, '/'); 2227 | } 2228 | 2229 | -function b64ToBn(b64) { 2230 | +function b64ToBn(b64, endian = 'big') { 2231 | var bin = atob(b64); 2232 | var hex = []; 2233 | 2234 | @@ -19,9 +19,11 @@ 2235 | if (h.length % 2) { h = '0' + h; } 2236 | hex.push(h); 2237 | }); 2238 | - 2239 | + if (endian == 'big') { 2240 | return BigInt('0x' + hex.join('')); 2241 | } 2242 | + return BigInt('0x' + hex.reverse().join('')); 2243 | +} 2244 | 2245 | 2246 | export var dump_json = function(instance, strip_privates = true) { 2247 | @@ -38,11 +40,15 @@ 2248 | return parse_json_object(JSON.parse(data), class_out); 2249 | } 2250 | 2251 | -export var parse_json_object = function(data, class_out) { 2252 | +export var parse_json_object = function(data, class_out, endian = 'big') { 2253 | if (class_out === 'Optional') 2254 | return data || null; 2255 | 2256 | if (Array.isArray(class_out)) { 2257 | + if (!data) { 2258 | + return []; 2259 | + } 2260 | + 2261 | switch(class_out[0].__name__) { 2262 | case 'str': 2263 | case 'int': 2264 | @@ -50,7 +56,7 @@ 2265 | return data; 2266 | default: 2267 | return data.map(function(currentValue) { 2268 | - return parse_json_object(currentValue, class_out[0]); 2269 | + return parse_json_object(currentValue, class_out[0], endian); 2270 | }); 2271 | } 2272 | } 2273 | @@ -61,7 +67,7 @@ 2274 | 2275 | case 'ElementModP': 2276 | case 'ElementModQ': 2277 | - return new class_out(new bigInt(b64ToBn(urlBase64ToBase64(data)))); 2278 | + return new class_out(new bigInt(b64ToBn(urlBase64ToBase64(data), endian))); 2279 | 2280 | case 'str': 2281 | case 'int': 2282 | diff -ruwN orig/tests/test_voter.py build/tests/test_voter.py 2283 | --- orig/tests/test_voter.py 2020-11-03 10:10:48.000000000 +0100 2284 | +++ build/tests/test_voter.py 2020-10-28 15:25:36.000000000 +0100 2285 | @@ -1,9 +1,8 @@ 2286 | -import unittest 2287 | from tests.utils import create_election_test_message, joint_election_key_test_message 2288 | from decidim.electionguard.voter import Voter 2289 | 2290 | 2291 | -class TestVoter(unittest.TestCase): 2292 | +class TestVoter: 2293 | def setUp(self): 2294 | self.voter = Voter('a-voter') 2295 | 2296 | @@ -16,7 +15,7 @@ 2297 | 'question2': ['question2-first-project-selection', 'question2-fourth-project-selection'] 2298 | }) 2299 | 2300 | - print(encrypted_ballot) 2301 | + print(JSON.stringify(encrypted_ballot)) 2302 | 2303 | # TODO: assert ballot keys 2304 | # TODO: assert ballot constests keys 2305 | @@ -25,6 +24,3 @@ 2306 | # TODO: assert number of selections for each contest 2307 | # TODO: assert decryption of the ballot 2308 | 2309 | - 2310 | -if __name__ == '__main__': 2311 | - unittest.main() 2312 | diff -ruwN orig/tests/utils.py build/tests/utils.py 2313 | --- orig/tests/utils.py 2020-11-03 10:10:48.000000000 +0100 2314 | +++ build/tests/utils.py 2020-10-30 09:15:04.000000000 +0100 2315 | @@ -10,7 +10,7 @@ 2316 | {'name': 'clara', 'public_key': '...'} 2317 | ], 2318 | 'description': { 2319 | - 'name': {'text': [{'value': 'Test election', 'language': 'en'}]}, 2320 | + '_name': {'text': [{'value': 'Test election', 'language': 'en'}]}, 2321 | 'start_date': '2021-03-01T08:00:00-05:00', 2322 | 'end_date': '2021-03-01T20:00:00-05:00', 2323 | 'candidates': [ 2324 | @@ -30,8 +30,10 @@ 2325 | '@type': 'ReferendumContest', 2326 | 'object_id': 'question1', 2327 | 'sequence_order': 0, 2328 | - 'vote_variation': 'one_of_m', 2329 | - 'name': 'Question 1', 2330 | + 'vote_variation': { 2331 | + '_name': 'one_of_m', 2332 | + }, 2333 | + '_name': 'Question 1', 2334 | 'number_elected': 1, 2335 | 'minimum_elected': 1, 2336 | 'ballot_title': {'text': [{'value': 'Do you agree?', 'language': 'en'}]}, 2337 | @@ -45,8 +47,10 @@ 2338 | '@type': 'CandidateContest', 2339 | 'object_id': 'question2', 2340 | 'sequence_order': 1, 2341 | - 'vote_variation': 'n_of_m', 2342 | - 'name': 'Question 2', 2343 | + 'vote_variation': { 2344 | + '_name': 'n_of_m', 2345 | + }, 2346 | + '_name': 'Question 2', 2347 | 'number_elected': 2, 2348 | 'minimum_elected': 0, 2349 | 'ballot_title': {'text': [{'value': 'Choose the projects that you like', 'language': 'en'}]}, 2350 | @@ -77,7 +81,7 @@ 2351 | 'election_public_key_proof': { 2352 | 'challenge': '7ER1o3ZlShoCD8Okg5Q6uaUAJUpL1l7SdD4pg6CkAw8=', 2353 | 'commitment': 'pl8zhjfiBLMCm/IgqapjuvmiU0d1EmfOANkf+ld3+R+EqWLZQ0K4l3Ae3Dv3ipP2dgKqNv/wGe61ZoRvhFGkQJnxXo/nFH3OR9bwqZLHpv6ZYeSX3ajD/f+M7nFUCbxDyZvlZYcPLcr2LSgFPXyKDr9tqpazpkQBo44ztZDIe1llv+aaxoiJP/A36IO0bVGKDG2BEU99+qUPMk8rxzaahI3u7yhI8MOC2FJUWTWwTZgnWsV2DnduGaW2cipCG1mbs3OfqHTodl10rOeLngw9CyybfvZdp0YIv1WLGO6S5jadJBYhptUCpamLW0C5pVpCG8uvTaR28kKS9h0NqEu6qa8g5Ow5bKrfxV2vb9SH4Ut1+2+9HqPyjGN16jKMIT79ZCu1iEEN2+RUw9K0rdRaqhKcOY9imLeHk8L5D0GJpR5MYgluFCmDl/+YDZbzE/m165OPiirqlSpY9gn9jCflDuXx9gR/kqsw28z1ImYo7eBDRPG2tQGgExibhycK4SLxPBL3nvYwtlrvd0EtP7eyA6Wb1RotJ92snSH5J5SleNBNCFEWm6cnlGGJ5el/2/kG/jcDVXLz4V02s6+FTbWXTF6s9mOg9Jp65av/T2umlfWvgT/r72AGyOn4hQtI9aGdyv7xPxyU7iiZa2h7BUpEQ3R5F2CO8NmBpdS0m5VEfrA=', # noqa: E501 2354 | - 'name': 'Schnorr Proof', 2355 | + '_name': 'Schnorr Proof', 2356 | 'public_key': 'UxmYOgII7XfkqtmIhkm2I3W5g83recLynlu+z6qK9/iSyB1hW+H7j7KpKnd+pLAOCnxrKA7Kglxk4x5jQdXfYRhiTkL9TzIhejVoXpFFEqH8I1+8mPCYMPY1GxBhZodbAl3CRSIRqS7IMQfFZ6gtZFjrij5P70SuXYyI+K2igVcQuG1BK8CKFXpzDxiKhbVltIjNk4un8K48lTkNqH+nyJ+GQiBUi+x2/gGxgF6naQO9Z/ZAgTcECf9J7LnPxY/0iFkAyJsQiyDpktPCVfy5aAfUYwbDxatIIoE7ujKXyxrbEHPu+VtX3GvLRb53kPp8Wuh3b8eDSagwmh4Fa/yHpcfq1CJSGT4C4K55VbtQ1PcMTQyGJEtgBjpU/3XGyLK1TarnmFlZonKRiuYhHMLKmj4E1F5jaJ12/AwS+jXjTASZwMHgVdflkMCH/rceutcLTKtISNtZdGmP9JcnQ1uOCr1EQe/2sTlz1M8YzvupoPtBUE9ZtgvxGzPKx+tldJQv1cqVsAYTmEL3McUFeK3YLa5819kAOdZ/1tGy3pk3mIRdbiD1GFyiW3MEOwEAKVikIxninC0TwIw0ZiKh5C6YP3mOTN2C8zmWle1uPihO7XLK0f4TKC0pyCMXklkyZa05ZmjWgctJ17RWjLE1boNVTFwFOmy3ASErzDGbJtu9g8A=', # noqa: E501 2357 | 'response': 'puH3SmTJnSn4JL3uDM5exiB0mDa67vibiu3qkA8IbCM=', 2358 | 'usage': 'SecretValue' 2359 | @@ -90,7 +94,7 @@ 2360 | 'election_public_key_proof': { 2361 | 'challenge': 'jTFR44h84a0/XwiKNwdtSDaTb3EYJjOuePX6uaYn2Ds=', 2362 | 'commitment': 'hQXsT3tF9zhu5LcK4IFr8sTZmMXbkUMRXqFzlTUdV8ekcB/0ZwoC6zWcdia5BpSv4og2eIUR1kuGS+rTSa4LkaUd4Zl3pZ2M+PYR4J/chnBkVl70fRQBbCBiOi1/QtVniY+hOFR/x/r/sUYoTTVql2uy/mAsk12DXz5jwzFOsLEXCuagJxk9cCmzU8VTFLkIespup0A3HXFkdIihhUoXI24itIPmknn8umeJcgdSZkAc0uUzn/PFeg2Oul7any6DoLVA5nqpisp04w0NVSCxGEdNpS5fNKxpjm1HJr3xzqyySm21o2W8gokThV1y+NZsxGyRekjeDN5e5LeK2gTkLXyhtpFbEH6nGYYxHGYZrpt1ynamK41j8xp/FkibkGqzdBbkVuaLL+FcGy0PO6fGH2GyJEgPdOrAk3Jby1zaAz9UixLTwX31vCXeYVVStCwuHjT6uWKLDFNjXAUDRepg+TvejGUWKAuVF7QvH7hXoy2yuQT7abf7Z07TE0ZBTte+7UMUp/4rd4V6pWv/TiryLpkV3/8gUgNghfX5kP4jVxmXB0SHwdyeJ8rOjRt6KNgkK+SBds8GvB4ndj3QI7V9MXSH127yfNgwGcI74tekWIWjV/Z9mZZroZgbWbirINHV/2R4jvNihoEJOUQyqR1GC+x39Ew8jfjb1WgtVGJcZ4U=', # noqa: E501 2363 | - 'name': 'Schnorr Proof', 2364 | + '_name': 'Schnorr Proof', 2365 | 'public_key': '7+o8hAuyqbgCBixFFyIxUim4sGgjsVhQVP0o10ohgBN5++f2ohuBvSbBb9VzKx4vmRzd7sfCxwpAeo+z/QQVmDHWLrcp17y/kYbipB3cE3ewsneojyh+qEqY5H9/QiRZkC2X3XVqPVxJv5aVS3Um4vFsfCgS6uWc6HxayWu0TOgQu5FNH4hQxQH7QI68u4uthuvyUfH0t7rqXgOoBbXpm3vPrJBnLvCaxL3FBUvQH27nW5prQoRFCjzUZgEJJtUbykIPWCI/VMT/Ui8jwx3EA8bsVwUti3Mdptlv9MilOlPKo7ChaNg/vzVqsmX2xKXGXhBijgG1iey+5FnIZPQBs/fCkHc9h13pVaEJemJDhZIhiYQ2KxDmf2bWk/ZR5LKX4kFpyY+MI3KSkK2HGrvFiLej2Wxc3jWUK3LmVBCxxB1JrAX94k5fOalVGA74cNte1dp644fR+7h/7K4huDfoVbqh+arkMfg1VDEnPCMjPuox+mwR0DfDvL1niOFNwSNuSdeFqRMdj0Fcx5vG4KXCZTDlPUQIcu9VwdQnXJWqf1izh7Ol2Oi4//Y+3PX8BzMUSyt3L1x2h8kC7sHvIVO+bA9zEjN105X0RJcLkP6WM/27MyrlcfSbWAD2LOBoXySTxiv0wBuDxowo/X2nW7nTcHqu2gaabeZQGMf8/F9VI/o=', # noqa: E501 2366 | 'response': 'AQy+BIv++6QaVlDQNIpd45ZIarh/0weEaGTs8a4yJE8=', 2367 | 'usage': 'SecretValue' 2368 | @@ -103,7 +107,7 @@ 2369 | 'election_public_key_proof': { 2370 | 'challenge': 'MK6CmlcHRCeI2Fq5GZAsonlUiD6VPcZHpXzM8gFWSoU=', 2371 | 'commitment': 'XRoO9PiizLZ2q7UiA2887LSbAuIE/NRLpNibEh6G3BZSJfkTpIGhmOm/1Qiw2wbWqM8ndlPh64GV+D5wjzmUEchva6mwtvVNfDCsP+hm+ELAKDUH9O+tiIbpDn+uzb2Y/O4En+eh6XtFyAjh6a6kDKkuEtxVQ6KLmbDuNjc5c0Bz6vM+97uFqLoIV9nDhDAypCXOsBsrRQhkBcQ+1n1U35astAUpSM3gdRNzQdHvjO1Gh+G9eprr3uWYaMMv8m/sp4xTrLB6vno/Tbcwa1vdkzo7EeIu+xQQYuttqPgvFVhxErEaw7CHMgqVN5LBWcmI5nulvEEWYtS6m6eZ1oqB7BnLTOADIlR1f1UIL2xiCOwKqWGXhD2j4Y/XTujzE0ZGxTp8/jXLmLiH3QgW31q61KPkvPHgCLwZ5Ty9vGmRwe18hAZwPqpYaZAmcMSZX7F43pHC2UNJTp5Kr2YeHxaEi6N3cKSg1zFsL9CWBD7dGbfIAMxUsE/yKH805x/+HWm5tGf2sdxaeOvBhL29gC2g1WJdw2hNiWKDzAjjk6KlLQEgA9UgT3/93WcN5bTTrO8ETYIMAg7U8Pd1Rp6GR7wgKCox5aDZsXkOitvNMOHXWosMWl602oRGXMYN6iFUyWPdkMxpaYx2kFXx0Kc9UDH2D3stlvI0fSNykEdxBpyEdVs=', # noqa: E501 2372 | - 'name': 'Schnorr Proof', 2373 | + '_name': 'Schnorr Proof', 2374 | 'public_key': 'b5B8KfI6Np2k4bLj5RPUGQHxdiwIo4mvPyd/tQCS1PFUrVQ/7oTZRP2tsWg50qAqIdGIjehholjWUgz6LRqSZiMRaWOaD6+GPvqVOA7W0hCBEKxca3M3qbCiRY5jCPIy1/s9cZgZtfATpNIKXpKCr9ekonPMFTbuJiiAOSZFwEIeFAi3xtVBon/NOgmRyqXPu7fUGzWNQlJAmOWMU7Xa9G/K3xlytgcvbicukuyv4aBONYzd2SNEQsO81mKDcI6fXnLbG/44pcbS6F2p2PATfMPLxTQV7cLN8+gG3FXsVt+J8+7Ou74iwoNK0/E/FtGhwqQxiLjsYasTRcXGcQfOBEvP5GpcHn+uGId9sZdCge/HPVQ1CNKA3XUOZSmEfyJirVew7PaTy9ux4YMWNliGTYGFDsIrQDilkEvUNH8P7B+9aTG2S24+gz5IuMGDZ9yEwXx4a1krk15XPsxMFmC0Asv95oueeDP6KZ2zln+9rqPhyDR9LXNYF82cGSi57oWgxdX/J7luwFrLlTbvKHneCxGaERNQZAq8KeuflqQFUrTbr6uNlRI7RfBYGkUbDkYuoX9v/kXH7FtwZMP+CVu9ntCjAQz0YT5SGQX4Qa8jSX89N1FP+caqX4ndqHw58bp0Fa6oG+GX3hlUBJeBQpYhIr8e4kWaxkzqt4P4wacYlSY=', # noqa: E501 2375 | 'response': '+rqEwVYM7oQoPJtI/kuIwnoWRqUoyRANbgv58j5vwc0=', 2376 | 'usage': 'SecretValue' 2377 | --------------------------------------------------------------------------------