forked from lnproxy/lnproxy-relay
-
Notifications
You must be signed in to change notification settings - Fork 0
/
header.go
143 lines (126 loc) · 4.24 KB
/
header.go
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
133
134
135
136
137
138
139
140
141
142
143
package lsat
import (
"encoding/base64"
"encoding/hex"
"errors"
"fmt"
"net/http"
"regexp"
"github.com/lightningnetwork/lnd/lntypes"
"gopkg.in/macaroon.v2"
)
const (
// HeaderAuthorization is the HTTP header field name that is used to
// send the LSAT by REST clients.
HeaderAuthorization = "Authorization"
// HeaderMacaroonMD is the HTTP header field name that is used to send
// the LSAT by certain REST and gRPC clients.
HeaderMacaroonMD = "Grpc-Metadata-Macaroon"
// HeaderMacaroon is the HTTP header field name that is used to send the
// LSAT by our own gRPC clients.
HeaderMacaroon = "Macaroon"
)
var (
authRegex = regexp.MustCompile("LSAT (.*?):([a-f0-9]{64})")
authFormat = "LSAT %s:%s"
)
// FromHeader tries to extract authentication information from HTTP headers.
// There are two supported formats that can be sent in three different header
// fields:
// 1. Authorization: LSAT <macBase64>:<preimageHex>
// 2. Grpc-Metadata-Macaroon: <macHex>
// 3. Macaroon: <macHex>
//
// If only the macaroon is sent in header 2 or three then it is expected to have
// a caveat with the preimage attached to it.
func FromHeader(header *http.Header) (*macaroon.Macaroon, lntypes.Preimage, error) {
var authHeader string
switch {
// Header field 1 contains the macaroon and the preimage as distinct
// values separated by a colon.
case header.Get(HeaderAuthorization) != "":
// Parse the content of the header field and check that it is in
// the correct format.
authHeader = header.Get(HeaderAuthorization)
log.Debugf("Trying to authorize with header value [%s].",
authHeader)
if !authRegex.MatchString(authHeader) {
return nil, lntypes.Preimage{}, fmt.Errorf("invalid "+
"auth header format: %s", authHeader)
}
matches := authRegex.FindStringSubmatch(authHeader)
if len(matches) != 3 {
return nil, lntypes.Preimage{}, fmt.Errorf("invalid "+
"auth header format: %s", authHeader)
}
// Decode the content of the two parts of the header value.
macBase64, preimageHex := matches[1], matches[2]
macBytes, err := base64.StdEncoding.DecodeString(macBase64)
if err != nil {
return nil, lntypes.Preimage{}, fmt.Errorf("base64 "+
"decode of macaroon failed: %v", err)
}
mac := &macaroon.Macaroon{}
err = mac.UnmarshalBinary(macBytes)
if err != nil {
return nil, lntypes.Preimage{}, fmt.Errorf("unable to "+
"unmarshal macaroon: %v", err)
}
preimage, err := lntypes.MakePreimageFromStr(preimageHex)
if err != nil {
return nil, lntypes.Preimage{}, fmt.Errorf("hex "+
"decode of preimage failed: %v", err)
}
// All done, we don't need to extract anything from the
// macaroon since the preimage was presented separately.
return mac, preimage, nil
// Header field 2: Contains only the macaroon.
case header.Get(HeaderMacaroonMD) != "":
authHeader = header.Get(HeaderMacaroonMD)
// Header field 3: Contains only the macaroon.
case header.Get(HeaderMacaroon) != "":
authHeader = header.Get(HeaderMacaroon)
default:
return nil, lntypes.Preimage{}, fmt.Errorf("no auth header " +
"provided")
}
// For case 2 and 3, we need to actually unmarshal the macaroon to
// extract the preimage.
macBytes, err := hex.DecodeString(authHeader)
if err != nil {
return nil, lntypes.Preimage{}, fmt.Errorf("hex decode of "+
"macaroon failed: %v", err)
}
mac := &macaroon.Macaroon{}
err = mac.UnmarshalBinary(macBytes)
if err != nil {
return nil, lntypes.Preimage{}, fmt.Errorf("unable to "+
"unmarshal macaroon: %v", err)
}
preimageHex, ok := HasCaveat(mac, PreimageKey)
if !ok {
return nil, lntypes.Preimage{}, errors.New("preimage caveat " +
"not found")
}
preimage, err := lntypes.MakePreimageFromStr(preimageHex)
if err != nil {
return nil, lntypes.Preimage{}, fmt.Errorf("hex decode of "+
"preimage failed: %v", err)
}
return mac, preimage, nil
}
// SetHeader sets the provided authentication elements as the default/standard
// HTTP header for the LSAT protocol.
func SetHeader(header *http.Header, mac *macaroon.Macaroon,
preimage fmt.Stringer) error {
macBytes, err := mac.MarshalBinary()
if err != nil {
return err
}
value := fmt.Sprintf(
authFormat, base64.StdEncoding.EncodeToString(macBytes),
preimage.String(),
)
header.Set(HeaderAuthorization, value)
return nil
}