You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

4 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536
  1. // Copyright 2019 Huawei Technologies Co.,Ltd.
  2. // Licensed under the Apache License, Version 2.0 (the "License"); you may not use
  3. // this file except in compliance with the License. You may obtain a copy of the
  4. // License at
  5. //
  6. // http://www.apache.org/licenses/LICENSE-2.0
  7. //
  8. // Unless required by applicable law or agreed to in writing, software distributed
  9. // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
  10. // CONDITIONS OF ANY KIND, either express or implied. See the License for the
  11. // specific language governing permissions and limitations under the License.
  12. //nolint:golint, unused
  13. package obs
  14. import (
  15. "crypto/hmac"
  16. "crypto/md5"
  17. "crypto/sha1"
  18. "crypto/sha256"
  19. "encoding/base64"
  20. "encoding/hex"
  21. "encoding/json"
  22. "encoding/xml"
  23. "fmt"
  24. "net/url"
  25. "regexp"
  26. "strconv"
  27. "strings"
  28. "time"
  29. )
  30. var regex = regexp.MustCompile("^[\u4e00-\u9fa5]$")
  31. var ipRegex = regexp.MustCompile("^((2[0-4]\\d|25[0-5]|[01]?\\d\\d?)\\.){3}(2[0-4]\\d|25[0-5]|[01]?\\d\\d?)$")
  32. var v4AuthRegex = regexp.MustCompile("Credential=(.+?),SignedHeaders=(.+?),Signature=.+")
  33. var regionRegex = regexp.MustCompile(".+/\\d+/(.+?)/.+")
  34. // StringContains replaces subStr in src with subTranscoding and returns the new string
  35. func StringContains(src string, subStr string, subTranscoding string) string {
  36. return strings.Replace(src, subStr, subTranscoding, -1)
  37. }
  38. // XmlTranscoding replaces special characters with their escaped form
  39. func XmlTranscoding(src string) string {
  40. srcTmp := StringContains(src, "&", "&")
  41. srcTmp = StringContains(srcTmp, "<", "&lt;")
  42. srcTmp = StringContains(srcTmp, ">", "&gt;")
  43. srcTmp = StringContains(srcTmp, "'", "&apos;")
  44. srcTmp = StringContains(srcTmp, "\"", "&quot;")
  45. return srcTmp
  46. }
  47. // StringToInt converts string value to int value with default value
  48. func StringToInt(value string, def int) int {
  49. ret, err := strconv.Atoi(value)
  50. if err != nil {
  51. ret = def
  52. }
  53. return ret
  54. }
  55. // StringToInt64 converts string value to int64 value with default value
  56. func StringToInt64(value string, def int64) int64 {
  57. ret, err := strconv.ParseInt(value, 10, 64)
  58. if err != nil {
  59. ret = def
  60. }
  61. return ret
  62. }
  63. // IntToString converts int value to string value
  64. func IntToString(value int) string {
  65. return strconv.Itoa(value)
  66. }
  67. // Int64ToString converts int64 value to string value
  68. func Int64ToString(value int64) string {
  69. return strconv.FormatInt(value, 10)
  70. }
  71. // GetCurrentTimestamp gets unix time in milliseconds
  72. func GetCurrentTimestamp() int64 {
  73. return time.Now().UnixNano() / 1000000
  74. }
  75. // FormatUtcNow gets a textual representation of the UTC format time value
  76. func FormatUtcNow(format string) string {
  77. return time.Now().UTC().Format(format)
  78. }
  79. // FormatUtcToRfc1123 gets a textual representation of the RFC1123 format time value
  80. func FormatUtcToRfc1123(t time.Time) string {
  81. ret := t.UTC().Format(time.RFC1123)
  82. return ret[:strings.LastIndex(ret, "UTC")] + "GMT"
  83. }
  84. // Md5 gets the md5 value of input
  85. func Md5(value []byte) []byte {
  86. m := md5.New()
  87. _, err := m.Write(value)
  88. if err != nil {
  89. doLog(LEVEL_WARN, "MD5 failed to write")
  90. }
  91. return m.Sum(nil)
  92. }
  93. // HmacSha1 gets hmac sha1 value of input
  94. func HmacSha1(key, value []byte) []byte {
  95. mac := hmac.New(sha1.New, key)
  96. _, err := mac.Write(value)
  97. if err != nil {
  98. doLog(LEVEL_WARN, "HmacSha1 failed to write")
  99. }
  100. return mac.Sum(nil)
  101. }
  102. // HmacSha256 get hmac sha256 value if input
  103. func HmacSha256(key, value []byte) []byte {
  104. mac := hmac.New(sha256.New, key)
  105. _, err := mac.Write(value)
  106. if err != nil {
  107. doLog(LEVEL_WARN, "HmacSha256 failed to write")
  108. }
  109. return mac.Sum(nil)
  110. }
  111. // Base64Encode wrapper of base64.StdEncoding.EncodeToString
  112. func Base64Encode(value []byte) string {
  113. return base64.StdEncoding.EncodeToString(value)
  114. }
  115. // Base64Decode wrapper of base64.StdEncoding.DecodeString
  116. func Base64Decode(value string) ([]byte, error) {
  117. return base64.StdEncoding.DecodeString(value)
  118. }
  119. // HexMd5 returns the md5 value of input in hexadecimal format
  120. func HexMd5(value []byte) string {
  121. return Hex(Md5(value))
  122. }
  123. // Base64Md5 returns the md5 value of input with Base64Encode
  124. func Base64Md5(value []byte) string {
  125. return Base64Encode(Md5(value))
  126. }
  127. // Sha256Hash returns sha256 checksum
  128. func Sha256Hash(value []byte) []byte {
  129. hash := sha256.New()
  130. _, err := hash.Write(value)
  131. if err != nil {
  132. doLog(LEVEL_WARN, "Sha256Hash failed to write")
  133. }
  134. return hash.Sum(nil)
  135. }
  136. // ParseXml wrapper of xml.Unmarshal
  137. func ParseXml(value []byte, result interface{}) error {
  138. if len(value) == 0 {
  139. return nil
  140. }
  141. return xml.Unmarshal(value, result)
  142. }
  143. // parseJSON wrapper of json.Unmarshal
  144. func parseJSON(value []byte, result interface{}) error {
  145. if len(value) == 0 {
  146. return nil
  147. }
  148. return json.Unmarshal(value, result)
  149. }
  150. // TransToXml wrapper of xml.Marshal
  151. func TransToXml(value interface{}) ([]byte, error) {
  152. if value == nil {
  153. return []byte{}, nil
  154. }
  155. return xml.Marshal(value)
  156. }
  157. // Hex wrapper of hex.EncodeToString
  158. func Hex(value []byte) string {
  159. return hex.EncodeToString(value)
  160. }
  161. // HexSha256 returns the Sha256Hash value of input in hexadecimal format
  162. func HexSha256(value []byte) string {
  163. return Hex(Sha256Hash(value))
  164. }
  165. // UrlDecode wrapper of url.QueryUnescape
  166. func UrlDecode(value string) (string, error) {
  167. ret, err := url.QueryUnescape(value)
  168. if err == nil {
  169. return ret, nil
  170. }
  171. return "", err
  172. }
  173. // UrlDecodeWithoutError wrapper of UrlDecode
  174. func UrlDecodeWithoutError(value string) string {
  175. ret, err := UrlDecode(value)
  176. if err == nil {
  177. return ret
  178. }
  179. if isErrorLogEnabled() {
  180. doLog(LEVEL_ERROR, "Url decode error")
  181. }
  182. return ""
  183. }
  184. // IsIP checks whether the value matches ip address
  185. func IsIP(value string) bool {
  186. return ipRegex.MatchString(value)
  187. }
  188. // UrlEncode encodes the input value
  189. func UrlEncode(value string, chineseOnly bool) string {
  190. if chineseOnly {
  191. values := make([]string, 0, len(value))
  192. for _, val := range value {
  193. _value := string(val)
  194. if regex.MatchString(_value) {
  195. _value = url.QueryEscape(_value)
  196. }
  197. values = append(values, _value)
  198. }
  199. return strings.Join(values, "")
  200. }
  201. return url.QueryEscape(value)
  202. }
  203. func copyHeaders(m map[string][]string) (ret map[string][]string) {
  204. if m != nil {
  205. ret = make(map[string][]string, len(m))
  206. for key, values := range m {
  207. _values := make([]string, 0, len(values))
  208. for _, value := range values {
  209. _values = append(_values, value)
  210. }
  211. ret[strings.ToLower(key)] = _values
  212. }
  213. } else {
  214. ret = make(map[string][]string)
  215. }
  216. return
  217. }
  218. func parseHeaders(headers map[string][]string) (signature string, region string, signedHeaders string) {
  219. signature = "v2"
  220. if receviedAuthorization, ok := headers[strings.ToLower(HEADER_AUTH_CAMEL)]; ok && len(receviedAuthorization) > 0 {
  221. if strings.HasPrefix(receviedAuthorization[0], V4_HASH_PREFIX) {
  222. signature = "v4"
  223. matches := v4AuthRegex.FindStringSubmatch(receviedAuthorization[0])
  224. if len(matches) >= 3 {
  225. region = matches[1]
  226. regions := regionRegex.FindStringSubmatch(region)
  227. if len(regions) >= 2 {
  228. region = regions[1]
  229. }
  230. signedHeaders = matches[2]
  231. }
  232. } else if strings.HasPrefix(receviedAuthorization[0], V2_HASH_PREFIX) {
  233. signature = "v2"
  234. }
  235. }
  236. return
  237. }
  238. func getTemporaryKeys() []string {
  239. return []string{
  240. "Signature",
  241. "signature",
  242. "X-Amz-Signature",
  243. "x-amz-signature",
  244. }
  245. }
  246. func getIsObs(isTemporary bool, querys []string, headers map[string][]string) bool {
  247. isObs := true
  248. if isTemporary {
  249. for _, value := range querys {
  250. keyPrefix := strings.ToLower(value)
  251. if strings.HasPrefix(keyPrefix, HEADER_PREFIX) {
  252. isObs = false
  253. } else if strings.HasPrefix(value, HEADER_ACCESSS_KEY_AMZ) {
  254. isObs = false
  255. }
  256. }
  257. } else {
  258. for key := range headers {
  259. keyPrefix := strings.ToLower(key)
  260. if strings.HasPrefix(keyPrefix, HEADER_PREFIX) {
  261. isObs = false
  262. break
  263. }
  264. }
  265. }
  266. return isObs
  267. }
  268. func isPathStyle(headers map[string][]string, bucketName string) bool {
  269. if receviedHost, ok := headers[HEADER_HOST]; ok && len(receviedHost) > 0 && !strings.HasPrefix(receviedHost[0], bucketName+".") {
  270. return true
  271. }
  272. return false
  273. }
  274. // GetV2Authorization v2 Authorization
  275. func GetV2Authorization(ak, sk, method, bucketName, objectKey, queryURL string, headers map[string][]string) (ret map[string]string) {
  276. if strings.HasPrefix(queryURL, "?") {
  277. queryURL = queryURL[1:]
  278. }
  279. method = strings.ToUpper(method)
  280. querys := strings.Split(queryURL, "&")
  281. querysResult := make([]string, 0)
  282. for _, value := range querys {
  283. if value != "=" && len(value) != 0 {
  284. querysResult = append(querysResult, value)
  285. }
  286. }
  287. params := make(map[string]string)
  288. for _, value := range querysResult {
  289. kv := strings.Split(value, "=")
  290. length := len(kv)
  291. if length == 1 {
  292. key := UrlDecodeWithoutError(kv[0])
  293. params[key] = ""
  294. } else if length >= 2 {
  295. key := UrlDecodeWithoutError(kv[0])
  296. vals := make([]string, 0, length-1)
  297. for i := 1; i < length; i++ {
  298. val := UrlDecodeWithoutError(kv[i])
  299. vals = append(vals, val)
  300. }
  301. params[key] = strings.Join(vals, "=")
  302. }
  303. }
  304. headers = copyHeaders(headers)
  305. pathStyle := isPathStyle(headers, bucketName)
  306. conf := &config{securityProvider: &securityProvider{ak: ak, sk: sk},
  307. urlHolder: &urlHolder{scheme: "https", host: "dummy", port: 443},
  308. pathStyle: pathStyle}
  309. conf.signature = SignatureObs
  310. _, canonicalizedURL := conf.formatUrls(bucketName, objectKey, params, false)
  311. ret = v2Auth(ak, sk, method, canonicalizedURL, headers, true)
  312. v2HashPrefix := OBS_HASH_PREFIX
  313. ret[HEADER_AUTH_CAMEL] = fmt.Sprintf("%s %s:%s", v2HashPrefix, ak, ret["Signature"])
  314. return
  315. }
  316. // GetAuthorization Authorization
  317. func GetAuthorization(ak, sk, method, bucketName, objectKey, queryURL string, headers map[string][]string) (ret map[string]string) {
  318. if strings.HasPrefix(queryURL, "?") {
  319. queryURL = queryURL[1:]
  320. }
  321. method = strings.ToUpper(method)
  322. querys := strings.Split(queryURL, "&")
  323. querysResult := make([]string, 0)
  324. for _, value := range querys {
  325. if value != "=" && len(value) != 0 {
  326. querysResult = append(querysResult, value)
  327. }
  328. }
  329. params := make(map[string]string)
  330. for _, value := range querysResult {
  331. kv := strings.Split(value, "=")
  332. length := len(kv)
  333. if length == 1 {
  334. key := UrlDecodeWithoutError(kv[0])
  335. params[key] = ""
  336. } else if length >= 2 {
  337. key := UrlDecodeWithoutError(kv[0])
  338. vals := make([]string, 0, length-1)
  339. for i := 1; i < length; i++ {
  340. val := UrlDecodeWithoutError(kv[i])
  341. vals = append(vals, val)
  342. }
  343. params[key] = strings.Join(vals, "=")
  344. }
  345. }
  346. isTemporary := false
  347. signature := "v2"
  348. temporaryKeys := getTemporaryKeys()
  349. for _, key := range temporaryKeys {
  350. if _, ok := params[key]; ok {
  351. isTemporary = true
  352. if strings.ToLower(key) == "signature" {
  353. signature = "v2"
  354. } else if strings.ToLower(key) == "x-amz-signature" {
  355. signature = "v4"
  356. }
  357. break
  358. }
  359. }
  360. isObs := getIsObs(isTemporary, querysResult, headers)
  361. headers = copyHeaders(headers)
  362. pathStyle := false
  363. if receviedHost, ok := headers[HEADER_HOST]; ok && len(receviedHost) > 0 && !strings.HasPrefix(receviedHost[0], bucketName+".") {
  364. pathStyle = true
  365. }
  366. conf := &config{securityProvider: &securityProvider{ak: ak, sk: sk},
  367. urlHolder: &urlHolder{scheme: "https", host: "dummy", port: 443},
  368. pathStyle: pathStyle}
  369. if isTemporary {
  370. return getTemporaryAuthorization(ak, sk, method, bucketName, objectKey, signature, conf, params, headers, isObs)
  371. }
  372. signature, region, signedHeaders := parseHeaders(headers)
  373. if signature == "v4" {
  374. conf.signature = SignatureV4
  375. requestURL, canonicalizedURL := conf.formatUrls(bucketName, objectKey, params, false)
  376. parsedRequestURL, _err := url.Parse(requestURL)
  377. if _err != nil {
  378. doLog(LEVEL_WARN, "Failed to parse requestURL")
  379. return nil
  380. }
  381. headerKeys := strings.Split(signedHeaders, ";")
  382. _headers := make(map[string][]string, len(headerKeys))
  383. for _, headerKey := range headerKeys {
  384. _headers[headerKey] = headers[headerKey]
  385. }
  386. ret = v4Auth(ak, sk, region, method, canonicalizedURL, parsedRequestURL.RawQuery, _headers)
  387. ret[HEADER_AUTH_CAMEL] = fmt.Sprintf("%s Credential=%s,SignedHeaders=%s,Signature=%s", V4_HASH_PREFIX, ret["Credential"], ret["SignedHeaders"], ret["Signature"])
  388. } else if signature == "v2" {
  389. if isObs {
  390. conf.signature = SignatureObs
  391. } else {
  392. conf.signature = SignatureV2
  393. }
  394. _, canonicalizedURL := conf.formatUrls(bucketName, objectKey, params, false)
  395. ret = v2Auth(ak, sk, method, canonicalizedURL, headers, isObs)
  396. v2HashPrefix := V2_HASH_PREFIX
  397. if isObs {
  398. v2HashPrefix = OBS_HASH_PREFIX
  399. }
  400. ret[HEADER_AUTH_CAMEL] = fmt.Sprintf("%s %s:%s", v2HashPrefix, ak, ret["Signature"])
  401. }
  402. return
  403. }
  404. func getTemporaryAuthorization(ak, sk, method, bucketName, objectKey, signature string, conf *config, params map[string]string,
  405. headers map[string][]string, isObs bool) (ret map[string]string) {
  406. if signature == "v4" {
  407. conf.signature = SignatureV4
  408. longDate, ok := params[PARAM_DATE_AMZ_CAMEL]
  409. if !ok {
  410. longDate = params[HEADER_DATE_AMZ]
  411. }
  412. shortDate := longDate[:8]
  413. credential, ok := params[PARAM_CREDENTIAL_AMZ_CAMEL]
  414. if !ok {
  415. credential = params[strings.ToLower(PARAM_CREDENTIAL_AMZ_CAMEL)]
  416. }
  417. _credential := UrlDecodeWithoutError(credential)
  418. regions := regionRegex.FindStringSubmatch(_credential)
  419. var region string
  420. if len(regions) >= 2 {
  421. region = regions[1]
  422. }
  423. _, scope := getCredential(ak, region, shortDate)
  424. expires, ok := params[PARAM_EXPIRES_AMZ_CAMEL]
  425. if !ok {
  426. expires = params[strings.ToLower(PARAM_EXPIRES_AMZ_CAMEL)]
  427. }
  428. signedHeaders, ok := params[PARAM_SIGNEDHEADERS_AMZ_CAMEL]
  429. if !ok {
  430. signedHeaders = params[strings.ToLower(PARAM_SIGNEDHEADERS_AMZ_CAMEL)]
  431. }
  432. algorithm, ok := params[PARAM_ALGORITHM_AMZ_CAMEL]
  433. if !ok {
  434. algorithm = params[strings.ToLower(PARAM_ALGORITHM_AMZ_CAMEL)]
  435. }
  436. if _, ok := params[PARAM_SIGNATURE_AMZ_CAMEL]; ok {
  437. delete(params, PARAM_SIGNATURE_AMZ_CAMEL)
  438. } else if _, ok := params[strings.ToLower(PARAM_SIGNATURE_AMZ_CAMEL)]; ok {
  439. delete(params, strings.ToLower(PARAM_SIGNATURE_AMZ_CAMEL))
  440. }
  441. ret = make(map[string]string, 6)
  442. ret[PARAM_ALGORITHM_AMZ_CAMEL] = algorithm
  443. ret[PARAM_CREDENTIAL_AMZ_CAMEL] = credential
  444. ret[PARAM_DATE_AMZ_CAMEL] = longDate
  445. ret[PARAM_EXPIRES_AMZ_CAMEL] = expires
  446. ret[PARAM_SIGNEDHEADERS_AMZ_CAMEL] = signedHeaders
  447. requestURL, canonicalizedURL := conf.formatUrls(bucketName, objectKey, params, false)
  448. parsedRequestURL, _err := url.Parse(requestURL)
  449. if _err != nil {
  450. doLog(LEVEL_WARN, "Failed to parse requestUrl")
  451. return nil
  452. }
  453. stringToSign := getV4StringToSign(method, canonicalizedURL, parsedRequestURL.RawQuery, scope, longDate, UNSIGNED_PAYLOAD, strings.Split(signedHeaders, ";"), headers)
  454. ret[PARAM_SIGNATURE_AMZ_CAMEL] = UrlEncode(getSignature(stringToSign, sk, region, shortDate), false)
  455. } else if signature == "v2" {
  456. if isObs {
  457. conf.signature = SignatureObs
  458. } else {
  459. conf.signature = SignatureV2
  460. }
  461. _, canonicalizedURL := conf.formatUrls(bucketName, objectKey, params, false)
  462. expires, ok := params["Expires"]
  463. if !ok {
  464. expires = params["expires"]
  465. }
  466. headers[HEADER_DATE_CAMEL] = []string{expires}
  467. stringToSign := getV2StringToSign(method, canonicalizedURL, headers, isObs)
  468. ret = make(map[string]string, 3)
  469. ret["Signature"] = UrlEncode(Base64Encode(HmacSha1([]byte(sk), []byte(stringToSign))), false)
  470. ret["AWSAccessKeyId"] = UrlEncode(ak, false)
  471. ret["Expires"] = UrlEncode(expires, false)
  472. }
  473. return
  474. }