implemented a version that works for odd numbers of shareholders
[shamirs_secret_web_implementation.git] / src / secret.html
1 <html>
2 <head>
3 <style type="text/css">
4         @media print {
5                 #pages > div{
6                         page-break-after: always;
7                 }
8         
9                 fieldset{
10                         display: none;
11                 };
12         }
13         
14         @media screen {
15                 #pages{
16                         display: none;
17                 }
18         }       
19
20         td{
21                 text-align: right;
22                 padding: 10px;
23         }
24         td:nth-child(2n){
25                 background:#ccc;
26         }
27         tr:nth-child(2) td{
28                 border-width: 0 0 5px 0;
29                 border-style: solid;
30                 
31         }
32         .half{
33                 border: 1px solid black;
34         }
35         .num{
36                 width: 40px;
37         }
38         
39         #pages span {
40         border: 1px solid gray;
41         display: inline-block;
42         margin: 3px;
43         padding: 3px;
44         }
45         textarea{ 
46                 width: 100%;
47                 min-height: 150px;
48         }
49         
50         
51 </style>
52 <script type="text/javascript">
53 var prime=257;
54 function e(e,t){return '<'+e+'>'+t+'</'+e+'>';} // html element with content t
55 function g(i){return document.getElementById(i);}
56 function h(i,t){g(i).innerHTML=t};
57 function v(i){return g(i).value;} // value of element with id
58
59
60 /* Split number into the shares */
61 function split(number, available, needed) {
62     var coef = [], x, exp, c, accum, shares = [];
63     for(c = 1, coef[0] = number; c < needed; c++) coef[c] = Math.floor(Math.random() * (prime  - 1));
64     for(x = 1; x <= available; x++) {
65         for(exp = 1, accum = coef[0]; exp < needed; exp++) accum = (accum + (coef[exp] * (Math.pow(x, exp) % prime) % prime)) % prime;
66         shares[x - 1] = accum;
67     }
68     return shares;
69 }
70
71 function share(index,string){
72         this.index=index;
73         this.codes=string.split(' ');
74         this.codeCount=function(){return this.codes.length};
75         this.code=function(i){return this.codes[i];};
76         this.pick=function(i){return [this.index,this.codes[i]]};
77         this.string=function(){
78                 return 'Share '+this.index+': '+this.codes.join(' / ');
79         };
80 }
81
82 function collect(){
83         var divs=document.getElementsByClassName('share');
84         var shares=[],lastValue,div,inputs,num;
85         for (i=0; i<divs.length; i++){
86                 div=divs[i];
87                 inputs = div.getElementsByTagName('input');
88                 num=0;                  
89                 for (var j=0; j<inputs.length;j++){
90                         var input = inputs[j];
91                         if (input.className == 'num') {
92                                 num=input.value;
93                         } else {
94                                 lastValue=input.value;
95                                 if (num>0 && lastValue!='') shares[i]=new share(num,lastValue);
96                         }
97                 }
98         } 
99         if (lastValue!=''){
100                 var clone=div.cloneNode(true);
101                 var inputs=clone.getElementsByTagName('input');
102                 for (var j=0; j<inputs.length; j++) inputs[j].value='';
103                 div.parentNode.appendChild(clone);
104         }
105         return shares;
106 }
107
108 function gcdD(a,b) { 
109     if (b == 0) return [a, 1, 0]; 
110     else { 
111         var n = Math.floor(a/b), c = a % b, r = gcdD(b,c); 
112         return [r[0], r[2], r[1]-r[2]*n];
113     }
114 }
115
116 function modInverse(k) { 
117     k = k % prime;
118     var r = (k < 0) ? -gcdD(prime,-k)[2] : gcdD(prime,k)[2];
119     return (prime + r) % prime;
120 }
121
122 function join(shares) {
123     var accum, count, formula, startposition, nextposition, value, numerator, denominator;
124     for(formula = accum = 0; formula < shares.length; formula++) {
125         for(count = 0, numerator = denominator = 1; count < shares.length; count++) {
126             if(formula == count) continue; // If not the same value
127             startposition = shares[formula][0];
128             nextposition = shares[count][0];
129             numerator = (numerator * -nextposition) % prime;
130             denominator = (denominator * (startposition - nextposition)) % prime;
131         }
132         value = shares[formula][1];
133         accum = (prime + accum + (value * numerator * modInverse(denominator))) % prime;
134     }
135     return accum;
136 }
137
138 function decode(){
139         var shares=collect(); // fetch shares
140         var codeCount=null;
141         for (var i=0; i<shares.length; i++) codeCount=(codeCount==null)?shares[i].codeCount():Math.min(shares[i].codeCount(),codeCount);
142         var ascii=document.getElementById('ascii');
143         ascii.innerHTML='ascii: ';
144         result.innerHTML='Passphrase: ';
145         for (codeIndex=0; codeIndex<codeCount; codeIndex++){
146                 var shareSet=[];
147                 for (var i=0; i<shares.length; i++){
148                         shareSet[i]=shares[i].pick(codeIndex);
149                 }
150                 var r=join(shareSet);
151                 ascii.innerHTML += r+' ';
152                 result.innerHTML+= String.fromCharCode(r);
153         }
154 }
155
156 function secret2numbers(s){
157         var i,j,coef,n,x,sum;
158         var shares = v('shares');
159         var treshold = v('treshold');
160         var results = [];
161         var th=e('th','char');;
162         var ns=e('th','ascii');;
163         
164         for (i=0; i<s.length;i++) {
165                 n=s.charCodeAt(i);
166                 th+=e('td',s[i]);
167                 ns+=e('td',n);
168                 
169                 coef=[n];
170                 for (j=1; j<treshold;j++) coef[j]=Math.floor(Math.random()*prime);
171                 
172                 results[i]=split(n,shares,treshold);
173         }       
174         
175         var code=e('tr',th)+e('tr',ns);
176         var pages=g('pages');
177         var explanation=v('explanation').replace('%n',v('treshold')).replace(/\n/g,'<br/>');
178         pages.innerHTML='';
179         for (j=0;j<shares;j++){
180                 var line=e('th','share '+(j+1));
181                 var page='';
182                 for (i=0;i<s.length;i++){
183                         line+=e('td',results[i][j]);
184                         page+=e('span',results[i][j]);
185                 }
186                 code+=e('tr',line);             
187                 pages.innerHTML+=e('div',  e('p',explanation)+e('p','Your share number: '+(j+1))+e('p',page)   );       
188         }
189         h('tab',code);
190         return n;
191 }
192 </script>
193 </head>
194 <body>
195         <fieldset>
196                 <legend>Decode secret from shares</legend>
197                 <div>
198                         <div class="share">
199                                 Share #<input class="num" value="1" />: <input class="code" onkeyup="decode();"/>
200                         </div>
201                 </div>          
202                 <p id="ascii"></p>
203                 <p id="result"></p>
204         </fieldset>
205         <fieldset>
206                 <legend>Create shares from secret</legend>
207                 Explanation to add:<br/>                
208                 <textarea id="explanation" name="explanation">
209 This document is a PART of a secret passphrase. At least %n of these secret parts are required to reconstruct the original passphrase.
210 In the event of (enter condition here), you may meet with other secret-part holders to unite your keys.
211
212 To obtain the secret phassphrase:
213 <ol>
214         <li>collect at least %n secret shares</li>
215         <li>go to [url of this page]</li>
216         <li>enter the share number and the respective numbers separated by spaces</li>
217 </ol>
218 The passphrase obtained will give you access to [insert description of secured thing here...].
219
220 [More instructions]
221
222 Info: These secrets were generated using the "Shamir's Secret Sharing" algorithm.
223                 </textarea><br/>
224                 Passphrase parts to generate: <input id="shares" value="5" /></br>
225                 Minimum nuber of shares required to recreate secret: <input id="treshold" value="3" /></br>
226                 Secret: <input name="secret" onkeyup="secret2numbers(this.value);" />
227                 <table id="tab"></table>
228                 <ol>
229                 <li>Set the explanation you want.</li>
230                 <li>Enter the number of parts and shares you wish.</li>
231                 <li>Enter your secret passphrase</li>
232                 <li>Hit Ctrl+P or go to print via the menu</li>
233                 </ol>
234         </fieldset>
235         <div id="pages"></div>
236 </body>
237 </html>