Compare commits

...

3 Commits

Author SHA1 Message Date
Zorchenhimer 268184dcac
Add some stuff to the todo 2024-08-05 22:54:45 -04:00
Zorchenhimer e37e5e2875
Search subkeys
Search subkeys when looking for KeyIds.  Do this for both public and
private keys.

The KeyIds are indexd in a map when keys are loaded to make finding them
easier.  Subkeys are not stored directly in the map; the whole parent
entity is stored instead which includes the subkey.
2024-08-05 22:24:56 -04:00
Zorchenhimer a18706f94a
Added todo.md 2024-08-05 22:23:29 -04:00
2 changed files with 136 additions and 16 deletions

113
server.go
View File

@ -19,24 +19,82 @@ type Server struct {
PublicKeys openpgp.EntityList PublicKeys openpgp.EntityList
PrivateKeys openpgp.EntityList PrivateKeys openpgp.EntityList
KeyMap map[string]*openpgp.Entity
//PubkeyGroups []*openpgp.Entity //PubkeyGroups []*openpgp.Entity
} }
func NewServer(publicKeys, privateKeys []string) (*Server, error) { func NewServer(publicKeys, privateKeys []string) (*Server, error) {
pub, err := loadKeysFromFiles(publicKeys) s := &Server{KeyMap: make(map[string]*openpgp.Entity)}
pub, err := s.loadKeysFromFiles(publicKeys)
if err != nil { if err != nil {
return nil, err return nil, err
} }
priv, err := loadKeysFromFiles(privateKeys) s.PublicKeys = pub
fmt.Println("Public keys:")
for _, k := range pub {
for name, _ := range k.Identities {
fmt.Println("\t"+name)
}
fmt.Printf("\t%X\n", k.PrimaryKey.KeyId)
if len(k.Subkeys) == 0 {
continue
}
fmt.Println("\tSubkeys:")
for _, sub := range k.Subkeys {
str := []string{}
if sub.PublicKey != nil {
str = append(str, fmt.Sprintf("public: %X", sub.PublicKey.KeyId))
}
if sub.PrivateKey != nil {
str = append(str, fmt.Sprintf("private: %X", sub.PrivateKey.KeyId))
}
fmt.Println("\t\t"+strings.Join(str, " / "))
}
}
priv, err := s.loadKeysFromFiles(privateKeys)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &Server{PublicKeys: pub, PrivateKeys: priv}, nil s.PrivateKeys = priv
fmt.Println("Private keys:")
for _, k := range priv {
for name, _ := range k.Identities {
fmt.Println("\t"+name)
}
fmt.Printf("\t%X\n", k.PrimaryKey.KeyId)
if len(k.Subkeys) == 0 {
continue
}
fmt.Println("\tSubkeys:")
for _, sub := range k.Subkeys {
str := []string{}
if sub.PublicKey != nil {
str = append(str, fmt.Sprintf("public: %X", sub.PublicKey.KeyId))
}
if sub.PrivateKey != nil {
str = append(str, fmt.Sprintf("private: %X", sub.PrivateKey.KeyId))
}
fmt.Println("\t\t"+strings.Join(str, " / "))
}
}
return s, nil
} }
func loadKeysFromFiles(filenames []string) (openpgp.EntityList, error) { func (s *Server) loadKeysFromFiles(filenames []string) (openpgp.EntityList, error) {
var keyring openpgp.EntityList var keyring openpgp.EntityList
for _, f := range filenames { for _, f := range filenames {
@ -52,7 +110,20 @@ func loadKeysFromFiles(filenames []string) (openpgp.EntityList, error) {
} }
file.Close() file.Close()
keyring = append(keyring, keys...) for _, key := range keys {
keyring = append(keyring, key)
s.KeyMap[fmt.Sprintf("%x", key.PrimaryKey.KeyId)] = key
for _, sub := range key.Subkeys {
if sub.PublicKey != nil {
s.KeyMap[fmt.Sprintf("%x", sub.PublicKey.KeyId)] = key
}
if sub.PrivateKey != nil {
s.KeyMap[fmt.Sprintf("%x", sub.PrivateKey.KeyId)] = key
}
}
}
} }
return keyring, nil return keyring, nil
@ -71,7 +142,7 @@ func (s *Server) handler_publickey(w http.ResponseWriter, r *http.Request) {
key := s.findPubKey(search) key := s.findPubKey(search)
if key == nil { if key == nil {
http.Error(w, "Key not found", 500) http.Error(w, fmt.Sprintf("Key not found; searched for %q", search), 500)
return return
} }
@ -82,6 +153,14 @@ func (s *Server) handler_publickey(w http.ResponseWriter, r *http.Request) {
} }
func (s *Server) findPubKey(search string) *openpgp.Entity { func (s *Server) findPubKey(search string) *openpgp.Entity {
if k, found := s.KeyMap[search]; found {
for _, sub := range k.Subkeys {
if sub.PublicKey != nil && fmt.Sprintf("%x", sub.PublicKey.KeyId) == search {
return k
}
}
}
for _, k := range s.PublicKeys { for _, k := range s.PublicKeys {
for _, id := range k.Identities { for _, id := range k.Identities {
if strings.ToLower(id.UserId.Name) == search { if strings.ToLower(id.UserId.Name) == search {
@ -121,6 +200,18 @@ func (s *Server) handler_privatekey(w http.ResponseWriter, r *http.Request) {
} }
func (s *Server) findPrivKey(search string) *openpgp.Entity { func (s *Server) findPrivKey(search string) *openpgp.Entity {
if k, found := s.KeyMap[search]; found {
if k.PrivateKey != nil && fmt.Sprintf("%x", k.PrivateKey.KeyId) == search {
return k
}
for _, sub := range k.Subkeys {
if sub.PrivateKey != nil && fmt.Sprintf("%x", sub.PrivateKey.KeyId) == search {
return k
}
}
}
for _, k := range s.PrivateKeys { for _, k := range s.PrivateKeys {
fmt.Printf("%X\n", k.PrimaryKey.KeyId) fmt.Printf("%X\n", k.PrimaryKey.KeyId)
for _, id := range k.Identities { for _, id := range k.Identities {
@ -132,16 +223,6 @@ func (s *Server) findPrivKey(search string) *openpgp.Entity {
return k return k
} }
} }
for _, sub := range k.Subkeys {
fmt.Printf("%#v\n", sub)
if sub.PrivateKey == nil {
continue
}
if fmt.Sprintf("%x", sub.PrivateKey.KeyId) == search {
return k
}
}
} }
return nil return nil

39
todo.md Normal file
View File

@ -0,0 +1,39 @@
# TODO
There's lots of things to add.
- TLS
- Autorenew this with an ACME server (eg, Boulder; use the Lego library)
- self-host the Boulder server & add the main CA pubkey to the client
- Work with self-signed stuff for now tho
- Authentication
- Give each client an API key
- Limit decryption keys that client can access?
- Admin UI on the server
- Manage client accounts
- Import public keys
- Add/Remove keys
- Private Key autorotation
- PGP Public Key Server
- a la keys.openpgp.org
- two servers? one public (company pub keys), one internal (customer pub
keys)
- Encryption groups
- Add a number of public keys to a group. When the client encrypts to a
group, use all the keys.
- Auto-remove expired keys from groups
## Technical TODO
More specific stuff
- Wrap keys in Armor when sending over the wire
- Figure out sending multiple keys in the same request. Wrap in json? Can
Armor handle multiple keys? Can I just concatinate multiple Armored keys?
- Auto-generate self-signed certs for testing TLS
- Password protect private keys?
- Where would this password be stored?
- Keys would ultimately need to be stored on disk *somewhere*, and they can't
be unprotected there.
- Reorganize code to split client and server and a common lib
- Look at KMIP. Do I want to implement this?