From 93a61a8f299292c9045ed7d714572b8975407bce Mon Sep 17 00:00:00 2001 From: DevMiner Date: Thu, 15 Aug 2024 19:22:17 +0200 Subject: [PATCH] refactor!: WD-4 signatures --- .../val_impls/request_validator_impl.go | 10 +- pkg/lysand/actor_user.go | 17 +-- pkg/lysand/crypto.go | 37 +------ pkg/lysand/crypto_test.go | 103 ++++-------------- pkg/lysand/federation_headers.go | 54 +++++++++ pkg/lysand/federation_headers_test.go | 18 +++ pkg/lysand/signature.go | 70 ------------ pkg/lysand/signature_data.go | 64 +++++++++++ pkg/lysand/signature_header.go | 66 ----------- pkg/lysand/signature_header_test.go | 41 ------- .../{public_key.go => spki_public_key.go} | 0 ...ic_key_test.go => spki_public_key_test.go} | 0 12 files changed, 175 insertions(+), 305 deletions(-) create mode 100644 pkg/lysand/federation_headers.go create mode 100644 pkg/lysand/federation_headers_test.go delete mode 100644 pkg/lysand/signature.go create mode 100644 pkg/lysand/signature_data.go delete mode 100644 pkg/lysand/signature_header.go delete mode 100644 pkg/lysand/signature_header_test.go rename pkg/lysand/{public_key.go => spki_public_key.go} (100%) rename pkg/lysand/{public_key_test.go => spki_public_key_test.go} (100%) diff --git a/internal/validators/val_impls/request_validator_impl.go b/internal/validators/val_impls/request_validator_impl.go index 17815dd..3e9e907 100644 --- a/internal/validators/val_impls/request_validator_impl.go +++ b/internal/validators/val_impls/request_validator_impl.go @@ -44,13 +44,13 @@ func (i RequestValidatorImpl) Validate(ctx context.Context, r *http.Request) err r = r.WithContext(ctx) - date, sigHeader, err := lysand.ExtractFederationHeaders(r.Header) + fedHeaders, err := lysand.ExtractFederationHeaders(r.Header) if err != nil { return err } // TODO: Fetch user from database instead of using the URI - user, err := i.repositories.Users().Resolve(ctx, lysand.URLFromStd(sigHeader.KeyID)) + user, err := i.repositories.Users().Resolve(ctx, lysand.URLFromStd(fedHeaders.SignedBy)) if err != nil { return err } @@ -60,13 +60,13 @@ func (i RequestValidatorImpl) Validate(ctx context.Context, r *http.Request) err return err } - if !(lysand.Verifier{PublicKey: user.PublicKey}).Verify(r.Method, date, r.Host, r.URL.Path, body, sigHeader) { - i.log.Info("signature verification failed", "user", user.URI, "ur", r.URL.Path) + if !(lysand.Verifier{PublicKey: user.PublicKey}).Verify(r.Method, r.URL, body, fedHeaders) { + i.log.Info("signature verification failed", "user", user.URI, "url", r.URL.Path) s.CaptureError(ErrInvalidSignature) return ErrInvalidSignature } else { - i.log.V(2).Info("signature verification succeeded", "user", user.URI, "ur", r.URL.Path) + i.log.V(2).Info("signature verification succeeded", "user", user.URI, "url", r.URL.Path) } return nil diff --git a/pkg/lysand/actor_user.go b/pkg/lysand/actor_user.go index 78adf31..ecf4e4c 100644 --- a/pkg/lysand/actor_user.go +++ b/pkg/lysand/actor_user.go @@ -4,11 +4,12 @@ import ( "bytes" "context" "crypto/ed25519" + "crypto/rand" + "encoding/base64" "encoding/json" "fmt" "net/http" "net/url" - "time" ) // User represents a user object in the Lysand protocol. For more information, see the [Spec]. @@ -105,13 +106,13 @@ func (c *FederationClient) GetUser(ctx context.Context, uri *url.URL) (*User, er return nil, err } - date, sigHeader, err := ExtractFederationHeaders(resp.Header) + fedHeaders, err := ExtractFederationHeaders(resp.Header) if err != nil { return nil, err } v := Verifier{ed25519.PublicKey(user.PublicKey.PublicKey)} - if !v.Verify("GET", date, uri.Host, uri.Path, body, sigHeader) { + if !v.Verify("GET", uri, body, fedHeaders) { c.log.V(2).Info("signature verification failed", "user", user.URI.String()) return nil, fmt.Errorf("signature verification failed") } @@ -128,9 +129,12 @@ func (c *FederationClient) SendToInbox(ctx context.Context, signer Signer, user return nil, err } - date := time.Now() + nonce := make([]byte, 32) + if _, err := rand.Read(nonce); err != nil { + return nil, err + } - sigData := NewSignatureData("POST", date, uri.Host, uri.Path, hashSHA256(body)) + sigData := NewSignatureData("POST", base64.StdEncoding.EncodeToString(nonce), uri, hashSHA256(body)) sig := signer.Sign(*sigData) req, err := http.NewRequestWithContext(ctx, "POST", uri.String(), bytes.NewReader(body)) @@ -138,8 +142,7 @@ func (c *FederationClient) SendToInbox(ctx context.Context, signer Signer, user return nil, err } - req.Header.Set("Date", TimeFromStd(date).String()) - req.Header.Set("Signature", sig.String()) + sig.Inject(req.Header) _, respBody, err := c.doReq(req) if err != nil { diff --git a/pkg/lysand/crypto.go b/pkg/lysand/crypto.go index 5dca960..406adbe 100644 --- a/pkg/lysand/crypto.go +++ b/pkg/lysand/crypto.go @@ -4,21 +4,18 @@ import ( "bytes" "crypto/ed25519" "crypto/sha256" - "fmt" "io" - "log" "net/http" - "time" ) func (c *FederationClient) ValidateSignatureHeader(req *http.Request) (bool, error) { - date, sigHeader, err := ExtractFederationHeaders(req.Header) + fedHeaders, err := ExtractFederationHeaders(req.Header) if err != nil { return false, err } // TODO: Fetch user from database instead of using the URI - user, err := c.GetUser(req.Context(), sigHeader.KeyID) + user, err := c.GetUser(req.Context(), fedHeaders.SignedBy) if err != nil { return false, err } @@ -29,39 +26,11 @@ func (c *FederationClient) ValidateSignatureHeader(req *http.Request) (bool, err } v := Verifier{ed25519.PublicKey(user.PublicKey.PublicKey)} - valid := v.Verify(req.Method, date, req.Host, req.URL.Path, body, sigHeader) + valid := v.Verify(req.Method, req.URL, body, fedHeaders) return valid, nil } -func ExtractFederationHeaders(h http.Header) (time.Time, *SignatureHeader, error) { - gotDates := h.Values("date") - var date *Time - for i, raw := range gotDates { - if parsed, err := ParseTime(raw); err != nil { - log.Printf("invalid date[%d] header: %s", i, raw) - continue - } else { - date = &parsed - break - } - } - if date == nil { - return time.Time{}, nil, fmt.Errorf("missing date header") - } - - gotSignature := h.Get("signature") - if gotSignature == "" { - return date.ToStd(), nil, fmt.Errorf("missing signature header") - } - sigHeader, err := ParseSignatureHeader(gotSignature) - if err != nil { - return date.ToStd(), nil, err - } - - return date.ToStd(), sigHeader, nil -} - func hashSHA256(data []byte) []byte { h := sha256.New() h.Write(data) diff --git a/pkg/lysand/crypto_test.go b/pkg/lysand/crypto_test.go index 09cba1f..0ebb5ce 100644 --- a/pkg/lysand/crypto_test.go +++ b/pkg/lysand/crypto_test.go @@ -4,97 +4,36 @@ import ( "crypto/ed25519" "crypto/x509" "encoding/base64" + "github.com/stretchr/testify/assert" "net/url" "testing" - "time" - - "github.com/stretchr/testify/assert" ) func TestFederationClient_ValidateSignatureHeader(t *testing.T) { var ( + bobURL = &url.URL{Scheme: "https", Host: "bob.com"} + bobPrivBytes = must(base64.StdEncoding.DecodeString, "MC4CAQAwBQYDK2VwBCIEINOATgmaya61Ha9OEE+DD3RnOEqDaHyQ3yLf5upwskUU") - bobPubBytes = must(base64.StdEncoding.DecodeString, "MCowBQYDK2VwAyEAQ08Z/FJ5f16o8mthLaFZMo4ssn0fJ7c+bipNYm3kId4=") + bobPriv = must(x509.ParsePKCS8PrivateKey, bobPrivBytes).(ed25519.PrivateKey) + signer = Signer{PrivateKey: bobPriv, UserURL: bobURL} + + bobPubBytes = must(base64.StdEncoding.DecodeString, "MCowBQYDK2VwAyEAQ08Z/FJ5f16o8mthLaFZMo4ssn0fJ7c+bipNYm3kId4=") + bobPub = must(x509.ParsePKIXPublicKey, bobPubBytes).(ed25519.PublicKey) + verifier = Verifier{PublicKey: bobPub} + + method = "POST" + nonce = "myrandomnonce" + u = &url.URL{Scheme: "https", Host: "bob.com", Path: "/a/b/c", RawQuery: "z=foo&a=bar"} + body = []byte("hello") ) - bobPub := must(x509.ParsePKIXPublicKey, bobPubBytes).(ed25519.PublicKey) - bobPriv := must(x509.ParsePKCS8PrivateKey, bobPrivBytes).(ed25519.PrivateKey) + toSign := NewSignatureData(method, nonce, u, hashSHA256(body)) + assert.Equal(t, `post /a/b/c?z=foo&a=bar myrandomnonce LPJNul+wow4m6DsqxbninhsWHlwfp0JecwQzYpOLmCQ=`, toSign.String()) - date := time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC) - body := []byte("hello") + signed := signer.Sign(*toSign) + assert.Equal(t, true, verifier.Verify(method, u, body, signed), "signature verification failed") - sigData := NewSignatureData("POST", date, "example2.com", "/users/bob", hashSHA256(body)) - - sig := Signer{PrivateKey: bobPriv, UserURL: &url.URL{Scheme: "https", Host: "example.com", Path: "/users/bob"}}. - Sign(*sigData) - - t.Run("validate against itself", func(t *testing.T) { - v := Verifier{ - PublicKey: bobPub, - } - - if !v.Verify("POST", date, "example2.com", "/users/bob", body, sig) { - t.Error("signature verification failed") - } - }) - - t.Run("validate against @lysand/api JS implementation", func(t *testing.T) { - expectedSignedString := `(request-target): post /users/bob -host: example2.com -date: 1970-01-01T00:00:00.000Z -digest: SHA-256=LPJNul+wow4m6DsqxbninhsWHlwfp0JecwQzYpOLmCQ= -` - assert.Equal(t, expectedSignedString, sigData.String()) - - expectedSignatureHeader := `keyId="https://example.com/users/bob",algorithm="ed25519",headers="(request-target) host date digest",signature="PbVicu1spnATYUznWn6N5ebNUC+w94U9k6y4dncLsr6hNfUD8CLInbUSkgR3AZrCWEZ+Md2+Lch70ofiSqXgAQ=="` - assert.Equal(t, expectedSignatureHeader, sig.String()) - }) -} - -func TestSignatureInterop(t *testing.T) { - var ( - bobPubBytes = must(base64.StdEncoding.DecodeString, "MCowBQYDK2VwAyEAgKNt+9eyOXdb7MSrrmHlsFD2H9NGwC+56PjpWD46Tcs=") - bobPrivBytes = must(base64.StdEncoding.DecodeString, "MC4CAQAwBQYDK2VwBCIEII+nkwT3nXwBp9FEE0q95RBBfikf6UTzPzdH2yrtIvL1") - ) - - bobPub := must(x509.ParsePKIXPublicKey, bobPubBytes).(ed25519.PublicKey) - bobPriv := must(x509.ParsePKCS8PrivateKey, bobPrivBytes).(ed25519.PrivateKey) - - signedString := `(request-target): post /api/users/ec042557-8c30-492d-87d6-9e6495993072/inbox -host: lysand-test.i.devminer.xyz -date: 2024-07-25T21:03:24.866Z -digest: SHA-256=mPN5WKMoC4k3zor6FPTJUhDQ1JKX6zqA2QfEGh3omuc= -` - method := "POST" - dateHeader := "2024-07-25T21:03:24.866Z" - date := must(ParseTime, dateHeader) - host := "lysand-test.i.devminer.xyz" - path := "/api/users/ec042557-8c30-492d-87d6-9e6495993072/inbox" - body := []byte(`{"type":"Follow","id":"2265b3b2-a176-4b20-8fcf-ac82cf2efd7d","author":"https://lysand.i.devminer.xyz/users/0190d697-c83a-7376-8d15-0f77fd09e180","followee":"https://lysand-test.i.devminer.xyz/api/users/ec042557-8c30-492d-87d6-9e6495993072/","created_at":"2024-07-25T21:03:24.863Z","uri":"https://lysand.i.devminer.xyz/follows/2265b3b2-a176-4b20-8fcf-ac82cf2efd7d"}`) - signatureHeader := `keyId="https://lysand.i.devminer.xyz/users/0190d697-c83a-7376-8d15-0f77fd09e180",algorithm="ed25519",headers="(request-target) host date digest",signature="KUkKYexLk2hOfE+NVIacLDHSJP2QpX4xJGclHhQIM39ce2or7UJauRtCL8eWrhpSgQdVPk11bYhvvi8fdCruBw=="` - - sigData := NewSignatureData(method, date.ToStd(), host, path, hashSHA256(body)) - assert.Equal(t, signedString, sigData.String()) - - t.Run("signature header parsing", func(t *testing.T) { - parsedSignatureHeader, err := ParseSignatureHeader(signatureHeader) - if err != nil { - t.Error(err) - } - assert.Equal(t, "https://lysand.i.devminer.xyz/users/0190d697-c83a-7376-8d15-0f77fd09e180", parsedSignatureHeader.KeyID.String()) - assert.Equal(t, "ed25519", parsedSignatureHeader.Algorithm) - assert.Equal(t, "(request-target) host date digest", parsedSignatureHeader.Headers) - assert.Equal(t, sigData.Sign(bobPriv), parsedSignatureHeader.Signature) - - v := Verifier{PublicKey: bobPub} - if !v.Verify(method, date.ToStd(), host, path, body, parsedSignatureHeader) { - t.Error("signature verification failed") - } - }) - - t.Run("signature header generation", func(t *testing.T) { - sig := Signer{PrivateKey: bobPriv, UserURL: &url.URL{Scheme: "https", Host: "lysand.i.devminer.xyz", Path: "/users/0190d697-c83a-7376-8d15-0f77fd09e180"}}. - Sign(*sigData) - assert.Equal(t, signatureHeader, sig.String()) - }) + assert.Equal(t, "myrandomnonce", signed.Nonce) + assert.Equal(t, bobURL, signed.SignedBy) + assert.Equal(t, "datQHNaqJ1jeKzK3UeReUVf+B65JPq5P9LxfqUUJTMv3QNqDu5KawosKoduIRk4/D/A+EKjDhlcw0c7GzUlMCA==", base64.StdEncoding.EncodeToString(signed.Signature)) } diff --git a/pkg/lysand/federation_headers.go b/pkg/lysand/federation_headers.go new file mode 100644 index 0000000..d110bfd --- /dev/null +++ b/pkg/lysand/federation_headers.go @@ -0,0 +1,54 @@ +package lysand + +import ( + "encoding/base64" + "fmt" + "net/http" + "net/url" +) + +// FederationHeaders represents the signature header of the Lysand protocol. For more information, see the [Spec]. +// +// [Spec]: https://versia.pub/signatures#signature-definition +type FederationHeaders struct { + // SignedBy is the URL to a user + SignedBy *url.URL + // Nonce is a random string, used to prevent replay attacks + Nonce string + // Signature is the signature of the request + Signature []byte +} + +func (f *FederationHeaders) Inject(h http.Header) { + h.Set("x-signed-by", f.SignedBy.String()) + h.Set("x-nonce", f.Nonce) + h.Set("x-signature", base64.StdEncoding.EncodeToString(f.Signature)) +} + +func ExtractFederationHeaders(h http.Header) (*FederationHeaders, error) { + signedBy := h.Get("x-signed-by") + if signedBy == "" { + return nil, fmt.Errorf("missing x-signed-by header") + } + + u, err := url.Parse(signedBy) + if err != nil { + return nil, err + } + + nonce := h.Get("x-nonce") + if nonce == "" { + return nil, fmt.Errorf("missing x-nonce header") + } + + signature := h.Get("x-signature") + if signature == "" { + return nil, fmt.Errorf("missing x-signature header") + } + + return &FederationHeaders{ + SignedBy: u, + Nonce: nonce, + Signature: []byte(signature), + }, nil +} diff --git a/pkg/lysand/federation_headers_test.go b/pkg/lysand/federation_headers_test.go new file mode 100644 index 0000000..ce88401 --- /dev/null +++ b/pkg/lysand/federation_headers_test.go @@ -0,0 +1,18 @@ +package lysand + +import ( + "github.com/stretchr/testify/assert" + "net/url" + "testing" +) + +func TestFederationHeaders_String(t *testing.T) { + one := SignatureData{ + RequestMethod: "POST", + Nonce: "1234567890", + URL: &url.URL{Scheme: "https", Host: "bob.com", Path: "/users/bob", RawQuery: "z=foo&a=bar"}, + Digest: hashSHA256([]byte("hello")), + } + + assert.Equal(t, "post /users/bob?z=foo&a=bar 1234567890 LPJNul+wow4m6DsqxbninhsWHlwfp0JecwQzYpOLmCQ=", one.String()) +} diff --git a/pkg/lysand/signature.go b/pkg/lysand/signature.go deleted file mode 100644 index bc408d1..0000000 --- a/pkg/lysand/signature.go +++ /dev/null @@ -1,70 +0,0 @@ -package lysand - -import ( - "crypto/ed25519" - "encoding/base64" - "fmt" - "net/url" - "strings" - "time" -) - -type SignatureData struct { - RequestMethod string - Date time.Time - Host string - Path string - Digest []byte -} - -func NewSignatureData(method string, date time.Time, host, path string, digest []byte) *SignatureData { - return &SignatureData{ - RequestMethod: method, - Date: date, - Host: host, - Path: path, - Digest: digest, - } -} - -func (s *SignatureData) String() string { - return strings.Join([]string{ - fmt.Sprintf("(request-target): %s %s", strings.ToLower(s.RequestMethod), s.Path), - fmt.Sprintf("host: %s", s.Host), - fmt.Sprintf("date: %s", TimeFromStd(s.Date).String()), - fmt.Sprintf("digest: SHA-256=%s", base64.StdEncoding.EncodeToString(s.Digest)), - "", - }, "\n") -} - -func (s *SignatureData) Validate(pubKey ed25519.PublicKey, signature []byte) bool { - return ed25519.Verify(pubKey, []byte(s.String()), signature) -} - -func (s *SignatureData) Sign(privKey ed25519.PrivateKey) []byte { - return ed25519.Sign(privKey, []byte(s.String())) -} - -type Signer struct { - PrivateKey ed25519.PrivateKey - UserURL *url.URL -} - -func (s Signer) Sign(signatureData SignatureData) *SignatureHeader { - return &SignatureHeader{ - KeyID: s.UserURL, - Algorithm: "ed25519", - Headers: "(request-target) host date digest", - Signature: signatureData.Sign(s.PrivateKey), - } -} - -type Verifier struct { - PublicKey ed25519.PublicKey -} - -func (v Verifier) Verify(method string, date time.Time, host, path string, body []byte, sigHeader *SignatureHeader) bool { - sigData := NewSignatureData(method, date, host, path, hashSHA256(body)) - - return sigData.Validate(v.PublicKey, sigHeader.Signature) -} diff --git a/pkg/lysand/signature_data.go b/pkg/lysand/signature_data.go new file mode 100644 index 0000000..1839510 --- /dev/null +++ b/pkg/lysand/signature_data.go @@ -0,0 +1,64 @@ +package lysand + +import ( + "crypto/ed25519" + "encoding/base64" + "fmt" + "net/url" + "strings" +) + +type SignatureData struct { + // RequestMethod is the *lowercase* HTTP method of the request + RequestMethod string + // Nonce is a random byte array, used to prevent replay attacks + Nonce string + // RawPath is the path of the request, without the query string + URL *url.URL + // Digest is the SHA-256 hash of the request body + Digest []byte +} + +func NewSignatureData(method, nonce string, u *url.URL, digest []byte) *SignatureData { + return &SignatureData{ + RequestMethod: method, + Nonce: nonce, + URL: u, + Digest: digest, + } +} + +func (s *SignatureData) String() string { + return fmt.Sprintf("%s %s?%s %s %s", strings.ToLower(s.RequestMethod), s.URL.Path, s.URL.RawQuery, s.Nonce, base64.StdEncoding.EncodeToString(s.Digest)) +} + +func (s *SignatureData) Validate(pubKey ed25519.PublicKey, signature []byte) bool { + return ed25519.Verify(pubKey, []byte(s.String()), signature) +} + +func (s *SignatureData) Sign(privKey ed25519.PrivateKey) []byte { + return ed25519.Sign(privKey, []byte(s.String())) +} + +type Signer struct { + PrivateKey ed25519.PrivateKey + UserURL *url.URL +} + +func (s Signer) Sign(signatureData SignatureData) *FederationHeaders { + return &FederationHeaders{ + SignedBy: s.UserURL, + Nonce: signatureData.Nonce, + Signature: signatureData.Sign(s.PrivateKey), + } +} + +type Verifier struct { + PublicKey ed25519.PublicKey +} + +func (v Verifier) Verify(method string, u *url.URL, body []byte, fedHeaders *FederationHeaders) bool { + sigData := NewSignatureData(method, fedHeaders.Nonce, u, hashSHA256(body)) + + return sigData.Validate(v.PublicKey, fedHeaders.Signature) +} diff --git a/pkg/lysand/signature_header.go b/pkg/lysand/signature_header.go deleted file mode 100644 index 729203c..0000000 --- a/pkg/lysand/signature_header.go +++ /dev/null @@ -1,66 +0,0 @@ -package lysand - -import ( - "encoding/base64" - "errors" - "fmt" - "net/url" - "strings" -) - -var ( - ErrInvalidSignatureHeader = errors.New("invalid signature header") -) - -type SignatureHeader struct { - // URL to a user - KeyID *url.URL - Headers string - Algorithm string - Signature []byte -} - -func (s SignatureHeader) String() string { - return strings.Join([]string{ - fmt.Sprintf(`keyId="%s"`, s.KeyID.String()), - fmt.Sprintf(`algorithm="%s"`, s.Algorithm), - fmt.Sprintf(`headers="%s"`, s.Headers), - fmt.Sprintf(`signature="%s"`, base64.StdEncoding.EncodeToString(s.Signature)), - }, ",") -} - -// ParseSignatureHeader parses strings in the form of -// `keyId="",algorithm="ed25519",headers="(request-target) host date digest",signature=""` -func ParseSignatureHeader(raw string) (*SignatureHeader, error) { - parts := strings.Split(raw, ",") - if len(parts) != 4 { - return nil, ErrInvalidSignatureHeader - } - - sig := &SignatureHeader{} - - for _, part := range parts { - kv := strings.SplitN(part, "=", 2) - kv[1] = strings.TrimPrefix(kv[1], "\"") - kv[1] = strings.TrimSuffix(kv[1], "\"") - - var err error - - switch kv[0] { - case "keyId": - sig.KeyID, err = url.Parse(kv[1]) - case "algorithm": - sig.Algorithm = kv[1] - case "headers": - sig.Headers = kv[1] - case "signature": - sig.Signature, err = base64.StdEncoding.DecodeString(kv[1]) - } - - if err != nil { - return nil, err - } - } - - return sig, nil -} diff --git a/pkg/lysand/signature_header_test.go b/pkg/lysand/signature_header_test.go deleted file mode 100644 index a96d58e..0000000 --- a/pkg/lysand/signature_header_test.go +++ /dev/null @@ -1,41 +0,0 @@ -package lysand - -import ( - "encoding/base64" - "github.com/stretchr/testify/assert" - "testing" - "time" -) - -func TestParseSignatureHeader(t *testing.T) { - data := `keyId="https://example.com/users/bob",algorithm="ed25519",headers="(request-target) host date digest",signature="PbVicu1spnATYUznWn6N5ebNUC+w94U9k6y4dncLsr6hNfUD8CLInbUSkgR3AZrCWEZ+Md2+Lch70ofiSqXgAQ=="` - expectedSignature := must(base64.StdEncoding.DecodeString, "PbVicu1spnATYUznWn6N5ebNUC+w94U9k6y4dncLsr6hNfUD8CLInbUSkgR3AZrCWEZ+Md2+Lch70ofiSqXgAQ==") - - sig, err := ParseSignatureHeader(data) - if err != nil { - t.Error(err) - } - - assert.Equal(t, "https://example.com/users/bob", sig.KeyID.String()) - assert.Equal(t, "ed25519", sig.Algorithm) - assert.Equal(t, "(request-target) host date digest", sig.Headers) - assert.Equal(t, expectedSignature, sig.Signature) -} - -func TestSignatureHeader_String(t *testing.T) { - one := SignatureData{ - RequestMethod: "POST", - Date: time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC), - Host: "example2.com", - Path: "/users/bob", - Digest: hashSHA256([]byte("hello")), - } - - expected := `(request-target): post /users/bob -host: example2.com -date: 1970-01-01T00:00:00.000Z -digest: SHA-256=LPJNul+wow4m6DsqxbninhsWHlwfp0JecwQzYpOLmCQ= -` - - assert.Equal(t, expected, one.String()) -} diff --git a/pkg/lysand/public_key.go b/pkg/lysand/spki_public_key.go similarity index 100% rename from pkg/lysand/public_key.go rename to pkg/lysand/spki_public_key.go diff --git a/pkg/lysand/public_key_test.go b/pkg/lysand/spki_public_key_test.go similarity index 100% rename from pkg/lysand/public_key_test.go rename to pkg/lysand/spki_public_key_test.go