mirror of
https://github.com/monero-project/research-lab.git
synced 2025-01-18 16:44:36 +00:00
Merge pull request #16 from b-g-goodell/master
RuffCT and reorganization
This commit is contained in:
commit
488a90888a
68 changed files with 14314 additions and 13 deletions
Binary file not shown.
|
@ -0,0 +1,208 @@
|
|||
@article{nakamoto2008bitcoin,
|
||||
title={{B}itcoin: {A} peer-to-peer electronic cash system},
|
||||
author={Nakamoto, Satoshi}
|
||||
}
|
||||
|
||||
@techreport{grenander1981abstract,
|
||||
title={Abstract {I}nference},
|
||||
author={Grenander, Ulf and Ulf, Grenander},
|
||||
year={1981}
|
||||
}
|
||||
|
||||
@article{massey1996estimating,
|
||||
title={{E}stimating the parameters of a nonhomogeneous {P}oisson process with linear rate},
|
||||
author={Massey, William A and Parker, Geraldine A and Whitt, Ward},
|
||||
journal={Telecommunication Systems},
|
||||
volume={5},
|
||||
number={2},
|
||||
pages={361--388},
|
||||
year={1996},
|
||||
publisher={Springer}
|
||||
}
|
||||
|
||||
@inproceedings{decker2013information,
|
||||
title={{I}nformation propagation in the {B}itcoin network},
|
||||
author={Decker, Christian and Wattenhofer, Roger},
|
||||
booktitle={Peer-to-Peer Computing (P2P), 2013 IEEE Thirteenth International Conference on},
|
||||
pages={1--10},
|
||||
year={2013},
|
||||
organization={IEEE}
|
||||
}
|
||||
|
||||
@article{sompolinsky2013accelerating,
|
||||
title={Accelerating {B}itcoin's Transaction Processing. Fast Money Grows on Trees, Not Chains.},
|
||||
author={Sompolinsky, Yonatan and Zohar, Aviv},
|
||||
journal={IACR Cryptology ePrint Archive},
|
||||
volume={2013},
|
||||
pages={881},
|
||||
year={2013}
|
||||
}
|
||||
|
||||
@article{macheta2014counterfeiting,
|
||||
title={Counterfeiting via Merkle Tree Exploits within Virtual Currencies Employing the {C}ryptoNote Protocol},
|
||||
author={Macheta, Jan and Noether, Sarang and Noether, Surae and Smooth, Javier},
|
||||
year={2014}
|
||||
}
|
||||
|
||||
@incollection{eyal2014majority,
|
||||
title={Majority is not enough: {B}itcoin mining is vulnerable},
|
||||
author={Eyal, Ittay and Sirer, Emin G{\"u}n},
|
||||
booktitle={Financial Cryptography and Data Security},
|
||||
pages={436--454},
|
||||
year={2014},
|
||||
publisher={Springer}
|
||||
}
|
||||
|
||||
|
||||
@article{kraft2015difficulty,
|
||||
title={Difficulty Control for Blockchain-Based Consensus Systems},
|
||||
author={Kraft, Daniel},
|
||||
year={2015}
|
||||
}
|
||||
|
||||
@book{serfozo2009basics,
|
||||
title={Basics of applied stochastic processes},
|
||||
author={Serfozo, Richard},
|
||||
year={2009},
|
||||
publisher={Springer Science \& Business Media}
|
||||
}
|
||||
|
||||
@article{miller2017empirical,
|
||||
title={An Empirical Analysis of Linkability in the {M}onero Blockchain},
|
||||
author={Miller, Andrew and M{\"o}ser, Malte and Lee, Kevin and Narayanan, Arvind},
|
||||
journal={arXiv preprint arXiv:1704.04299},
|
||||
year={2017}
|
||||
}
|
||||
|
||||
@inproceedings{au2006constant,
|
||||
title={Constant-size {ID}-based linkable and revocable-iff-linked ring signature},
|
||||
author={Au, Man Ho and Liu, Joseph K and Susilo, Willy and Yuen, Tsz Hon},
|
||||
booktitle={International Conference on Cryptology in India},
|
||||
pages={364--378},
|
||||
year={2006},
|
||||
organization={Springer}
|
||||
}
|
||||
|
||||
@inproceedings{au2006event,
|
||||
title={Event-oriented k-times revocable-iff-linked group signatures},
|
||||
author={Au, Man Ho and Susilo, Willy and Yiu, Siu-Ming},
|
||||
booktitle={Australasian Conference on Information Security and Privacy},
|
||||
pages={223--234},
|
||||
year={2006},
|
||||
organization={Springer}
|
||||
}
|
||||
|
||||
@inproceedings{chandran2007ring,
|
||||
title={Ring signatures of sub-linear size without random oracles},
|
||||
author={Chandran, Nishanth and Groth, Jens and Sahai, Amit},
|
||||
booktitle={International Colloquium on Automata, Languages, and Programming},
|
||||
pages={423--434},
|
||||
year={2007},
|
||||
organization={Springer}
|
||||
}
|
||||
|
||||
@article{kumar2017traceability,
|
||||
title={A Traceability Analysis of {M}onero's Blockchain},
|
||||
author={Kumar, Amrit and Fischer, Cl{\'e}ment and Tople, Shruti and Saxena, Prateek},
|
||||
year={2017}
|
||||
}
|
||||
|
||||
@misc{knaccc2017,
|
||||
author = {knaccc},
|
||||
title = {Potential Privacy Leaks in {M}onero and Churning},
|
||||
year = {2017},
|
||||
publisher = {GitHub},
|
||||
journal = {GitHub repository},
|
||||
howpublished = {\url{https://github.com/monero-project/monero/issues/1673#issuecomment-278509986}}
|
||||
}
|
||||
|
||||
@misc{kenshi2017,
|
||||
author = {kenshi84},
|
||||
title = {Monero Subaddresses},
|
||||
year = {2017},
|
||||
publisher = {GitHub},
|
||||
journal = {GitHub repository},
|
||||
howpublished = {\url{https://github.com/monero-project/monero/pull/2056}}
|
||||
}
|
||||
|
||||
@misc{mcElrathBraid,
|
||||
author = {Bob McElrath},
|
||||
title = {Braiding the Blockchain},
|
||||
year = {2017},
|
||||
howpublished = {\url{https://scalingbitcoin.org/hongkong2015/presentations/DAY2/2_breaking_the_chain_1_mcelrath.pdf}}
|
||||
}
|
||||
|
||||
@article{noether2016ring,
|
||||
title={Ring Confidential Transactions},
|
||||
author={Noether, Shen and Mackenzie, Adam and others},
|
||||
journal={Ledger},
|
||||
volume={1},
|
||||
pages={1--18},
|
||||
year={2016}
|
||||
}
|
||||
|
||||
@article{T-1955,
|
||||
doi = {10.2307/166755},
|
||||
title = {Applications of Game Theory in Fighter versus Bomber Combat},
|
||||
author = {T. E. Caywood and C. J. Thomas},
|
||||
journal = {Journal of the Operations Research Society of America},
|
||||
issnp = {0096-3984},
|
||||
issne = {2326-3229},
|
||||
year = {1955},
|
||||
month = {11},
|
||||
volume = {3},
|
||||
issue = {4},
|
||||
page = {402--411},
|
||||
url = {http://gen.lib.rus.ec/scimag/index.php?s=10.2307/166755},
|
||||
}
|
||||
|
||||
@book{strogatz2014nonlinear,
|
||||
title={Nonlinear dynamics and chaos: with applications to physics, biology, chemistry, and engineering},
|
||||
author={Strogatz, Steven H},
|
||||
year={2014},
|
||||
publisher={Westview press}
|
||||
}
|
||||
|
||||
@article{doob1942topics,
|
||||
title={Topics in the theory of {M}arkoff chains},
|
||||
author={Doob, Joseph L},
|
||||
journal={Transactions of the American Mathematical Society},
|
||||
volume={52},
|
||||
number={1},
|
||||
pages={37--64},
|
||||
year={1942},
|
||||
publisher={JSTOR}
|
||||
}
|
||||
|
||||
@article{doob1945markoff,
|
||||
title={{M}arkoff chains--denumerable case},
|
||||
author={Doob, Joseph L},
|
||||
journal={Transactions of the American Mathematical Society},
|
||||
volume={58},
|
||||
number={3},
|
||||
pages={455--473},
|
||||
year={1945},
|
||||
publisher={JSTOR}
|
||||
}
|
||||
|
||||
@article{gillespie1977exact,
|
||||
title={Exact stochastic simulation of coupled chemical reactions},
|
||||
author={Gillespie, Daniel T},
|
||||
journal={The journal of physical chemistry},
|
||||
volume={81},
|
||||
number={25},
|
||||
pages={2340--2361},
|
||||
year={1977},
|
||||
publisher={ACS Publications}
|
||||
}
|
||||
|
||||
@article{gillespie1976general,
|
||||
title={A general method for numerically simulating the stochastic time evolution of coupled chemical reactions},
|
||||
author={Gillespie, Daniel T},
|
||||
journal={Journal of computational physics},
|
||||
volume={22},
|
||||
number={4},
|
||||
pages={403--434},
|
||||
year={1976},
|
||||
publisher={Elsevier}
|
||||
}
|
|
@ -0,0 +1,256 @@
|
|||
\documentclass[12pt,english]{mrl}
|
||||
\usepackage{graphicx}
|
||||
\usepackage{listings}
|
||||
\usepackage{cite}
|
||||
\usepackage{amsthm}
|
||||
|
||||
\usepackage[toc,page]{appendix}
|
||||
|
||||
\renewcommand{\familydefault}{\rmdefault}
|
||||
\usepackage[T1]{fontenc}
|
||||
\usepackage[latin9]{inputenc}
|
||||
\usepackage{color}
|
||||
\usepackage{babel}
|
||||
\usepackage{verbatim}
|
||||
\usepackage{float}
|
||||
\usepackage{url}
|
||||
\usepackage{amsthm}
|
||||
\usepackage{amsmath}
|
||||
\usepackage{amssymb}
|
||||
\usepackage[unicode=true,pdfusetitle, bookmarks=true,bookmarksnumbered=false,bookmarksopen=false, breaklinks=false,pdfborder={0 0 1},backref=false,colorlinks=true]{hyperref}
|
||||
\usepackage{breakurl}
|
||||
\usepackage{todonotes}
|
||||
|
||||
|
||||
\usepackage{amsmath}
|
||||
\usepackage{amsfonts}
|
||||
\usepackage{amssymb,enumerate}
|
||||
\usepackage{amsthm}
|
||||
\usepackage{cite}
|
||||
\usepackage{comment}
|
||||
\usepackage[all]{xy}
|
||||
%\usepackage[notref,notcite]{showkeys}
|
||||
\usepackage{hyperref}
|
||||
\usepackage{todonotes}
|
||||
|
||||
% THEOREM ENVIRONMENTS
|
||||
\newtheorem*{example}{Example}
|
||||
\theoremstyle{definition}
|
||||
\newtheorem{lem}{Lemma}[section]
|
||||
\newtheorem{cor}[lem]{Corollary}
|
||||
\newtheorem{prop}[lem]{Proposition}
|
||||
\newtheorem{thm}[lem]{Theorem}
|
||||
\newtheorem{soln}[]{Solution}
|
||||
\newtheorem{conj}[lem]{Conjecture}
|
||||
\newtheorem{Defn}[lem]{Definition}
|
||||
\newtheorem{Ex}[lem]{Example}
|
||||
\newtheorem{Question}[lem]{Question}
|
||||
\newtheorem{Property}[lem]{Property}
|
||||
\newtheorem{Properties}[lem]{Properties}
|
||||
\newtheorem{Discussion}[lem]{Remark}
|
||||
\newtheorem{Construction}[lem]{Construction}
|
||||
\newtheorem{Notation}[lem]{Notation}
|
||||
\newtheorem{Fact}[lem]{Fact}
|
||||
\newtheorem{Notationdefinition}[lem]{Definition/Notation}
|
||||
\newtheorem{Remarkdefinition}[lem]{Remark/Definition}
|
||||
\newtheorem{rem}[lem]{Remark}
|
||||
\newtheorem{Subprops}{}[lem]
|
||||
\newtheorem{Para}[lem]{}
|
||||
\newtheorem{Exer}[lem]{Exercise}
|
||||
\newtheorem{Exerc}{Exercise}
|
||||
|
||||
\newenvironment{defn}{\begin{Defn}\rm}{\end{Defn}}
|
||||
\newenvironment{ex}{\begin{Ex}\rm}{\end{Ex}}
|
||||
\newenvironment{question}{\begin{Question}\rm}{\end{Question}}
|
||||
\newenvironment{property}{\begin{Property}\rm}{\end{Property}}
|
||||
\newenvironment{properties}{\begin{Properties}\rm}{\end{Properties}}
|
||||
\newenvironment{notation}{\begin{Notation}\rm}{\end{Notation}}
|
||||
\newenvironment{fact}{\begin{Fact}\rm}{\end{Fact}}
|
||||
\newenvironment{notationdefinition}{\begin{Notationdefinition}\rm}{\end{Notationdefinition}}
|
||||
\newenvironment{remarkdefinition}{\begin{Remarkdefinition}\rm}{\end{Remarkdefinition}}
|
||||
\newenvironment{subprops}{\begin{Subprops}\rm}{\end{Subprops}}
|
||||
\newenvironment{para}{\begin{Para}\rm}{\end{Para}}
|
||||
\newenvironment{disc}{\begin{Discussion}\rm}{\end{Discussion}}
|
||||
\newenvironment{construction}{\begin{Construction}\rm}{\end{Construction}}
|
||||
\newenvironment{exer}{\begin{Exer}\rm}{\end{Exer}}
|
||||
\newenvironment{exerc}{\begin{Exerc}\rm}{\end{Exerc}}
|
||||
|
||||
\newtheorem{intthm}{Theorem}
|
||||
\renewcommand{\theintthm}{\Alph{intthm}}
|
||||
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% LyX specific LaTeX commands.
|
||||
\floatstyle{ruled}
|
||||
\newfloat{algorithm}{tbp}{loa}
|
||||
\providecommand{\algorithmname}{Algorithm}
|
||||
\floatname{algorithm}{\protect\algorithmname}
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Textclass specific LaTeX commands.
|
||||
\numberwithin{equation}{section}
|
||||
\numberwithin{figure}{section}
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% User specified LaTeX commands.
|
||||
\usepackage{algpseudocode}
|
||||
|
||||
\usepackage{subcaption}
|
||||
|
||||
\numberwithin{equation}{section}
|
||||
|
||||
|
||||
\makeatletter
|
||||
|
||||
|
||||
\makeatletter
|
||||
|
||||
\newcommand{\h}{\mathcal{H}}
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% LyX specific LaTeX commands.
|
||||
\floatstyle{ruled}
|
||||
\newfloat{algorithm}{tbp}{loa}
|
||||
\providecommand{\algorithmname}{Algorithm}
|
||||
\floatname{algorithm}{\protect\algorithmname}
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Textclass specific LaTeX commands.
|
||||
\numberwithin{equation}{section}
|
||||
\numberwithin{figure}{section}
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% User specified LaTeX commands.
|
||||
\usepackage{algpseudocode}
|
||||
|
||||
\makeatother
|
||||
|
||||
\begin{document}
|
||||
\begin{frontmatter}
|
||||
|
||||
\begin{fmbox}
|
||||
{\huge\sffamily Quarterly update} \hfill\setlength{\fboxrule}{0px}\setlength{\fboxsep}{5px}\fbox{\includegraphics[width=2in]{moneroLogo.png}}
|
||||
\dochead{}
|
||||
\date{\today}
|
||||
\author[
|
||||
addressref={mrl},
|
||||
email={bggoode@g.clemson.edu}
|
||||
]{\fnm{Brandon} \snm{Goodell}}
|
||||
|
||||
|
||||
\address[id=mrl]{
|
||||
\orgname{Monero Research Lab}
|
||||
}
|
||||
\end{fmbox}
|
||||
|
||||
%\begin{abstractbox}
|
||||
%This document describes
|
||||
%\begin{abstract}
|
||||
%We outline the various ideas currently under investigation by the Monero Research Lab, provide context for each task, and present some informative sources regarding each task. \end{abstract}
|
||||
%\end{abstractbox}
|
||||
\end{frontmatter}
|
||||
|
||||
This document is intended to inform the community of the work done at MRL in the past quarter, sort of as a response to the first MRL Roadmap, MRL-R001, and sort of as a newsletter to inform everyone about Monero Research Lab. We try to address the partial MRL ``to-do'' list of research items from the first MRL Research Bulletin, MRL-R001, and document the work that has been done both directly and indirectly toward those ends. We document which items on those lists are being de-prioritized on the next MRL Roadmap, and we introduce a few new items that came up over the past few months that will make it onto the next MRL Roadmap (which we expect to put out in the next two to three weeks).
|
||||
|
||||
The Monero Research Lab wishes to state emphatically that our concern is to report our findings on Monero, which is an open source project, as honestly and transparently as possible, subject to the restriction that we do not compromise the safety or security of the funds of our users by doing so. Our goal is not to persuade, re-assure, or enrich speculators or investors; our goal is to assist the Monero community and the Monero Core Team in the design of a robust and strong cryptocurrency with an emphasis on user privacy. Consequently, all findings will be responsibly disclosed to the Monero community. Responsible disclosure may involve maintaining secrecy regarding security flaws for a period of time before disclosure to the public, which provides the development team time to correct known issues and protect our users. This also provides time to discreetly contact the developers of other cryptocurrencies so they, also, may protect their users.
|
||||
|
||||
Now that is out of the way, here is what we did with our summer:
|
||||
|
||||
|
||||
\section{RingCT Security Proofs}
|
||||
|
||||
We have combined this topic with the threshold signature topic (see next item). In \cite{noether2016ring}, there are certain flaws in the proofs of security for MLSAG signatures that need correction, but in the construction of the threshold signature security proofs, we must first establish the security of the MLSAG signatures and then generalize to the threshold setting. Due to this, the corrected RingCT proofs are now part-and-parcel with the threshold multisignature paper.
|
||||
|
||||
|
||||
\section{Threshold multisignatures}
|
||||
|
||||
We are fleshing out an implementation proposed by former contributor \texttt{shen} of $t$-of-$n$ threshold MLSAG multisignatures in Monero. The details of this implementation have been available to the community for months, and vetting those implementations and developing security proofs has been one of the more pressing areas of work for \texttt{surae}, especially in the past six weeks. We currently have a partial draft of MRL Research Bulletin MRL-0006 detailing the implementation in preparation (see the MRL github for a current copy). However, this is only partial because completing this document requires novel security models against insider attacks, where an adversary may corrupt a subthreshold number of private keys. Novel security models require novel security proofs, and so we are ``in the weeds'' on that right now. We hoped this would be accomplished before the end of August but the novelty of the security models have taken MRL a little by surprise. A delightful, surprise, but a surprise nonetheless: the novelty of these results may lead to a peer-reviewed publication on behalf of MRL!
|
||||
|
||||
|
||||
On a related note, \texttt{surae} began in June approaching the threshold multisignature scheme as a mere problem of \textit{computing private keys jointly}, and this was a complete misunderstanding of the problem at hand, which is to \textit{compute signatures without directly computing the private key.} Consequently, our work on this topic was initially unified with our work on the sub-address scheme described by \texttt{kenshi84} and \texttt{knaccc}, by considering these both as problems involving revamping Monero's addressing schemes. This is also one of the reasons that MRL-R001 failed to elaborate upon the sub-address scheme. Once this mis-understanding was clarified to us (after several very educational and helpful conversations with \texttt{luigi} and \texttt{kenshi84}), MRL is once again approaching these two topics separately. We anticipate an MRL Research Bulletin on the sub-address scheme very soon after MRL-0006 describing threshold signatures is released; security proofs for the sub-address scheme are (ostensibly) remarkably easier than in the threshold case.
|
||||
|
||||
\section{Recent criticisms}
|
||||
|
||||
Critics have claimed the Monero blockchain is traceable, as in \cite{miller2017empirical} and \cite{kumar2017traceability}; these papers make use of the \emph{distributional problem} mentioned in the first roadmap together with a few other routes of analysis. Some of the concerns and claims made in these papers are irrelevant because they only apply to pre-RingCT Monero outputs. Some of the concerns are relevant and related to the so-called EABE attack (see next item).
|
||||
|
||||
|
||||
|
||||
\section{Churning, EABE Attack, Large Rings}
|
||||
|
||||
Detailed by \texttt{knaccc} in \cite{knaccc2017}, the EABE (Eve-Alice-Bob-Eve) attack is described. A merchant, Bob, and his customer, Alice, use an exchange, Eve, to convert cryptocurrency to fiat and back again. If Eve sends some moneroj to Alice, who uses it to purchase items from the merchant Bob several times, and if Bob immediately converts all cryptocurrency to fiat after each transaction to limit his exposure to the volatility of cryptocurrency-to-fiat exchange rates, then Bob unintentionally provides information Eve needs to determine the purchasing habits of Alice. This problem is exacerbated if Eve is a know-your-customer exchange. Urgency on this problem is higher than our original estimation: most merchants who accept cryptocurrency enact this behavior, most users do not churn to avoid this problem, and moreover churning transactions can leave a statistical signal (in the sense of the Miller and Kumar criticisms) that is quite undesirable.
|
||||
|
||||
This work dovetailed nicely with our road map item \textbf{hardness of blockchain analysis}. In the study of this (as well as the Miller and Kumar criticisms), \texttt{surae} established three separate probabilistic models of transaction output ownership in a ring signature setting in analyzing this problem. None of these models will see publication soon, however, because each one, a refinement of the previous, is insufficient to describe the problem at hand. We do, however, anticipate some explanatory details to be made public over the coming months. MRL has been reluctant to provide more details, as we stated very clearly in our first MRL Roadmap, \emph{we will not comment thoroughly on these criticisms until our review is complete for security reasons.} We will take as much time as necessary for this, and we recognize that issues such as this one are urgent.
|
||||
|
||||
In the current CryptoNote framework, an elegant solution would be to simply increase ring sizes dramatically. This seems impractical, however, unless ring signatures can be made small (perhaps sub-linearly sized with respect to the number of ring members) which leads us to the next item.
|
||||
|
||||
\section{Signature Size and RingCT}
|
||||
|
||||
Blockchain bloat can be mitigated with efficient signatures. MRL was made aware of research by two separate international teams of researchers (see Section \ref{sec:academ}) making progress on compact Ring Confidental Transactions. One scheme (put forth by Sun, Au, Liu, and Yuen) is very efficient and fast but requires a trusted set-up. Another scheme (put forth by Ruffing, Thyagarajan, Ronge, and Schr{\"o}der, or RTRS) does not use a trusted set-up, but experiences a trade-off since the computation and verification times are quite beastly. An RTRS RingCT may contain thousands of ring members and take up the space of a classic RingCT signature with only a few dozen ring members, but the RTRS RingCT could take hours, days, or more to compute. We believe verification time may be optimized to an extent, but it is also quite slow. Currently, \texttt{knaccc} is working with \texttt{surae} and \texttt{sarang} on a Java prototype of the implementation for testing purposes, and discussions with \texttt{smooth}, \texttt{moneromooo}, and \texttt{luigi} on the practicalities of implementation are constant. MRL anticipates that the scheme may be made sufficiently stream-lined to include in Monero for a moderate increase ring size that was previously unreasonable, but not the epic increase initially hoped for.
|
||||
|
||||
|
||||
\section{Zero-knowledge Lit Review}
|
||||
|
||||
We began communication with Jeffrey Quesnelle, a computer science graduate student at the University of Michigan at Dearborn, at the start of this quarter. Jeffrey wrote an extremely helpful and detailed literature review on zero-knowledge schemes with an eye toward ZK-SNARKS. We listed this zero-knowledge literature review first in MRL-R001 because it was rather low-hanging fruit... but it also did not present a high priority compared to practical implementation issues (threshold signatures, sub-addresses) or security issues (the EABE attack, see below). Our original date for pushing this out was the end of August 2017, which has come and gone. To be clear, this work has not come to a stop, it is merely delayed; now that \texttt{sarang} has joined MRL, \texttt{surae} has more time to put into finishing this project. MRL anticipates movement on this document before the end of September (in fact, the first week of September).
|
||||
|
||||
|
||||
\section{Future-proofing Monero}
|
||||
|
||||
Unlike the above topics, this is actually a constant ``in-the-background'' thing to keep in mind. For example, when we use Pedersen commitments, we have certain hiding and binding properties, but when we use El Gamal commitments, which are similar, these properties change and the commitments are no longer sufficiently hiding against adversaries with quantum computing. Making decisions such as these throughout algorithm design is a constant issue to be considered. Consequently, this item will be removed from future MRL road maps, as it is more of a design philosophy.
|
||||
|
||||
\section{New stuff}
|
||||
|
||||
We have put effort into projects not initially on the MRL Roadmap either due to merit of those projects or urgency. Something related to these items will each make it onto the next MRL Roadmap.
|
||||
|
||||
\begin{enumerate}[i.]
|
||||
\item \textbf{Viewkey solutions}. Since the CryptoNote framework is not \emph{unlinkable} in the sense of the original CryptoNote whitepaper, an adversary can infer much information about whether a certain address has received transactions without knowing the associated viewkey (as in the EABE scenario). Moreover, viewkeys lack functionality. For example, users may desire revocable viewkeys, or viewkeys only valid for certain periods of time, or may desire viewkeys that grant visibility to outgoing transactions (which should also be revocable). Discussions on viewkey solutions have begun between contributors \texttt{endogenic}, \texttt{knaccc}, \texttt{moneromooo}, \texttt{surae}, and \texttt{fluffypony}.
|
||||
|
||||
\item \textbf{Zidechains}. Even with very large ring sizes, since the CryptoNote framework is not zero-knowledge, information is leaked with each transaction by definition. One method proposed by \texttt{fluffypony} to mitigate this is to construct a zero-knowledge sidechain to peg to the Monero blockchain which we are tentatively calling \textit{zidechains}.
|
||||
|
||||
\item \textbf{Blacklisting provably spent outputs}. Wallet software should avoid including provably-spent outputs in ring signatures if possible, because doing so reduces the relative signer ambiguity of the signature, degrades Monero's claims toward untraceability, and degrades the fungibility of all other Monero outputs. Recently, \texttt{fluffypony} had a conversation with \texttt{gmaxwell} on maintaining curated blacklists of provably spent outputs, and \texttt{surae} has begun work on algorithms for finding provably-spent transaction outputs.
|
||||
\end{enumerate}
|
||||
|
||||
|
||||
\section{Dead Items}
|
||||
|
||||
Recall that the items deeper on the MRL Roadmap were items of lower priority. We did not have an opportunity to make progress on the following issues, all of which are very long-term, in terms of priority. These items are worthwhile side hustles for future research, but do not have a lot of immediate pay-off.
|
||||
|
||||
\begin{enumerate}[i.]
|
||||
\item \textbf{Testing Blockchain Dynamics with Population-driven Modeling.}
|
||||
|
||||
\item \textbf{Blockchain Design}.
|
||||
|
||||
\item \textbf{Traceability, extending RingCT to obscure transaction time.}
|
||||
\end{enumerate}
|
||||
|
||||
\section{Academic Engagement}\label{sec:academ}
|
||||
|
||||
In the past three months, Monero Research Lab has had some great interaction with the broader academic community, briefly mentioned above. We wish to highlight the following, which is big news!
|
||||
|
||||
\begin{enumerate}[(i)]
|
||||
|
||||
\item Shi-Feng Sun at Hong Kong Polytechnic University, Man Ho Au at Shanghai Jiao Tong University, Joseph K Liu at Monash University, and Tsz Hon Yuen at Huawei Technologies wrote ``RingCT 2.0: A Compact Accumulator-Based (Linkable Ring Signature) Protocol for Blockchain Cryptocurrency Monero,'' a paper proposing a much more efficient and speedy implementation of Ring Confidential Transactions. These researchers have been instrumental in ID-based cryptography and ring signatures, so their contribution directly to Monero, literally mentioning us in their paper title was surprising, exciting, and a huge honor!
|
||||
|
||||
\item Nearly at the same time, Tim Ruffing at Saarlang University together with Sri Aravinda Thyagarajan, Viktoria Ronge, and Dominique Schr{\"o}der at Friedrich-Alexander-Universit{\"a}t contacted us directly with a separate Ring Confidental Transaction scheme, with a very different Ring Confidential Transaction scheme (see below). We have had a few conversations with him about implementation choices; thanks to hard work by \texttt{knaccc} and \texttt{surae}, we have a nearly-working prototype (to Ruffing's surprise! \texttt{knaccc} works quick).
|
||||
|
||||
\item In implementing the Ruffing scheme, Monero Research Lab has also been in contact with Jonathan Bootle at University College in London about a set-up presented in one of his papers used in the Ruffing scheme; not only are we the first (to his knowledge) to implement his set-ups, but we also identified a small mistake in the notation of his paper that will be corrected.
|
||||
|
||||
\item Thanks to community donations to the Forum Funding System, hired Sarang Noether! He recently graduated with his Ph.D.\ in Computational Physics (and has a strong background in pure and applied mathematics, computer science, and network security) and was a contributor to MRL several years back. We are already enjoying his contribution to our work. We are extremely grateful that the community has welcomed him; he was facing several competitive offers for some very interesting and varied jobs in a few different engineering sectors, so we are lucky to have sniped him away from the traditional economy!
|
||||
|
||||
\end{enumerate}
|
||||
|
||||
|
||||
|
||||
|
||||
\section{Conclusion}
|
||||
|
||||
We request members of the community contribute their opinions on our above work and ideas they would like to see added. Please do not hesitate to contact us. We will make the current threshold MRL Bulletin (which will be MRL-0006) available on the MRL github upon publication of this quarterly update so that contributors and community members can monitor our progress on that front.
|
||||
|
||||
In the next four weeks, we anticipate MRL-R002 roadmap to be put out, the second draft of the zero-knowledge literature review with Jeffrey Quesnelle to be made available to the community, and MRL-0006 to be completed and put out (unless the novelty of the security proofs becomes a rabbit hole of uknown depth). We also anticipate that the RTRS Ring Confidential Transaction scheme to be finished prototyping and beginning testing very soon. Once MRL-0006 is finished, we will begin an MRL Research Bulletin describing the sub-address scheme invented by \texttt{kenshi84} and \texttt{knaccc} to be fleshed out (MRL-0007).
|
||||
|
||||
|
||||
|
||||
|
||||
\section{Special Thanks}
|
||||
|
||||
We would like to issue a special thanks to the members of the Monero community who used the GetMonero.org Forum Funding System to support the Monero Research Lab. Readers may also regard this as a statement of conflict of interest, since our funding is denominated in Monero and provided directly by members of the Monero community by the Forum Funding System.
|
||||
|
||||
\medskip{}
|
||||
|
||||
\bibliographystyle{plain}
|
||||
\bibliography{biblio.bib}
|
||||
|
||||
\end{document}
|
File diff suppressed because it is too large
Load diff
1
publications/research-bulletins/in-prog/Readme.md
Normal file
1
publications/research-bulletins/in-prog/Readme.md
Normal file
|
@ -0,0 +1 @@
|
|||
|
BIN
publications/research-bulletins/in-prog/snark.pdf
Normal file
BIN
publications/research-bulletins/in-prog/snark.pdf
Normal file
Binary file not shown.
|
@ -0,0 +1,256 @@
|
|||
\documentclass[12pt,english]{mrl}
|
||||
\usepackage{graphicx}
|
||||
\usepackage{listings}
|
||||
\usepackage{cite}
|
||||
\usepackage{amsthm}
|
||||
|
||||
\usepackage[toc,page]{appendix}
|
||||
|
||||
\renewcommand{\familydefault}{\rmdefault}
|
||||
\usepackage[T1]{fontenc}
|
||||
\usepackage[latin9]{inputenc}
|
||||
\usepackage{color}
|
||||
\usepackage{babel}
|
||||
\usepackage{verbatim}
|
||||
\usepackage{float}
|
||||
\usepackage{url}
|
||||
\usepackage{amsthm}
|
||||
\usepackage{amsmath}
|
||||
\usepackage{amssymb}
|
||||
\usepackage[unicode=true,pdfusetitle, bookmarks=true,bookmarksnumbered=false,bookmarksopen=false, breaklinks=false,pdfborder={0 0 1},backref=false,colorlinks=true]{hyperref}
|
||||
\usepackage{breakurl}
|
||||
\usepackage{todonotes}
|
||||
|
||||
|
||||
\usepackage{amsmath}
|
||||
\usepackage{amsfonts}
|
||||
\usepackage{amssymb,enumerate}
|
||||
\usepackage{amsthm}
|
||||
\usepackage{cite}
|
||||
\usepackage{comment}
|
||||
\usepackage[all]{xy}
|
||||
%\usepackage[notref,notcite]{showkeys}
|
||||
\usepackage{hyperref}
|
||||
\usepackage{todonotes}
|
||||
|
||||
% THEOREM ENVIRONMENTS
|
||||
\newtheorem*{example}{Example}
|
||||
\theoremstyle{definition}
|
||||
\newtheorem{lem}{Lemma}[section]
|
||||
\newtheorem{cor}[lem]{Corollary}
|
||||
\newtheorem{prop}[lem]{Proposition}
|
||||
\newtheorem{thm}[lem]{Theorem}
|
||||
\newtheorem{soln}[]{Solution}
|
||||
\newtheorem{conj}[lem]{Conjecture}
|
||||
\newtheorem{Defn}[lem]{Definition}
|
||||
\newtheorem{Ex}[lem]{Example}
|
||||
\newtheorem{Question}[lem]{Question}
|
||||
\newtheorem{Property}[lem]{Property}
|
||||
\newtheorem{Properties}[lem]{Properties}
|
||||
\newtheorem{Discussion}[lem]{Remark}
|
||||
\newtheorem{Construction}[lem]{Construction}
|
||||
\newtheorem{Notation}[lem]{Notation}
|
||||
\newtheorem{Fact}[lem]{Fact}
|
||||
\newtheorem{Notationdefinition}[lem]{Definition/Notation}
|
||||
\newtheorem{Remarkdefinition}[lem]{Remark/Definition}
|
||||
\newtheorem{rem}[lem]{Remark}
|
||||
\newtheorem{Subprops}{}[lem]
|
||||
\newtheorem{Para}[lem]{}
|
||||
\newtheorem{Exer}[lem]{Exercise}
|
||||
\newtheorem{Exerc}{Exercise}
|
||||
|
||||
\newenvironment{defn}{\begin{Defn}\rm}{\end{Defn}}
|
||||
\newenvironment{ex}{\begin{Ex}\rm}{\end{Ex}}
|
||||
\newenvironment{question}{\begin{Question}\rm}{\end{Question}}
|
||||
\newenvironment{property}{\begin{Property}\rm}{\end{Property}}
|
||||
\newenvironment{properties}{\begin{Properties}\rm}{\end{Properties}}
|
||||
\newenvironment{notation}{\begin{Notation}\rm}{\end{Notation}}
|
||||
\newenvironment{fact}{\begin{Fact}\rm}{\end{Fact}}
|
||||
\newenvironment{notationdefinition}{\begin{Notationdefinition}\rm}{\end{Notationdefinition}}
|
||||
\newenvironment{remarkdefinition}{\begin{Remarkdefinition}\rm}{\end{Remarkdefinition}}
|
||||
\newenvironment{subprops}{\begin{Subprops}\rm}{\end{Subprops}}
|
||||
\newenvironment{para}{\begin{Para}\rm}{\end{Para}}
|
||||
\newenvironment{disc}{\begin{Discussion}\rm}{\end{Discussion}}
|
||||
\newenvironment{construction}{\begin{Construction}\rm}{\end{Construction}}
|
||||
\newenvironment{exer}{\begin{Exer}\rm}{\end{Exer}}
|
||||
\newenvironment{exerc}{\begin{Exerc}\rm}{\end{Exerc}}
|
||||
|
||||
\newtheorem{intthm}{Theorem}
|
||||
\renewcommand{\theintthm}{\Alph{intthm}}
|
||||
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% LyX specific LaTeX commands.
|
||||
\floatstyle{ruled}
|
||||
\newfloat{algorithm}{tbp}{loa}
|
||||
\providecommand{\algorithmname}{Algorithm}
|
||||
\floatname{algorithm}{\protect\algorithmname}
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Textclass specific LaTeX commands.
|
||||
\numberwithin{equation}{section}
|
||||
\numberwithin{figure}{section}
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% User specified LaTeX commands.
|
||||
\usepackage{algpseudocode}
|
||||
|
||||
\usepackage{subcaption}
|
||||
|
||||
\numberwithin{equation}{section}
|
||||
|
||||
|
||||
\makeatletter
|
||||
|
||||
|
||||
\makeatletter
|
||||
|
||||
\newcommand{\h}{\mathcal{H}}
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% LyX specific LaTeX commands.
|
||||
\floatstyle{ruled}
|
||||
\newfloat{algorithm}{tbp}{loa}
|
||||
\providecommand{\algorithmname}{Algorithm}
|
||||
\floatname{algorithm}{\protect\algorithmname}
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Textclass specific LaTeX commands.
|
||||
\numberwithin{equation}{section}
|
||||
\numberwithin{figure}{section}
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% User specified LaTeX commands.
|
||||
\usepackage{algpseudocode}
|
||||
|
||||
\makeatother
|
||||
|
||||
\begin{document}
|
||||
\begin{frontmatter}
|
||||
|
||||
\begin{fmbox}
|
||||
{\huge\sffamily Quarterly update} \hfill\setlength{\fboxrule}{0px}\setlength{\fboxsep}{5px}\fbox{\includegraphics[width=2in]{moneroLogo.png}}
|
||||
\dochead{}
|
||||
\date{\today}
|
||||
\author[
|
||||
addressref={mrl},
|
||||
email={bggoode@g.clemson.edu}
|
||||
]{\fnm{Brandon} \snm{Goodell}}
|
||||
|
||||
|
||||
\address[id=mrl]{
|
||||
\orgname{Monero Research Lab}
|
||||
}
|
||||
\end{fmbox}
|
||||
|
||||
%\begin{abstractbox}
|
||||
%This document describes
|
||||
%\begin{abstract}
|
||||
%We outline the various ideas currently under investigation by the Monero Research Lab, provide context for each task, and present some informative sources regarding each task. \end{abstract}
|
||||
%\end{abstractbox}
|
||||
\end{frontmatter}
|
||||
|
||||
This document is intended to inform the community of the work done at MRL in the past quarter, sort of as a response to the first MRL Roadmap, MRL-R001, and sort of as a newsletter to inform everyone about Monero Research Lab. We try to address the partial MRL ``to-do'' list of research items from the first MRL Research Bulletin, MRL-R001, and document the work that has been done both directly and indirectly toward those ends. We document which items on those lists are being de-prioritized on the next MRL Roadmap, and we introduce a few new items that came up over the past few months that will make it onto the next MRL Roadmap (which we expect to put out in the next two to three weeks).
|
||||
|
||||
The Monero Research Lab wishes to state emphatically that our concern is to report our findings on Monero, which is an open source project, as honestly and transparently as possible, subject to the restriction that we do not compromise the safety or security of the funds of our users by doing so. Our goal is not to persuade, re-assure, or enrich speculators or investors; our goal is to assist the Monero community and the Monero Core Team in the design of a robust and strong cryptocurrency with an emphasis on user privacy. Consequently, all findings will be responsibly disclosed to the Monero community. Responsible disclosure may involve maintaining secrecy regarding security flaws for a period of time before disclosure to the public, which provides the development team time to correct known issues and protect our users. This also provides time to discreetly contact the developers of other cryptocurrencies so they, also, may protect their users.
|
||||
|
||||
Now that is out of the way, here is what we did with our summer:
|
||||
|
||||
|
||||
\section{RingCT Security Proofs}
|
||||
|
||||
We have combined this topic with the threshold signature topic (see next item). In \cite{noether2016ring}, there are certain flaws in the proofs of security for MLSAG signatures that need correction, but in the construction of the threshold signature security proofs, we must first establish the security of the MLSAG signatures and then generalize to the threshold setting. Due to this, the corrected RingCT proofs are now part-and-parcel with the threshold multisignature paper.
|
||||
|
||||
|
||||
\section{Threshold multisignatures}
|
||||
|
||||
We are fleshing out an implementation proposed by former contributor \texttt{shen} of $t$-of-$n$ threshold MLSAG multisignatures in Monero. The details of this implementation have been available to the community for months, and vetting those implementations and developing security proofs has been one of the more pressing areas of work for \texttt{surae}, especially in the past six weeks. We currently have a partial draft of MRL Research Bulletin MRL-0006 detailing the implementation in preparation (see the MRL github for a current copy). However, this is only partial because completing this document requires novel security models against insider attacks, where an adversary may corrupt a subthreshold number of private keys. Novel security models require novel security proofs, and so we are ``in the weeds'' on that right now. We hoped this would be accomplished before the end of August but the novelty of the security models have taken MRL a little by surprise. A delightful, surprise, but a surprise nonetheless: the novelty of these results may lead to a peer-reviewed publication on behalf of MRL!
|
||||
|
||||
|
||||
On a related note, \texttt{surae} began in June approaching the threshold multisignature scheme as a mere problem of \textit{computing private keys jointly}, and this was a complete misunderstanding of the problem at hand, which is to \textit{compute signatures without directly computing the private key.} Consequently, our work on this topic was initially unified with our work on the sub-address scheme described by \texttt{kenshi84} and \texttt{knaccc}, by considering these both as problems involving revamping Monero's addressing schemes. This is also one of the reasons that MRL-R001 failed to elaborate upon the sub-address scheme. Once this mis-understanding was clarified to us (after several very educational and helpful conversations with \texttt{luigi} and \texttt{kenshi84}), MRL is once again approaching these two topics separately. We anticipate an MRL Research Bulletin on the sub-address scheme very soon after MRL-0006 describing threshold signatures is released; security proofs for the sub-address scheme are (ostensibly) remarkably easier than in the threshold case.
|
||||
|
||||
\section{Recent criticisms}
|
||||
|
||||
Critics have claimed the Monero blockchain is traceable, as in \cite{miller2017empirical} and \cite{kumar2017traceability}; these papers make use of the \emph{distributional problem} mentioned in the first roadmap together with a few other routes of analysis. Some of the concerns and claims made in these papers are irrelevant because they only apply to pre-RingCT Monero outputs. Some of the concerns are relevant and related to the so-called EABE attack (see next item).
|
||||
|
||||
|
||||
|
||||
\section{Churning, EABE Attack, Large Rings}
|
||||
|
||||
Detailed by \texttt{knaccc} in \cite{knaccc2017}, the EABE (Eve-Alice-Bob-Eve) attack is described. A merchant, Bob, and his customer, Alice, use an exchange, Eve, to convert cryptocurrency to fiat and back again. If Eve sends some moneroj to Alice, who uses it to purchase items from the merchant Bob several times, and if Bob immediately converts all cryptocurrency to fiat after each transaction to limit his exposure to the volatility of cryptocurrency-to-fiat exchange rates, then Bob unintentionally provides information Eve needs to determine the purchasing habits of Alice. This problem is exacerbated if Eve is a know-your-customer exchange. Urgency on this problem is higher than our original estimation: most merchants who accept cryptocurrency enact this behavior, most users do not churn to avoid this problem, and moreover churning transactions can leave a statistical signal (in the sense of the Miller and Kumar criticisms) that is quite undesirable.
|
||||
|
||||
This work dovetailed nicely with our road map item \textbf{hardness of blockchain analysis}. In the study of this (as well as the Miller and Kumar criticisms), \texttt{surae} established three separate probabilistic models of transaction output ownership in a ring signature setting in analyzing this problem. None of these models will see publication soon, however, because each one, a refinement of the previous, is insufficient to describe the problem at hand. We do, however, anticipate some explanatory details to be made public over the coming months. MRL has been reluctant to provide more details, as we stated very clearly in our first MRL Roadmap, \emph{we will not comment thoroughly on these criticisms until our review is complete for security reasons.} We will take as much time as necessary for this, and we recognize that issues such as this one are urgent.
|
||||
|
||||
In the current CryptoNote framework, an elegant solution would be to simply increase ring sizes dramatically. This seems impractical, however, unless ring signatures can be made small (perhaps sub-linearly sized with respect to the number of ring members) which leads us to the next item.
|
||||
|
||||
\section{Signature Size and RingCT}
|
||||
|
||||
Blockchain bloat can be mitigated with efficient signatures. MRL was made aware of research by two separate international teams of researchers (see Section \ref{sec:academ}) making progress on compact Ring Confidental Transactions. One scheme (put forth by Sun, Au, Liu, and Yuen) is very efficient and fast but requires a trusted set-up. Another scheme (put forth by Ruffing, Thyagarajan, Ronge, and Schr{\"o}der, or RTRS) does not use a trusted set-up, but experiences a trade-off since the computation and verification times are quite beastly. An RTRS RingCT may contain thousands of ring members and take up the space of a classic RingCT signature with only a few dozen ring members, but the RTRS RingCT could take hours, days, or more to compute. We believe verification time may be optimized to an extent, but it is also quite slow. Currently, \texttt{knaccc} is working with \texttt{surae} and \texttt{sarang} on a Java prototype of the implementation for testing purposes, and discussions with \texttt{smooth}, \texttt{moneromooo}, and \texttt{luigi} on the practicalities of implementation are constant. MRL anticipates that the scheme may be made sufficiently stream-lined to include in Monero for a moderate increase ring size that was previously unreasonable, but not the epic increase initially hoped for.
|
||||
|
||||
|
||||
\section{Zero-knowledge Lit Review}
|
||||
|
||||
We began communication with Jeffrey Quesnelle, a computer science graduate student at the University of Michigan at Dearborn, at the start of this quarter. Jeffrey wrote an extremely helpful and detailed literature review on zero-knowledge schemes with an eye toward ZK-SNARKS. We listed this zero-knowledge literature review first in MRL-R001 because it was rather low-hanging fruit... but it also did not present a high priority compared to practical implementation issues (threshold signatures, sub-addresses) or security issues (the EABE attack, see below). Our original date for pushing this out was the end of August 2017, which has come and gone. To be clear, this work has not come to a stop, it is merely delayed; now that \texttt{sarang} has joined MRL, \texttt{surae} has more time to put into finishing this project. MRL anticipates movement on this document before the end of September (in fact, the first week of September).
|
||||
|
||||
|
||||
\section{Future-proofing Monero}
|
||||
|
||||
Unlike the above topics, this is actually a constant ``in-the-background'' thing to keep in mind. For example, when we use Pedersen commitments, we have certain hiding and binding properties, but when we use El Gamal commitments, which are similar, these properties change and the commitments are no longer sufficiently hiding against adversaries with quantum computing. Making decisions such as these throughout algorithm design is a constant issue to be considered. Consequently, this item will be removed from future MRL road maps, as it is more of a design philosophy.
|
||||
|
||||
\section{New stuff}
|
||||
|
||||
We have put effort into projects not initially on the MRL Roadmap either due to merit of those projects or urgency. Something related to these items will each make it onto the next MRL Roadmap.
|
||||
|
||||
\begin{enumerate}[i.]
|
||||
\item \textbf{Viewkey solutions}. Since the CryptoNote framework is not \emph{unlinkable} in the sense of the original CryptoNote whitepaper, an adversary can infer much information about whether a certain address has received transactions without knowing the associated viewkey (as in the EABE scenario). Moreover, viewkeys lack functionality. For example, users may desire revocable viewkeys, or viewkeys only valid for certain periods of time, or may desire viewkeys that grant visibility to outgoing transactions (which should also be revocable). Discussions on viewkey solutions have begun between contributors \texttt{endogenic}, \texttt{knaccc}, \texttt{moneromooo}, \texttt{surae}, and \texttt{fluffypony}.
|
||||
|
||||
\item \textbf{Zidechains}. Even with very large ring sizes, since the CryptoNote framework is not zero-knowledge, information is leaked with each transaction by definition. One method proposed by \texttt{fluffypony} to mitigate this is to construct a zero-knowledge sidechain to peg to the Monero blockchain which we are tentatively calling \textit{zidechains}.
|
||||
|
||||
\item \textbf{Blacklisting provably spent outputs}. Wallet software should avoid including provably-spent outputs in ring signatures if possible, because doing so reduces the relative signer ambiguity of the signature, degrades Monero's claims toward untraceability, and degrades the fungibility of all other Monero outputs. Recently, \texttt{fluffypony} had a conversation with \texttt{gmaxwell} on maintaining curated blacklists of provably spent outputs, and \texttt{surae} has begun work on algorithms for finding provably-spent transaction outputs.
|
||||
\end{enumerate}
|
||||
|
||||
|
||||
\section{Dead Items}
|
||||
|
||||
Recall that the items deeper on the MRL Roadmap were items of lower priority. We did not have an opportunity to make progress on the following issues, all of which are very long-term, in terms of priority. These items are worthwhile side hustles for future research, but do not have a lot of immediate pay-off.
|
||||
|
||||
\begin{enumerate}[i.]
|
||||
\item \textbf{Testing Blockchain Dynamics with Population-driven Modeling.}
|
||||
|
||||
\item \textbf{Blockchain Design}.
|
||||
|
||||
\item \textbf{Traceability, extending RingCT to obscure transaction time.}
|
||||
\end{enumerate}
|
||||
|
||||
\section{Academic Engagement}\label{sec:academ}
|
||||
|
||||
In the past three months, Monero Research Lab has had some great interaction with the broader academic community, briefly mentioned above. We wish to highlight the following, which is big news!
|
||||
|
||||
\begin{enumerate}[(i)]
|
||||
|
||||
\item Shi-Feng Sun at Hong Kong Polytechnic University, Man Ho Au at Shanghai Jiao Tong University, Joseph K Liu at Monash University, and Tsz Hon Yuen at Huawei Technologies wrote ``RingCT 2.0: A Compact Accumulator-Based (Linkable Ring Signature) Protocol for Blockchain Cryptocurrency Monero,'' a paper proposing a much more efficient and speedy implementation of Ring Confidential Transactions. These researchers have been instrumental in ID-based cryptography and ring signatures, so their contribution directly to Monero, literally mentioning us in their paper title was surprising, exciting, and a huge honor!
|
||||
|
||||
\item Nearly at the same time, Tim Ruffing at Saarlang University together with Sri Aravinda Thyagarajan, Viktoria Ronge, and Dominique Schr{\"o}der at Friedrich-Alexander-Universit{\"a}t contacted us directly with a separate Ring Confidental Transaction scheme, with a very different Ring Confidential Transaction scheme (see below). We have had a few conversations with him about implementation choices; thanks to hard work by \texttt{knaccc} and \texttt{surae}, we have a nearly-working prototype (to Ruffing's surprise! \texttt{knaccc} works quick).
|
||||
|
||||
\item In implementing the Ruffing scheme, Monero Research Lab has also been in contact with Jonathan Bootle at University College in London about a set-up presented in one of his papers used in the Ruffing scheme; not only are we the first (to his knowledge) to implement his set-ups, but we also identified a small mistake in the notation of his paper that will be corrected.
|
||||
|
||||
\item Thanks to community donations to the Forum Funding System, hired Sarang Noether! He recently graduated with his Ph.D.\ in Computational Physics (and has a strong background in pure and applied mathematics, computer science, and network security) and was a contributor to MRL several years back. We are already enjoying his contribution to our work. We are extremely grateful that the community has welcomed him; he was facing several competitive offers for some very interesting and varied jobs in a few different engineering sectors, so we are lucky to have sniped him away from the traditional economy!
|
||||
|
||||
\end{enumerate}
|
||||
|
||||
|
||||
|
||||
|
||||
\section{Conclusion}
|
||||
|
||||
We request members of the community contribute their opinions on our above work and ideas they would like to see added. Please do not hesitate to contact us. We will make the current threshold MRL Bulletin (which will be MRL-0006) available on the MRL github upon publication of this quarterly update so that contributors and community members can monitor our progress on that front.
|
||||
|
||||
In the next four weeks, we anticipate MRL-R002 roadmap to be put out, the second draft of the zero-knowledge literature review with Jeffrey Quesnelle to be made available to the community, and MRL-0006 to be completed and put out (unless the novelty of the security proofs becomes a rabbit hole of uknown depth). We also anticipate that the RTRS Ring Confidential Transaction scheme to be finished prototyping and beginning testing very soon. Once MRL-0006 is finished, we will begin an MRL Research Bulletin describing the sub-address scheme invented by \texttt{kenshi84} and \texttt{knaccc} to be fleshed out (MRL-0007).
|
||||
|
||||
|
||||
|
||||
|
||||
\section{Special Thanks}
|
||||
|
||||
We would like to issue a special thanks to the members of the Monero community who used the GetMonero.org Forum Funding System to support the Monero Research Lab. Readers may also regard this as a statement of conflict of interest, since our funding is denominated in Monero and provided directly by members of the Monero community by the Forum Funding System.
|
||||
|
||||
\medskip{}
|
||||
|
||||
\bibliographystyle{plain}
|
||||
\bibliography{biblio.bib}
|
||||
|
||||
\end{document}
|
Binary file not shown.
Binary file not shown.
|
@ -1,16 +1,16 @@
|
|||
@article{nakamoto2008bitcoin,
|
||||
title={Bitcoin: A peer-to-peer electronic cash system},
|
||||
title={{B}itcoin: {A} peer-to-peer electronic cash system},
|
||||
author={Nakamoto, Satoshi}
|
||||
}
|
||||
|
||||
@techreport{grenander1981abstract,
|
||||
title={Abstract inference},
|
||||
title={Abstract {I}nference},
|
||||
author={Grenander, Ulf and Ulf, Grenander},
|
||||
year={1981}
|
||||
}
|
||||
|
||||
@article{massey1996estimating,
|
||||
title={Estimating the parameters of a nonhomogeneous Poisson process with linear rate},
|
||||
title={{E}stimating the parameters of a nonhomogeneous {P}oisson process with linear rate},
|
||||
author={Massey, William A and Parker, Geraldine A and Whitt, Ward},
|
||||
journal={Telecommunication Systems},
|
||||
volume={5},
|
||||
|
@ -21,7 +21,7 @@
|
|||
}
|
||||
|
||||
@inproceedings{decker2013information,
|
||||
title={Information propagation in the bitcoin network},
|
||||
title={{I}nformation propagation in the {B}itcoin network},
|
||||
author={Decker, Christian and Wattenhofer, Roger},
|
||||
booktitle={Peer-to-Peer Computing (P2P), 2013 IEEE Thirteenth International Conference on},
|
||||
pages={1--10},
|
||||
|
@ -30,7 +30,7 @@
|
|||
}
|
||||
|
||||
@article{sompolinsky2013accelerating,
|
||||
title={Accelerating Bitcoin's Transaction Processing. Fast Money Grows on Trees, Not Chains.},
|
||||
title={Accelerating {B}itcoin's Transaction Processing. Fast Money Grows on Trees, Not Chains.},
|
||||
author={Sompolinsky, Yonatan and Zohar, Aviv},
|
||||
journal={IACR Cryptology ePrint Archive},
|
||||
volume={2013},
|
||||
|
@ -39,13 +39,13 @@
|
|||
}
|
||||
|
||||
@article{macheta2014counterfeiting,
|
||||
title={Counterfeiting via Merkle Tree Exploits within Virtual Currencies Employing the CryptoNote Protocol},
|
||||
title={Counterfeiting via Merkle Tree Exploits within Virtual Currencies Employing the {C}ryptoNote Protocol},
|
||||
author={Macheta, Jan and Noether, Sarang and Noether, Surae and Smooth, Javier},
|
||||
year={2014}
|
||||
}
|
||||
|
||||
@incollection{eyal2014majority,
|
||||
title={Majority is not enough: Bitcoin mining is vulnerable},
|
||||
title={Majority is not enough: {B}itcoin mining is vulnerable},
|
||||
author={Eyal, Ittay and Sirer, Emin G{\"u}n},
|
||||
booktitle={Financial Cryptography and Data Security},
|
||||
pages={436--454},
|
||||
|
@ -68,14 +68,14 @@
|
|||
}
|
||||
|
||||
@article{miller2017empirical,
|
||||
title={An Empirical Analysis of Linkability in the Monero Blockchain},
|
||||
title={An Empirical Analysis of Linkability in the {M}onero Blockchain},
|
||||
author={Miller, Andrew and M{\"o}ser, Malte and Lee, Kevin and Narayanan, Arvind},
|
||||
journal={arXiv preprint arXiv:1704.04299},
|
||||
year={2017}
|
||||
}
|
||||
|
||||
@inproceedings{au2006constant,
|
||||
title={Constant-size ID-based linkable and revocable-iff-linked ring signature},
|
||||
title={Constant-size {ID}-based linkable and revocable-iff-linked ring signature},
|
||||
author={Au, Man Ho and Liu, Joseph K and Susilo, Willy and Yuen, Tsz Hon},
|
||||
booktitle={International Conference on Cryptology in India},
|
||||
pages={364--378},
|
||||
|
@ -102,14 +102,14 @@
|
|||
}
|
||||
|
||||
@article{kumar2017traceability,
|
||||
title={A Traceability Analysis of Monero's Blockchain},
|
||||
title={A Traceability Analysis of {M}onero's Blockchain},
|
||||
author={Kumar, Amrit and Fischer, Cl{\'e}ment and Tople, Shruti and Saxena, Prateek},
|
||||
year={2017}
|
||||
}
|
||||
|
||||
@misc{knaccc2017,
|
||||
author = {knaccc},
|
||||
title = {Potential Privacy Leaks in Monero and Churning},
|
||||
title = {Potential Privacy Leaks in {M}onero and Churning},
|
||||
year = {2017},
|
||||
publisher = {GitHub},
|
||||
journal = {GitHub repository},
|
||||
|
@ -164,7 +164,7 @@ url = {http://gen.lib.rus.ec/scimag/index.php?s=10.2307/166755},
|
|||
}
|
||||
|
||||
@article{doob1942topics,
|
||||
title={Topics in the theory of Markoff chains},
|
||||
title={Topics in the theory of {M}arkoff chains},
|
||||
author={Doob, Joseph L},
|
||||
journal={Transactions of the American Mathematical Society},
|
||||
volume={52},
|
||||
|
@ -175,7 +175,7 @@ url = {http://gen.lib.rus.ec/scimag/index.php?s=10.2307/166755},
|
|||
}
|
||||
|
||||
@article{doob1945markoff,
|
||||
title={Markoff chains--denumerable case},
|
||||
title={{M}arkoff chains--denumerable case},
|
||||
author={Doob, Joseph L},
|
||||
journal={Transactions of the American Mathematical Society},
|
||||
volume={58},
|
||||
|
|
441
publications/txt-standards/Ruffct.txt
Normal file
441
publications/txt-standards/Ruffct.txt
Normal file
|
@ -0,0 +1,441 @@
|
|||
BOOTLE-RUFFING RINGCT SCHEME
|
||||
----------------------------
|
||||
Sublinear-sized ring signatures without trusted
|
||||
set-ups or bilinear pairings. Summarized for
|
||||
Monero Research Lab by B Goodell
|
||||
|
||||
We describe sublinear-sized ring signatures for use in cryptocurrency. This
|
||||
scheme was first sent to MRL by Ruffing and co-authors. These results use
|
||||
tools first described by Bootle, et al in the paper "Short Accountable Ring
|
||||
Signatures Based on DDH," 2015 European Symposium on Research in Computer Sec-
|
||||
urity, use a "vanilla" elliptic curve multisignature scheme first described by
|
||||
Bellare and Neven in "Multi-signatures in the plain public-key model and a gen-\
|
||||
eral forking lemma," 2006 Proceedings of the 13th, ACM conference on Computer and
|
||||
Communications Security.
|
||||
|
||||
This is a living document and will be updated. In section I, the introduction,
|
||||
we introduce the general idea of the scheme together with preliminary stuff.
|
||||
We have a section on homomorphic commitments and encryption, a section on
|
||||
ordinary multi-signatures, and a section on the NIZK proof systems presented
|
||||
by Bootle et al. In section II, we present pseudocode describing Ruffing's
|
||||
scheme. In section III, references.
|
||||
|
||||
I. INTRODUCTION
|
||||
|
||||
The scheme presented by Ruffing roughly works as follows, exploiting homo-
|
||||
morphic commitments. Sender Sally uses L of her txnout keys to send some amount
|
||||
stored in a commitment to receiver Roy. Sally wishes to implicate N-1 other
|
||||
senders, so she constructs an LxN matrix of public keys (L = # key images used,
|
||||
N = ring size) by picking random public keys from the anonymity set; she stores
|
||||
her own pubkeys in some secret column i*. Sally demonstrates that the output
|
||||
amount from her secret column and the input amount are the same without re-
|
||||
vealing that column or the amounts by using a NIZK proof from the Bootle paper
|
||||
to show that at least one commitment in a vector opens to zero. In the constru-
|
||||
ction of this proof, she uses information from her secret keys, which binds the
|
||||
index she can open to zero to the column of pubkeys in the matrix without re-
|
||||
vealing which index. She uses her secret keys to construct a multisignature on
|
||||
the Bootle proof that is verifiable with the key images only. This way, her
|
||||
signature consists of some random information, a Bootle proof, and a multi-
|
||||
signature on the Bootle proof. The multisignature is efficient in construction,
|
||||
verification, and storage. The Bootle proof, for a vector of commitments of
|
||||
length N = n^m, takes O(n*m) (roughly) for construction, verification, and sto-
|
||||
rage. For a ring size N=n^m, for a fixed ring base n (say all ring sizes are
|
||||
powers of n=16), the Ruffing scheme is approximately log(N).
|
||||
|
||||
The scheme consists of four algorithms, KEYGEN, SPEND, and VER, which we des-
|
||||
cribe in the next section. The scheme uses multisignatures and Bootle proofs
|
||||
as subroutines, and hammers on homomorphic commitment very hard, so we describe
|
||||
these in this section as "pre-requisites". The Bootle method is an interactive
|
||||
sigma protocol that uses another interactive sigma protocol as a subroutine. We
|
||||
made these both non-interactive and present the pseudocode in PROVE1, VALID1,
|
||||
PROVE2, VALID2 (the Ruffing scheme uses PROVE2, VALID2, which use PROVE1,
|
||||
VALID1). The Ruffing scheme did not specify a multisignature scheme; we desc-
|
||||
ribe here the pseudocode for the Bellare-Neven multisignature scheme, KEYGEN*,
|
||||
SIG*, VER*.
|
||||
|
||||
a. Commitments and Encryption
|
||||
|
||||
To commit to a scalar x with some random mask r, we use the following options:
|
||||
|
||||
COMp(x; r) := rG + xHp(xG) # Unconditionally hiding Pedersen
|
||||
COMeg(x; r) := (rG + xHp(xG), rG) # Computationally hiding El Gamal
|
||||
|
||||
We may extend these to a vector/array/matrix B=[b[j][i]; j=0...m-1,i=0..n-1]:
|
||||
|
||||
COMp(B; r) := rG + b[0][0]Hp(b[0][0]G) + ... + b[m-1][n-1]Hp(b[m-1][n-1]G)
|
||||
|
||||
and we can similarly lift this to an El Gamal commitment by appending rG. In
|
||||
the first case, we refer to x or the matrix B as the "data under commitment"
|
||||
and the value r as the "mask." Given a Pedersen commitment c, we open c by
|
||||
revealing x and r, where a verifier checks that c == rG + xHp(xG). Given an
|
||||
El Gamal commitment (C1, C2), we open by revealing r and x, a verifier
|
||||
computes C2' = rG, C1' = rG + xHp(xG) from these, and lastly checks that
|
||||
C1 == C1' and C2 == C2'.
|
||||
|
||||
These commitments are additive under the following definition:
|
||||
|
||||
COMp(x; r) + COMp(x'; r') := COMp(x+x'; r+r')
|
||||
COMeg(x; r) + COMeg(x'; r') := COMeg(x+x'; r+r')
|
||||
|
||||
|
||||
b. Ordinary Multi-signature
|
||||
|
||||
The Ruffing scheme utilizes as a subroutine an efficient multisignature scheme,
|
||||
consisting of three algorithms (KEYGEN*, SIG*, VER*). We present a scheme based
|
||||
on the scheme described by Bellare and Neven in 2006 [3] (which is, in turn,
|
||||
based on Schnorr signatures). Our variation of the B&N scheme is that it is
|
||||
executed only by one party holding all the keys, so interaction is unnecessary.
|
||||
We use a group of prime order q. Ruffing's scheme assumes that the Decisional
|
||||
Diffie-Hellman assumption holds, so there is no harm in making this assumption
|
||||
for the ordinary multi-signature scheme. Let G be a commonly known generator of
|
||||
the group. Let Hs be a hash function that produces a scalar in Zq. Let Zq denote
|
||||
the integers modulo q.
|
||||
|
||||
KEYGEN*: Each user selects x at random from Zq. The secret key is x. The
|
||||
public key is X=xG. Output (sk,pk) = (x,X).
|
||||
|
||||
SIG*: Take as input a message M and a list of private keys L = x[0], x[1],
|
||||
..., x[n-1]. Let L' be the associated list of public keys X[0], ..., X[n-1],
|
||||
and assume L' is lexicographically ordered.
|
||||
1) Compute L* = H(L').
|
||||
2) For each i=0,1,...,n-1, select r[i] at random from Zq.
|
||||
3) Compute r=r[0]+r[1]+...+r[n-1] and R=rG.
|
||||
4) For each i=0,1,...,n-1:
|
||||
i) Compute c[i] := Hs(X[i], R, L*, M)
|
||||
ii) Compute s[i] := r[i] + x[i]*c[i]
|
||||
5) Compute s = s[0] + ... + s[n-1].
|
||||
6) Output the signature sigma = (R, s)
|
||||
|
||||
VER*: Take as input a message M, a set of keys L' = X[0], ..., X[n-1], and a
|
||||
signature sigma = (R,s).
|
||||
1) Compute L* = H(L')
|
||||
2) For each i=0,1,...,n-1, compute c[i] = Hs(X[i], R, L*, M)
|
||||
3) Accept if and only if sG = R + c[0]*X[0] + ... + c[n-1]*X[n-1]
|
||||
|
||||
c. NIZK Proofs
|
||||
====|====|====|====|====|====|====|====|====|====|====|====|====|====|====|====
|
||||
Lastly, the scheme also utilizes the following algorithms, (PROVE1, VALID1),
|
||||
and (PROVE2, VALID2), which are NIZK prove-and-verify algorithm pairs. These
|
||||
use the Fiat-Shamir transformation to make the zero-knowledge sigma protocols
|
||||
presented by Bootle non-interactive under the random oracle model. The second
|
||||
algorithm requires the usage of a helper algorithm, COEFs, which computes
|
||||
coefficients of certain polynomials.
|
||||
|
||||
The pair (PROVE1, VALID1) allows a prover to demonstrate that each row of a
|
||||
matrix is a set of commitments to bits that open to exactly one 1 (the rest
|
||||
open to 0). The implementation works like this:
|
||||
|
||||
PROVE1: Take as input ([[b[0][0], b[0][1], ..., b[m-1][n-1]] ], r).
|
||||
1) Select r[A], r[C], r[D] at random from Zq.
|
||||
2) For each j=0, 1,..., m-1 and for each i=1,2,...,n-1, select a[j][i]
|
||||
from Zq at random.
|
||||
3) For each j, compute a[j][0] = -a[j][1] - a[j][2] - ... - a[j][n-1].
|
||||
4) Compute A:=COMp(a[0][0], ..., a[m-1][n-1]; r[A])
|
||||
5) For each j=0,...,m-1 and i=0,...,n-1, compute the values
|
||||
c[j][i] := a[j][i]*(1-2*b[j][i])
|
||||
d[j][i] := -a[j][i]^2
|
||||
6) Compute the commitments C:=COMp(c[0][0], ..., c[m-1][n-1]; r[C]) and
|
||||
D:=COMp(d[0][0],...,d[m-1][n-1]; r[D]).
|
||||
7) Compute x := Hs(A,C,D)
|
||||
8) For each j=0,...,m-1, i=0,...,n-1, compute f[j][i]:=b[j][i]*x + a[j][i]
|
||||
9) Compute z[A]:= r*x + r[A], z[C]:=r[C]*x+r[D]
|
||||
10) Output proof P:=A,C,D, f[0][1],f[0][2], ..., f[0][n-1], f[1][1],
|
||||
f[1][2], ..., f[1][n-1], ..., f[m-1][1], ..., f[m-1][n-1], z[A], z[C].
|
||||
|
||||
When PROVE1 is run as a subroutine for PROVE2, the prover will also output the
|
||||
values of each a[j][i]; these are not part of the formal proof, but they are
|
||||
used elsewhere. Note that we do not output the first column of the matrix F
|
||||
whose (ji)^th entry is f[j][i]. Essentially here, we are taking a matrix, we
|
||||
are showing each row sums to 1 and each element satisfies the equation
|
||||
b[j][i]*(1-b[j][i]) = 0.
|
||||
|
||||
VALID1: Take as input B (the prover wishes to demonstrate B is a commitment to
|
||||
the values b[j][i] as described in PROVE1) and proof P.
|
||||
1) If A,B,C,D are each elliptic curve points, both z[A] and z[C] are ele-
|
||||
ments of Zq, and each f[j][i] are elements of Zq, compute the value
|
||||
x := Hs(A,C,D). Else, output FAIL and terminate.
|
||||
2) For each j=0, ..., m-1, compute f[j][0] = x-f[j][1] - ... - f[j][n-1].
|
||||
3) For each j=0, ..., m-1, i=0,...,n-1, compute each of the values
|
||||
f'[j][i] := f[j][i]*(x-f[j][i])
|
||||
4) Return 1 if and only if all of the conditions hold true:
|
||||
i) For each j=0, ..., m-1, f[j][0]=x-f[j][1]-f[j][2]- ... -f[j][n-1]
|
||||
ii) xB + A = COMp(f[0][0], ..., f[m-1][n-1]; z[A])
|
||||
iii) xC + D = COMp(f'[0][0], ..., f'[m-1][n-1]; z[C])
|
||||
(otherwise return 0).
|
||||
|
||||
The pair (PROVE2, VALID2) allows a prover to demonstrate that a sequence of
|
||||
commitments contains at least one commitment to 1. The implementation works
|
||||
like this:
|
||||
|
||||
PROVE2: Take as input a sequence of values c[0], c[1], ... c[N-1] for
|
||||
some N= n^m, a secret index i* in this list corresponding to a commitment that
|
||||
opens to 1, and a random scalar r in Zq. For all integers j, i, define the
|
||||
Kronecker delta function as DELTA(j,i) := 1 if j=i and 0 otherwise.
|
||||
1) For k=0, 1, ..., m-1, select random coefficient u[k] at random from Zq.
|
||||
2) Select r[B] at random from Zq.
|
||||
3) Write i* in n-ary i* = i*[0] + i*[1]*(n) + i*[2]*(n^2) + ...
|
||||
... + i*[m-1]*(n^(m-1)), and represent i* as the sequence i*[j].
|
||||
4) For each j=0, 1, ..., m-1 and i=0, 1, ..., n-1, define the values
|
||||
d[j][i] := DELTA(i*[j],i).
|
||||
5) Compute B:=COMp(d[0][0], ..., d[m-1][n-1]; r[B]).
|
||||
6) Prover runs PROVE1 and stores the output as the list of data
|
||||
P <- PROVE1(B, (d[0][0], ..., d[m-1][n-1], r[B])) and stores the values
|
||||
a[j][i] for use in the next step. Note the prover receives A, C, D from
|
||||
PROVE1, all the values f[j][i], and the values z[A], z[C].
|
||||
7) coefs <- COEFS(a[0][0], ..., a[m-1][n-1], i*)
|
||||
8) For k=0, ..., m-1:
|
||||
i) G[k] := ENCeg(0, u[k]) # = (rHp(G), rG)
|
||||
ii) For i=0, ..., N-1, update G[k] by multiplying:
|
||||
G[k]=G[k]*(co[i]^coefs[i][k])
|
||||
=(G[k][1]+coefs[i][k]*co[i][1], G[k][2]+coefs[i][k]*co[i][2])
|
||||
The final value for each G[k] from step 8 can be written explicitly:
|
||||
G[k]=(rHp(G)+coefs[0][k]*co[0][1] + ... +coefs[n-1][k]*co[n-1][1],
|
||||
rG+coefs[0][k]*co[0][2] + ... +coefs[n-1][k]*co[n-1][2])
|
||||
9) Compute x' = Hs(A,B,C,D, G[0], ..., G[m-1]).
|
||||
10) Compute z:= r*(x')^m - u[m-1]*(x')^(m-1) - ... - u[1]*(x')^1 - u[0]
|
||||
11) Output proof P':=P, B, G[0], ..., G[m-1], z.
|
||||
|
||||
VALID2: Take as input a list of N El Gamal commitments co[0], ..., co[N-1] and a
|
||||
proof P' parsed as P, B, G[0],...,G[m-1], where P is a proof parsed as
|
||||
P = A,C,D, f[0][1],f[0][2], ..., f[m-1][n-1], z[A], z[C].
|
||||
1) If A,B,C,D, each G[k] are all elliptic curve points, and if z[A], z[C],
|
||||
and z are elements of Zq, and if each f[j][i] are elements of Zq, and
|
||||
if VALID1(B,P)=1, then compute the value
|
||||
x' := Hs(A, B, C,D,G[0],...,G[m-1]).
|
||||
Else, output FAIL and terminate.
|
||||
2) Compute c := ENCeg(0,z).
|
||||
3) For each k=0,...,m-1, compute
|
||||
G[k]^(-x^k) := (-x^k*G[k][1],-x^k*G[k][2]).
|
||||
4) Compute (G[0]^(-(x')^0))*(G[1]^(-(x')^1))*...*(G[m-1]^(-(x')^(m-1))
|
||||
which explicitly is the ordered pair (G*[1], G*[2]):
|
||||
(-G[0][1] - (x')G[1][1] - ... - (x')^(m-1)G[m-1][1],
|
||||
-G[0][2] - (x')G[1][2] - ... - (x')^(m-1)G[m-1][2] )
|
||||
5) For each j=0, ..., m-1, i=0,...,n-1, compute each of the values
|
||||
f[j][0] := x' - f[j][1] - f[j][2] - ... - f[j][n-1].
|
||||
6) For each i=0,...,N-1, write i in n-ary arithmetic as usual as
|
||||
i = i[0] + i[1]*n + i[2]*(n^2) + ... + i[m-1]*(n^(m-1)). Compute the
|
||||
values g[i] := f[0][i[0]]*f[1][i[1]]*...*f[m-1][i[m-1] and the
|
||||
commitments co*[i] := co[i]^g[i] = (g[i]*co[i][1], g[i]*co[i][2]).
|
||||
7) Compute (co*[0])*(co*[1])*...*(co*[N-1]) which ends up as the ordered
|
||||
pair (c**[1], c**[2])
|
||||
(c*[0][1] + c*[1][1] + ... c*[N-1][1], c*[0][2] + ... c*[N-1][2])
|
||||
8) Compute c'=(c**[1],c**[2])*(G*[1],G*[2])=(c**[1]+G*[1], c**[2] + G*[2]).
|
||||
9) Return 1 if and only if c' == c and 0 otherwise.
|
||||
|
||||
COEFS: We split this into two algorithms. The outer layer, COEFS, is specific
|
||||
to the Ruffing scheme. The inner layer, COEFPROD, takes two sequences of coef-
|
||||
ficients as input, representing polynomials, and outputs a sequence of coef-
|
||||
ficients of the resulting product of those two polynomials. We denote the
|
||||
Discrete Fourier Transform as DFT, and the inverse IDFT.
|
||||
|
||||
COEFS takes as input a matrix A = a[0][0], ..., a[m-1][n-1] and index i* such
|
||||
that 0 <= i* < n^m. We decompose 0 <= i* < N into the n-ary representation
|
||||
i* = i*[0] + i*[1]*n + ... + i*[m-1]*n^(m-1)
|
||||
In this decomposition, for each 0 <= j < m, we have the constraint
|
||||
0 <= i*[j] < n (otherwise we could include the "runoff" above n into the co-
|
||||
efficient one index higher). For index 0 <= k < N we again decompose into
|
||||
the n-ary representation
|
||||
k = k[0] + k[1]*n + ... + k[m-1]*n^(m-1)
|
||||
where for each index 0 <= j < m (where N=n^m), we have 0 <= k[j] < n. We rep-
|
||||
resent the polynomial defined by DELTA(i*[j],k[j])*x + a[j][k[j]] as an array
|
||||
q[j][k] = [a[j][k[j]], DELTA(i*[j],k[j])]
|
||||
We then do the following:
|
||||
1) For each k = 0, ..., N-1:
|
||||
i) Compute coefList[k] : = q[0][k]
|
||||
ii) For 1<=j<m update coefList[k] = COEFPROD(coefList[k], q[j][k[j]])
|
||||
2) Output coefList[0], coefList[1], ..., coefList[N-1].
|
||||
|
||||
COEFPROD: Take as input lists c[0],...,c[n-1] and d[0],...,d[n-1]. If these are
|
||||
not the same length, the shorter one may be padded with zeros at the end. We
|
||||
can parse the polynomial p(x) = c[0] + c[1]*x + ... + c[n-1]*x^(n-1) and the
|
||||
polynomial q(x) = d[0] + d[1]*x + ... + d[n-1]*x^(n-1). Let m = 2n-1.
|
||||
1) Compute P := DFT(p), Q := DFT(q). Both P and Q should have length m and
|
||||
This should take O(n*log(n)) time.
|
||||
2) Compute PQ := [P[0]*Q[0], P[1]*Q[1], ...]. This should take O(n) time.
|
||||
3) Compute t:= IDFT(PQ) = t[0], t[1], ..., t[m]. This should take
|
||||
O(n*log(n)).
|
||||
4) Output t as the coefficients of p(x)*q(x).
|
||||
|
||||
|
||||
II. BOOTLE-RUFFING RINGCT SCHEME
|
||||
|
||||
We are now in a position to present Ruffing's scheme in its entirety. We select
|
||||
a group of prime order q under which the Decisional Diffie-Hellman assumption
|
||||
holds. Let G be a commonly known generator of the group. Let Hp be a hash func-
|
||||
tion that produces an elliptic curve point. Let Hs be a hash function that prod-
|
||||
uces a scalar in Zq. Let Zq denote the integers modulo q.
|
||||
|
||||
KEYGEN:
|
||||
1) Select r,r' randomly from Zq.
|
||||
2) Set sk := (r,r')
|
||||
3) Set ki := r'G
|
||||
4) Set pk := ENCeg(ki,r)
|
||||
5) Output (sk,ki,pk).
|
||||
|
||||
SPEND: Take as input an (L x N) matrix of public keys PK written as
|
||||
PK = [pk[j][i]; j=0,1,...,L-1, i=0,1,...,N-1], such that the signer knows the
|
||||
associated secret keys of the column with (secret) index i*, a commitment for
|
||||
each column co = co[0], co[1], ...., co[N-1] such that co[i*] opens to zero, a
|
||||
set of secret keys sk = sk[0],sk[1],...,sk[L-1], a set of key images ki, a
|
||||
message M, and a random scalar s from Zq. Write each secret key as
|
||||
sk[i]=(r[i],r'[i]).
|
||||
|
||||
1) Compute co' := sG.
|
||||
2) Set f := (ki, PK, co, co', M)
|
||||
3) Compute (c, f') := SUB(f)
|
||||
4) Compute s' := s + r[0]*f'[0] + r[1]*f'[1] + ... + r[L-1]*f'[L-1]
|
||||
5) Compute sigma[1] := PROVE2(c, i*,s')
|
||||
6) Compute sigma[2] := SIG*((r'[0], r'[1], ..., r'[L-1]), (sigma[1], f))
|
||||
7) Output signature = (co', sigma[1], sigma[2])
|
||||
|
||||
SUB: Take as input key images ki = ki[0], ki[1], ..., ki[L-1], a matrix of
|
||||
public keys PK = [pk[j][i]; j=1,2,...,L, i=1,2,...,N], a set of Pedersen com-
|
||||
mitments co = co[1], ..., co[N], an elliptic curve point co', and message M.
|
||||
|
||||
1) For j=0, 1, ..., L-1:
|
||||
i) Compute pkz[j] := (ki[j],0)
|
||||
ii) Compute f[j] := Hs(ki[j],f,j)
|
||||
2) For i=0, 1, ..., N-1 do:
|
||||
i) Set c[i] := (co[i], co')
|
||||
ii) For j=0,1, ..., L-1, update c[i] = c[i]*(pk[j][i]/pkz[j])^(f[j])
|
||||
3) Output (c,f) where c = c[0], ..., c[N-1], f=f[0], ..., f[L-1].
|
||||
|
||||
Note that due to commitment arithmetic the result c[i] is written explicitly as:
|
||||
(co[i]+f[0]*(pk[0][i][1]-pkz[0][1])+ ... +f[L-1]*(pk[L-1][i][1]-pkz[L-1][1]),
|
||||
co'+f[0]*(pk[0][i][2]-pkz[0][2])+ ... +f[L-1]*(pk[L-1][i][2]-pkz[L-1][2]))
|
||||
|
||||
VER: This verifies a ring signature. Take as input a set of key images
|
||||
ki = ki[0], ki[1], ..., ki[L-1], a matrix of public keys PK, commitments
|
||||
co = co[0], ..., co[N-1], an elliptic curve point co', a message M, and a sig-
|
||||
nature from SPEND, signature = (co', sigma[1], sigma[2]).
|
||||
|
||||
1) Set f := (ki, PK, co, co', M).
|
||||
2) Compute (c, f') := SUB(f) (we only use c for verification)
|
||||
3) Return 1 iff
|
||||
(i) the signature sigma[2] on message (sigma[1], f) passes VER* with
|
||||
keys ki, i.e. VER*((sigma[1],f), ki, sigma[2])==1
|
||||
(ii) the proof sigma[1] is a valid NIZK proof that one of the commit-
|
||||
ments in co open to 0, i.e. VALID2(sigma[1], co) == 1.
|
||||
Return 0 otherwise.
|
||||
|
||||
III. Specific Example
|
||||
|
||||
Here is a quick concrete example using integer scalars of the Bootle NIZK
|
||||
proof that a commitment opens appropriately* as in PROVE1/VALID1 above.
|
||||
We start with random data we wish to commit to. Each entry must be
|
||||
a bit and each row must sum to 1 if VALID1 is to validate the proof. We
|
||||
may as well go with the identity matrix:
|
||||
|
||||
[ [b[0][0], b[0][1] ], = [ [1, 0],
|
||||
[b[1][0], b[1][1] ] ] [0, 1] ]
|
||||
|
||||
We compute the commitment B = COMp([b[j][i]], rB):
|
||||
|
||||
B = rB*G + 1*H[0,0] + 0*H[0,1] + 0*H[1,0] + 1*H[1,1]
|
||||
|
||||
We pick random a[0][1], say 7, and a random a[1][1], say -5. We compute a[j][0]
|
||||
as -sum(a[j][i]):
|
||||
|
||||
[ [a[0][0], a[0][1] ], = [ [-7, 7],
|
||||
[a[1][0], a[1][1] ] ] [ 5, -5] ]
|
||||
|
||||
We compute commitment A = COMp( [a[j][i]], rA):
|
||||
|
||||
A = rA*G - 7*H[0,0] + 7*H[0,1] + 5*H[1,0] - 5*H[1,1]
|
||||
|
||||
We compute c[j][i] = a[j][i]*(1-2b[j][i]), d[j][i] = -a[j][i]^2:
|
||||
|
||||
[ [c[0][0], c[0][1] ], = [ [7, 7],
|
||||
[c[1][0], c[1][1] ] ] [5, 5] ]
|
||||
|
||||
[ [d[0][0], d[0][1] ], = [ [-49, -49],
|
||||
[d[1][0], d[1][1] ] ] [-25, -25] ]
|
||||
|
||||
We compute commitments C and D:
|
||||
|
||||
C = rC*G + 7*H[0,0] + 7*H[0,1] + 5*H[1,0] + 5*H[1,1]
|
||||
D = rD*G - 49*H[0,0] - 49*H[0,1] - 25*H[1,0] - 25*H[1,1]
|
||||
|
||||
We compute x = Hs(A,C,D). We compute f[j][i] = b[j][i]*x + a[j][i]:
|
||||
|
||||
[ [f[0][0], f[0][1] ], = [ [x-7, 7],
|
||||
[f[1][0], f[1][1] ] ] [ 5, x-5] ]
|
||||
|
||||
We compute zA = rB*x + rA, zC = rC*x + rD and send [A,B,C,D, zA, zC] together
|
||||
with all columns except the first column of [f[j][i]] as our NIZK proof that B
|
||||
is a well-formed Bootle commitment*. Now a verifier receives the left hand side
|
||||
of the following system of equations
|
||||
|
||||
A = rA*G - 7*H[0,0] + 7*H[0,1] + 5*H[1,0] - 5*H[1,1]
|
||||
B = rB*G + 1*H[0,0] + 0*H[0,1] + 0*H[1,0] + 1*H[1,1]
|
||||
C = rC*G + 7*H[0,0] + 7*H[0,1] + 5*H[1,0] + 5*H[1,1]
|
||||
D = rD*G - 49*H[0,0] - 49*H[0,1] - 25*H[1,0] - 25*H[1,1]
|
||||
f[0][1] = 7
|
||||
f[1][1] = x-5
|
||||
zA = rB*x + rA
|
||||
zC = rC*x + rD
|
||||
|
||||
Using the left hand side only, the verifier computes:
|
||||
x := H(A,C,D)
|
||||
f[0][0] := x - f[0][1]
|
||||
f[1][0] := x - f[1][1]
|
||||
Now the verifier computes the matrix [f'[j][i]] = [f[j][i]*(x-f[j][i])]:
|
||||
[ [f'[0][0], f'[0][1] ], = [ [f[0][0]*(x-f[0][0]), f[0][1]*(x-f[0][1])],
|
||||
[f'[1][0], f'[1][1] ] ] [ f[1][0]*(x-f[1][0]), f[1][1]*(x-f[1][1])] ]
|
||||
and computes the commitments COM(f[j][i], zA), COM(f'[j][i], zC):
|
||||
|
||||
COM( f[j][i], zA) := zA*G + f[0][0]*H[0,0] + f[0][1]*H[0,1] + ...
|
||||
COM(f'[j][i], zC) := zC*G + f'[0][0]*H[0,0] + f'[0][1]*H[0,1] + ...
|
||||
|
||||
The verifier can now check whether
|
||||
i) xB + A =? COM(f[j][i], zA)
|
||||
ii) xC + D =? COM(f'[j][i], zC)
|
||||
|
||||
Note that if the verifier received the values as specified above, she computes
|
||||
the same f[0][0], f[1][0] as the prover used, x-7 and 5, so the verifier
|
||||
obtains the matrix
|
||||
|
||||
[ [f[0][0], f[0][1] ], = [ [x-7, 7],
|
||||
[f[1][0], f[1][1] ] ] [ 5, x-5] ]
|
||||
|
||||
and computes
|
||||
|
||||
[ [f'[0][0], f'[0][1] ], = [ [7(x-7), 7(x-7)],
|
||||
[f'[1][0], f'[1][1] ] ] [5(x-5), 5(x-5)] ]
|
||||
|
||||
And thus computes the commitments
|
||||
|
||||
COM([ f[j][i]], zA) = zA*G + (x-7)*H[0,0] + 7*H[0,1] + 5*H[1,0] + (x-5)*H[1,1]
|
||||
COM([f'[j][i]], zC) = zC*G + 7(x-7)*H[0,0] + 7(x-7)*H[0,1] + 5*(x-5)*H[1,0]
|
||||
+ 5(x-5)*H[1,1]
|
||||
|
||||
On the other hand, she is given B and A, and with her x, she can compute xB+A:
|
||||
|
||||
xB + A = x(rB*G + 1*H[0,0] + 0*H[0,1] + 0*H[1,0] + 1*H[1,1])
|
||||
+ (rA*G - 7*H[0,0] + 7*H[0,1] + 5*H[1,0] - 5*H[1,1])
|
||||
= (rB*x + rA)*G + (x-7)*H[0,0] + 7*H[0,1] + 5*H[1,0] + (x-5)*H[1,1]
|
||||
|
||||
Lo and behold, this matches COM([ f[j][i]], zA). She can do the same with C,D:
|
||||
|
||||
xC + D = x(rC*G + 7*H[0,0] + 7*H[0,1] + 5*H[1,0] + 5*H[1,1])
|
||||
+ (rD*G - 49*H[0,0] - 49*H[0,1] - 25*H[1,0] - 25*H[1,1])
|
||||
= (rC*x + rD)*G + 7(x-7)*H[0,0] + 7(x-7)*H[0,1] + 5(x-5)*H[1,0]
|
||||
+ 5*(x-5)*H[1,1]
|
||||
|
||||
Voila!
|
||||
|
||||
|
||||
|
||||
IV. REFERENCES
|
||||
|
||||
[1] Ruffing, Thyagarajan, Ronge, Schröder. "Boosting Private Payments in
|
||||
Monero: New Attacks, Exact Cryptographic Definitions, and Sublinear
|
||||
Ring Signatures." In preparation.
|
||||
[2] Bootle, Cerulli, Chaidos, Ghadafi, Groth, Petit. "Short accountable
|
||||
ring signatures based on DDH." European Symposium on Research in
|
||||
Computer Security. Springer, Cham, 2015.
|
||||
[3] Bellare, Mihir, and Gregory Neven. "Multi-signatures in the plain
|
||||
public-key model and a general forking lemma." Proceedings of the 13th
|
||||
ACM conference on Computer and communications security. ACM, 2006.
|
||||
|
||||
|
872
source-code/RuffCT-java/c/crypto-ops-data.c
Normal file
872
source-code/RuffCT-java/c/crypto-ops-data.c
Normal file
|
@ -0,0 +1,872 @@
|
|||
// Copyright (c) 2014-2017, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "crypto-ops.h"
|
||||
|
||||
/* sqrt(x) is such an integer y that 0 <= y <= p - 1, y % 2 = 0, and y^2 = x (mod p). */
|
||||
/* d = -121665 / 121666 */
|
||||
const fe fe_d = {-10913610, 13857413, -15372611, 6949391, 114729, -8787816, -6275908, -3247719, -18696448, -12055116}; /* d */
|
||||
const fe fe_sqrtm1 = {-32595792, -7943725, 9377950, 3500415, 12389472, -272473, -25146209, -2005654, 326686, 11406482}; /* sqrt(-1) */
|
||||
const fe fe_d2 = {-21827239, -5839606, -30745221, 13898782, 229458, 15978800, -12551817, -6495438, 29715968, 9444199}; /* 2 * d */
|
||||
|
||||
/* base[i][j] = (j+1)*256^i*B */
|
||||
const ge_precomp ge_base[32][8] = {
|
||||
{
|
||||
{{25967493, -14356035, 29566456, 3660896, -12694345, 4014787, 27544626, -11754271, -6079156, 2047605},
|
||||
{-12545711, 934262, -2722910, 3049990, -727428, 9406986, 12720692, 5043384, 19500929, -15469378},
|
||||
{-8738181, 4489570, 9688441, -14785194, 10184609, -12363380, 29287919, 11864899, -24514362, -4438546}},
|
||||
{{-12815894, -12976347, -21581243, 11784320, -25355658, -2750717, -11717903, -3814571, -358445, -10211303},
|
||||
{-21703237, 6903825, 27185491, 6451973, -29577724, -9554005, -15616551, 11189268, -26829678, -5319081},
|
||||
{26966642, 11152617, 32442495, 15396054, 14353839, -12752335, -3128826, -9541118, -15472047, -4166697}},
|
||||
{{15636291, -9688557, 24204773, -7912398, 616977, -16685262, 27787600, -14772189, 28944400, -1550024},
|
||||
{16568933, 4717097, -11556148, -1102322, 15682896, -11807043, 16354577, -11775962, 7689662, 11199574},
|
||||
{30464156, -5976125, -11779434, -15670865, 23220365, 15915852, 7512774, 10017326, -17749093, -9920357}},
|
||||
{{-17036878, 13921892, 10945806, -6033431, 27105052, -16084379, -28926210, 15006023, 3284568, -6276540},
|
||||
{23599295, -8306047, -11193664, -7687416, 13236774, 10506355, 7464579, 9656445, 13059162, 10374397},
|
||||
{7798556, 16710257, 3033922, 2874086, 28997861, 2835604, 32406664, -3839045, -641708, -101325}},
|
||||
{{10861363, 11473154, 27284546, 1981175, -30064349, 12577861, 32867885, 14515107, -15438304, 10819380},
|
||||
{4708026, 6336745, 20377586, 9066809, -11272109, 6594696, -25653668, 12483688, -12668491, 5581306},
|
||||
{19563160, 16186464, -29386857, 4097519, 10237984, -4348115, 28542350, 13850243, -23678021, -15815942}},
|
||||
{{-15371964, -12862754, 32573250, 4720197, -26436522, 5875511, -19188627, -15224819, -9818940, -12085777},
|
||||
{-8549212, 109983, 15149363, 2178705, 22900618, 4543417, 3044240, -15689887, 1762328, 14866737},
|
||||
{-18199695, -15951423, -10473290, 1707278, -17185920, 3916101, -28236412, 3959421, 27914454, 4383652}},
|
||||
{{5153746, 9909285, 1723747, -2777874, 30523605, 5516873, 19480852, 5230134, -23952439, -15175766},
|
||||
{-30269007, -3463509, 7665486, 10083793, 28475525, 1649722, 20654025, 16520125, 30598449, 7715701},
|
||||
{28881845, 14381568, 9657904, 3680757, -20181635, 7843316, -31400660, 1370708, 29794553, -1409300}},
|
||||
{{14499471, -2729599, -33191113, -4254652, 28494862, 14271267, 30290735, 10876454, -33154098, 2381726},
|
||||
{-7195431, -2655363, -14730155, 462251, -27724326, 3941372, -6236617, 3696005, -32300832, 15351955},
|
||||
{27431194, 8222322, 16448760, -3907995, -18707002, 11938355, -32961401, -2970515, 29551813, 10109425}}
|
||||
}, {
|
||||
{{-13657040, -13155431, -31283750, 11777098, 21447386, 6519384, -2378284, -1627556, 10092783, -4764171},
|
||||
{27939166, 14210322, 4677035, 16277044, -22964462, -12398139, -32508754, 12005538, -17810127, 12803510},
|
||||
{17228999, -15661624, -1233527, 300140, -1224870, -11714777, 30364213, -9038194, 18016357, 4397660}},
|
||||
{{-10958843, -7690207, 4776341, -14954238, 27850028, -15602212, -26619106, 14544525, -17477504, 982639},
|
||||
{29253598, 15796703, -2863982, -9908884, 10057023, 3163536, 7332899, -4120128, -21047696, 9934963},
|
||||
{5793303, 16271923, -24131614, -10116404, 29188560, 1206517, -14747930, 4559895, -30123922, -10897950}},
|
||||
{{-27643952, -11493006, 16282657, -11036493, 28414021, -15012264, 24191034, 4541697, -13338309, 5500568},
|
||||
{12650548, -1497113, 9052871, 11355358, -17680037, -8400164, -17430592, 12264343, 10874051, 13524335},
|
||||
{25556948, -3045990, 714651, 2510400, 23394682, -10415330, 33119038, 5080568, -22528059, 5376628}},
|
||||
{{-26088264, -4011052, -17013699, -3537628, -6726793, 1920897, -22321305, -9447443, 4535768, 1569007},
|
||||
{-2255422, 14606630, -21692440, -8039818, 28430649, 8775819, -30494562, 3044290, 31848280, 12543772},
|
||||
{-22028579, 2943893, -31857513, 6777306, 13784462, -4292203, -27377195, -2062731, 7718482, 14474653}},
|
||||
{{2385315, 2454213, -22631320, 46603, -4437935, -15680415, 656965, -7236665, 24316168, -5253567},
|
||||
{13741529, 10911568, -33233417, -8603737, -20177830, -1033297, 33040651, -13424532, -20729456, 8321686},
|
||||
{21060490, -2212744, 15712757, -4336099, 1639040, 10656336, 23845965, -11874838, -9984458, 608372}},
|
||||
{{-13672732, -15087586, -10889693, -7557059, -6036909, 11305547, 1123968, -6780577, 27229399, 23887},
|
||||
{-23244140, -294205, -11744728, 14712571, -29465699, -2029617, 12797024, -6440308, -1633405, 16678954},
|
||||
{-29500620, 4770662, -16054387, 14001338, 7830047, 9564805, -1508144, -4795045, -17169265, 4904953}},
|
||||
{{24059557, 14617003, 19037157, -15039908, 19766093, -14906429, 5169211, 16191880, 2128236, -4326833},
|
||||
{-16981152, 4124966, -8540610, -10653797, 30336522, -14105247, -29806336, 916033, -6882542, -2986532},
|
||||
{-22630907, 12419372, -7134229, -7473371, -16478904, 16739175, 285431, 2763829, 15736322, 4143876}},
|
||||
{{2379352, 11839345, -4110402, -5988665, 11274298, 794957, 212801, -14594663, 23527084, -16458268},
|
||||
{33431127, -11130478, -17838966, -15626900, 8909499, 8376530, -32625340, 4087881, -15188911, -14416214},
|
||||
{1767683, 7197987, -13205226, -2022635, -13091350, 448826, 5799055, 4357868, -4774191, -16323038}}
|
||||
}, {
|
||||
{{6721966, 13833823, -23523388, -1551314, 26354293, -11863321, 23365147, -3949732, 7390890, 2759800},
|
||||
{4409041, 2052381, 23373853, 10530217, 7676779, -12885954, 21302353, -4264057, 1244380, -12919645},
|
||||
{-4421239, 7169619, 4982368, -2957590, 30256825, -2777540, 14086413, 9208236, 15886429, 16489664}},
|
||||
{{1996075, 10375649, 14346367, 13311202, -6874135, -16438411, -13693198, 398369, -30606455, -712933},
|
||||
{-25307465, 9795880, -2777414, 14878809, -33531835, 14780363, 13348553, 12076947, -30836462, 5113182},
|
||||
{-17770784, 11797796, 31950843, 13929123, -25888302, 12288344, -30341101, -7336386, 13847711, 5387222}},
|
||||
{{-18582163, -3416217, 17824843, -2340966, 22744343, -10442611, 8763061, 3617786, -19600662, 10370991},
|
||||
{20246567, -14369378, 22358229, -543712, 18507283, -10413996, 14554437, -8746092, 32232924, 16763880},
|
||||
{9648505, 10094563, 26416693, 14745928, -30374318, -6472621, 11094161, 15689506, 3140038, -16510092}},
|
||||
{{-16160072, 5472695, 31895588, 4744994, 8823515, 10365685, -27224800, 9448613, -28774454, 366295},
|
||||
{19153450, 11523972, -11096490, -6503142, -24647631, 5420647, 28344573, 8041113, 719605, 11671788},
|
||||
{8678025, 2694440, -6808014, 2517372, 4964326, 11152271, -15432916, -15266516, 27000813, -10195553}},
|
||||
{{-15157904, 7134312, 8639287, -2814877, -7235688, 10421742, 564065, 5336097, 6750977, -14521026},
|
||||
{11836410, -3979488, 26297894, 16080799, 23455045, 15735944, 1695823, -8819122, 8169720, 16220347},
|
||||
{-18115838, 8653647, 17578566, -6092619, -8025777, -16012763, -11144307, -2627664, -5990708, -14166033}},
|
||||
{{-23308498, -10968312, 15213228, -10081214, -30853605, -11050004, 27884329, 2847284, 2655861, 1738395},
|
||||
{-27537433, -14253021, -25336301, -8002780, -9370762, 8129821, 21651608, -3239336, -19087449, -11005278},
|
||||
{1533110, 3437855, 23735889, 459276, 29970501, 11335377, 26030092, 5821408, 10478196, 8544890}},
|
||||
{{32173121, -16129311, 24896207, 3921497, 22579056, -3410854, 19270449, 12217473, 17789017, -3395995},
|
||||
{-30552961, -2228401, -15578829, -10147201, 13243889, 517024, 15479401, -3853233, 30460520, 1052596},
|
||||
{-11614875, 13323618, 32618793, 8175907, -15230173, 12596687, 27491595, -4612359, 3179268, -9478891}},
|
||||
{{31947069, -14366651, -4640583, -15339921, -15125977, -6039709, -14756777, -16411740, 19072640, -9511060},
|
||||
{11685058, 11822410, 3158003, -13952594, 33402194, -4165066, 5977896, -5215017, 473099, 5040608},
|
||||
{-20290863, 8198642, -27410132, 11602123, 1290375, -2799760, 28326862, 1721092, -19558642, -3131606}}
|
||||
}, {
|
||||
{{7881532, 10687937, 7578723, 7738378, -18951012, -2553952, 21820786, 8076149, -27868496, 11538389},
|
||||
{-19935666, 3899861, 18283497, -6801568, -15728660, -11249211, 8754525, 7446702, -5676054, 5797016},
|
||||
{-11295600, -3793569, -15782110, -7964573, 12708869, -8456199, 2014099, -9050574, -2369172, -5877341}},
|
||||
{{-22472376, -11568741, -27682020, 1146375, 18956691, 16640559, 1192730, -3714199, 15123619, 10811505},
|
||||
{14352098, -3419715, -18942044, 10822655, 32750596, 4699007, -70363, 15776356, -28886779, -11974553},
|
||||
{-28241164, -8072475, -4978962, -5315317, 29416931, 1847569, -20654173, -16484855, 4714547, -9600655}},
|
||||
{{15200332, 8368572, 19679101, 15970074, -31872674, 1959451, 24611599, -4543832, -11745876, 12340220},
|
||||
{12876937, -10480056, 33134381, 6590940, -6307776, 14872440, 9613953, 8241152, 15370987, 9608631},
|
||||
{-4143277, -12014408, 8446281, -391603, 4407738, 13629032, -7724868, 15866074, -28210621, -8814099}},
|
||||
{{26660628, -15677655, 8393734, 358047, -7401291, 992988, -23904233, 858697, 20571223, 8420556},
|
||||
{14620715, 13067227, -15447274, 8264467, 14106269, 15080814, 33531827, 12516406, -21574435, -12476749},
|
||||
{236881, 10476226, 57258, -14677024, 6472998, 2466984, 17258519, 7256740, 8791136, 15069930}},
|
||||
{{1276410, -9371918, 22949635, -16322807, -23493039, -5702186, 14711875, 4874229, -30663140, -2331391},
|
||||
{5855666, 4990204, -13711848, 7294284, -7804282, 1924647, -1423175, -7912378, -33069337, 9234253},
|
||||
{20590503, -9018988, 31529744, -7352666, -2706834, 10650548, 31559055, -11609587, 18979186, 13396066}},
|
||||
{{24474287, 4968103, 22267082, 4407354, 24063882, -8325180, -18816887, 13594782, 33514650, 7021958},
|
||||
{-11566906, -6565505, -21365085, 15928892, -26158305, 4315421, -25948728, -3916677, -21480480, 12868082},
|
||||
{-28635013, 13504661, 19988037, -2132761, 21078225, 6443208, -21446107, 2244500, -12455797, -8089383}},
|
||||
{{-30595528, 13793479, -5852820, 319136, -25723172, -6263899, 33086546, 8957937, -15233648, 5540521},
|
||||
{-11630176, -11503902, -8119500, -7643073, 2620056, 1022908, -23710744, -1568984, -16128528, -14962807},
|
||||
{23152971, 775386, 27395463, 14006635, -9701118, 4649512, 1689819, 892185, -11513277, -15205948}},
|
||||
{{9770129, 9586738, 26496094, 4324120, 1556511, -3550024, 27453819, 4763127, -19179614, 5867134},
|
||||
{-32765025, 1927590, 31726409, -4753295, 23962434, -16019500, 27846559, 5931263, -29749703, -16108455},
|
||||
{27461885, -2977536, 22380810, 1815854, -23033753, -3031938, 7283490, -15148073, -19526700, 7734629}}
|
||||
}, {
|
||||
{{-8010264, -9590817, -11120403, 6196038, 29344158, -13430885, 7585295, -3176626, 18549497, 15302069},
|
||||
{-32658337, -6171222, -7672793, -11051681, 6258878, 13504381, 10458790, -6418461, -8872242, 8424746},
|
||||
{24687205, 8613276, -30667046, -3233545, 1863892, -1830544, 19206234, 7134917, -11284482, -828919}},
|
||||
{{11334899, -9218022, 8025293, 12707519, 17523892, -10476071, 10243738, -14685461, -5066034, 16498837},
|
||||
{8911542, 6887158, -9584260, -6958590, 11145641, -9543680, 17303925, -14124238, 6536641, 10543906},
|
||||
{-28946384, 15479763, -17466835, 568876, -1497683, 11223454, -2669190, -16625574, -27235709, 8876771}},
|
||||
{{-25742899, -12566864, -15649966, -846607, -33026686, -796288, -33481822, 15824474, -604426, -9039817},
|
||||
{10330056, 70051, 7957388, -9002667, 9764902, 15609756, 27698697, -4890037, 1657394, 3084098},
|
||||
{10477963, -7470260, 12119566, -13250805, 29016247, -5365589, 31280319, 14396151, -30233575, 15272409}},
|
||||
{{-12288309, 3169463, 28813183, 16658753, 25116432, -5630466, -25173957, -12636138, -25014757, 1950504},
|
||||
{-26180358, 9489187, 11053416, -14746161, -31053720, 5825630, -8384306, -8767532, 15341279, 8373727},
|
||||
{28685821, 7759505, -14378516, -12002860, -31971820, 4079242, 298136, -10232602, -2878207, 15190420}},
|
||||
{{-32932876, 13806336, -14337485, -15794431, -24004620, 10940928, 8669718, 2742393, -26033313, -6875003},
|
||||
{-1580388, -11729417, -25979658, -11445023, -17411874, -10912854, 9291594, -16247779, -12154742, 6048605},
|
||||
{-30305315, 14843444, 1539301, 11864366, 20201677, 1900163, 13934231, 5128323, 11213262, 9168384}},
|
||||
{{-26280513, 11007847, 19408960, -940758, -18592965, -4328580, -5088060, -11105150, 20470157, -16398701},
|
||||
{-23136053, 9282192, 14855179, -15390078, -7362815, -14408560, -22783952, 14461608, 14042978, 5230683},
|
||||
{29969567, -2741594, -16711867, -8552442, 9175486, -2468974, 21556951, 3506042, -5933891, -12449708}},
|
||||
{{-3144746, 8744661, 19704003, 4581278, -20430686, 6830683, -21284170, 8971513, -28539189, 15326563},
|
||||
{-19464629, 10110288, -17262528, -3503892, -23500387, 1355669, -15523050, 15300988, -20514118, 9168260},
|
||||
{-5353335, 4488613, -23803248, 16314347, 7780487, -15638939, -28948358, 9601605, 33087103, -9011387}},
|
||||
{{-19443170, -15512900, -20797467, -12445323, -29824447, 10229461, -27444329, -15000531, -5996870, 15664672},
|
||||
{23294591, -16632613, -22650781, -8470978, 27844204, 11461195, 13099750, -2460356, 18151676, 13417686},
|
||||
{-24722913, -4176517, -31150679, 5988919, -26858785, 6685065, 1661597, -12551441, 15271676, -15452665}}
|
||||
}, {
|
||||
{{11433042, -13228665, 8239631, -5279517, -1985436, -725718, -18698764, 2167544, -6921301, -13440182},
|
||||
{-31436171, 15575146, 30436815, 12192228, -22463353, 9395379, -9917708, -8638997, 12215110, 12028277},
|
||||
{14098400, 6555944, 23007258, 5757252, -15427832, -12950502, 30123440, 4617780, -16900089, -655628}},
|
||||
{{-4026201, -15240835, 11893168, 13718664, -14809462, 1847385, -15819999, 10154009, 23973261, -12684474},
|
||||
{-26531820, -3695990, -1908898, 2534301, -31870557, -16550355, 18341390, -11419951, 32013174, -10103539},
|
||||
{-25479301, 10876443, -11771086, -14625140, -12369567, 1838104, 21911214, 6354752, 4425632, -837822}},
|
||||
{{-10433389, -14612966, 22229858, -3091047, -13191166, 776729, -17415375, -12020462, 4725005, 14044970},
|
||||
{19268650, -7304421, 1555349, 8692754, -21474059, -9910664, 6347390, -1411784, -19522291, -16109756},
|
||||
{-24864089, 12986008, -10898878, -5558584, -11312371, -148526, 19541418, 8180106, 9282262, 10282508}},
|
||||
{{-26205082, 4428547, -8661196, -13194263, 4098402, -14165257, 15522535, 8372215, 5542595, -10702683},
|
||||
{-10562541, 14895633, 26814552, -16673850, -17480754, -2489360, -2781891, 6993761, -18093885, 10114655},
|
||||
{-20107055, -929418, 31422704, 10427861, -7110749, 6150669, -29091755, -11529146, 25953725, -106158}},
|
||||
{{-4234397, -8039292, -9119125, 3046000, 2101609, -12607294, 19390020, 6094296, -3315279, 12831125},
|
||||
{-15998678, 7578152, 5310217, 14408357, -33548620, -224739, 31575954, 6326196, 7381791, -2421839},
|
||||
{-20902779, 3296811, 24736065, -16328389, 18374254, 7318640, 6295303, 8082724, -15362489, 12339664}},
|
||||
{{27724736, 2291157, 6088201, -14184798, 1792727, 5857634, 13848414, 15768922, 25091167, 14856294},
|
||||
{-18866652, 8331043, 24373479, 8541013, -701998, -9269457, 12927300, -12695493, -22182473, -9012899},
|
||||
{-11423429, -5421590, 11632845, 3405020, 30536730, -11674039, -27260765, 13866390, 30146206, 9142070}},
|
||||
{{3924129, -15307516, -13817122, -10054960, 12291820, -668366, -27702774, 9326384, -8237858, 4171294},
|
||||
{-15921940, 16037937, 6713787, 16606682, -21612135, 2790944, 26396185, 3731949, 345228, -5462949},
|
||||
{-21327538, 13448259, 25284571, 1143661, 20614966, -8849387, 2031539, -12391231, -16253183, -13582083}},
|
||||
{{31016211, -16722429, 26371392, -14451233, -5027349, 14854137, 17477601, 3842657, 28012650, -16405420},
|
||||
{-5075835, 9368966, -8562079, -4600902, -15249953, 6970560, -9189873, 16292057, -8867157, 3507940},
|
||||
{29439664, 3537914, 23333589, 6997794, -17555561, -11018068, -15209202, -15051267, -9164929, 6580396}}
|
||||
}, {
|
||||
{{-12185861, -7679788, 16438269, 10826160, -8696817, -6235611, 17860444, -9273846, -2095802, 9304567},
|
||||
{20714564, -4336911, 29088195, 7406487, 11426967, -5095705, 14792667, -14608617, 5289421, -477127},
|
||||
{-16665533, -10650790, -6160345, -13305760, 9192020, -1802462, 17271490, 12349094, 26939669, -3752294}},
|
||||
{{-12889898, 9373458, 31595848, 16374215, 21471720, 13221525, -27283495, -12348559, -3698806, 117887},
|
||||
{22263325, -6560050, 3984570, -11174646, -15114008, -566785, 28311253, 5358056, -23319780, 541964},
|
||||
{16259219, 3261970, 2309254, -15534474, -16885711, -4581916, 24134070, -16705829, -13337066, -13552195}},
|
||||
{{9378160, -13140186, -22845982, -12745264, 28198281, -7244098, -2399684, -717351, 690426, 14876244},
|
||||
{24977353, -314384, -8223969, -13465086, 28432343, -1176353, -13068804, -12297348, -22380984, 6618999},
|
||||
{-1538174, 11685646, 12944378, 13682314, -24389511, -14413193, 8044829, -13817328, 32239829, -5652762}},
|
||||
{{-18603066, 4762990, -926250, 8885304, -28412480, -3187315, 9781647, -10350059, 32779359, 5095274},
|
||||
{-33008130, -5214506, -32264887, -3685216, 9460461, -9327423, -24601656, 14506724, 21639561, -2630236},
|
||||
{-16400943, -13112215, 25239338, 15531969, 3987758, -4499318, -1289502, -6863535, 17874574, 558605}},
|
||||
{{-13600129, 10240081, 9171883, 16131053, -20869254, 9599700, 33499487, 5080151, 2085892, 5119761},
|
||||
{-22205145, -2519528, -16381601, 414691, -25019550, 2170430, 30634760, -8363614, -31999993, -5759884},
|
||||
{-6845704, 15791202, 8550074, -1312654, 29928809, -12092256, 27534430, -7192145, -22351378, 12961482}},
|
||||
{{-24492060, -9570771, 10368194, 11582341, -23397293, -2245287, 16533930, 8206996, -30194652, -5159638},
|
||||
{-11121496, -3382234, 2307366, 6362031, -135455, 8868177, -16835630, 7031275, 7589640, 8945490},
|
||||
{-32152748, 8917967, 6661220, -11677616, -1192060, -15793393, 7251489, -11182180, 24099109, -14456170}},
|
||||
{{5019558, -7907470, 4244127, -14714356, -26933272, 6453165, -19118182, -13289025, -6231896, -10280736},
|
||||
{10853594, 10721687, 26480089, 5861829, -22995819, 1972175, -1866647, -10557898, -3363451, -6441124},
|
||||
{-17002408, 5906790, 221599, -6563147, 7828208, -13248918, 24362661, -2008168, -13866408, 7421392}},
|
||||
{{8139927, -6546497, 32257646, -5890546, 30375719, 1886181, -21175108, 15441252, 28826358, -4123029},
|
||||
{6267086, 9695052, 7709135, -16603597, -32869068, -1886135, 14795160, -7840124, 13746021, -1742048},
|
||||
{28584902, 7787108, -6732942, -15050729, 22846041, -7571236, -3181936, -363524, 4771362, -8419958}}
|
||||
}, {
|
||||
{{24949256, 6376279, -27466481, -8174608, -18646154, -9930606, 33543569, -12141695, 3569627, 11342593},
|
||||
{26514989, 4740088, 27912651, 3697550, 19331575, -11472339, 6809886, 4608608, 7325975, -14801071},
|
||||
{-11618399, -14554430, -24321212, 7655128, -1369274, 5214312, -27400540, 10258390, -17646694, -8186692}},
|
||||
{{11431204, 15823007, 26570245, 14329124, 18029990, 4796082, -31446179, 15580664, 9280358, -3973687},
|
||||
{-160783, -10326257, -22855316, -4304997, -20861367, -13621002, -32810901, -11181622, -15545091, 4387441},
|
||||
{-20799378, 12194512, 3937617, -5805892, -27154820, 9340370, -24513992, 8548137, 20617071, -7482001}},
|
||||
{{-938825, -3930586, -8714311, 16124718, 24603125, -6225393, -13775352, -11875822, 24345683, 10325460},
|
||||
{-19855277, -1568885, -22202708, 8714034, 14007766, 6928528, 16318175, -1010689, 4766743, 3552007},
|
||||
{-21751364, -16730916, 1351763, -803421, -4009670, 3950935, 3217514, 14481909, 10988822, -3994762}},
|
||||
{{15564307, -14311570, 3101243, 5684148, 30446780, -8051356, 12677127, -6505343, -8295852, 13296005},
|
||||
{-9442290, 6624296, -30298964, -11913677, -4670981, -2057379, 31521204, 9614054, -30000824, 12074674},
|
||||
{4771191, -135239, 14290749, -13089852, 27992298, 14998318, -1413936, -1556716, 29832613, -16391035}},
|
||||
{{7064884, -7541174, -19161962, -5067537, -18891269, -2912736, 25825242, 5293297, -27122660, 13101590},
|
||||
{-2298563, 2439670, -7466610, 1719965, -27267541, -16328445, 32512469, -5317593, -30356070, -4190957},
|
||||
{-30006540, 10162316, -33180176, 3981723, -16482138, -13070044, 14413974, 9515896, 19568978, 9628812}},
|
||||
{{33053803, 199357, 15894591, 1583059, 27380243, -4580435, -17838894, -6106839, -6291786, 3437740},
|
||||
{-18978877, 3884493, 19469877, 12726490, 15913552, 13614290, -22961733, 70104, 7463304, 4176122},
|
||||
{-27124001, 10659917, 11482427, -16070381, 12771467, -6635117, -32719404, -5322751, 24216882, 5944158}},
|
||||
{{8894125, 7450974, -2664149, -9765752, -28080517, -12389115, 19345746, 14680796, 11632993, 5847885},
|
||||
{26942781, -2315317, 9129564, -4906607, 26024105, 11769399, -11518837, 6367194, -9727230, 4782140},
|
||||
{19916461, -4828410, -22910704, -11414391, 25606324, -5972441, 33253853, 8220911, 6358847, -1873857}},
|
||||
{{801428, -2081702, 16569428, 11065167, 29875704, 96627, 7908388, -4480480, -13538503, 1387155},
|
||||
{19646058, 5720633, -11416706, 12814209, 11607948, 12749789, 14147075, 15156355, -21866831, 11835260},
|
||||
{19299512, 1155910, 28703737, 14890794, 2925026, 7269399, 26121523, 15467869, -26560550, 5052483}}
|
||||
}, {
|
||||
{{-3017432, 10058206, 1980837, 3964243, 22160966, 12322533, -6431123, -12618185, 12228557, -7003677},
|
||||
{32944382, 14922211, -22844894, 5188528, 21913450, -8719943, 4001465, 13238564, -6114803, 8653815},
|
||||
{22865569, -4652735, 27603668, -12545395, 14348958, 8234005, 24808405, 5719875, 28483275, 2841751}},
|
||||
{{-16420968, -1113305, -327719, -12107856, 21886282, -15552774, -1887966, -315658, 19932058, -12739203},
|
||||
{-11656086, 10087521, -8864888, -5536143, -19278573, -3055912, 3999228, 13239134, -4777469, -13910208},
|
||||
{1382174, -11694719, 17266790, 9194690, -13324356, 9720081, 20403944, 11284705, -14013818, 3093230}},
|
||||
{{16650921, -11037932, -1064178, 1570629, -8329746, 7352753, -302424, 16271225, -24049421, -6691850},
|
||||
{-21911077, -5927941, -4611316, -5560156, -31744103, -10785293, 24123614, 15193618, -21652117, -16739389},
|
||||
{-9935934, -4289447, -25279823, 4372842, 2087473, 10399484, 31870908, 14690798, 17361620, 11864968}},
|
||||
{{-11307610, 6210372, 13206574, 5806320, -29017692, -13967200, -12331205, -7486601, -25578460, -16240689},
|
||||
{14668462, -12270235, 26039039, 15305210, 25515617, 4542480, 10453892, 6577524, 9145645, -6443880},
|
||||
{5974874, 3053895, -9433049, -10385191, -31865124, 3225009, -7972642, 3936128, -5652273, -3050304}},
|
||||
{{30625386, -4729400, -25555961, -12792866, -20484575, 7695099, 17097188, -16303496, -27999779, 1803632},
|
||||
{-3553091, 9865099, -5228566, 4272701, -5673832, -16689700, 14911344, 12196514, -21405489, 7047412},
|
||||
{20093277, 9920966, -11138194, -5343857, 13161587, 12044805, -32856851, 4124601, -32343828, -10257566}},
|
||||
{{-20788824, 14084654, -13531713, 7842147, 19119038, -13822605, 4752377, -8714640, -21679658, 2288038},
|
||||
{-26819236, -3283715, 29965059, 3039786, -14473765, 2540457, 29457502, 14625692, -24819617, 12570232},
|
||||
{-1063558, -11551823, 16920318, 12494842, 1278292, -5869109, -21159943, -3498680, -11974704, 4724943}},
|
||||
{{17960970, -11775534, -4140968, -9702530, -8876562, -1410617, -12907383, -8659932, -29576300, 1903856},
|
||||
{23134274, -14279132, -10681997, -1611936, 20684485, 15770816, -12989750, 3190296, 26955097, 14109738},
|
||||
{15308788, 5320727, -30113809, -14318877, 22902008, 7767164, 29425325, -11277562, 31960942, 11934971}},
|
||||
{{-27395711, 8435796, 4109644, 12222639, -24627868, 14818669, 20638173, 4875028, 10491392, 1379718},
|
||||
{-13159415, 9197841, 3875503, -8936108, -1383712, -5879801, 33518459, 16176658, 21432314, 12180697},
|
||||
{-11787308, 11500838, 13787581, -13832590, -22430679, 10140205, 1465425, 12689540, -10301319, -13872883}}
|
||||
}, {
|
||||
{{5414091, -15386041, -21007664, 9643570, 12834970, 1186149, -2622916, -1342231, 26128231, 6032912},
|
||||
{-26337395, -13766162, 32496025, -13653919, 17847801, -12669156, 3604025, 8316894, -25875034, -10437358},
|
||||
{3296484, 6223048, 24680646, -12246460, -23052020, 5903205, -8862297, -4639164, 12376617, 3188849}},
|
||||
{{29190488, -14659046, 27549113, -1183516, 3520066, -10697301, 32049515, -7309113, -16109234, -9852307},
|
||||
{-14744486, -9309156, 735818, -598978, -20407687, -5057904, 25246078, -15795669, 18640741, -960977},
|
||||
{-6928835, -16430795, 10361374, 5642961, 4910474, 12345252, -31638386, -494430, 10530747, 1053335}},
|
||||
{{-29265967, -14186805, -13538216, -12117373, -19457059, -10655384, -31462369, -2948985, 24018831, 15026644},
|
||||
{-22592535, -3145277, -2289276, 5953843, -13440189, 9425631, 25310643, 13003497, -2314791, -15145616},
|
||||
{-27419985, -603321, -8043984, -1669117, -26092265, 13987819, -27297622, 187899, -23166419, -2531735}},
|
||||
{{-21744398, -13810475, 1844840, 5021428, -10434399, -15911473, 9716667, 16266922, -5070217, 726099},
|
||||
{29370922, -6053998, 7334071, -15342259, 9385287, 2247707, -13661962, -4839461, 30007388, -15823341},
|
||||
{-936379, 16086691, 23751945, -543318, -1167538, -5189036, 9137109, 730663, 9835848, 4555336}},
|
||||
{{-23376435, 1410446, -22253753, -12899614, 30867635, 15826977, 17693930, 544696, -11985298, 12422646},
|
||||
{31117226, -12215734, -13502838, 6561947, -9876867, -12757670, -5118685, -4096706, 29120153, 13924425},
|
||||
{-17400879, -14233209, 19675799, -2734756, -11006962, -5858820, -9383939, -11317700, 7240931, -237388}},
|
||||
{{-31361739, -11346780, -15007447, -5856218, -22453340, -12152771, 1222336, 4389483, 3293637, -15551743},
|
||||
{-16684801, -14444245, 11038544, 11054958, -13801175, -3338533, -24319580, 7733547, 12796905, -6335822},
|
||||
{-8759414, -10817836, -25418864, 10783769, -30615557, -9746811, -28253339, 3647836, 3222231, -11160462}},
|
||||
{{18606113, 1693100, -25448386, -15170272, 4112353, 10045021, 23603893, -2048234, -7550776, 2484985},
|
||||
{9255317, -3131197, -12156162, -1004256, 13098013, -9214866, 16377220, -2102812, -19802075, -3034702},
|
||||
{-22729289, 7496160, -5742199, 11329249, 19991973, -3347502, -31718148, 9936966, -30097688, -10618797}},
|
||||
{{21878590, -5001297, 4338336, 13643897, -3036865, 13160960, 19708896, 5415497, -7360503, -4109293},
|
||||
{27736861, 10103576, 12500508, 8502413, -3413016, -9633558, 10436918, -1550276, -23659143, -8132100},
|
||||
{19492550, -12104365, -29681976, -852630, -3208171, 12403437, 30066266, 8367329, 13243957, 8709688}}
|
||||
}, {
|
||||
{{12015105, 2801261, 28198131, 10151021, 24818120, -4743133, -11194191, -5645734, 5150968, 7274186},
|
||||
{2831366, -12492146, 1478975, 6122054, 23825128, -12733586, 31097299, 6083058, 31021603, -9793610},
|
||||
{-2529932, -2229646, 445613, 10720828, -13849527, -11505937, -23507731, 16354465, 15067285, -14147707}},
|
||||
{{7840942, 14037873, -33364863, 15934016, -728213, -3642706, 21403988, 1057586, -19379462, -12403220},
|
||||
{915865, -16469274, 15608285, -8789130, -24357026, 6060030, -17371319, 8410997, -7220461, 16527025},
|
||||
{32922597, -556987, 20336074, -16184568, 10903705, -5384487, 16957574, 52992, 23834301, 6588044}},
|
||||
{{32752030, 11232950, 3381995, -8714866, 22652988, -10744103, 17159699, 16689107, -20314580, -1305992},
|
||||
{-4689649, 9166776, -25710296, -10847306, 11576752, 12733943, 7924251, -2752281, 1976123, -7249027},
|
||||
{21251222, 16309901, -2983015, -6783122, 30810597, 12967303, 156041, -3371252, 12331345, -8237197}},
|
||||
{{8651614, -4477032, -16085636, -4996994, 13002507, 2950805, 29054427, -5106970, 10008136, -4667901},
|
||||
{31486080, 15114593, -14261250, 12951354, 14369431, -7387845, 16347321, -13662089, 8684155, -10532952},
|
||||
{19443825, 11385320, 24468943, -9659068, -23919258, 2187569, -26263207, -6086921, 31316348, 14219878}},
|
||||
{{-28594490, 1193785, 32245219, 11392485, 31092169, 15722801, 27146014, 6992409, 29126555, 9207390},
|
||||
{32382935, 1110093, 18477781, 11028262, -27411763, -7548111, -4980517, 10843782, -7957600, -14435730},
|
||||
{2814918, 7836403, 27519878, -7868156, -20894015, -11553689, -21494559, 8550130, 28346258, 1994730}},
|
||||
{{-19578299, 8085545, -14000519, -3948622, 2785838, -16231307, -19516951, 7174894, 22628102, 8115180},
|
||||
{-30405132, 955511, -11133838, -15078069, -32447087, -13278079, -25651578, 3317160, -9943017, 930272},
|
||||
{-15303681, -6833769, 28856490, 1357446, 23421993, 1057177, 24091212, -1388970, -22765376, -10650715}},
|
||||
{{-22751231, -5303997, -12907607, -12768866, -15811511, -7797053, -14839018, -16554220, -1867018, 8398970},
|
||||
{-31969310, 2106403, -4736360, 1362501, 12813763, 16200670, 22981545, -6291273, 18009408, -15772772},
|
||||
{-17220923, -9545221, -27784654, 14166835, 29815394, 7444469, 29551787, -3727419, 19288549, 1325865}},
|
||||
{{15100157, -15835752, -23923978, -1005098, -26450192, 15509408, 12376730, -3479146, 33166107, -8042750},
|
||||
{20909231, 13023121, -9209752, 16251778, -5778415, -8094914, 12412151, 10018715, 2213263, -13878373},
|
||||
{32529814, -11074689, 30361439, -16689753, -9135940, 1513226, 22922121, 6382134, -5766928, 8371348}}
|
||||
}, {
|
||||
{{9923462, 11271500, 12616794, 3544722, -29998368, -1721626, 12891687, -8193132, -26442943, 10486144},
|
||||
{-22597207, -7012665, 8587003, -8257861, 4084309, -12970062, 361726, 2610596, -23921530, -11455195},
|
||||
{5408411, -1136691, -4969122, 10561668, 24145918, 14240566, 31319731, -4235541, 19985175, -3436086}},
|
||||
{{-13994457, 16616821, 14549246, 3341099, 32155958, 13648976, -17577068, 8849297, 65030, 8370684},
|
||||
{-8320926, -12049626, 31204563, 5839400, -20627288, -1057277, -19442942, 6922164, 12743482, -9800518},
|
||||
{-2361371, 12678785, 28815050, 4759974, -23893047, 4884717, 23783145, 11038569, 18800704, 255233}},
|
||||
{{-5269658, -1773886, 13957886, 7990715, 23132995, 728773, 13393847, 9066957, 19258688, -14753793},
|
||||
{-2936654, -10827535, -10432089, 14516793, -3640786, 4372541, -31934921, 2209390, -1524053, 2055794},
|
||||
{580882, 16705327, 5468415, -2683018, -30926419, -14696000, -7203346, -8994389, -30021019, 7394435}},
|
||||
{{23838809, 1822728, -15738443, 15242727, 8318092, -3733104, -21672180, -3492205, -4821741, 14799921},
|
||||
{13345610, 9759151, 3371034, -16137791, 16353039, 8577942, 31129804, 13496856, -9056018, 7402518},
|
||||
{2286874, -4435931, -20042458, -2008336, -13696227, 5038122, 11006906, -15760352, 8205061, 1607563}},
|
||||
{{14414086, -8002132, 3331830, -3208217, 22249151, -5594188, 18364661, -2906958, 30019587, -9029278},
|
||||
{-27688051, 1585953, -10775053, 931069, -29120221, -11002319, -14410829, 12029093, 9944378, 8024},
|
||||
{4368715, -3709630, 29874200, -15022983, -20230386, -11410704, -16114594, -999085, -8142388, 5640030}},
|
||||
{{10299610, 13746483, 11661824, 16234854, 7630238, 5998374, 9809887, -16694564, 15219798, -14327783},
|
||||
{27425505, -5719081, 3055006, 10660664, 23458024, 595578, -15398605, -1173195, -18342183, 9742717},
|
||||
{6744077, 2427284, 26042789, 2720740, -847906, 1118974, 32324614, 7406442, 12420155, 1994844}},
|
||||
{{14012521, -5024720, -18384453, -9578469, -26485342, -3936439, -13033478, -10909803, 24319929, -6446333},
|
||||
{16412690, -4507367, 10772641, 15929391, -17068788, -4658621, 10555945, -10484049, -30102368, -4739048},
|
||||
{22397382, -7767684, -9293161, -12792868, 17166287, -9755136, -27333065, 6199366, 21880021, -12250760}},
|
||||
{{-4283307, 5368523, -31117018, 8163389, -30323063, 3209128, 16557151, 8890729, 8840445, 4957760},
|
||||
{-15447727, 709327, -6919446, -10870178, -29777922, 6522332, -21720181, 12130072, -14796503, 5005757},
|
||||
{-2114751, -14308128, 23019042, 15765735, -25269683, 6002752, 10183197, -13239326, -16395286, -2176112}}
|
||||
}, {
|
||||
{{-19025756, 1632005, 13466291, -7995100, -23640451, 16573537, -32013908, -3057104, 22208662, 2000468},
|
||||
{3065073, -1412761, -25598674, -361432, -17683065, -5703415, -8164212, 11248527, -3691214, -7414184},
|
||||
{10379208, -6045554, 8877319, 1473647, -29291284, -12507580, 16690915, 2553332, -3132688, 16400289}},
|
||||
{{15716668, 1254266, -18472690, 7446274, -8448918, 6344164, -22097271, -7285580, 26894937, 9132066},
|
||||
{24158887, 12938817, 11085297, -8177598, -28063478, -4457083, -30576463, 64452, -6817084, -2692882},
|
||||
{13488534, 7794716, 22236231, 5989356, 25426474, -12578208, 2350710, -3418511, -4688006, 2364226}},
|
||||
{{16335052, 9132434, 25640582, 6678888, 1725628, 8517937, -11807024, -11697457, 15445875, -7798101},
|
||||
{29004207, -7867081, 28661402, -640412, -12794003, -7943086, 31863255, -4135540, -278050, -15759279},
|
||||
{-6122061, -14866665, -28614905, 14569919, -10857999, -3591829, 10343412, -6976290, -29828287, -10815811}},
|
||||
{{27081650, 3463984, 14099042, -4517604, 1616303, -6205604, 29542636, 15372179, 17293797, 960709},
|
||||
{20263915, 11434237, -5765435, 11236810, 13505955, -10857102, -16111345, 6493122, -19384511, 7639714},
|
||||
{-2830798, -14839232, 25403038, -8215196, -8317012, -16173699, 18006287, -16043750, 29994677, -15808121}},
|
||||
{{9769828, 5202651, -24157398, -13631392, -28051003, -11561624, -24613141, -13860782, -31184575, 709464},
|
||||
{12286395, 13076066, -21775189, -1176622, -25003198, 4057652, -32018128, -8890874, 16102007, 13205847},
|
||||
{13733362, 5599946, 10557076, 3195751, -5557991, 8536970, -25540170, 8525972, 10151379, 10394400}},
|
||||
{{4024660, -16137551, 22436262, 12276534, -9099015, -2686099, 19698229, 11743039, -33302334, 8934414},
|
||||
{-15879800, -4525240, -8580747, -2934061, 14634845, -698278, -9449077, 3137094, -11536886, 11721158},
|
||||
{17555939, -5013938, 8268606, 2331751, -22738815, 9761013, 9319229, 8835153, -9205489, -1280045}},
|
||||
{{-461409, -7830014, 20614118, 16688288, -7514766, -4807119, 22300304, 505429, 6108462, -6183415},
|
||||
{-5070281, 12367917, -30663534, 3234473, 32617080, -8422642, 29880583, -13483331, -26898490, -7867459},
|
||||
{-31975283, 5726539, 26934134, 10237677, -3173717, -605053, 24199304, 3795095, 7592688, -14992079}},
|
||||
{{21594432, -14964228, 17466408, -4077222, 32537084, 2739898, 6407723, 12018833, -28256052, 4298412},
|
||||
{-20650503, -11961496, -27236275, 570498, 3767144, -1717540, 13891942, -1569194, 13717174, 10805743},
|
||||
{-14676630, -15644296, 15287174, 11927123, 24177847, -8175568, -796431, 14860609, -26938930, -5863836}}
|
||||
}, {
|
||||
{{12962541, 5311799, -10060768, 11658280, 18855286, -7954201, 13286263, -12808704, -4381056, 9882022},
|
||||
{18512079, 11319350, -20123124, 15090309, 18818594, 5271736, -22727904, 3666879, -23967430, -3299429},
|
||||
{-6789020, -3146043, 16192429, 13241070, 15898607, -14206114, -10084880, -6661110, -2403099, 5276065}},
|
||||
{{30169808, -5317648, 26306206, -11750859, 27814964, 7069267, 7152851, 3684982, 1449224, 13082861},
|
||||
{10342826, 3098505, 2119311, 193222, 25702612, 12233820, 23697382, 15056736, -21016438, -8202000},
|
||||
{-33150110, 3261608, 22745853, 7948688, 19370557, -15177665, -26171976, 6482814, -10300080, -11060101}},
|
||||
{{32869458, -5408545, 25609743, 15678670, -10687769, -15471071, 26112421, 2521008, -22664288, 6904815},
|
||||
{29506923, 4457497, 3377935, -9796444, -30510046, 12935080, 1561737, 3841096, -29003639, -6657642},
|
||||
{10340844, -6630377, -18656632, -2278430, 12621151, -13339055, 30878497, -11824370, -25584551, 5181966}},
|
||||
{{25940115, -12658025, 17324188, -10307374, -8671468, 15029094, 24396252, -16450922, -2322852, -12388574},
|
||||
{-21765684, 9916823, -1300409, 4079498, -1028346, 11909559, 1782390, 12641087, 20603771, -6561742},
|
||||
{-18882287, -11673380, 24849422, 11501709, 13161720, -4768874, 1925523, 11914390, 4662781, 7820689}},
|
||||
{{12241050, -425982, 8132691, 9393934, 32846760, -1599620, 29749456, 12172924, 16136752, 15264020},
|
||||
{-10349955, -14680563, -8211979, 2330220, -17662549, -14545780, 10658213, 6671822, 19012087, 3772772},
|
||||
{3753511, -3421066, 10617074, 2028709, 14841030, -6721664, 28718732, -15762884, 20527771, 12988982}},
|
||||
{{-14822485, -5797269, -3707987, 12689773, -898983, -10914866, -24183046, -10564943, 3299665, -12424953},
|
||||
{-16777703, -15253301, -9642417, 4978983, 3308785, 8755439, 6943197, 6461331, -25583147, 8991218},
|
||||
{-17226263, 1816362, -1673288, -6086439, 31783888, -8175991, -32948145, 7417950, -30242287, 1507265}},
|
||||
{{29692663, 6829891, -10498800, 4334896, 20945975, -11906496, -28887608, 8209391, 14606362, -10647073},
|
||||
{-3481570, 8707081, 32188102, 5672294, 22096700, 1711240, -33020695, 9761487, 4170404, -2085325},
|
||||
{-11587470, 14855945, -4127778, -1531857, -26649089, 15084046, 22186522, 16002000, -14276837, -8400798}},
|
||||
{{-4811456, 13761029, -31703877, -2483919, -3312471, 7869047, -7113572, -9620092, 13240845, 10965870},
|
||||
{-7742563, -8256762, -14768334, -13656260, -23232383, 12387166, 4498947, 14147411, 29514390, 4302863},
|
||||
{-13413405, -12407859, 20757302, -13801832, 14785143, 8976368, -5061276, -2144373, 17846988, -13971927}}
|
||||
}, {
|
||||
{{-2244452, -754728, -4597030, -1066309, -6247172, 1455299, -21647728, -9214789, -5222701, 12650267},
|
||||
{-9906797, -16070310, 21134160, 12198166, -27064575, 708126, 387813, 13770293, -19134326, 10958663},
|
||||
{22470984, 12369526, 23446014, -5441109, -21520802, -9698723, -11772496, -11574455, -25083830, 4271862}},
|
||||
{{-25169565, -10053642, -19909332, 15361595, -5984358, 2159192, 75375, -4278529, -32526221, 8469673},
|
||||
{15854970, 4148314, -8893890, 7259002, 11666551, 13824734, -30531198, 2697372, 24154791, -9460943},
|
||||
{15446137, -15806644, 29759747, 14019369, 30811221, -9610191, -31582008, 12840104, 24913809, 9815020}},
|
||||
{{-4709286, -5614269, -31841498, -12288893, -14443537, 10799414, -9103676, 13438769, 18735128, 9466238},
|
||||
{11933045, 9281483, 5081055, -5183824, -2628162, -4905629, -7727821, -10896103, -22728655, 16199064},
|
||||
{14576810, 379472, -26786533, -8317236, -29426508, -10812974, -102766, 1876699, 30801119, 2164795}},
|
||||
{{15995086, 3199873, 13672555, 13712240, -19378835, -4647646, -13081610, -15496269, -13492807, 1268052},
|
||||
{-10290614, -3659039, -3286592, 10948818, 23037027, 3794475, -3470338, -12600221, -17055369, 3565904},
|
||||
{29210088, -9419337, -5919792, -4952785, 10834811, -13327726, -16512102, -10820713, -27162222, -14030531}},
|
||||
{{-13161890, 15508588, 16663704, -8156150, -28349942, 9019123, -29183421, -3769423, 2244111, -14001979},
|
||||
{-5152875, -3800936, -9306475, -6071583, 16243069, 14684434, -25673088, -16180800, 13491506, 4641841},
|
||||
{10813417, 643330, -19188515, -728916, 30292062, -16600078, 27548447, -7721242, 14476989, -12767431}},
|
||||
{{10292079, 9984945, 6481436, 8279905, -7251514, 7032743, 27282937, -1644259, -27912810, 12651324},
|
||||
{-31185513, -813383, 22271204, 11835308, 10201545, 15351028, 17099662, 3988035, 21721536, -3148940},
|
||||
{10202177, -6545839, -31373232, -9574638, -32150642, -8119683, -12906320, 3852694, 13216206, 14842320}},
|
||||
{{-15815640, -10601066, -6538952, -7258995, -6984659, -6581778, -31500847, 13765824, -27434397, 9900184},
|
||||
{14465505, -13833331, -32133984, -14738873, -27443187, 12990492, 33046193, 15796406, -7051866, -8040114},
|
||||
{30924417, -8279620, 6359016, -12816335, 16508377, 9071735, -25488601, 15413635, 9524356, -7018878}},
|
||||
{{12274201, -13175547, 32627641, -1785326, 6736625, 13267305, 5237659, -5109483, 15663516, 4035784},
|
||||
{-2951309, 8903985, 17349946, 601635, -16432815, -4612556, -13732739, -15889334, -22258478, 4659091},
|
||||
{-16916263, -4952973, -30393711, -15158821, 20774812, 15897498, 5736189, 15026997, -2178256, -13455585}}
|
||||
}, {
|
||||
{{-8858980, -2219056, 28571666, -10155518, -474467, -10105698, -3801496, 278095, 23440562, -290208},
|
||||
{10226241, -5928702, 15139956, 120818, -14867693, 5218603, 32937275, 11551483, -16571960, -7442864},
|
||||
{17932739, -12437276, -24039557, 10749060, 11316803, 7535897, 22503767, 5561594, -3646624, 3898661}},
|
||||
{{7749907, -969567, -16339731, -16464, -25018111, 15122143, -1573531, 7152530, 21831162, 1245233},
|
||||
{26958459, -14658026, 4314586, 8346991, -5677764, 11960072, -32589295, -620035, -30402091, -16716212},
|
||||
{-12165896, 9166947, 33491384, 13673479, 29787085, 13096535, 6280834, 14587357, -22338025, 13987525}},
|
||||
{{-24349909, 7778775, 21116000, 15572597, -4833266, -5357778, -4300898, -5124639, -7469781, -2858068},
|
||||
{9681908, -6737123, -31951644, 13591838, -6883821, 386950, 31622781, 6439245, -14581012, 4091397},
|
||||
{-8426427, 1470727, -28109679, -1596990, 3978627, -5123623, -19622683, 12092163, 29077877, -14741988}},
|
||||
{{5269168, -6859726, -13230211, -8020715, 25932563, 1763552, -5606110, -5505881, -20017847, 2357889},
|
||||
{32264008, -15407652, -5387735, -1160093, -2091322, -3946900, 23104804, -12869908, 5727338, 189038},
|
||||
{14609123, -8954470, -6000566, -16622781, -14577387, -7743898, -26745169, 10942115, -25888931, -14884697}},
|
||||
{{20513500, 5557931, -15604613, 7829531, 26413943, -2019404, -21378968, 7471781, 13913677, -5137875},
|
||||
{-25574376, 11967826, 29233242, 12948236, -6754465, 4713227, -8940970, 14059180, 12878652, 8511905},
|
||||
{-25656801, 3393631, -2955415, -7075526, -2250709, 9366908, -30223418, 6812974, 5568676, -3127656}},
|
||||
{{11630004, 12144454, 2116339, 13606037, 27378885, 15676917, -17408753, -13504373, -14395196, 8070818},
|
||||
{27117696, -10007378, -31282771, -5570088, 1127282, 12772488, -29845906, 10483306, -11552749, -1028714},
|
||||
{10637467, -5688064, 5674781, 1072708, -26343588, -6982302, -1683975, 9177853, -27493162, 15431203}},
|
||||
{{20525145, 10892566, -12742472, 12779443, -29493034, 16150075, -28240519, 14943142, -15056790, -7935931},
|
||||
{-30024462, 5626926, -551567, -9981087, 753598, 11981191, 25244767, -3239766, -3356550, 9594024},
|
||||
{-23752644, 2636870, -5163910, -10103818, 585134, 7877383, 11345683, -6492290, 13352335, -10977084}},
|
||||
{{-1931799, -5407458, 3304649, -12884869, 17015806, -4877091, -29783850, -7752482, -13215537, -319204},
|
||||
{20239939, 6607058, 6203985, 3483793, -18386976, -779229, -20723742, 15077870, -22750759, 14523817},
|
||||
{27406042, -6041657, 27423596, -4497394, 4996214, 10002360, -28842031, -4545494, -30172742, -4805667}}
|
||||
}, {
|
||||
{{11374242, 12660715, 17861383, -12540833, 10935568, 1099227, -13886076, -9091740, -27727044, 11358504},
|
||||
{-12730809, 10311867, 1510375, 10778093, -2119455, -9145702, 32676003, 11149336, -26123651, 4985768},
|
||||
{-19096303, 341147, -6197485, -239033, 15756973, -8796662, -983043, 13794114, -19414307, -15621255}},
|
||||
{{6490081, 11940286, 25495923, -7726360, 8668373, -8751316, 3367603, 6970005, -1691065, -9004790},
|
||||
{1656497, 13457317, 15370807, 6364910, 13605745, 8362338, -19174622, -5475723, -16796596, -5031438},
|
||||
{-22273315, -13524424, -64685, -4334223, -18605636, -10921968, -20571065, -7007978, -99853, -10237333}},
|
||||
{{17747465, 10039260, 19368299, -4050591, -20630635, -16041286, 31992683, -15857976, -29260363, -5511971},
|
||||
{31932027, -4986141, -19612382, 16366580, 22023614, 88450, 11371999, -3744247, 4882242, -10626905},
|
||||
{29796507, 37186, 19818052, 10115756, -11829032, 3352736, 18551198, 3272828, -5190932, -4162409}},
|
||||
{{12501286, 4044383, -8612957, -13392385, -32430052, 5136599, -19230378, -3529697, 330070, -3659409},
|
||||
{6384877, 2899513, 17807477, 7663917, -2358888, 12363165, 25366522, -8573892, -271295, 12071499},
|
||||
{-8365515, -4042521, 25133448, -4517355, -6211027, 2265927, -32769618, 1936675, -5159697, 3829363}},
|
||||
{{28425966, -5835433, -577090, -4697198, -14217555, 6870930, 7921550, -6567787, 26333140, 14267664},
|
||||
{-11067219, 11871231, 27385719, -10559544, -4585914, -11189312, 10004786, -8709488, -21761224, 8930324},
|
||||
{-21197785, -16396035, 25654216, -1725397, 12282012, 11008919, 1541940, 4757911, -26491501, -16408940}},
|
||||
{{13537262, -7759490, -20604840, 10961927, -5922820, -13218065, -13156584, 6217254, -15943699, 13814990},
|
||||
{-17422573, 15157790, 18705543, 29619, 24409717, -260476, 27361681, 9257833, -1956526, -1776914},
|
||||
{-25045300, -10191966, 15366585, 15166509, -13105086, 8423556, -29171540, 12361135, -18685978, 4578290}},
|
||||
{{24579768, 3711570, 1342322, -11180126, -27005135, 14124956, -22544529, 14074919, 21964432, 8235257},
|
||||
{-6528613, -2411497, 9442966, -5925588, 12025640, -1487420, -2981514, -1669206, 13006806, 2355433},
|
||||
{-16304899, -13605259, -6632427, -5142349, 16974359, -10911083, 27202044, 1719366, 1141648, -12796236}},
|
||||
{{-12863944, -13219986, -8318266, -11018091, -6810145, -4843894, 13475066, -3133972, 32674895, 13715045},
|
||||
{11423335, -5468059, 32344216, 8962751, 24989809, 9241752, -13265253, 16086212, -28740881, -15642093},
|
||||
{-1409668, 12530728, -6368726, 10847387, 19531186, -14132160, -11709148, 7791794, -27245943, 4383347}}
|
||||
}, {
|
||||
{{-28970898, 5271447, -1266009, -9736989, -12455236, 16732599, -4862407, -4906449, 27193557, 6245191},
|
||||
{-15193956, 5362278, -1783893, 2695834, 4960227, 12840725, 23061898, 3260492, 22510453, 8577507},
|
||||
{-12632451, 11257346, -32692994, 13548177, -721004, 10879011, 31168030, 13952092, -29571492, -3635906}},
|
||||
{{3877321, -9572739, 32416692, 5405324, -11004407, -13656635, 3759769, 11935320, 5611860, 8164018},
|
||||
{-16275802, 14667797, 15906460, 12155291, -22111149, -9039718, 32003002, -8832289, 5773085, -8422109},
|
||||
{-23788118, -8254300, 1950875, 8937633, 18686727, 16459170, -905725, 12376320, 31632953, 190926}},
|
||||
{{-24593607, -16138885, -8423991, 13378746, 14162407, 6901328, -8288749, 4508564, -25341555, -3627528},
|
||||
{8884438, -5884009, 6023974, 10104341, -6881569, -4941533, 18722941, -14786005, -1672488, 827625},
|
||||
{-32720583, -16289296, -32503547, 7101210, 13354605, 2659080, -1800575, -14108036, -24878478, 1541286}},
|
||||
{{2901347, -1117687, 3880376, -10059388, -17620940, -3612781, -21802117, -3567481, 20456845, -1885033},
|
||||
{27019610, 12299467, -13658288, -1603234, -12861660, -4861471, -19540150, -5016058, 29439641, 15138866},
|
||||
{21536104, -6626420, -32447818, -10690208, -22408077, 5175814, -5420040, -16361163, 7779328, 109896}},
|
||||
{{30279744, 14648750, -8044871, 6425558, 13639621, -743509, 28698390, 12180118, 23177719, -554075},
|
||||
{26572847, 3405927, -31701700, 12890905, -19265668, 5335866, -6493768, 2378492, 4439158, -13279347},
|
||||
{-22716706, 3489070, -9225266, -332753, 18875722, -1140095, 14819434, -12731527, -17717757, -5461437}},
|
||||
{{-5056483, 16566551, 15953661, 3767752, -10436499, 15627060, -820954, 2177225, 8550082, -15114165},
|
||||
{-18473302, 16596775, -381660, 15663611, 22860960, 15585581, -27844109, -3582739, -23260460, -8428588},
|
||||
{-32480551, 15707275, -8205912, -5652081, 29464558, 2713815, -22725137, 15860482, -21902570, 1494193}},
|
||||
{{-19562091, -14087393, -25583872, -9299552, 13127842, 759709, 21923482, 16529112, 8742704, 12967017},
|
||||
{-28464899, 1553205, 32536856, -10473729, -24691605, -406174, -8914625, -2933896, -29903758, 15553883},
|
||||
{21877909, 3230008, 9881174, 10539357, -4797115, 2841332, 11543572, 14513274, 19375923, -12647961}},
|
||||
{{8832269, -14495485, 13253511, 5137575, 5037871, 4078777, 24880818, -6222716, 2862653, 9455043},
|
||||
{29306751, 5123106, 20245049, -14149889, 9592566, 8447059, -2077124, -2990080, 15511449, 4789663},
|
||||
{-20679756, 7004547, 8824831, -9434977, -4045704, -3750736, -5754762, 108893, 23513200, 16652362}}
|
||||
}, {
|
||||
{{-33256173, 4144782, -4476029, -6579123, 10770039, -7155542, -6650416, -12936300, -18319198, 10212860},
|
||||
{2756081, 8598110, 7383731, -6859892, 22312759, -1105012, 21179801, 2600940, -9988298, -12506466},
|
||||
{-24645692, 13317462, -30449259, -15653928, 21365574, -10869657, 11344424, 864440, -2499677, -16710063}},
|
||||
{{-26432803, 6148329, -17184412, -14474154, 18782929, -275997, -22561534, 211300, 2719757, 4940997},
|
||||
{-1323882, 3911313, -6948744, 14759765, -30027150, 7851207, 21690126, 8518463, 26699843, 5276295},
|
||||
{-13149873, -6429067, 9396249, 365013, 24703301, -10488939, 1321586, 149635, -15452774, 7159369}},
|
||||
{{9987780, -3404759, 17507962, 9505530, 9731535, -2165514, 22356009, 8312176, 22477218, -8403385},
|
||||
{18155857, -16504990, 19744716, 9006923, 15154154, -10538976, 24256460, -4864995, -22548173, 9334109},
|
||||
{2986088, -4911893, 10776628, -3473844, 10620590, -7083203, -21413845, 14253545, -22587149, 536906}},
|
||||
{{4377756, 8115836, 24567078, 15495314, 11625074, 13064599, 7390551, 10589625, 10838060, -15420424},
|
||||
{-19342404, 867880, 9277171, -3218459, -14431572, -1986443, 19295826, -15796950, 6378260, 699185},
|
||||
{7895026, 4057113, -7081772, -13077756, -17886831, -323126, -716039, 15693155, -5045064, -13373962}},
|
||||
{{-7737563, -5869402, -14566319, -7406919, 11385654, 13201616, 31730678, -10962840, -3918636, -9669325},
|
||||
{10188286, -15770834, -7336361, 13427543, 22223443, 14896287, 30743455, 7116568, -21786507, 5427593},
|
||||
{696102, 13206899, 27047647, -10632082, 15285305, -9853179, 10798490, -4578720, 19236243, 12477404}},
|
||||
{{-11229439, 11243796, -17054270, -8040865, -788228, -8167967, -3897669, 11180504, -23169516, 7733644},
|
||||
{17800790, -14036179, -27000429, -11766671, 23887827, 3149671, 23466177, -10538171, 10322027, 15313801},
|
||||
{26246234, 11968874, 32263343, -5468728, 6830755, -13323031, -15794704, -101982, -24449242, 10890804}},
|
||||
{{-31365647, 10271363, -12660625, -6267268, 16690207, -13062544, -14982212, 16484931, 25180797, -5334884},
|
||||
{-586574, 10376444, -32586414, -11286356, 19801893, 10997610, 2276632, 9482883, 316878, 13820577},
|
||||
{-9882808, -4510367, -2115506, 16457136, -11100081, 11674996, 30756178, -7515054, 30696930, -3712849}},
|
||||
{{32988917, -9603412, 12499366, 7910787, -10617257, -11931514, -7342816, -9985397, -32349517, 7392473},
|
||||
{-8855661, 15927861, 9866406, -3649411, -2396914, -16655781, -30409476, -9134995, 25112947, -2926644},
|
||||
{-2504044, -436966, 25621774, -5678772, 15085042, -5479877, -24884878, -13526194, 5537438, -13914319}}
|
||||
}, {
|
||||
{{-11225584, 2320285, -9584280, 10149187, -33444663, 5808648, -14876251, -1729667, 31234590, 6090599},
|
||||
{-9633316, 116426, 26083934, 2897444, -6364437, -2688086, 609721, 15878753, -6970405, -9034768},
|
||||
{-27757857, 247744, -15194774, -9002551, 23288161, -10011936, -23869595, 6503646, 20650474, 1804084}},
|
||||
{{-27589786, 15456424, 8972517, 8469608, 15640622, 4439847, 3121995, -10329713, 27842616, -202328},
|
||||
{-15306973, 2839644, 22530074, 10026331, 4602058, 5048462, 28248656, 5031932, -11375082, 12714369},
|
||||
{20807691, -7270825, 29286141, 11421711, -27876523, -13868230, -21227475, 1035546, -19733229, 12796920}},
|
||||
{{12076899, -14301286, -8785001, -11848922, -25012791, 16400684, -17591495, -12899438, 3480665, -15182815},
|
||||
{-32361549, 5457597, 28548107, 7833186, 7303070, -11953545, -24363064, -15921875, -33374054, 2771025},
|
||||
{-21389266, 421932, 26597266, 6860826, 22486084, -6737172, -17137485, -4210226, -24552282, 15673397}},
|
||||
{{-20184622, 2338216, 19788685, -9620956, -4001265, -8740893, -20271184, 4733254, 3727144, -12934448},
|
||||
{6120119, 814863, -11794402, -622716, 6812205, -15747771, 2019594, 7975683, 31123697, -10958981},
|
||||
{30069250, -11435332, 30434654, 2958439, 18399564, -976289, 12296869, 9204260, -16432438, 9648165}},
|
||||
{{32705432, -1550977, 30705658, 7451065, -11805606, 9631813, 3305266, 5248604, -26008332, -11377501},
|
||||
{17219865, 2375039, -31570947, -5575615, -19459679, 9219903, 294711, 15298639, 2662509, -16297073},
|
||||
{-1172927, -7558695, -4366770, -4287744, -21346413, -8434326, 32087529, -1222777, 32247248, -14389861}},
|
||||
{{14312628, 1221556, 17395390, -8700143, -4945741, -8684635, -28197744, -9637817, -16027623, -13378845},
|
||||
{-1428825, -9678990, -9235681, 6549687, -7383069, -468664, 23046502, 9803137, 17597934, 2346211},
|
||||
{18510800, 15337574, 26171504, 981392, -22241552, 7827556, -23491134, -11323352, 3059833, -11782870}},
|
||||
{{10141598, 6082907, 17829293, -1947643, 9830092, 13613136, -25556636, -5544586, -33502212, 3592096},
|
||||
{33114168, -15889352, -26525686, -13343397, 33076705, 8716171, 1151462, 1521897, -982665, -6837803},
|
||||
{-32939165, -4255815, 23947181, -324178, -33072974, -12305637, -16637686, 3891704, 26353178, 693168}},
|
||||
{{30374239, 1595580, -16884039, 13186931, 4600344, 406904, 9585294, -400668, 31375464, 14369965},
|
||||
{-14370654, -7772529, 1510301, 6434173, -18784789, -6262728, 32732230, -13108839, 17901441, 16011505},
|
||||
{18171223, -11934626, -12500402, 15197122, -11038147, -15230035, -19172240, -16046376, 8764035, 12309598}}
|
||||
}, {
|
||||
{{5975908, -5243188, -19459362, -9681747, -11541277, 14015782, -23665757, 1228319, 17544096, -10593782},
|
||||
{5811932, -1715293, 3442887, -2269310, -18367348, -8359541, -18044043, -15410127, -5565381, 12348900},
|
||||
{-31399660, 11407555, 25755363, 6891399, -3256938, 14872274, -24849353, 8141295, -10632534, -585479}},
|
||||
{{-12675304, 694026, -5076145, 13300344, 14015258, -14451394, -9698672, -11329050, 30944593, 1130208},
|
||||
{8247766, -6710942, -26562381, -7709309, -14401939, -14648910, 4652152, 2488540, 23550156, -271232},
|
||||
{17294316, -3788438, 7026748, 15626851, 22990044, 113481, 2267737, -5908146, -408818, -137719}},
|
||||
{{16091085, -16253926, 18599252, 7340678, 2137637, -1221657, -3364161, 14550936, 3260525, -7166271},
|
||||
{-4910104, -13332887, 18550887, 10864893, -16459325, -7291596, -23028869, -13204905, -12748722, 2701326},
|
||||
{-8574695, 16099415, 4629974, -16340524, -20786213, -6005432, -10018363, 9276971, 11329923, 1862132}},
|
||||
{{14763076, -15903608, -30918270, 3689867, 3511892, 10313526, -21951088, 12219231, -9037963, -940300},
|
||||
{8894987, -3446094, 6150753, 3013931, 301220, 15693451, -31981216, -2909717, -15438168, 11595570},
|
||||
{15214962, 3537601, -26238722, -14058872, 4418657, -15230761, 13947276, 10730794, -13489462, -4363670}},
|
||||
{{-2538306, 7682793, 32759013, 263109, -29984731, -7955452, -22332124, -10188635, 977108, 699994},
|
||||
{-12466472, 4195084, -9211532, 550904, -15565337, 12917920, 19118110, -439841, -30534533, -14337913},
|
||||
{31788461, -14507657, 4799989, 7372237, 8808585, -14747943, 9408237, -10051775, 12493932, -5409317}},
|
||||
{{-25680606, 5260744, -19235809, -6284470, -3695942, 16566087, 27218280, 2607121, 29375955, 6024730},
|
||||
{842132, -2794693, -4763381, -8722815, 26332018, -12405641, 11831880, 6985184, -9940361, 2854096},
|
||||
{-4847262, -7969331, 2516242, -5847713, 9695691, -7221186, 16512645, 960770, 12121869, 16648078}},
|
||||
{{-15218652, 14667096, -13336229, 2013717, 30598287, -464137, -31504922, -7882064, 20237806, 2838411},
|
||||
{-19288047, 4453152, 15298546, -16178388, 22115043, -15972604, 12544294, -13470457, 1068881, -12499905},
|
||||
{-9558883, -16518835, 33238498, 13506958, 30505848, -1114596, -8486907, -2630053, 12521378, 4845654}},
|
||||
{{-28198521, 10744108, -2958380, 10199664, 7759311, -13088600, 3409348, -873400, -6482306, -12885870},
|
||||
{-23561822, 6230156, -20382013, 10655314, -24040585, -11621172, 10477734, -1240216, -3113227, 13974498},
|
||||
{12966261, 15550616, -32038948, -1615346, 21025980, -629444, 5642325, 7188737, 18895762, 12629579}}
|
||||
}, {
|
||||
{{14741879, -14946887, 22177208, -11721237, 1279741, 8058600, 11758140, 789443, 32195181, 3895677},
|
||||
{10758205, 15755439, -4509950, 9243698, -4879422, 6879879, -2204575, -3566119, -8982069, 4429647},
|
||||
{-2453894, 15725973, -20436342, -10410672, -5803908, -11040220, -7135870, -11642895, 18047436, -15281743}},
|
||||
{{-25173001, -11307165, 29759956, 11776784, -22262383, -15820455, 10993114, -12850837, -17620701, -9408468},
|
||||
{21987233, 700364, -24505048, 14972008, -7774265, -5718395, 32155026, 2581431, -29958985, 8773375},
|
||||
{-25568350, 454463, -13211935, 16126715, 25240068, 8594567, 20656846, 12017935, -7874389, -13920155}},
|
||||
{{6028182, 6263078, -31011806, -11301710, -818919, 2461772, -31841174, -5468042, -1721788, -2776725},
|
||||
{-12278994, 16624277, 987579, -5922598, 32908203, 1248608, 7719845, -4166698, 28408820, 6816612},
|
||||
{-10358094, -8237829, 19549651, -12169222, 22082623, 16147817, 20613181, 13982702, -10339570, 5067943}},
|
||||
{{-30505967, -3821767, 12074681, 13582412, -19877972, 2443951, -19719286, 12746132, 5331210, -10105944},
|
||||
{30528811, 3601899, -1957090, 4619785, -27361822, -15436388, 24180793, -12570394, 27679908, -1648928},
|
||||
{9402404, -13957065, 32834043, 10838634, -26580150, -13237195, 26653274, -8685565, 22611444, -12715406}},
|
||||
{{22190590, 1118029, 22736441, 15130463, -30460692, -5991321, 19189625, -4648942, 4854859, 6622139},
|
||||
{-8310738, -2953450, -8262579, -3388049, -10401731, -271929, 13424426, -3567227, 26404409, 13001963},
|
||||
{-31241838, -15415700, -2994250, 8939346, 11562230, -12840670, -26064365, -11621720, -15405155, 11020693}},
|
||||
{{1866042, -7949489, -7898649, -10301010, 12483315, 13477547, 3175636, -12424163, 28761762, 1406734},
|
||||
{-448555, -1777666, 13018551, 3194501, -9580420, -11161737, 24760585, -4347088, 25577411, -13378680},
|
||||
{-24290378, 4759345, -690653, -1852816, 2066747, 10693769, -29595790, 9884936, -9368926, 4745410}},
|
||||
{{-9141284, 6049714, -19531061, -4341411, -31260798, 9944276, -15462008, -11311852, 10931924, -11931931},
|
||||
{-16561513, 14112680, -8012645, 4817318, -8040464, -11414606, -22853429, 10856641, -20470770, 13434654},
|
||||
{22759489, -10073434, -16766264, -1871422, 13637442, -10168091, 1765144, -12654326, 28445307, -5364710}},
|
||||
{{29875063, 12493613, 2795536, -3786330, 1710620, 15181182, -10195717, -8788675, 9074234, 1167180},
|
||||
{-26205683, 11014233, -9842651, -2635485, -26908120, 7532294, -18716888, -9535498, 3843903, 9367684},
|
||||
{-10969595, -6403711, 9591134, 9582310, 11349256, 108879, 16235123, 8601684, -139197, 4242895}}
|
||||
}, {
|
||||
{{22092954, -13191123, -2042793, -11968512, 32186753, -11517388, -6574341, 2470660, -27417366, 16625501},
|
||||
{-11057722, 3042016, 13770083, -9257922, 584236, -544855, -7770857, 2602725, -27351616, 14247413},
|
||||
{6314175, -10264892, -32772502, 15957557, -10157730, 168750, -8618807, 14290061, 27108877, -1180880}},
|
||||
{{-8586597, -7170966, 13241782, 10960156, -32991015, -13794596, 33547976, -11058889, -27148451, 981874},
|
||||
{22833440, 9293594, -32649448, -13618667, -9136966, 14756819, -22928859, -13970780, -10479804, -16197962},
|
||||
{-7768587, 3326786, -28111797, 10783824, 19178761, 14905060, 22680049, 13906969, -15933690, 3797899}},
|
||||
{{21721356, -4212746, -12206123, 9310182, -3882239, -13653110, 23740224, -2709232, 20491983, -8042152},
|
||||
{9209270, -15135055, -13256557, -6167798, -731016, 15289673, 25947805, 15286587, 30997318, -6703063},
|
||||
{7392032, 16618386, 23946583, -8039892, -13265164, -1533858, -14197445, -2321576, 17649998, -250080}},
|
||||
{{-9301088, -14193827, 30609526, -3049543, -25175069, -1283752, -15241566, -9525724, -2233253, 7662146},
|
||||
{-17558673, 1763594, -33114336, 15908610, -30040870, -12174295, 7335080, -8472199, -3174674, 3440183},
|
||||
{-19889700, -5977008, -24111293, -9688870, 10799743, -16571957, 40450, -4431835, 4862400, 1133}},
|
||||
{{-32856209, -7873957, -5422389, 14860950, -16319031, 7956142, 7258061, 311861, -30594991, -7379421},
|
||||
{-3773428, -1565936, 28985340, 7499440, 24445838, 9325937, 29727763, 16527196, 18278453, 15405622},
|
||||
{-4381906, 8508652, -19898366, -3674424, -5984453, 15149970, -13313598, 843523, -21875062, 13626197}},
|
||||
{{2281448, -13487055, -10915418, -2609910, 1879358, 16164207, -10783882, 3953792, 13340839, 15928663},
|
||||
{31727126, -7179855, -18437503, -8283652, 2875793, -16390330, -25269894, -7014826, -23452306, 5964753},
|
||||
{4100420, -5959452, -17179337, 6017714, -18705837, 12227141, -26684835, 11344144, 2538215, -7570755}},
|
||||
{{-9433605, 6123113, 11159803, -2156608, 30016280, 14966241, -20474983, 1485421, -629256, -15958862},
|
||||
{-26804558, 4260919, 11851389, 9658551, -32017107, 16367492, -20205425, -13191288, 11659922, -11115118},
|
||||
{26180396, 10015009, -30844224, -8581293, 5418197, 9480663, 2231568, -10170080, 33100372, -1306171}},
|
||||
{{15121113, -5201871, -10389905, 15427821, -27509937, -15992507, 21670947, 4486675, -5931810, -14466380},
|
||||
{16166486, -9483733, -11104130, 6023908, -31926798, -1364923, 2340060, -16254968, -10735770, -10039824},
|
||||
{28042865, -3557089, -12126526, 12259706, -3717498, -6945899, 6766453, -8689599, 18036436, 5803270}}
|
||||
}, {
|
||||
{{-817581, 6763912, 11803561, 1585585, 10958447, -2671165, 23855391, 4598332, -6159431, -14117438},
|
||||
{-31031306, -14256194, 17332029, -2383520, 31312682, -5967183, 696309, 50292, -20095739, 11763584},
|
||||
{-594563, -2514283, -32234153, 12643980, 12650761, 14811489, 665117, -12613632, -19773211, -10713562}},
|
||||
{{30464590, -11262872, -4127476, -12734478, 19835327, -7105613, -24396175, 2075773, -17020157, 992471},
|
||||
{18357185, -6994433, 7766382, 16342475, -29324918, 411174, 14578841, 8080033, -11574335, -10601610},
|
||||
{19598397, 10334610, 12555054, 2555664, 18821899, -10339780, 21873263, 16014234, 26224780, 16452269}},
|
||||
{{-30223925, 5145196, 5944548, 16385966, 3976735, 2009897, -11377804, -7618186, -20533829, 3698650},
|
||||
{14187449, 3448569, -10636236, -10810935, -22663880, -3433596, 7268410, -10890444, 27394301, 12015369},
|
||||
{19695761, 16087646, 28032085, 12999827, 6817792, 11427614, 20244189, -1312777, -13259127, -3402461}},
|
||||
{{30860103, 12735208, -1888245, -4699734, -16974906, 2256940, -8166013, 12298312, -8550524, -10393462},
|
||||
{-5719826, -11245325, -1910649, 15569035, 26642876, -7587760, -5789354, -15118654, -4976164, 12651793},
|
||||
{-2848395, 9953421, 11531313, -5282879, 26895123, -12697089, -13118820, -16517902, 9768698, -2533218}},
|
||||
{{-24719459, 1894651, -287698, -4704085, 15348719, -8156530, 32767513, 12765450, 4940095, 10678226},
|
||||
{18860224, 15980149, -18987240, -1562570, -26233012, -11071856, -7843882, 13944024, -24372348, 16582019},
|
||||
{-15504260, 4970268, -29893044, 4175593, -20993212, -2199756, -11704054, 15444560, -11003761, 7989037}},
|
||||
{{31490452, 5568061, -2412803, 2182383, -32336847, 4531686, -32078269, 6200206, -19686113, -14800171},
|
||||
{-17308668, -15879940, -31522777, -2831, -32887382, 16375549, 8680158, -16371713, 28550068, -6857132},
|
||||
{-28126887, -5688091, 16837845, -1820458, -6850681, 12700016, -30039981, 4364038, 1155602, 5988841}},
|
||||
{{21890435, -13272907, -12624011, 12154349, -7831873, 15300496, 23148983, -4470481, 24618407, 8283181},
|
||||
{-33136107, -10512751, 9975416, 6841041, -31559793, 16356536, 3070187, -7025928, 1466169, 10740210},
|
||||
{-1509399, -15488185, -13503385, -10655916, 32799044, 909394, -13938903, -5779719, -32164649, -15327040}},
|
||||
{{3960823, -14267803, -28026090, -15918051, -19404858, 13146868, 15567327, 951507, -3260321, -573935},
|
||||
{24740841, 5052253, -30094131, 8961361, 25877428, 6165135, -24368180, 14397372, -7380369, -6144105},
|
||||
{-28888365, 3510803, -28103278, -1158478, -11238128, -10631454, -15441463, -14453128, -1625486, -6494814}}
|
||||
}, {
|
||||
{{793299, -9230478, 8836302, -6235707, -27360908, -2369593, 33152843, -4885251, -9906200, -621852},
|
||||
{5666233, 525582, 20782575, -8038419, -24538499, 14657740, 16099374, 1468826, -6171428, -15186581},
|
||||
{-4859255, -3779343, -2917758, -6748019, 7778750, 11688288, -30404353, -9871238, -1558923, -9863646}},
|
||||
{{10896332, -7719704, 824275, 472601, -19460308, 3009587, 25248958, 14783338, -30581476, -15757844},
|
||||
{10566929, 12612572, -31944212, 11118703, -12633376, 12362879, 21752402, 8822496, 24003793, 14264025},
|
||||
{27713862, -7355973, -11008240, 9227530, 27050101, 2504721, 23886875, -13117525, 13958495, -5732453}},
|
||||
{{-23481610, 4867226, -27247128, 3900521, 29838369, -8212291, -31889399, -10041781, 7340521, -15410068},
|
||||
{4646514, -8011124, -22766023, -11532654, 23184553, 8566613, 31366726, -1381061, -15066784, -10375192},
|
||||
{-17270517, 12723032, -16993061, 14878794, 21619651, -6197576, 27584817, 3093888, -8843694, 3849921}},
|
||||
{{-9064912, 2103172, 25561640, -15125738, -5239824, 9582958, 32477045, -9017955, 5002294, -15550259},
|
||||
{-12057553, -11177906, 21115585, -13365155, 8808712, -12030708, 16489530, 13378448, -25845716, 12741426},
|
||||
{-5946367, 10645103, -30911586, 15390284, -3286982, -7118677, 24306472, 15852464, 28834118, -7646072}},
|
||||
{{-17335748, -9107057, -24531279, 9434953, -8472084, -583362, -13090771, 455841, 20461858, 5491305},
|
||||
{13669248, -16095482, -12481974, -10203039, -14569770, -11893198, -24995986, 11293807, -28588204, -9421832},
|
||||
{28497928, 6272777, -33022994, 14470570, 8906179, -1225630, 18504674, -14165166, 29867745, -8795943}},
|
||||
{{-16207023, 13517196, -27799630, -13697798, 24009064, -6373891, -6367600, -13175392, 22853429, -4012011},
|
||||
{24191378, 16712145, -13931797, 15217831, 14542237, 1646131, 18603514, -11037887, 12876623, -2112447},
|
||||
{17902668, 4518229, -411702, -2829247, 26878217, 5258055, -12860753, 608397, 16031844, 3723494}},
|
||||
{{-28632773, 12763728, -20446446, 7577504, 33001348, -13017745, 17558842, -7872890, 23896954, -4314245},
|
||||
{-20005381, -12011952, 31520464, 605201, 2543521, 5991821, -2945064, 7229064, -9919646, -8826859},
|
||||
{28816045, 298879, -28165016, -15920938, 19000928, -1665890, -12680833, -2949325, -18051778, -2082915}},
|
||||
{{16000882, -344896, 3493092, -11447198, -29504595, -13159789, 12577740, 16041268, -19715240, 7847707},
|
||||
{10151868, 10572098, 27312476, 7922682, 14825339, 4723128, -32855931, -6519018, -10020567, 3852848},
|
||||
{-11430470, 15697596, -21121557, -4420647, 5386314, 15063598, 16514493, -15932110, 29330899, -15076224}}
|
||||
}, {
|
||||
{{-25499735, -4378794, -15222908, -6901211, 16615731, 2051784, 3303702, 15490, -27548796, 12314391},
|
||||
{15683520, -6003043, 18109120, -9980648, 15337968, -5997823, -16717435, 15921866, 16103996, -3731215},
|
||||
{-23169824, -10781249, 13588192, -1628807, -3798557, -1074929, -19273607, 5402699, -29815713, -9841101}},
|
||||
{{23190676, 2384583, -32714340, 3462154, -29903655, -1529132, -11266856, 8911517, -25205859, 2739713},
|
||||
{21374101, -3554250, -33524649, 9874411, 15377179, 11831242, -33529904, 6134907, 4931255, 11987849},
|
||||
{-7732, -2978858, -16223486, 7277597, 105524, -322051, -31480539, 13861388, -30076310, 10117930}},
|
||||
{{-29501170, -10744872, -26163768, 13051539, -25625564, 5089643, -6325503, 6704079, 12890019, 15728940},
|
||||
{-21972360, -11771379, -951059, -4418840, 14704840, 2695116, 903376, -10428139, 12885167, 8311031},
|
||||
{-17516482, 5352194, 10384213, -13811658, 7506451, 13453191, 26423267, 4384730, 1888765, -5435404}},
|
||||
{{-25817338, -3107312, -13494599, -3182506, 30896459, -13921729, -32251644, -12707869, -19464434, -3340243},
|
||||
{-23607977, -2665774, -526091, 4651136, 5765089, 4618330, 6092245, 14845197, 17151279, -9854116},
|
||||
{-24830458, -12733720, -15165978, 10367250, -29530908, -265356, 22825805, -7087279, -16866484, 16176525}},
|
||||
{{-23583256, 6564961, 20063689, 3798228, -4740178, 7359225, 2006182, -10363426, -28746253, -10197509},
|
||||
{-10626600, -4486402, -13320562, -5125317, 3432136, -6393229, 23632037, -1940610, 32808310, 1099883},
|
||||
{15030977, 5768825, -27451236, -2887299, -6427378, -15361371, -15277896, -6809350, 2051441, -15225865}},
|
||||
{{-3362323, -7239372, 7517890, 9824992, 23555850, 295369, 5148398, -14154188, -22686354, 16633660},
|
||||
{4577086, -16752288, 13249841, -15304328, 19958763, -14537274, 18559670, -10759549, 8402478, -9864273},
|
||||
{-28406330, -1051581, -26790155, -907698, -17212414, -11030789, 9453451, -14980072, 17983010, 9967138}},
|
||||
{{-25762494, 6524722, 26585488, 9969270, 24709298, 1220360, -1677990, 7806337, 17507396, 3651560},
|
||||
{-10420457, -4118111, 14584639, 15971087, -15768321, 8861010, 26556809, -5574557, -18553322, -11357135},
|
||||
{2839101, 14284142, 4029895, 3472686, 14402957, 12689363, -26642121, 8459447, -5605463, -7621941}},
|
||||
{{-4839289, -3535444, 9744961, 2871048, 25113978, 3187018, -25110813, -849066, 17258084, -7977739},
|
||||
{18164541, -10595176, -17154882, -1542417, 19237078, -9745295, 23357533, -15217008, 26908270, 12150756},
|
||||
{-30264870, -7647865, 5112249, -7036672, -1499807, -6974257, 43168, -5537701, -32302074, 16215819}}
|
||||
}, {
|
||||
{{-6898905, 9824394, -12304779, -4401089, -31397141, -6276835, 32574489, 12532905, -7503072, -8675347},
|
||||
{-27343522, -16515468, -27151524, -10722951, 946346, 16291093, 254968, 7168080, 21676107, -1943028},
|
||||
{21260961, -8424752, -16831886, -11920822, -23677961, 3968121, -3651949, -6215466, -3556191, -7913075}},
|
||||
{{16544754, 13250366, -16804428, 15546242, -4583003, 12757258, -2462308, -8680336, -18907032, -9662799},
|
||||
{-2415239, -15577728, 18312303, 4964443, -15272530, -12653564, 26820651, 16690659, 25459437, -4564609},
|
||||
{-25144690, 11425020, 28423002, -11020557, -6144921, -15826224, 9142795, -2391602, -6432418, -1644817}},
|
||||
{{-23104652, 6253476, 16964147, -3768872, -25113972, -12296437, -27457225, -16344658, 6335692, 7249989},
|
||||
{-30333227, 13979675, 7503222, -12368314, -11956721, -4621693, -30272269, 2682242, 25993170, -12478523},
|
||||
{4364628, 5930691, 32304656, -10044554, -8054781, 15091131, 22857016, -10598955, 31820368, 15075278}},
|
||||
{{31879134, -8918693, 17258761, 90626, -8041836, -4917709, 24162788, -9650886, -17970238, 12833045},
|
||||
{19073683, 14851414, -24403169, -11860168, 7625278, 11091125, -19619190, 2074449, -9413939, 14905377},
|
||||
{24483667, -11935567, -2518866, -11547418, -1553130, 15355506, -25282080, 9253129, 27628530, -7555480}},
|
||||
{{17597607, 8340603, 19355617, 552187, 26198470, -3176583, 4593324, -9157582, -14110875, 15297016},
|
||||
{510886, 14337390, -31785257, 16638632, 6328095, 2713355, -20217417, -11864220, 8683221, 2921426},
|
||||
{18606791, 11874196, 27155355, -5281482, -24031742, 6265446, -25178240, -1278924, 4674690, 13890525}},
|
||||
{{13609624, 13069022, -27372361, -13055908, 24360586, 9592974, 14977157, 9835105, 4389687, 288396},
|
||||
{9922506, -519394, 13613107, 5883594, -18758345, -434263, -12304062, 8317628, 23388070, 16052080},
|
||||
{12720016, 11937594, -31970060, -5028689, 26900120, 8561328, -20155687, -11632979, -14754271, -10812892}},
|
||||
{{15961858, 14150409, 26716931, -665832, -22794328, 13603569, 11829573, 7467844, -28822128, 929275},
|
||||
{11038231, -11582396, -27310482, -7316562, -10498527, -16307831, -23479533, -9371869, -21393143, 2465074},
|
||||
{20017163, -4323226, 27915242, 1529148, 12396362, 15675764, 13817261, -9658066, 2463391, -4622140}},
|
||||
{{-16358878, -12663911, -12065183, 4996454, -1256422, 1073572, 9583558, 12851107, 4003896, 12673717},
|
||||
{-1731589, -15155870, -3262930, 16143082, 19294135, 13385325, 14741514, -9103726, 7903886, 2348101},
|
||||
{24536016, -16515207, 12715592, -3862155, 1511293, 10047386, -3842346, -7129159, -28377538, 10048127}}
|
||||
}, {
|
||||
{{-12622226, -6204820, 30718825, 2591312, -10617028, 12192840, 18873298, -7297090, -32297756, 15221632},
|
||||
{-26478122, -11103864, 11546244, -1852483, 9180880, 7656409, -21343950, 2095755, 29769758, 6593415},
|
||||
{-31994208, -2907461, 4176912, 3264766, 12538965, -868111, 26312345, -6118678, 30958054, 8292160}},
|
||||
{{31429822, -13959116, 29173532, 15632448, 12174511, -2760094, 32808831, 3977186, 26143136, -3148876},
|
||||
{22648901, 1402143, -22799984, 13746059, 7936347, 365344, -8668633, -1674433, -3758243, -2304625},
|
||||
{-15491917, 8012313, -2514730, -12702462, -23965846, -10254029, -1612713, -1535569, -16664475, 8194478}},
|
||||
{{27338066, -7507420, -7414224, 10140405, -19026427, -6589889, 27277191, 8855376, 28572286, 3005164},
|
||||
{26287124, 4821776, 25476601, -4145903, -3764513, -15788984, -18008582, 1182479, -26094821, -13079595},
|
||||
{-7171154, 3178080, 23970071, 6201893, -17195577, -4489192, -21876275, -13982627, 32208683, -1198248}},
|
||||
{{-16657702, 2817643, -10286362, 14811298, 6024667, 13349505, -27315504, -10497842, -27672585, -11539858},
|
||||
{15941029, -9405932, -21367050, 8062055, 31876073, -238629, -15278393, -1444429, 15397331, -4130193},
|
||||
{8934485, -13485467, -23286397, -13423241, -32446090, 14047986, 31170398, -1441021, -27505566, 15087184}},
|
||||
{{-18357243, -2156491, 24524913, -16677868, 15520427, -6360776, -15502406, 11461896, 16788528, -5868942},
|
||||
{-1947386, 16013773, 21750665, 3714552, -17401782, -16055433, -3770287, -10323320, 31322514, -11615635},
|
||||
{21426655, -5650218, -13648287, -5347537, -28812189, -4920970, -18275391, -14621414, 13040862, -12112948}},
|
||||
{{11293895, 12478086, -27136401, 15083750, -29307421, 14748872, 14555558, -13417103, 1613711, 4896935},
|
||||
{-25894883, 15323294, -8489791, -8057900, 25967126, -13425460, 2825960, -4897045, -23971776, -11267415},
|
||||
{-15924766, -5229880, -17443532, 6410664, 3622847, 10243618, 20615400, 12405433, -23753030, -8436416}},
|
||||
{{-7091295, 12556208, -20191352, 9025187, -17072479, 4333801, 4378436, 2432030, 23097949, -566018},
|
||||
{4565804, -16025654, 20084412, -7842817, 1724999, 189254, 24767264, 10103221, -18512313, 2424778},
|
||||
{366633, -11976806, 8173090, -6890119, 30788634, 5745705, -7168678, 1344109, -3642553, 12412659}},
|
||||
{{-24001791, 7690286, 14929416, -168257, -32210835, -13412986, 24162697, -15326504, -3141501, 11179385},
|
||||
{18289522, -14724954, 8056945, 16430056, -21729724, 7842514, -6001441, -1486897, -18684645, -11443503},
|
||||
{476239, 6601091, -6152790, -9723375, 17503545, -4863900, 27672959, 13403813, 11052904, 5219329}}
|
||||
}, {
|
||||
{{20678546, -8375738, -32671898, 8849123, -5009758, 14574752, 31186971, -3973730, 9014762, -8579056},
|
||||
{-13644050, -10350239, -15962508, 5075808, -1514661, -11534600, -33102500, 9160280, 8473550, -3256838},
|
||||
{24900749, 14435722, 17209120, -15292541, -22592275, 9878983, -7689309, -16335821, -24568481, 11788948}},
|
||||
{{-3118155, -11395194, -13802089, 14797441, 9652448, -6845904, -20037437, 10410733, -24568470, -1458691},
|
||||
{-15659161, 16736706, -22467150, 10215878, -9097177, 7563911, 11871841, -12505194, -18513325, 8464118},
|
||||
{-23400612, 8348507, -14585951, -861714, -3950205, -6373419, 14325289, 8628612, 33313881, -8370517}},
|
||||
{{-20186973, -4967935, 22367356, 5271547, -1097117, -4788838, -24805667, -10236854, -8940735, -5818269},
|
||||
{-6948785, -1795212, -32625683, -16021179, 32635414, -7374245, 15989197, -12838188, 28358192, -4253904},
|
||||
{-23561781, -2799059, -32351682, -1661963, -9147719, 10429267, -16637684, 4072016, -5351664, 5596589}},
|
||||
{{-28236598, -3390048, 12312896, 6213178, 3117142, 16078565, 29266239, 2557221, 1768301, 15373193},
|
||||
{-7243358, -3246960, -4593467, -7553353, -127927, -912245, -1090902, -4504991, -24660491, 3442910},
|
||||
{-30210571, 5124043, 14181784, 8197961, 18964734, -11939093, 22597931, 7176455, -18585478, 13365930}},
|
||||
{{-7877390, -1499958, 8324673, 4690079, 6261860, 890446, 24538107, -8570186, -9689599, -3031667},
|
||||
{25008904, -10771599, -4305031, -9638010, 16265036, 15721635, 683793, -11823784, 15723479, -15163481},
|
||||
{-9660625, 12374379, -27006999, -7026148, -7724114, -12314514, 11879682, 5400171, 519526, -1235876}},
|
||||
{{22258397, -16332233, -7869817, 14613016, -22520255, -2950923, -20353881, 7315967, 16648397, 7605640},
|
||||
{-8081308, -8464597, -8223311, 9719710, 19259459, -15348212, 23994942, -5281555, -9468848, 4763278},
|
||||
{-21699244, 9220969, -15730624, 1084137, -25476107, -2852390, 31088447, -7764523, -11356529, 728112}},
|
||||
{{26047220, -11751471, -6900323, -16521798, 24092068, 9158119, -4273545, -12555558, -29365436, -5498272},
|
||||
{17510331, -322857, 5854289, 8403524, 17133918, -3112612, -28111007, 12327945, 10750447, 10014012},
|
||||
{-10312768, 3936952, 9156313, -8897683, 16498692, -994647, -27481051, -666732, 3424691, 7540221}},
|
||||
{{30322361, -6964110, 11361005, -4143317, 7433304, 4989748, -7071422, -16317219, -9244265, 15258046},
|
||||
{13054562, -2779497, 19155474, 469045, -12482797, 4566042, 5631406, 2711395, 1062915, -5136345},
|
||||
{-19240248, -11254599, -29509029, -7499965, -5835763, 13005411, -6066489, 12194497, 32960380, 1459310}}
|
||||
}, {
|
||||
{{19852034, 7027924, 23669353, 10020366, 8586503, -6657907, 394197, -6101885, 18638003, -11174937},
|
||||
{31395534, 15098109, 26581030, 8030562, -16527914, -5007134, 9012486, -7584354, -6643087, -5442636},
|
||||
{-9192165, -2347377, -1997099, 4529534, 25766844, 607986, -13222, 9677543, -32294889, -6456008}},
|
||||
{{-2444496, -149937, 29348902, 8186665, 1873760, 12489863, -30934579, -7839692, -7852844, -8138429},
|
||||
{-15236356, -15433509, 7766470, 746860, 26346930, -10221762, -27333451, 10754588, -9431476, 5203576},
|
||||
{31834314, 14135496, -770007, 5159118, 20917671, -16768096, -7467973, -7337524, 31809243, 7347066}},
|
||||
{{-9606723, -11874240, 20414459, 13033986, 13716524, -11691881, 19797970, -12211255, 15192876, -2087490},
|
||||
{-12663563, -2181719, 1168162, -3804809, 26747877, -14138091, 10609330, 12694420, 33473243, -13382104},
|
||||
{33184999, 11180355, 15832085, -11385430, -1633671, 225884, 15089336, -11023903, -6135662, 14480053}},
|
||||
{{31308717, -5619998, 31030840, -1897099, 15674547, -6582883, 5496208, 13685227, 27595050, 8737275},
|
||||
{-20318852, -15150239, 10933843, -16178022, 8335352, -7546022, -31008351, -12610604, 26498114, 66511},
|
||||
{22644454, -8761729, -16671776, 4884562, -3105614, -13559366, 30540766, -4286747, -13327787, -7515095}},
|
||||
{{-28017847, 9834845, 18617207, -2681312, -3401956, -13307506, 8205540, 13585437, -17127465, 15115439},
|
||||
{23711543, -672915, 31206561, -8362711, 6164647, -9709987, -33535882, -1426096, 8236921, 16492939},
|
||||
{-23910559, -13515526, -26299483, -4503841, 25005590, -7687270, 19574902, 10071562, 6708380, -6222424}},
|
||||
{{2101391, -4930054, 19702731, 2367575, -15427167, 1047675, 5301017, 9328700, 29955601, -11678310},
|
||||
{3096359, 9271816, -21620864, -15521844, -14847996, -7592937, -25892142, -12635595, -9917575, 6216608},
|
||||
{-32615849, 338663, -25195611, 2510422, -29213566, -13820213, 24822830, -6146567, -26767480, 7525079}},
|
||||
{{-23066649, -13985623, 16133487, -7896178, -3389565, 778788, -910336, -2782495, -19386633, 11994101},
|
||||
{21691500, -13624626, -641331, -14367021, 3285881, -3483596, -25064666, 9718258, -7477437, 13381418},
|
||||
{18445390, -4202236, 14979846, 11622458, -1727110, -3582980, 23111648, -6375247, 28535282, 15779576}},
|
||||
{{30098053, 3089662, -9234387, 16662135, -21306940, 11308411, -14068454, 12021730, 9955285, -16303356},
|
||||
{9734894, -14576830, -7473633, -9138735, 2060392, 11313496, -18426029, 9924399, 20194861, 13380996},
|
||||
{-26378102, -7965207, -22167821, 15789297, -18055342, -6168792, -1984914, 15707771, 26342023, 10146099}}
|
||||
}, {
|
||||
{{-26016874, -219943, 21339191, -41388, 19745256, -2878700, -29637280, 2227040, 21612326, -545728},
|
||||
{-13077387, 1184228, 23562814, -5970442, -20351244, -6348714, 25764461, 12243797, -20856566, 11649658},
|
||||
{-10031494, 11262626, 27384172, 2271902, 26947504, -15997771, 39944, 6114064, 33514190, 2333242}},
|
||||
{{-21433588, -12421821, 8119782, 7219913, -21830522, -9016134, -6679750, -12670638, 24350578, -13450001},
|
||||
{-4116307, -11271533, -23886186, 4843615, -30088339, 690623, -31536088, -10406836, 8317860, 12352766},
|
||||
{18200138, -14475911, -33087759, -2696619, -23702521, -9102511, -23552096, -2287550, 20712163, 6719373}},
|
||||
{{26656208, 6075253, -7858556, 1886072, -28344043, 4262326, 11117530, -3763210, 26224235, -3297458},
|
||||
{-17168938, -14854097, -3395676, -16369877, -19954045, 14050420, 21728352, 9493610, 18620611, -16428628},
|
||||
{-13323321, 13325349, 11432106, 5964811, 18609221, 6062965, -5269471, -9725556, -30701573, -16479657}},
|
||||
{{-23860538, -11233159, 26961357, 1640861, -32413112, -16737940, 12248509, -5240639, 13735342, 1934062},
|
||||
{25089769, 6742589, 17081145, -13406266, 21909293, -16067981, -15136294, -3765346, -21277997, 5473616},
|
||||
{31883677, -7961101, 1083432, -11572403, 22828471, 13290673, -7125085, 12469656, 29111212, -5451014}},
|
||||
{{24244947, -15050407, -26262976, 2791540, -14997599, 16666678, 24367466, 6388839, -10295587, 452383},
|
||||
{-25640782, -3417841, 5217916, 16224624, 19987036, -4082269, -24236251, -5915248, 15766062, 8407814},
|
||||
{-20406999, 13990231, 15495425, 16395525, 5377168, 15166495, -8917023, -4388953, -8067909, 2276718}},
|
||||
{{30157918, 12924066, -17712050, 9245753, 19895028, 3368142, -23827587, 5096219, 22740376, -7303417},
|
||||
{2041139, -14256350, 7783687, 13876377, -25946985, -13352459, 24051124, 13742383, -15637599, 13295222},
|
||||
{33338237, -8505733, 12532113, 7977527, 9106186, -1715251, -17720195, -4612972, -4451357, -14669444}},
|
||||
{{-20045281, 5454097, -14346548, 6447146, 28862071, 1883651, -2469266, -4141880, 7770569, 9620597},
|
||||
{23208068, 7979712, 33071466, 8149229, 1758231, -10834995, 30945528, -1694323, -33502340, -14767970},
|
||||
{1439958, -16270480, -1079989, -793782, 4625402, 10647766, -5043801, 1220118, 30494170, -11440799}},
|
||||
{{-5037580, -13028295, -2970559, -3061767, 15640974, -6701666, -26739026, 926050, -1684339, -13333647},
|
||||
{13908495, -3549272, 30919928, -6273825, -21521863, 7989039, 9021034, 9078865, 3353509, 4033511},
|
||||
{-29663431, -15113610, 32259991, -344482, 24295849, -12912123, 23161163, 8839127, 27485041, 7356032}}
|
||||
}, {
|
||||
{{9661027, 705443, 11980065, -5370154, -1628543, 14661173, -6346142, 2625015, 28431036, -16771834},
|
||||
{-23839233, -8311415, -25945511, 7480958, -17681669, -8354183, -22545972, 14150565, 15970762, 4099461},
|
||||
{29262576, 16756590, 26350592, -8793563, 8529671, -11208050, 13617293, -9937143, 11465739, 8317062}},
|
||||
{{-25493081, -6962928, 32500200, -9419051, -23038724, -2302222, 14898637, 3848455, 20969334, -5157516},
|
||||
{-20384450, -14347713, -18336405, 13884722, -33039454, 2842114, -21610826, -3649888, 11177095, 14989547},
|
||||
{-24496721, -11716016, 16959896, 2278463, 12066309, 10137771, 13515641, 2581286, -28487508, 9930240}},
|
||||
{{-17751622, -2097826, 16544300, -13009300, -15914807, -14949081, 18345767, -13403753, 16291481, -5314038},
|
||||
{-33229194, 2553288, 32678213, 9875984, 8534129, 6889387, -9676774, 6957617, 4368891, 9788741},
|
||||
{16660756, 7281060, -10830758, 12911820, 20108584, -8101676, -21722536, -8613148, 16250552, -11111103}},
|
||||
{{-19765507, 2390526, -16551031, 14161980, 1905286, 6414907, 4689584, 10604807, -30190403, 4782747},
|
||||
{-1354539, 14736941, -7367442, -13292886, 7710542, -14155590, -9981571, 4383045, 22546403, 437323},
|
||||
{31665577, -12180464, -16186830, 1491339, -18368625, 3294682, 27343084, 2786261, -30633590, -14097016}},
|
||||
{{-14467279, -683715, -33374107, 7448552, 19294360, 14334329, -19690631, 2355319, -19284671, -6114373},
|
||||
{15121312, -15796162, 6377020, -6031361, -10798111, -12957845, 18952177, 15496498, -29380133, 11754228},
|
||||
{-2637277, -13483075, 8488727, -14303896, 12728761, -1622493, 7141596, 11724556, 22761615, -10134141}},
|
||||
{{16918416, 11729663, -18083579, 3022987, -31015732, -13339659, -28741185, -12227393, 32851222, 11717399},
|
||||
{11166634, 7338049, -6722523, 4531520, -29468672, -7302055, 31474879, 3483633, -1193175, -4030831},
|
||||
{-185635, 9921305, 31456609, -13536438, -12013818, 13348923, 33142652, 6546660, -19985279, -3948376}},
|
||||
{{-32460596, 11266712, -11197107, -7899103, 31703694, 3855903, -8537131, -12833048, -30772034, -15486313},
|
||||
{-18006477, 12709068, 3991746, -6479188, -21491523, -10550425, -31135347, -16049879, 10928917, 3011958},
|
||||
{-6957757, -15594337, 31696059, 334240, 29576716, 14796075, -30831056, -12805180, 18008031, 10258577}},
|
||||
{{-22448644, 15655569, 7018479, -4410003, -30314266, -1201591, -1853465, 1367120, 25127874, 6671743},
|
||||
{29701166, -14373934, -10878120, 9279288, -17568, 13127210, 21382910, 11042292, 25838796, 4642684},
|
||||
{-20430234, 14955537, -24126347, 8124619, -5369288, -5990470, 30468147, -13900640, 18423289, 4177476}}
|
||||
}
|
||||
};
|
||||
|
||||
const ge_precomp ge_Bi[8] = {
|
||||
{{25967493, -14356035, 29566456, 3660896, -12694345, 4014787, 27544626, -11754271, -6079156, 2047605},
|
||||
{-12545711, 934262, -2722910, 3049990, -727428, 9406986, 12720692, 5043384, 19500929, -15469378},
|
||||
{-8738181, 4489570, 9688441, -14785194, 10184609, -12363380, 29287919, 11864899, -24514362, -4438546}}, {{15636291, -9688557, 24204773, -7912398, 616977, -16685262, 27787600, -14772189, 28944400, -1550024},
|
||||
{16568933, 4717097, -11556148, -1102322, 15682896, -11807043, 16354577, -11775962, 7689662, 11199574},
|
||||
{30464156, -5976125, -11779434, -15670865, 23220365, 15915852, 7512774, 10017326, -17749093, -9920357}}, {{10861363, 11473154, 27284546, 1981175, -30064349, 12577861, 32867885, 14515107, -15438304, 10819380},
|
||||
{4708026, 6336745, 20377586, 9066809, -11272109, 6594696, -25653668, 12483688, -12668491, 5581306},
|
||||
{19563160, 16186464, -29386857, 4097519, 10237984, -4348115, 28542350, 13850243, -23678021, -15815942}}, {{5153746, 9909285, 1723747, -2777874, 30523605, 5516873, 19480852, 5230134, -23952439, -15175766},
|
||||
{-30269007, -3463509, 7665486, 10083793, 28475525, 1649722, 20654025, 16520125, 30598449, 7715701},
|
||||
{28881845, 14381568, 9657904, 3680757, -20181635, 7843316, -31400660, 1370708, 29794553, -1409300}}, {{-22518993, -6692182, 14201702, -8745502, -23510406, 8844726, 18474211, -1361450, -13062696, 13821877},
|
||||
{-6455177, -7839871, 3374702, -4740862, -27098617, -10571707, 31655028, -7212327, 18853322, -14220951},
|
||||
{4566830, -12963868, -28974889, -12240689, -7602672, -2830569, -8514358, -10431137, 2207753, -3209784}}, {{-25154831, -4185821, 29681144, 7868801, -6854661, -9423865, -12437364, -663000, -31111463, -16132436},
|
||||
{25576264, -2703214, 7349804, -11814844, 16472782, 9300885, 3844789, 15725684, 171356, 6466918},
|
||||
{23103977, 13316479, 9739013, -16149481, 817875, -15038942, 8965339, -14088058, -30714912, 16193877}}, {{-33521811, 3180713, -2394130, 14003687, -16903474, -16270840, 17238398, 4729455, -18074513, 9256800},
|
||||
{-25182317, -4174131, 32336398, 5036987, -21236817, 11360617, 22616405, 9761698, -19827198, 630305},
|
||||
{-13720693, 2639453, -24237460, -7406481, 9494427, -5774029, -6554551, -15960994, -2449256, -14291300}}, {{-3151181, -5046075, 9282714, 6866145, -31907062, -863023, -18940575, 15033784, 25105118, -7894876},
|
||||
{-24326370, 15950226, -31801215, -14592823, -11662737, -5090925, 1573892, -2625887, 2198790, -15804619},
|
||||
{-3099351, 10324967, -2241613, 7453183, -5446979, -2735503, -13812022, -16236442, -32461234, -12290683}}
|
||||
};
|
||||
|
||||
/* A = 2 * (1 - d) / (1 + d) = 486662 */
|
||||
const fe fe_ma2 = {-12721188, -3529, 0, 0, 0, 0, 0, 0, 0, 0}; /* -A^2 */
|
||||
const fe fe_ma = {-486662, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* -A */
|
||||
const fe fe_fffb1 = {-31702527, -2466483, -26106795, -12203692, -12169197, -321052, 14850977, -10296299, -16929438, -407568}; /* sqrt(-2 * A * (A + 2)) */
|
||||
const fe fe_fffb2 = {8166131, -6741800, -17040804, 3154616, 21461005, 1466302, -30876704, -6368709, 10503587, -13363080}; /* sqrt(2 * A * (A + 2)) */
|
||||
const fe fe_fffb3 = {-13620103, 14639558, 4532995, 7679154, 16815101, -15883539, -22863840, -14813421, 13716513, -6477756}; /* sqrt(-sqrt(-1) * A * (A + 2)) */
|
||||
const fe fe_fffb4 = {-21786234, -12173074, 21573800, 4524538, -4645904, 16204591, 8012863, -8444712, 3212926, 6885324}; /* sqrt(sqrt(-1) * A * (A + 2)) */
|
2923
source-code/RuffCT-java/c/crypto-ops.c
Normal file
2923
source-code/RuffCT-java/c/crypto-ops.c
Normal file
File diff suppressed because it is too large
Load diff
153
source-code/RuffCT-java/c/crypto-ops.h
Normal file
153
source-code/RuffCT-java/c/crypto-ops.h
Normal file
|
@ -0,0 +1,153 @@
|
|||
// Copyright (c) 2014-2017, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
|
||||
|
||||
#pragma once
|
||||
|
||||
/* From fe.h */
|
||||
|
||||
typedef int32_t fe[10];
|
||||
|
||||
/* From ge.h */
|
||||
|
||||
typedef struct {
|
||||
fe X;
|
||||
fe Y;
|
||||
fe Z;
|
||||
} ge_p2;
|
||||
|
||||
typedef struct {
|
||||
fe X;
|
||||
fe Y;
|
||||
fe Z;
|
||||
fe T;
|
||||
} ge_p3;
|
||||
|
||||
typedef struct {
|
||||
fe X;
|
||||
fe Y;
|
||||
fe Z;
|
||||
fe T;
|
||||
} ge_p1p1;
|
||||
|
||||
typedef struct {
|
||||
fe yplusx;
|
||||
fe yminusx;
|
||||
fe xy2d;
|
||||
} ge_precomp;
|
||||
|
||||
typedef struct {
|
||||
fe YplusX;
|
||||
fe YminusX;
|
||||
fe Z;
|
||||
fe T2d;
|
||||
} ge_cached;
|
||||
|
||||
/* From ge_add.c */
|
||||
|
||||
void ge_add(ge_p1p1 *, const ge_p3 *, const ge_cached *);
|
||||
|
||||
/* From ge_double_scalarmult.c, modified */
|
||||
|
||||
typedef ge_cached ge_dsmp[8];
|
||||
extern const ge_precomp ge_Bi[8];
|
||||
void ge_dsm_precomp(ge_dsmp r, const ge_p3 *s);
|
||||
void ge_double_scalarmult_base_vartime(ge_p2 *, const unsigned char *, const ge_p3 *, const unsigned char *);
|
||||
|
||||
/* From ge_frombytes.c, modified */
|
||||
|
||||
extern const fe fe_sqrtm1;
|
||||
extern const fe fe_d;
|
||||
int ge_frombytes_vartime(ge_p3 *, const unsigned char *);
|
||||
|
||||
/* From ge_p1p1_to_p2.c */
|
||||
|
||||
void ge_p1p1_to_p2(ge_p2 *, const ge_p1p1 *);
|
||||
|
||||
/* From ge_p1p1_to_p3.c */
|
||||
|
||||
void ge_p1p1_to_p3(ge_p3 *, const ge_p1p1 *);
|
||||
|
||||
/* From ge_p2_dbl.c */
|
||||
|
||||
void ge_p2_dbl(ge_p1p1 *, const ge_p2 *);
|
||||
|
||||
/* From ge_p3_to_cached.c */
|
||||
|
||||
extern const fe fe_d2;
|
||||
void ge_p3_to_cached(ge_cached *, const ge_p3 *);
|
||||
|
||||
/* From ge_p3_to_p2.c */
|
||||
|
||||
void ge_p3_to_p2(ge_p2 *, const ge_p3 *);
|
||||
|
||||
/* From ge_p3_tobytes.c */
|
||||
|
||||
void ge_p3_tobytes(unsigned char *, const ge_p3 *);
|
||||
|
||||
/* From ge_scalarmult_base.c */
|
||||
|
||||
extern const ge_precomp ge_base[32][8];
|
||||
void ge_scalarmult_base(ge_p3 *, const unsigned char *);
|
||||
|
||||
/* From ge_tobytes.c */
|
||||
|
||||
void ge_tobytes(unsigned char *, const ge_p2 *);
|
||||
|
||||
/* From sc_reduce.c */
|
||||
|
||||
void sc_reduce(unsigned char *);
|
||||
|
||||
/* New code */
|
||||
|
||||
void ge_scalarmult(ge_p2 *, const unsigned char *, const ge_p3 *);
|
||||
void ge_double_scalarmult_precomp_vartime(ge_p2 *, const unsigned char *, const ge_p3 *, const unsigned char *, const ge_dsmp);
|
||||
void ge_mul8(ge_p1p1 *, const ge_p2 *);
|
||||
extern const fe fe_ma2;
|
||||
extern const fe fe_ma;
|
||||
extern const fe fe_fffb1;
|
||||
extern const fe fe_fffb2;
|
||||
extern const fe fe_fffb3;
|
||||
extern const fe fe_fffb4;
|
||||
void ge_fromfe_frombytes_vartime(ge_p2 *, const unsigned char *);
|
||||
void sc_0(unsigned char *);
|
||||
void sc_reduce32(unsigned char *);
|
||||
void sc_add(unsigned char *, const unsigned char *, const unsigned char *);
|
||||
void sc_sub(unsigned char *, const unsigned char *, const unsigned char *);
|
||||
void sc_mulsub(unsigned char *, const unsigned char *, const unsigned char *, const unsigned char *);
|
||||
int sc_check(const unsigned char *);
|
||||
int sc_isnonzero(const unsigned char *); /* Doesn't normalize */
|
||||
|
||||
// internal
|
||||
uint64_t load_3(const unsigned char *in);
|
||||
uint64_t load_4(const unsigned char *in);
|
||||
void ge_sub(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q);
|
||||
void fe_add(fe h, const fe f, const fe g);
|
||||
void fe_tobytes(unsigned char *, const fe);
|
||||
void fe_invert(fe out, const fe z);
|
|
@ -0,0 +1,87 @@
|
|||
#include "how_monero_hodl_jni_CryptoOpsUtil.h"
|
||||
#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
#include "crypto-ops.h"
|
||||
|
||||
JNIEXPORT jbyteArray JNICALL Java_how_monero_hodl_jni_CryptoOpsUtil_scalarMult
|
||||
(JNIEnv *env, jclass cls, jbyteArray pointBytes, jbyteArray scalarBytes) {
|
||||
|
||||
jbyte* pointBytesBuf = (*env)->GetByteArrayElements(env, pointBytes, NULL);
|
||||
jbyte* scalarBytesBuf = (*env)->GetByteArrayElements(env, scalarBytes, NULL);
|
||||
|
||||
ge_p3 pointp3;
|
||||
ge_p2 pointp2;
|
||||
ge_p2 pointp2result;
|
||||
|
||||
ge_frombytes_vartime(&pointp3, (const unsigned char *) pointBytesBuf);
|
||||
|
||||
ge_p3_to_p2(&pointp2, &pointp3);
|
||||
|
||||
ge_scalarmult(&pointp2result, (const unsigned char *) scalarBytesBuf, &pointp3);
|
||||
|
||||
unsigned char resultBytes[32];
|
||||
ge_tobytes(resultBytes, &pointp2result);
|
||||
|
||||
|
||||
jbyteArray returnData = (*env)->NewByteArray(env, 32);
|
||||
(*env)->SetByteArrayRegion(env, returnData, 0, 32, (jbyte *) resultBytes);
|
||||
|
||||
return returnData;
|
||||
|
||||
}
|
||||
|
||||
//void ge_scalarmult_base(ge_p3 *h, const unsigned char *a) {
|
||||
JNIEXPORT jbyteArray JNICALL Java_how_monero_hodl_jni_CryptoOpsUtil_scalarMultBase
|
||||
(JNIEnv *env, jclass cls, jbyteArray scalarBytes) {
|
||||
|
||||
jbyte* scalarBytesBuf = (*env)->GetByteArrayElements(env, scalarBytes, NULL);
|
||||
|
||||
ge_p3 pointp3result;
|
||||
ge_p2 pointp2result;
|
||||
|
||||
ge_scalarmult_base(&pointp3result, (const unsigned char *) scalarBytesBuf);
|
||||
|
||||
ge_p3_to_p2(&pointp2result, &pointp3result);
|
||||
|
||||
unsigned char resultBytes[32];
|
||||
ge_tobytes(resultBytes, &pointp2result);
|
||||
|
||||
|
||||
jbyteArray returnData = (*env)->NewByteArray(env, 32);
|
||||
(*env)->SetByteArrayRegion(env, returnData, 0, 32, (jbyte *) resultBytes);
|
||||
|
||||
return returnData;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//ge_double_scalarmult_base_vartime(ge_p2 *r, const unsigned char *a, const ge_p3 *A, const unsigned char *b)
|
||||
//r = a * A + b * B
|
||||
JNIEXPORT jbyteArray JNICALL Java_how_monero_hodl_jni_CryptoOpsUtil_doubleScalarMultBaseVartime
|
||||
(JNIEnv *env, jclass cls, jbyteArray aScalarBytes, jbyteArray pointBytes, jbyteArray bScalarBytes) {
|
||||
|
||||
jbyte* pointBytesBuf = (*env)->GetByteArrayElements(env, pointBytes, NULL);
|
||||
jbyte* aScalarBytesBuf = (*env)->GetByteArrayElements(env, aScalarBytes, NULL);
|
||||
jbyte* bScalarBytesBuf = (*env)->GetByteArrayElements(env, bScalarBytes, NULL);
|
||||
|
||||
ge_p3 pointp3;
|
||||
ge_p2 pointp2;
|
||||
ge_p2 pointp2result;
|
||||
|
||||
ge_frombytes_vartime(&pointp3, (const unsigned char *) pointBytesBuf);
|
||||
|
||||
ge_double_scalarmult_base_vartime(&pointp2result, (const unsigned char *) aScalarBytesBuf, &pointp3, (const unsigned char *) bScalarBytesBuf);
|
||||
|
||||
unsigned char resultBytes[32];
|
||||
ge_tobytes(resultBytes, &pointp2result);
|
||||
|
||||
|
||||
jbyteArray returnData = (*env)->NewByteArray(env, 32);
|
||||
(*env)->SetByteArrayRegion(env, returnData, 0, 32, (jbyte *) resultBytes);
|
||||
|
||||
return returnData;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
/* DO NOT EDIT THIS FILE - it is machine generated */
|
||||
#include <jni.h>
|
||||
/* Header for class how_monero_hodl_jni_CryptoOpsUtil */
|
||||
|
||||
#ifndef _Included_how_monero_hodl_jni_CryptoOpsUtil
|
||||
#define _Included_how_monero_hodl_jni_CryptoOpsUtil
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
/*
|
||||
* Class: how_monero_hodl_jni_CryptoOpsUtil
|
||||
* Method: scalarMult
|
||||
* Signature: ([B[B)[B
|
||||
*/
|
||||
JNIEXPORT jbyteArray JNICALL Java_how_monero_hodl_jni_CryptoOpsUtil_scalarMult
|
||||
(JNIEnv *, jclass, jbyteArray, jbyteArray);
|
||||
|
||||
/*
|
||||
* Class: how_monero_hodl_jni_CryptoOpsUtil
|
||||
* Method: doubleScalarMultBaseVartime
|
||||
* Signature: ([B[B[B)[B
|
||||
*/
|
||||
JNIEXPORT jbyteArray JNICALL Java_how_monero_hodl_jni_CryptoOpsUtil_doubleScalarMultBaseVartime
|
||||
(JNIEnv *, jclass, jbyteArray, jbyteArray, jbyteArray);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
30
source-code/RuffCT-java/c/warnings.h
Normal file
30
source-code/RuffCT-java/c/warnings.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
#pragma once
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
|
||||
#define PUSH_WARNINGS __pragma(warning(push))
|
||||
#define POP_WARNINGS __pragma(warning(pop))
|
||||
#define DISABLE_VS_WARNINGS(w) __pragma(warning(disable: w))
|
||||
#define DISABLE_GCC_WARNING(w)
|
||||
#define DISABLE_CLANG_WARNING(w)
|
||||
#define DISABLE_GCC_AND_CLANG_WARNING(w)
|
||||
|
||||
#else
|
||||
|
||||
#include <boost/preprocessor/stringize.hpp>
|
||||
|
||||
#define PUSH_WARNINGS _Pragma("GCC diagnostic push")
|
||||
#define POP_WARNINGS _Pragma("GCC diagnostic pop")
|
||||
#define DISABLE_VS_WARNINGS(w)
|
||||
|
||||
#if defined(__clang__)
|
||||
#define DISABLE_GCC_WARNING(w)
|
||||
#define DISABLE_CLANG_WARNING DISABLE_GCC_AND_CLANG_WARNING
|
||||
#else
|
||||
#define DISABLE_GCC_WARNING DISABLE_GCC_AND_CLANG_WARNING
|
||||
#define DISABLE_CLANG_WARNING(w)
|
||||
#endif
|
||||
|
||||
#define DISABLE_GCC_AND_CLANG_WARNING(w) _Pragma(BOOST_PP_STRINGIZE(GCC diagnostic ignored BOOST_PP_STRINGIZE(-W##w)))
|
||||
|
||||
#endif
|
19
source-code/RuffCT-java/doc/readme.txt
Normal file
19
source-code/RuffCT-java/doc/readme.txt
Normal file
|
@ -0,0 +1,19 @@
|
|||
Instructions for compiling the Ed25519 native library
|
||||
-----------------------------------------------------
|
||||
|
||||
Note: You don't need to do any of this if you don't want to. The code will automatically fall back to a pure Java Ed25519 implementation.
|
||||
|
||||
|
||||
To compile the JNI C library:
|
||||
|
||||
cd <path to RuffCT-java/c directory>
|
||||
|
||||
# replace <OS> with darwin on OSX, or with linux on Linux.
|
||||
gcc -fPIC -c how_monero_hodl_jni_CryptoOpsUtil.c crypto-ops.c crypto-ops-data.c -I /<path to jdk>/include/<OS>/ -I /<path to jdk>/include
|
||||
|
||||
gcc -dynamiclib -o libcryptoopsutil.jnilib how_monero_hodl_jni_CryptoOpsUtil.o crypto-ops.o crypto-ops-data.o -framework JavaVM
|
||||
|
||||
When running Java code that uses this JNI library, provide the following command line argument:
|
||||
|
||||
-Djava.library.path=/<path to directory containing libcryptoopsutil.jnilib>
|
||||
|
BIN
source-code/RuffCT-java/lib/commons-codec-1.10.jar
Normal file
BIN
source-code/RuffCT-java/lib/commons-codec-1.10.jar
Normal file
Binary file not shown.
BIN
source-code/RuffCT-java/lib/commons-pool2-2.4.2.jar
Normal file
BIN
source-code/RuffCT-java/lib/commons-pool2-2.4.2.jar
Normal file
Binary file not shown.
449
source-code/RuffCT-java/src/com/joemelsha/crypto/hash/Keccak.java
Executable file
449
source-code/RuffCT-java/src/com/joemelsha/crypto/hash/Keccak.java
Executable file
|
@ -0,0 +1,449 @@
|
|||
package com.joemelsha.crypto.hash;
|
||||
|
||||
import java.nio.*;
|
||||
|
||||
/**
|
||||
* @author Joseph Robert Melsha (jrmelsha@olivet.edu)
|
||||
*
|
||||
* Source: https://github.com/jrmelsha/keccak
|
||||
* Created: Jun 23, 2016
|
||||
*
|
||||
* Copyright 2016 Joseph Robert Melsha
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
public class Keccak {
|
||||
private static final int MAX_STATE_SIZE = 1600;
|
||||
private static final int MAX_STATE_SIZE_WORDS = MAX_STATE_SIZE / 64;
|
||||
|
||||
protected int rateSizeBits, digestSizeBits;
|
||||
private long[] state = new long[MAX_STATE_SIZE_WORDS];
|
||||
private int rateBits;
|
||||
private boolean padded;
|
||||
|
||||
public Keccak(int digestSizeBits) {
|
||||
reset(digestSizeBits);
|
||||
}
|
||||
|
||||
public Keccak(Keccak other) {
|
||||
System.arraycopy(other.state, 0, state, 0, other.state.length);
|
||||
rateBits = other.rateBits;
|
||||
rateSizeBits = other.rateSizeBits;
|
||||
digestSizeBits = other.digestSizeBits;
|
||||
padded = other.padded;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Keccak-" + digestSizeBits;
|
||||
}
|
||||
|
||||
public int rateSize() {
|
||||
return rateSizeBits >>> 3;
|
||||
}
|
||||
|
||||
public int digestSize() {
|
||||
return digestSizeBits >>> 3;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
reset(rateSizeBits, digestSizeBits);
|
||||
}
|
||||
|
||||
protected int rateSizeBitsFor(int digestSizeBits) {
|
||||
//@formatter:off
|
||||
switch (digestSizeBits) {
|
||||
case 288: return 1024;
|
||||
case 128: return 1344;
|
||||
case 224: return 1152;
|
||||
case 256: return 1088;
|
||||
case 384: return 832;
|
||||
case 512: return 576;
|
||||
default: throw new IllegalArgumentException("Invalid digestSizeBits: " + digestSizeBits + " ⊄ { 128, 224, 256, 288, 384, 512 }");
|
||||
}
|
||||
//@formatter:on
|
||||
}
|
||||
|
||||
public void reset(int digestSizeBits) {
|
||||
reset(rateSizeBitsFor(digestSizeBits), digestSizeBits);
|
||||
}
|
||||
|
||||
protected void reset(int rateSizebits, int digestSizeBits) {
|
||||
if (rateSizebits + digestSizeBits * 2 != MAX_STATE_SIZE)
|
||||
throw new IllegalArgumentException("Invalid rateSizebits + digestSizeBits * 2: " + rateSizebits + " + " + digestSizeBits + " * 2 != " + MAX_STATE_SIZE);
|
||||
if (rateSizebits <= 0 || (rateSizebits & 0x3f) > 0)
|
||||
throw new IllegalArgumentException("Invalid rateSizebits: " + rateSizebits);
|
||||
|
||||
for (int i = 0; i < MAX_STATE_SIZE_WORDS; ++i)
|
||||
state[i] = 0;
|
||||
rateBits = 0;
|
||||
|
||||
rateSizeBits = rateSizebits;
|
||||
this.digestSizeBits = digestSizeBits;
|
||||
padded = false;
|
||||
}
|
||||
|
||||
public void update(byte in) {
|
||||
updateBits(in & 0xff, 8);
|
||||
}
|
||||
|
||||
public void update(byte[] in) {
|
||||
update(ByteBuffer.wrap(in));
|
||||
}
|
||||
|
||||
public void update(byte[] in, int offset, int length) {
|
||||
update(ByteBuffer.wrap(in, offset, length));
|
||||
}
|
||||
|
||||
public void update(ByteBuffer in) {
|
||||
int inBytes = in.remaining();
|
||||
if (inBytes <= 0)
|
||||
return;
|
||||
|
||||
if (padded)
|
||||
throw new IllegalStateException("Cannot update while padded");
|
||||
|
||||
int rateBits = this.rateBits;
|
||||
if ((rateBits & 0x7) > 0) //this could be implemented but would introduce considerable performance degradation - also, it's never technically possible.
|
||||
throw new IllegalStateException("Cannot update while in bit-mode");
|
||||
|
||||
long[] state = this.state;
|
||||
int rateBytes = rateBits >>> 3;
|
||||
|
||||
int rateBytesWord = rateBytes & 0x7;
|
||||
if (rateBytesWord > 0) {
|
||||
//logically must have space at this point
|
||||
int c = 8 - rateBytesWord;
|
||||
if (c > inBytes)
|
||||
c = inBytes;
|
||||
int i = rateBytes >>> 3;
|
||||
long w = state[i];
|
||||
rateBytes += c;
|
||||
inBytes -= c;
|
||||
rateBytesWord <<= 3;
|
||||
c = rateBytesWord + (c << 3);
|
||||
do {
|
||||
w ^= (long) (in.get() & 0xff) << rateBytesWord;
|
||||
rateBytesWord += 8;
|
||||
} while (rateBytesWord < c);
|
||||
state[i] = w;
|
||||
|
||||
if (inBytes > 0) {
|
||||
this.rateBits = rateBytes << 3;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int rateWords = rateBytes >>> 3;
|
||||
int rateSizeWords = rateSizeBits >>> 6;
|
||||
|
||||
int inWords = inBytes >>> 3;
|
||||
if (inWords > 0) {
|
||||
ByteOrder order = in.order();
|
||||
try {
|
||||
in.order(ByteOrder.LITTLE_ENDIAN);
|
||||
do {
|
||||
if (rateWords >= rateSizeWords) {
|
||||
Keccak.keccak(state);
|
||||
rateWords = 0;
|
||||
}
|
||||
int c = rateSizeWords - rateWords;
|
||||
if (c > inWords)
|
||||
c = inWords;
|
||||
inWords -= c;
|
||||
c += rateWords;
|
||||
do {
|
||||
state[rateWords] ^= in.getLong();
|
||||
rateWords++;
|
||||
} while (rateWords < c);
|
||||
} while (inWords > 0);
|
||||
} finally {
|
||||
in.order(order);
|
||||
}
|
||||
inBytes &= 0x7;
|
||||
if (inBytes <= 0) {
|
||||
this.rateBits = rateWords << 6;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (rateWords >= rateSizeWords) {
|
||||
Keccak.keccak(state);
|
||||
rateWords = 0;
|
||||
}
|
||||
long w = state[rateWords];
|
||||
inBytes <<= 3;
|
||||
int i = 0;
|
||||
do {
|
||||
w ^= (long) (in.get() & 0xff) << i;
|
||||
i += 8;
|
||||
} while (i < inBytes);
|
||||
state[rateWords] = w;
|
||||
|
||||
this.rateBits = (rateWords << 6) | inBytes;
|
||||
}
|
||||
|
||||
protected void updateBits(long in, int inBits) {
|
||||
if (inBits < 0 || inBits > 64)
|
||||
throw new IllegalArgumentException("Invalid valueBits: " + 0 + " < " + inBits + " > " + 64);
|
||||
|
||||
if (inBits <= 0)
|
||||
return;
|
||||
|
||||
if (padded)
|
||||
throw new IllegalStateException("Cannot update while padded");
|
||||
|
||||
long[] state = this.state;
|
||||
int rateBits = this.rateBits;
|
||||
int rateBitsWord = rateBits & 0x3f;
|
||||
if (rateBitsWord > 0) {
|
||||
//logically must have space at this point
|
||||
int c = 64 - rateBitsWord;
|
||||
if (c > inBits)
|
||||
c = inBits;
|
||||
state[rateBits >>> 6] ^= (in & (-1L >>> c)) << rateBitsWord;
|
||||
rateBits += c;
|
||||
inBits -= c;
|
||||
if (inBits <= 0) {
|
||||
this.rateBits = rateBits;
|
||||
return;
|
||||
}
|
||||
in >>>= c;
|
||||
}
|
||||
if (rateBits >= rateSizeBits) {
|
||||
Keccak.keccak(state);
|
||||
rateBits = 0;
|
||||
}
|
||||
state[rateBits >>> 6] ^= in & (-1L >>> inBits);
|
||||
this.rateBits = rateBits + inBits;
|
||||
}
|
||||
|
||||
public ByteBuffer digest() {
|
||||
return digest(digestSize());
|
||||
}
|
||||
|
||||
public ByteBuffer digest(int outSize) {
|
||||
return digest(outSize, false);
|
||||
}
|
||||
|
||||
public ByteBuffer digest(int outSize, boolean direct) {
|
||||
ByteBuffer buffer = direct ? ByteBuffer.allocateDirect(outSize) : ByteBuffer.allocate(outSize);
|
||||
digest(buffer);
|
||||
buffer.flip();
|
||||
return buffer;
|
||||
}
|
||||
|
||||
public byte[] digestArray() {
|
||||
return digestArray(digestSize());
|
||||
}
|
||||
|
||||
public byte[] digestArray(int outSize) {
|
||||
byte[] array = new byte[outSize];
|
||||
digest(array, 0, outSize);
|
||||
return array;
|
||||
}
|
||||
|
||||
public void digest(byte[] out) {
|
||||
digest(ByteBuffer.wrap(out));
|
||||
}
|
||||
|
||||
public void digest(byte[] out, int offset, int length) {
|
||||
digest(ByteBuffer.wrap(out, offset, length));
|
||||
}
|
||||
|
||||
public void digest(ByteBuffer out) {
|
||||
int outBytes = out.remaining();
|
||||
if (outBytes <= 0)
|
||||
return;
|
||||
|
||||
long[] state = this.state;
|
||||
int rateBits = this.rateBits;
|
||||
int rateBytes;
|
||||
if (!padded) {
|
||||
pad();
|
||||
padded = true;
|
||||
rateBits = 0;
|
||||
rateBytes = 0;
|
||||
} else {
|
||||
if ((rateBits & 0x7) > 0)
|
||||
throw new IllegalStateException("Cannot digest while in bit-mode"); //this could be implemented but would introduce considerable performance degradation - also, it's never technically possible.
|
||||
|
||||
rateBytes = rateBits >>> 3;
|
||||
int rateBytesWord = rateBytes & 0x7;
|
||||
if (rateBytesWord > 0) {
|
||||
int c = 8 - rateBytesWord;
|
||||
if (c > outBytes)
|
||||
c = outBytes;
|
||||
long w = state[rateBytes >>> 3];
|
||||
outBytes -= c;
|
||||
rateBytes += c;
|
||||
rateBytesWord <<= 3;
|
||||
c = (c << 3) + rateBytesWord;
|
||||
do {
|
||||
out.put((byte) (w >>> rateBytesWord));
|
||||
rateBytesWord += 8;
|
||||
} while (rateBytesWord < c);
|
||||
if (outBytes <= 0) {
|
||||
this.rateBits = rateBytes << 3;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int rateSizeWords = rateSizeBits >>> 6;
|
||||
int rateWords = rateBytes >>> 3;
|
||||
|
||||
int outWords = outBytes >>> 3;
|
||||
if (outWords > 0) {
|
||||
ByteOrder order = out.order();
|
||||
try {
|
||||
out.order(ByteOrder.LITTLE_ENDIAN);
|
||||
do {
|
||||
if (rateWords >= rateSizeWords) {
|
||||
squeeze();
|
||||
rateWords = 0;
|
||||
}
|
||||
int c = rateSizeWords - rateWords;
|
||||
if (c > outWords)
|
||||
c = outWords;
|
||||
outWords -= c;
|
||||
c += rateWords;
|
||||
do {
|
||||
out.putLong(state[rateWords]);
|
||||
rateWords++;
|
||||
} while (rateWords < c);
|
||||
} while (outWords > 0);
|
||||
} finally {
|
||||
out.order(order);
|
||||
}
|
||||
outBytes &= 0x7;
|
||||
if (outBytes <= 0) {
|
||||
this.rateBits = rateWords << 6;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (rateWords >= rateSizeWords) {
|
||||
squeeze();
|
||||
rateWords = 0;
|
||||
}
|
||||
long w = state[rateWords];
|
||||
outBytes <<= 3;
|
||||
int i = 0;
|
||||
do {
|
||||
out.put((byte) (w >>> i));
|
||||
i += 8;
|
||||
} while (i < outBytes);
|
||||
this.rateBits = (rateWords << 6) | outBytes;
|
||||
}
|
||||
|
||||
protected void squeeze() {
|
||||
Keccak.keccak(state);
|
||||
}
|
||||
|
||||
protected void pad() {
|
||||
updateBits(0x1, 1);
|
||||
if (rateBits >= rateSizeBits) {
|
||||
Keccak.keccak(state);
|
||||
rateBits = 0;
|
||||
}
|
||||
rateBits = rateSizeBits - 1;
|
||||
updateBits(0x1, 1);
|
||||
Keccak.keccak(state);
|
||||
}
|
||||
|
||||
/**
|
||||
* @formatter:off
|
||||
*/
|
||||
private static void keccak(long[] a) {
|
||||
//@formatter:off
|
||||
int c, i;
|
||||
long x, a_10_;
|
||||
long x0, x1, x2, x3, x4;
|
||||
long t0, t1, t2, t3, t4;
|
||||
long c0, c1, c2, c3, c4;
|
||||
long[] rc = RC;
|
||||
|
||||
i = 0;
|
||||
do {
|
||||
//theta (precalculation part)
|
||||
c0 = a[0] ^ a[5 + 0] ^ a[10 + 0] ^ a[15 + 0] ^ a[20 + 0];
|
||||
c1 = a[1] ^ a[5 + 1] ^ a[10 + 1] ^ a[15 + 1] ^ a[20 + 1];
|
||||
c2 = a[2] ^ a[5 + 2] ^ a[10 + 2] ^ a[15 + 2] ^ a[20 + 2];
|
||||
c3 = a[3] ^ a[5 + 3] ^ a[10 + 3] ^ a[15 + 3] ^ a[20 + 3];
|
||||
c4 = a[4] ^ a[5 + 4] ^ a[10 + 4] ^ a[15 + 4] ^ a[20 + 4];
|
||||
|
||||
t0 = (c0 << 1) ^ (c0 >>> (64 - 1)) ^ c3;
|
||||
t1 = (c1 << 1) ^ (c1 >>> (64 - 1)) ^ c4;
|
||||
t2 = (c2 << 1) ^ (c2 >>> (64 - 1)) ^ c0;
|
||||
t3 = (c3 << 1) ^ (c3 >>> (64 - 1)) ^ c1;
|
||||
t4 = (c4 << 1) ^ (c4 >>> (64 - 1)) ^ c2;
|
||||
|
||||
//theta (xorring part) + rho + pi
|
||||
a[ 0] ^= t1;
|
||||
x = a[ 1] ^ t2; a_10_ = (x << 1) | (x >>> (64 - 1));
|
||||
x = a[ 6] ^ t2; a[ 1] = (x << 44) | (x >>> (64 - 44));
|
||||
x = a[ 9] ^ t0; a[ 6] = (x << 20) | (x >>> (64 - 20));
|
||||
x = a[22] ^ t3; a[ 9] = (x << 61) | (x >>> (64 - 61));
|
||||
|
||||
x = a[14] ^ t0; a[22] = (x << 39) | (x >>> (64 - 39));
|
||||
x = a[20] ^ t1; a[14] = (x << 18) | (x >>> (64 - 18));
|
||||
x = a[ 2] ^ t3; a[20] = (x << 62) | (x >>> (64 - 62));
|
||||
x = a[12] ^ t3; a[ 2] = (x << 43) | (x >>> (64 - 43));
|
||||
x = a[13] ^ t4; a[12] = (x << 25) | (x >>> (64 - 25));
|
||||
|
||||
x = a[19] ^ t0; a[13] = (x << 8) | (x >>> (64 - 8));
|
||||
x = a[23] ^ t4; a[19] = (x << 56) | (x >>> (64 - 56));
|
||||
x = a[15] ^ t1; a[23] = (x << 41) | (x >>> (64 - 41));
|
||||
x = a[ 4] ^ t0; a[15] = (x << 27) | (x >>> (64 - 27));
|
||||
x = a[24] ^ t0; a[ 4] = (x << 14) | (x >>> (64 - 14));
|
||||
|
||||
x = a[21] ^ t2; a[24] = (x << 2) | (x >>> (64 - 2));
|
||||
x = a[ 8] ^ t4; a[21] = (x << 55) | (x >>> (64 - 55));
|
||||
x = a[16] ^ t2; a[ 8] = (x << 45) | (x >>> (64 - 45));
|
||||
x = a[ 5] ^ t1; a[16] = (x << 36) | (x >>> (64 - 36));
|
||||
x = a[ 3] ^ t4; a[ 5] = (x << 28) | (x >>> (64 - 28));
|
||||
|
||||
x = a[18] ^ t4; a[ 3] = (x << 21) | (x >>> (64 - 21));
|
||||
x = a[17] ^ t3; a[18] = (x << 15) | (x >>> (64 - 15));
|
||||
x = a[11] ^ t2; a[17] = (x << 10) | (x >>> (64 - 10));
|
||||
x = a[ 7] ^ t3; a[11] = (x << 6) | (x >>> (64 - 6));
|
||||
x = a[10] ^ t1; a[ 7] = (x << 3) | (x >>> (64 - 3));
|
||||
a[10] = a_10_;
|
||||
|
||||
//chi
|
||||
c = 0;
|
||||
do {
|
||||
x0 = a[c + 0]; x1 = a[c + 1]; x2 = a[c + 2]; x3 = a[c + 3]; x4 = a[c + 4];
|
||||
a[c + 0] = x0 ^ ((~x1) & x2);
|
||||
a[c + 1] = x1 ^ ((~x2) & x3);
|
||||
a[c + 2] = x2 ^ ((~x3) & x4);
|
||||
a[c + 3] = x3 ^ ((~x4) & x0);
|
||||
a[c + 4] = x4 ^ ((~x0) & x1);
|
||||
|
||||
c += 5;
|
||||
} while (c < 25);
|
||||
|
||||
//iota
|
||||
a[0] ^= rc[i];
|
||||
|
||||
i++;
|
||||
} while (i < 24);
|
||||
//@formatter:on
|
||||
}
|
||||
|
||||
private static final long[] RC = { 0x0000000000000001L, 0x0000000000008082L, 0x800000000000808AL, 0x8000000080008000L, 0x000000000000808BL, 0x0000000080000001L, 0x8000000080008081L,
|
||||
0x8000000000008009L, 0x000000000000008AL, 0x0000000000000088L, 0x0000000080008009L, 0x000000008000000AL, 0x000000008000808BL, 0x800000000000008BL,
|
||||
0x8000000000008089L, 0x8000000000008003L, 0x8000000000008002L, 0x8000000000000080L, 0x000000000000800AL, 0x800000008000000AL, 0x8000000080008081L,
|
||||
0x8000000000008080L, 0x0000000080000001L, 0x8000000080008008L };
|
||||
}
|
|
@ -0,0 +1,155 @@
|
|||
package how.monero.hodl.crypto;
|
||||
|
||||
import com.joemelsha.crypto.hash.Keccak;
|
||||
import how.monero.hodl.util.ExceptionAdapter;
|
||||
import org.apache.commons.pool2.BasePooledObjectFactory;
|
||||
import org.apache.commons.pool2.PooledObject;
|
||||
import org.apache.commons.pool2.impl.DefaultPooledObject;
|
||||
import org.apache.commons.pool2.impl.GenericObjectPool;
|
||||
import org.nem.core.crypto.ed25519.arithmetic.*;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
|
||||
import static how.monero.hodl.crypto.HashToPoint.hashToPoint;
|
||||
import static how.monero.hodl.util.ByteUtil.*;
|
||||
|
||||
public class CryptoUtil {
|
||||
|
||||
public static GenericObjectPool<Keccak> keccakPool = new GenericObjectPool<Keccak>(new BasePooledObjectFactory<Keccak>() {
|
||||
|
||||
@Override
|
||||
public PooledObject<Keccak> wrap(Keccak keccak) {
|
||||
return new DefaultPooledObject<>(keccak);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Keccak create() throws Exception {
|
||||
return new Keccak(256);
|
||||
}
|
||||
});
|
||||
|
||||
public static final Ed25519GroupElement G = Ed25519Group.BASE_POINT;
|
||||
public static final Ed25519GroupElement H = new Ed25519EncodedGroupElement(hexToBytes("8b655970153799af2aeadc9ff1add0ea6c7251d54154cfa92c173a0dd39c1f94")).decode();
|
||||
static {
|
||||
H.precomputeForScalarMultiplication();
|
||||
}
|
||||
|
||||
public static Scalar hashToScalar(byte[] a) {
|
||||
return new Scalar(scReduce32(fastHash(a)));
|
||||
}
|
||||
|
||||
public static byte[] fastHash(byte[] a) {
|
||||
try {
|
||||
Keccak keccak = keccakPool.borrowObject();
|
||||
try {
|
||||
keccak.reset();
|
||||
keccak.update(a);
|
||||
return keccak.digestArray();
|
||||
} finally {
|
||||
keccakPool.returnObject(keccak);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw ExceptionAdapter.toRuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static BigInteger l = BigInteger.valueOf(2).pow(252).add(new BigInteger("27742317777372353535851937790883648493"));
|
||||
|
||||
public static byte[] scReduce32(byte[] a) {
|
||||
byte[] r = getBigIntegerFromUnsignedLittleEndianByteArray(a).mod(l).toByteArray();
|
||||
return ensure32BytesAndConvertToLittleEndian(r);
|
||||
}
|
||||
|
||||
public static byte[] ensure32BytesAndConvertToLittleEndian(byte[] r) {
|
||||
byte[] s = new byte[32];
|
||||
if(r.length>32) System.arraycopy(r, 1, s, 0, s.length);
|
||||
else System.arraycopy(r, 0, s, 32 - r.length, r.length);
|
||||
reverseByteArrayInPlace(s);
|
||||
return s;
|
||||
}
|
||||
|
||||
public static BigInteger getBigIntegerFromUnsignedLittleEndianByteArray(byte[] a1) {
|
||||
byte[] a = new byte[a1.length];
|
||||
System.arraycopy(a1, 0, a, 0, 32);
|
||||
reverseByteArrayInPlace(a);
|
||||
byte[] a2 = new byte[33];
|
||||
System.arraycopy(a, 0, a2, 1, 32);
|
||||
return new BigInteger(a2);
|
||||
}
|
||||
public static byte[] getUnsignedLittleEndianByteArrayFromBigInteger(BigInteger n) {
|
||||
byte[] a = n.toByteArray();
|
||||
byte[] a2 = new byte[32];
|
||||
System.arraycopy(a, 0, a2, 32-a.length, a.length);
|
||||
reverseByteArrayInPlace(a2);
|
||||
return a2;
|
||||
}
|
||||
|
||||
public static final Random random = new SecureRandom();
|
||||
public static byte[] randomPointAsBytes() {
|
||||
return randomPoint().encode().getRaw();
|
||||
}
|
||||
public static Ed25519GroupElement randomPoint() {
|
||||
return Ed25519Group.BASE_POINT.scalarMultiply(Scalar.randomScalar());
|
||||
}
|
||||
public static byte[] randomMessage(int len) {
|
||||
byte[] m = new byte[len];
|
||||
random.nextBytes(m);
|
||||
return m;
|
||||
}
|
||||
|
||||
public static byte[] toBytes(Ed25519GroupElement[] a) {
|
||||
byte[] r = new byte[0];
|
||||
for(Ed25519GroupElement ai : a) r = concat(r, ai.encode().getRaw());
|
||||
return r;
|
||||
}
|
||||
|
||||
public static Scalar sumArray(Scalar[] a) {
|
||||
Scalar r = Scalar.ZERO;
|
||||
for(Scalar ai : a) r = r.add(ai);
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static PointPair COMeg(Scalar xAmount, Scalar rMask) {
|
||||
return new PointPair(G.scalarMultiply(xAmount).add(getHpnGLookup(1).scalarMultiply(rMask).toCached()), G.scalarMultiply(rMask));
|
||||
}
|
||||
|
||||
public static Ed25519GroupElement COMp(Scalar xAmount, Scalar rMask) {
|
||||
return G.scalarMultiply(xAmount).add(getHpnGLookup(1).scalarMultiply(rMask).toCached());
|
||||
}
|
||||
|
||||
public static Ed25519GroupElement COMb(Scalar[][] x, Scalar r) {
|
||||
int m = x.length;
|
||||
int n = x[0].length;
|
||||
Ed25519GroupElement A = G.scalarMultiply(r);
|
||||
for(int j=0; j<m; j++) {
|
||||
for(int i=0; i<n; i++) {
|
||||
A = A.toP3().add(getHpnGLookup(j * n + i + 1).scalarMultiply(x[j][i]).toCached());
|
||||
}
|
||||
}
|
||||
return A;
|
||||
}
|
||||
|
||||
|
||||
public static Map<Integer, Ed25519GroupElement> HpnGLookup = new HashMap<>();
|
||||
|
||||
public static Ed25519GroupElement getHpnGLookup(int n) {
|
||||
if(!HpnGLookup.containsKey(n)) {
|
||||
Ed25519GroupElement HpnG = hashToPoint(G.scalarMultiply(Scalar.intToScalar(n)));
|
||||
//HpnG.precomputeForScalarMultiplication(); // try precomputed vs non-precomputed to check best performance
|
||||
HpnGLookup.put(n, HpnG);
|
||||
}
|
||||
return HpnGLookup.get(n);
|
||||
}
|
||||
|
||||
public static PointPair ENCeg(Ed25519GroupElement X, Scalar r) {
|
||||
return new PointPair(getHpnGLookup(1).scalarMultiply(r).toP3().add(X.toCached()), G.scalarMultiply(r));
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,136 @@
|
|||
package how.monero.hodl.crypto;
|
||||
|
||||
import org.nem.core.crypto.ed25519.arithmetic.Ed25519EncodedGroupElement;
|
||||
import org.nem.core.crypto.ed25519.arithmetic.Ed25519GroupElement;
|
||||
import org.nem.core.utils.ArrayUtils;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
import static how.monero.hodl.crypto.CryptoUtil.*;
|
||||
import static how.monero.hodl.util.ByteUtil.*;
|
||||
|
||||
public class HashToPoint {
|
||||
|
||||
private static BigInteger b = BigInteger.valueOf(256);
|
||||
private static BigInteger q = BigInteger.valueOf(2).pow(255).subtract(BigInteger.valueOf(19));
|
||||
private static BigInteger p = q;
|
||||
private static BigInteger l = BigInteger.valueOf(2).pow(252).add(new BigInteger("27742317777372353535851937790883648493"));
|
||||
private static BigInteger I = expmod(BigInteger.valueOf(2), (q.subtract(BigInteger.ONE)).divide(BigInteger.valueOf(4)), q);
|
||||
|
||||
|
||||
public static Ed25519GroupElement hashToPoint(Ed25519GroupElement a) {
|
||||
return hashToPoint(a.encode().getRaw());
|
||||
}
|
||||
|
||||
public static Ed25519GroupElement hashToPoint(byte[] a) {
|
||||
|
||||
byte[] hash = fastHash(a);
|
||||
|
||||
BigInteger u = ArrayUtils.toBigInteger(hash).mod(q);
|
||||
|
||||
BigInteger A = BigInteger.valueOf(486662);
|
||||
|
||||
BigInteger sqrtm1 = ed25519Sqroot(BigInteger.valueOf(-1));
|
||||
|
||||
BigInteger w = (BigInteger.valueOf(2).multiply(u).multiply(u).add(BigInteger.ONE)).mod(q);
|
||||
BigInteger xp = w.multiply(w).subtract(BigInteger.valueOf(2).multiply(A).multiply(A).multiply(u).multiply(u)).mod(q);
|
||||
|
||||
BigInteger b = w.multiply(ed25519Inv(xp));
|
||||
BigInteger e = (q.add(BigInteger.valueOf(3)).divide(BigInteger.valueOf(8)));
|
||||
BigInteger rx = expmod(b, e, q);
|
||||
|
||||
BigInteger x = rx.multiply(rx).multiply(w.multiply(w).subtract(BigInteger.valueOf(2).multiply(A).multiply(A).multiply(u).multiply(u))).mod(q);
|
||||
|
||||
BigInteger y = (BigInteger.valueOf(2).multiply(u).multiply(u).add(BigInteger.ONE).subtract(x)).mod(q);
|
||||
|
||||
BigInteger z;
|
||||
|
||||
boolean negative = false;
|
||||
if (!y.equals(BigInteger.ZERO)) {
|
||||
y = (w.add(x)).mod(q);
|
||||
if (!y.equals(BigInteger.ZERO)) {
|
||||
negative = true;
|
||||
} else {
|
||||
rx = rx.multiply(BigInteger.valueOf(-1)).multiply(ed25519Sqroot(BigInteger.valueOf(-2).multiply(A).multiply((A.add(BigInteger.valueOf(2)))))).mod(q);
|
||||
negative = false;
|
||||
}
|
||||
} else {
|
||||
rx = rx.multiply(BigInteger.valueOf(-1)).multiply(ed25519Sqroot(BigInteger.valueOf(2).multiply(A).multiply((A.add(BigInteger.valueOf(2)))))).mod(q);
|
||||
}
|
||||
|
||||
BigInteger sign;
|
||||
if (!negative) {
|
||||
rx = (rx.multiply(u)).mod(q);
|
||||
z = (BigInteger.valueOf(-2).multiply(A).multiply(u).multiply(u)).mod(q);
|
||||
sign = BigInteger.ZERO;
|
||||
} else {
|
||||
z = BigInteger.valueOf(-1).multiply(A);
|
||||
x = x.multiply(sqrtm1).mod(q);
|
||||
y = (w.subtract(x)).mod(q);
|
||||
if (!y.equals(BigInteger.ZERO)) {
|
||||
rx = rx.multiply(ed25519Sqroot(BigInteger.valueOf(-1).multiply(sqrtm1).multiply(A).multiply((A.add(BigInteger.valueOf(2)))))).mod(q);
|
||||
} else {
|
||||
rx = rx.multiply(BigInteger.valueOf(-1)).multiply(ed25519Sqroot(sqrtm1.multiply(A).multiply((A.add(BigInteger.valueOf(2)))))).mod(q);
|
||||
}
|
||||
sign = BigInteger.ONE;
|
||||
}
|
||||
if(!(rx.mod(BigInteger.valueOf(2)).equals(sign))) {
|
||||
rx = rx.mod(q).negate();
|
||||
}
|
||||
BigInteger rz = (z.add(w)).mod(q);
|
||||
BigInteger ry = (z.subtract(w)).mod(q);
|
||||
rx = (rx.multiply(rz)).mod(q);
|
||||
|
||||
Ed25519GroupElement P = pointCompress(rx, ry, rz);
|
||||
Ed25519GroupElement P8 = P.toP2().dbl().toP2().dbl().toP2().dbl().toP3();
|
||||
return P8;
|
||||
|
||||
}
|
||||
|
||||
private static Ed25519GroupElement pointCompress(BigInteger P0, BigInteger P1, BigInteger P2) {
|
||||
BigInteger zinv = modpInv(P2);
|
||||
BigInteger x = P0.multiply(zinv).mod(p);
|
||||
BigInteger y = P1.multiply(zinv).mod(p);
|
||||
|
||||
BigInteger r = y.or((x.and(BigInteger.ONE)).shiftLeft(255));
|
||||
|
||||
byte[] a = r.toByteArray();
|
||||
|
||||
byte[] b = new byte[32];
|
||||
|
||||
if(a.length>32) System.arraycopy(a, a.length-32, b, 0, 32);
|
||||
else if(a.length<32) System.arraycopy(a, 0, b, 32-a.length, a.length);
|
||||
else System.arraycopy(a, 0, b, 0, 32);
|
||||
|
||||
reverseByteArrayInPlace(b);
|
||||
return new Ed25519EncodedGroupElement(b).decode();
|
||||
}
|
||||
|
||||
private static BigInteger modpInv(BigInteger x) {
|
||||
return x.modPow(p.subtract(BigInteger.valueOf(2)), p);
|
||||
}
|
||||
|
||||
private static BigInteger ed25519Inv(BigInteger x) {
|
||||
return expmod(x, q.subtract(BigInteger.valueOf(2)), q);
|
||||
}
|
||||
|
||||
private static BigInteger ed25519Sqroot(BigInteger xx) {
|
||||
BigInteger x = expmod(xx, q.add(BigInteger.valueOf(3)).divide(BigInteger.valueOf(8)), q);
|
||||
if (!x.multiply(x).subtract(xx).mod(q).equals(BigInteger.ZERO)) {
|
||||
x = x.multiply(I).mod(q);
|
||||
}
|
||||
if (!x.multiply(x).subtract(xx).mod(q).equals(BigInteger.ZERO)) {
|
||||
throw new RuntimeException("no square root!");
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
private static BigInteger expmod(BigInteger b, BigInteger e, BigInteger m) {
|
||||
if (e.equals(BigInteger.ZERO)) return BigInteger.ONE;
|
||||
BigInteger t = expmod(b, e.divide(BigInteger.valueOf(2)), m).pow(2).mod(m);
|
||||
if(e.and(BigInteger.ONE).equals(BigInteger.ONE)) t = (t.multiply(b)).mod(m);
|
||||
return t;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package how.monero.hodl.crypto;
|
||||
|
||||
import org.nem.core.crypto.ed25519.arithmetic.Ed25519GroupElement;
|
||||
|
||||
import static how.monero.hodl.util.ByteUtil.*;
|
||||
|
||||
public class PointPair {
|
||||
public Ed25519GroupElement P1;
|
||||
public Ed25519GroupElement P2;
|
||||
public PointPair(Ed25519GroupElement P1, Ed25519GroupElement P2) {
|
||||
this.P1 = P1;
|
||||
this.P2 = P2;
|
||||
}
|
||||
public byte[] toBytes() {
|
||||
return concat(P1.encode().getRaw(), P2.encode().getRaw());
|
||||
}
|
||||
public PointPair add(PointPair a) {
|
||||
return new PointPair(P1.toP3().add(a.P1.toP3().toCached()), P2.toP3().add(a.P2.toP3().toCached()));
|
||||
}
|
||||
public PointPair subtract(PointPair a) {
|
||||
return new PointPair(P1.toP3().subtract(a.P1.toCached()), P2.toP3().subtract(a.P2.toCached()));
|
||||
}
|
||||
public PointPair multiply(Scalar n) {
|
||||
return new PointPair(P1.toP3().scalarMultiply(n), P2.toP3().scalarMultiply(n));
|
||||
}
|
||||
|
||||
public boolean equals(PointPair obj) {
|
||||
return P1.toP3().equals(obj.P1.toP3()) && P2.toP3().equals(obj.P2.toP3());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "(P1: " + bytesToHex(P1.encode().getRaw()) + ", P2: " + P2 + ")";
|
||||
}
|
||||
|
||||
}
|
||||
|
100
source-code/RuffCT-java/src/how/monero/hodl/crypto/Scalar.java
Normal file
100
source-code/RuffCT-java/src/how/monero/hodl/crypto/Scalar.java
Normal file
|
@ -0,0 +1,100 @@
|
|||
package how.monero.hodl.crypto;
|
||||
|
||||
import how.monero.hodl.util.ByteUtil;
|
||||
import org.nem.core.crypto.ed25519.arithmetic.Ed25519EncodedFieldElement;
|
||||
import org.nem.core.crypto.ed25519.arithmetic.Ed25519FieldElement;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.Arrays;
|
||||
|
||||
import static how.monero.hodl.crypto.CryptoUtil.*;
|
||||
import static how.monero.hodl.util.ByteUtil.*;
|
||||
|
||||
public class Scalar {
|
||||
|
||||
public final static Scalar ZERO = intToScalar(0);
|
||||
public final static Scalar ONE = intToScalar(1);
|
||||
public final static Scalar TWO = intToScalar(2);
|
||||
public final static Scalar MINUS_ONE = intToScalar(-1);
|
||||
|
||||
// use only for small numbers
|
||||
public static Scalar intToScalar(int a) {
|
||||
return new Scalar(scReduce32(getUnsignedLittleEndianByteArrayFromBigInteger(BigInteger.valueOf(a).mod(l))));
|
||||
}
|
||||
|
||||
public byte[] bytes;
|
||||
public Scalar(byte[] bytes) {
|
||||
this.bytes = bytes;
|
||||
}
|
||||
public Scalar(String hex) {
|
||||
this.bytes = ByteUtil.hexToBytes(hex);
|
||||
}
|
||||
public Scalar(BigInteger a) {
|
||||
this(scReduce32(getUnsignedLittleEndianByteArrayFromBigInteger(a.mod(l))));
|
||||
}
|
||||
public Ed25519EncodedFieldElement toEd25519EncodedFieldElement() {
|
||||
return new Ed25519EncodedFieldElement(bytes);
|
||||
}
|
||||
public Ed25519FieldElement toEd25519FieldElement() {
|
||||
return new Ed25519EncodedFieldElement(bytes).decode();
|
||||
}
|
||||
|
||||
public static Scalar randomScalar() {
|
||||
byte[] s = new byte[32];
|
||||
random.nextBytes(s);
|
||||
s = CryptoUtil.scReduce32(s);
|
||||
return new Scalar(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return bytesToHex(bytes);
|
||||
}
|
||||
|
||||
public BigInteger toBigInteger() {
|
||||
return getBigIntegerFromUnsignedLittleEndianByteArray(this.bytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return Arrays.equals(this.bytes, ((Scalar)obj).bytes);
|
||||
}
|
||||
|
||||
public static BigInteger[] scalarArrayToBigIntegerArray(Scalar[] a) {
|
||||
BigInteger[] r = new BigInteger[a.length];
|
||||
for(int i=0; i<a.length; i++) r[i] = a[i].toBigInteger();
|
||||
return r;
|
||||
}
|
||||
|
||||
public static void printScalarArray(Scalar[] a) {
|
||||
for(int i=0; i<a.length; i++) {
|
||||
System.out.print(a[i].toBigInteger()+"");
|
||||
if(i==a.length-1) System.out.println("");
|
||||
else System.out.print(", ");
|
||||
}
|
||||
}
|
||||
|
||||
public static Scalar[] bigIntegerArrayToScalarArray(BigInteger[] a) {
|
||||
int len = a.length;
|
||||
Scalar[] r = new Scalar[len];
|
||||
for(int i=0; i<len; i++) {
|
||||
r[i] = new Scalar(a[i]);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
public Scalar add(Scalar a) { return new Scalar(ensure32BytesAndConvertToLittleEndian(getBigIntegerFromUnsignedLittleEndianByteArray(this.bytes).add(getBigIntegerFromUnsignedLittleEndianByteArray(a.bytes)).mod(l).toByteArray())); }
|
||||
public Scalar sub(Scalar a) { return new Scalar(ensure32BytesAndConvertToLittleEndian(getBigIntegerFromUnsignedLittleEndianByteArray(this.bytes).subtract(getBigIntegerFromUnsignedLittleEndianByteArray(a.bytes)).mod(l).toByteArray())); }
|
||||
public Scalar mul(Scalar a) { return new Scalar(ensure32BytesAndConvertToLittleEndian(getBigIntegerFromUnsignedLittleEndianByteArray(this.bytes).multiply(getBigIntegerFromUnsignedLittleEndianByteArray(a.bytes)).mod(l).toByteArray())); }
|
||||
public Scalar sq() { return new Scalar(ensure32BytesAndConvertToLittleEndian(getBigIntegerFromUnsignedLittleEndianByteArray(this.bytes).multiply(getBigIntegerFromUnsignedLittleEndianByteArray(this.bytes)).mod(l).toByteArray())); }
|
||||
|
||||
public Scalar pow(int b) {
|
||||
Scalar result = Scalar.ONE;
|
||||
for(int i=0; i<b; i++) {
|
||||
result = result.mul(this);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package how.monero.hodl.crypto;
|
||||
|
||||
import org.nem.core.crypto.ed25519.arithmetic.Ed25519FieldElement;
|
||||
|
||||
public class ScalarPair {
|
||||
Ed25519FieldElement p1;
|
||||
Ed25519FieldElement p2;
|
||||
public ScalarPair(Ed25519FieldElement p1, Ed25519FieldElement p2) {
|
||||
this.p1 = p1;
|
||||
this.p2 = p2;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package how.monero.hodl.cursor;
|
||||
|
||||
import how.monero.hodl.crypto.PointPair;
|
||||
import how.monero.hodl.crypto.Scalar;
|
||||
import org.nem.core.crypto.ed25519.arithmetic.Ed25519EncodedFieldElement;
|
||||
import org.nem.core.crypto.ed25519.arithmetic.Ed25519EncodedGroupElement;
|
||||
import org.nem.core.crypto.ed25519.arithmetic.Ed25519FieldElement;
|
||||
import org.nem.core.crypto.ed25519.arithmetic.Ed25519GroupElement;
|
||||
|
||||
public class BootleRuffingCursor extends Cursor {
|
||||
public byte[] data;
|
||||
|
||||
public BootleRuffingCursor(byte[] data) {
|
||||
super(data);
|
||||
}
|
||||
|
||||
public Ed25519GroupElement readGroupElement() {
|
||||
return new Ed25519EncodedGroupElement(readBytes(32)).decode();
|
||||
}
|
||||
public Ed25519FieldElement readFieldElement() {
|
||||
return new Ed25519EncodedFieldElement(readBytes(32)).decode();
|
||||
}
|
||||
public PointPair[] readPointPairArray(int len) {
|
||||
PointPair[] result = new PointPair[len];
|
||||
for(int i=0; i<len; i++) result[i] = new PointPair(readGroupElement(), readGroupElement());
|
||||
return result;
|
||||
}
|
||||
public Scalar[][] readScalar2DArray(int m, int n) {
|
||||
Scalar[][] result = new Scalar[m][n];
|
||||
for(int j=0; j<m; j++) {
|
||||
for(int i=0; i<n; i++) {
|
||||
result[j][i] = readScalar();
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
package how.monero.hodl.cursor;
|
||||
|
||||
import how.monero.hodl.crypto.Scalar;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
public class Cursor {
|
||||
|
||||
public int pos = 0;
|
||||
protected byte[] data;
|
||||
public Cursor(byte[] data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public long readVarInt() {
|
||||
|
||||
long result = 0;
|
||||
int c = 0;
|
||||
|
||||
while(true) {
|
||||
boolean isLastByteInVarInt = true;
|
||||
int i = Byte.toUnsignedInt(data[pos]);
|
||||
if(i>=128) {
|
||||
isLastByteInVarInt = false;
|
||||
i-=128;
|
||||
}
|
||||
result += Math.round(i * Math.pow(128, c));
|
||||
c++;
|
||||
pos++;
|
||||
if(isLastByteInVarInt) break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
public BigInteger readVarIntAsBigInteger() {
|
||||
|
||||
BigInteger result = BigInteger.ZERO;
|
||||
int c = 0;
|
||||
|
||||
while(true) {
|
||||
boolean isLastByteInVarInt = true;
|
||||
int i = Byte.toUnsignedInt(data[pos]);
|
||||
if(i>=128) {
|
||||
isLastByteInVarInt = false;
|
||||
i-=128;
|
||||
}
|
||||
result = result.add(BigInteger.valueOf(Math.round(i * Math.pow(128, c))));
|
||||
c++;
|
||||
pos++;
|
||||
if(isLastByteInVarInt) break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public byte[] readBytes(int len) {
|
||||
byte[] bytes = new byte[len];
|
||||
System.arraycopy(data, pos, bytes, 0, len);
|
||||
pos+=len;
|
||||
return bytes;
|
||||
}
|
||||
public Scalar readScalar() {
|
||||
return new Scalar(readBytes(32));
|
||||
}
|
||||
|
||||
|
||||
public int readByte() {
|
||||
pos++;
|
||||
return Byte.toUnsignedInt(data[pos-1]);
|
||||
}
|
||||
|
||||
public byte[] readKey() {
|
||||
return readBytes(32);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package how.monero.hodl.jni;
|
||||
|
||||
public class CryptoOpsUtil {
|
||||
|
||||
static { System.loadLibrary("cryptoopsutil"); }
|
||||
|
||||
public static native byte[] scalarMult(byte[] point, byte[] scalar);
|
||||
public static native byte[] scalarMultBase(byte[] scalar);
|
||||
public static native byte[] doubleScalarMultBaseVartime(byte[] aScalar, byte[] point, byte[] bScalar);
|
||||
|
||||
}
|
|
@ -0,0 +1,560 @@
|
|||
package how.monero.hodl.ringSignature;
|
||||
|
||||
import how.monero.hodl.crypto.PointPair;
|
||||
import how.monero.hodl.crypto.Scalar;
|
||||
import how.monero.hodl.cursor.BootleRuffingCursor;
|
||||
import how.monero.hodl.util.VarInt;
|
||||
import org.nem.core.crypto.ed25519.arithmetic.*;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static how.monero.hodl.crypto.CryptoUtil.*;
|
||||
import static how.monero.hodl.crypto.HashToPoint.hashToPoint;
|
||||
import static how.monero.hodl.crypto.Scalar.bigIntegerArrayToScalarArray;
|
||||
import static how.monero.hodl.crypto.Scalar.randomScalar;
|
||||
import static how.monero.hodl.util.ByteUtil.*;
|
||||
|
||||
|
||||
public class BootleRuffing {
|
||||
|
||||
public static class SK {
|
||||
public Scalar r;
|
||||
public Scalar r1;
|
||||
public SK(Scalar r, Scalar r1) {
|
||||
this.r = r; this.r1 = r1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "(r: " + bytesToHex(r) + ", r1: " + bytesToHex(r1) + ")";
|
||||
}
|
||||
}
|
||||
|
||||
public static KeyGenResult KEYGEN() {
|
||||
SK sk = new SK(randomScalar(), randomScalar());
|
||||
Ed25519GroupElement ki = G.scalarMultiply(sk.r1);
|
||||
PointPair pk = ENCeg(ki, sk.r);
|
||||
return new KeyGenResult(sk, ki, pk);
|
||||
}
|
||||
public static class KeyGenResult {
|
||||
public SK sk;
|
||||
public Ed25519GroupElement ki;
|
||||
public PointPair pk = null;
|
||||
public KeyGenResult(SK sk, Ed25519GroupElement ki, PointPair pk) {
|
||||
this.sk = sk; this.ki = ki; this.pk = pk;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "sk: " + sk.toString() + ", ki: " + bytesToHex(ki.encode().getRaw()) + ", pk: " + (pk==null ? "(no pk)" : "pk: " + pk);
|
||||
}
|
||||
}
|
||||
|
||||
public static class F {
|
||||
public Ed25519GroupElement[] ki;
|
||||
public PointPair[][] pk;
|
||||
public Ed25519GroupElement[] co;
|
||||
public Ed25519GroupElement co1;
|
||||
byte[] M;
|
||||
public F(Ed25519GroupElement[] ki, PointPair[][] pk, Ed25519GroupElement[] co, Ed25519GroupElement co1, byte[] M) {
|
||||
this.ki = ki; this.pk = pk; this.co = co; this.co1 = co1; this.M = M;
|
||||
}
|
||||
byte[] toBytes() {
|
||||
byte[] r = new byte[0];
|
||||
for(int i=0; i<ki.length; i++) r = concat(r, ki[i].encode().getRaw());
|
||||
for(int i=0; i<pk.length; i++) for(int j=0; j<pk[i].length; j++) r = concat(r, pk[i][j].toBytes());
|
||||
for(int i=0; i<co.length; i++) r = concat(r, co[i].encode().getRaw());
|
||||
r = concat(r, co1.encode().getRaw());
|
||||
r = concat(r, M);
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
public static SpendSignature SPEND(SpendParams sp) {
|
||||
|
||||
int iAsterisk = sp.iAsterisk;
|
||||
PointPair[][] pk = sp.pk;
|
||||
SK[] sk = sp.sk;
|
||||
Ed25519GroupElement[] ki = sp.ki;
|
||||
Ed25519GroupElement[] co = sp.co;
|
||||
byte[] M = sp.M;
|
||||
Scalar s = sp.s;
|
||||
int decompositionBase = sp.decompositionBase;
|
||||
int decompositionExponent = sp.decompositionExponent;
|
||||
|
||||
Ed25519GroupElement co1 = G.scalarMultiply(s);
|
||||
F f = new F(ki, pk, co, co1, M);
|
||||
SubResult cf1 = SUB(f);
|
||||
Scalar s1 = s;
|
||||
for(int i=0; i<sk.length; i++) s1 = s1.add(sk[i].r.mul(cf1.f[i]));
|
||||
Proof2 sigma1 = PROVE2(cf1.c, iAsterisk, s1, pk.length, decompositionBase, decompositionExponent);
|
||||
|
||||
Scalar[] r1 = new Scalar[sk.length];
|
||||
for(int i=0; i<r1.length; i++) r1[i] = sk[i].r1;
|
||||
|
||||
Multisignature.Signature sigma2 = Multisignature.sign(concat(sigma1.toBytes(decompositionBase, decompositionExponent), f.toBytes()), r1, null);
|
||||
return new SpendSignature(decompositionBase, decompositionExponent, co1, sigma1, sigma2);
|
||||
}
|
||||
|
||||
public static class SpendSignature {
|
||||
public int decompositionBase;
|
||||
public int decompositionExponent;
|
||||
public Ed25519GroupElement co1;
|
||||
public Proof2 sigma1;
|
||||
Multisignature.Signature sigma2;
|
||||
public SpendSignature(int decompositionBase, int decompositionExponent, Ed25519GroupElement co1, Proof2 sigma1, Multisignature.Signature sigma2) {
|
||||
this.decompositionBase = decompositionBase; this.decompositionExponent = decompositionExponent; this.co1 = co1; this.sigma1 = sigma1; this.sigma2 = sigma2;
|
||||
}
|
||||
public byte[] toBytes() {
|
||||
byte[] result;
|
||||
result = concat(VarInt.writeVarInt(decompositionBase), VarInt.writeVarInt(decompositionExponent));
|
||||
result = concat(result, co1.encode().getRaw(), sigma1.toBytes(decompositionBase, decompositionExponent), sigma2.toBytes());
|
||||
return result;
|
||||
}
|
||||
public static SpendSignature fromBytes(byte[] a) {
|
||||
BootleRuffingCursor cursor = new BootleRuffingCursor(a);
|
||||
int decompositionBase = (int) cursor.readVarInt();
|
||||
int decompositionExponent = (int) cursor.readVarInt();
|
||||
return new SpendSignature(decompositionBase, decompositionExponent,
|
||||
cursor.readGroupElement(),
|
||||
new Proof2(
|
||||
new Proof1(cursor.readGroupElement(), cursor.readGroupElement(), cursor.readGroupElement(),
|
||||
cursor.readScalar2DArray(decompositionExponent, decompositionBase-1), cursor.readScalar(), cursor.readScalar(), null),
|
||||
cursor.readGroupElement(),
|
||||
cursor.readPointPairArray(decompositionExponent), cursor.readScalar()),
|
||||
new Multisignature.Signature(cursor.readGroupElement(), cursor.readScalar()));
|
||||
}
|
||||
}
|
||||
|
||||
public static SubResult SUB(F fin) {
|
||||
int L = fin.pk.length; // inputs
|
||||
int N = fin.pk[0].length; // ring size
|
||||
PointPair[] pkz = new PointPair[L];
|
||||
Scalar[] f = new Scalar[L];
|
||||
for(int j=0; j<L; j++) {
|
||||
pkz[j] = new PointPair(fin.ki[j], Ed25519Group.ZERO_P3);
|
||||
f[j] = hashToScalar(concat(fin.ki[j].encode().getRaw(), fin.toBytes(), longToLittleEndianUint32ByteArray(j)));
|
||||
}
|
||||
PointPair[] c = new PointPair[N];
|
||||
for(int i=0; i<N; i++) {
|
||||
c[i] = new PointPair(fin.co[i], fin.co1);
|
||||
for(int j=0; j<L; j++) {
|
||||
c[i] = c[i].add( (fin.pk[j][i].subtract(pkz[j])).multiply(f[j]) );
|
||||
}
|
||||
}
|
||||
return new SubResult(c, f);
|
||||
}
|
||||
public static class SubResult {
|
||||
public PointPair[] c;
|
||||
public Scalar[] f;
|
||||
public SubResult(PointPair[] c, Scalar[] f) {
|
||||
this.c = c; this.f = f;
|
||||
}
|
||||
}
|
||||
|
||||
public static Proof1 PROVE1(Scalar[][] b, Scalar r) {
|
||||
|
||||
int m = b.length; // decompositionExponent
|
||||
int n = b[0].length; // decompositionBase
|
||||
|
||||
Scalar rA = randomScalar();
|
||||
Scalar rC = randomScalar();
|
||||
Scalar rD = randomScalar();
|
||||
|
||||
Scalar[][] a = new Scalar[m][n];
|
||||
for(int j=0; j<m; j++) {
|
||||
for(int i=1; i<n; i++) {
|
||||
a[j][i] = randomScalar();
|
||||
}
|
||||
}
|
||||
|
||||
for(int j=0; j<m; j++) {
|
||||
a[j][0] = Scalar.ZERO;
|
||||
for(int i=1; i<n; i++) {
|
||||
a[j][0] = a[j][0].sub(a[j][i]);
|
||||
}
|
||||
}
|
||||
|
||||
Ed25519GroupElement A = COMb(a, rA);
|
||||
|
||||
Scalar[][] c = new Scalar[m][n];
|
||||
Scalar[][] d = new Scalar[m][n];
|
||||
for(int j=0; j<m; j++) {
|
||||
for(int i=0; i<n; i++) {
|
||||
c[j][i] = a[j][i].mul(Scalar.ONE.sub(b[j][i].mul(Scalar.TWO)));
|
||||
d[j][i] = a[j][i].sq().mul(Scalar.MINUS_ONE);
|
||||
}
|
||||
}
|
||||
|
||||
Ed25519GroupElement C = COMb(c, rC);
|
||||
Ed25519GroupElement D = COMb(d, rD);
|
||||
|
||||
Scalar x = hashToScalar(concat(A.encode().getRaw(), C.encode().getRaw(), D.encode().getRaw()));
|
||||
|
||||
Scalar[][] f = new Scalar[m][n];
|
||||
for(int j=0; j<m; j++) {
|
||||
for(int i=0; i<n; i++) {
|
||||
f[j][i] = b[j][i].mul(x).add(a[j][i]);
|
||||
}
|
||||
}
|
||||
|
||||
Scalar[][] fTrimmed = new Scalar[m][n-1];
|
||||
for(int j=0; j<m; j++) {
|
||||
for(int i=1; i<n; i++) {
|
||||
fTrimmed[j][i-1] = f[j][i];
|
||||
}
|
||||
}
|
||||
|
||||
Scalar zA = r.mul(x).add(rA);
|
||||
Scalar zC = rC.mul(x).add(rD);
|
||||
|
||||
return new Proof1(A, C, D, fTrimmed, zA, zC, a);
|
||||
}
|
||||
|
||||
public static class Proof1 {
|
||||
public Ed25519GroupElement A;
|
||||
public Ed25519GroupElement C;
|
||||
public Ed25519GroupElement D;
|
||||
private Scalar[][] fTrimmed;
|
||||
private Scalar zA;
|
||||
private Scalar zC;
|
||||
public transient Scalar[][] a;
|
||||
|
||||
private Proof1(Ed25519GroupElement A, Ed25519GroupElement C, Ed25519GroupElement D, Scalar[][] fTrimmed,
|
||||
Scalar zA, Scalar zC, Scalar[][] a) {
|
||||
this.A = A; this.C = C; this.D = D; this.fTrimmed = fTrimmed; this.zA = zA; this.zC = zC; this.a = a;
|
||||
}
|
||||
private byte[] toBytes(int decompositionBase, int decompositionExponent) {
|
||||
byte[] result = concat(A.encode().getRaw(), C.encode().getRaw(), D.encode().getRaw());
|
||||
for(int j=0; j<decompositionExponent; j++) {
|
||||
for(int i=0; i<decompositionBase-1; i++) {
|
||||
result = concat(result, fTrimmed[j][i].bytes);
|
||||
}
|
||||
}
|
||||
result = concat(result, zA.bytes, zC.bytes);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public static Proof2 PROVE2(PointPair[] co, int iAsterisk, Scalar r, int inputs, int decompositionBase, int decompositionExponent) {
|
||||
|
||||
int ringSize = (int) Math.pow(decompositionBase, decompositionExponent);
|
||||
|
||||
Scalar[] u = new Scalar[decompositionExponent];
|
||||
for(int k=0; k<decompositionExponent; k++) u[k] = randomScalar();
|
||||
|
||||
Scalar rB = randomScalar();
|
||||
|
||||
int[] iAsteriskSequence = nAryDecompose(decompositionBase, iAsterisk, decompositionExponent);
|
||||
|
||||
Scalar[][] d = new Scalar[decompositionExponent][decompositionBase];
|
||||
for(int j=0; j<decompositionExponent; j++) {
|
||||
for(int i=0; i<decompositionBase; i++) {
|
||||
d[j][i] = Scalar.intToScalar(delta(iAsteriskSequence[j], i));
|
||||
}
|
||||
}
|
||||
|
||||
Ed25519GroupElement B = COMb(d, rB);
|
||||
|
||||
Proof1 P = PROVE1(d, rB);
|
||||
|
||||
Scalar[][] coefs = COEFS(P.a, iAsterisk);
|
||||
|
||||
PointPair[] G = new PointPair[decompositionExponent];
|
||||
|
||||
for(int k=0; k<decompositionExponent; k++) {
|
||||
G[k] = ENCeg(Ed25519Group.ZERO_P3, u[k]);
|
||||
for (int i = 0; i < ringSize; i++) {
|
||||
G[k] = G[k].add(co[i].multiply(coefs[i][k]));
|
||||
}
|
||||
}
|
||||
|
||||
byte[] bytes = concat(P.A.encode().getRaw(), P.C.encode().getRaw(), P.D.encode().getRaw());
|
||||
Scalar x1 = hashToScalar(bytes);
|
||||
|
||||
Scalar z = r.mul(x1.pow(decompositionExponent));
|
||||
for(int i=decompositionExponent-1; i>=0; i--) {
|
||||
z = z.sub(u[i].mul(x1.pow(i)));
|
||||
}
|
||||
return new Proof2(P, B, G, z);
|
||||
}
|
||||
|
||||
|
||||
public static class Proof2 {
|
||||
Proof1 P;
|
||||
public Ed25519GroupElement B;
|
||||
public PointPair[] G;
|
||||
public Scalar z;
|
||||
|
||||
private Proof2(Proof1 P, Ed25519GroupElement B, PointPair[] G, Scalar z) {
|
||||
this.P = P;
|
||||
this.B = B;
|
||||
this.G = G;
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
private byte[] toBytes(int decompositionBase, int decompositionExponent) {
|
||||
byte[] bytes;
|
||||
bytes = concat(P.toBytes(decompositionBase, decompositionExponent), B.encode().getRaw());
|
||||
for(PointPair g : G) bytes = concat(bytes, g.toBytes());
|
||||
bytes = concat(bytes, z.bytes);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static int delta(int j, int i) {
|
||||
return j==i ? 1 : 0;
|
||||
}
|
||||
private static int intPow(int a, int b) {
|
||||
return (int) Math.round(Math.pow(a, b));
|
||||
}
|
||||
public static int[] nAryDecompose(int base, int n, int decompositionExponent) {
|
||||
int[] r = new int[decompositionExponent];
|
||||
for(int i=decompositionExponent-1; i>=0; i--) {
|
||||
int basePow = intPow(base, i);
|
||||
r[i] = n / basePow;
|
||||
n-=basePow*r[i];
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
public static boolean VALID1(Ed25519GroupElement B, Proof1 P) {
|
||||
boolean abcdOnCurve =
|
||||
P.A.satisfiesCurveEquation()
|
||||
&& B.satisfiesCurveEquation()
|
||||
&& P.C.satisfiesCurveEquation()
|
||||
&& P.D.satisfiesCurveEquation();
|
||||
if(!abcdOnCurve) {
|
||||
System.out.println("VALID1: ABCD not on curve");
|
||||
return false;
|
||||
}
|
||||
|
||||
int m = P.fTrimmed.length;
|
||||
int n = P.fTrimmed[0].length + 1;
|
||||
|
||||
Scalar[][] f = new Scalar[m][n];
|
||||
for(int j=0; j<m; j++) {
|
||||
for(int i=1; i<n; i++) {
|
||||
f[j][i] = P.fTrimmed[j][i-1];
|
||||
}
|
||||
}
|
||||
|
||||
Scalar x = hashToScalar(concat(P.A.encode().getRaw(), P.C.encode().getRaw(), P.D.encode().getRaw()));
|
||||
|
||||
for(int j=0; j<m; j++) {
|
||||
f[j][0] = x;
|
||||
for(int i=1; i<n; i++) {
|
||||
f[j][0] = f[j][0].sub(f[j][i]);
|
||||
}
|
||||
}
|
||||
|
||||
Scalar[][] f1 = new Scalar[m][n];
|
||||
for(int j=0; j<m; j++) {
|
||||
for(int i=0; i<n; i++) {
|
||||
f1[j][i] = f[j][i].mul(x.sub(f[j][i]));
|
||||
}
|
||||
}
|
||||
|
||||
for(int j=0; j<m; j++) {
|
||||
Scalar colSum = x;
|
||||
for(int i=1; i<n; i++) {
|
||||
colSum = colSum.sub(f[j][i]);
|
||||
}
|
||||
if(!f[j][0].equals(colSum)) {
|
||||
System.out.println("VALID1: FAILED For each j=0, ..., m-1, f[j][0] == x-f[j][1]-f[j][2]- ... -f[j][n-1]");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if(!B.toP3().scalarMultiply(x).toP3().add(P.A.toP3().toCached()).equals(COMb(f, P.zA))) {
|
||||
System.out.println("VALID1: FAILED xB + A == COMp(f[0][0], ..., f[m-1][n-1]; z[A])");
|
||||
return false;
|
||||
}
|
||||
if(!P.C.toP3().scalarMultiply(x).toP3().add(P.D.toP3().toCached()).equals(COMb(f1, P.zC))) {
|
||||
System.out.println("VALID1: FAILED xC + D == COMp(f'[0][0], ..., f'[m-1][n-1]; z[C])");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
public static boolean VALID2(int decompositionBase, Proof2 P1, PointPair[] co) {
|
||||
|
||||
boolean abcdOnCurve =
|
||||
P1.P.A.satisfiesCurveEquation()
|
||||
&& P1.B.satisfiesCurveEquation()
|
||||
&& P1.P.C.satisfiesCurveEquation()
|
||||
&& P1.P.D.satisfiesCurveEquation();
|
||||
if(!abcdOnCurve) {
|
||||
System.out.println("VALID2: FAILED: ABCD not on curve");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!VALID1(P1.B, P1.P)) {
|
||||
System.out.println("VALID2: FAILED: VALID1 failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
Scalar x1 = hashToScalar(concat(P1.P.A.encode().getRaw(), P1.P.C.encode().getRaw(), P1.P.D.encode().getRaw()));
|
||||
|
||||
int decompositionExponent = P1.P.fTrimmed.length;
|
||||
Scalar[][] f = new Scalar[decompositionExponent][decompositionBase];
|
||||
for(int j=0; j<decompositionExponent; j++) {
|
||||
for(int i=1; i<decompositionBase; i++) {
|
||||
f[j][i] = P1.P.fTrimmed[j][i-1];
|
||||
}
|
||||
}
|
||||
|
||||
int ringSize = (int) Math.pow(decompositionBase, decompositionExponent);
|
||||
|
||||
PointPair c = ENCeg(Ed25519Group.ZERO_P3, P1.z);
|
||||
|
||||
Scalar x = hashToScalar(concat(P1.P.A.encode().getRaw(), P1.P.C.encode().getRaw(), P1.P.D.encode().getRaw()));
|
||||
for(int j=0; j<decompositionExponent; j++) {
|
||||
f[j][0] = x;
|
||||
for(int i=1; i<decompositionBase; i++) {
|
||||
f[j][0] = f[j][0].sub(f[j][i]);
|
||||
}
|
||||
}
|
||||
|
||||
Scalar[] g = new Scalar[ringSize];
|
||||
g[0] = f[0][0];
|
||||
for(int j=1; j<decompositionExponent; j++) {
|
||||
g[0] = g[0].mul(f[j][0]);
|
||||
}
|
||||
|
||||
PointPair c1 = co[0].multiply(g[0]);
|
||||
for(int i=1; i<ringSize; i++) {
|
||||
int[] iSequence = nAryDecompose(decompositionBase, i, decompositionExponent);
|
||||
g[i] = f[0][iSequence[0]];
|
||||
for(int j=1; j<decompositionExponent; j++) {
|
||||
g[i] = g[i].mul(f[j][iSequence[j]]);
|
||||
}
|
||||
c1 = c1.add(co[i].multiply(g[i]));
|
||||
}
|
||||
|
||||
for(int k=0; k<decompositionExponent; k++) {
|
||||
c1 = c1.subtract(P1.G[k].multiply(x1.pow(k)));
|
||||
}
|
||||
|
||||
|
||||
boolean result = c1.equals(c);
|
||||
if(!result) {
|
||||
System.out.println("VALID2: FAILED: c' != c");
|
||||
System.out.println("c: (" + bytesToHex(c.P1.encode().getRaw()) + ", " + bytesToHex(c.P2.encode().getRaw()));
|
||||
System.out.println("c': (" + bytesToHex(c1.P1.encode().getRaw()) + ", " + bytesToHex(c1.P2.encode().getRaw()));
|
||||
}
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
public static boolean VER(Ed25519GroupElement[] ki, PointPair[][] pk, Ed25519GroupElement[] co, Ed25519GroupElement co1, byte[] M, SpendSignature spendSignature) {
|
||||
|
||||
F f = new F(ki, pk, co, co1, M);
|
||||
|
||||
SubResult cf1 = SUB(f);
|
||||
|
||||
if(!Multisignature.verify(concat(spendSignature.sigma1.toBytes(spendSignature.decompositionBase, spendSignature.decompositionExponent), f.toBytes()), ki, spendSignature.sigma2)) {
|
||||
System.out.println("Multisignature.verify failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!VALID2(spendSignature.decompositionBase, spendSignature.sigma1, cf1.c)) {
|
||||
System.out.println("VALID2 failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static class Output {
|
||||
public SK sk;
|
||||
public Ed25519GroupElement ki;
|
||||
public PointPair pk;
|
||||
|
||||
public Scalar mask;
|
||||
public Ed25519GroupElement co;
|
||||
public BigInteger amount;
|
||||
public static Output genRandomOutput(BigInteger amount) {
|
||||
Output o = new Output();
|
||||
KeyGenResult keyGenResult = KEYGEN();
|
||||
o.sk = keyGenResult.sk;
|
||||
o.pk = keyGenResult.pk;
|
||||
o.ki = keyGenResult.ki;
|
||||
o.amount = amount;
|
||||
o.mask = randomScalar();
|
||||
o.co = COMp(new Scalar(o.amount), o.mask);
|
||||
return o;
|
||||
}
|
||||
}
|
||||
|
||||
public static Scalar[][] COEFS(Scalar[][] a, int iAsterisk) {
|
||||
int decompositionBase = a[0].length; // n
|
||||
int decompositionExponent = a.length; // m
|
||||
int ringSize = (int) Math.pow(decompositionBase, decompositionExponent); // N = n^m
|
||||
|
||||
int[] iAsteriskSequence = nAryDecompose(decompositionBase, iAsterisk, decompositionExponent);
|
||||
|
||||
Scalar[][] coefList = new Scalar[ringSize][decompositionExponent];
|
||||
|
||||
for(int k=0; k<ringSize; k++) {
|
||||
int[] kSequence = nAryDecompose(decompositionBase, k, decompositionExponent);
|
||||
coefList[k] = new Scalar[]{
|
||||
a[0][kSequence[0]],
|
||||
Scalar.intToScalar(delta(iAsteriskSequence[0], kSequence[0]))
|
||||
};
|
||||
|
||||
for(int j=1; j<decompositionExponent; j++) {
|
||||
coefList[k] = COEFPROD(coefList[k], new Scalar[]{
|
||||
a[j][kSequence[j]],
|
||||
Scalar.intToScalar(delta(iAsteriskSequence[j], kSequence[j]))
|
||||
});
|
||||
}
|
||||
}
|
||||
for(int k=0; k<ringSize; k++) {
|
||||
coefList[k] = trimScalarArray(coefList[k], decompositionExponent, decompositionExponent);
|
||||
}
|
||||
return coefList;
|
||||
}
|
||||
|
||||
private static Scalar[] trimScalarArray(Scalar[] a, int len, int indexWhere1ValueCanBeTrimmed) {
|
||||
Scalar[] r = new Scalar[len];
|
||||
for(int i=0; i<a.length; i++) {
|
||||
if(i<len) r[i] = a[i];
|
||||
else {
|
||||
if(i==indexWhere1ValueCanBeTrimmed) {
|
||||
if(!(a[i].equals(Scalar.ZERO) || a[i].equals(Scalar.ONE))) throw new RuntimeException("Attempt to trim non-zero or non-one in column " + i + ": value: " + a[i].toBigInteger());
|
||||
}
|
||||
else {
|
||||
if(!(a[i].equals(Scalar.ZERO))) throw new RuntimeException("Attempt to trim non-zero in column " + i + ": value: " + a[i].toBigInteger());
|
||||
}
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
public static Scalar[] COEFPROD(Scalar[] c, Scalar[] d) {
|
||||
int maxLen = Math.max(c.length, d.length);
|
||||
int resultLen = 2*maxLen-1;
|
||||
BigInteger[] result = new BigInteger[resultLen];
|
||||
|
||||
for (int i = 0; i<resultLen; i++) result[i] = BigInteger.ZERO;
|
||||
for (int i=0; i<maxLen; i++)
|
||||
{
|
||||
for (int j=0; j<maxLen; j++) {
|
||||
result[i+j] = result[i+j].add(getBigIntegerAtArrayIndex(c, i).multiply(getBigIntegerAtArrayIndex(d, j)));
|
||||
}
|
||||
}
|
||||
return bigIntegerArrayToScalarArray(result);
|
||||
}
|
||||
|
||||
private static BigInteger getBigIntegerAtArrayIndex(Scalar[] a, int index) {
|
||||
if(index>=a.length) return BigInteger.ZERO;
|
||||
else return a[index].toBigInteger();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,114 @@
|
|||
package how.monero.hodl.ringSignature;
|
||||
|
||||
import how.monero.hodl.crypto.Scalar;
|
||||
import org.nem.core.crypto.ed25519.arithmetic.Ed25519GroupElement;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static how.monero.hodl.crypto.Scalar.randomScalar;
|
||||
import static how.monero.hodl.util.ByteUtil.bytesToHex;
|
||||
import static how.monero.hodl.util.ByteUtil.concat;
|
||||
import static how.monero.hodl.crypto.CryptoUtil.*;
|
||||
|
||||
public class Multisignature {
|
||||
|
||||
public static Ed25519GroupElement[] lexicographicalSort(Ed25519GroupElement[] X) {
|
||||
SortedMap<String, Ed25519GroupElement> hexToPoint = new TreeMap<>();
|
||||
for(Ed25519GroupElement Xi : X) hexToPoint.put(bytesToHex(Xi.encode().getRaw()), Xi);
|
||||
return hexToPoint.values().stream().toArray(Ed25519GroupElement[]::new);
|
||||
}
|
||||
|
||||
/*
|
||||
VER*: Take as input a message M, public keys L' = X[1], ..., X[n], and a
|
||||
signature sigma = (R,s).
|
||||
1) Compute L* = H(L')
|
||||
2) For each i=1,2,...,n, compute c[i] = Hs(X[i], R, L*, M)
|
||||
3) Accept if and only if sG = R + c[1]*X[1] + ... + c[n]*X[n]
|
||||
*/
|
||||
public static boolean verify(byte[] M, Ed25519GroupElement[] X, Signature signature) {
|
||||
int n = X.length;
|
||||
|
||||
Scalar XAsterisk = hashToScalar(toBytes(lexicographicalSort(X)));
|
||||
|
||||
Scalar[] c = new Scalar[n];
|
||||
for(int i=0; i<n; i++) {
|
||||
c[i] = hashToScalar(concat(X[i].encode().getRaw(), signature.R.encode().getRaw(), XAsterisk.bytes, M));
|
||||
}
|
||||
Ed25519GroupElement sG = G.scalarMultiply(signature.s);
|
||||
Ed25519GroupElement sG1 = signature.R;
|
||||
for(int i=0; i<n; i++) sG1 = sG1.toP3().add(X[i].scalarMultiply(c[i]).toCached());
|
||||
return sG.equals(sG1);
|
||||
}
|
||||
|
||||
/*
|
||||
SIG*: Take as input a message M and a list of private keys L = x[0], x[1],
|
||||
..., x[n-1]. Let L' be the associated list of public keys X[0], ..., X[n-1], and
|
||||
assume L' is lexicographically ordered.
|
||||
1) Compute L* = H(L').
|
||||
2) For each i=0,1,...,n-1, select r[i] at random from Zq.
|
||||
3) Compute r=r[0]+r[1]+...+r[n-1] and R=rG.
|
||||
4) For each i=0,1,...,n-1:
|
||||
i) Compute c[i] := Hs(X[i], R, L*, M)
|
||||
ii) Compute s[i] := r[i] + x[i]*c[i]
|
||||
5) Compute s = s[1] + ... + s[n].
|
||||
6) Output the signature sigma = (R, s)
|
||||
*/
|
||||
public static Signature sign(byte[] M, Scalar[] x, Ed25519GroupElement[] X) {
|
||||
int n = x.length;
|
||||
if(X==null) {
|
||||
X = new Ed25519GroupElement[n];
|
||||
for(int i=0; i<n; i++) {
|
||||
X[i] = G.scalarMultiply(x[i]);
|
||||
}
|
||||
|
||||
}
|
||||
Scalar XAsterisk = hashToScalar(toBytes(lexicographicalSort(X)));
|
||||
|
||||
Scalar[] rArray = new Scalar[n];
|
||||
for(int i=0; i<n; i++) rArray[i] = randomScalar();
|
||||
Scalar r = sumArray(rArray);
|
||||
|
||||
Ed25519GroupElement R = G.scalarMultiply(r);
|
||||
Scalar[] c = new Scalar[n];
|
||||
Scalar[] sArray = new Scalar[n];
|
||||
for(int i=0; i<n; i++) {
|
||||
c[i] = hashToScalar(concat(X[i].encode().getRaw(), R.encode().getRaw(), XAsterisk.bytes, M));
|
||||
sArray[i] = rArray[i].add(x[i].mul(c[i]));
|
||||
}
|
||||
Scalar s = sumArray(sArray);
|
||||
return new Signature(R, s);
|
||||
}
|
||||
|
||||
public static class Signature {
|
||||
Ed25519GroupElement R;
|
||||
Scalar s;
|
||||
public Signature(Ed25519GroupElement R, Scalar s) {
|
||||
this.R = R; this.s = s;
|
||||
}
|
||||
public byte[] toBytes() {
|
||||
return concat(R.encode().getRaw(), s.bytes);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
KEYGEN*: Each user selects x at random from Zq. The secret key is x. The
|
||||
public key is X=xG. Output (sk,pk) = (x,X).
|
||||
*/
|
||||
public static KeyPair keygen() {
|
||||
Scalar x = randomScalar();
|
||||
Ed25519GroupElement X = G.scalarMultiply(x);
|
||||
return new KeyPair(x, X);
|
||||
}
|
||||
public static class KeyPair {
|
||||
public Scalar x;
|
||||
public Ed25519GroupElement X;
|
||||
public KeyPair(Scalar x, Ed25519GroupElement X) {
|
||||
this.x = x; this.X = X;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package how.monero.hodl.ringSignature;
|
||||
|
||||
import how.monero.hodl.crypto.PointPair;
|
||||
import how.monero.hodl.crypto.Scalar;
|
||||
import org.nem.core.crypto.ed25519.arithmetic.Ed25519GroupElement;
|
||||
|
||||
public class SpendParams {
|
||||
|
||||
public int iAsterisk;
|
||||
public PointPair[][] pk;
|
||||
public BootleRuffing.SK[] sk;
|
||||
public Ed25519GroupElement[] ki;
|
||||
public Ed25519GroupElement[] co;
|
||||
public byte[] M;
|
||||
public Scalar s;
|
||||
public int decompositionBase;
|
||||
public int decompositionExponent;
|
||||
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
package how.monero.hodl.util;
|
||||
|
||||
import how.monero.hodl.crypto.Scalar;
|
||||
|
||||
public class ByteUtil {
|
||||
|
||||
public static String byteToHex(int v) {
|
||||
byte[] array = new byte[1];
|
||||
array[0] = (byte) v;
|
||||
return bytesToHex(array);
|
||||
}
|
||||
|
||||
final protected static char[] hexArray = "0123456789abcdef".toCharArray();
|
||||
public static String bytesToHex(Scalar a) {
|
||||
return bytesToHex(a.bytes);
|
||||
}
|
||||
public static String bytesToHex(byte[] bytes) {
|
||||
char[] hexChars = new char[bytes.length * 2];
|
||||
for ( int j = 0; j < bytes.length; j++ ) {
|
||||
int v = bytes[j] & 0xFF;
|
||||
hexChars[j * 2] = hexArray[v >>> 4];
|
||||
hexChars[j * 2 + 1] = hexArray[v & 0x0F];
|
||||
}
|
||||
return new String(hexChars);
|
||||
}
|
||||
|
||||
public static byte[] hexToBytes(String s) {
|
||||
int len = s.length();
|
||||
byte[] data = new byte[len / 2];
|
||||
for (int i = 0; i < len; i += 2) {
|
||||
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
|
||||
+ Character.digit(s.charAt(i+1), 16));
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
public static void reverseByteArrayInPlace(byte[] array) {
|
||||
for(int i = 0; i < array.length / 2; i++)
|
||||
{
|
||||
byte temp = array[i];
|
||||
array[i] = array[array.length - i - 1];
|
||||
array[array.length - i - 1] = temp;
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] concat(byte a, byte[] b) {
|
||||
byte[] r = new byte[1+b.length];
|
||||
r[0] = a;
|
||||
System.arraycopy(b, 0, r, 1, b.length);
|
||||
return r;
|
||||
}
|
||||
public static byte[] concat(byte[] a, byte[] b) {
|
||||
byte[] r = new byte[a.length+b.length];
|
||||
System.arraycopy(a, 0, r, 0, a.length);
|
||||
System.arraycopy(b, 0, r, a.length, b.length);
|
||||
return r;
|
||||
}
|
||||
public static byte[] concat(byte[] a, byte[] b, byte[] c) {
|
||||
return concat(concat(a, b), c);
|
||||
}
|
||||
public static byte[] concat(byte[] a, byte[] b, byte[] c, byte[] d) {
|
||||
return concat(concat(a, b), concat(c, d));
|
||||
}
|
||||
|
||||
public static byte[] subarray(byte[] a, int start, int end) {
|
||||
byte[] r = new byte[end-start];
|
||||
System.arraycopy(a, start, r, 0, r.length);
|
||||
return r;
|
||||
}
|
||||
|
||||
public static byte[] xor(byte[] a, byte[] b) {
|
||||
byte[] r = new byte[a.length];
|
||||
for(int i=0; i<r.length; i++) r[i] = (byte) (a[i] ^ b[i]);
|
||||
return r;
|
||||
}
|
||||
|
||||
public static byte[] longToLittleEndianUint32ByteArray(long value) {
|
||||
byte[] array = new byte[4];
|
||||
for (int i=3; i>=0; i--) array[i] = (byte) (value >> i*8);
|
||||
return array;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
package how.monero.hodl.util;
|
||||
|
||||
public class ExceptionAdapter {
|
||||
public static RuntimeException toRuntimeException(Exception e) {
|
||||
if(RuntimeException.class.isInstance(e)) return (RuntimeException) e;
|
||||
else return new RuntimeException(e);
|
||||
}
|
||||
}
|
45
source-code/RuffCT-java/src/how/monero/hodl/util/VarInt.java
Normal file
45
source-code/RuffCT-java/src/how/monero/hodl/util/VarInt.java
Normal file
|
@ -0,0 +1,45 @@
|
|||
package how.monero.hodl.util;
|
||||
|
||||
public class VarInt {
|
||||
|
||||
public static byte[] writeVarInt(long value) {
|
||||
byte[] data = new byte[8];
|
||||
int pos = 0;
|
||||
|
||||
while ((value & 0xFFFFFF80) != 0L) {
|
||||
data[pos] = (byte) ((value & 0x7F) | 0x80);
|
||||
pos++;
|
||||
value >>>= 7;
|
||||
}
|
||||
data[pos] = (byte) (value & 0x7F);
|
||||
pos++;
|
||||
|
||||
byte[] result = new byte[pos];
|
||||
System.arraycopy(data, 0, result, 0, pos);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static long readVarInt(byte[] data) {
|
||||
|
||||
long result = 0;
|
||||
int c = 0;
|
||||
int pos = 0;
|
||||
|
||||
while(true) {
|
||||
boolean isLastByteInVarInt = true;
|
||||
int i = Byte.toUnsignedInt(data[pos]);
|
||||
if(i>=128) {
|
||||
isLastByteInVarInt = false;
|
||||
i-=128;
|
||||
}
|
||||
result += Math.round(i * Math.pow(128, c));
|
||||
c++;
|
||||
pos++;
|
||||
if(isLastByteInVarInt) break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
}
|
30
source-code/RuffCT-java/src/org/nem/core/crypto/Curve.java
Executable file
30
source-code/RuffCT-java/src/org/nem/core/crypto/Curve.java
Executable file
|
@ -0,0 +1,30 @@
|
|||
package org.nem.core.crypto;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
/**
|
||||
* Interface for getting information for a curve.
|
||||
*/
|
||||
public interface Curve {
|
||||
|
||||
/**
|
||||
* Gets the name of the curve.
|
||||
*
|
||||
* @return The name of the curve.
|
||||
*/
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* Gets the group order.
|
||||
*
|
||||
* @return The group order.
|
||||
*/
|
||||
BigInteger getGroupOrder();
|
||||
|
||||
/**
|
||||
* Gets the group order / 2.
|
||||
*
|
||||
* @return The group order / 2.
|
||||
*/
|
||||
BigInteger getHalfGroupOrder();
|
||||
}
|
41
source-code/RuffCT-java/src/org/nem/core/crypto/ed25519/Ed25519Curve.java
Executable file
41
source-code/RuffCT-java/src/org/nem/core/crypto/ed25519/Ed25519Curve.java
Executable file
|
@ -0,0 +1,41 @@
|
|||
package org.nem.core.crypto.ed25519;
|
||||
|
||||
import org.nem.core.crypto.ed25519.arithmetic.Ed25519Group;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
/**
|
||||
* Class that wraps the elliptic curve Ed25519.
|
||||
*/
|
||||
public class Ed25519Curve implements org.nem.core.crypto.Curve {
|
||||
|
||||
private static final Ed25519Curve ED25519;
|
||||
|
||||
static {
|
||||
ED25519 = new Ed25519Curve();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "ed25519";
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigInteger getGroupOrder() {
|
||||
return Ed25519Group.GROUP_ORDER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigInteger getHalfGroupOrder() {
|
||||
return Ed25519Group.GROUP_ORDER.shiftRight(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Ed25519 instance.
|
||||
*
|
||||
* @return The Ed25519 instance.
|
||||
*/
|
||||
public static Ed25519Curve ed25519() {
|
||||
return ED25519;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package org.nem.core.crypto.ed25519.arithmetic;
|
||||
|
||||
/**
|
||||
* Available coordinate systems for a group element.
|
||||
*/
|
||||
public enum CoordinateSystem {
|
||||
|
||||
/**
|
||||
* Affine coordinate system (x, y).
|
||||
*/
|
||||
AFFINE,
|
||||
|
||||
/**
|
||||
* Projective coordinate system (X:Y:Z) satisfying x=X/Z, y=Y/Z.
|
||||
*/
|
||||
P2,
|
||||
|
||||
/**
|
||||
* Extended projective coordinate system (X:Y:Z:T) satisfying x=X/Z, y=Y/Z, XY=ZT.
|
||||
*/
|
||||
P3,
|
||||
|
||||
/**
|
||||
* Completed coordinate system ((X:Z), (Y:T)) satisfying x=X/Z, y=Y/T.
|
||||
*/
|
||||
P1xP1,
|
||||
|
||||
/**
|
||||
* Precomputed coordinate system (y+x, y-x, 2dxy).
|
||||
*/
|
||||
PRECOMPUTED,
|
||||
|
||||
/**
|
||||
* Cached coordinate system (Y+X, Y-X, Z, 2dT).
|
||||
*/
|
||||
CACHED
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,129 @@
|
|||
package org.nem.core.crypto.ed25519.arithmetic;
|
||||
|
||||
import org.nem.core.utils.ArrayUtils;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class Ed25519EncodedGroupElement {
|
||||
|
||||
private final byte[] values;
|
||||
|
||||
/**
|
||||
* Creates a new encoded group element.
|
||||
*
|
||||
* @param values The values.
|
||||
*/
|
||||
public Ed25519EncodedGroupElement(final byte[] values) {
|
||||
if (32 != values.length) {
|
||||
throw new IllegalArgumentException("Invalid encoded group element.");
|
||||
}
|
||||
|
||||
this.values = values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the underlying byte array.
|
||||
*
|
||||
* @return The byte array.
|
||||
*/
|
||||
public byte[] getRaw() {
|
||||
return this.values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes this encoded group element and returns a new group element in P3 coordinates.
|
||||
*
|
||||
* @return The group element.
|
||||
*/
|
||||
public Ed25519GroupElement decode() {
|
||||
final Ed25519FieldElement x = this.getAffineX();
|
||||
final Ed25519FieldElement y = this.getAffineY();
|
||||
return Ed25519GroupElement.p3(x, y, Ed25519Field.ONE, x.multiply(y));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the affine x-coordinate.
|
||||
* x is recovered in the following way (p = field size):
|
||||
* <br>
|
||||
* x = sign(x) * sqrt((y^2 - 1) / (d * y^2 + 1)) = sign(x) * sqrt(u / v) with u = y^2 - 1 and v = d * y^2 + 1.
|
||||
* Setting β = (u * v^3) * (u * v^7)^((p - 5) / 8) one has β^2 = +-(u / v).
|
||||
* If v * β = -u multiply β with i=sqrt(-1).
|
||||
* Set x := β.
|
||||
* If sign(x) != bit 255 of s then negate x.
|
||||
*
|
||||
* @return the affine x-coordinate.
|
||||
*/
|
||||
public Ed25519FieldElement getAffineX() {
|
||||
Ed25519FieldElement x;
|
||||
final Ed25519FieldElement y;
|
||||
final Ed25519FieldElement ySquare;
|
||||
final Ed25519FieldElement u;
|
||||
final Ed25519FieldElement v;
|
||||
final Ed25519FieldElement vxSquare;
|
||||
Ed25519FieldElement checkForZero;
|
||||
y = this.getAffineY();
|
||||
ySquare = y.square();
|
||||
|
||||
// u = y^2 - 1
|
||||
u = ySquare.subtract(Ed25519Field.ONE);
|
||||
|
||||
// v = d * y^2 + 1
|
||||
v = ySquare.multiply(Ed25519Field.D).add(Ed25519Field.ONE);
|
||||
|
||||
// x = sqrt(u / v)
|
||||
x = Ed25519FieldElement.sqrt(u, v);
|
||||
|
||||
vxSquare = x.square().multiply(v);
|
||||
checkForZero = vxSquare.subtract(u);
|
||||
if (checkForZero.isNonZero()) {
|
||||
checkForZero = vxSquare.add(u);
|
||||
if (checkForZero.isNonZero()) {
|
||||
throw new IllegalArgumentException("not a valid Ed25519EncodedGroupElement.");
|
||||
}
|
||||
|
||||
x = x.multiply(Ed25519Field.I);
|
||||
}
|
||||
|
||||
if ((x.isNegative() ? 1 : 0) != ArrayUtils.getBit(this.values, 255)) {
|
||||
x = x.negate();
|
||||
}
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the affine y-coordinate.
|
||||
*
|
||||
* @return the affine y-coordinate.
|
||||
*/
|
||||
public Ed25519FieldElement getAffineY() {
|
||||
// The affine y-coordinate is in bits 0 to 254.
|
||||
// Since the decode() method of Ed25519EncodedFieldElement ignores bit 255,
|
||||
// we can use that method without problems.
|
||||
final Ed25519EncodedFieldElement encoded = new Ed25519EncodedFieldElement(this.values);
|
||||
return encoded.decode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Arrays.hashCode(this.values);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
if (!(obj instanceof Ed25519EncodedGroupElement)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final Ed25519EncodedGroupElement encoded = (Ed25519EncodedGroupElement)obj;
|
||||
return 1 == ArrayUtils.isEqualConstantTime(this.values, encoded.values);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format(
|
||||
"x=%s\ny=%s\n",
|
||||
this.getAffineX().toString(),
|
||||
this.getAffineY().toString());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
package org.nem.core.crypto.ed25519.arithmetic;
|
||||
|
||||
import org.nem.core.utils.*;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
/**
|
||||
* Represents the underlying finite field for Ed25519.
|
||||
* The field has p = 2^255 - 19 elements.
|
||||
*/
|
||||
public class Ed25519Field {
|
||||
|
||||
/**
|
||||
* P: 2^255 - 19
|
||||
*/
|
||||
public static final BigInteger P = new BigInteger(HexEncoder.getBytes("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed"));
|
||||
public static final Ed25519FieldElement ZERO = getFieldElement(0);
|
||||
public static final Ed25519FieldElement ONE = getFieldElement(1);
|
||||
public static final Ed25519FieldElement TWO = getFieldElement(2);
|
||||
public static final Ed25519FieldElement D = getD();
|
||||
public static final Ed25519FieldElement D_Times_TWO = D.multiply(TWO);
|
||||
public static final byte[] ZERO_SHORT = new byte[32];
|
||||
public static final byte[] ZERO_LONG = new byte[64];
|
||||
|
||||
/**
|
||||
* I ^ 2 = -1
|
||||
*/
|
||||
public static final Ed25519FieldElement I = new Ed25519EncodedFieldElement(HexEncoder.getBytes(
|
||||
"b0a00e4a271beec478e42fad0618432fa7d7fb3d99004d2b0bdfc14f8024832b")).decode();
|
||||
|
||||
private static Ed25519FieldElement getFieldElement(final int value) {
|
||||
final int[] f = new int[10];
|
||||
f[0] = value;
|
||||
return new Ed25519FieldElement(f);
|
||||
}
|
||||
|
||||
private static Ed25519FieldElement getD() {
|
||||
final BigInteger d = new BigInteger("-121665")
|
||||
.multiply(new BigInteger("121666").modInverse(Ed25519Field.P))
|
||||
.mod(Ed25519Field.P);
|
||||
return new Ed25519EncodedFieldElement(ArrayUtils.toByteArray(d, 32)).decode();
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,36 @@
|
|||
package org.nem.core.crypto.ed25519.arithmetic;
|
||||
|
||||
import org.nem.core.utils.HexEncoder;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
/**
|
||||
* Represents the underlying group for Ed25519.
|
||||
*/
|
||||
public class Ed25519Group {
|
||||
|
||||
/**
|
||||
* 2^252 - 27742317777372353535851937790883648493
|
||||
*/
|
||||
public static final BigInteger GROUP_ORDER = BigInteger.ONE.shiftLeft(252).add(new BigInteger("27742317777372353535851937790883648493"));
|
||||
|
||||
/**
|
||||
* <pre>{@code
|
||||
* (x, 4/5); x > 0
|
||||
* }</pre>
|
||||
*/
|
||||
public static final Ed25519GroupElement BASE_POINT = getBasePoint();
|
||||
|
||||
// different representations of zero
|
||||
public static final Ed25519GroupElement ZERO_P3 = Ed25519GroupElement.p3(Ed25519Field.ZERO, Ed25519Field.ONE, Ed25519Field.ONE, Ed25519Field.ZERO);
|
||||
public static final Ed25519GroupElement ZERO_P2 = Ed25519GroupElement.p2(Ed25519Field.ZERO, Ed25519Field.ONE, Ed25519Field.ONE);
|
||||
public static final Ed25519GroupElement ZERO_PRECOMPUTED = Ed25519GroupElement.precomputed(Ed25519Field.ONE, Ed25519Field.ONE, Ed25519Field.ZERO);
|
||||
|
||||
private static Ed25519GroupElement getBasePoint() {
|
||||
final byte[] rawEncodedGroupElement = HexEncoder.getBytes("5866666666666666666666666666666666666666666666666666666666666666");
|
||||
final Ed25519GroupElement basePoint = new Ed25519EncodedGroupElement(rawEncodedGroupElement).decode();
|
||||
basePoint.precomputeForScalarMultiplication();
|
||||
basePoint.precomputeForDoubleScalarMultiplication();
|
||||
return basePoint;
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load diff
74
source-code/RuffCT-java/src/org/nem/core/utils/AbstractTwoLevelMap.java
Executable file
74
source-code/RuffCT-java/src/org/nem/core/utils/AbstractTwoLevelMap.java
Executable file
|
@ -0,0 +1,74 @@
|
|||
package org.nem.core.utils;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* A two-level map of items.
|
||||
* <br>
|
||||
* Items are automatically created on access.
|
||||
* Item associations are order-dependent.
|
||||
*/
|
||||
public abstract class AbstractTwoLevelMap<TKey, TValue> {
|
||||
private final Map<TKey, Map<TKey, TValue>> impl = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* Gets the TValue associated with key1 and key2.
|
||||
*
|
||||
* @param key1 The first key.
|
||||
* @param key2 The second key.
|
||||
* @return The value associated with key and key2.
|
||||
*/
|
||||
public TValue getItem(final TKey key1, final TKey key2) {
|
||||
final Map<TKey, TValue> keyOneValues = this.getItems(key1);
|
||||
|
||||
TValue value = keyOneValues.get(key2);
|
||||
if (null == value) {
|
||||
value = this.createValue();
|
||||
keyOneValues.put(key2, value);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the (TKey, TValue) map associated with key.
|
||||
*
|
||||
* @param key The first key.
|
||||
* @return The map associated with key.
|
||||
*/
|
||||
public Map<TKey, TValue> getItems(final TKey key) {
|
||||
Map<TKey, TValue> keyValues = this.impl.get(key);
|
||||
if (null == keyValues) {
|
||||
keyValues = new ConcurrentHashMap<>();
|
||||
this.impl.put(key, keyValues);
|
||||
}
|
||||
|
||||
return keyValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a key from the map.
|
||||
*
|
||||
* @param key The key to remove.
|
||||
*/
|
||||
public void remove(final TKey key) {
|
||||
this.impl.remove(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the key set of this map.
|
||||
*
|
||||
* @return The key set.
|
||||
*/
|
||||
public Set<TKey> keySet() {
|
||||
return this.impl.keySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new blank value.
|
||||
*
|
||||
* @return A new value.
|
||||
*/
|
||||
protected abstract TValue createValue();
|
||||
}
|
156
source-code/RuffCT-java/src/org/nem/core/utils/ArrayUtils.java
Executable file
156
source-code/RuffCT-java/src/org/nem/core/utils/ArrayUtils.java
Executable file
|
@ -0,0 +1,156 @@
|
|||
package org.nem.core.utils;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
/**
|
||||
* Static class that contains a handful of array helper functions.
|
||||
*/
|
||||
public class ArrayUtils {
|
||||
|
||||
/**
|
||||
* Creates duplicate of given array
|
||||
*
|
||||
* @param src The array to duplicate.
|
||||
* @return A copy of the array.
|
||||
*/
|
||||
public static byte[] duplicate(final byte[] src) {
|
||||
final byte[] result = new byte[src.length];
|
||||
System.arraycopy(src, 0, result, 0, src.length);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Concatenates byte arrays and returns the result.
|
||||
*
|
||||
* @param arrays The arrays.
|
||||
* @return A single array containing all elements in all arrays.
|
||||
*/
|
||||
public static byte[] concat(final byte[]... arrays) {
|
||||
int totalSize = 0;
|
||||
for (final byte[] array : arrays) {
|
||||
totalSize += array.length;
|
||||
}
|
||||
|
||||
int startIndex = 0;
|
||||
final byte[] result = new byte[totalSize];
|
||||
for (final byte[] array : arrays) {
|
||||
System.arraycopy(array, 0, result, startIndex, array.length);
|
||||
startIndex += array.length;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits a single array into two arrays.
|
||||
*
|
||||
* @param bytes The input array.
|
||||
* @param splitIndex The index at which the array should be split.
|
||||
* @return Two arrays split at the splitIndex.
|
||||
* The first array will contain the first splitIndex elements.
|
||||
* The second array will contain all trailing elements.
|
||||
*/
|
||||
public static byte[][] split(final byte[] bytes, final int splitIndex) {
|
||||
if (splitIndex < 0 || bytes.length < splitIndex) {
|
||||
throw new IllegalArgumentException("split index is out of range");
|
||||
}
|
||||
|
||||
final byte[] lhs = new byte[splitIndex];
|
||||
final byte[] rhs = new byte[bytes.length - splitIndex];
|
||||
|
||||
System.arraycopy(bytes, 0, lhs, 0, lhs.length);
|
||||
System.arraycopy(bytes, splitIndex, rhs, 0, rhs.length);
|
||||
return new byte[][] { lhs, rhs };
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a BigInteger to a little endian byte array.
|
||||
*
|
||||
* @param value The value to convert.
|
||||
* @param numBytes The number of bytes in the destination array.
|
||||
* @return The resulting little endian byte array.
|
||||
*/
|
||||
public static byte[] toByteArray(final BigInteger value, final int numBytes) {
|
||||
final byte[] outputBytes = new byte[numBytes];
|
||||
final byte[] bigIntegerBytes = value.toByteArray();
|
||||
|
||||
int copyStartIndex = (0x00 == bigIntegerBytes[0]) ? 1 : 0;
|
||||
int numBytesToCopy = bigIntegerBytes.length - copyStartIndex;
|
||||
if (numBytesToCopy > numBytes) {
|
||||
copyStartIndex += numBytesToCopy - numBytes;
|
||||
numBytesToCopy = numBytes;
|
||||
}
|
||||
|
||||
for (int i = 0; i < numBytesToCopy; ++i) {
|
||||
outputBytes[i] = bigIntegerBytes[copyStartIndex + numBytesToCopy - i - 1];
|
||||
}
|
||||
|
||||
return outputBytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a little endian byte array to a BigInteger.
|
||||
*
|
||||
* @param bytes The bytes to convert.
|
||||
* @return The resulting BigInteger.
|
||||
*/
|
||||
public static BigInteger toBigInteger(final byte[] bytes) {
|
||||
final byte[] bigEndianBytes = new byte[bytes.length + 1];
|
||||
for (int i = 0; i < bytes.length; ++i) {
|
||||
bigEndianBytes[i + 1] = bytes[bytes.length - i - 1];
|
||||
}
|
||||
|
||||
return new BigInteger(bigEndianBytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constant-time byte[] comparison. The constant time behavior eliminates side channel attacks.
|
||||
*
|
||||
* @param b An array.
|
||||
* @param c An array.
|
||||
* @return 1 if b and c are equal, 0 otherwise.
|
||||
*/
|
||||
public static int isEqualConstantTime(final byte[] b, final byte[] c) {
|
||||
int result = 0;
|
||||
result |= b.length - c.length;
|
||||
for (int i = 0; i < b.length; i++) {
|
||||
result |= b[i] ^ c[i];
|
||||
}
|
||||
|
||||
return ByteUtils.isEqualConstantTime(result, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* NON constant-time lexicographical byte[] comparison.
|
||||
*
|
||||
* @param b first of arrays to compare.
|
||||
* @param c second of arrays to compare.
|
||||
* @return 1, -1, or 0 depending on comparison result.
|
||||
*/
|
||||
public static int compare(final byte[] b, final byte[] c) {
|
||||
int result = b.length - c.length;
|
||||
if (0 != result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
for (int i = 0; i < b.length; i++) {
|
||||
result = b[i] - c[i];
|
||||
if (0 != result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the i'th bit of a byte array.
|
||||
*
|
||||
* @param h The byte array.
|
||||
* @param i The bit index.
|
||||
* @return The value of the i'th bit in h
|
||||
*/
|
||||
public static int getBit(final byte[] h, final int i) {
|
||||
return (h[i >> 3] >> (i & 7)) & 1;
|
||||
}
|
||||
}
|
37
source-code/RuffCT-java/src/org/nem/core/utils/Base32Encoder.java
Executable file
37
source-code/RuffCT-java/src/org/nem/core/utils/Base32Encoder.java
Executable file
|
@ -0,0 +1,37 @@
|
|||
package org.nem.core.utils;
|
||||
|
||||
import org.apache.commons.codec.binary.Base32;
|
||||
|
||||
/**
|
||||
* Static class that contains utility functions for converting Base32 strings to and from bytes.
|
||||
*/
|
||||
public class Base32Encoder {
|
||||
|
||||
/**
|
||||
* Converts a string to a byte array.
|
||||
*
|
||||
* @param base32String The input Base32 string.
|
||||
* @return The output byte array.
|
||||
*/
|
||||
public static byte[] getBytes(final String base32String) {
|
||||
final Base32 codec = new Base32();
|
||||
final byte[] encodedBytes = StringEncoder.getBytes(base32String);
|
||||
if (!codec.isInAlphabet(encodedBytes, true)) {
|
||||
throw new IllegalArgumentException("malformed base32 string passed to getBytes");
|
||||
}
|
||||
|
||||
return codec.decode(encodedBytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a byte array to a Base32 string.
|
||||
*
|
||||
* @param bytes The input byte array.
|
||||
* @return The output Base32 string.
|
||||
*/
|
||||
public static String getString(final byte[] bytes) {
|
||||
final Base32 codec = new Base32();
|
||||
final byte[] decodedBytes = codec.encode(bytes);
|
||||
return StringEncoder.getString(decodedBytes);
|
||||
}
|
||||
}
|
37
source-code/RuffCT-java/src/org/nem/core/utils/Base64Encoder.java
Executable file
37
source-code/RuffCT-java/src/org/nem/core/utils/Base64Encoder.java
Executable file
|
@ -0,0 +1,37 @@
|
|||
package org.nem.core.utils;
|
||||
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
|
||||
/**
|
||||
* Static class that contains utility functions for converting Base64 strings to and from bytes.
|
||||
*/
|
||||
public class Base64Encoder {
|
||||
|
||||
/**
|
||||
* Converts a string to a byte array.
|
||||
*
|
||||
* @param base64String The input Base64 string.
|
||||
* @return The output byte array.
|
||||
*/
|
||||
public static byte[] getBytes(final String base64String) {
|
||||
final Base64 codec = new Base64();
|
||||
final byte[] encodedBytes = StringEncoder.getBytes(base64String);
|
||||
if (!codec.isInAlphabet(encodedBytes, true)) {
|
||||
throw new IllegalArgumentException("malformed base64 string passed to getBytes");
|
||||
}
|
||||
|
||||
return codec.decode(encodedBytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a byte array to a Base64 string.
|
||||
*
|
||||
* @param bytes The input byte array.
|
||||
* @return The output Base64 string.
|
||||
*/
|
||||
public static String getString(final byte[] bytes) {
|
||||
final Base64 codec = new Base64();
|
||||
final byte[] decodedBytes = codec.encode(bytes);
|
||||
return StringEncoder.getString(decodedBytes);
|
||||
}
|
||||
}
|
100
source-code/RuffCT-java/src/org/nem/core/utils/ByteUtils.java
Executable file
100
source-code/RuffCT-java/src/org/nem/core/utils/ByteUtils.java
Executable file
|
@ -0,0 +1,100 @@
|
|||
package org.nem.core.utils;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public class ByteUtils {
|
||||
|
||||
/**
|
||||
* Converts an array of 8 bytes into a long.
|
||||
*
|
||||
* @param bytes The bytes.
|
||||
* @return The long.
|
||||
*/
|
||||
public static long bytesToLong(final byte[] bytes) {
|
||||
final ByteBuffer buffer = ByteBuffer.allocate(8);
|
||||
buffer.put(bytes, 0, 8);
|
||||
buffer.flip();
|
||||
return buffer.getLong();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a long value into an array of 8 bytes.
|
||||
*
|
||||
* @param x The long.
|
||||
* @return The bytes.
|
||||
*/
|
||||
public static byte[] longToBytes(final long x) {
|
||||
final ByteBuffer buffer = ByteBuffer.allocate(8);
|
||||
buffer.putLong(x);
|
||||
return buffer.array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an array of 4 bytes into a int.
|
||||
*
|
||||
* @param bytes The bytes.
|
||||
* @return The int.
|
||||
*/
|
||||
public static int bytesToInt(final byte[] bytes) {
|
||||
final ByteBuffer buffer = ByteBuffer.allocate(4);
|
||||
buffer.put(bytes, 0, 4);
|
||||
buffer.flip();
|
||||
return buffer.getInt();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an int value into an array of 4 bytes.
|
||||
*
|
||||
* @param x The int.
|
||||
* @return The bytes.
|
||||
*/
|
||||
public static byte[] intToBytes(final int x) {
|
||||
final ByteBuffer buffer = ByteBuffer.allocate(4);
|
||||
buffer.putInt(x);
|
||||
return buffer.array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constant-time byte comparison. The constant time behavior eliminates side channel attacks.
|
||||
*
|
||||
* @param b One byte.
|
||||
* @param c Another byte.
|
||||
* @return 1 if b and c are equal, 0 otherwise.
|
||||
*/
|
||||
public static int isEqualConstantTime(final int b, final int c) {
|
||||
int result = 0;
|
||||
final int xor = b ^ c;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
result |= xor >> i;
|
||||
}
|
||||
|
||||
return (result ^ 0x01) & 0x01;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constant-time check if byte is negative. The constant time behavior eliminates side channel attacks.
|
||||
*
|
||||
* @param b The byte to check.
|
||||
* @return 1 if the byte is negative, 0 otherwise.
|
||||
*/
|
||||
public static int isNegativeConstantTime(final int b) {
|
||||
return (b >> 8) & 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a human readable representation of an array of bytes.
|
||||
*
|
||||
* @param bytes The bytes.
|
||||
* @return An string representation of the bytes.
|
||||
*/
|
||||
public static String toString(final byte[] bytes) {
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
builder.append("{ ");
|
||||
for (final byte b : bytes) {
|
||||
builder.append(String.format("%02X ", (byte)(0xFF & b)));
|
||||
}
|
||||
|
||||
builder.append("}");
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
88
source-code/RuffCT-java/src/org/nem/core/utils/CircularStack.java
Executable file
88
source-code/RuffCT-java/src/org/nem/core/utils/CircularStack.java
Executable file
|
@ -0,0 +1,88 @@
|
|||
package org.nem.core.utils;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Circular stack is a last in first out buffer with fixed size that replace its oldest element if full.
|
||||
* The removal order is inverse of insertion order. The iteration order is the same as insertion order.
|
||||
* <strong>Note that implementation is not synchronized.</strong>
|
||||
*
|
||||
* @param <E> Type of elements on the stack.
|
||||
*/
|
||||
public class CircularStack<E> implements Iterable<E> {
|
||||
private final List<E> elements;
|
||||
private final int limit;
|
||||
|
||||
/**
|
||||
* Creates circular stack with at most limit elements.
|
||||
*
|
||||
* @param limit Maximum number of elements on the stack.
|
||||
*/
|
||||
public CircularStack(final int limit) {
|
||||
this.elements = new ArrayList<>(limit);
|
||||
this.limit = limit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates shallow copy in destination.
|
||||
*
|
||||
* @param destination CircularStack to which elements should be copied.
|
||||
*/
|
||||
public void shallowCopyTo(final CircularStack<E> destination) {
|
||||
destination.elements.clear();
|
||||
destination.pushAll(this);
|
||||
}
|
||||
|
||||
private void pushAll(final CircularStack<E> rhs) {
|
||||
int i = 0;
|
||||
for (final E element : rhs) {
|
||||
if (i >= rhs.size() - this.limit) {
|
||||
this.push(element);
|
||||
}
|
||||
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds element to the stack.
|
||||
*
|
||||
* @param element Element to be added.
|
||||
*/
|
||||
public void push(final E element) {
|
||||
this.elements.add(element);
|
||||
if (this.elements.size() > this.limit) {
|
||||
this.elements.remove(0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets most recently added element.
|
||||
*
|
||||
* @return Most recently added element.
|
||||
*/
|
||||
public E peek() {
|
||||
return this.elements.get(this.elements.size() - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes most recently added element.
|
||||
*/
|
||||
public void pop() {
|
||||
this.elements.remove(this.elements.size() - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns size of a stack.
|
||||
*
|
||||
* @return Size of a stack (can be smaller than limit).
|
||||
*/
|
||||
public int size() {
|
||||
return this.elements.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<E> iterator() {
|
||||
return this.elements.iterator();
|
||||
}
|
||||
}
|
102
source-code/RuffCT-java/src/org/nem/core/utils/ExceptionUtils.java
Executable file
102
source-code/RuffCT-java/src/org/nem/core/utils/ExceptionUtils.java
Executable file
|
@ -0,0 +1,102 @@
|
|||
package org.nem.core.utils;
|
||||
|
||||
import java.util.concurrent.*;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Static class that contains helper functions for dealing with exceptions.
|
||||
*/
|
||||
public class ExceptionUtils {
|
||||
|
||||
private ExceptionUtils() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface that mimics Runnable but can additionally throw checked exceptions.
|
||||
*/
|
||||
public interface CheckedRunnable {
|
||||
|
||||
/**
|
||||
* Executes the runnable.
|
||||
*
|
||||
* @throws Exception Any exception.
|
||||
*/
|
||||
void call() throws Exception;
|
||||
}
|
||||
|
||||
/**
|
||||
* Propagates checked exceptions as a runtime exception.
|
||||
*
|
||||
* @param runnable The checked runnable.
|
||||
*/
|
||||
public static void propagateVoid(final CheckedRunnable runnable) {
|
||||
propagateVoid(runnable, RuntimeException::new);
|
||||
}
|
||||
|
||||
/**
|
||||
* Propagates checked exceptions as a specific runtime exception.
|
||||
*
|
||||
* @param runnable The checked runnable.
|
||||
* @param wrap A function that wraps an exception in a runtime exception.
|
||||
* @param <E> The specific exception type.
|
||||
*/
|
||||
public static <E extends RuntimeException> void propagateVoid(
|
||||
final CheckedRunnable runnable,
|
||||
final Function<Exception, E> wrap) {
|
||||
propagate(new CheckedRuntimeToCallableAdapter(runnable), wrap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Propagates checked exceptions as a runtime exception.
|
||||
*
|
||||
* @param callable The function.
|
||||
* @param <T> The function return type.
|
||||
* @return The function result.
|
||||
*/
|
||||
public static <T> T propagate(final Callable<T> callable) {
|
||||
return propagate(callable, RuntimeException::new);
|
||||
}
|
||||
|
||||
/**
|
||||
* Propagates checked exceptions as a specific runtime exception.
|
||||
*
|
||||
* @param callable The function.
|
||||
* @param wrap A function that wraps an exception in a runtime exception.
|
||||
* @param <T> The function return type.
|
||||
* @param <E> The specific exception type.
|
||||
* @return The function result.
|
||||
*/
|
||||
public static <T, E extends RuntimeException> T propagate(
|
||||
final Callable<T> callable,
|
||||
final Function<Exception, E> wrap) {
|
||||
try {
|
||||
return callable.call();
|
||||
} catch (final ExecutionException e) {
|
||||
if (RuntimeException.class.isAssignableFrom(e.getCause().getClass())) {
|
||||
throw (RuntimeException)e.getCause();
|
||||
}
|
||||
throw wrap.apply(e);
|
||||
} catch (final RuntimeException e) {
|
||||
throw e;
|
||||
} catch (final InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw new IllegalStateException(e);
|
||||
} catch (final Exception e) {
|
||||
throw wrap.apply(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static class CheckedRuntimeToCallableAdapter implements Callable<Void> {
|
||||
private final CheckedRunnable runnable;
|
||||
|
||||
public CheckedRuntimeToCallableAdapter(final CheckedRunnable runnable) {
|
||||
this.runnable = runnable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void call() throws Exception {
|
||||
this.runnable.call();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
63
source-code/RuffCT-java/src/org/nem/core/utils/FormatUtils.java
Executable file
63
source-code/RuffCT-java/src/org/nem/core/utils/FormatUtils.java
Executable file
|
@ -0,0 +1,63 @@
|
|||
package org.nem.core.utils;
|
||||
|
||||
import java.text.*;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Static class containing helper functions for formatting.
|
||||
*/
|
||||
public class FormatUtils {
|
||||
|
||||
/**
|
||||
* Gets a default decimal format that should be used for formatting decimal values.
|
||||
*
|
||||
* @return A default decimal format.
|
||||
*/
|
||||
public static DecimalFormat getDefaultDecimalFormat() {
|
||||
final DecimalFormatSymbols decimalFormatSymbols = new DecimalFormatSymbols();
|
||||
decimalFormatSymbols.setDecimalSeparator('.');
|
||||
final DecimalFormat format = new DecimalFormat("#0.000", decimalFormatSymbols);
|
||||
format.setGroupingUsed(false);
|
||||
return format;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a decimal format that with the desired number of decimal places.
|
||||
*
|
||||
* @param decimalPlaces The number of decimal places.
|
||||
* @return The desired decimal format.
|
||||
*/
|
||||
public static DecimalFormat getDecimalFormat(final int decimalPlaces) {
|
||||
if (decimalPlaces < 0) {
|
||||
throw new IllegalArgumentException("decimalPlaces must be non-negative");
|
||||
}
|
||||
|
||||
final DecimalFormatSymbols decimalFormatSymbols = new DecimalFormatSymbols();
|
||||
decimalFormatSymbols.setDecimalSeparator('.');
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
builder.append("#0");
|
||||
|
||||
if (decimalPlaces > 0) {
|
||||
builder.append('.');
|
||||
final char[] zeros = new char[decimalPlaces];
|
||||
Arrays.fill(zeros, '0');
|
||||
builder.append(zeros);
|
||||
}
|
||||
|
||||
final DecimalFormat format = new DecimalFormat(builder.toString(), decimalFormatSymbols);
|
||||
format.setGroupingUsed(false);
|
||||
return format;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a double value with a given number of decimal places.
|
||||
*
|
||||
* @param value The value to format.
|
||||
* @param decimalPlaces The desired number of decimal places.
|
||||
* @return The formatted string.
|
||||
*/
|
||||
public static String format(final double value, final int decimalPlaces) {
|
||||
final DecimalFormat formatter = getDecimalFormat(decimalPlaces);
|
||||
return formatter.format(value);
|
||||
}
|
||||
}
|
57
source-code/RuffCT-java/src/org/nem/core/utils/HexEncoder.java
Executable file
57
source-code/RuffCT-java/src/org/nem/core/utils/HexEncoder.java
Executable file
|
@ -0,0 +1,57 @@
|
|||
package org.nem.core.utils;
|
||||
|
||||
import org.apache.commons.codec.DecoderException;
|
||||
import org.apache.commons.codec.binary.Hex;
|
||||
|
||||
/**
|
||||
* Static class that contains utility functions for converting hex strings to and from bytes.
|
||||
*/
|
||||
public class HexEncoder {
|
||||
|
||||
/**
|
||||
* Converts a hex string to a byte array.
|
||||
*
|
||||
* @param hexString The input hex string.
|
||||
* @return The output byte array.
|
||||
*/
|
||||
public static byte[] getBytes(final String hexString) {
|
||||
try {
|
||||
return getBytesInternal(hexString);
|
||||
} catch (final DecoderException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to convert a hex string to a byte array.
|
||||
*
|
||||
* @param hexString The input hex string.
|
||||
* @return The output byte array or null if the input string is malformed.
|
||||
*/
|
||||
public static byte[] tryGetBytes(final String hexString) {
|
||||
try {
|
||||
return getBytesInternal(hexString);
|
||||
} catch (final DecoderException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] getBytesInternal(final String hexString) throws DecoderException {
|
||||
final Hex codec = new Hex();
|
||||
final String paddedHexString = 0 == hexString.length() % 2 ? hexString : "0" + hexString;
|
||||
final byte[] encodedBytes = StringEncoder.getBytes(paddedHexString);
|
||||
return codec.decode(encodedBytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a byte array to a hex string.
|
||||
*
|
||||
* @param bytes The input byte array.
|
||||
* @return The output hex string.
|
||||
*/
|
||||
public static String getString(final byte[] bytes) {
|
||||
final Hex codec = new Hex();
|
||||
final byte[] decodedBytes = codec.encode(bytes);
|
||||
return StringEncoder.getString(decodedBytes);
|
||||
}
|
||||
}
|
494
source-code/RuffCT-java/src/org/nem/core/utils/HttpStatus.java
Executable file
494
source-code/RuffCT-java/src/org/nem/core/utils/HttpStatus.java
Executable file
|
@ -0,0 +1,494 @@
|
|||
package org.nem.core.utils;
|
||||
|
||||
/* We don't want to get spring dependancy just to get this... */
|
||||
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Java 5 enumeration of HTTP status codes.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @author Sebastien Deleuze
|
||||
* @see <a href="http://www.iana.org/assignments/http-status-codes">HTTP Status Code Registry</a>
|
||||
* @see <a href="http://en.wikipedia.org/wiki/List_of_HTTP_status_codes">List of HTTP status codes - Wikipedia</a>
|
||||
*/
|
||||
public enum HttpStatus {
|
||||
|
||||
// 1xx Informational
|
||||
|
||||
/**
|
||||
* {@code 100 Continue}.
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc7231#section-6.2.1">HTTP/1.1: Semantics and Content, section 6.2.1</a>
|
||||
*/
|
||||
CONTINUE(100, "Continue"),
|
||||
/**
|
||||
* {@code 101 Switching Protocols}.
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc7231#section-6.2.2">HTTP/1.1: Semantics and Content, section 6.2.2</a>
|
||||
*/
|
||||
SWITCHING_PROTOCOLS(101, "Switching Protocols"),
|
||||
/**
|
||||
* {@code 102 Processing}.
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2518#section-10.1">WebDAV</a>
|
||||
*/
|
||||
PROCESSING(102, "Processing"),
|
||||
/**
|
||||
* {@code 103 Checkpoint}.
|
||||
*
|
||||
* @see <a href="http://code.google.com/p/gears/wiki/ResumableHttpRequestsProposal">A proposal for supporting
|
||||
* resumable POST/PUT HTTP requests in HTTP/1.0</a>
|
||||
*/
|
||||
CHECKPOINT(103, "Checkpoint"),
|
||||
|
||||
// 2xx Success
|
||||
|
||||
/**
|
||||
* {@code 200 OK}.
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc7231#section-6.3.1">HTTP/1.1: Semantics and Content, section 6.3.1</a>
|
||||
*/
|
||||
OK(200, "OK"),
|
||||
/**
|
||||
* {@code 201 Created}.
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc7231#section-6.3.2">HTTP/1.1: Semantics and Content, section 6.3.2</a>
|
||||
*/
|
||||
CREATED(201, "Created"),
|
||||
/**
|
||||
* {@code 202 Accepted}.
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc7231#section-6.3.3">HTTP/1.1: Semantics and Content, section 6.3.3</a>
|
||||
*/
|
||||
ACCEPTED(202, "Accepted"),
|
||||
/**
|
||||
* {@code 203 Non-Authoritative Information}.
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc7231#section-6.3.4">HTTP/1.1: Semantics and Content, section 6.3.4</a>
|
||||
*/
|
||||
NON_AUTHORITATIVE_INFORMATION(203, "Non-Authoritative Information"),
|
||||
/**
|
||||
* {@code 204 No Content}.
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc7231#section-6.3.5">HTTP/1.1: Semantics and Content, section 6.3.5</a>
|
||||
*/
|
||||
NO_CONTENT(204, "No Content"),
|
||||
/**
|
||||
* {@code 205 Reset Content}.
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc7231#section-6.3.6">HTTP/1.1: Semantics and Content, section 6.3.6</a>
|
||||
*/
|
||||
RESET_CONTENT(205, "Reset Content"),
|
||||
/**
|
||||
* {@code 206 Partial Content}.
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc7233#section-4.1">HTTP/1.1: Range Requests, section 4.1</a>
|
||||
*/
|
||||
PARTIAL_CONTENT(206, "Partial Content"),
|
||||
/**
|
||||
* {@code 207 Multi-Status}.
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc4918#section-13">WebDAV</a>
|
||||
*/
|
||||
MULTI_STATUS(207, "Multi-Status"),
|
||||
/**
|
||||
* {@code 208 Already Reported}.
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5842#section-7.1">WebDAV Binding Extensions</a>
|
||||
*/
|
||||
ALREADY_REPORTED(208, "Already Reported"),
|
||||
/**
|
||||
* {@code 226 IM Used}.
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc3229#section-10.4.1">Delta encoding in HTTP</a>
|
||||
*/
|
||||
IM_USED(226, "IM Used"),
|
||||
|
||||
// 3xx Redirection
|
||||
|
||||
/**
|
||||
* {@code 300 Multiple Choices}.
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc7231#section-6.4.1">HTTP/1.1: Semantics and Content, section 6.4.1</a>
|
||||
*/
|
||||
MULTIPLE_CHOICES(300, "Multiple Choices"),
|
||||
/**
|
||||
* {@code 301 Moved Permanently}.
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc7231#section-6.4.2">HTTP/1.1: Semantics and Content, section 6.4.2</a>
|
||||
*/
|
||||
MOVED_PERMANENTLY(301, "Moved Permanently"),
|
||||
/**
|
||||
* {@code 302 Found}.
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc7231#section-6.4.3">HTTP/1.1: Semantics and Content, section 6.4.3</a>
|
||||
*/
|
||||
FOUND(302, "Found"),
|
||||
/**
|
||||
* {@code 302 Moved Temporarily}.
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc1945#section-9.3">HTTP/1.0, section 9.3</a>
|
||||
* @deprecated In favor of {@link #FOUND} which will be returned from {@code HttpStatus.valueOf(302)}
|
||||
*/
|
||||
@Deprecated
|
||||
MOVED_TEMPORARILY(302, "Moved Temporarily"),
|
||||
/**
|
||||
* {@code 303 See Other}.
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc7231#section-6.4.4">HTTP/1.1: Semantics and Content, section 6.4.4</a>
|
||||
*/
|
||||
SEE_OTHER(303, "See Other"),
|
||||
/**
|
||||
* {@code 304 Not Modified}.
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc7232#section-4.1">HTTP/1.1: Conditional Requests, section 4.1</a>
|
||||
*/
|
||||
NOT_MODIFIED(304, "Not Modified"),
|
||||
/**
|
||||
* {@code 305 Use Proxy}.
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc7231#section-6.4.5">HTTP/1.1: Semantics and Content, section 6.4.5</a>
|
||||
* @deprecated due to security concerns regarding in-band configuration of a proxy
|
||||
*/
|
||||
@Deprecated
|
||||
USE_PROXY(305, "Use Proxy"),
|
||||
/**
|
||||
* {@code 307 Temporary Redirect}.
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc7231#section-6.4.7">HTTP/1.1: Semantics and Content, section 6.4.7</a>
|
||||
*/
|
||||
TEMPORARY_REDIRECT(307, "Temporary Redirect"),
|
||||
/**
|
||||
* {@code 308 Permanent Redirect}.
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc7238">RFC 7238</a>
|
||||
*/
|
||||
PERMANENT_REDIRECT(308, "Permanent Redirect"),
|
||||
|
||||
// --- 4xx Client Error ---
|
||||
|
||||
/**
|
||||
* {@code 400 Bad Request}.
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc7231#section-6.5.1">HTTP/1.1: Semantics and Content, section 6.5.1</a>
|
||||
*/
|
||||
BAD_REQUEST(400, "Bad Request"),
|
||||
/**
|
||||
* {@code 401 Unauthorized}.
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc7235#section-3.1">HTTP/1.1: Authentication, section 3.1</a>
|
||||
*/
|
||||
UNAUTHORIZED(401, "Unauthorized"),
|
||||
/**
|
||||
* {@code 402 Payment Required}.
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc7231#section-6.5.2">HTTP/1.1: Semantics and Content, section 6.5.2</a>
|
||||
*/
|
||||
PAYMENT_REQUIRED(402, "Payment Required"),
|
||||
/**
|
||||
* {@code 403 Forbidden}.
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc7231#section-6.5.3">HTTP/1.1: Semantics and Content, section 6.5.3</a>
|
||||
*/
|
||||
FORBIDDEN(403, "Forbidden"),
|
||||
/**
|
||||
* {@code 404 Not Found}.
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc7231#section-6.5.4">HTTP/1.1: Semantics and Content, section 6.5.4</a>
|
||||
*/
|
||||
NOT_FOUND(404, "Not Found"),
|
||||
/**
|
||||
* {@code 405 Method Not Allowed}.
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc7231#section-6.5.5">HTTP/1.1: Semantics and Content, section 6.5.5</a>
|
||||
*/
|
||||
METHOD_NOT_ALLOWED(405, "Method Not Allowed"),
|
||||
/**
|
||||
* {@code 406 Not Acceptable}.
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc7231#section-6.5.6">HTTP/1.1: Semantics and Content, section 6.5.6</a>
|
||||
*/
|
||||
NOT_ACCEPTABLE(406, "Not Acceptable"),
|
||||
/**
|
||||
* {@code 407 Proxy Authentication Required}.
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc7235#section-3.2">HTTP/1.1: Authentication, section 3.2</a>
|
||||
*/
|
||||
PROXY_AUTHENTICATION_REQUIRED(407, "Proxy Authentication Required"),
|
||||
/**
|
||||
* {@code 408 Request Timeout}.
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc7231#section-6.5.7">HTTP/1.1: Semantics and Content, section 6.5.7</a>
|
||||
*/
|
||||
REQUEST_TIMEOUT(408, "Request Timeout"),
|
||||
/**
|
||||
* {@code 409 Conflict}.
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc7231#section-6.5.8">HTTP/1.1: Semantics and Content, section 6.5.8</a>
|
||||
*/
|
||||
CONFLICT(409, "Conflict"),
|
||||
/**
|
||||
* {@code 410 Gone}.
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc7231#section-6.5.9">HTTP/1.1: Semantics and Content, section 6.5.9</a>
|
||||
*/
|
||||
GONE(410, "Gone"),
|
||||
/**
|
||||
* {@code 411 Length Required}.
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc7231#section-6.5.10">HTTP/1.1: Semantics and Content, section 6.5.10</a>
|
||||
*/
|
||||
LENGTH_REQUIRED(411, "Length Required"),
|
||||
/**
|
||||
* {@code 412 Precondition failed}.
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc7232#section-4.2">HTTP/1.1: Conditional Requests, section 4.2</a>
|
||||
*/
|
||||
PRECONDITION_FAILED(412, "Precondition Failed"),
|
||||
/**
|
||||
* {@code 413 Payload Too Large}.
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc7231#section-6.5.11">HTTP/1.1: Semantics and Content, section 6.5.11</a>
|
||||
* @since 4.1
|
||||
*/
|
||||
PAYLOAD_TOO_LARGE(413, "Payload Too Large"),
|
||||
/**
|
||||
* {@code 413 Request Entity Too Large}.
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2616#section-10.4.14">HTTP/1.1, section 10.4.14</a>
|
||||
* @deprecated In favor of {@link #PAYLOAD_TOO_LARGE} which will be returned from {@code HttpStatus.valueOf(413)}
|
||||
*/
|
||||
@Deprecated
|
||||
REQUEST_ENTITY_TOO_LARGE(413, "Request Entity Too Large"),
|
||||
/**
|
||||
* {@code 414 URI Too Long}.
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc7231#section-6.5.12">HTTP/1.1: Semantics and Content, section 6.5.12</a>
|
||||
* @since 4.1
|
||||
*/
|
||||
URI_TOO_LONG(414, "URI Too Long"),
|
||||
/**
|
||||
* {@code 414 Request-URI Too Long}.
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2616#section-10.4.15">HTTP/1.1, section 10.4.15</a>
|
||||
* @deprecated In favor of {@link #URI_TOO_LONG} which will be returned from {@code HttpStatus.valueOf(414)}
|
||||
*/
|
||||
@Deprecated
|
||||
REQUEST_URI_TOO_LONG(414, "Request-URI Too Long"),
|
||||
/**
|
||||
* {@code 415 Unsupported Media Type}.
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc7231#section-6.5.13">HTTP/1.1: Semantics and Content, section 6.5.13</a>
|
||||
*/
|
||||
UNSUPPORTED_MEDIA_TYPE(415, "Unsupported Media Type"),
|
||||
/**
|
||||
* {@code 416 Requested Range Not Satisfiable}.
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc7233#section-4.4">HTTP/1.1: Range Requests, section 4.4</a>
|
||||
*/
|
||||
REQUESTED_RANGE_NOT_SATISFIABLE(416, "Requested range not satisfiable"),
|
||||
/**
|
||||
* {@code 417 Expectation Failed}.
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc7231#section-6.5.14">HTTP/1.1: Semantics and Content, section 6.5.14</a>
|
||||
*/
|
||||
EXPECTATION_FAILED(417, "Expectation Failed"),
|
||||
/**
|
||||
* {@code 418 I'm a teapot}.
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2324#section-2.3.2">HTCPCP/1.0</a>
|
||||
*/
|
||||
I_AM_A_TEAPOT(418, "I'm a teapot"),
|
||||
/**
|
||||
* @deprecated See <a href="http://tools.ietf.org/rfcdiff?difftype=--hwdiff&url2=draft-ietf-webdav-protocol-06.txt">WebDAV Draft Changes</a>
|
||||
*/
|
||||
@Deprecated
|
||||
INSUFFICIENT_SPACE_ON_RESOURCE(419, "Insufficient Space On Resource"),
|
||||
/**
|
||||
* @deprecated See <a href="http://tools.ietf.org/rfcdiff?difftype=--hwdiff&url2=draft-ietf-webdav-protocol-06.txt">WebDAV Draft Changes</a>
|
||||
*/
|
||||
@Deprecated
|
||||
METHOD_FAILURE(420, "Method Failure"),
|
||||
/**
|
||||
* @deprecated See <a href="http://tools.ietf.org/rfcdiff?difftype=--hwdiff&url2=draft-ietf-webdav-protocol-06.txt">WebDAV Draft Changes</a>
|
||||
*/
|
||||
@Deprecated
|
||||
DESTINATION_LOCKED(421, "Destination Locked"),
|
||||
/**
|
||||
* {@code 422 Unprocessable Entity}.
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc4918#section-11.2">WebDAV</a>
|
||||
*/
|
||||
UNPROCESSABLE_ENTITY(422, "Unprocessable Entity"),
|
||||
/**
|
||||
* {@code 423 Locked}.
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc4918#section-11.3">WebDAV</a>
|
||||
*/
|
||||
LOCKED(423, "Locked"),
|
||||
/**
|
||||
* {@code 424 Failed Dependency}.
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc4918#section-11.4">WebDAV</a>
|
||||
*/
|
||||
FAILED_DEPENDENCY(424, "Failed Dependency"),
|
||||
/**
|
||||
* {@code 426 Upgrade Required}.
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2817#section-6">Upgrading to TLS Within HTTP/1.1</a>
|
||||
*/
|
||||
UPGRADE_REQUIRED(426, "Upgrade Required"),
|
||||
/**
|
||||
* {@code 428 Precondition Required}.
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc6585#section-3">Additional HTTP Status Codes</a>
|
||||
*/
|
||||
PRECONDITION_REQUIRED(428, "Precondition Required"),
|
||||
/**
|
||||
* {@code 429 Too Many Requests}.
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc6585#section-4">Additional HTTP Status Codes</a>
|
||||
*/
|
||||
TOO_MANY_REQUESTS(429, "Too Many Requests"),
|
||||
/**
|
||||
* {@code 431 Request Header Fields Too Large}.
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc6585#section-5">Additional HTTP Status Codes</a>
|
||||
*/
|
||||
REQUEST_HEADER_FIELDS_TOO_LARGE(431, "Request Header Fields Too Large"),
|
||||
|
||||
// --- 5xx Server Error ---
|
||||
|
||||
/**
|
||||
* {@code 500 Internal Server Error}.
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc7231#section-6.6.1">HTTP/1.1: Semantics and Content, section 6.6.1</a>
|
||||
*/
|
||||
INTERNAL_SERVER_ERROR(500, "Internal Server Error"),
|
||||
/**
|
||||
* {@code 501 Not Implemented}.
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc7231#section-6.6.2">HTTP/1.1: Semantics and Content, section 6.6.2</a>
|
||||
*/
|
||||
NOT_IMPLEMENTED(501, "Not Implemented"),
|
||||
/**
|
||||
* {@code 502 Bad Gateway}.
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc7231#section-6.6.3">HTTP/1.1: Semantics and Content, section 6.6.3</a>
|
||||
*/
|
||||
BAD_GATEWAY(502, "Bad Gateway"),
|
||||
/**
|
||||
* {@code 503 Service Unavailable}.
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc7231#section-6.6.4">HTTP/1.1: Semantics and Content, section 6.6.4</a>
|
||||
*/
|
||||
SERVICE_UNAVAILABLE(503, "Service Unavailable"),
|
||||
/**
|
||||
* {@code 504 Gateway Timeout}.
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc7231#section-6.6.5">HTTP/1.1: Semantics and Content, section 6.6.5</a>
|
||||
*/
|
||||
GATEWAY_TIMEOUT(504, "Gateway Timeout"),
|
||||
/**
|
||||
* {@code 505 HTTP Version Not Supported}.
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc7231#section-6.6.6">HTTP/1.1: Semantics and Content, section 6.6.6</a>
|
||||
*/
|
||||
HTTP_VERSION_NOT_SUPPORTED(505, "HTTP Version not supported"),
|
||||
/**
|
||||
* {@code 506 Variant Also Negotiates}
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2295#section-8.1">Transparent Content Negotiation</a>
|
||||
*/
|
||||
VARIANT_ALSO_NEGOTIATES(506, "Variant Also Negotiates"),
|
||||
/**
|
||||
* {@code 507 Insufficient Storage}
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc4918#section-11.5">WebDAV</a>
|
||||
*/
|
||||
INSUFFICIENT_STORAGE(507, "Insufficient Storage"),
|
||||
/**
|
||||
* {@code 508 Loop Detected}
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc5842#section-7.2">WebDAV Binding Extensions</a>
|
||||
*/
|
||||
LOOP_DETECTED(508, "Loop Detected"),
|
||||
/**
|
||||
* {@code 509 Bandwidth Limit Exceeded}
|
||||
*/
|
||||
BANDWIDTH_LIMIT_EXCEEDED(509, "Bandwidth Limit Exceeded"),
|
||||
/**
|
||||
* {@code 510 Not Extended}
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc2774#section-7">HTTP Extension Framework</a>
|
||||
*/
|
||||
NOT_EXTENDED(510, "Not Extended"),
|
||||
/**
|
||||
* {@code 511 Network Authentication Required}.
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc6585#section-6">Additional HTTP Status Codes</a>
|
||||
*/
|
||||
NETWORK_AUTHENTICATION_REQUIRED(511, "Network Authentication Required");
|
||||
|
||||
private final int value;
|
||||
|
||||
private final String reasonPhrase;
|
||||
|
||||
HttpStatus(final int value, final String reasonPhrase) {
|
||||
this.value = value;
|
||||
this.reasonPhrase = reasonPhrase;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the integer value of this status code.
|
||||
*/
|
||||
public int value() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the reason phrase of this status code.
|
||||
*/
|
||||
public String getReasonPhrase() {
|
||||
return this.reasonPhrase;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a string representation of this status code.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return Integer.toString(this.value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the enum constant of this type with the specified numeric value.
|
||||
*
|
||||
* @param statusCode the numeric value of the enum to be returned
|
||||
* @return the enum constant with the specified numeric value
|
||||
* @throws IllegalArgumentException if this enum has no constant for the specified numeric value
|
||||
*/
|
||||
public static HttpStatus valueOf(final int statusCode) {
|
||||
for (final HttpStatus status : values()) {
|
||||
if (status.value == statusCode) {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("No matching constant for [" + statusCode + "]");
|
||||
}
|
||||
}
|
82
source-code/RuffCT-java/src/org/nem/core/utils/LockFile.java
Executable file
82
source-code/RuffCT-java/src/org/nem/core/utils/LockFile.java
Executable file
|
@ -0,0 +1,82 @@
|
|||
package org.nem.core.utils;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.channels.*;
|
||||
|
||||
/**
|
||||
* Static class that exposes functions for interacting with lock files.
|
||||
*/
|
||||
public class LockFile {
|
||||
|
||||
/**
|
||||
* Tries to acquire a file lock for the specified file.
|
||||
*
|
||||
* @param lockFile The lock file.
|
||||
* @return A handle to the file lock if acquired, or null otherwise.
|
||||
*/
|
||||
public static Closeable tryAcquireLock(final File lockFile) {
|
||||
FileLockHandle handle = null;
|
||||
try {
|
||||
handle = new FileLockHandle(lockFile);
|
||||
|
||||
// try to acquire the lock 5 times
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
if (handle.tryLock()) {
|
||||
return handle;
|
||||
}
|
||||
|
||||
ExceptionUtils.propagateVoid(() -> Thread.sleep(10));
|
||||
}
|
||||
|
||||
return null;
|
||||
} catch (final IOException | OverlappingFileLockException e) {
|
||||
return null;
|
||||
} finally {
|
||||
if (null != handle && null == handle.lock) {
|
||||
try {
|
||||
handle.close();
|
||||
} catch (final IOException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether or not the specified file is locked.
|
||||
*
|
||||
* @param lockFile The lock file.
|
||||
* @return true if the file is locked, false otherwise.
|
||||
*/
|
||||
public static boolean isLocked(final File lockFile) {
|
||||
try (final FileLockHandle handle = new FileLockHandle(lockFile)) {
|
||||
return !handle.tryLock();
|
||||
} catch (final OverlappingFileLockException e) {
|
||||
return true;
|
||||
} catch (final IOException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static class FileLockHandle implements Closeable {
|
||||
private final RandomAccessFile file;
|
||||
private FileLock lock;
|
||||
|
||||
public FileLockHandle(final File lockFile) throws IOException {
|
||||
this.file = new RandomAccessFile(lockFile, "rw");
|
||||
}
|
||||
|
||||
private boolean tryLock() throws IOException {
|
||||
this.lock = this.file.getChannel().tryLock();
|
||||
return null != this.lock;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
if (null != this.lock) {
|
||||
this.lock.close();
|
||||
}
|
||||
|
||||
this.file.close();
|
||||
}
|
||||
}
|
||||
}
|
118
source-code/RuffCT-java/src/org/nem/core/utils/MustBe.java
Executable file
118
source-code/RuffCT-java/src/org/nem/core/utils/MustBe.java
Executable file
|
@ -0,0 +1,118 @@
|
|||
package org.nem.core.utils;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Helper class for validating parameters.
|
||||
*/
|
||||
public class MustBe {
|
||||
|
||||
/**
|
||||
* Throws an exception if the specified object is null.
|
||||
*
|
||||
* @param obj The object.
|
||||
* @param name The object name.
|
||||
*/
|
||||
public static void notNull(final Object obj, final String name) {
|
||||
if (null == obj) {
|
||||
final String message = String.format("%s cannot be null", name);
|
||||
throw new IllegalArgumentException(message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws an exception if the specified string contains no non-whitespace characters.
|
||||
*
|
||||
* @param str The string.
|
||||
* @param name The string name.
|
||||
* @param maxLength The max length.
|
||||
*/
|
||||
public static void notWhitespace(final String str, final String name, final int maxLength) {
|
||||
if (StringUtils.isNullOrWhitespace(str) || str.length() > maxLength) {
|
||||
final String message = String.format("%s cannot be null, empty, or whitespace, or have length greater than %d", name, maxLength);
|
||||
throw new IllegalArgumentException(message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws an exception if the specified string does not match the pattern, is empty, or longer than the max length.
|
||||
*
|
||||
* @param str The string.
|
||||
* @param name The string name.
|
||||
* @param pattern The pattern to match.
|
||||
* @param maxLength The max length.
|
||||
*/
|
||||
public static void match(final String str, final String name, final Pattern pattern, final int maxLength) {
|
||||
if (null == str || str.isEmpty() || str.length() > maxLength || !pattern.matcher(str).matches()) {
|
||||
final String message = String.format("%s does not match the desired pattern", name);
|
||||
throw new IllegalArgumentException(message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws an exception if the specified integer value is not in the specified inclusive range.
|
||||
*
|
||||
* @param value The integer value.
|
||||
* @param name The value name.
|
||||
* @param minInclusive The min allowed value (inclusive).
|
||||
* @param maxInclusive The max allowed value (inclusive).
|
||||
*/
|
||||
public static void inRange(final int value, final String name, final int minInclusive, final int maxInclusive) {
|
||||
inRange((long)value, name, minInclusive, maxInclusive);
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws an exception if the specified long value is not in the specified inclusive range.
|
||||
*
|
||||
* @param value The long value.
|
||||
* @param name The value name.
|
||||
* @param minInclusive The min allowed value (inclusive).
|
||||
* @param maxInclusive The max allowed value (inclusive).
|
||||
*/
|
||||
public static void inRange(final long value, final String name, final long minInclusive, final long maxInclusive) {
|
||||
if (value < minInclusive || value > maxInclusive) {
|
||||
final String message = String.format("%s must be between %d and %d inclusive", name, minInclusive, maxInclusive);
|
||||
throw new IllegalArgumentException(message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws an exception if the specified collection is not empty.
|
||||
*
|
||||
* @param collection The collection.
|
||||
* @param name The collection name.
|
||||
*/
|
||||
public static void empty(final Collection<?> collection, final String name) {
|
||||
if (!collection.isEmpty()) {
|
||||
final String message = String.format("%s must be empty", name);
|
||||
throw new IllegalArgumentException(message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws an exception if the specified value is not true.
|
||||
*
|
||||
* @param value The value.
|
||||
* @param name The value name.
|
||||
*/
|
||||
public static void trueValue(final boolean value, final String name) {
|
||||
if (!value) {
|
||||
final String message = String.format("%s must be true", name);
|
||||
throw new IllegalArgumentException(message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws an exception if the specified value is not false.
|
||||
*
|
||||
* @param value The value.
|
||||
* @param name The value name.
|
||||
*/
|
||||
public static void falseValue(final boolean value, final String name) {
|
||||
if (value) {
|
||||
final String message = String.format("%s must be false", name);
|
||||
throw new IllegalArgumentException(message);
|
||||
}
|
||||
}
|
||||
}
|
42
source-code/RuffCT-java/src/org/nem/core/utils/SetOnce.java
Executable file
42
source-code/RuffCT-java/src/org/nem/core/utils/SetOnce.java
Executable file
|
@ -0,0 +1,42 @@
|
|||
package org.nem.core.utils;
|
||||
|
||||
/**
|
||||
* Wrapper that allows an object to be set once (or reset and set again).
|
||||
*
|
||||
* @param <T> The inner type.
|
||||
*/
|
||||
public class SetOnce<T> {
|
||||
private final T defaultValue;
|
||||
private T value;
|
||||
|
||||
/**
|
||||
* Creates a wrapper.
|
||||
*
|
||||
* @param defaultValue The default value.
|
||||
*/
|
||||
public SetOnce(final T defaultValue) {
|
||||
this.defaultValue = defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the inner object.
|
||||
*
|
||||
* @return The inner object.
|
||||
*/
|
||||
public T get() {
|
||||
return null == this.value ? this.defaultValue : this.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the inner object.
|
||||
*
|
||||
* @param value The inner object.
|
||||
*/
|
||||
public void set(final T value) {
|
||||
if (null != this.value && null != value) {
|
||||
throw new IllegalStateException("cannot change value because it is already set");
|
||||
}
|
||||
|
||||
this.value = value;
|
||||
}
|
||||
}
|
31
source-code/RuffCT-java/src/org/nem/core/utils/StringEncoder.java
Executable file
31
source-code/RuffCT-java/src/org/nem/core/utils/StringEncoder.java
Executable file
|
@ -0,0 +1,31 @@
|
|||
package org.nem.core.utils;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
/**
|
||||
* Static class that contains utility functions for converting strings to and from UTF-8 bytes.
|
||||
*/
|
||||
public class StringEncoder {
|
||||
|
||||
private static final Charset ENCODING_CHARSET = Charset.forName("UTF-8");
|
||||
|
||||
/**
|
||||
* Converts a string to a UTF-8 byte array.
|
||||
*
|
||||
* @param s The input string.
|
||||
* @return The output byte array.
|
||||
*/
|
||||
public static byte[] getBytes(final String s) {
|
||||
return s.getBytes(ENCODING_CHARSET);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a UTF-8 byte array to a string.
|
||||
*
|
||||
* @param bytes The input byte array.
|
||||
* @return The output string.
|
||||
*/
|
||||
public static String getString(final byte[] bytes) {
|
||||
return new String(bytes, ENCODING_CHARSET);
|
||||
}
|
||||
}
|
50
source-code/RuffCT-java/src/org/nem/core/utils/StringUtils.java
Executable file
50
source-code/RuffCT-java/src/org/nem/core/utils/StringUtils.java
Executable file
|
@ -0,0 +1,50 @@
|
|||
package org.nem.core.utils;
|
||||
|
||||
/**
|
||||
* Static class that contains string utility functions.
|
||||
*/
|
||||
public class StringUtils {
|
||||
|
||||
/**
|
||||
* Determines if the specified string is null or empty.
|
||||
*
|
||||
* @param str The string.
|
||||
* @return true if the string is null or empty.
|
||||
*/
|
||||
public static boolean isNullOrEmpty(final String str) {
|
||||
return null == str || str.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the specified string is null or whitespace.
|
||||
*
|
||||
* @param str The string.
|
||||
* @return true if the string is null or whitespace.
|
||||
*/
|
||||
public static boolean isNullOrWhitespace(final String str) {
|
||||
if (isNullOrEmpty(str)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (int i = 0; i < str.length(); i++) {
|
||||
if (!Character.isWhitespace(str.charAt(i))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces a variable contained in a string with a value. A variable is defined as ${variable}.
|
||||
* This pattern is replaced by the given value.
|
||||
*
|
||||
* @param string String that contains variables.
|
||||
* @param name Name of the variable to be replaced with its value.
|
||||
* @param value Value that will replace the variable.
|
||||
* @return string with value replacing the variable with the given name
|
||||
*/
|
||||
public static String replaceVariable(final String string, final String name, final String value) {
|
||||
return string.replace(String.format("${%s}", name), value);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,126 @@
|
|||
package test.how.monero.hodl;
|
||||
|
||||
import how.monero.hodl.ringSignature.SpendParams;
|
||||
import org.nem.core.crypto.ed25519.arithmetic.Ed25519GroupElement;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import static how.monero.hodl.ringSignature.BootleRuffing.*;
|
||||
import static test.how.monero.hodl.BootleRuffingSpendTest.createTestSpendParams;
|
||||
|
||||
public class BootleRuffingBenchmarks {
|
||||
|
||||
public static void spendTest() throws IOException {
|
||||
|
||||
int[] inputsVariants = new int[]{1, 2, 3, 4, 5, 10, 20};
|
||||
int[] decompositionExponentVariants = new int[]{2, 3, 4, 5, 6, 7, 8, 9, 10};
|
||||
|
||||
List<List<String>> sheet = new ArrayList<>();
|
||||
List<String> sheetColTitles = new ArrayList<>();
|
||||
sheetColTitles.add("Inputs");
|
||||
sheetColTitles.add("Base");
|
||||
sheetColTitles.add("Exponent");
|
||||
sheetColTitles.add("Ring size");
|
||||
sheetColTitles.add("Generation time (ms)");
|
||||
sheetColTitles.add("Generation scalar mults");
|
||||
sheetColTitles.add("Generation G scalar mults");
|
||||
sheetColTitles.add("Verification time (ms)");
|
||||
sheetColTitles.add("Verification scalar mults");
|
||||
sheetColTitles.add("Verification G scalar mults");
|
||||
sheetColTitles.add("Signature length excl. vins (bytes)");
|
||||
sheetColTitles.add("MLSAG equiv. length excl. vins (bytes)");
|
||||
sheet.add(sheetColTitles);
|
||||
|
||||
int testIterations = 1;
|
||||
int decompositionBase = 2;
|
||||
|
||||
for(int inputs : inputsVariants) {
|
||||
for(int decompositionExponent : decompositionExponentVariants) {
|
||||
|
||||
System.out.println("******* inputs: " + inputs + ", decompositionExponent: " + decompositionExponent);
|
||||
|
||||
long startMs = new Date().getTime();
|
||||
SpendParams[] sp = new SpendParams[testIterations];
|
||||
for (int i=0; i<testIterations; i++) sp[i] = createTestSpendParams(decompositionBase, decompositionExponent, inputs);
|
||||
long spendParamsGenerationDuration = (new Date().getTime()-startMs);
|
||||
System.out.println("Spend params generation duration: " + spendParamsGenerationDuration + " ms");
|
||||
|
||||
startMs = new Date().getTime();
|
||||
// create a transaction to spend the outputs, resulting in a signature that proves the authority to send them
|
||||
SpendSignature[] spendSignature = new SpendSignature[testIterations];
|
||||
for (int i=0; i<testIterations; i++) {
|
||||
Ed25519GroupElement.scalarMults = 0;
|
||||
Ed25519GroupElement.scalarBaseMults = 0;
|
||||
spendSignature[i] = SPEND(sp[i]);
|
||||
}
|
||||
int spendScalarMults = Ed25519GroupElement.scalarMults;
|
||||
int spendScalarBaseMults = Ed25519GroupElement.scalarBaseMults;
|
||||
|
||||
long spendSignatureGenerationDuration = (new Date().getTime()-startMs);
|
||||
System.out.println("Spend signature generation duration: " + spendSignatureGenerationDuration + " ms");
|
||||
|
||||
byte[][] spendSignatureBytes = new byte[testIterations][];
|
||||
spendSignatureBytes[0] = spendSignature[0].toBytes();
|
||||
System.out.println("Spend Signature length (bytes):" + spendSignatureBytes[0].length);
|
||||
|
||||
startMs = new Date().getTime();
|
||||
|
||||
// verify the spend transaction
|
||||
for (int i=0; i<testIterations; i++) {
|
||||
Ed25519GroupElement.scalarMults = 0;
|
||||
Ed25519GroupElement.scalarBaseMults = 0;
|
||||
boolean verified = VER(sp[i].ki, sp[i].pk, sp[i].co, spendSignature[i].co1, sp[i].M, spendSignature[i]);
|
||||
System.out.println("verified: " + verified);
|
||||
}
|
||||
int verifyScalarMults = Ed25519GroupElement.scalarMults;
|
||||
int verifyScalarBaseMults = Ed25519GroupElement.scalarBaseMults;
|
||||
|
||||
|
||||
long spendSignatureVerificationDuration = (new Date().getTime()-startMs);
|
||||
System.out.println("Signature verification duration: " + spendSignatureVerificationDuration + " ms");
|
||||
|
||||
|
||||
List<String> sheetCols = new ArrayList<>();
|
||||
sheetCols.add(inputs+"");
|
||||
sheetCols.add(decompositionBase+"");
|
||||
sheetCols.add(decompositionExponent+"");
|
||||
sheetCols.add(((int)Math.pow(decompositionBase, decompositionExponent))+"");
|
||||
sheetCols.add((spendSignatureGenerationDuration/testIterations)+"");
|
||||
sheetCols.add((spendScalarMults)+"");
|
||||
sheetCols.add((spendScalarBaseMults)+"");
|
||||
sheetCols.add((spendSignatureVerificationDuration/testIterations)+"");
|
||||
sheetCols.add((verifyScalarMults)+"");
|
||||
sheetCols.add((verifyScalarBaseMults)+"");
|
||||
sheetCols.add((spendSignatureBytes[0].length)+"");
|
||||
sheetCols.add((inputs * (32 + 64 * ((int) Math.pow(decompositionBase, decompositionExponent))))+"");
|
||||
sheet.add(sheetCols);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
String csv = "";
|
||||
for(List<String> row : sheet) {
|
||||
for(int i=0; i<row.size(); i++) {
|
||||
csv+=row.get(i);
|
||||
if(i!=(row.size()-1)) csv+=",";
|
||||
else csv+="\n";
|
||||
}
|
||||
}
|
||||
System.out.println(csv);
|
||||
Files.write(new File(System.getProperty("user.home"), "bootleruffing-benchmarks.csv").toPath(), csv.getBytes());
|
||||
|
||||
}
|
||||
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
long startTime = new Date().getTime();
|
||||
spendTest();
|
||||
System.out.println("\nTotal duration: " + (new Date().getTime()-startTime) + " ms");
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,168 @@
|
|||
package test.how.monero.hodl;
|
||||
|
||||
import how.monero.hodl.crypto.PointPair;
|
||||
import how.monero.hodl.crypto.Scalar;
|
||||
import how.monero.hodl.ringSignature.SpendParams;
|
||||
import org.nem.core.crypto.ed25519.arithmetic.Ed25519GroupElement;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
|
||||
import static how.monero.hodl.crypto.CryptoUtil.*;
|
||||
import static how.monero.hodl.ringSignature.BootleRuffing.*;
|
||||
|
||||
public class BootleRuffingSpendTest {
|
||||
|
||||
public static SpendParams createTestSpendParams(int decompositionBase, int decompositionExponent, int inputs) {
|
||||
|
||||
// ring size must be a power of the decomposition base
|
||||
int ringSize = (int) Math.pow(decompositionBase, decompositionExponent);
|
||||
|
||||
SpendParams sp = new SpendParams();
|
||||
|
||||
sp.decompositionBase = decompositionBase;
|
||||
sp.decompositionExponent = decompositionExponent;
|
||||
|
||||
// The owned inputs that are going to be spent
|
||||
Output[] realInputs = new Output[inputs];
|
||||
for(int i=0; i<inputs; i++) realInputs[i] = Output.genRandomOutput(BigInteger.valueOf((long)(Math.random()*1000+1000)));
|
||||
|
||||
// The new outputs to be created (typically one for the recipient one for change)
|
||||
BigInteger fee = BigInteger.valueOf(0); //keep fee as zero for now, to avoid overcomplicating things
|
||||
Output[] outputs = new Output[2];
|
||||
outputs[0] = Output.genRandomOutput(realInputs[0].amount.divide(BigInteger.valueOf(2)));
|
||||
outputs[1] = Output.genRandomOutput(BigInteger.valueOf(Arrays.stream(realInputs).mapToLong(i->i.amount.longValue()).sum()).subtract(fee).subtract(outputs[0].amount));
|
||||
|
||||
sp.iAsterisk = (int) Math.floor(Math.random()*ringSize); // the ring index of the sender's owned inputs
|
||||
|
||||
// input commitments
|
||||
// commitments to the amounts of all inputs referenced in the transaction (real inputs and decoys)
|
||||
Ed25519GroupElement[][] inputCommitments = new Ed25519GroupElement[inputs][ringSize];
|
||||
for(int j=0; j<inputs; j++) {
|
||||
for(int i=0; i<ringSize; i++) {
|
||||
if(i==sp.iAsterisk) inputCommitments[j][i] = realInputs[j].co;
|
||||
else inputCommitments[j][i] = randomPoint();
|
||||
}
|
||||
}
|
||||
|
||||
// there is a co commitment for each ring
|
||||
// each member of co is sum(COMp(input amt i)) - sum(COMp(output amt i))
|
||||
sp.co = new Ed25519GroupElement[ringSize];
|
||||
for(int i=0; i<ringSize; i++) {
|
||||
sp.co[i] = inputCommitments[0][i];
|
||||
for(int j=1; j<inputs; j++) {
|
||||
sp.co[i] = sp.co[i].toP3().add(inputCommitments[j][i].toP3().toCached());
|
||||
}
|
||||
for(int k=0; k<outputs.length; k++) {
|
||||
sp.co[i] = sp.co[i].toP3().subtract(outputs[k].co.toP3().toCached());
|
||||
}
|
||||
}
|
||||
|
||||
// the public keys for every input referenced (including real inputs and decoys)
|
||||
sp.pk = new PointPair[inputs][ringSize];
|
||||
|
||||
// the secret key for every real input referenced
|
||||
sp.sk = new SK[inputs];
|
||||
|
||||
// the key image for every real input referenced
|
||||
sp.ki = new Ed25519GroupElement[inputs];
|
||||
|
||||
for(int j=0; j<inputs; j++) {
|
||||
for(int i=0; i<ringSize; i++) {
|
||||
sp.pk[j][i] = (i==sp.iAsterisk) ? realInputs[j].pk : KEYGEN().pk;
|
||||
}
|
||||
sp.sk[j] = realInputs[j].sk;
|
||||
sp.ki[j] = realInputs[j].ki;
|
||||
}
|
||||
|
||||
// the message being signed is a hash of the transaction, as defined in the C codebase as get_pre_mlsag_hash
|
||||
sp.M = fastHash(randomMessage(100));
|
||||
|
||||
sp.s = Scalar.ZERO;
|
||||
for(int i=0; i<realInputs.length; i++) sp.s = sp.s.add(realInputs[i].mask);
|
||||
for(int i=0; i<outputs.length; i++) sp.s = sp.s.sub(outputs[i].mask);
|
||||
|
||||
Ed25519GroupElement S = realInputs[0].co;
|
||||
for(int i=1; i<realInputs.length; i++) S = S.toP3().add(realInputs[i].co.toP3().toCached());
|
||||
S = S.toP3().subtract(G.scalarMultiply(new Scalar(fee)).toP3().toCached());
|
||||
for(int i=0; i<outputs.length; i++) S = S.toP3().subtract(outputs[i].co.toP3().toCached());
|
||||
|
||||
Ed25519GroupElement S1 = getHpnGLookup(1).scalarMultiply(sp.s);
|
||||
|
||||
if(!S.toP3().equals(S1)) throw new RuntimeException("S != S'");
|
||||
|
||||
return sp;
|
||||
}
|
||||
|
||||
public static void spendTest() {
|
||||
|
||||
boolean pauseAtEachStage = false;
|
||||
int testIterations = 1;
|
||||
int decompositionBase = 2;
|
||||
int decompositionExponent = 8;
|
||||
int inputs = 2;
|
||||
|
||||
System.out.println("Ring size: " + Math.pow(decompositionBase, decompositionExponent));
|
||||
System.out.println("Inputs: " + inputs);
|
||||
|
||||
long startMs = new Date().getTime();
|
||||
SpendParams[] sp = new SpendParams[testIterations];
|
||||
for (int i=0; i<testIterations; i++) sp[i] = createTestSpendParams(decompositionBase, decompositionExponent, inputs);
|
||||
System.out.println("Spend params generation duration: " + (new Date().getTime()-startMs) + " ms");
|
||||
|
||||
if(pauseAtEachStage) { System.out.println("Press enter to continue"); try { System.in.read(); } catch (Exception e) {}; System.out.println("Continuing..."); }
|
||||
|
||||
Ed25519GroupElement.scalarMults = 0;
|
||||
Ed25519GroupElement.scalarBaseMults = 0;
|
||||
|
||||
startMs = new Date().getTime();
|
||||
// create a transaction to spend the outputs, resulting in a signature that proves the authority to send them
|
||||
SpendSignature[] spendSignature = new SpendSignature[testIterations];
|
||||
for (int i=0; i<testIterations; i++) spendSignature[i] = SPEND(sp[i]);
|
||||
|
||||
System.out.println("Spend signature generation duration: " + (new Date().getTime()-startMs) + " ms");
|
||||
|
||||
byte[][] spendSignatureBytes = new byte[testIterations][];
|
||||
for (int i=0; i<testIterations; i++) {
|
||||
spendSignatureBytes[i] = spendSignature[i].toBytes();
|
||||
System.out.println("Spend Signature length (bytes):" + spendSignatureBytes[i].length);
|
||||
}
|
||||
|
||||
if(pauseAtEachStage) { System.out.println("Press enter to continue"); try { System.in.read(); } catch (Exception e) {}; System.out.println("Continuing..."); }
|
||||
startMs = new Date().getTime();
|
||||
|
||||
System.out.println("Spend ScalarMults: " + Ed25519GroupElement.scalarMults);
|
||||
System.out.println("Spend BaseScalarMults: " + Ed25519GroupElement.scalarBaseMults);
|
||||
Ed25519GroupElement.scalarMults = 0;
|
||||
Ed25519GroupElement.scalarBaseMults = 0;
|
||||
|
||||
//Ed25519GroupElement.enableLineRecording = true;
|
||||
Ed25519GroupElement.lineRecordingSourceFile = "BootleRuffing.java";
|
||||
|
||||
// verify the spend transaction
|
||||
for (int i=0; i<testIterations; i++) {
|
||||
|
||||
spendSignature[i] = SpendSignature.fromBytes(spendSignatureBytes[i]);
|
||||
|
||||
boolean verified = VER(sp[i].ki, sp[i].pk, sp[i].co, spendSignature[i].co1, sp[i].M, spendSignature[i]);
|
||||
System.out.println("verified: " + verified);
|
||||
}
|
||||
|
||||
System.out.println("Verify ScalarMults: " + Ed25519GroupElement.scalarMults);
|
||||
System.out.println("Verify BaseScalarMults: " + Ed25519GroupElement.scalarBaseMults);
|
||||
|
||||
System.out.println("Signature verification duration: " + (new Date().getTime()-startMs) + " ms");
|
||||
|
||||
if(Ed25519GroupElement.enableLineRecording) Ed25519GroupElement.lineNumberCallFrequencyMap.entrySet().stream().forEach(e->{System.out.println("line: " + e.getKey() + ", calls: " + e.getValue());});
|
||||
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
long startTime = new Date().getTime();
|
||||
spendTest();
|
||||
System.out.println("Total duration: " + (new Date().getTime()-startTime) + " ms");
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package test.how.monero.hodl;
|
||||
|
||||
import how.monero.hodl.crypto.Scalar;
|
||||
import org.nem.core.crypto.ed25519.arithmetic.Ed25519GroupElement;
|
||||
|
||||
import static how.monero.hodl.crypto.CryptoUtil.COMb;
|
||||
import static how.monero.hodl.ringSignature.BootleRuffing.*;
|
||||
|
||||
public class Prove1Valid1Test1 {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
Scalar[][] b = new Scalar[][] {
|
||||
new Scalar[]{Scalar.ONE, Scalar.ZERO},
|
||||
new Scalar[]{Scalar.ZERO, Scalar.ONE}
|
||||
};
|
||||
|
||||
Scalar r = Scalar.ONE;
|
||||
|
||||
Proof1 P = PROVE1(b, r);
|
||||
|
||||
Ed25519GroupElement B = COMb(b, r);
|
||||
|
||||
System.out.println("VALID1 returns " + VALID1(B, P));
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package test.how.monero.hodl;
|
||||
|
||||
import how.monero.hodl.crypto.PointPair;
|
||||
import how.monero.hodl.crypto.Scalar;
|
||||
|
||||
import static how.monero.hodl.crypto.CryptoUtil.COMeg;
|
||||
import static how.monero.hodl.ringSignature.BootleRuffing.*;
|
||||
|
||||
public class Prove2Valid2Test1 {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
System.out.println("Test: " + new Object(){}.getClass().getEnclosingClass().getName());
|
||||
|
||||
// [c[0], c[1]] = [ COMeg(0, r), COMeg(1, s) ] with secret index 0 should PASS
|
||||
Scalar r = Scalar.ONE;
|
||||
Scalar s = Scalar.ONE;
|
||||
|
||||
PointPair[] co = new PointPair[]{
|
||||
COMeg(Scalar.ZERO, r),
|
||||
COMeg(Scalar.ONE, s)
|
||||
};
|
||||
|
||||
int iAsterisk = 0;
|
||||
|
||||
int inputs = 1;
|
||||
int decompositionBase = 2;
|
||||
int decompositionExponent = 1;
|
||||
|
||||
Proof2 P2 = PROVE2(co, iAsterisk, r, inputs, decompositionBase, decompositionExponent);
|
||||
|
||||
System.out.println("VALID2 result: " + VALID2(decompositionBase, P2, co));
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
package test.how.monero.hodl;
|
||||
|
||||
import how.monero.hodl.crypto.PointPair;
|
||||
import how.monero.hodl.crypto.Scalar;
|
||||
|
||||
import static how.monero.hodl.crypto.CryptoUtil.COMeg;
|
||||
import static how.monero.hodl.ringSignature.BootleRuffing.*;
|
||||
|
||||
public class Prove2Valid2Test1a {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
System.out.println("Test: " + new Object(){}.getClass().getEnclosingClass().getName());
|
||||
|
||||
Scalar r = Scalar.ONE;
|
||||
|
||||
PointPair[] co = new PointPair[]{
|
||||
COMeg(Scalar.ONE,Scalar.ONE),
|
||||
COMeg(Scalar.ONE,Scalar.ONE),
|
||||
COMeg(Scalar.ONE,Scalar.ONE),
|
||||
COMeg(Scalar.ONE,Scalar.ONE),
|
||||
COMeg(Scalar.ONE,Scalar.ONE),
|
||||
COMeg(Scalar.ZERO, Scalar.ONE),
|
||||
COMeg(Scalar.ONE,Scalar.ONE),
|
||||
COMeg(Scalar.ONE,Scalar.ONE),
|
||||
COMeg(Scalar.ONE,Scalar.ONE),
|
||||
};
|
||||
|
||||
int iAsterisk = 5;
|
||||
|
||||
int inputs = 1;
|
||||
int decompositionBase = 3;
|
||||
int decompositionExponent = 2;
|
||||
|
||||
Proof2 P2 = PROVE2(co, iAsterisk, r, inputs, decompositionBase, decompositionExponent);
|
||||
|
||||
System.out.println("VALID2 result: " + VALID2(decompositionBase, P2, co));
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
package test.how.monero.hodl;
|
||||
|
||||
import how.monero.hodl.crypto.PointPair;
|
||||
import how.monero.hodl.crypto.Scalar;
|
||||
import org.nem.core.crypto.ed25519.arithmetic.Ed25519GroupElement;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import static how.monero.hodl.crypto.CryptoUtil.COMeg;
|
||||
import static how.monero.hodl.ringSignature.BootleRuffing.*;
|
||||
|
||||
public class Prove2Valid2Test1b {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
System.out.println("Test: " + new Object(){}.getClass().getEnclosingClass().getName());
|
||||
|
||||
Scalar r = Scalar.ONE;
|
||||
|
||||
for(int k=1; k<=64; k++) {
|
||||
|
||||
System.out.println("---------------------------------------------------------------------------");
|
||||
int len = (int) Math.pow(2, k);
|
||||
PointPair[] co = new PointPair[len];
|
||||
for (int i = 0; i < len; i++) co[i] = COMeg(Scalar.intToScalar(i), Scalar.ONE);
|
||||
|
||||
int iAsterisk = 0;
|
||||
|
||||
int inputs = 1;
|
||||
int decompositionBase = 2;
|
||||
int decompositionExponent = k;
|
||||
|
||||
System.out.println("k: " + k);
|
||||
System.out.println("decompositionBase: " + decompositionBase);
|
||||
System.out.println("decompositionExponent: " + decompositionExponent);
|
||||
|
||||
Ed25519GroupElement.scalarMults = 0;
|
||||
Ed25519GroupElement.scalarBaseMults = 0;
|
||||
long startMs = new Date().getTime();
|
||||
|
||||
Proof2 P2 = PROVE2(co, iAsterisk, r, inputs, decompositionBase, decompositionExponent);
|
||||
|
||||
System.out.println("PROVE2 duration: " + (new Date().getTime() - startMs) + " ms");
|
||||
System.out.println("PROVE2 ScalarMults: " + Ed25519GroupElement.scalarMults);
|
||||
System.out.println("PROVE2 BaseScalarMults: " + Ed25519GroupElement.scalarBaseMults);
|
||||
Ed25519GroupElement.scalarMults = 0;
|
||||
Ed25519GroupElement.scalarBaseMults = 0;
|
||||
startMs = new Date().getTime();
|
||||
|
||||
System.out.println("VALID2 result: " + VALID2(decompositionBase, P2, co));
|
||||
|
||||
System.out.println("VALID2 ScalarMults: " + Ed25519GroupElement.scalarMults);
|
||||
System.out.println("VALID2 BaseScalarMults: " + Ed25519GroupElement.scalarBaseMults);
|
||||
|
||||
System.out.println("VALID2 duration: " + (new Date().getTime() - startMs) + " ms");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
Loading…
Reference in a new issue