1 /* 2 * Copyright 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #ifndef SYSTEM_KEYMASTER_AUTHORIZATION_SET_H_ 18 #define SYSTEM_KEYMASTER_AUTHORIZATION_SET_H_ 19 20 #include <UniquePtr.h> 21 22 #include <hardware/keymaster_defs.h> 23 #include <keymaster/keymaster_tags.h> 24 #include <keymaster/serializable.h> 25 26 namespace keymaster { 27 28 class AuthorizationSetBuilder; 29 30 /** 31 * An extension of the keymaster_key_param_set_t struct, which provides serialization memory 32 * management and methods for easy manipulation and construction. 33 */ 34 class AuthorizationSet : public Serializable, public keymaster_key_param_set_t { 35 public: 36 /** 37 * Construct an empty, dynamically-allocated, growable AuthorizationSet. Does not actually 38 * allocate any storage until elements are added, so there is no cost to creating an 39 * AuthorizationSet with this constructor and then reinitializing it to point at pre-allocated 40 * buffers, with \p Reinitialize. 41 */ 42 AuthorizationSet() 43 : elems_capacity_(0), indirect_data_(NULL), indirect_data_size_(0), 44 indirect_data_capacity_(0), error_(OK) { 45 elems_ = nullptr; 46 elems_size_ = 0; 47 } 48 49 /** 50 * Construct an AuthorizationSet from the provided array. The AuthorizationSet copies the data 51 * from the provided array (and the data referenced by its embedded pointers, if any) into 52 * dynamically-allocated storage. If allocation of the needed storage fails, \p is_valid() will 53 * return ALLOCATION_FAILURE. It is the responsibility of the caller to check before using the 54 * set, if allocations might fail. 55 */ 56 AuthorizationSet(const keymaster_key_param_t* elems, size_t count) : indirect_data_(nullptr) { 57 elems_ = nullptr; 58 Reinitialize(elems, count); 59 } 60 61 explicit AuthorizationSet(const keymaster_key_param_set_t& set) : indirect_data_(nullptr) { 62 elems_ = nullptr; 63 Reinitialize(set.params, set.length); 64 } 65 66 explicit AuthorizationSet(const uint8_t* serialized_set, size_t serialized_size) 67 : indirect_data_(nullptr) { 68 elems_ = nullptr; 69 Deserialize(&serialized_set, serialized_set + serialized_size); 70 } 71 72 /** 73 * Construct an AuthorizationSet from the provided builder. This extracts the data from the 74 * builder, rather than copying it, so after this call the builder is empty. 75 */ 76 explicit AuthorizationSet(/* NOT const */ AuthorizationSetBuilder& builder); 77 78 // Copy constructor. 79 AuthorizationSet(const AuthorizationSet& set) : Serializable(), indirect_data_(nullptr) { 80 elems_ = nullptr; 81 error_ = set.error_; 82 if (error_ != OK) return; 83 Reinitialize(set.elems_, set.elems_size_); 84 } 85 86 // Move constructor. 87 AuthorizationSet(AuthorizationSet&& set) : Serializable() { 88 MoveFrom(set); 89 } 90 91 // Copy assignment. 92 AuthorizationSet& operator=(const AuthorizationSet& set) { 93 Reinitialize(set.elems_, set.elems_size_); 94 error_ = set.error_; 95 return *this; 96 } 97 98 // Move assignment. 99 AuthorizationSet& operator=(AuthorizationSet&& set) { 100 FreeData(); 101 MoveFrom(set); 102 return *this; 103 } 104 105 /** 106 * Clear existing authorization set data 107 */ 108 void Clear(); 109 110 /** 111 * Reinitialize an AuthorizationSet as a dynamically-allocated, growable copy of the data in the 112 * provided array (and the data referenced by its embedded pointers, if any). If the allocation 113 * of the needed storage fails this method will return false and \p is_valid() will return 114 * ALLOCATION_FAILURE. 115 */ 116 bool Reinitialize(const keymaster_key_param_t* elems, size_t count); 117 118 bool Reinitialize(const AuthorizationSet& set) { 119 return Reinitialize(set.elems_, set.elems_size_); 120 } 121 122 bool Reinitialize(const keymaster_key_param_set_t& set) { 123 return Reinitialize(set.params, set.length); 124 } 125 126 ~AuthorizationSet(); 127 128 enum Error { 129 OK, 130 ALLOCATION_FAILURE, 131 MALFORMED_DATA, 132 }; 133 134 Error is_valid() const { return error_; } 135 136 /** 137 * Returns the size of the set. 138 */ 139 size_t size() const { return elems_size_; } 140 141 /** 142 * Returns true if the set is empty. 143 */ 144 bool empty() const { return size() == 0; } 145 146 /** 147 * Returns the total size of all indirect data referenced by set elements. 148 */ 149 size_t indirect_size() const { return indirect_data_size_; } 150 151 /** 152 * Returns the data in the set, directly. Be careful with this. 153 */ 154 const keymaster_key_param_t* data() const { return elems_; } 155 156 /** 157 * Sorts the set 158 */ 159 void Sort(); 160 161 /** 162 * Sorts the set and removes duplicates (inadvertently duplicating tags is easy to do with the 163 * AuthorizationSetBuilder). 164 */ 165 void Deduplicate(); 166 167 /** 168 * Adds all elements from \p set that are not already present in this AuthorizationSet. As a 169 * side-effect, if \p set is not null this AuthorizationSet will end up sorted. 170 */ 171 void Union(const keymaster_key_param_set_t& set); 172 173 /** 174 * Removes all elements in \p set from this AuthorizationSet. 175 */ 176 void Difference(const keymaster_key_param_set_t& set); 177 178 /** 179 * Returns the data in a keymaster_key_param_set_t, suitable for returning to C code. For C 180 * compatibility, the contents are malloced, not new'ed, and so must be freed with free(), or 181 * better yet with keymaster_free_param_set, not delete. The caller takes ownership. 182 */ 183 void CopyToParamSet(keymaster_key_param_set_t* set) const; 184 185 /** 186 * Returns the offset of the next entry that matches \p tag, starting from the element after \p 187 * begin. If not found, returns -1. 188 */ 189 int find(keymaster_tag_t tag, int begin = -1) const; 190 191 /** 192 * Removes the entry at the specified index. Returns true if successful, false if the index was 193 * out of bounds. 194 */ 195 bool erase(int index); 196 197 /** 198 * Returns iterator (pointer) to beginning of elems array, to enable STL-style iteration 199 */ 200 const keymaster_key_param_t* begin() const { return elems_; } 201 202 /** 203 * Returns iterator (pointer) one past end of elems array, to enable STL-style iteration 204 */ 205 const keymaster_key_param_t* end() const { return elems_ + elems_size_; } 206 207 /** 208 * Returns the nth element of the set. 209 */ 210 keymaster_key_param_t& operator[](int n); 211 212 /** 213 * Returns the nth element of the set. 214 */ 215 keymaster_key_param_t operator[](int n) const; 216 217 /** 218 * Returns true if the set contains at least one instance of \p tag 219 */ 220 bool Contains(keymaster_tag_t tag) const { 221 return find(tag) != -1; 222 } 223 224 /** 225 * Returns the number of \p tag entries. 226 */ 227 size_t GetTagCount(keymaster_tag_t tag) const; 228 229 /** 230 * Returns true if the set contains the specified tag and value. 231 */ 232 template <keymaster_tag_t Tag, typename T> 233 bool Contains(TypedEnumTag<KM_ENUM_REP, Tag, T> tag, T val) const { 234 return ContainsEnumValue(tag, val); 235 } 236 237 /** 238 * Returns true if the set contains the specified tag and value. 239 */ 240 template <keymaster_tag_t Tag, typename T> 241 bool Contains(TypedEnumTag<KM_ENUM, Tag, T> tag, T val) const { 242 return ContainsEnumValue(tag, val); 243 } 244 245 /** 246 * Returns true if the set contains the specified tag and value. 247 */ 248 template <keymaster_tag_t Tag> 249 bool Contains(TypedTag<KM_UINT, Tag> tag, uint32_t val) const { 250 return ContainsIntValue(tag, val); 251 } 252 253 /** 254 * If the specified integer-typed \p tag exists, places its value in \p val and returns true. 255 * If \p tag is not present, leaves \p val unmodified and returns false. 256 */ 257 template <keymaster_tag_t T> 258 inline bool GetTagValue(TypedTag<KM_UINT, T> tag, uint32_t* val) const { 259 return GetTagValueInt(tag, val); 260 } 261 262 /** 263 * If the specified instance of the specified integer-typed \p tag exists, places its value 264 * in \p val and returns true. If \p tag is not present, leaves \p val unmodified and returns 265 * false. 266 */ 267 template <keymaster_tag_t Tag> 268 bool GetTagValue(TypedTag<KM_UINT_REP, Tag> tag, size_t instance, uint32_t* val) const { 269 return GetTagValueIntRep(tag, instance, val); 270 } 271 272 /** 273 * If the specified long-typed \p tag exists, places its value in \p val and returns true. 274 * If \p tag is not present, leaves \p val unmodified and returns false. 275 */ 276 template <keymaster_tag_t T> 277 inline bool GetTagValue(TypedTag<KM_ULONG, T> tag, uint64_t* val) const { 278 return GetTagValueLong(tag, val); 279 } 280 281 /** 282 * If the specified instance of the specified integer-typed \p tag exists, places its value 283 * in \p val and returns true. If \p tag is not present, leaves \p val unmodified and returns 284 * false. 285 */ 286 template <keymaster_tag_t Tag> 287 bool GetTagValue(TypedTag<KM_ULONG_REP, Tag> tag, size_t instance, uint64_t* val) const { 288 return GetTagValueLongRep(tag, instance, val); 289 } 290 291 /** 292 * If the specified enumeration-typed \p tag exists, places its value in \p val and returns 293 * true. If \p tag is not present, leaves \p val unmodified and returns false. 294 */ 295 template <keymaster_tag_t Tag, typename T> 296 bool GetTagValue(TypedEnumTag<KM_ENUM, Tag, T> tag, T* val) const { 297 return GetTagValueEnum(tag, reinterpret_cast<uint32_t*>(val)); 298 } 299 300 /** 301 * If the specified instance of the specified enumeration-typed \p tag exists, places its value 302 * in \p val and returns true. If \p tag is not present, leaves \p val unmodified and returns 303 * false. 304 */ 305 template <keymaster_tag_t Tag, typename T> 306 bool GetTagValue(TypedEnumTag<KM_ENUM_REP, Tag, T> tag, size_t instance, T* val) const { 307 return GetTagValueEnumRep(tag, instance, reinterpret_cast<uint32_t*>(val)); 308 } 309 310 /** 311 * If exactly one instance of the specified enumeration-typed \p tag exists, places its value in 312 * \p val and returns true. If \p tag is not present or if multiple copies are present, leaves 313 * \p val unmodified and returns false. 314 */ 315 template <keymaster_tag_t Tag, typename T> 316 bool GetTagValue(TypedEnumTag<KM_ENUM_REP, Tag, T> tag, T* val) const { 317 if (GetTagCount(tag) != 1) 318 return false; 319 return GetTagValueEnumRep(tag, 0, reinterpret_cast<uint32_t*>(val)); 320 } 321 322 /** 323 * If the specified date-typed \p tag exists, places its value in \p val and returns 324 * true. If \p tag is not present, leaves \p val unmodified and returns false. 325 */ 326 template <keymaster_tag_t Tag> 327 bool GetTagValue(TypedTag<KM_UINT_REP, Tag> tag, size_t instance, 328 typename TypedTag<KM_UINT_REP, Tag>::value_type* val) const { 329 return GetTagValueIntRep(tag, instance, val); 330 } 331 332 /** 333 * If the specified bytes-typed \p tag exists, places its value in \p val and returns 334 * true. If \p tag is not present, leaves \p val unmodified and returns false. 335 */ 336 template <keymaster_tag_t Tag> 337 bool GetTagValue(TypedTag<KM_BYTES, Tag> tag, keymaster_blob_t* val) const { 338 return GetTagValueBlob(tag, val); 339 } 340 341 /** 342 * If the specified bignum-typed \p tag exists, places its value in \p val and returns 343 * true. If \p tag is not present, leaves \p val unmodified and returns false. 344 */ 345 template <keymaster_tag_t Tag> 346 bool GetTagValue(TypedTag<KM_BIGNUM, Tag> tag, keymaster_blob_t* val) const { 347 return GetTagValueBlob(tag, val); 348 } 349 350 /** 351 * Returns true if the specified tag is present, and therefore has the value 'true'. 352 */ 353 template <keymaster_tag_t Tag> bool GetTagValue(TypedTag<KM_BOOL, Tag> tag) const { 354 return GetTagValueBool(tag); 355 } 356 357 /** 358 * If the specified \p tag exists, places its value in \p val and returns true. If \p tag is 359 * not present, leaves \p val unmodified and returns false. 360 */ 361 template <keymaster_tag_t Tag, keymaster_tag_type_t Type> 362 bool GetTagValue(TypedTag<Type, Tag> tag, typename TagValueType<Type>::value_type* val) const { 363 return GetTagValueLong(tag, val); 364 } 365 366 bool push_back(keymaster_key_param_t elem); 367 368 /** 369 * Grow the elements array to ensure it can contain \p count entries. Preserves any existing 370 * entries. 371 */ 372 bool reserve_elems(size_t count); 373 374 /** 375 * Grow the indirect data array to ensure it can contain \p length bytes. Preserves any 376 * existing indirect data. 377 */ 378 bool reserve_indirect(size_t length); 379 380 bool push_back(const keymaster_key_param_set_t& set); 381 382 /** 383 * Append the tag and enumerated value to the set. 384 */ 385 template <keymaster_tag_t Tag, keymaster_tag_type_t Type, typename KeymasterEnum> 386 bool push_back(TypedEnumTag<Type, Tag, KeymasterEnum> tag, KeymasterEnum val) { 387 return push_back(Authorization(tag, val)); 388 } 389 390 /** 391 * Append the boolean tag (value "true") to the set. 392 */ 393 template <keymaster_tag_t Tag> bool push_back(TypedTag<KM_BOOL, Tag> tag) { 394 return push_back(Authorization(tag)); 395 } 396 397 /** 398 * Append the tag and byte array to the set. Copies the array into internal storage; does not 399 * take ownership of the passed-in array. 400 */ 401 template <keymaster_tag_t Tag> 402 bool push_back(TypedTag<KM_BYTES, Tag> tag, const void* bytes, size_t bytes_len) { 403 return push_back(keymaster_param_blob(tag, static_cast<const uint8_t*>(bytes), bytes_len)); 404 } 405 406 /** 407 * Append the tag and blob to the set. Copies the blob contents into internal storage; does not 408 * take ownership of the blob's data. 409 */ 410 template <keymaster_tag_t Tag> 411 bool push_back(TypedTag<KM_BYTES, Tag> tag, const keymaster_blob_t& blob) { 412 return push_back(tag, blob.data, blob.data_length); 413 } 414 415 /** 416 * Append the tag and bignum array to the set. Copies the array into internal storage; does not 417 * take ownership of the passed-in array. 418 */ 419 template <keymaster_tag_t Tag> 420 bool push_back(TypedTag<KM_BIGNUM, Tag> tag, const void* bytes, size_t bytes_len) { 421 return push_back(keymaster_param_blob(tag, static_cast<const uint8_t*>(bytes), bytes_len)); 422 } 423 424 template <keymaster_tag_t Tag, keymaster_tag_type_t Type> 425 bool push_back(TypedTag<Type, Tag> tag, typename TypedTag<Type, Tag>::value_type val) { 426 return push_back(Authorization(tag, val)); 427 } 428 429 template <keymaster_tag_t Tag, keymaster_tag_type_t Type> 430 bool push_back(TypedTag<Type, Tag> tag, const void* bytes, size_t bytes_len) { 431 return push_back(Authorization(tag, bytes, bytes_len)); 432 } 433 434 /* Virtual methods from Serializable */ 435 size_t SerializedSize() const; 436 uint8_t* Serialize(uint8_t* serialized_set, const uint8_t* end) const; 437 bool Deserialize(const uint8_t** buf_ptr, const uint8_t* end); 438 439 size_t SerializedSizeOfElements() const; 440 441 private: 442 void FreeData(); 443 void MoveFrom(AuthorizationSet& set); 444 445 void set_invalid(Error err); 446 447 static size_t ComputeIndirectDataSize(const keymaster_key_param_t* elems, size_t count); 448 void CopyIndirectData(); 449 bool CheckIndirectData(); 450 451 bool DeserializeIndirectData(const uint8_t** buf_ptr, const uint8_t* end); 452 bool DeserializeElementsData(const uint8_t** buf_ptr, const uint8_t* end); 453 454 bool GetTagValueEnum(keymaster_tag_t tag, uint32_t* val) const; 455 bool GetTagValueEnumRep(keymaster_tag_t tag, size_t instance, uint32_t* val) const; 456 bool GetTagValueInt(keymaster_tag_t tag, uint32_t* val) const; 457 bool GetTagValueIntRep(keymaster_tag_t tag, size_t instance, uint32_t* val) const; 458 bool GetTagValueLong(keymaster_tag_t tag, uint64_t* val) const; 459 bool GetTagValueLongRep(keymaster_tag_t tag, size_t instance, uint64_t* val) const; 460 bool GetTagValueDate(keymaster_tag_t tag, uint64_t* val) const; 461 bool GetTagValueBlob(keymaster_tag_t tag, keymaster_blob_t* val) const; 462 bool GetTagValueBool(keymaster_tag_t tag) const; 463 464 bool ContainsEnumValue(keymaster_tag_t tag, uint32_t val) const; 465 bool ContainsIntValue(keymaster_tag_t tag, uint32_t val) const; 466 467 // Define elems_ and elems_size_ as aliases to params and length, respectively. This is to 468 // avoid using the variables without the trailing underscore in the implementation. 469 keymaster_key_param_t*& elems_ = keymaster_key_param_set_t::params; 470 size_t& elems_size_ = keymaster_key_param_set_t::length; 471 472 size_t elems_capacity_; 473 uint8_t* indirect_data_; 474 size_t indirect_data_size_; 475 size_t indirect_data_capacity_; 476 Error error_; 477 }; 478 479 class AuthorizationSetBuilder { 480 public: 481 template <typename TagType, typename ValueType> 482 AuthorizationSetBuilder& Authorization(TagType tag, ValueType value) { 483 set.push_back(tag, value); 484 return *this; 485 } 486 487 template <keymaster_tag_t Tag> 488 AuthorizationSetBuilder& Authorization(TypedTag<KM_BOOL, Tag> tag) { 489 set.push_back(tag); 490 return *this; 491 } 492 493 template <keymaster_tag_t Tag> 494 AuthorizationSetBuilder& Authorization(TypedTag<KM_INVALID, Tag> tag) { 495 keymaster_key_param_t param; 496 param.tag = tag; 497 set.push_back(param); 498 return *this; 499 } 500 501 template <keymaster_tag_t Tag> 502 AuthorizationSetBuilder& Authorization(TypedTag<KM_BYTES, Tag> tag, const uint8_t* data, 503 size_t data_length) { 504 set.push_back(tag, data, data_length); 505 return *this; 506 } 507 508 template <keymaster_tag_t Tag> 509 AuthorizationSetBuilder& Authorization(TypedTag<KM_BYTES, Tag> tag, const char* data, 510 size_t data_length) { 511 return Authorization(tag, reinterpret_cast<const uint8_t*>(data), data_length); 512 } 513 514 AuthorizationSetBuilder& RsaKey(uint32_t key_size, uint64_t public_exponent); 515 AuthorizationSetBuilder& EcdsaKey(uint32_t key_size); 516 AuthorizationSetBuilder& AesKey(uint32_t key_size); 517 AuthorizationSetBuilder& HmacKey(uint32_t key_size); 518 519 AuthorizationSetBuilder& RsaSigningKey(uint32_t key_size, uint64_t public_exponent); 520 AuthorizationSetBuilder& RsaEncryptionKey(uint32_t key_size, uint64_t public_exponent); 521 AuthorizationSetBuilder& EcdsaSigningKey(uint32_t key_size); 522 AuthorizationSetBuilder& AesEncryptionKey(uint32_t key_size); 523 524 AuthorizationSetBuilder& SigningKey(); 525 AuthorizationSetBuilder& EncryptionKey(); 526 AuthorizationSetBuilder& NoDigestOrPadding(); 527 AuthorizationSetBuilder& EcbMode(); 528 529 AuthorizationSetBuilder& Digest(keymaster_digest_t digest) { 530 return Authorization(TAG_DIGEST, digest); 531 } 532 533 AuthorizationSetBuilder& Padding(keymaster_padding_t padding) { 534 return Authorization(TAG_PADDING, padding); 535 } 536 537 AuthorizationSetBuilder& Deduplicate() { 538 set.Deduplicate(); 539 return *this; 540 } 541 542 AuthorizationSet build() const { return set; } 543 544 private: 545 friend AuthorizationSet; 546 AuthorizationSet set; 547 }; 548 549 inline AuthorizationSetBuilder& AuthorizationSetBuilder::RsaKey(uint32_t key_size, 550 uint64_t public_exponent) { 551 Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA); 552 Authorization(TAG_KEY_SIZE, key_size); 553 Authorization(TAG_RSA_PUBLIC_EXPONENT, public_exponent); 554 return *this; 555 } 556 557 inline AuthorizationSetBuilder& AuthorizationSetBuilder::EcdsaKey(uint32_t key_size) { 558 Authorization(TAG_ALGORITHM, KM_ALGORITHM_EC); 559 Authorization(TAG_KEY_SIZE, key_size); 560 return *this; 561 } 562 563 inline AuthorizationSetBuilder& AuthorizationSetBuilder::AesKey(uint32_t key_size) { 564 Authorization(TAG_ALGORITHM, KM_ALGORITHM_AES); 565 return Authorization(TAG_KEY_SIZE, key_size); 566 } 567 568 inline AuthorizationSetBuilder& AuthorizationSetBuilder::HmacKey(uint32_t key_size) { 569 Authorization(TAG_ALGORITHM, KM_ALGORITHM_HMAC); 570 Authorization(TAG_KEY_SIZE, key_size); 571 return SigningKey(); 572 } 573 574 inline AuthorizationSetBuilder& AuthorizationSetBuilder::RsaSigningKey(uint32_t key_size, 575 uint64_t public_exponent) { 576 RsaKey(key_size, public_exponent); 577 return SigningKey(); 578 } 579 580 inline AuthorizationSetBuilder& 581 AuthorizationSetBuilder::RsaEncryptionKey(uint32_t key_size, uint64_t public_exponent) { 582 RsaKey(key_size, public_exponent); 583 return EncryptionKey(); 584 } 585 586 inline AuthorizationSetBuilder& AuthorizationSetBuilder::EcdsaSigningKey(uint32_t key_size) { 587 EcdsaKey(key_size); 588 return SigningKey(); 589 } 590 591 inline AuthorizationSetBuilder& AuthorizationSetBuilder::AesEncryptionKey(uint32_t key_size) { 592 AesKey(key_size); 593 return EncryptionKey(); 594 } 595 596 inline AuthorizationSetBuilder& AuthorizationSetBuilder::SigningKey() { 597 Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN); 598 return Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY); 599 } 600 601 inline AuthorizationSetBuilder& AuthorizationSetBuilder::EncryptionKey() { 602 Authorization(TAG_PURPOSE, KM_PURPOSE_ENCRYPT); 603 return Authorization(TAG_PURPOSE, KM_PURPOSE_DECRYPT); 604 } 605 606 inline AuthorizationSetBuilder& AuthorizationSetBuilder::NoDigestOrPadding() { 607 Authorization(TAG_DIGEST, KM_DIGEST_NONE); 608 return Authorization(TAG_PADDING, KM_PAD_NONE); 609 } 610 611 inline AuthorizationSetBuilder& AuthorizationSetBuilder::EcbMode() { 612 return Authorization(TAG_BLOCK_MODE, KM_MODE_ECB); 613 } 614 615 } // namespace keymaster 616 617 #endif // SYSTEM_KEYMASTER_KEY_AUTHORIZATION_SET_H_ 618