diff --git a/internal/annotators/base.go b/internal/annotators/base.go index 4076fb7..c2191fa 100644 --- a/internal/annotators/base.go +++ b/internal/annotators/base.go @@ -27,7 +27,7 @@ import ( "io/ioutil" ) -func deriveHash(hash contracts.HashType, data []byte) string { +func DeriveHash(hash contracts.HashType, data []byte) string { var h hashprovider.Provider switch hash { case contracts.MD5Hash: @@ -41,7 +41,7 @@ func deriveHash(hash contracts.HashType, data []byte) string { return h.Derive(data) } -func signAnnotation(key config.KeyInfo, a contracts.Annotation) (string, error) { +func SignAnnotation(key config.KeyInfo, a contracts.Annotation) (string, error) { var s signprovider.Provider switch key.Type { case contracts.KeyEd25519: @@ -64,7 +64,7 @@ func signAnnotation(key config.KeyInfo, a contracts.Annotation) (string, error) return signed, nil } -func verifySignature(key config.KeyInfo, src contracts.Annotation) (bool, error) { +func VerifySignature(key config.KeyInfo, src contracts.Annotation) (bool, error) { var s signprovider.Provider switch key.Type { case contracts.KeyEd25519: diff --git a/internal/annotators/base_test.go b/internal/annotators/base_test.go index c7739bd..4da70a6 100644 --- a/internal/annotators/base_test.go +++ b/internal/annotators/base_test.go @@ -45,7 +45,7 @@ func TestDeriveHash(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - result := deriveHash(tt.hashType, tt.input) + result := DeriveHash(tt.hashType, tt.input) assert.Equal(t, tt.output, result) }) } @@ -80,7 +80,7 @@ func TestSignAnnotation(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - result, err := signAnnotation(tt.cfg, tt.annotation) + result, err := SignAnnotation(tt.cfg, tt.annotation) test.CheckError(err, tt.expectError, tt.name, t) if err == nil { diff --git a/internal/annotators/http/constants.go b/internal/annotators/http/constants.go new file mode 100644 index 0000000..f280138 --- /dev/null +++ b/internal/annotators/http/constants.go @@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright 2022 Dell Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + *******************************************************************************/ + +package http + +type specialtyComponent string + +const ( + method specialtyComponent = "@method" + authority specialtyComponent = "@authority" + scheme specialtyComponent = "@scheme" + requestTarget specialtyComponent = "@request-target" + path specialtyComponent = "@path" + query specialtyComponent = "@query" + queryParams specialtyComponent = "@query-params" +) + +const ( + contentLength string = "Content-Length" + contentType string = "Content-Type" + testRequest string = "testRequest" +) + +func (s specialtyComponent) Validate() bool { + if s == method || s == authority || s == scheme || s == requestTarget || s == path || s == query || s == queryParams { + return true + } + return false +} diff --git a/internal/annotators/http/parser.go b/internal/annotators/http/parser.go new file mode 100644 index 0000000..4068e68 --- /dev/null +++ b/internal/annotators/http/parser.go @@ -0,0 +1,141 @@ +/******************************************************************************* + * Copyright 2022 Dell Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + *******************************************************************************/ + +package http + +import ( + "bytes" + "fmt" + "net/http" + "strings" +) + +type parseResult struct { + Seed string + Signature string + Keyid string + Algorithm string +} + +func RemoveExtraSpaces(s string) string { + return strings.Join(strings.Fields(s), " ") +} + +func parseRequest(r *http.Request) (parseResult, error) { + //Signature Inputs extraction + signatureInput := r.Header.Get("Signature-Input") + signature := r.Header.Get("Signature") + + signatureInputList := strings.SplitN(signatureInput, ";", 2) + + signatureInputHeader := strings.Fields(signatureInputList[0]) + signatureInputTail := signatureInputList[1] + + var keyid, algorithm string + + signatureInputParsedTail := strings.Split(signatureInputTail, ";") + for _, s := range signatureInputParsedTail { + + if strings.Contains(s, "alg") { + raw := strings.Split(s, "=")[1] + algorithm = strings.Trim(raw, "\"") + } + + if strings.Contains(s, "keyid") { + raw := strings.Split(s, "=")[1] + keyid = strings.Trim(raw, "\"") + } + } + + signatureInputFields := make(map[string][]string) + + parsedSignatureInput := "" + var s parseResult + + for _, field := range signatureInputHeader { + //remove double quotes from the field to access it directly in the header map + key := field[1 : len(field)-1] + if key[0:1] == "@" { + switch specialtyComponent(key) { + case method: + signatureInputFields[key] = []string{r.Method} + case authority: + signatureInputFields[key] = []string{r.Host} + case scheme: + protool := r.Proto + scheme := strings.ToLower(strings.Split(protool, "/")[0]) + signatureInputFields[key] = []string{scheme} + case requestTarget: + signatureInputFields[key] = []string{r.RequestURI} + case path: + signatureInputFields[key] = []string{r.URL.Path} + case query: + var query string = "?" + query += r.URL.RawQuery + signatureInputFields[key] = []string{query} + case queryParams: + rawQueryParams := strings.Split(r.URL.RawQuery, "&") + var queryParams []string + for _, rawQueryParam := range rawQueryParams { + if rawQueryParam != "" { + parameter := strings.Split(rawQueryParam, "=") + name := parameter[0] + value := parameter[1] + b := new(bytes.Buffer) + fmt.Fprintf(b, ";name=\"%s\": %s", name, value) + queryParams = append(queryParams, b.String()) + } + } + signatureInputFields[key] = queryParams + default: + return s, fmt.Errorf("Unhandled Specialty Component %s", key) + } + } else { + fieldValues := r.Header.Values(key) + + if len(fieldValues) == 0 { + return s, fmt.Errorf("Unhandled Specialty Component %s", key) + } else if len(fieldValues) == 1 { + value := RemoveExtraSpaces(r.Header.Get(key)) + signatureInputFields[key] = []string{value} + + } else { + + value := "" + for i := 0; i < len(fieldValues); i++ { + value += fieldValues[i] + if i != (len(fieldValues) - 1) { + value += ", " + } + } + value = RemoveExtraSpaces(value) + signatureInputFields[key] = []string{value} + } + } + // Construct final output string + keyValues := signatureInputFields[key] + if len(keyValues) == 1 { + parsedSignatureInput += ("\"" + key + "\" " + keyValues[0] + "\n") + } else { + for _, v := range keyValues { + parsedSignatureInput += ("\"" + key + "\"" + v + "\n") + } + } + } + + parsedSignatureInput = fmt.Sprintf("%s;%s", parsedSignatureInput, signatureInputTail) + s = parseResult{Seed: parsedSignatureInput, Signature: signature, Keyid: keyid, Algorithm: algorithm} + + return s, nil +} diff --git a/internal/annotators/http/parser_test.go b/internal/annotators/http/parser_test.go new file mode 100644 index 0000000..beba04e --- /dev/null +++ b/internal/annotators/http/parser_test.go @@ -0,0 +1,103 @@ +/******************************************************************************* + * Copyright 2022 Dell Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + *******************************************************************************/ + +package http + +import ( + "encoding/json" + "io/ioutil" + "net/http/httptest" + "testing" + + "github.com/project-alvarium/alvarium-sdk-go/pkg/config" + "github.com/stretchr/testify/assert" +) + +func TestHttpPkiAnnotator_RequestParser(t *testing.T) { + b, err := ioutil.ReadFile("./test/config.json") + if err != nil { + t.Fatalf(err.Error()) + } + + var cfg config.SdkInfo + err = json.Unmarshal(b, &cfg) + if err != nil { + t.Fatalf(err.Error()) + } + + base := httptest.NewRequest("POST", "/foo?var1=&var2=2", nil) + + base.Header.Set("Host", "example.com") + base.Header.Set("Date", "Tue, 20 Apr 2021 02:07:55 GMT") + base.Header.Set("Content-Type", "application/json") + base.Header.Set("Content-Length", "18") + base.Header.Set("Signature", "whatever") + + seedTests := []struct { + name string + signatureInput string + expectedSeed string + expectError bool + }{ + {"testing integeration of all Signature-Input fields", + "\"date\" \"@method\" \"@path\" \"@authority\" \"content-type\" \"content-length\" \"@query-params\" \"@query\";created=1644758607;keyid=\"public.key\";alg=\"ed25519\";", + "\"date\" Tue, 20 Apr 2021 02:07:55 GMT\n\"@method\" POST\n\"@path\" /foo\n\"@authority\" example.com\n\"content-type\" application/json\n\"content-length\" 18\n\"@query-params\";name=\"var1\": \n\"@query-params\";name=\"var2\": 2\n\"@query\" ?var1=&var2=2\n;created=1644758607;keyid=\"public.key\";alg=\"ed25519\";", false}, + + {"testing @method", "\"@method\";", "\"@method\" POST\n;", false}, + {"testing @authority", "\"@authority\";", "\"@authority\" example.com\n;", false}, + + {"testing @scheme", "\"@scheme\";", "\"@scheme\" http\n;", false}, + {"testing @request-target", "\"@request-target\";", "\"@request-target\" /foo?var1=&var2=2\n;", false}, + + {"testing @path", "\"@path\";", "\"@path\" /foo\n;", false}, + + {"testing @query", "\"@query\";", "\"@query\" ?var1=&var2=2\n;", false}, + {"testing @query-params", "\"@query-params\";", "\"@query-params\";name=\"var1\": \n\"@query-params\";name=\"var2\": 2\n;", false}, + + {"testing non-existant derived component", "\"@x-test\";", "", true}, + {"testing non-existant header field", "\"x-test\";", "", true}, + } + + for _, tt := range seedTests { + + req := base.Clone(base.Context()) + req.Header.Set("Signature-Input", tt.signatureInput) + + t.Run(tt.name, func(t *testing.T) { + signatureInfo, err := parseRequest(req) + if tt.expectError { + assert.Error(t, err) + } else { + assert.Equal(t, tt.expectedSeed, signatureInfo.Seed) + } + }) + + } + + req := base.Clone(base.Context()) + req.Header.Set("Signature-Input", "\"@query\";created=1644758607;keyid=\"public.key\";alg=\"ed25519\";") + parsed, err := parseRequest(req) + + t.Run("testing signature", func(t *testing.T) { + assert.Equal(t, "whatever", parsed.Signature) + }) + + t.Run("testing keyid", func(t *testing.T) { + assert.Equal(t, "public.key", parsed.Keyid) + }) + + t.Run("testing algorithm", func(t *testing.T) { + assert.Equal(t, "ed25519", parsed.Algorithm) + }) +} diff --git a/internal/annotators/http/pki.go b/internal/annotators/http/pki.go new file mode 100644 index 0000000..c268e25 --- /dev/null +++ b/internal/annotators/http/pki.go @@ -0,0 +1,111 @@ +/******************************************************************************* + * Copyright 2022 Dell Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + *******************************************************************************/ + +package http + +import ( + "context" + "errors" + "fmt" + "io/ioutil" + "net/http" + "os" + "path/filepath" + "strings" + + "github.com/project-alvarium/alvarium-sdk-go/internal/annotators" + "github.com/project-alvarium/alvarium-sdk-go/internal/signprovider" + "github.com/project-alvarium/alvarium-sdk-go/internal/signprovider/ed25519" + "github.com/project-alvarium/alvarium-sdk-go/pkg/config" + "github.com/project-alvarium/alvarium-sdk-go/pkg/contracts" + "github.com/project-alvarium/alvarium-sdk-go/pkg/interfaces" +) + +// HttpPkiAnnotator is used to validate whether the signature on a given piece of data is valid, both sent in the HTTP message +type HttpPkiAnnotator struct { + hash contracts.HashType + kind contracts.AnnotationType + sign config.SignatureInfo +} + +func NewHttpPkiAnnotator(cfg config.SdkInfo) interfaces.Annotator { + a := HttpPkiAnnotator{} + a.hash = cfg.Hash.Type + a.kind = contracts.AnnotationPKIHttp + a.sign = cfg.Signature + return &a +} + +func (a *HttpPkiAnnotator) Do(ctx context.Context, data []byte) (contracts.Annotation, error) { + key := annotators.DeriveHash(a.hash, data) + hostname, _ := os.Hostname() + + //Call parser on request + req := ctx.Value(testRequest) + parsed, err := parseRequest(req.(*http.Request)) + + if err != nil { + return contracts.Annotation{}, err + } + var sig signable + sig.Seed = parsed.Seed + sig.Signature = parsed.Signature + + // Use the parsed request to obtain the key name and type we should use to validate the signature + var k config.KeyInfo + directory := filepath.Dir(a.sign.PublicKey.Path) + k.Path = strings.Join([]string{directory, parsed.Keyid}, "/") + k.Type = contracts.KeyAlgorithm(parsed.Algorithm) + if !(k.Type.Validate()) { + return contracts.Annotation{}, errors.New("invalid key type specified: " + parsed.Algorithm) + } + + ok, err := sig.verifySignature(k) + if err != nil { + return contracts.Annotation{}, err + } + + annotation := contracts.NewAnnotation(string(key), a.hash, hostname, a.kind, ok) + signed, err := annotators.SignAnnotation(a.sign.PrivateKey, annotation) + if err != nil { + return contracts.Annotation{}, err + } + annotation.Signature = string(signed) + return annotation, nil +} + +type signable struct { + Seed string + Signature string +} + +func (s *signable) verifySignature(key config.KeyInfo) (bool, error) { + if len(s.Signature) == 0 { // no signature detected + return false, nil + } + var p signprovider.Provider + switch contracts.KeyAlgorithm(key.Type) { + case contracts.KeyEd25519: + p = ed25519.New() + + default: + return false, fmt.Errorf("unrecognized key type %s", key.Type) + } + pub, err := ioutil.ReadFile(key.Path) + if err != nil { + return false, err + } + ok := p.Verify(pub, []byte(s.Seed), []byte(s.Signature)) + return ok, nil +} diff --git a/internal/annotators/http/pki_test.go b/internal/annotators/http/pki_test.go new file mode 100644 index 0000000..53f4def --- /dev/null +++ b/internal/annotators/http/pki_test.go @@ -0,0 +1,210 @@ +/******************************************************************************* + * Copyright 2022 Dell Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + *******************************************************************************/ + +package http + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "net/http/httptest" + "path/filepath" + "regexp" + "strconv" + "strings" + "testing" + "time" + + "github.com/project-alvarium/alvarium-sdk-go/internal/signprovider/ed25519" + "github.com/project-alvarium/alvarium-sdk-go/pkg/contracts" + + "github.com/project-alvarium/alvarium-sdk-go/internal/annotators" + "github.com/project-alvarium/alvarium-sdk-go/pkg/config" + "github.com/project-alvarium/alvarium-sdk-go/test" +) + +func TestHttpPkiAnnotator_Do(t *testing.T) { + b, err := ioutil.ReadFile("./test/config.json") + if err != nil { + t.Fatalf(err.Error()) + } + + var cfg config.SdkInfo + err = json.Unmarshal(b, &cfg) + if err != nil { + t.Fatalf(err.Error()) + } + + req, data, err := buildRequest(cfg.Signature) + if err != nil { + t.Fatalf(err.Error()) + } + + tests := []struct { + name string + expectError bool + }{ + {"pki annotation OK", false}, + {"pki bad key type", true}, + {"pki key not found", true}, + {"pki empty signature", false}, + {"pki invalid signature", false}, + } + + for _, tt := range tests { + ctx := buildContext(tt.name, req) + t.Run(tt.name, func(t *testing.T) { + pki := NewHttpPkiAnnotator(cfg) + anno, err := pki.Do(ctx, data) + test.CheckError(err, tt.expectError, tt.name, t) + if err == nil { + result, err := annotators.VerifySignature(cfg.Signature.PublicKey, anno) + if err != nil { + t.Error(err.Error()) + } else if !result { + t.Error("signature not verified") + } + if tt.name == "pki empty signature" || tt.name == "pki invalid signature" { + if anno.IsSatisfied { + t.Errorf("satisfied should be false") + } + } else if tt.name == "pki annotation OK" { + if !anno.IsSatisfied { + t.Errorf("satisfied should be true") + } + } + } + }) + } +} + +func buildContext(testName string, req *http.Request) context.Context { + reqClone := req.Clone(req.Context()) + switch testName { + case "pki annotation OK": + ctx := context.WithValue(req.Context(), testRequest, req) + return ctx + case "pki bad key type": + signatureInput := reqClone.Header.Get("Signature-Input") + + //Finding and replacing the alg parameter value in the Signature-Input by invalid + m := regexp.MustCompile("(alg=\")([^\"]*)(\")") + res := m.ReplaceAllString(signatureInput, "${1}invalid$3") + + reqClone.Header.Set("Signature-Input", res) + case "pki key not found": + signatureInput := reqClone.Header.Get("Signature-Input") + + //Finding and replacing the keyid parameter value in the Signature-Input by invalid + m := regexp.MustCompile("(keyid=\")([^\"]*)(\")") + res := m.ReplaceAllString(signatureInput, "${1}invalid$3") + + reqClone.Header.Set("Signature-Input", res) + case "pki empty signature": + reqClone.Header.Set("Signature", "") + case "pki invalid signature": + reqClone.Header.Set("Signature", "invalid") + } + ctx := context.WithValue(reqClone.Context(), testRequest, reqClone) + return ctx +} + +func buildRequest(keys config.SignatureInfo) (*http.Request, []byte, error) { + type sample struct { + Key string `json:"key"` + Value string `json:"value"` + } + + t := sample{Key: "keyA", Value: "This is some test data"} + b, _ := json.Marshal(t) + + req := httptest.NewRequest("POST", "/foo?param=value&foo=bar&baz=batman", bytes.NewReader(b)) + req.Header.Set("Host", "example.com") + + ticks := time.Now() + now := ticks.String() + req.Header.Set("Date", now) + req.Header.Set(contentType, string(contracts.ContentTypeJSON)) + req.Header.Set(contentLength, strconv.FormatInt(req.ContentLength, 10)) + + fields := []string{string(method), string(path), string(authority), contentType, contentLength} + headerValue, signature, err := signRequest(ticks, fields, keys, req) + + req.Header.Set("Signature-Input", headerValue) + req.Header.Set("Signature", signature) + + return req, b, err +} + +func signRequest(ticks time.Time, fields []string, keys config.SignatureInfo, req *http.Request) (string, string, error) { + headerValue := "" //This will be the value returned for populating the Signature-Input header + inputValue := "" //This will be the value used as input for the signature + + for i, f := range fields { + headerValue += fmt.Sprintf("\"%s\"", f) + switch f { + case contentType: + inputValue += fmt.Sprintf("\"%s\" %s", f, req.Header.Get(contentType)) + case contentLength: + inputValue += fmt.Sprintf("\"%s\" %s", f, strconv.FormatInt(req.ContentLength, 10)) + case string(method): + inputValue += fmt.Sprintf("\"%s\" %s", f, req.Method) + case string(authority): + inputValue += fmt.Sprintf("\"%s\" %s", f, req.Host) + case string(scheme): + scheme := strings.ToLower(strings.Split(req.Proto, "/")[0]) + inputValue += fmt.Sprintf("\"%s\" %s", f, scheme) + case string(requestTarget): + inputValue += fmt.Sprintf("\"%s\" %s", f, req.RequestURI) + case string(path): + inputValue += fmt.Sprintf("\"%s\" %s", f, req.URL.Path) + case string(query): + var query string = "?" + req.URL.RawQuery + inputValue += fmt.Sprintf("\"%s\" %s", f, query) + case string(queryParams): + queryParamsRawMap := req.URL.Query() + var queryParams []string + for key, value := range queryParamsRawMap { + b := new(bytes.Buffer) + fmt.Fprintf(b, ";name=\"%s\": %s", key, value[0]) + queryParams = append(queryParams, b.String()) + } + + inputValue += fmt.Sprintf("\"%s\" %s", f, query) + } + + inputValue += "\n" + if i < len(fields)-1 { + headerValue += " " + } + } + + tail := fmt.Sprintf(";created=%s;keyid=\"%s\";alg=\"%s\";", strconv.FormatInt(ticks.Unix(), 10), + filepath.Base(keys.PublicKey.Path), keys.PublicKey.Type) + + headerValue += tail + inputValue += tail + + signer := ed25519.New() + prv, err := ioutil.ReadFile(keys.PrivateKey.Path) + if err != nil { + return "", "", err + } + + signature := signer.Sign(prv, []byte(inputValue)) + return headerValue, signature, nil +} diff --git a/internal/annotators/http/test/config.json b/internal/annotators/http/test/config.json new file mode 100644 index 0000000..6fd6584 --- /dev/null +++ b/internal/annotators/http/test/config.json @@ -0,0 +1,35 @@ +{ + "annotators": [ + "tpm", + "pki" + ], + "hash": { + "type": "sha256" + }, + "signature": { + "public": { + "type": "ed25519", + "path": "../../../test/keys/ed25519/public.key" + }, + "private": { + "type": "ed25519", + "path": "../../../test/keys/ed25519/private.key" + } + }, + "stream": { + "type": "iota", + "config": { + "provider": { + "host": "localhost", + "protocol": "http", + "port": 8080 + }, + "tangle": { + "host": "localhost", + "protocol": "http", + "port": 8080 + }, + "encoding": "utf-8" + } + } +} \ No newline at end of file diff --git a/internal/annotators/pki.go b/internal/annotators/pki.go index 68339d8..e74b924 100644 --- a/internal/annotators/pki.go +++ b/internal/annotators/pki.go @@ -42,7 +42,7 @@ func NewPkiAnnotator(cfg config.SdkInfo) interfaces.Annotator { } func (a *PkiAnnotator) Do(ctx context.Context, data []byte) (contracts.Annotation, error) { - key := deriveHash(a.hash, data) + key := DeriveHash(a.hash, data) hostname, _ := os.Hostname() var sig signable @@ -56,7 +56,7 @@ func (a *PkiAnnotator) Do(ctx context.Context, data []byte) (contracts.Annotatio return contracts.Annotation{}, err } annotation := contracts.NewAnnotation(string(key), a.hash, hostname, a.kind, ok) - signed, err := signAnnotation(a.sign.PrivateKey, annotation) + signed, err := SignAnnotation(a.sign.PrivateKey, annotation) if err != nil { return contracts.Annotation{}, err } diff --git a/internal/annotators/pki_test.go b/internal/annotators/pki_test.go index 58ea0af..610c5c5 100644 --- a/internal/annotators/pki_test.go +++ b/internal/annotators/pki_test.go @@ -84,7 +84,7 @@ func TestPkiAnnotator_Do(t *testing.T) { anno, err := tpm.Do(context.Background(), b) test.CheckError(err, tt.expectError, tt.name, t) if err == nil { - result, err := verifySignature(tt.cfg.Signature.PublicKey, anno) + result, err := VerifySignature(tt.cfg.Signature.PublicKey, anno) if err != nil { t.Error(err.Error()) } else if !result { diff --git a/internal/annotators/source.go b/internal/annotators/source.go index 5da938f..f53b7dc 100644 --- a/internal/annotators/source.go +++ b/internal/annotators/source.go @@ -37,11 +37,11 @@ func NewSourceAnnotator(cfg config.SdkInfo) interfaces.Annotator { } func (a *SourceAnnotator) Do(ctx context.Context, data []byte) (contracts.Annotation, error) { - key := deriveHash(a.hash, data) + key := DeriveHash(a.hash, data) hostname, _ := os.Hostname() annotation := contracts.NewAnnotation(key, a.hash, hostname, a.kind, true) - sig, err := signAnnotation(a.sign.PrivateKey, annotation) + sig, err := SignAnnotation(a.sign.PrivateKey, annotation) if err != nil { return contracts.Annotation{}, err } diff --git a/internal/annotators/tls.go b/internal/annotators/tls.go index 547c43a..f034ebe 100644 --- a/internal/annotators/tls.go +++ b/internal/annotators/tls.go @@ -26,7 +26,7 @@ func NewTlsAnnotator(cfg config.SdkInfo) interfaces.Annotator { } func (a *TlsAnnotator) Do(ctx context.Context, data []byte) (contracts.Annotation, error) { - key := deriveHash(a.hash, data) + key := DeriveHash(a.hash, data) hostname, _ := os.Hostname() isSatisfied := false @@ -49,7 +49,7 @@ func (a *TlsAnnotator) Do(ctx context.Context, data []byte) (contracts.Annotatio } } annotation := contracts.NewAnnotation(key, a.hash, hostname, a.kind, isSatisfied) - sig, err := signAnnotation(a.sign.PrivateKey, annotation) + sig, err := SignAnnotation(a.sign.PrivateKey, annotation) if err != nil { return contracts.Annotation{}, err } diff --git a/internal/annotators/tls_test.go b/internal/annotators/tls_test.go index 7decdd6..30ae19c 100644 --- a/internal/annotators/tls_test.go +++ b/internal/annotators/tls_test.go @@ -52,7 +52,7 @@ func TestTlsAnnotator_Base(t *testing.T) { anno, err := tls.Do(context.Background(), []byte(tt.data)) test.CheckError(err, tt.expectError, tt.name, t) if err == nil { - result, err := verifySignature(tt.cfg.Signature.PublicKey, anno) + result, err := VerifySignature(tt.cfg.Signature.PublicKey, anno) if err != nil { t.Error(err.Error()) } else if !result { diff --git a/internal/annotators/tpm.go b/internal/annotators/tpm.go index 11652fc..bf83525 100644 --- a/internal/annotators/tpm.go +++ b/internal/annotators/tpm.go @@ -40,7 +40,7 @@ func NewTpmAnnotator(cfg config.SdkInfo) interfaces.Annotator { } func (a *TpmAnnotator) Do(ctx context.Context, data []byte) (contracts.Annotation, error) { - key := deriveHash(a.hash, data) + key := DeriveHash(a.hash, data) hostname, _ := os.Hostname() isSatisfied := false @@ -56,7 +56,7 @@ func (a *TpmAnnotator) Do(ctx context.Context, data []byte) (contracts.Annotatio } annotation := contracts.NewAnnotation(key, a.hash, hostname, a.kind, isSatisfied) - sig, err := signAnnotation(a.sign.PrivateKey, annotation) + sig, err := SignAnnotation(a.sign.PrivateKey, annotation) if err != nil { return contracts.Annotation{}, err } diff --git a/internal/annotators/tpm_test.go b/internal/annotators/tpm_test.go index dd54c65..034659c 100644 --- a/internal/annotators/tpm_test.go +++ b/internal/annotators/tpm_test.go @@ -61,7 +61,7 @@ func TestTpmAnnotator_Do(t *testing.T) { anno, err := tpm.Do(context.Background(), []byte(tt.data)) test.CheckError(err, tt.expectError, tt.name, t) if err == nil { - result, err := verifySignature(tt.cfg.Signature.PublicKey, anno) + result, err := VerifySignature(tt.cfg.Signature.PublicKey, anno) if err != nil { t.Error(err.Error()) } else if !result { diff --git a/pkg/contracts/constants.go b/pkg/contracts/constants.go index c3dd3c4..f64fe07 100644 --- a/pkg/contracts/constants.go +++ b/pkg/contracts/constants.go @@ -13,6 +13,12 @@ *******************************************************************************/ package contracts +type ContentType string + +const ( + ContentTypeJSON ContentType = "application/json" +) + type HashType string const ( @@ -60,10 +66,11 @@ func (t StreamType) Validate() bool { type AnnotationType string const ( - AnnotationPKI AnnotationType = "pki" - AnnotationSource AnnotationType = "src" - AnnotationTLS AnnotationType = "tls" - AnnotationTPM AnnotationType = "tpm" + AnnotationPKI AnnotationType = "pki" + AnnotationPKIHttp AnnotationType = "pki-http" + AnnotationSource AnnotationType = "src" + AnnotationTLS AnnotationType = "tls" + AnnotationTPM AnnotationType = "tpm" ) func (t AnnotationType) Validate() bool { diff --git a/pkg/sdk.go b/pkg/sdk.go index 7742164..bb11fda 100644 --- a/pkg/sdk.go +++ b/pkg/sdk.go @@ -150,7 +150,7 @@ func (s *sdk) Transit(ctx context.Context, data []byte) { func (s *sdk) Publish(ctx context.Context, data []byte) { var list contracts.AnnotationList - + for _, a := range s.annotators { annotation, err := a.Do(ctx, data) if err != nil { @@ -162,12 +162,12 @@ func (s *sdk) Publish(ctx context.Context, data []byte) { b, _ := json.Marshal(list) wrap := message.PublishWrapper{ - Action: message.ActionPublish, + Action: message.ActionPublish, MessageType: fmt.Sprintf("%T", list), - Content: b, + Content: b, } err := s.stream.Publish(wrap) if err != nil { s.logger.Error(err.Error()) } -} \ No newline at end of file +}