5 ;;; http://docs.aws.amazon.com/general/latest/gr/rande.html#s3_region
6 ;;; can be used to map endpoint to region, maybe?
8 (defun headers-for-signing (request)
9 (append (all-amazon-headers request
)
10 (extra-http-headers request
)
11 (parameters-alist "host" (host request
)
12 "content-type" (content-type request
))))
14 (defun canonical-headers (headers)
16 (string-trim " " string
)))
18 (loop for
(name . value
) in headers
19 collect
(cons (string-downcase name
)
21 (sort encoded
#'string
< :key
'car
))))
23 (defun signed-headers (request)
24 (mapcar 'first
(canonical-headers (headers-for-signing request
))))
26 (defun parameters-for-signing (request)
27 (cond ((sub-resource request
)
28 (list (cons (sub-resource request
) "")))
30 (parameters request
))))
32 (defun canonical-parameters (parameters)
34 (loop for
(name . value
) in parameters
37 (url-encode value
)))))
38 (sort encoded
#'string
< :key
'car
)))
40 (defun canonical-parameters-string (request)
41 (format nil
"~{~A=~A~^&~}"
42 (alist-plist (canonical-parameters
43 (parameters-for-signing request
)))))
45 (defun hashed-payload (request)
46 *empty-string-sha256
*)
48 (defun path-to-sign (request)
49 "Everything in the PATH of the request, up to the first ?"
50 (let ((path (request-path request
)))
51 (subseq path
0 (position #\? path
))))
53 (defun canonicalized-request-strings (request)
54 "Return a list of lines canonicalizing the request according to
55 http://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-header-based-auth.html."
56 (let* ((headers (headers-for-signing request
))
57 (canonical-headers (canonical-headers headers
)))
59 (list (http-method request
)
60 (path-to-sign request
)
61 (canonical-parameters-string request
)
62 (loop for
(name . value
) in canonical-headers
63 collect
(format nil
"~A:~A" name value
))
65 (format nil
"~{~A~^;~}" (mapcar 'first canonical-headers
))
66 (hashed-payload request
)))))
68 (defun string-to-sign-lines (request)
69 "Return a list of strings to sign to construc"
70 (list "AWS4-HMAC-SHA256"
71 (iso8601-basic-timestamp-string)
72 (with-output-to-string (s)
73 (format s
"~A/~A/s3/aws4_request"
74 (iso8601-basic-date-string)
76 (strings-sha256/hex
(canonicalized-request-strings request
))))
78 (defun make-signing-key (credentials &key region service
)
79 (let* ((k1 (format nil
"AWS4~A" (secret-key credentials
)))
80 (date-key (hmac-sha256 k1
(iso8601-basic-date-string)))
81 (region-key (hmac-sha256 date-key region
))
82 (service-key (hmac-sha256 region-key service
)))
83 (hmac-sha256 service-key
"aws4_request")))
85 (defclass aws4-auth-request
(request)
92 (defmethod authorization-header-value ((request aws4-auth-request
))
93 (let ((key (make-signing-key *credentials
*
94 :region
(region request
)
96 (with-output-to-string (s)
97 (write-string "AWS4-HMAC-SHA256" s
)
98 (format s
" Credential=~A/~A/~A/s3/aws4_request"
99 (access-key *credentials
*)
100 (iso8601-basic-date-string)
102 (format s
",SignedHeaders=~{~A~^;~}" (signed-headers request
))
103 (format s
",Signature=~A"
104 (strings-hmac-sha256/hex key
(string-to-sign-lines request
))))))
106 (defun test-aws4 (&key
(region "us-east-1"))
107 (let ((request (make-instance 'aws4-auth-request
109 (parameters-alist :content-sha256
*empty-string-sha256
*
110 :date
(iso8601-basic-timestamp-string))