6
1
mirror of https://git.mills.io/saltyim/saltyim.git synced 2024-06-30 18:51:03 +00:00
prologic-saltyim/options.go
James Mills ddd16c202f Add blob service and support for signing and verifying HTTP requests (#178)
Alternative to #177

The way this works is:

Client:

- Client creates a normal `net/http.Request{}` object using the `Request()` function in `utils.go`. The `http.Request{}` object is then signed using the Client's Ed25519 private key.
- The HTTP Method and Path (_note this is important_) are hashed, as well as the request body (if any) using the FNV128a hashing algorithm.
- This hash is then signed by the Client's's Ed25519 private key.
- The resulting signature is then encoded to Base64 (_standard encoding_) and added to the HTTP headers as a `Signature:` header.
- In addition the Client's Ed25519 public key is added to the HTTP headers as `Signer:`

Server:

- The server calculates the same FNV128a hash of the HTTP Request Method and Path and the body (if any)
- The server decodes the HTTP header `Signature:`
- The server then uses the Client's Ed25519 public key in the HTTP header `Signer:` to verify the signature of the `Signature:` HTTP header which gives us back the original FNV128a hash the Client calculated for the request.
- The server then compares the Client's hash with the expected hash to see if they compare equally.

Co-authored-by: James Mills <1290234+prologic@users.noreply.github.com>
Co-authored-by: Jon Lundy <jon@xuu.cc>
Reviewed-on: https://git.mills.io/saltyim/saltyim/pulls/178
Reviewed-by: xuu <xuu@noreply@mills.io>
2023-01-25 23:05:29 +00:00

99 lines
2.0 KiB
Go

package saltyim
import (
"bytes"
"fmt"
"os"
log "github.com/sirupsen/logrus"
)
// WithClientIdentity sets the client's identity
func WithClientIdentity(options ...IdentityOption) ClientOption {
return func(cli *Client) error {
id, err := GetIdentity(options...)
if err != nil {
return fmt.Errorf("error loading identity: %w", err)
}
cli.id = id
return nil
}
}
// WithStateFromFile sets the client's state from a file on disk
func WithStateFromFile(fn string) ClientOption {
return func(cli *Client) error {
f, err := os.Open(fn)
if err != nil {
log.WithError(err).Warnf("error opening state file %s, creating an empty state", fn)
cli.state = NewState()
return nil
}
defer f.Close()
s, err := LoadState(f)
if err != nil {
return fmt.Errorf("error loading state: %w", err)
}
cli.state = s
return nil
}
}
// WithStateFromBytes sets the client's state from a byte array
func WithStateFromBytes(data []byte) ClientOption {
return func(cli *Client) error {
s, err := LoadState(bytes.NewBuffer(data))
if err != nil {
return fmt.Errorf("error loading state: %w", err)
}
cli.state = s
return nil
}
}
// WithIdentity sets the client's identity from an identity object
func WithIdentity(id *Identity) ClientOption {
return func(cli *Client) error {
cli.id = id
return nil
}
}
// WithState sets the client's state from a state object
func WithState(state *State) ClientOption {
return func(cli *Client) error {
cli.state = state
return nil
}
}
// WithAddr sets the client's `me` Salty Address
func WithAddr(me *Addr) ClientOption {
return func(cli *Client) error {
cli.me = me
return nil
}
}
// WithUser sets the client's `me` Salty Address if a non-nil or non-empty and valid
// Salty Address for `user` is supplifed, otherwise the user in the client's identity
// is used.
func WithUser(user string) ClientOption {
return func(cli *Client) error {
if user == "" {
return nil
}
addr, err := ParseAddr(user)
if err != nil {
return err
}
cli.me = addr
return nil
}
}