all repos — honk @ 7982b4d31637478d39d5398e66c2a87cb17f2580

my fork of honk

zig.go (view raw)

  1//
  2// Copyright (c) 2019 Ted Unangst <tedu@tedunangst.com>
  3//
  4// Permission to use, copy, modify, and distribute this software for any
  5// purpose with or without fee is hereby granted, provided that the above
  6// copyright notice and this permission notice appear in all copies.
  7//
  8// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  9// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 10// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 11// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 12// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 13// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 14// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 15
 16package main
 17
 18import (
 19	"bytes"
 20	"crypto"
 21	"crypto/rand"
 22	"crypto/rsa"
 23	"crypto/sha256"
 24	"crypto/x509"
 25	"encoding/base64"
 26	"encoding/pem"
 27	"fmt"
 28	"io"
 29	"net/http"
 30	"regexp"
 31	"strings"
 32	"time"
 33)
 34
 35func sb64(data []byte) string {
 36	var sb strings.Builder
 37	b64 := base64.NewEncoder(base64.StdEncoding, &sb)
 38	b64.Write(data)
 39	b64.Close()
 40	return sb.String()
 41
 42}
 43func b64s(s string) []byte {
 44	var buf bytes.Buffer
 45	b64 := base64.NewDecoder(base64.StdEncoding, strings.NewReader(s))
 46	io.Copy(&buf, b64)
 47	return buf.Bytes()
 48}
 49func sb64sha256(content []byte) string {
 50	h := sha256.New()
 51	h.Write(content)
 52	return sb64(h.Sum(nil))
 53}
 54
 55func zig(keyname string, key *rsa.PrivateKey, req *http.Request, content []byte) {
 56	headers := []string{"(request-target)", "date", "host", "content-type", "digest"}
 57	var stuff []string
 58	for _, h := range headers {
 59		var s string
 60		switch h {
 61		case "(request-target)":
 62			s = strings.ToLower(req.Method) + " " + req.URL.RequestURI()
 63		case "date":
 64			s = req.Header.Get(h)
 65			if s == "" {
 66				s = time.Now().UTC().Format(http.TimeFormat)
 67				req.Header.Set(h, s)
 68			}
 69		case "host":
 70			s = req.Header.Get(h)
 71			if s == "" {
 72				s = req.URL.Hostname()
 73				req.Header.Set(h, s)
 74			}
 75		case "content-type":
 76			s = req.Header.Get(h)
 77		case "digest":
 78			s = req.Header.Get(h)
 79			if s == "" {
 80				s = "SHA-256=" + sb64sha256(content)
 81				req.Header.Set(h, s)
 82			}
 83		}
 84		stuff = append(stuff, h+": "+s)
 85	}
 86
 87	h := sha256.New()
 88	h.Write([]byte(strings.Join(stuff, "\n")))
 89	sig, _ := rsa.SignPKCS1v15(rand.Reader, key, crypto.SHA256, h.Sum(nil))
 90	bsig := sb64(sig)
 91
 92	sighdr := fmt.Sprintf(`keyId="%s",algorithm="%s",headers="%s",signature="%s"`,
 93		keyname, "rsa-sha256", strings.Join(headers, " "), bsig)
 94	req.Header.Set("Signature", sighdr)
 95}
 96
 97var re_sighdrval = regexp.MustCompile(`(.*)="(.*)"`)
 98
 99func zag(req *http.Request, content []byte) (string, error) {
100	sighdr := req.Header.Get("Signature")
101
102	var keyname, algo, heads, bsig string
103	for _, v := range strings.Split(sighdr, ",") {
104		m := re_sighdrval.FindStringSubmatch(v)
105		if len(m) != 3 {
106			return "", fmt.Errorf("bad scan: %s from %s\n", v, sighdr)
107		}
108		switch m[1] {
109		case "keyId":
110			keyname = m[2]
111		case "algorithm":
112			algo = m[2]
113		case "headers":
114			heads = m[2]
115		case "signature":
116			bsig = m[2]
117		default:
118			return "", fmt.Errorf("bad sig val: %s", m[1])
119		}
120	}
121	if keyname == "" || algo == "" || heads == "" || bsig == "" {
122		return "", fmt.Errorf("missing a sig value")
123	}
124
125	key := zaggy(keyname)
126	if key == nil {
127		return keyname, fmt.Errorf("no key for %s", keyname)
128	}
129	headers := strings.Split(heads, " ")
130	var stuff []string
131	for _, h := range headers {
132		var s string
133		switch h {
134		case "(request-target)":
135			s = strings.ToLower(req.Method) + " " + req.URL.RequestURI()
136		case "host":
137			s = req.Host
138		default:
139			s = req.Header.Get(h)
140		}
141		stuff = append(stuff, h+": "+s)
142	}
143
144	h := sha256.New()
145	h.Write([]byte(strings.Join(stuff, "\n")))
146	sig := b64s(bsig)
147	err := rsa.VerifyPKCS1v15(key, crypto.SHA256, h.Sum(nil), sig)
148	if err != nil {
149		return keyname, err
150	}
151	return keyname, nil
152}
153
154func pez(s string) (pri *rsa.PrivateKey, pub *rsa.PublicKey, err error) {
155	block, _ := pem.Decode([]byte(s))
156	if block == nil {
157		err = fmt.Errorf("no pem data")
158		return
159	}
160	switch block.Type {
161	case "PUBLIC KEY":
162		var k interface{}
163		k, err = x509.ParsePKIXPublicKey(block.Bytes)
164		if k != nil {
165			pub, _ = k.(*rsa.PublicKey)
166		}
167	case "RSA PUBLIC KEY":
168		pub, err = x509.ParsePKCS1PublicKey(block.Bytes)
169	case "RSA PRIVATE KEY":
170		pri, err = x509.ParsePKCS1PrivateKey(block.Bytes)
171		if err == nil {
172			pub = &pri.PublicKey
173		}
174	default:
175		err = fmt.Errorf("unknown key type")
176	}
177	return
178}
179
180func zem(i interface{}) (string, error) {
181	var b pem.Block
182	var err error
183	switch k := i.(type) {
184	case *rsa.PrivateKey:
185		b.Type = "RSA PRIVATE KEY"
186		b.Bytes = x509.MarshalPKCS1PrivateKey(k)
187	case *rsa.PublicKey:
188		b.Type = "PUBLIC KEY"
189		b.Bytes, err = x509.MarshalPKIXPublicKey(k)
190	default:
191		err = fmt.Errorf("unknown key type: %s", k)
192	}
193	if err != nil {
194		return "", err
195	}
196	return string(pem.EncodeToMemory(&b)), nil
197}