implemented a version that works for odd numbers of shareholders
authorStephan Richter <github@keawe.de>
Tue, 19 Sep 2017 08:20:11 +0000 (10:20 +0200)
committerStephan Richter <github@keawe.de>
Tue, 19 Sep 2017 08:20:11 +0000 (10:20 +0200)
src/secret.html

index 3a32cfc91c6652d3493e1a38a947890e30d67127..996ccd23e9686dfffbdafd05f61ebbc59270649f 100644 (file)
 <html>
 <head>
 <style type="text/css">
+       @media print {
+               #pages > div{
+                       page-break-after: always;
+               }
+       
+               fieldset{
+                       display: none;
+               };
+       }
+       
+       @media screen {
+               #pages{
+                       display: none;
+               }
+       }       
+
        td{
                text-align: right;
-               border-style: solid;
-               border-width: 0 0 0 1px;
                padding: 10px;
        }
+       td:nth-child(2n){
+               background:#ccc;
+       }
+       tr:nth-child(2) td{
+               border-width: 0 0 5px 0;
+               border-style: solid;
+               
+       }
+       .half{
+               border: 1px solid black;
+       }
+       .num{
+               width: 40px;
+       }
+       
+       #pages span {
+       border: 1px solid gray;
+       display: inline-block;
+       margin: 3px;
+       padding: 3px;
+       }
+       textarea{ 
+               width: 100%;
+               min-height: 150px;
+       }
+       
+       
 </style>
 <script type="text/javascript">
 var prime=257;
 function e(e,t){return '<'+e+'>'+t+'</'+e+'>';} // html element with content t
-function v(i){return document.getElementById(i).value;} // value of element with id
-function h(i,t){document.getElementById(i).innerHTML=t};
+function g(i){return document.getElementById(i);}
+function h(i,t){g(i).innerHTML=t};
+function v(i){return g(i).value;} // value of element with id
+
+
+/* Split number into the shares */
+function split(number, available, needed) {
+    var coef = [], x, exp, c, accum, shares = [];
+    for(c = 1, coef[0] = number; c < needed; c++) coef[c] = Math.floor(Math.random() * (prime  - 1));
+    for(x = 1; x <= available; x++) {
+        for(exp = 1, accum = coef[0]; exp < needed; exp++) accum = (accum + (coef[exp] * (Math.pow(x, exp) % prime) % prime)) % prime;
+        shares[x - 1] = accum;
+    }
+    return shares;
+}
 
