File: auth.go

package info (click to toggle)
golang-github-joyent-gosign 0.0~git20161114.9abcee2-3
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, bullseye, forky, sid, trixie
  • size: 96 kB
  • sloc: makefile: 2
file content (132 lines) | stat: -rw-r--r-- 3,530 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
//
// gosign - Go HTTP signing library for the Joyent Public Cloud and Joyent Manta
//
//
// Copyright (c) 2013 Joyent Inc.
//
// Written by Daniele Stroppa <daniele.stroppa@joyent.com>
//

package auth

import (
	"crypto"
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"encoding/base64"
	"encoding/pem"
	"fmt"
	"net/http"
	"net/url"
	"strings"
)

const (
	// Authorization Headers
	SdcSignature   = "Signature keyId=\"/%s/keys/%s\",algorithm=\"%s\" %s"
	MantaSignature = "Signature keyId=\"/%s/keys/%s\",algorithm=\"%s\",signature=\"%s\""
)

type Endpoint struct {
	URL string
}

type Auth struct {
	User      string
	PrivateKey PrivateKey
	Algorithm string
}

type Credentials struct {
	UserAuthentication *Auth
	SdcKeyId           string
	SdcEndpoint        Endpoint
	MantaKeyId         string
	MantaEndpoint      Endpoint
}

type PrivateKey struct {
	key *rsa.PrivateKey
}

// NewAuth creates a new Auth.
func NewAuth(user, privateKey, algorithm string) (*Auth, error) {
	block, _ := pem.Decode([]byte(privateKey))
	if block == nil {
		return nil, fmt.Errorf("invalid private key data: %s", privateKey)
	}
	rsakey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
	if err != nil {
		return nil, fmt.Errorf("An error occurred while parsing the key: %s", err)
	}
	return &Auth{user, PrivateKey{rsakey}, algorithm}, nil
}

// The CreateAuthorizationHeader returns the Authorization header for the give request.
func CreateAuthorizationHeader(headers http.Header, credentials *Credentials, isMantaRequest bool) (string, error) {
	if isMantaRequest {
		signature, err := GetSignature(credentials.UserAuthentication, "date: "+headers.Get("Date"))
		if err != nil {
			return "", err
		}
		return fmt.Sprintf(MantaSignature, credentials.UserAuthentication.User, credentials.MantaKeyId,
			credentials.UserAuthentication.Algorithm, signature), nil
	}
	signature, err := GetSignature(credentials.UserAuthentication, headers.Get("Date"))
	if err != nil {
		return "", err
	}
	return fmt.Sprintf(SdcSignature, credentials.UserAuthentication.User, credentials.SdcKeyId,
		credentials.UserAuthentication.Algorithm, signature), nil
}

// The GetSignature method signs the specified key according to http://apidocs.joyent.com/cloudapi/#issuing-requests
// and http://apidocs.joyent.com/manta/api.html#authentication.
func GetSignature(auth *Auth, signing string) (string, error) {
	hashFunc := getHashFunction(auth.Algorithm)
	hash := hashFunc.New()
	hash.Write([]byte(signing))

	digest := hash.Sum(nil)

	signed, err := rsa.SignPKCS1v15(rand.Reader, auth.PrivateKey.key, hashFunc, digest)
	if err != nil {
		return "", fmt.Errorf("An error occurred while signing the key: %s", err)
	}

	return base64.StdEncoding.EncodeToString(signed), nil
}

// Helper method to get the Hash function based on the algorithm
func getHashFunction(algorithm string) (hashFunc crypto.Hash) {
	switch strings.ToLower(algorithm) {
	case "rsa-sha1":
		hashFunc = crypto.SHA1
	case "rsa-sha224", "rsa-sha256":
		hashFunc = crypto.SHA256
	case "rsa-sha384", "rsa-sha512":
		hashFunc = crypto.SHA512
	default:
		hashFunc = crypto.SHA256
	}
	return
}

func (cred *Credentials) Region() string {
	parsedUrl, err := url.Parse(cred.SdcEndpoint.URL)
	if err != nil {
		// Bogus URL - no region.
		return ""
	}
	if strings.HasPrefix(parsedUrl.Host, "localhost") || strings.HasPrefix(parsedUrl.Host, "127.0.0.1") {
		return "some-region"
	}

	host := parsedUrl.Host
	firstDotIdx := strings.Index(host, ".")
	if firstDotIdx >= 0 {
		return host[:firstDotIdx]
	}
	return host
}