001/* 002 * Copyright 2007-2024 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2007-2024 Ping Identity Corporation 007 * 008 * Licensed under the Apache License, Version 2.0 (the "License"); 009 * you may not use this file except in compliance with the License. 010 * You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, software 015 * distributed under the License is distributed on an "AS IS" BASIS, 016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 017 * See the License for the specific language governing permissions and 018 * limitations under the License. 019 */ 020/* 021 * Copyright (C) 2007-2024 Ping Identity Corporation 022 * 023 * This program is free software; you can redistribute it and/or modify 024 * it under the terms of the GNU General Public License (GPLv2 only) 025 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 026 * as published by the Free Software Foundation. 027 * 028 * This program is distributed in the hope that it will be useful, 029 * but WITHOUT ANY WARRANTY; without even the implied warranty of 030 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 031 * GNU General Public License for more details. 032 * 033 * You should have received a copy of the GNU General Public License 034 * along with this program; if not, see <http://www.gnu.org/licenses>. 035 */ 036package com.unboundid.asn1; 037 038 039 040import com.unboundid.util.ByteString; 041import com.unboundid.util.ByteStringBuffer; 042import com.unboundid.util.Debug; 043import com.unboundid.util.NotMutable; 044import com.unboundid.util.NotNull; 045import com.unboundid.util.Nullable; 046import com.unboundid.util.StaticUtils; 047import com.unboundid.util.ThreadSafety; 048import com.unboundid.util.ThreadSafetyLevel; 049import com.unboundid.util.Validator; 050 051import static com.unboundid.asn1.ASN1Messages.*; 052 053 054 055/** 056 * This class provides an ASN.1 octet string element, whose value is simply 057 * comprised of zero or more bytes. Octet string elements are frequently used 058 * to represent string values as well. 059 */ 060@NotMutable() 061@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 062public final class ASN1OctetString 063 extends ASN1Element 064 implements ByteString 065{ 066 /** 067 * The serial version UID for this serializable class. 068 */ 069 private static final long serialVersionUID = -7857753188341295516L; 070 071 072 073 /* 074 * NOTE: This class uses lazy initialization for the value. The value may 075 * be initially specified as either a string or a byte array, and if the value 076 * is provided as a string, then the byte array version of that value will be 077 * computed on-demand later. Even though this class is externally immutable, 078 * that does not by itself make it completely threadsafe, because weirdness in 079 * the Java memory model could allow the assignment to be performed out of 080 * order. By passing the value through a volatile variable any time the value 081 * is set other than in the constructor (which will always be safe) we ensure 082 * that this reordering cannot happen. This is only needed for the valueBytes 083 * array because it is not required for primitives (like length and offset) or 084 * for objects with only final fields (like stringValue). 085 * 086 * In the majority of cases, passing the value through a volatile variable is 087 * much faster than declaring valueBytes itself to be volatile because a 088 * volatile variable cannot be held in CPU caches or registers and must only 089 * be accessed from memory visible to all threads. Since the value may be 090 * read much more often than it is written, passing it through a volatile 091 * variable rather than making it volatile directly can help avoid that 092 * penalty when possible. 093 */ 094 095 096 097 // The binary representation of the value for this element. 098 @Nullable private byte[] valueBytes; 099 100 // A volatile variable used to guard publishing the valueBytes array. See the 101 // note above to explain why this is needed. 102 @Nullable private volatile byte[] valueBytesGuard; 103 104 // The length of the value in the byte array, if applicable. 105 private int length; 106 107 // The offset in the byte array at which the value begins, if applicable. 108 private int offset; 109 110 // The string representation of the value for this element. 111 @Nullable private String stringValue; 112 113 114 115 /** 116 * Creates a new ASN.1 octet string element with the default BER type and 117 * no value. 118 */ 119 public ASN1OctetString() 120 { 121 super(ASN1Constants.UNIVERSAL_OCTET_STRING_TYPE); 122 123 valueBytes = StaticUtils.NO_BYTES; 124 stringValue = ""; 125 offset = 0; 126 length = 0; 127 } 128 129 130 131 /** 132 * Creates a new ASN.1 octet string element with the specified type and no 133 * value. 134 * 135 * @param type The BER type to use for this element. 136 */ 137 public ASN1OctetString(final byte type) 138 { 139 super(type); 140 141 valueBytes = StaticUtils.NO_BYTES; 142 stringValue = ""; 143 offset = 0; 144 length = 0; 145 } 146 147 148 149 /** 150 * Creates a new ASN.1 octet string element with the default BER type and the 151 * provided value. 152 * 153 * @param value The value to use for this element. 154 */ 155 public ASN1OctetString(@Nullable final byte[] value) 156 { 157 super(ASN1Constants.UNIVERSAL_OCTET_STRING_TYPE); 158 159 if (value == null) 160 { 161 valueBytes = StaticUtils.NO_BYTES; 162 stringValue = ""; 163 offset = 0; 164 length = 0; 165 } 166 else 167 { 168 valueBytes = value; 169 stringValue = null; 170 offset = 0; 171 length = value.length; 172 } 173 } 174 175 176 177 /** 178 * Creates a new ASN.1 octet string element with the default BER type and the 179 * provided value. 180 * 181 * @param value The byte array containing the value to use for this 182 * element It must not be {@code null}. 183 * @param offset The offset within the array at which the value begins. It 184 * must be greater than or equal to zero and less than or 185 * equal to the length of the array. 186 * @param length The length in bytes of the value. It must be greater than 187 * or equal to zero, and it must not extend beyond the end of 188 * the array. 189 */ 190 public ASN1OctetString(@NotNull final byte[] value, final int offset, 191 final int length) 192 { 193 super(ASN1Constants.UNIVERSAL_OCTET_STRING_TYPE); 194 195 Validator.ensureNotNull(value); 196 Validator.ensureTrue((offset >= 0) && (length >= 0) && 197 (offset+length <= value.length)); 198 199 valueBytes = value; 200 stringValue = null; 201 this.offset = offset; 202 this.length = length; 203 } 204 205 206 207 /** 208 * Creates a new ASN.1 octet string element with the specified type and the 209 * provided value. 210 * 211 * @param type The BER type to use for this element. 212 * @param value The value to use for this element. 213 */ 214 public ASN1OctetString(final byte type, @Nullable final byte[] value) 215 { 216 super(type); 217 218 if (value == null) 219 { 220 valueBytes = StaticUtils.NO_BYTES; 221 stringValue = ""; 222 offset = 0; 223 length = 0; 224 } 225 else 226 { 227 valueBytes = value; 228 stringValue = null; 229 offset = 0; 230 length = value.length; 231 } 232 } 233 234 235 236 /** 237 * Creates a new ASN.1 octet string element with the specified type and the 238 * provided value. 239 * 240 * @param type The BER type to use for this element. 241 * @param value The byte array containing the value to use for this 242 * element. It must not be {@code null}. 243 * @param offset The offset within the array at which the value begins. It 244 * must be greater than or equal to zero and less than or 245 * equal to the length of the array. 246 * @param length The length in bytes of the value. It must be greater than 247 * or equal to zero, and it must not extend beyond the end of 248 * the array. 249 */ 250 public ASN1OctetString(final byte type, @NotNull final byte[] value, 251 final int offset, final int length) 252 { 253 super(type); 254 255 Validator.ensureTrue((offset >= 0) && (length >= 0) && 256 (offset+length <= value.length)); 257 258 valueBytes = value; 259 stringValue = null; 260 this.offset = offset; 261 this.length = length; 262 } 263 264 265 266 /** 267 * Creates a new ASN.1 octet string element with the default BER type and the 268 * provided value. 269 * 270 * @param value The value to use for this element. 271 */ 272 public ASN1OctetString(@Nullable final String value) 273 { 274 super(ASN1Constants.UNIVERSAL_OCTET_STRING_TYPE); 275 276 if (value == null) 277 { 278 valueBytes = StaticUtils.NO_BYTES; 279 stringValue = ""; 280 offset = 0; 281 length = 0; 282 } 283 else 284 { 285 valueBytes = null; 286 stringValue = value; 287 offset = -1; 288 length = -1; 289 } 290 } 291 292 293 294 /** 295 * Creates a new ASN.1 octet string element with the specified type and the 296 * provided value. 297 * 298 * @param type The BER type to use for this element. 299 * @param value The value to use for this element. 300 */ 301 public ASN1OctetString(final byte type, @Nullable final String value) 302 { 303 super(type); 304 305 if (value == null) 306 { 307 valueBytes = StaticUtils.NO_BYTES; 308 stringValue = ""; 309 offset = 0; 310 length = 0; 311 } 312 else 313 { 314 valueBytes = null; 315 stringValue = value; 316 offset = -1; 317 length = -1; 318 } 319 } 320 321 322 323 /** 324 * {@inheritDoc} 325 */ 326 @Override() 327 @NotNull() 328 byte[] getValueArray() 329 { 330 return getValue(); 331 } 332 333 334 335 /** 336 * {@inheritDoc} 337 */ 338 @Override() 339 int getValueOffset() 340 { 341 return 0; 342 } 343 344 345 346 /** 347 * {@inheritDoc} 348 */ 349 @Override() 350 public int getValueLength() 351 { 352 return getValue().length; 353 } 354 355 356 357 /** 358 * {@inheritDoc} 359 */ 360 @Override() 361 @NotNull() 362 public byte[] getValue() 363 { 364 if (valueBytes == null) 365 { 366 valueBytesGuard = StaticUtils.getBytes(stringValue); 367 offset = 0; 368 length = valueBytesGuard.length; 369 valueBytes = valueBytesGuard; 370 } 371 else if ((offset != 0) || (length != valueBytes.length)) 372 { 373 final byte[] newArray = new byte[length]; 374 System.arraycopy(valueBytes, offset, newArray, 0, length); 375 offset = 0; 376 valueBytesGuard = newArray; 377 valueBytes = valueBytesGuard; 378 } 379 380 return valueBytes; 381 } 382 383 384 385 /** 386 * {@inheritDoc} 387 */ 388 @Override() 389 public void encodeTo(@NotNull final ByteStringBuffer buffer) 390 { 391 buffer.append(getType()); 392 393 if (valueBytes == null) 394 { 395 // Assume that the string contains only ASCII characters. That will be 396 // true most of the time and we can optimize for it. If it's not true, 397 // then we'll fix it later. 398 final int stringLength = stringValue.length(); 399 final int lengthStartPos = buffer.length(); 400 encodeLengthTo(stringLength, buffer); 401 final int valueStartPos = buffer.length(); 402 buffer.append(stringValue); 403 final int stringBytesLength = buffer.length() - valueStartPos; 404 if (stringBytesLength != stringLength) 405 { 406 // This must mean that the string had non-ASCII characters in it, so 407 // fix the encoded representation. 408 final byte[] newLengthBytes = encodeLength(stringBytesLength); 409 if (newLengthBytes.length == (valueStartPos - lengthStartPos)) 410 { 411 // It takes the same number of bytes to encode the new length as 412 // the length we previously expected, so we can just overwrite the 413 // length bytes in the backing array. 414 System.arraycopy(newLengthBytes, 0, buffer.getBackingArray(), 415 lengthStartPos, newLengthBytes.length); 416 } 417 else 418 { 419 buffer.setLength(lengthStartPos); 420 buffer.append(newLengthBytes); 421 buffer.append(stringValue); 422 } 423 } 424 } 425 else 426 { 427 encodeLengthTo(length, buffer); 428 buffer.append(valueBytes, offset, length); 429 } 430 } 431 432 433 434 /** 435 * Retrieves the string value for this element. 436 * 437 * @return The String value for this element. 438 */ 439 @Override() 440 @NotNull() 441 public String stringValue() 442 { 443 if (stringValue == null) 444 { 445 if (length == 0) 446 { 447 stringValue = ""; 448 } 449 else 450 { 451 stringValue = StaticUtils.toUTF8String(valueBytes, offset, length); 452 } 453 } 454 455 return stringValue; 456 } 457 458 459 460 /** 461 * Decodes the contents of the provided byte array as an octet string element. 462 * 463 * @param elementBytes The byte array to decode as an ASN.1 octet string 464 * element. 465 * 466 * @return The decoded ASN.1 octet string element. 467 * 468 * @throws ASN1Exception If the provided array cannot be decoded as an 469 * octet string element. 470 */ 471 @NotNull() 472 public static ASN1OctetString decodeAsOctetString( 473 @NotNull final byte[] elementBytes) 474 throws ASN1Exception 475 { 476 try 477 { 478 int valueStartPos = 2; 479 int length = (elementBytes[1] & 0x7F); 480 if (length != elementBytes[1]) 481 { 482 final int numLengthBytes = length; 483 484 length = 0; 485 for (int i=0; i < numLengthBytes; i++) 486 { 487 length <<= 8; 488 length |= (elementBytes[valueStartPos++] & 0xFF); 489 } 490 } 491 492 if ((elementBytes.length - valueStartPos) != length) 493 { 494 throw new ASN1Exception(ERR_ELEMENT_LENGTH_MISMATCH.get(length, 495 (elementBytes.length - valueStartPos))); 496 } 497 498 return new ASN1OctetString(elementBytes[0], elementBytes, valueStartPos, 499 length); 500 } 501 catch (final ASN1Exception ae) 502 { 503 Debug.debugException(ae); 504 throw ae; 505 } 506 catch (final Exception e) 507 { 508 Debug.debugException(e); 509 throw new ASN1Exception(ERR_ELEMENT_DECODE_EXCEPTION.get(e), e); 510 } 511 } 512 513 514 515 /** 516 * Decodes the provided ASN.1 element as an octet string element. 517 * 518 * @param element The ASN.1 element to be decoded. 519 * 520 * @return The decoded ASN.1 octet string element. 521 */ 522 @NotNull() 523 public static ASN1OctetString decodeAsOctetString( 524 @NotNull final ASN1Element element) 525 { 526 return new ASN1OctetString(element.getType(), element.getValue()); 527 } 528 529 530 531 /** 532 * Appends the value of this ASN.1 octet string to the provided buffer. 533 * 534 * @param buffer The buffer to which the value is to be appended. 535 */ 536 @Override() 537 public void appendValueTo(@NotNull final ByteStringBuffer buffer) 538 { 539 if (valueBytes == null) 540 { 541 buffer.append(stringValue); 542 } 543 else 544 { 545 buffer.append(valueBytes, offset, length); 546 } 547 } 548 549 550 551 /** 552 * Converts this byte string to an ASN.1 octet string. 553 * 554 * @return An ASN.1 octet string with the value of this byte string. 555 */ 556 @Override() 557 @NotNull() 558 public ASN1OctetString toASN1OctetString() 559 { 560 return this; 561 } 562 563 564 565 /** 566 * {@inheritDoc} 567 */ 568 @Override() 569 public void toString(@NotNull final StringBuilder buffer) 570 { 571 buffer.append(stringValue()); 572 } 573}