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