allow adding a raw key

This commit is contained in:
Mikaël Cluseau
2026-03-16 11:08:16 +01:00
parent 06a87a6d07
commit 1ad9785d07
7 changed files with 56 additions and 11 deletions

View File

@@ -24,6 +24,7 @@ type State struct {
Store struct { Store struct {
DownloadToken string DownloadToken string
KeyNames []string KeyNames []string
Salt []byte
} }
Clusters []ClusterState Clusters []ClusterState
@@ -157,6 +158,7 @@ func updateState() {
wState.Change(func(v *State) { wState.Change(func(v *State) {
v.HasConfig = true v.HasConfig = true
v.Store.KeyNames = keyNames v.Store.KeyNames = keyNames
v.Store.Salt = secStore.Salt[:]
v.Clusters = clusters v.Clusters = clusters
v.Hosts = hosts v.Hosts = hosts
v.HostTemplates = hostTemplates v.HostTemplates = hostTemplates

View File

@@ -8,8 +8,13 @@ import (
"novit.tech/direktil/local-server/secretstore" "novit.tech/direktil/local-server/secretstore"
) )
type AddKeyReq struct {
NamedPassphrase `json:",inline"`
Hash []byte `json:",omitempty"`
}
func wsStoreAddKey(req *restful.Request, resp *restful.Response) { func wsStoreAddKey(req *restful.Request, resp *restful.Response) {
np := NamedPassphrase{} np := AddKeyReq{}
err := req.ReadEntity(&np) err := req.ReadEntity(&np)
if err != nil { if err != nil {
@@ -24,8 +29,13 @@ func wsStoreAddKey(req *restful.Request, resp *restful.Response) {
return return
} }
if len(np.Passphrase) == 0 { if len(np.Hash) == 0 && len(np.Passphrase) == 0 {
wsBadRequest(resp, "no passphrase given") wsBadRequest(resp, "no hash or passphrase given")
return
}
if len(np.Hash) != 0 && len(np.Hash) != 32 {
wsBadRequest(resp, "hash of a wrong length")
return return
} }
@@ -36,7 +46,14 @@ func wsStoreAddKey(req *restful.Request, resp *restful.Response) {
} }
} }
if len(np.Hash) != 0 {
hash := [32]byte{}
copy(hash[:], np.Hash[:32])
secStore.AddRawKey(np.Name, hash)
} else {
secStore.AddKey(np.Name, np.Passphrase) secStore.AddKey(np.Name, np.Passphrase)
}
defer updateState() defer updateState()
err = secStore.SaveTo(secKeysStorePath()) err = secStore.SaveTo(secKeysStorePath())

View File

@@ -103,7 +103,12 @@ createApp({
return {Name: this.forms.store.name, Passphrase: btoa(this.forms.store.pass1)} return {Name: this.forms.store.name, Passphrase: btoa(this.forms.store.pass1)}
}, },
storeAddKey() { storeAddKey() {
this.apiPost('/store/add-key', this.namedPassphrase(), (v) => { const params = this.namedPassphrase();
if (this.forms.store.byHash) {
params.Hash = this.forms.store.hash;
}
this.apiPost('/store/add-key', params, (v) => {
this.forms.store = {} this.forms.store = {}
}) })
}, },

View File

@@ -10,7 +10,7 @@
<style>@import url('/ui/style.css');@import url('/ui/app.css');</style> <style>@import url('/ui/style.css');@import url('/ui/app.css');</style>
<script src="/ui/jsonpatch.min-942279a1c4209cc2.js" integrity="sha384-GcPrkRS12jtrElEkbJcrZ8asvvb9s3mc+CUq9UW/8bL4L0bpkmh9M+oFnWN6qLq2"></script> <script src="/ui/jsonpatch.min-942279a1c4209cc2.js" integrity="sha384-GcPrkRS12jtrElEkbJcrZ8asvvb9s3mc+CUq9UW/8bL4L0bpkmh9M+oFnWN6qLq2"></script>
<script src="/ui/app-7f4645e84eaae7ce.js" defer integrity="sha384-31uiQnXVSPLDq61o+chfyKRkSdkmzv023M7KafOo+0xRw5AM70BUFyYO6yo0kPiZ" type="module"></script> <script src="/ui/app-e7fb26679b9aa0f2.js" defer integrity="sha384-4oRQalb7IIBcqQzfDkeCj53qYOP6dLsTwqcjnm3EiBa92oNDD3chUw38W2gEC+3p" type="module"></script>
<script src="/ui/Downloads-c9374f19f52c46d.js" integrity="sha384-WQIkCSxlUkvu4jFIWwS3bESMaazGwwnBn9dyvB7nItz3L8BmusRMsJACzfJa7sC4"></script> <script src="/ui/Downloads-c9374f19f52c46d.js" integrity="sha384-WQIkCSxlUkvu4jFIWwS3bESMaazGwwnBn9dyvB7nItz3L8BmusRMsJACzfJa7sC4"></script>
<script src="/ui/GetCopy-7e3c9678f9647d40.js" integrity="sha384-LzxUXylxE/t25HyTch8y2qvKcehvP2nqCo37swIBGEKZZUzHVJVQrS5UJDWNskTA"></script> <script src="/ui/GetCopy-7e3c9678f9647d40.js" integrity="sha384-LzxUXylxE/t25HyTch8y2qvKcehvP2nqCo37swIBGEKZZUzHVJVQrS5UJDWNskTA"></script>
<script src="/ui/Cluster-703dcdca97841304.js" integrity="sha384-ifGpq/GB1nDfqczm5clTRtcfnc9UMkT+SptMyS3UG9Di3xoKORtOGGjmK5RnJx+v"></script> <script src="/ui/Cluster-703dcdca97841304.js" integrity="sha384-ifGpq/GB1nDfqczm5clTRtcfnc9UMkT+SptMyS3UG9Di3xoKORtOGGjmK5RnJx+v"></script>
@@ -106,9 +106,15 @@
<form @submit="storeAddKey" action="/store/add-key"> <form @submit="storeAddKey" action="/store/add-key">
<p>Add an unlock phrase:</p> <p>Add an unlock phrase:</p>
<input type="text" v-model="forms.store.name" name="name" required placeholder="Name" /><br/> <input type="text" v-model="forms.store.name" name="name" required placeholder="Name" /><br/>
<label><input type="checkbox" v-model="forms.store.byHash"> {{ forms.store.byHash ? "hash (base64)" : "passphrase"}}</label>
<div v-if="forms.store.byHash">
<input type="hash" v-model="forms.store.hash" name="passphrase" required placeholder="Hash" />
{{" "}}generate with <code>dls hash '{{state.Store.Salt}}'</code>
</div><div v-else>
<input type="password" v-model="forms.store.pass1" name="passphrase" autocomplete="new-password" required placeholder="Phrase" /> <input type="password" v-model="forms.store.pass1" name="passphrase" autocomplete="new-password" required placeholder="Phrase" />
<input type="password" v-model="forms.store.pass2" autocomplete="new-password" required placeholder="Phrase confirmation" /> <input type="password" v-model="forms.store.pass2" autocomplete="new-password" required placeholder="Phrase confirmation" />
<input type="submit" value="add unlock phrase" :disabled="!forms.store.pass1 || forms.store.pass1 != forms.store.pass2" /> </div>
<input type="submit" value="add unlock phrase" :disabled="!((forms.store.pass1 && forms.store.pass1 == forms.store.pass2) || forms.store.hash)" />
</form> </form>
<form @submit="storeDelKey" action="/store/delete-key"> <form @submit="storeDelKey" action="/store/delete-key">
<p>Remove an unlock phrase:</p> <p>Remove an unlock phrase:</p>

View File

@@ -211,10 +211,13 @@ func (s *Store) Unlock(passphrase []byte) (ok bool) {
} }
func (s *Store) AddKey(name string, passphrase []byte) { func (s *Store) AddKey(name string, passphrase []byte) {
key, hash := s.keyPairFromPassword(passphrase) key, _ := s.keyPairFromPassword(passphrase)
memzero(passphrase) memzero(passphrase)
s.AddRawKey(name, key)
}
defer memzero(key[:]) func (s *Store) AddRawKey(name string, key [32]byte) {
hash := sha512.Sum512(key[:])
k := KeyEntry{Name: name, Hash: hash} k := KeyEntry{Name: name, Hash: hash}
@@ -222,6 +225,7 @@ func (s *Store) AddKey(name string, passphrase []byte) {
copy(k.EncKey[:], encKey) copy(k.EncKey[:], encKey)
s.Keys = append(s.Keys, k) s.Keys = append(s.Keys, k)
memzero(key[:])
} }
func (s *Store) keyPairFromPassword(password []byte) (key [32]byte, hash [64]byte) { func (s *Store) keyPairFromPassword(password []byte) (key [32]byte, hash [64]byte) {

View File

@@ -106,9 +106,15 @@
<form @submit="storeAddKey" action="/store/add-key"> <form @submit="storeAddKey" action="/store/add-key">
<p>Add an unlock phrase:</p> <p>Add an unlock phrase:</p>
<input type="text" v-model="forms.store.name" name="name" required placeholder="Name" /><br/> <input type="text" v-model="forms.store.name" name="name" required placeholder="Name" /><br/>
<label><input type="checkbox" v-model="forms.store.byHash"> {{ forms.store.byHash ? "hash (base64)" : "passphrase"}}</label>
<div v-if="forms.store.byHash">
<input type="hash" v-model="forms.store.hash" name="passphrase" required placeholder="Hash" />
{{" "}}generate with <code>dls hash '{{state.Store.Salt}}'</code>
</div><div v-else>
<input type="password" v-model="forms.store.pass1" name="passphrase" autocomplete="new-password" required placeholder="Phrase" /> <input type="password" v-model="forms.store.pass1" name="passphrase" autocomplete="new-password" required placeholder="Phrase" />
<input type="password" v-model="forms.store.pass2" autocomplete="new-password" required placeholder="Phrase confirmation" /> <input type="password" v-model="forms.store.pass2" autocomplete="new-password" required placeholder="Phrase confirmation" />
<input type="submit" value="add unlock phrase" :disabled="!forms.store.pass1 || forms.store.pass1 != forms.store.pass2" /> </div>
<input type="submit" value="add unlock phrase" :disabled="!((forms.store.pass1 && forms.store.pass1 == forms.store.pass2) || forms.store.hash)" />
</form> </form>
<form @submit="storeDelKey" action="/store/delete-key"> <form @submit="storeDelKey" action="/store/delete-key">
<p>Remove an unlock phrase:</p> <p>Remove an unlock phrase:</p>

View File

@@ -103,7 +103,12 @@ createApp({
return {Name: this.forms.store.name, Passphrase: btoa(this.forms.store.pass1)} return {Name: this.forms.store.name, Passphrase: btoa(this.forms.store.pass1)}
}, },
storeAddKey() { storeAddKey() {
this.apiPost('/store/add-key', this.namedPassphrase(), (v) => { const params = this.namedPassphrase();
if (this.forms.store.byHash) {
params.Hash = this.forms.store.hash;
}
this.apiPost('/store/add-key', params, (v) => {
this.forms.store = {} this.forms.store = {}
}) })
}, },