001/*
002 * Copyright 2016-2019 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2016-2019 Ping Identity Corporation
007 *
008 * This program is free software; you can redistribute it and/or modify
009 * it under the terms of the GNU General Public License (GPLv2 only)
010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
011 * as published by the Free Software Foundation.
012 *
013 * This program is distributed in the hope that it will be useful,
014 * but WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
016 * GNU General Public License for more details.
017 *
018 * You should have received a copy of the GNU General Public License
019 * along with this program; if not, see <http://www.gnu.org/licenses>.
020 */
021package com.unboundid.ldap.sdk.unboundidds.extensions;
022
023
024
025import java.util.ArrayList;
026
027import com.unboundid.asn1.ASN1Element;
028import com.unboundid.asn1.ASN1OctetString;
029import com.unboundid.asn1.ASN1Sequence;
030import com.unboundid.ldap.sdk.Control;
031import com.unboundid.ldap.sdk.ExtendedRequest;
032import com.unboundid.ldap.sdk.LDAPConnection;
033import com.unboundid.ldap.sdk.LDAPException;
034import com.unboundid.ldap.sdk.ResultCode;
035import com.unboundid.util.Debug;
036import com.unboundid.util.NotMutable;
037import com.unboundid.util.StaticUtils;
038import com.unboundid.util.ThreadSafety;
039import com.unboundid.util.ThreadSafetyLevel;
040import com.unboundid.util.Validator;
041
042import static com.unboundid.ldap.sdk.unboundidds.extensions.ExtOpMessages.*;
043
044
045
046/**
047 * This class provides an implementation of an extended request that may be used
048 * to generate a shared secret for use in generating TOTP authentication codes
049 * (as per <A HREF="http://www.ietf.org/rfc/rfc6238.txt">RFC 6238</A>, for
050 * example, using the mechanism provided in the
051 * {@link com.unboundid.ldap.sdk.unboundidds.OneTimePassword} class), which can
052 * be used to authenticate to the server via the
053 * {@link com.unboundid.ldap.sdk.unboundidds.UnboundIDTOTPBindRequest}.
054 * <BR>
055 * <BLOCKQUOTE>
056 *   <B>NOTE:</B>  This class, and other classes within the
057 *   {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only
058 *   supported for use against Ping Identity, UnboundID, and
059 *   Nokia/Alcatel-Lucent 8661 server products.  These classes provide support
060 *   for proprietary functionality or for external specifications that are not
061 *   considered stable or mature enough to be guaranteed to work in an
062 *   interoperable way with other types of LDAP servers.
063 * </BLOCKQUOTE>
064 * <BR>
065 * This request may be invoked in one of following ways:
066 * <BR><BR>
067 * <UL>
068 *   <LI>
069 *     With a {@code null} authentication identity and a non-{@code null} static
070 *     password.  In this case, the authorization identity for the operation
071 *     (typically the user as whom the underlying connection is authenticated,
072 *     but possibly a different user if the request also includes a control like
073 *     the proxied authorization or intermediate client request control that
074 *     specifies and alternate authorization identity, or if the client
075 *     authenticated with a SASL mechanism that included an alternate
076 *     authorization identity) will be used as the authentication identity for
077 *     this request, and the static password must be valid for that user.  This
078 *     will be treated as a user requesting a TOTP shared secret for their own
079 *     account.
080 *   </LI>
081 *   <LI>
082 *     With a non-{@code null} authentication identity (which may or may not
083 *     match the authorization identity for the operation) and a
084 *     non-{@code null} static password that is valid for the provided
085 *     authentication identity.  This will also be treated as a user requesting
086 *     a TOTP shared secret for their own account.
087 *   </LI>
088 *   <LI>
089 *     With a non-{@code null} authentication identity and a {@code null} static
090 *     password.  In this case, the authentication identity must not match the
091 *     authorization identity for the operation, and the authorization identity
092 *     must have the password-reset privilege.  This will be treated as an
093 *     administrator requesting a TOTP shared secret on behalf of a user and is
094 *     recommended only for the case in which the identity of the user has been
095 *     verified through some means other than a static password.
096 *   </LI>
097 * </UL>
098 * <BR><BR>
099 * If the request is processed successfully, the server will generate a TOTP
100 * shared secret for the user, will store it in the user's entry, and will
101 * return that secret back to the client via the
102 * {@link GenerateTOTPSharedSecretExtendedResult}.
103 * <BR><BR>
104 * Note that this operation will not interfere with any other TOTP shared
105 * secrets that may already exist in the user's entry; the new shared secret
106 * will be merged with any existing shared secret values for the user.  If a
107 * TOTP shared secret is no longer needed, the
108 * {@link RevokeTOTPSharedSecretExtendedRequest} may be used to remove it from
109 * the user's account.
110 * <BR><BR>
111 * This extended request has an OID of 1.3.6.1.4.1.30221.2.6.56, and it must
112 * include a request value with the following encoding:
113 * <BR><BR>
114 * <PRE>
115 *   GenerateTOTPSharedSecretRequest ::= SEQUENCE {
116 *        authenticationID     [0] OCTET STRING OPTIONAL,
117 *        staticPassword       [1] OCTET STRING OPTIONAL,
118 *        ... }
119 * </PRE>
120 *
121 */
122@NotMutable()
123@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
124public final class GenerateTOTPSharedSecretExtendedRequest
125       extends ExtendedRequest
126{
127  /**
128   * The OID (1.3.6.1.4.1.30221.2.6.56) for the generate TOTP shared secret
129   * extended request.
130   */
131  public static final String GENERATE_TOTP_SHARED_SECRET_REQUEST_OID =
132       "1.3.6.1.4.1.30221.2.6.56";
133
134
135
136  /**
137   * The BER type for the authentication ID element of the request value
138   * sequence.
139   */
140  private static final byte TYPE_AUTHENTICATION_ID = (byte) 0x80;
141
142
143
144  /**
145   * The BER type for the static password element of the request value sequence.
146   */
147  private static final byte TYPE_STATIC_PASSWORD = (byte) 0x81;
148
149
150
151  /**
152   * The serial version UID for this serializable class.
153   */
154  private static final long serialVersionUID = -1617090986047944957L;
155
156
157
158  // The static password for the request.
159  private final ASN1OctetString staticPassword;
160
161  // The authentication ID for the request.
162  private final String authenticationID;
163
164
165
166  /**
167   * Creates a new generate TOTP shared secret extended request with the
168   * provided information.
169   *
170   * @param  authenticationID  The authentication ID to use to identify the user
171   *                           for whom to generate the TOTP shared secret.  It
172   *                           should be a string in the form "dn:" followed by
173   *                           the DN of the target user, or "u:" followed by
174   *                           the username.  It may be {@code null} if the TOTP
175   *                           shared secret is to be generated for the
176   *                           authorization identity for the operation, and
177   *                           only if the {@code staticPassword} is
178   *                           non-{@code null}).
179   * @param  staticPassword    The static password of the user for whom to
180   *                           generate the TOTP shared secret.  It may be
181   *                           {@code null} only if the {@code authenticationID}
182   *                           is non-{@code null}, is different from the
183   *                           operation's authorization identity, and the
184   *                           operation's authorization identity has the
185   *                           password-reset privilege.
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 GenerateTOTPSharedSecretExtendedRequest(final String authenticationID,
191                                                 final String staticPassword,
192                                                 final Control... controls)
193  {
194    this(authenticationID, encodePassword(staticPassword), controls);
195  }
196
197
198
199  /**
200   * Creates a new generate TOTP shared secret extended request with the
201   * provided information.
202   *
203   * @param  authenticationID  The authentication ID to use to identify the user
204   *                           for whom to generate the TOTP shared secret.  It
205   *                           should be a string in the form "dn:" followed by
206   *                           the DN of the target user, or "u:" followed by
207   *                           the username.  It may be {@code null} if the TOTP
208   *                           shared secret is to be generated for the
209   *                           authorization identity for the operation, and
210   *                           only if the {@code staticPassword} is
211   *                           non-{@code null}).
212   * @param  staticPassword    The static password of the user for whom to
213   *                           generate the TOTP shared secret.  It may be
214   *                           {@code null} only if the {@code authenticationID}
215   *                           is non-{@code null}, is different from the
216   *                           operation's authorization identity, and the
217   *                           operation's authorization identity has the
218   *                           password-reset privilege.
219   * @param  controls          The set of controls to include in the request.
220   *                           It may be {@code null} or empty if there should
221   *                           not be any request controls.
222   */
223  public GenerateTOTPSharedSecretExtendedRequest(final String authenticationID,
224                                                 final byte[] staticPassword,
225                                                 final Control... controls)
226  {
227    this(authenticationID, encodePassword(staticPassword), controls);
228  }
229
230
231
232  /**
233   * Creates a new generate TOTP shared secret extended request with the
234   * provided information.
235   *
236   * @param  authenticationID  The authentication ID to use to identify the user
237   *                           for whom to generate the TOTP shared secret.  It
238   *                           should be a string in the form "dn:" followed by
239   *                           the DN of the target user, or "u:" followed by
240   *                           the username.  It may be {@code null} if the TOTP
241   *                           shared secret is to be generated for the
242   *                           authorization identity for the operation, and
243   *                           only if the {@code staticPassword} is
244   *                           non-{@code null}).
245   * @param  staticPassword    The static password of the user for whom to
246   *                           generate the TOTP shared secret.  It may be
247   *                           {@code null} only if the {@code authenticationID}
248   *                           is non-{@code null}, is different from the
249   *                           operation's authorization identity, and the
250   *                           operation's authorization identity has the
251   *                           password-reset privilege.
252   * @param  controls          The set of controls to include in the request.
253   *                           It may be {@code null} or empty if there should
254   *                           not be any request controls.
255   */
256  public GenerateTOTPSharedSecretExtendedRequest(final String authenticationID,
257               final ASN1OctetString staticPassword, final Control... controls)
258  {
259    super(GENERATE_TOTP_SHARED_SECRET_REQUEST_OID,
260         encodeValue(authenticationID, staticPassword), controls);
261
262    this.authenticationID = authenticationID;
263    this.staticPassword   = staticPassword;
264  }
265
266
267
268  /**
269   * Creates a new generate TOTP shared secret extended request that is decoded
270   * from the provided generic extended request.
271   *
272   * @param  request  The generic extended request to decode as a generate TOTP
273   *                  shared secret request.
274   *
275   * @throws  LDAPException  If a problem is encountered while attempting to
276   *                         decode the provided request.
277   */
278  public GenerateTOTPSharedSecretExtendedRequest(final ExtendedRequest request)
279         throws LDAPException
280  {
281    super(request);
282
283    final ASN1OctetString value = request.getValue();
284    if (value == null)
285    {
286      throw new LDAPException(ResultCode.DECODING_ERROR,
287           ERR_GEN_TOTP_SECRET_REQUEST_NO_VALUE.get());
288    }
289
290    try
291    {
292      String authID = null;
293      ASN1OctetString staticPW = null;
294      for (final ASN1Element e :
295           ASN1Sequence.decodeAsSequence(value.getValue()).elements())
296      {
297        switch (e.getType())
298        {
299          case TYPE_AUTHENTICATION_ID:
300            authID = ASN1OctetString.decodeAsOctetString(e).stringValue();
301            break;
302          case TYPE_STATIC_PASSWORD:
303            staticPW = ASN1OctetString.decodeAsOctetString(e);
304            break;
305          default:
306            throw new LDAPException(ResultCode.DECODING_ERROR,
307                 ERR_GEN_TOTP_SECRET_REQUEST_UNRECOGNIZED_TYPE.get(
308                      StaticUtils.toHex(e.getType())));
309        }
310      }
311
312      if ((authID == null) && (staticPW == null))
313      {
314        throw new LDAPException(ResultCode.DECODING_ERROR,
315             ERR_GEN_TOTP_SECRET_REQUEST_NEITHER_AUTHN_ID_NOR_PW.get());
316      }
317
318      authenticationID = authID;
319      staticPassword   = staticPW;
320    }
321    catch (final LDAPException le)
322    {
323      Debug.debugException(le);
324      throw le;
325    }
326    catch (final Exception e)
327    {
328      Debug.debugException(e);
329      throw new LDAPException(ResultCode.DECODING_ERROR,
330           ERR_GEN_TOTP_SECRET_REQUEST_ERROR_DECODING_VALUE.get(
331                StaticUtils.getExceptionMessage(e)),
332           e);
333    }
334  }
335
336
337
338  /**
339   * Encodes the provided password as an ASN.1 octet string suitable for
340   * inclusion in the encoded request.
341   *
342   * @param  password  The password to be encoded.  It may be {@code null} if
343   *                   no password should be included.  If it is
344   *                   non-{@code null}, then it must be a string or a byte
345   *                   array.
346   *
347   * @return  The encoded password, or {@code null} if no password was given.
348   */
349  private static ASN1OctetString encodePassword(final Object password)
350  {
351    if (password == null)
352    {
353      return null;
354    }
355    else if (password instanceof byte[])
356    {
357      return new ASN1OctetString(TYPE_STATIC_PASSWORD, (byte[]) password);
358    }
359    else
360    {
361      return new ASN1OctetString(TYPE_STATIC_PASSWORD,
362           String.valueOf(password));
363    }
364  }
365
366
367
368  /**
369   * Encodes the provided information into an ASN.1 octet string suitable for
370   * use as the value of this extended request.
371   *
372   * @param  authenticationID  The authentication ID to use to identify the user
373   *                           for whom to generate the TOTP shared secret.  It
374   *                           should be a string in the form "dn:" followed by
375   *                           the DN of the target user, or "u:" followed by
376   *                           the username.  It may be {@code null} if the TOTP
377   *                           shared secret is to be generated for the
378   *                           authorization identity for the operation, and
379   *                           only if the {@code staticPassword} is
380   *                           non-{@code null}).
381   * @param  staticPassword    The static password of the user for whom to
382   *                           generate the TOTP shared secret.  It may be
383   *                           {@code null} only if the {@code authenticationID}
384   *                           is non-{@code null}, is different from the
385   *                           operation's authorization identity, and the
386   *                           operation's authorization identity has the
387   *                           password-reset privilege.
388   *
389   * @return  The ASN.1 octet string containing the encoded request value.
390   */
391  private static ASN1OctetString encodeValue(final String authenticationID,
392                                      final ASN1OctetString staticPassword)
393  {
394    if (authenticationID == null)
395    {
396      Validator.ensureTrue((staticPassword != null),
397           "If the authentication ID is null, the static password must be " +
398                "non-null.");
399    }
400
401    final ArrayList<ASN1Element> elements = new ArrayList<>(2);
402
403    if (authenticationID != null)
404    {
405      elements.add(
406           new ASN1OctetString(TYPE_AUTHENTICATION_ID, authenticationID));
407    }
408
409    if (staticPassword != null)
410    {
411      elements.add(staticPassword);
412    }
413
414    return new ASN1OctetString(new ASN1Sequence(elements).encode());
415  }
416
417
418
419  /**
420   * Retrieves the authentication ID that identifies the user for whom to
421   * generate the TOTP shared secret, if provided.
422   *
423   * @return  The authentication ID that identifies the target user, or
424   *          {@code null} if the shared secret is to be generated for the
425   *          authorization identity associated with the extended request.
426   */
427  public String getAuthenticationID()
428  {
429    return authenticationID;
430  }
431
432
433
434  /**
435   * Retrieves the string representation of the static password for the target
436   * user, if provided.
437   *
438   * @return  The string representation of the static password for the target
439   *          user, or {@code null} if no static password was provided.
440   */
441  public String getStaticPasswordString()
442  {
443    if (staticPassword == null)
444    {
445      return null;
446    }
447    else
448    {
449      return staticPassword.stringValue();
450    }
451  }
452
453
454
455  /**
456   * Retrieves the bytes that comprise the static password for the target user,
457   * if provided.
458   *
459   * @return  The bytes that comprise the static password for the target user,
460   *          or {@code null} if no static password was provided.
461   */
462  public byte[] getStaticPasswordBytes()
463  {
464    if (staticPassword == null)
465    {
466      return null;
467    }
468    else
469    {
470      return staticPassword.getValue();
471    }
472  }
473
474
475
476  /**
477   * {@inheritDoc}
478   */
479  @Override()
480  protected GenerateTOTPSharedSecretExtendedResult process(
481                 final LDAPConnection connection, final int depth)
482            throws LDAPException
483  {
484    return new GenerateTOTPSharedSecretExtendedResult(
485         super.process(connection, depth));
486  }
487
488
489
490  /**
491   * {@inheritDoc}
492   */
493  @Override()
494  public GenerateTOTPSharedSecretExtendedRequest duplicate()
495  {
496    return duplicate(getControls());
497  }
498
499
500
501  /**
502   * {@inheritDoc}
503   */
504  @Override()
505  public GenerateTOTPSharedSecretExtendedRequest duplicate(
506                                                      final Control[] controls)
507  {
508    final GenerateTOTPSharedSecretExtendedRequest r =
509         new GenerateTOTPSharedSecretExtendedRequest(authenticationID,
510              staticPassword, controls);
511    r.setResponseTimeoutMillis(getResponseTimeoutMillis(null));
512    return r;
513  }
514
515
516
517  /**
518   * {@inheritDoc}
519   */
520  @Override()
521  public String getExtendedRequestName()
522  {
523    return INFO_GEN_TOTP_SECRET_REQUEST_NAME.get();
524  }
525
526
527
528  /**
529   * {@inheritDoc}
530   */
531  @Override()
532  public void toString(final StringBuilder buffer)
533  {
534    buffer.append("GenerateTOTPSharedSecretExtendedRequest(");
535
536    if (authenticationID != null)
537    {
538      buffer.append("authenticationID='");
539      buffer.append(authenticationID);
540      buffer.append("', ");
541    }
542
543    buffer.append("staticPasswordProvided=");
544    buffer.append(staticPassword != null);
545
546    final Control[] controls = getControls();
547    if (controls.length > 0)
548    {
549      buffer.append(", controls={");
550      for (int i=0; i < controls.length; i++)
551      {
552        if (i > 0)
553        {
554          buffer.append(", ");
555        }
556
557        buffer.append(controls[i]);
558      }
559      buffer.append('}');
560    }
561
562    buffer.append(')');
563  }
564}