Altered Text
[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 createElement(e,t){return '<'+e+'>'+t+'</'+e+'>';} // html element with content t
55 function getElement(i){return document.getElementById(i);}
56 function setHtmlOf(i,t){getElement(i).innerHTML=t};
57 function valueOfField(i){return getElement(i).value;} // value of element with id
58
59 function rand256(){
60         return Math.floor(Math.random() * 256);
61 }
62
63 /* Split number into the shares */
64 function split(number, number_of_shares, needed) {
65     var coef = [number]; // first coefficient always equals the number itself.
66     var shares = [];
67     
68     for(var coef_index = 1; coef_index < needed; coef_index++) coef[coef_index] = rand256();    
69     for(var share_number = 1; share_number <= number_of_shares; share_number++) {
70         var sum = 0;
71         for (var coef_index =0; coef_index<needed; coef_index++) sum += coef[coef_index]*Math.pow(share_number,coef_index);
72         shares[share_number-1] = sum % prime; 
73     }
74     return shares;
75 }
76
77 function collect(){
78         var divs=document.getElementsByClassName('share');
79         var shares=[];
80         var string;
81         
82         // gather values from the input fields
83         for (var div_index=0; div_index<divs.length; div_index++){
84                 var div=divs[div_index];
85                 var inputs = div.getElementsByTagName('input');
86                 var share_number=0;                     
87                 for (var j=0; j<inputs.length;j++){
88                         var input = inputs[j];
89                         if (input.className == 'num') {
90                                 share_number=input.value;
91                         } else {
92                                 string=input.value;
93                                 if (share_number>0 && string!='') {
94                                         var parts = string.split(' ');
95                                         shares[div_index] = { share_number, parts };
96                                 }
97                         }
98                 }
99         }       
100         
101         // the create new input fields on demand
102         if (string!=''){
103                 var clone=div.cloneNode(true);
104                 var inputs=clone.getElementsByTagName('input');
105                 for (var j=0; j<inputs.length; j++) inputs[j].value='';
106                 div.parentNode.appendChild(clone);
107         }
108         return shares;
109 }
110
111 function gcdD(a,b) { 
112     if (b == 0) return [a, 1, 0]; 
113     else { 
114         var n = Math.floor(a/b), c = a % b, r = gcdD(b,c); 
115         return [r[0], r[2], r[1]-r[2]*n];
116     }
117 }
118
119 function modInverse(k) { 
120     k = k % prime;
121     var r = (k < 0) ? -gcdD(prime,-k)[2] : gcdD(prime,k)[2];
122     return (prime + r) % prime;
123 }
124
125 function join(shares) {
126     var sum = 0;
127     
128     for(j = 0; j < shares.length; j++) {
129         var numerator = 1;
130         var denominator = 1;
131         for(m = 0; m < shares.length; m++) {
132             if(j == m) continue; // If not the same value
133             numerator = (numerator * shares[m].share_number) % prime;
134             denominator = (denominator * (shares[m].share_number - shares[j].share_number)) % prime;
135         }
136         sum = (prime + sum + (shares[j].share * numerator * modInverse(denominator))) % prime;
137     }
138     
139     return sum;
140 }
141
142 /* get the number of codes from the list of shares */
143 function numberOfCodesIn(shares_list){
144         var result=null;
145         for (var i=0; i<shares_list.length; i++) {
146                 if (result == null){            
147                         result = shares_list[i].parts.length;
148                 } else {
149                         result=Math.min(shares_list[i].parts.length,result);
150                 }               
151         }
152         return result;
153 }
154
155 /* fetches values from the input fields, decomposes each input string into several shares and reconstructs the original code/char for each position */
156 function decode(){
157         var shares_list=collect(); // fetch shares
158         var ascii=getElement('ascii');
159         var result=getElement('result');        
160         
161         ascii.innerHTML='ascii: ';
162         result.innerHTML='Passphrase: ';
163         
164         var codeCount = numberOfCodesIn(shares_list);
165         for (var codeIndex = 0; codeIndex<codeCount; codeIndex++){
166                 shares_for_code = [];
167                 
168                 shares_list.forEach(function(share_set){
169                         var share_number = share_set.share_number;
170                         var share = share_set.parts[codeIndex];
171                         shares_for_code.push({ share_number, share });
172                 });
173                 
174                 var code = join(shares_for_code);
175                 ascii.innerHTML += code+' ';
176                 result.innerHTML+= String.fromCharCode(code);
177         }
178 }
179
180 /* decomposes the given secrets into single characters. Then, the ascii code of each caracter is treated as secre and split into keys */
181 function secret2numbers(string){
182         var string_pos,share_number,coef,char_code,x,sum;
183         var number_of_shares = valueOfField('shares');
184         var treshold             = valueOfField('treshold');
185         var results = [];
186         
187         // create table elements for displaying the shares
188         var char_cells      = createElement('th','char');;
189         var char_code_cells = createElement('th','ascii');;
190         
191         // decompose string, for each character create secrets from asccii code
192         for (string_pos=0; string_pos<string.length;string_pos++) {
193                 char_code        = string.charCodeAt(string_pos);
194                 char_cells      += createElement('td',string[string_pos]);
195                 char_code_cells += createElement('td',char_code);
196                 
197                 results[string_pos] = split(char_code,number_of_shares,treshold);
198         }       
199         
200         var code=createElement('tr',char_cells)+createElement('tr',char_code_cells);
201         var pages=getElement('pages');
202         var explanation=valueOfField('explanation').replace('%n',valueOfField('treshold')).replace(/\n/g,'<br/>');
203         pages.innerHTML='';
204         for (share_number=0;share_number<number_of_shares;share_number++){
205                 var line=createElement('th','share '+(share_number+1));
206                 var page='';
207                 for (string_pos=0;string_pos<string.length;string_pos++){
208                         line+=createElement('td',results[string_pos][share_number]);
209                         page+=createElement('span',results[string_pos][share_number]);
210                 }
211                 code+=createElement('tr',line);         
212                 pages.innerHTML+=createElement('div',  createElement('p',explanation)+createElement('p','Your share number: '+(share_number+1))+createElement('p',page)   );    
213         }
214         setHtmlOf('tab',code);
215         return char_code;
216 }
217 </script>
218 </head>
219 <body>
220         <fieldset>
221                 <legend>Decode secret from shares</legend>
222                 <div>
223                         <div class="share">
224                                 Share #<input class="num" value="1" />: <input class="code" onkeyup="decode();"/>
225                         </div>
226                 </div>          
227                 <p id="ascii"></p>
228                 <p id="result"></p>
229         </fieldset>
230         <fieldset>
231                 <legend>Create shares from secret</legend>
232                 Explanation to add:<br/>                
233                 <textarea id="explanation" name="explanation">
234 This document is a PART of a secret passphrase. Exactly %n of these secret parts are required to reconstruct the original passphrase.
235 In the event of (enter condition here), you may meet with other secret-part holders to unite your keys.
236
237 To obtain the secret phassphrase:
238 <ol>
239         <li>collect at least %n secret shares</li>
240         <li>go to [url of this page]</li>
241         <li>enter the share number and the respective numbers separated by spaces</li>
242 </ol>
243 The passphrase obtained will give you access to [insert description of secured thing here...].
244
245 [More instructions]
246
247 Info: These secrets were generated using the "Shamir's Secret Sharing" algorithm.
248                 </textarea><br/>
249                 Passphrase parts to generate: <input id="shares" value="5" /></br>
250                 Minimum nuber of shares required to recreate secret: <input id="treshold" value="3" /></br>
251                 Secret: <input name="secret" onkeyup="secret2numbers(this.value);" />
252                 <table id="tab"></table>
253                 <ol>
254                 <li>Set the explanation you want.</li>
255                 <li>Enter the number of parts and shares you wish.</li>
256                 <li>Enter your secret passphrase</li>
257                 <li>Hit Ctrl+P or go to print via the menu</li>
258                 </ol>
259         </fieldset>
260         <div id="pages"></div>
261 </body>
262 </html>