package httpsig import ( "bytes" "crypto" "encoding/base64" "fmt" "hash" "net/http" "strings" ) type DigestAlgorithm string const ( DigestSha256 DigestAlgorithm = "SHA-256" DigestSha512 = "SHA-512" ) var digestToDef = map[DigestAlgorithm]crypto.Hash{ DigestSha256: crypto.SHA256, DigestSha512: crypto.SHA512, } func getHash(alg DigestAlgorithm) (h hash.Hash, toUse DigestAlgorithm, err error) { upper := DigestAlgorithm(strings.ToUpper(string(alg))) c, ok := digestToDef[upper] if !ok { err = fmt.Errorf("unknown or unsupported Digest algorithm: %s", alg) } else if !c.Available() { err = fmt.Errorf("unavailable Digest algorithm: %s", alg) } else { h = c.New() toUse = upper } return } const ( digestHeader = "Digest" digestDelim = "=" ) func addDigest(r *http.Request, algo DigestAlgorithm, b []byte) (err error) { _, ok := r.Header[digestHeader] if ok { err = fmt.Errorf("cannot add Digest: Digest is already set") return } var h hash.Hash var a DigestAlgorithm h, a, err = getHash(algo) if err != nil { return } sum := h.Sum(b) r.Header.Add(digestHeader, fmt.Sprintf("%s%s%s", a, digestDelim, base64.StdEncoding.EncodeToString(sum[:]))) return } func verifyDigest(r *http.Request, body *bytes.Buffer) (err error) { d := r.Header.Get(digestHeader) if len(d) == 0 { err = fmt.Errorf("cannot verify Digest: request has no Digest header") return } elem := strings.SplitN(d, digestDelim, 2) if len(elem) != 2 { err = fmt.Errorf("cannot verify Digest: malformed Digest: %s", d) return } var h hash.Hash h, _, err = getHash(DigestAlgorithm(elem[0])) if err != nil { return } sum := h.Sum(body.Bytes()) encSum := base64.StdEncoding.EncodeToString(sum[:]) if encSum != elem[1] { err = fmt.Errorf("cannot verify Digest: header Digest does not match the digest of the request body") return } return }