<commit_before>//go:build !race
// +build !race

package main

import (
	"bytes"
	"crypto/sha256"
	"crypto/x509"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/mozilla-services/autograph/formats"
	"github.com/mozilla-services/autograph/signer/apk2"
	"github.com/mozilla-services/autograph/signer/contentsignature"
	"github.com/mozilla-services/autograph/signer/mar"
	"github.com/mozilla-services/autograph/signer/xpi"

	margo "go.mozilla.org/mar"
)

func TestSignaturePass(t *testing.T) {
	t.Parallel()

	var TESTCASES = []struct {
		endpoint          string
		signaturerequests []formats.SignatureRequest
	}{
		{
			// Sign hash with Content Signature
			"/sign/hash",
			[]formats.SignatureRequest{
				// request signature of a precomputed sha384 hash
				formats.SignatureRequest{
					Input: "<KEY>",
					KeyID: "appkey1",
				},
			},
		},
		{
			// Sign a regular add-on
			"/sign/data",
			[]formats.SignatureRequest{
				formats.SignatureRequest{
					Input: "<KEY>
					KeyID: "webextensions-rsa",
					Options: map[string]string{
						"id": "<EMAIL>",
					},
				},
			},
		},
		{
			// Sign an extension
			"/sign/data",
			[]formats.SignatureRequest{
				formats.SignatureRequest{
					Input: "<KEY>
					KeyID: "extensions-ecdsa",
					Options: map[string]string{
						"id": "<EMAIL>",
					},
				},
			},
		},
		{
			// Sign data with Content-Signature
			"/sign/data",
			[]formats.SignatureRequest{
				formats.SignatureRequest{
					Input: "<KEY>",
					KeyID: "appkey2",
				},
			},
		},
		{
			// Sign an APK file
			"/sign/file",
			[]formats.SignatureRequest{
				formats.SignatureRequest{
					Input: "<KEY>",
					KeyID: "testapp-android",
				},
			},
		},
		{
			// Sign MAR data
			"/sign/data",
			[]formats.SignatureRequest{
				formats.SignatureRequest{
					Input: "Y2FyaWJvdW1hdXJpY2U=",
					KeyID: "testmar",
				},
			},
		},
		{
			// Sign a MAR file
			"/sign/file",
			[]formats.SignatureRequest{
				formats.SignatureRequest{
					// Input is the base64 of miniMarB from the mar signer unit tests
					Input: "<KEY>
					KeyID: "testmar",
				},
			},
		},
	}
	for i, testcase := range TESTCASES {
		userid := conf.Authorizations[0].ID
		body, err := json.Marshal(testcase.signaturerequests)
		if err != nil {
			t.Fatal(err)
		}
		rdr := bytes.NewReader(body)
		req, err := http.NewRequest("POST",
			"http://foo.bar"+testcase.endpoint,
			rdr)
		if err != nil {
			t.Fatal(err)
		}
		req.Header.Set("Content-Type", "application/json")

		// generate a hawk header for the request
		testAuth, err := ag.getAuthByID(userid)
		if err != nil {
			t.Fatal(err)
		}

		authheader := getAuthHeader(req,
			testAuth.ID,
			testAuth.Key,
			sha256.New,
			id(),
			"application/json",
			body)
		req.Header.Set("Authorization", authheader)

		// send the request to the handler
		w := httptest.NewRecorder()
		ag.handleSignature(w, req)
		if w.Code != http.StatusCreated || w.Body.String() == "" {
			t.Fatalf("failed with %d: %s; request was: %+v", w.Code, w.Body.String(), req)
		}

		// parse the response
		var responses []formats.SignatureResponse
		err = json.Unmarshal(w.Body.Bytes(), &responses)
		if err != nil {
			t.Fatal(err)
		}

		// we should have received the same number of responses as we sent requests
		if len(responses) != len(testcase.signaturerequests) {
			t.Fatalf("in test case %d, failed to receive as many responses (%d) as we sent requests (%d)",
				i, len(responses), len(testcase.signaturerequests))
		}

		// verify the signature in each response
		for j, response := range responses {
			switch response.Type {
			case contentsignature.Type:
				err = verifyContentSignatureResponse(
					testcase.signaturerequests[j].Input,
					response,
					testcase.endpoint)
			case xpi.Type:
				err = verifyXPISignature(testcase.signaturerequests[j].Input, response.Signature)
			case apk2.Type:
				if req.URL.RequestURI() == "/sign/data" {
					// ok this is a bit of a hack but since apk signatures are really just pkcs7 we can
					// reuse the xpi verification code here...
					err = verifyAPKManifestSignature(testcase.signaturerequests[j].Input, response.Signature)
				} else {
					signedAPK, _ := base64.StdEncoding.DecodeString(response.SignedFile)
					err = verifyAPKSignature(signedAPK)
				}
			case mar.Type:
				switch req.URL.RequestURI() {
				case "/sign/file":
					// use the margo pkg to calculate the signable block of the mar file
					// as input for the signature verification
					rawInput, _ := base64.StdEncoding.DecodeString(testcase.signaturerequests[j].Input)
					var marFile margo.File
					margo.Unmarshal(rawInput, &marFile)
					rawKey, err := base64.StdEncoding.DecodeString(response.PublicKey)
					if err != nil {
						t.Fatalf("in test case %d on endpoint %q, error '%v' in response %d;\nrequest was: %+v\nresponse was: %+v",
							i, testcase.endpoint, err, j, testcase.signaturerequests[j], response)
					}
					key, err := x509.ParsePKIXPublicKey(rawKey)
					if err != nil {
						t.Fatalf("in test case %d on endpoint %q, error '%v' in response %d;\nrequest was: %+v\nresponse was: %+v",
							i, testcase.endpoint, err, j, testcase.signaturerequests[j], response)
					}
					err = marFile.VerifySignature(key)
					if err != nil {
						t.Fatalf("in test case %d on endpoint %q, error verifying mar signature '%v' in response %d;\nrequest was: %+v\nresponse was: %+v",
							i, testcase.endpoint, err, j, testcase.signaturerequests[j], response)
					}
				default:
					err = verifyMARSignature(testcase.signaturerequests[j].Input, response.Signature, response.PublicKey, margo.SigAlgRsaPkcs1Sha384)
				}
			default:
				err = fmt.Errorf("unknown signature type %q", response.Type)
			}<commit_msg>check errs in base64 decode and mar unmarshal sig tests<commit_after>//go:build !race
// +build !race

package main

import (
	"bytes"
	"crypto/sha256"
	"crypto/x509"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/mozilla-services/autograph/formats"
	"github.com/mozilla-services/autograph/signer/apk2"
	"github.com/mozilla-services/autograph/signer/contentsignature"
	"github.com/mozilla-services/autograph/signer/mar"
	"github.com/mozilla-services/autograph/signer/xpi"

	margo "go.mozilla.org/mar"
)

func TestSignaturePass(t *testing.T) {
	t.Parallel()

	var TESTCASES = []struct {
		endpoint          string
		signaturerequests []formats.SignatureRequest
	}{
		{
			// Sign hash with Content Signature
			"/sign/hash",
			[]formats.SignatureRequest{
				// request signature of a precomputed sha384 hash
				formats.SignatureRequest{
					Input: "y0hdfsN8tHlCG82JLywb4d2U+VGWWry8dzwIC3Hk6j32mryUHxUel9SWM5TWkk0d",
					KeyID: "appkey1",
				},
			},
		},
		{
			// Sign a regular add-on
			"/sign/data",
			[]formats.SignatureRequest{
				formats.SignatureRequest{
					Input: "<KEY>
					KeyID: "webextensions-rsa",
					Options: map[string]string{
						"id": "<EMAIL>",
					},
				},
			},
		},
		{
			// Sign an extension
			"/sign/data",
			[]formats.SignatureRequest{
				formats.SignatureRequest{
					Input: "<KEY>
					KeyID: "extensions-ecdsa",
					Options: map[string]string{
						"id": "<EMAIL>",
					},
				},
			},
		},
		{
			// Sign data with Content-Signature
			"/sign/data",
			[]formats.SignatureRequest{
				formats.SignatureRequest{
					Input: "<KEY>",
					KeyID: "appkey2",
				},
			},
		},
		{
			// Sign an APK file
			"/sign/file",
			[]formats.SignatureRequest{
				formats.SignatureRequest{
					Input: "<KEY>",
					KeyID: "testapp-android",
				},
			},
		},
		{
			// Sign MAR data
			"/sign/data",
			[]formats.SignatureRequest{
				formats.SignatureRequest{
					Input: "Y2FyaWJvdW1hdXJpY2U=",
					KeyID: "testmar",
				},
			},
		},
		{
			// Sign a MAR file
			"/sign/file",
			[]formats.SignatureRequest{
				formats.SignatureRequest{
					// Input is the base64 of miniMarB from the mar signer unit tests
					Input: "<KEY>
					KeyID: "testmar",
				},
			},
		},
	}
	for i, testcase := range TESTCASES {
		userid := conf.Authorizations[0].ID
		body, err := json.Marshal(testcase.signaturerequests)
		if err != nil {
			t.Fatal(err)
		}
		rdr := bytes.NewReader(body)
		req, err := http.NewRequest("POST",
			"http://foo.bar"+testcase.endpoint,
			rdr)
		if err != nil {
			t.Fatal(err)
		}
		req.Header.Set("Content-Type", "application/json")

		// generate a hawk header for the request
		testAuth, err := ag.getAuthByID(userid)
		if err != nil {
			t.Fatal(err)
		}

		authheader := getAuthHeader(req,
			testAuth.ID,
			testAuth.Key,
			sha256.New,
			id(),
			"application/json",
			body)
		req.Header.Set("Authorization", authheader)

		// send the request to the handler
		w := httptest.NewRecorder()
		ag.handleSignature(w, req)
		if w.Code != http.StatusCreated || w.Body.String() == "" {
			t.Fatalf("failed with %d: %s; request was: %+v", w.Code, w.Body.String(), req)
		}

		// parse the response
		var responses []formats.SignatureResponse
		err = json.Unmarshal(w.Body.Bytes(), &responses)
		if err != nil {
			t.Fatal(err)
		}

		// we should have received the same number of responses as we sent requests
		if len(responses) != len(testcase.signaturerequests) {
			t.Fatalf("in test case %d, failed to receive as many responses (%d) as we sent requests (%d)",
				i, len(responses), len(testcase.signaturerequests))
		}

		// verify the signature in each response
		for j, response := range responses {
			switch response.Type {
			case contentsignature.Type:
				err = verifyContentSignatureResponse(
					testcase.signaturerequests[j].Input,
					response,
					testcase.endpoint)
			case xpi.Type:
				err = verifyXPISignature(testcase.signaturerequests[j].Input, response.Signature)
			case apk2.Type:
				if req.URL.RequestURI() == "/sign/data" {
					// ok this is a bit of a hack but since apk signatures are really just pkcs7 we can
					// reuse the xpi verification code here...
					err = verifyAPKManifestSignature(testcase.signaturerequests[j].Input, response.Signature)
				} else {
					signedAPK, _ := base64.StdEncoding.DecodeString(response.SignedFile)
					err = verifyAPKSignature(signedAPK)
				}
			case mar.Type:
				switch req.URL.RequestURI() {
				case "/sign/file":
					// use the margo pkg to calculate the signable block of the mar file
					// as input for the signature verification
					rawInput, err := base64.StdEncoding.DecodeString(testcase.signaturerequests[j].Input)
					if err != nil {
						t.Fatalf("in test case %d on endpoint %q, error '%v' in response %d;\nrequest was: %+v\nresponse was: %+v failed to decode input",
							i, testcase.endpoint, err, j, testcase.signaturerequests[j], response)
					}
					var marFile margo.File
					err = margo.Unmarshal(rawInput, &marFile)
					if err != nil {
						t.Fatalf("in test case %d on endpoint %q, error '%v' in response %d;\nrequest was: %+v\nresponse was: %+v failed to unmarshal mar sig",
							i, testcase.endpoint, err, j, testcase.signaturerequests[j], response)
					}
					rawKey, err := base64.StdEncoding.DecodeString(response.PublicKey)
					if err != nil {
						t.Fatalf("in test case %d on endpoint %q, error '%v' in response %d;\nrequest was: %+v\nresponse was: %+v",
							i, testcase.endpoint, err, j, testcase.signaturerequests[j], response)
					}
					key, err := x509.ParsePKIXPublicKey(rawKey)
					if err != nil {
						t.Fatalf("in test case %d on endpoint %q, error '%v' in response %d;\nrequest was: %+v\nresponse was: %+v",
							i, testcase.endpoint, err, j, testcase.signaturerequests[j], response)
					}
					err = marFile.VerifySignature(key)
					if err != nil {
						t.Fatalf("in test case %d on endpoint %q, error verifying mar signature '%v' in response %d;\nrequest was: %+v\nresponse was: %+v",
							i, testcase.endpoint, err, j, testcase.signaturerequests[j], response)
					}
				default:
					err = verifyMARSignature(testcase.signaturerequests[j].Input, response.Signature, response.PublicKey, margo.SigAlgRsaPkcs1Sha384)
				}
			default:
				err = fmt.Errorf("unknown signature type %q", response.Type)
			}
{'fraction_non_alphanumeric': 0.1378231516548658, 'fraction_numerical': 0.007574510126790713, 'mean_word_length': 3.044955044955045, 'pattern_counts': {'":': 6, '<': 18, '<?xml version=': 0, '>': 18, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '885791', 'n_tokens_mistral': 5175, 'n_tokens_neox': 4089, 'n_words': 976}