Skip to content

Commit

Permalink
initial working demo, basic elements styles, new go method to get lim…
Browse files Browse the repository at this point in the history
…its of number of shares and threshold
  • Loading branch information
lucasmenendez committed Apr 6, 2024
1 parent 7cd17a8 commit 29828b6
Show file tree
Hide file tree
Showing 4 changed files with 176 additions and 43 deletions.
44 changes: 31 additions & 13 deletions cmd/webassembly/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,33 @@ const (
jsClassName = "GoSSS"
jsHideMethod = "hide"
jsRecoverMethod = "recover"
jsLimitsMethod = "limits"
// required number of arguments for each method
hidedNArgs = 3 // msg, nshares, minshares
recoverdNArgs = 1 // shares
limitsNArgs = 1 // msg
)

func wasmError(err error) js.Value {
return js.Global().Call("throwError", js.ValueOf(err.Error()))
func wasmResult(data interface{}, err error) js.Value {
response := map[string]interface{}{}
if data != nil {
response["data"] = data
}
if err != nil {
response["error"] = err.Error()
}
result, err := json.Marshal(response)
if err != nil {
return js.ValueOf(fmt.Sprintf(`{"error": "%s"}`, err.Error()))
}
return js.ValueOf(string(result))
}

func main() {
gosssClass := js.ValueOf(map[string]interface{}{})
gosssClass.Set(jsHideMethod, js.FuncOf(func(this js.Value, p []js.Value) interface{} {
if len(p) != hidedNArgs {
return wasmError(fmt.Errorf("invalid number of arguments"))
return wasmResult(nil, fmt.Errorf("invalid number of arguments"))
}
msg := p[0].String()
nshares := p[1].Int()
Expand All @@ -40,30 +53,35 @@ func main() {
Min: minshares,
})
if err != nil {
return wasmError(err)
return wasmResult(nil, err)
}
jsonShares, err := json.Marshal(shares)
if err != nil {
return wasmError(err)
}
return string(jsonShares)
return wasmResult(shares, nil)
}))

gosssClass.Set(jsRecoverMethod, js.FuncOf(func(this js.Value, p []js.Value) interface{} {
if len(p) != recoverdNArgs {
return wasmError(fmt.Errorf("invalid number of arguments"))
return wasmResult(nil, fmt.Errorf("invalid number of arguments"))
}
// recover the shares from the json string
var shares []string
if err := json.Unmarshal([]byte(p[0].String()), &shares); err != nil {
return wasmError(err)
return wasmResult(nil, err)
}
// recover the message
msg, err := gosss.RecoverMessage(shares, nil)
if err != nil {
return wasmError(err)
return wasmResult(nil, err)
}
return wasmResult(msg, nil)
}))

gosssClass.Set(jsLimitsMethod, js.FuncOf(func(this js.Value, p []js.Value) interface{} {
if len(p) != limitsNArgs {
return wasmResult(nil, fmt.Errorf("invalid number of arguments"))
}
return string(msg)
// recover the message
minShares, maxShares, minMin, maxMin := gosss.ConfigLimits([]byte(p[0].String()))
return wasmResult([]int{minShares, maxShares, minMin, maxMin}, nil)
}))

js.Global().Set(jsClassName, gosssClass)
Expand Down
17 changes: 17 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,23 @@ var DefaultPrime, _ = new(big.Int).SetString("2188824287183927522224640574525727
// using 2 bytes (255^2)
const maxShares = 65536

// ConfigLimits returns the limits for the number of shares and minimum shares
// to recover the original message based on the message size. It returns the
// minimum number of shares, the maximum number of shares, the minimum number
// of shares to recover the secret, and the maximum number of shares to recover
// the secret. The message is divided into parts based on the size of the prime
// number used as finite field, the number of parts is used to calculate the
// limits.
func ConfigLimits(message []byte) (int, int, int, int) {
c := &Config{Prime: DefaultPrime}
secrets := encodeMessage(message, c.maxSecretPartSize())
nSecrets := len(secrets)
minShares := nSecrets * 3
minMin := nSecrets * 2
maxMin := maxShares - nSecrets
return minShares, maxShares, minMin, maxMin
}

// Config struct defines the configuration for the Shamir Secret Sharing
// algorithm. It includes the number of shares to generate, the minimum number
// of shares to recover the secret, and the prime number to use as finite field.
Expand Down
105 changes: 105 additions & 0 deletions web/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import { createApp } from 'https://unpkg.com/vue@3/dist/vue.esm-browser.prod.js'

const app = createApp({
data() {
return {
config: {
minShares: 0,
maxShares: 0,
minMin: 0,
maxMin: 0,
},
message: "",
sharesCount: 0,
threshold: 0,
shares: "",
currentTab: "hide",
hide_result: "",
recovered_message: "",
}
},
async created() {
await this.setupWebAssembly();
},
template: `
<div>
<button class="button" @click="currentTab = 'hide'">Hide</button>
<button class="button" @click="currentTab = 'recover'">Recover</button>
<div v-show="currentTab === 'hide'">
<h3>Hide a message</h3>
<textarea class="textarea" v-model="message" placeholder="Enter your message" rows="6"></textarea>
<div v-show="message !== ''">
<label class="label">Shares <small>({{ config.minShares }} - {{ config.maxShares }})</small></label>
<input v-model="sharesCount" type="number" class="input" :min="config.minShares" :max="config.maxShares" :step="config.minShares">
</div>
<div v-show="message !== ''">
<label class="label">Threshold <small>({{ config.minMin }} - {{ sharesCount - 1 }})</small></label>
<input v-model="threshold" type="number" class="input" :min="config.minMin" :max="sharesCount - 1">
</div>
<button class="button is-secondary" :class="{'is-disabled': message == ''}" @click="hideMessage">Hide</button>
<textarea class="textarea" readonly v-model="hide_result" placeholder="Resulting secret parts" rows="6"></textarea>
</div>
<div v-show="currentTab === 'recover'">
<h3>Recover a message</h3>
<textarea class="textarea" v-model="shares" placeholder="Enter shares" rows="6"></textarea>
<button class="button is-secondary" :class="{'is-disabled': message == ''}" @click="recoverMessage">Recover</button>
<textarea class="textarea" readonly v-model="recovered_message" placeholder="Recovered message" rows="6"></textarea>
</div>
</div>
`,
methods: {
async setupWebAssembly() {
const go = new Go();
const result = await WebAssembly.instantiateStreaming(fetch("gosss.wasm"), go.importObject);
go.run(result.instance);
},
getConfig() {
const rawResult = GoSSS.limits(this.message);
const result = JSON.parse(rawResult);
if (!result.error) {
this.config = {
minShares: result.data[0],
maxShares: result.data[1],
minMin: result.data[2],
maxMin: result.data[3],
}
if (this.sharesCount < this.config.minShares) this.sharesCount = this.config.minShares;
if (this.sharesCount > this.config.maxShares) this.sharesCount = this.config.maxShares;
if (this.threshold < this.config.minMin) this.threshold = this.config.minMin;
if (this.threshold > this.sharesCount - 1) this.threshold = this.sharesCount - 1;
} else {
alert(`Error getting configuration: ${result.error}`);
}
},
hideMessage() {
const rawResult = GoSSS.hide(this.message, this.sharesCount, this.threshold);
const result = JSON.parse(rawResult);
if (!result.error) {
this.hide_result = result.data.join("\n");
} else {
alert(`Error hiding message: ${result.error}`);
}
},
recoverMessage() {
const shares = JSON.stringify(this.shares.split("\n"));
const rawResult = GoSSS.recover(shares);
const result = JSON.parse(rawResult);
if (!result.error) {
this.recovered_message = window.atob(result.data);
} else {
alert(`Error recovering message: ${result.error}`);
}
}
},
watch: {
message() {
this.getConfig();
}
},
components: {

}
});

app.mount('#app');
53 changes: 23 additions & 30 deletions web/index.html
Original file line number Diff line number Diff line change
@@ -1,30 +1,23 @@
<html>
<head>
<title>Go Shamir Secret Sharing demo</title>
</head>
<body>
<script src="wasm_exec.js"></script>
<script>
function throwError(e) {
console.error(e);
}

function hide(message, sharesCount, threshold) {
let rawshares = GoSSS.hide(message, sharesCount, threshold);
return JSON.parse(rawshares);
}

function recover(shares) {
let message = GoSSS.recover(JSON.stringify(shares));
return message;
}

(async function() {
const go = new Go();
const result = await WebAssembly.instantiateStreaming(fetch("gosss.wasm"), go.importObject);
go.run(result.instance);
})()
</script>
</body>
</html>

<!DOCTYPE html>
<html data-theme="light">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!-- Title and favicon (SVG emoji)-->
<title>Shamir Secret Sharing Demo</title>
<link rel="icon"
href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>🔐</text></svg>">
<!-- Siimple.css assets -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/siimple/siimple.css" />
</head>
<body>
<!-- App slot -->
<div id="app"></div>
<!-- Import Go WebAssembly engine -->
<script src="wasm_exec.js"></script>
<!-- Import app -->
<script type="module" src="app.js"></script>
</body>
</html>

0 comments on commit 29828b6

Please sign in to comment.