1 package org.oxerr.peatio.rest.service;
2
3 import java.lang.annotation.Annotation;
4 import java.lang.reflect.Field;
5 import java.nio.charset.StandardCharsets;
6 import java.util.Map;
7 import java.util.SortedMap;
8 import java.util.TreeMap;
9
10 import javax.crypto.Mac;
11 import javax.ws.rs.FormParam;
12 import javax.ws.rs.QueryParam;
13 import javax.ws.rs.core.UriBuilder;
14
15 import org.slf4j.Logger;
16 import org.slf4j.LoggerFactory;
17
18 import si.mazi.rescu.Params;
19 import si.mazi.rescu.ParamsDigest;
20 import si.mazi.rescu.RestInvocation;
21
22 import com.xeiam.xchange.service.BaseParamsDigest;
23 import com.xeiam.xchange.utils.DigestUtils;
24
25
26
27
28 public class PeatioDigest extends BaseParamsDigest {
29
30 private final Logger log = LoggerFactory.getLogger(PeatioDigest.class);
31 private final Field invocationUrlField;
32
33 public PeatioDigest(String secretKey)
34 throws IllegalArgumentException {
35 super(secretKey, HMAC_SHA_256);
36
37 try {
38 invocationUrlField = RestInvocation.class.getDeclaredField("invocationUrl");
39 invocationUrlField.setAccessible(true);
40 } catch (NoSuchFieldException | SecurityException e) {
41 throw new RuntimeException(e);
42 }
43 }
44
45
46
47
48 @Override
49 public String digestParams(RestInvocation restInvocation) {
50 final String verb = restInvocation.getHttpMethod();
51 final String uri = restInvocation.getPath();
52 final String query = getSortedParams(restInvocation);
53
54 final String payload = String.join("|", verb, uri, query);
55 log.debug("payload: {}", payload);
56
57 String signature = sign(payload);
58
59
60
61 String invocationUrl = restInvocation.getInvocationUrl();
62 log.debug("old invocationUrl: {}", invocationUrl);
63 String newInvocationUrl = UriBuilder.fromUri(invocationUrl).replaceQueryParam("signature", signature).build().toString();
64 try {
65 invocationUrlField.set(restInvocation, newInvocationUrl);
66 } catch (IllegalArgumentException | IllegalAccessException e) {
67 throw new RuntimeException(e);
68 }
69 log.debug("new invocationUrl: {}", restInvocation.getInvocationUrl());
70
71 return signature;
72 }
73
74 public String sign(String payload) {
75 Mac mac = getMac();
76 byte[] hash = mac.doFinal(payload.getBytes(StandardCharsets.UTF_8));
77 String signature = DigestUtils.bytesToHex(hash).toLowerCase();
78 return signature;
79 }
80
81 private String getSortedParams(RestInvocation restInvocation) {
82 final Map<Class<? extends Annotation>, Params> paramsMap = restInvocation.getParamsMap();
83
84 final Params queryParams = paramsMap.get(QueryParam.class);
85 final Params formParams = paramsMap.get(FormParam.class);
86
87 final SortedMap<String, String> sortedQueryParams = new TreeMap<>();
88 sortedQueryParams.putAll(queryParams.asHttpHeaders());
89 sortedQueryParams.putAll(formParams.asHttpHeaders());
90
91 final Params sortedParams = Params.of();
92 for (Map.Entry<String, String> param : sortedQueryParams.entrySet()) {
93 if (!param.getKey().equals("signature")) {
94 sortedParams.add(param.getKey(), param.getValue());
95 }
96 }
97
98 return sortedParams.asQueryString();
99 }
100
101 }