<commit_before>package aws

import (
	"bytes"
	"crypto/hmac"
	"crypto/sha256"
	"io"
	"net/http"
	"net/url"
	"os"
	"path"
	"sort"
	"strings"
	"time"
)

const (
	ISO8601BasicFormat      = "20060102T150405Z"
	ISO8601BasicFormatShort = "20060102"
)

var (
	unreserved = make([]bool, 128)
)

func init() {
	// RFC3986
	u := "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890-_.~"
	for _, c := range u {
		unreserved[c] = true
	}
}

// from https://launchpad.net/goamz
func encode(s string) string {
	hex := "0123456789ABCDEF"
	encode := false
	for i := 0; i != len(s); i++ {
		c := s[i]
		if c > 127 || !unreserved[c] {
			encode = true
			break
		}
	}
	if !encode {
		return s
	}
	e := make([]byte, len(s)*3)
	ei := 0
	for i := 0; i != len(s); i++ {
		c := s[i]
		if c > 127 || !unreserved[c] {
			e[ei] = '%'
			e[ei+1] = hex[c>>4]
			e[ei+2] = hex[c&0xF]
			ei += 3
		} else {
			e[ei] = c
			ei += 1
		}
	}
	return string(e[:ei])
}

func toHex(x []byte) []byte {
	hex := "0123456789abcdef"
	z := make([]byte, 2*len(x))
	for i, v := range x {
		z[2*i] = hex[(v&0xf0)>>4]
		z[2*i+1] = hex[v&0x0f]
	}
	return z
}

// Get secret and access ID keys (in that order) from enviroment variables
// AWS_SECRET_KEY and AWS_ACCESS_KEY.
func KeysFromEnviroment() (string, string) {
	return os.Getenv("AWS_SECRET_KEY"), os.Getenv("AWS_ACCESS_KEY")
}

// Signature contains the access ID key, UTC date in YYYYMMDD format, region,
// service name, and the signing key.
type Signature struct {
	AccessID   string
	Date       string
	Region     *Region
	Service    string
	SigningKey [sha256.Size]byte
}

// NewSignature creates a new signature from the secret key, access key,
// region, and service with the date set to UTC now.
func NewSignature(secret, access string, r *Region, service string) *Signature {
	var s Signature

	s.AccessID = access
	s.Date = time.Now().UTC().Format(ISO8601BasicFormatShort)
	s.Region = r
	s.Service = service
	s.generateSigningKey(secret)

	return &s
}

// separate function so that test suite can set a custom date
func (s *Signature) generateSigningKey(secret string) {
	h := hmac.New(sha256.New, []byte("AWS4"+secret))
	h.Write([]byte(s.Date))
	h = hmac.New(sha256.New, h.Sum(s.SigningKey[:0]))
	h.Write([]byte(s.Region.Name))
	h = hmac.New(sha256.New, h.Sum(s.SigningKey[:0]))
	h.Write([]byte(s.Service))
	h = hmac.New(sha256.New, h.Sum(s.SigningKey[:0]))
	h.Write([]byte("aws4_request"))
	h.Sum(s.SigningKey[:0])
}