-function s2n(s){
+function share(index,string){
+       this.index=index;
+       this.codes=string.split(' ');
+       this.codeCount=function(){return this.codes.length};
+       this.code=function(i){return this.codes[i];};
+       this.pick=function(i){return [this.index,this.codes[i]]};
+       this.string=function(){
+               return 'Share '+this.index+': '+this.codes.join(' / ');
+       };
+}
+
+function collect(){
+       var divs=document.getElementsByClassName('share');
+       var shares=[],lastValue,div,inputs,num;
+       for (i=0; i<divs.length; i++){
+               div=divs[i];
+               inputs = div.getElementsByTagName('input');
+               num=0;                  
+               for (var j=0; j<inputs.length;j++){
+                       var input = inputs[j];
+                       if (input.className == 'num') {
+                               num=input.value;
+                       } else {
+                               lastValue=input.value;
+                               if (num>0 && lastValue!='') shares[i]=new share(num,lastValue);
+                       }
+               }
+       } 
+       if (lastValue!=''){
+               var clone=div.cloneNode(true);
+               var inputs=clone.getElementsByTagName('input');
+               for (var j=0; j<inputs.length; j++) inputs[j].value='';
+               div.parentNode.appendChild(clone);
+       }
+       return shares;
+}
+
+function gcdD(a,b) { 
+    if (b == 0) return [a, 1, 0]; 
+    else { 
+        var n = Math.floor(a/b), c = a % b, r = gcdD(b,c); 
+        return [r[0], r[2], r[1]-r[2]*n];
+    }
+}
+
+function modInverse(k) { 
+    k = k % prime;
+    var r = (k < 0) ? -gcdD(prime,-k)[2] : gcdD(prime,k)[2];
+    return (prime + r) % prime;
+}
+
+function join(shares) {
+    var accum, count, formula, startposition, nextposition, value, numerator, denominator;
+    for(formula = accum = 0; formula < shares.length; formula++) {
+        for(count = 0, numerator = denominator = 1; count < shares.length; count++) {
+            if(formula == count) continue; // If not the same value
+            startposition = shares[formula][0];
+            nextposition = shares[count][0];
+            numerator = (numerator * -nextposition) % prime;
+            denominator = (denominator * (startposition - nextposition)) % prime;
+        }
+        value = shares[formula][1];
+        accum = (prime + accum + (value * numerator * modInverse(denominator))) % prime;
+    }
+    return accum;
+}
+
+function decode(){
+       var shares=collect(); // fetch shares
+       var codeCount=null;
+       for (var i=0; i<shares.length; i++) codeCount=(codeCount==null)?shares[i].codeCount():Math.min(shares[i].codeCount(),codeCount);
+       var ascii=document.getElementById('ascii');
+       ascii.innerHTML='ascii: ';
+       result.innerHTML='Passphrase: ';
+       for (codeIndex=0; codeIndex<codeCount; codeIndex++){
+               var shareSet=[];
+               for (var i=0; i<shares.length; i++){
+                       shareSet[i]=shares[i].pick(codeIndex);
+               }
+               var r=join(shareSet);
+               ascii.innerHTML += r+' ';
+               result.innerHTML+= String.fromCharCode(r);
+       }
+}
+
+function secret2numbers(s){
        var i,j,coef,n,x,sum;
        var shares = v('shares');
        var treshold = v('treshold');
@@ -30,21 +169,22 @@ function s2n(s){
                coef=[n];
                for (j=1; j<treshold;j++) coef[j]=Math.floor(Math.random()*prime);
                
-               results[i]=[];
-               
-               for (sum=0,x=0; x<shares; x++){                         
-                       for (j in coef) sum+=coef[j]*Math.pow(x+1,j);
-                       results[i][x]=sum%prime;                                        
-               }
+               results[i]=split(n,shares,treshold);
        }       
        
        var code=e('tr',th)+e('tr',ns);
+       var pages=g('pages');
+       var explanation=v('explanation').replace('%n',v('treshold')).replace(/\n/g,'<br/>');
+       pages.innerHTML='';
        for (j=0;j<shares;j++){
                var line=e('th','share '+(j+1));
+               var page='';
                for (i=0;i<s.length;i++){
                        line+=e('td',results[i][j]);
+                       page+=e('span',results[i][j]);
                }
-               code+=e('tr',line);
+               code+=e('tr',line);             
+               pages.innerHTML+=e('div',  e('p',explanation)+e('p','Your share number: '+(j+1))+e('p',page)   );       
        }
        h('tab',code);
        return n;
@@ -52,10 +192,46 @@ function s2n(s){
 </script>
 </head>
 <body>
-Shares: <input id="shares" value="5"></br>
-Min: <input id="treshold" value="3"></br>
-Secret: <input name="secret" onkeyup="s2n(this.value);">
-<table id="tab">
-</table>
+       <fieldset>
+               <legend>Decode secret from shares</legend>
+               <div>
+                       <div class="share">
+                               Share #<input class="num" value="1" />: <input class="code" onkeyup="decode();"/>
+                       </div>
+               </div>          
+               <p id="ascii"></p>
+               <p id="result"></p>
+       </fieldset>
+       <fieldset>
+               <legend>Create shares from secret</legend>
+               Explanation to add:<br/>                
+               <textarea id="explanation" name="explanation">
+This document is a PART of a secret passphrase. At least %n of these secret parts are required to reconstruct the original passphrase.
+In the event of (enter condition here), you may meet with other secret-part holders to unite your keys.
+
+To obtain the secret phassphrase:
+<ol>
+       <li>collect at least %n secret shares</li>
+       <li>go to [url of this page]</li>
+       <li>enter the share number and the respective numbers separated by spaces</li>
+</ol>
+The passphrase obtained will give you access to [insert description of secured thing here...].
+
+[More instructions]
+
+Info: These secrets were generated using the "Shamir's Secret Sharing" algorithm.
+               </textarea><br/>
+               Passphrase parts to generate: <input id="shares" value="5" /></br>
+               Minimum nuber of shares required to recreate secret: <input id="treshold" value="3" /></br>
+               Secret: <input name="secret" onkeyup="secret2numbers(this.value);" />
+               <table id="tab"></table>
+               <ol>
+               <li>Set the explanation you want.</li>
+               <li>Enter the number of parts and shares you wish.</li>
+               <li>Enter your secret passphrase</li>
+               <li>Hit Ctrl+P or go to print via the menu</li>
+               </ol>
+       </fieldset>
+       <div id="pages"></div>
 </body>
 </html>
\ No newline at end of file