001/*
002 * Copyright 2016-2024 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2016-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) 2016-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.ldap.sdk.unboundidds.extensions;
037
038
039
040import java.util.ArrayList;
041
042import com.unboundid.asn1.ASN1Element;
043import com.unboundid.asn1.ASN1OctetString;
044import com.unboundid.asn1.ASN1Sequence;
045import com.unboundid.ldap.sdk.Control;
046import com.unboundid.ldap.sdk.ExtendedRequest;
047import com.unboundid.ldap.sdk.LDAPException;
048import com.unboundid.ldap.sdk.ResultCode;
049import com.unboundid.util.Debug;
050import com.unboundid.util.NotMutable;
051import com.unboundid.util.NotNull;
052import com.unboundid.util.Nullable;
053import com.unboundid.util.StaticUtils;
054import com.unboundid.util.ThreadSafety;
055import com.unboundid.util.ThreadSafetyLevel;
056import com.unboundid.util.Validator;
057
058import static com.unboundid.ldap.sdk.unboundidds.extensions.ExtOpMessages.*;
059
060
061
062/**
063 * This class provides an implementation of an extended request that may be used
064 * to register a YubiKey OTP device with the Directory Server so that it may be
065 * used to authenticate using the UNBOUNDID-YUBIKEY-OTP SASL mechanism.
066 * <BR>
067 * <BLOCKQUOTE>
068 *   <B>NOTE:</B>  This class, and other classes within the
069 *   {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only
070 *   supported for use against Ping Identity, UnboundID, and
071 *   Nokia/Alcatel-Lucent 8661 server products.  These classes provide support
072 *   for proprietary functionality or for external specifications that are not
073 *   considered stable or mature enough to be guaranteed to work in an
074 *   interoperable way with other types of LDAP servers.
075 * </BLOCKQUOTE>
076 * <BR>
077 * This extended request has an OID of 1.3.6.1.4.1.30221.2.6.54, and it must
078 * include a request value with the following encoding:
079 * <BR><BR>
080 * <PRE>
081 *   RegisterYubiKeyOTPDeviceRequest ::= SEQUENCE {
082 *        authenticationID     [0] OCTET STRING OPTIONAL,
083 *        staticPassword       [1] OCTET STRING OPTIONAL,
084 *        yubiKeyOTP           [2] OCTET STRING,
085 *        ... }
086 * </PRE>
087 *
088 *
089 * @see  DeregisterYubiKeyOTPDeviceExtendedRequest
090 * @see  com.unboundid.ldap.sdk.unboundidds.UnboundIDYubiKeyOTPBindRequest
091 * @see  com.unboundid.ldap.sdk.unboundidds.RegisterYubiKeyOTPDevice
092 */
093@NotMutable()
094@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
095public final class RegisterYubiKeyOTPDeviceExtendedRequest
096       extends ExtendedRequest
097{
098  /**
099   * The OID (1.3.6.1.4.1.30221.2.6.54) for the register YubiKey OTP device
100   * extended request.
101   */
102  @NotNull public static final String REGISTER_YUBIKEY_OTP_DEVICE_REQUEST_OID =
103       "1.3.6.1.4.1.30221.2.6.54";
104
105
106
107  /**
108   * The BER type for the authentication ID element of the request value
109   * sequence.
110   */
111  private static final byte TYPE_AUTHENTICATION_ID = (byte) 0x80;
112
113
114
115  /**
116   * The BER type for the static password element of the request value sequence.
117   */
118  private static final byte TYPE_STATIC_PASSWORD = (byte) 0x81;
119
120
121
122  /**
123   * The BER type for the YubiKey OTP element of the request value sequence.
124   */
125  private static final byte TYPE_YUBIKEY_OTP = (byte) 0x82;
126
127
128
129  /**
130   * The serial version UID for this serializable class.
131   */
132  private static final long serialVersionUID = 4833523148133015294L;
133
134
135
136  // The static password for the request.
137  @Nullable private final ASN1OctetString staticPassword;
138
139  // The authentication ID for the request.
140  @Nullable private final String authenticationID;
141
142  // The YubiKey OTP for the request.
143  @NotNull private final String yubiKeyOTP;
144
145
146
147  /**
148   * Creates a new register YubiKey OTP device extended request that will be
149   * used to register a new device for the user as whom the underlying
150   * connection is authenticated.
151   *
152   * @param  yubiKeyOTP  A one-time password generated by the YubiKey device to
153   *                     be registered.  It must not be {@code null}.
154   * @param  controls    The set of controls to include in the request.  It may
155   *                     be {@code null} or empty if there should not be any
156   *                     request controls.
157   */
158  public RegisterYubiKeyOTPDeviceExtendedRequest(
159              @NotNull final String yubiKeyOTP,
160              @Nullable final Control... controls)
161  {
162    this(null, (ASN1OctetString) null, yubiKeyOTP, controls);
163  }
164
165
166
167  /**
168   * Creates a new register YubiKey OTP device extended request with the
169   * provided information.
170   *
171   * @param  authenticationID  The authentication ID that identifies the user
172   *                           for whom the YubiKey OTP device is to be
173   *                           registered.  It may be {@code null} if the device
174   *                           is to be registered for the user as whom the
175   *                           underlying connection is authenticated.
176   * @param  staticPassword    The static password of the user for whom the
177   *                           device is to be registered.  It may be
178   *                           {@code null} if the device is to be registered
179   *                           for a user other than the user authenticated on
180   *                           the underlying connection and the server is
181   *                           configured to not require the target user's
182   *                           static password in this case.
183   * @param  yubiKeyOTP        A one-time password generated by the YubiKey
184   *                           device to be registered.  It must not be
185   *                           {@code null}.
186   * @param  controls          The set of controls to include in the request.
187   *                           It may be {@code null} or empty if there should
188   *                           not be any request controls.
189   */
190  public RegisterYubiKeyOTPDeviceExtendedRequest(
191              @Nullable final String authenticationID,
192              @Nullable final String staticPassword,
193              @NotNull final String yubiKeyOTP,
194              @Nullable final Control... controls)
195  {
196    this(authenticationID, encodePassword(staticPassword), yubiKeyOTP,
197         controls);
198  }
199
200
201
202  /**
203   * Creates a new register YubiKey OTP device extended request with the
204   * provided information.
205   *
206   * @param  authenticationID  The authentication ID that identifies the user
207   *                           for whom the YubiKey OTP device is to be
208   *                           registered.  It may be {@code null} if the device
209   *                           is to be registered for the user as whom the
210   *                           underlying connection is authenticated.
211   * @param  staticPassword    The static password of the user for whom the
212   *                           device is to be registered.  It may be
213   *                           {@code null} if the device is to be registered
214   *                           for a user other than the user authenticated on
215   *                           the underlying connection and the server is
216   *                           configured to not require the target user's
217   *                           static password in this case.
218   * @param  yubiKeyOTP        A one-time password generated by the YubiKey
219   *                           device to be registered.  It must not be
220   *                           {@code null}.
221   * @param  controls          The set of controls to include in the request.
222   *                           It may be {@code null} or empty if there should
223   *                           not be any request controls.
224   */
225  public RegisterYubiKeyOTPDeviceExtendedRequest(
226              @Nullable final String authenticationID,
227              @Nullable final byte[] staticPassword,
228              @NotNull final String yubiKeyOTP,
229              @Nullable final Control... controls)
230  {
231    this(authenticationID, encodePassword(staticPassword), yubiKeyOTP,
232         controls);
233  }
234
235
236
237  /**
238   * Creates a new register YubiKey OTP device extended request with the
239   * provided information.
240   *
241   * @param  authenticationID  The authentication ID that identifies the user
242   *                           for whom the YubiKey OTP device is to be
243   *                           registered.  It may be {@code null} if the device
244   *                           is to be registered for the user as whom the
245   *                           underlying connection is authenticated.
246   * @param  staticPassword    The static password of the user for whom the
247   *                           device is to be registered.  It may be
248   *                           {@code null} if the device is to be registered
249   *                           for a user other than the user authenticated on
250   *                           the underlying connection and the server is
251   *                           configured to not require the target user's
252   *                           static password in this case.
253   * @param  yubiKeyOTP        A one-time password generated by the YubiKey
254   *                           device to be registered.  It must not be
255   *                           {@code null}.
256   * @param  controls          The set of controls to include in the request.
257   *                           It may be {@code null} or empty if there should
258   *                           not be any request controls.
259   */
260  private RegisterYubiKeyOTPDeviceExtendedRequest(
261               @Nullable final String authenticationID,
262               @Nullable final ASN1OctetString staticPassword,
263               @NotNull final String yubiKeyOTP,
264               @Nullable final Control... controls)
265  {
266    super(REGISTER_YUBIKEY_OTP_DEVICE_REQUEST_OID,
267         encodeValue(authenticationID, staticPassword, yubiKeyOTP), controls);
268
269    this.authenticationID = authenticationID;
270    this.staticPassword   = staticPassword;
271    this.yubiKeyOTP       = yubiKeyOTP;
272  }
273
274
275
276  /**
277   * Creates a new register YubiKey OTP device extended request that is decoded
278   * from the provided generic extended request.
279   *
280   * @param  request  The generic extended request to decode as a register
281   *                  YubiKey OTP device request.
282   *
283   * @throws  LDAPException  If a problem is encountered while attempting to
284   *                         decode the provided request.
285   */
286  public RegisterYubiKeyOTPDeviceExtendedRequest(
287              @NotNull final ExtendedRequest request)
288         throws LDAPException
289  {
290    super(request);
291
292    final ASN1OctetString value = request.getValue();
293    if (value == null)
294    {
295      throw new LDAPException(ResultCode.DECODING_ERROR,
296           ERR_REGISTER_YUBIKEY_OTP_REQUEST_NO_VALUE.get());
297    }
298
299    try
300    {
301      String authID = null;
302      ASN1OctetString staticPW = null;
303      String otp = null;
304      for (final ASN1Element e :
305           ASN1Sequence.decodeAsSequence(value.getValue()).elements())
306      {
307        switch (e.getType())
308        {
309          case TYPE_AUTHENTICATION_ID:
310            authID = ASN1OctetString.decodeAsOctetString(e).stringValue();
311            break;
312          case TYPE_STATIC_PASSWORD:
313            staticPW = ASN1OctetString.decodeAsOctetString(e);
314            break;
315          case TYPE_YUBIKEY_OTP:
316            otp = ASN1OctetString.decodeAsOctetString(e).stringValue();
317            break;
318          default:
319            throw new LDAPException(ResultCode.DECODING_ERROR,
320                 ERR_REGISTER_YUBIKEY_OTP_REQUEST_UNRECOGNIZED_TYPE.get(
321                      StaticUtils.toHex(e.getType())));
322        }
323      }
324
325      if (otp == null)
326      {
327        throw new LDAPException(ResultCode.DECODING_ERROR,
328             ERR_REGISTER_YUBIKEY_OTP_REQUEST_MISSING_OTP.get());
329      }
330
331      authenticationID = authID;
332      staticPassword   = staticPW;
333      yubiKeyOTP       = otp;
334    }
335    catch (final LDAPException le)
336    {
337      Debug.debugException(le);
338      throw le;
339    }
340    catch (final Exception e)
341    {
342      Debug.debugException(e);
343      throw new LDAPException(ResultCode.DECODING_ERROR,
344           ERR_REGISTER_YUBIKEY_OTP_REQUEST_ERROR_DECODING_VALUE.get(
345                StaticUtils.getExceptionMessage(e)),
346           e);
347    }
348  }
349
350
351
352  /**
353   * Encodes the provided password as an ASN.1 octet string suitable for
354   * inclusion in the encoded request.
355   *
356   * @param  password  The password to be encoded.  It may be {@code null} if
357   *                   no password should be included.  If it is
358   *                   non-{@code null}, then it must be a string or a byte
359   *                   array.
360   *
361   * @return  The encoded password, or {@code null} if no password was given.
362   */
363  @Nullable()
364  static ASN1OctetString encodePassword(@Nullable final Object password)
365  {
366    if (password == null)
367    {
368      return null;
369    }
370    else if (password instanceof byte[])
371    {
372      return new ASN1OctetString(TYPE_STATIC_PASSWORD, (byte[]) password);
373    }
374    else
375    {
376      return new ASN1OctetString(TYPE_STATIC_PASSWORD,
377           String.valueOf(password));
378    }
379  }
380
381
382
383  /**
384   * Encodes the provided information into an ASN.1 octet string suitable for
385   * use as the value of this extended request.
386   *
387   * @param  authenticationID  The authentication ID that identifies the user
388   *                           for whom the YubiKey OTP device is to be
389   *                           registered.  It may be {@code null} if the device
390   *                           is to be registered for the user as whom the
391   *                           underlying connection is authenticated.
392   * @param  staticPassword    The static password of the user for whom the
393   *                           device is to be registered.  It may be
394   *                           {@code null} if the device is to be registered
395   *                           for a user other than the user authenticated on
396   *                           the underlying connection and the server is
397   *                           configured to not require the target user's
398   *                           static password in this case.
399   * @param  yubiKeyOTP        A one-time password generated by the YubiKey
400   *                           device to be registered.  It must not be
401   *                           {@code null}.
402   *
403   * @return  The ASN.1 octet string containing the encoded request value.
404   */
405  @NotNull()
406  private static ASN1OctetString encodeValue(
407               @Nullable final String authenticationID,
408               @Nullable final ASN1OctetString staticPassword,
409               @NotNull final String yubiKeyOTP)
410  {
411    Validator.ensureNotNull(yubiKeyOTP);
412
413    final ArrayList<ASN1Element> elements = new ArrayList<>(3);
414
415    if (authenticationID != null)
416    {
417      elements.add(
418           new ASN1OctetString(TYPE_AUTHENTICATION_ID, authenticationID));
419    }
420
421    if (staticPassword != null)
422    {
423      elements.add(staticPassword);
424    }
425
426    elements.add(new ASN1OctetString(TYPE_YUBIKEY_OTP, yubiKeyOTP));
427    return new ASN1OctetString(new ASN1Sequence(elements).encode());
428  }
429
430
431
432  /**
433   * Retrieves the authentication ID that identifies the user for whom the
434   * YubiKey OTP device is to be registered, if provided.
435   *
436   * @return  The authentication ID that identifies the target user, or
437   *          {@code null} if the device is to be registered as the user as
438   *          whom the underlying connection is authenticated.
439   */
440  @Nullable()
441  public String getAuthenticationID()
442  {
443    return authenticationID;
444  }
445
446
447
448  /**
449   * Retrieves the string representation of the static password for the target
450   * user, if provided.
451   *
452   * @return  The string representation of the static password for the target
453   *          user, or {@code null} if no static password was provided.
454   */
455  @Nullable()
456  public String getStaticPasswordString()
457  {
458    if (staticPassword == null)
459    {
460      return null;
461    }
462    else
463    {
464      return staticPassword.stringValue();
465    }
466  }
467
468
469
470  /**
471   * Retrieves the bytes that comprise the static password for the target user,
472   * if provided.
473   *
474   * @return  The bytes that comprise the static password for the target user,
475   *          or {@code null} if no static password was provided.
476   */
477  @Nullable()
478  public byte[] getStaticPasswordBytes()
479  {
480    if (staticPassword == null)
481    {
482      return null;
483    }
484    else
485    {
486      return staticPassword.getValue();
487    }
488  }
489
490
491
492  /**
493   * Retrieves a one-time password generated by the YubiKey device to be
494   * registered.
495   *
496   * @return  A one-time password generated by the YubiKey device to be
497   *          registered.
498   */
499  @NotNull()
500  public String getYubiKeyOTP()
501  {
502    return yubiKeyOTP;
503  }
504
505
506
507  /**
508   * {@inheritDoc}
509   */
510  @Override()
511  @NotNull()
512  public RegisterYubiKeyOTPDeviceExtendedRequest duplicate()
513  {
514    return duplicate(getControls());
515  }
516
517
518
519  /**
520   * {@inheritDoc}
521   */
522  @Override()
523  @NotNull()
524  public RegisterYubiKeyOTPDeviceExtendedRequest duplicate(
525              @Nullable final Control[] controls)
526  {
527    final RegisterYubiKeyOTPDeviceExtendedRequest r =
528         new RegisterYubiKeyOTPDeviceExtendedRequest(authenticationID,
529              staticPassword, yubiKeyOTP, controls);
530    r.setResponseTimeoutMillis(getResponseTimeoutMillis(null));
531    r.setIntermediateResponseListener(getIntermediateResponseListener());
532    r.setReferralDepth(getReferralDepth());
533    r.setReferralConnector(getReferralConnectorInternal());
534    return r;
535  }
536
537
538
539  /**
540   * {@inheritDoc}
541   */
542  @Override()
543  @NotNull()
544  public String getExtendedRequestName()
545  {
546    return INFO_REGISTER_YUBIKEY_OTP_REQUEST_NAME.get();
547  }
548
549
550
551  /**
552   * {@inheritDoc}
553   */
554  @Override()
555  public void toString(@NotNull final StringBuilder buffer)
556  {
557    buffer.append("RegisterYubiKeyOTPDeviceExtendedRequest(");
558
559    if (authenticationID != null)
560    {
561      buffer.append("authenticationID='");
562      buffer.append(authenticationID);
563      buffer.append("', ");
564    }
565
566    buffer.append("staticPasswordProvided=");
567    buffer.append(staticPassword != null);
568
569    final Control[] controls = getControls();
570    if (controls.length > 0)
571    {
572      buffer.append(", controls={");
573      for (int i=0; i < controls.length; i++)
574      {
575        if (i > 0)
576        {
577          buffer.append(", ");
578        }
579
580        buffer.append(controls[i]);
581      }
582      buffer.append('}');
583    }
584
585    buffer.append(')');
586  }
587}