// Sign uses signature s to sign the HTTP request. It sets the Authorization
// header and sets/overwrites the Date header for now.
// If the signature was created on a different UTC day the signing will be
// invalid.
func (s *Signature) Sign(r *http.Request) error {
	// TODO check all error cases first
	// TODO compare request date to signature date? or have signature update
	// when it expires? or have whatever is using signature check it?

	credential := s.Date + "/" + s.Region.Name + "/" + s.Service +
		"/aws4_request"

	// create canonical request
	var crb bytes.Buffer // canonical request buffer

	// 1
	crb.WriteString(r.Method)
	crb.WriteByte('\n')

	// 2
	var cp bytes.Buffer // canonical path
	parts := strings.Split(path.Clean(r.URL.Path)[1:], "/")
	for i := range parts {
		cp.WriteByte('/')
		cp.WriteString(encode(parts[i]))<commit_msg>Updated documentation.<commit_after>package aws

import (
	"bytes"
	"crypto/hmac"
	"crypto/sha256"
	"io"
	"net/http"
	"net/url"
	"os"
	"path"
	"sort"
	"strings"
	"time"
)

const (
	ISO8601BasicFormat      = "20060102T150405Z"
	ISO8601BasicFormatShort = "20060102"
)

var (
	unreserved = make([]bool, 128)
)

func init() {
	// RFC3986
	u := "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890-_.~"
	for _, c := range u {
		unreserved[c] = true
	}
}

// from https://launchpad.net/goamz
func encode(s string) string {
	hex := "0123456789ABCDEF"
	encode := false
	for i := 0; i != len(s); i++ {
		c := s[i]
		if c > 127 || !unreserved[c] {
			encode = true
			break
		}
	}
	if !encode {
		return s
	}
	e := make([]byte, len(s)*3)
	ei := 0
	for i := 0; i != len(s); i++ {
		c := s[i]
		if c > 127 || !unreserved[c] {
			e[ei] = '%'
			e[ei+1] = hex[c>>4]
			e[ei+2] = hex[c&0xF]
			ei += 3
		} else {
			e[ei] = c
			ei += 1
		}
	}
	return string(e[:ei])
}

func toHex(x []byte) []byte {
	hex := "0123456789abcdef"
	z := make([]byte, 2*len(x))
	for i, v := range x {
		z[2*i] = hex[(v&0xf0)>>4]
		z[2*i+1] = hex[v&0x0f]
	}
	return z
}

// Get secret and access ID keys (in that order) from enviroment variables
// AWS_SECRET_KEY and AWS_ACCESS_KEY.
func KeysFromEnviroment() (string, string) {
	return os.Getenv("AWS_SECRET_KEY"), os.Getenv("AWS_ACCESS_KEY")
}

// Signature contains the access ID key, UTC date in YYYYMMDD format, region,
// service name, and the signing key.
type Signature struct {
	AccessID   string
	Date       string
	Region     *Region
	Service    string
	SigningKey [sha256.Size]byte
}

// NewSignature creates a new signature from the secret key, access key,
// region, and service with the date set to UTC now.
func NewSignature(secret, access string, r *Region, service string) *Signature {
	var s Signature

	s.AccessID = access
	s.Date = time.Now().UTC().Format(ISO8601BasicFormatShort)
	s.Region = r
	s.Service = service
	s.generateSigningKey(secret)

	return &s
}

// separate function so that test suite can set a custom date
func (s *Signature) generateSigningKey(secret string) {
	h := hmac.New(sha256.New, []byte("AWS4"+secret))
	h.Write([]byte(s.Date))
	h = hmac.New(sha256.New, h.Sum(s.SigningKey[:0]))
	h.Write([]byte(s.Region.Name))
	h = hmac.New(sha256.New, h.Sum(s.SigningKey[:0]))
	h.Write([]byte(s.Service))
	h = hmac.New(sha256.New, h.Sum(s.SigningKey[:0]))
	h.Write([]byte("aws4_request"))
	h.Sum(s.SigningKey[:0])
}

// Sign uses signature s to sign the HTTP request. It sets the Authorization
// header and sets/overwrites the Date header for now.
// If the signature was created on a different UTC day the signing will be
// invalid.
// NOTE: Sign reads the request's body in order to hash it. Thus it must be
// reset, it is the caller's responsibility to do this.
func (s *Signature) Sign(r *http.Request) error {
	// TODO check all error cases first
	// TODO compare request date to signature date? or have signature update
	// when it expires? or have whatever is using signature check it?

	credential := s.Date + "/" + s.Region.Name + "/" + s.Service +
		"/aws4_request"

	// create canonical request
	var crb bytes.Buffer // canonical request buffer

	// 1
	crb.WriteString(r.Method)
	crb.WriteByte('\n')

	// 2
	var cp bytes.Buffer // canonical path
	parts := strings.Split(path.Clean(r.URL.Path)[1:], "/")
	for i := range parts {
		cp.WriteByte('/')
		cp.WriteString(encode(parts[i]))
{'fraction_non_alphanumeric': 0.15397130601981956, 'fraction_numerical': 0.036976778583049846, 'mean_word_length': 3.523076923076923, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 15, 'https://': 2, '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': '990189', 'n_tokens_mistral': 2826, 'n_tokens_neox': 2579, 'n_words': 714}