Token.php 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. <?php
  2. namespace ACES\Common;
  3. use ACES\Common\domain\JosBaseInfo;
  4. use ACES\Common\domain\JosBaseResponse;
  5. use ACES\Common\domain\JosVoucherInfoGetRequest;
  6. use ACES\Common\domain\JosVoucherInfoGetResponse;
  7. use ACES\Common\Exception\InvalidTokenException;
  8. use ACES\Common\Exception\JosGwException;
  9. use ACES\Common\Exception\MalformedException;
  10. use ACES\Common\Exception\VoucherInfoGetException;
  11. use Exception;
  12. class Token
  13. {
  14. private $label; // label, could be create, update, and other types
  15. private $effectiveTs; // token active timestamp, unix time format
  16. private $expiredTs; // token expired timesatmp, unix time format
  17. private $id; // token identifier, encoded in Base64
  18. private $key; // token credential, symmetric key for HMAC
  19. private $service = "Unknown"; // token major service
  20. private $stype; // service type, 0 for IDC, 1 for Beta,
  21. private $isVerify = false; // token is verified or not
  22. private $zone = "CN-0"; // zone field, default value is CN-0 if not assigned
  23. // used to verify token signature
  24. private static $scert = null; // X509Certificate
  25. // Utility for TokenCipher/TokenSignature
  26. private $se; // Mac
  27. private $de; // DataEncryption
  28. /**
  29. * Token constructor to initialize itself by given certain fields,
  30. * including label, identifier, key, service name, effective and
  31. * expired time stamp, and issue type (stype, online or offline).
  32. * <p>
  33. *
  34. * @param string $label token label
  35. * @param string $id token identifier byte array
  36. * @param string $key token symmetric key byte array
  37. * @param int $effectiveTs token effective timestamp
  38. * @param int $expiredTs token expired timestamp
  39. * @param int $stype operation type
  40. * @param string $service token major service name (for encryption)
  41. * @param string $zone
  42. *
  43. */
  44. private function __construct($label, $id, $key, $effectiveTs,
  45. $expiredTs, $stype, $service, $zone)
  46. {
  47. $this->label = $label;
  48. $this->effectiveTs = $effectiveTs;
  49. $this->expiredTs = $expiredTs;
  50. $this->id = $id;
  51. $this->key = $key;
  52. $this->service = $service;
  53. $this->stype = $stype;
  54. $this->isVerify = TRUE;
  55. if($zone !=null) $this->zone = $zone;
  56. $this->de = new DataEncryption($key);
  57. }
  58. /* Return Token object from parsing token string
  59. *
  60. * @param string $input
  61. * @param bool $isProd
  62. *
  63. * @return Token
  64. */
  65. public static function parseFromString($input, $isProd)
  66. {
  67. // parse token json string
  68. $json = json_decode($input);
  69. // get token data sig
  70. $sigbytes = base64_decode($json->sig, TRUE);
  71. $d = $json->data;
  72. $label = $d->act;
  73. $startTs = $d->effective;
  74. $endTs = $d->expired;
  75. $id = $d->id;
  76. $key = base64_decode($d->key, TRUE);
  77. $service = $d->service;
  78. $sType = $d->stype;
  79. $zone = NULL;
  80. // get external structure, new feature
  81. if(!empty($json->externalData)){
  82. $zone = $json->externalData->zone;
  83. }
  84. // for dummy check
  85. if($sType == ORIGIN::BETA || $sType == ORIGIN::DEV){
  86. if($isProd) throw new MalformedException("token source type does not match the isProd flag.");
  87. }else if($sType == ORIGIN::IDC){
  88. if(!$isProd) throw new MalformedException("token source type does not match the isProd flag.");
  89. }
  90. // load certificate
  91. $pemdata = $isProd ? Constants::TMS_PROD_TOKEN_CERT : Constants::TMS_BTEA_TOKEN_CERT;
  92. $cert = openssl_x509_read($pemdata);
  93. $pub_key = openssl_get_publickey($cert);
  94. $data = json_encode($json->data);
  95. $data = str_replace("\\", "", $data);
  96. if (!openssl_verify($data, $sigbytes, $pub_key, "sha256WithRSAEncryption"))
  97. {
  98. throw new InvalidTokenException("Signature validation failed for service $service");
  99. }
  100. // assign parsing fields back to Token t and return it
  101. return new Token($label, $id, $key, $startTs, $endTs, $sType, $service, $zone);
  102. }
  103. /**
  104. * Returns Token's identifier in byte array. This identifier is
  105. * encapsulated in protocol headers while TDE client SDK requests
  106. * the services from MKS clusters.
  107. * <p>
  108. *
  109. * @return string The token identifier.
  110. */
  111. public function get_id() { return $this->id; }
  112. /**
  113. * Returns name of Token's major service.
  114. *
  115. * @return token major service name.
  116. */
  117. public function get_service_name() { return $this->service; }
  118. public function getOriginType() { return $this->stype; }
  119. /**
  120. * Check token is effective (active) or not.
  121. *
  122. * @return true if token is active; otherwise not.
  123. */
  124. public function check_effective() {
  125. $cur = round(microtime(true) * 1000) + 8*60*60*1000; // in millisecond
  126. return $cur >= $this->effectiveTs;
  127. }
  128. public function check_expired($delta)
  129. {
  130. $now = round(microtime(true) * 1000); // in millisecond
  131. if($this->expiredTs >= $now)
  132. return STATE::VALID;
  133. else if($this->expiredTs + $delta >= $now)
  134. return STATE::EXPIREWARNING;
  135. return STATE::EXPIRED;
  136. }
  137. public function getExpiredDate()
  138. {
  139. return date("F j, Y, H:i:s", $this->expiredTs);
  140. }
  141. public function getExpiredDateInLong()
  142. {
  143. return $this->expiredTs;
  144. }
  145. public function getEffectiveDate()
  146. {
  147. return date("F j, Y, H:i:s", $this->effectiveTs);
  148. }
  149. public function getZone()
  150. {
  151. return $this->zone;
  152. }
  153. public function getTokenOrigin()
  154. {
  155. return ORIGIN::getName($this->stype);
  156. }
  157. public function do_sign($input) {
  158. if(!$this->isVerify)
  159. throw new InvalidTokenException("Not a verified token.");
  160. $sig = hash_hmac(Constants::DEFAULT_TOKEN_SIGN_ALGO, $input, $this->key, TRUE);
  161. return $sig;
  162. }
  163. public function do_verify($input, $sig)
  164. {
  165. if(!$this->isVerify)
  166. throw new InvalidTokenException("Not a verified token.");
  167. $cal_sig = hash_hmac(Constants::DEFAULT_TOKEN_SIGN_ALGO, $input, $this->key, TRUE);
  168. return $sig == $cal_sig;
  169. }
  170. public function do_encrypt($plaintext)
  171. {
  172. if(!$this->isVerify)
  173. throw new InvalidTokenException("Not a verified token.");
  174. $ct = $this->de->encrypt($plaintext);
  175. return $ct;
  176. }
  177. public function do_decrypt($ciphertext)
  178. {
  179. if(!$this->isVerify)
  180. throw new InvalidTokenException("Not a verified token.");
  181. $pt = $this->de->decrypt($ciphertext);
  182. return $pt;
  183. }
  184. public function transferToken(Token $from)
  185. {
  186. $this->de = $from->de;
  187. $this->effectiveTs = $from->effectiveTs;
  188. $this->expiredTs = $from->expiredTs;
  189. $this->id = $from->id;
  190. $this->isVerify = $from->isVerify;
  191. $this->key = $from->key;
  192. $this->label = $from->label;
  193. $this->se = $from->se;
  194. $this->service = $from->service;
  195. $this->stype = $from->stype;
  196. $this->zone = $from->zone;
  197. }
  198. /**
  199. * @param JosBaseInfo $josBaseInfo
  200. * @return Token
  201. * @throws InvalidTokenException
  202. * @throws JosGwException
  203. * @throws MalformedException
  204. * @throws VoucherInfoGetException
  205. * @throws \JsonMapper_Exception
  206. */
  207. public static function requestJosVoucher($josBaseInfo)
  208. {
  209. $voucherBase64Json = Token::requestJosVoucherString($josBaseInfo);
  210. $voucherJson = base64_decode($voucherBase64Json);
  211. $voucher = Token::parseFromString($voucherJson, true);
  212. return $voucher;
  213. }
  214. /**
  215. * @param JosBaseInfo $josBaseInfo
  216. * @return string
  217. * @throws JosGwException
  218. * @throws VoucherInfoGetException
  219. * @throws \JsonMapper_Exception
  220. */
  221. public static function requestJosVoucherString($josBaseInfo)
  222. {
  223. $requestUrl = $josBaseInfo->getServerUrl();
  224. $josVoucherInfoGetRequest = new JosVoucherInfoGetRequest($josBaseInfo->getAccessToken());
  225. $payload = $josVoucherInfoGetRequest->toFormParams($josBaseInfo);
  226. $jsonResponse = HttpsClient::postForm($requestUrl, $payload);
  227. $response = JosBaseResponse::parse($jsonResponse, new JosVoucherInfoGetResponse());
  228. if (!$response){
  229. throw new Exception('request jos error, while request voucher');
  230. }
  231. if ($response->getCode() !== 0) {
  232. throw new JosGwException('request jos error, while request voucher, code=' . $response->getCode() . ', message=' . $response->getEnDesc());
  233. }
  234. $voucherResponse = $response->getResponse();
  235. if (!$voucherResponse) {
  236. throw new VoucherInfoGetException('request voucher failed');
  237. }
  238. if ($voucherResponse->getErrorCode() !== '0') {
  239. throw new VoucherInfoGetException('request voucher failed, code=' . $voucherResponse->getErrorCode() . ', message=' . $voucherResponse->getErrorMsg());
  240. }
  241. $voucherBase64Json = $voucherResponse->getData()->getVoucher();
  242. return$voucherBase64Json;
  243. }
  244. }
  245. abstract class ORIGIN{
  246. const UNDEFINED = 0;
  247. const IDC = 1;
  248. const BETA = 2;
  249. const DEV = 3;
  250. public static function getName($code){
  251. switch ($code){
  252. case 0: return "UNDEFINED";
  253. case 1: return "IDC";
  254. case 2: return "BETA";
  255. case 3: return "DEV";
  256. default: return "Unsupported origin code.";
  257. }
  258. }
  259. }
  260. // Token status
  261. abstract class STATE{
  262. const VALID = 0;
  263. const EXPIREWARNING = 1;
  264. const EXPIRED = 2;
  265. }
  266. // Token zone
  267. abstract class ZONE{
  268. const CN_ZONE = "CN-0";
  269. const ID_ZONE = "ID-1";
  270. const TH_ZONE = "TH-1";
  271. }