001/*
002 * Copyright 2012-2019 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2015-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;
022
023
024
025import java.nio.charset.StandardCharsets;
026import java.util.ArrayList;
027import java.util.List;
028
029import com.unboundid.asn1.ASN1OctetString;
030import com.unboundid.ldap.sdk.Control;
031import com.unboundid.ldap.sdk.LDAPException;
032import com.unboundid.ldap.sdk.ToCodeArgHelper;
033import com.unboundid.ldap.sdk.ToCodeHelper;
034import com.unboundid.util.NotMutable;
035import com.unboundid.util.ThreadSafety;
036import com.unboundid.util.ThreadSafetyLevel;
037import com.unboundid.util.Validator;
038
039
040
041/**
042 * This class provides an implementation of the UNBOUNDID-TOTP SASL bind request
043 * that may be used to repeatedly generate one-time password values.  Because it
044 * is configured with the shared secret rather than a point-in-time version of
045 * the password, it can be used for cases in which the authentication process
046 * may need to be repeated (e.g., for use in a connection pool, following
047 * referrals, or if the auto-reconnect feature is enabled).  If the shared
048 * secret is not known and the one-time password will be provided from an
049 * external source (e.g., entered by a user), then the
050 * {@link SingleUseTOTPBindRequest} variant should be used instead.
051 * <BR>
052 * <BLOCKQUOTE>
053 *   <B>NOTE:</B>  This class, and other classes within the
054 *   {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only
055 *   supported for use against Ping Identity, UnboundID, and
056 *   Nokia/Alcatel-Lucent 8661 server products.  These classes provide support
057 *   for proprietary functionality or for external specifications that are not
058 *   considered stable or mature enough to be guaranteed to work in an
059 *   interoperable way with other types of LDAP servers.
060 * </BLOCKQUOTE>
061 */
062@NotMutable()
063@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE)
064public final class ReusableTOTPBindRequest
065       extends UnboundIDTOTPBindRequest
066{
067  /**
068   * The serial version UID for this serializable class.
069   */
070  private static final long serialVersionUID = -8283436883838802510L;
071
072
073
074  // The shared secret key to use when generating the TOTP password.
075  private final byte[] sharedSecret;
076
077  // The duration (in seconds) of the time interval to use when generating the
078  // TOTP password.
079  private final int totpIntervalDurationSeconds;
080
081  // The number of digits to include in the generated TOTP password.
082  private final int totpNumDigits;
083
084
085
086  /**
087   * Creates a new SASL TOTP bind request with the provided information.
088   *
089   * @param  authenticationID  The authentication identity for the bind request.
090   *                           It must not be {@code null}, and must be in the
091   *                           form "u:" followed by a username, or "dn:"
092   *                           followed by a DN.
093   * @param  authorizationID   The authorization identity for the bind request.
094   *                           It may be {@code null} if the authorization
095   *                           identity should be the same as the authentication
096   *                           identity.  If an authorization identity is
097   *                           specified, it must be in the form "u:" followed
098   *                           by a username, or "dn:" followed by a DN.  The
099   *                           value "dn:" may indicate an authorization
100   *                           identity of the anonymous user.
101   * @param  sharedSecret      The shared secret key to use when generating the
102   *                           TOTP password.
103   * @param  staticPassword    The static password for the target user.  It may
104   *                           be {@code null} if only the one-time password is
105   *                           to be used for authentication (which may or may
106   *                           not be allowed by the server).
107   * @param  controls          The set of controls to include in the bind
108   *                           request.
109   */
110  public ReusableTOTPBindRequest(final String authenticationID,
111                                 final String authorizationID,
112                                 final byte[] sharedSecret,
113                                 final String staticPassword,
114                                 final Control... controls)
115  {
116    this(authenticationID, authorizationID, sharedSecret, staticPassword,
117         OneTimePassword.DEFAULT_TOTP_INTERVAL_DURATION_SECONDS,
118         OneTimePassword.DEFAULT_TOTP_NUM_DIGITS, controls);
119  }
120
121
122
123  /**
124   * Creates a new SASL TOTP bind request with the provided information.
125   *
126   * @param  authenticationID  The authentication identity for the bind request.
127   *                           It must not be {@code null}, and must be in the
128   *                           form "u:" followed by a username, or "dn:"
129   *                           followed by a DN.
130   * @param  authorizationID   The authorization identity for the bind request.
131   *                           It may be {@code null} if the authorization
132   *                           identity should be the same as the authentication
133   *                           identity.  If an authorization identity is
134   *                           specified, it must be in the form "u:" followed
135   *                           by a username, or "dn:" followed by a DN.  The
136   *                           value "dn:" may indicate an authorization
137   *                           identity of the anonymous user.
138   * @param  sharedSecret      The shared secret key to use when generating the
139   *                           TOTP password.
140   * @param  staticPassword    The static password for the target user.  It may
141   *                           be {@code null} if only the one-time password is
142   *                           to be used for authentication (which may or may
143   *                           not be allowed by the server).
144   * @param  controls          The set of controls to include in the bind
145   *                           request.
146   */
147  public ReusableTOTPBindRequest(final String authenticationID,
148                                 final String authorizationID,
149                                 final byte[] sharedSecret,
150                                 final byte[] staticPassword,
151                                 final Control... controls)
152  {
153    this(authenticationID, authorizationID, sharedSecret, staticPassword,
154         OneTimePassword.DEFAULT_TOTP_INTERVAL_DURATION_SECONDS,
155         OneTimePassword.DEFAULT_TOTP_NUM_DIGITS, controls);
156  }
157
158
159
160  /**
161   * Creates a new SASL TOTP bind request with the provided information.
162   *
163   * @param  authenticationID             The authentication identity for the
164   *                                      bind request.  It must not be
165   *                                      {@code null}, and must be in the form
166   *                                      "u:" followed by a username, or "dn:"
167   *                                      followed by a DN.
168   * @param  authorizationID              The authorization identity for the
169   *                                      bind request.  It may be {@code null}
170   *                                      if the authorization identity should
171   *                                      be the same as the authentication
172   *                                      identity.  If an authorization
173   *                                      identity is specified, it must be in
174   *                                      the form "u:" followed by a username,
175   *                                      or "dn:" followed by a DN.  The value
176   *                                      "dn:" may indicate an authorization
177   *                                      identity of the anonymous user.
178   * @param  sharedSecret                 The shared secret key to use when
179   *                                      generating the TOTP password.
180   * @param  staticPassword               The static password for the target
181   *                                      user.  It may be {@code null} if only
182   *                                      the one-time password is to be used
183   *                                      for authentication (which may or may
184   *                                      not be allowed by the server).
185   * @param  totpIntervalDurationSeconds  The duration (in seconds) of the time
186   *                                      interval to use for TOTP processing.
187   *                                      It must be greater than zero.
188   * @param  totpNumDigits                The number of digits to include in the
189   *                                      generated TOTP password.  It must be
190   *                                      greater than or equal to six and less
191   *                                      than or equal to eight.
192   * @param  controls                     The set of controls to include in the
193   *                                      bind request.
194   */
195  public ReusableTOTPBindRequest(final String authenticationID,
196                                 final String authorizationID,
197                                 final byte[] sharedSecret,
198                                 final String staticPassword,
199                                 final int totpIntervalDurationSeconds,
200                                 final int totpNumDigits,
201                                 final Control... controls)
202  {
203    super(authenticationID, authorizationID, staticPassword, controls);
204
205    Validator.ensureTrue(totpIntervalDurationSeconds > 0);
206    Validator.ensureTrue((totpNumDigits >= 6) && (totpNumDigits <= 8));
207
208    this.sharedSecret                = sharedSecret;
209    this.totpIntervalDurationSeconds = totpIntervalDurationSeconds;
210    this.totpNumDigits               = totpNumDigits;
211  }
212
213
214
215  /**
216   * Creates a new SASL TOTP bind request with the provided information.
217   *
218   * @param  authenticationID             The authentication identity for the
219   *                                      bind request.  It must not be
220   *                                      {@code null}, and must be in the form
221   *                                      "u:" followed by a username, or "dn:"
222   *                                      followed by a DN.
223   * @param  authorizationID              The authorization identity for the
224   *                                      bind request.  It may be {@code null}
225   *                                      if the authorization identity should
226   *                                      be the same as the authentication
227   *                                      identity.  If an authorization
228   *                                      identity is specified, it must be in
229   *                                      the form "u:" followed by a username,
230   *                                      or "dn:" followed by a DN.  The value
231   *                                      "dn:" may indicate an authorization
232   *                                      identity of the anonymous user.
233   * @param  sharedSecret                 The shared secret key to use when
234   *                                      generating the TOTP password.
235   * @param  staticPassword               The static password for the target
236   *                                      user.  It may be {@code null} if only
237   *                                      the one-time password is to be used
238   *                                      for authentication (which may or may
239   *                                      not be allowed by the server).
240   * @param  totpIntervalDurationSeconds  The duration (in seconds) of the time
241   *                                      interval to use for TOTP processing.
242   *                                      It must be greater than zero.
243   * @param  totpNumDigits                The number of digits to include in the
244   *                                      generated TOTP password.  It must be
245   *                                      greater than or equal to six and less
246   *                                      than or equal to eight.
247   * @param  controls                     The set of controls to include in the
248   *                                      bind request.
249   */
250  public ReusableTOTPBindRequest(final String authenticationID,
251                                 final String authorizationID,
252                                 final byte[] sharedSecret,
253                                 final byte[] staticPassword,
254                                 final int totpIntervalDurationSeconds,
255                                 final int totpNumDigits,
256                                 final Control... controls)
257  {
258    super(authenticationID, authorizationID, staticPassword, controls);
259
260    Validator.ensureTrue(totpIntervalDurationSeconds > 0);
261    Validator.ensureTrue((totpNumDigits >= 6) && (totpNumDigits <= 8));
262
263    this.sharedSecret                = sharedSecret;
264    this.totpIntervalDurationSeconds = totpIntervalDurationSeconds;
265    this.totpNumDigits               = totpNumDigits;
266  }
267
268
269
270  /**
271   * Creates a new SASL TOTP bind request with the provided information.
272   *
273   * @param  authenticationID             The authentication identity for the
274   *                                      bind request.  It must not be
275   *                                      {@code null}, and must be in the form
276   *                                      "u:" followed by a username, or "dn:"
277   *                                      followed by a DN.
278   * @param  authorizationID              The authorization identity for the
279   *                                      bind request.  It may be {@code null}
280   *                                      if the authorization identity should
281   *                                      be the same as the authentication
282   *                                      identity.  If an authorization
283   *                                      identity is specified, it must be in
284   *                                      the form "u:" followed by a username,
285   *                                      or "dn:" followed by a DN.  The value
286   *                                      "dn:" may indicate an authorization
287   *                                      identity of the anonymous user.
288   * @param  sharedSecret                 The shared secret key to use when
289   *                                      generating the TOTP password.
290   * @param  staticPassword               The static password for the target
291   *                                      user.  It may be {@code null} if only
292   *                                      the one-time password is to be used
293   *                                      for authentication (which may or may
294   *                                      not be allowed by the server).
295   * @param  totpIntervalDurationSeconds  The duration (in seconds) of the time
296   *                                      interval to use when generating the
297   *                                      TOTP password.  It must be greater
298   *                                      than zero.
299   * @param  totpNumDigits                The number of digits to include in the
300   *                                      generated TOTP password.  It must be
301   *                                      greater than or equal to six and less
302   *                                      than or equal to eight.
303   * @param  controls                     The set of controls to include in the
304   *                                      bind request.
305   */
306  private ReusableTOTPBindRequest(final String authenticationID,
307                                  final String authorizationID,
308                                  final byte[] sharedSecret,
309                                  final ASN1OctetString staticPassword,
310                                  final int totpIntervalDurationSeconds,
311                                  final int totpNumDigits,
312                                  final Control... controls)
313  {
314    super(authenticationID, authorizationID, staticPassword, controls);
315
316    this.sharedSecret                = sharedSecret;
317    this.totpIntervalDurationSeconds = totpIntervalDurationSeconds;
318    this.totpNumDigits               = totpNumDigits;
319  }
320
321
322
323  /**
324   * Retrieves the shared secret key to use when generating the TOTP password.
325   *
326   * @return  The shared secret key to use when generating the TOTP password.
327   */
328  public byte[] getSharedSecret()
329  {
330    return sharedSecret;
331  }
332
333
334
335  /**
336   * Retrieves the duration (in seconds) of the time interval to use when
337   * generating the TOTP password.
338   *
339   * @return  The duration (in seconds) of the time interval to use when
340   *          generating the TOTP password.
341   */
342  public int getTOTPIntervalDurationSeconds()
343  {
344    return totpIntervalDurationSeconds;
345  }
346
347
348
349  /**
350   * Retrieves the number of digits to include in the generated TOTP password.
351   *
352   * @return  The number of digits to include in the generated TOTP password.
353   */
354  public int getTOTPNumDigits()
355  {
356    return totpNumDigits;
357  }
358
359
360
361  /**
362   * {@inheritDoc}
363   */
364  @Override()
365  protected ASN1OctetString getSASLCredentials()
366            throws LDAPException
367  {
368    // Generate the TOTP password.
369    final String totpPassword = OneTimePassword.totp(sharedSecret,
370         System.currentTimeMillis(), totpIntervalDurationSeconds,
371         totpNumDigits);
372
373    return encodeCredentials(getAuthenticationID(), getAuthorizationID(),
374         totpPassword, getStaticPassword());
375  }
376
377
378
379  /**
380   * {@inheritDoc}
381   */
382  @Override()
383  public ReusableTOTPBindRequest getRebindRequest(final String host,
384                                                  final int port)
385  {
386    return duplicate();
387  }
388
389
390
391  /**
392   * {@inheritDoc}
393   */
394  @Override()
395  public ReusableTOTPBindRequest duplicate()
396  {
397    return duplicate(getControls());
398  }
399
400
401
402  /**
403   * {@inheritDoc}
404   */
405  @Override()
406  public ReusableTOTPBindRequest duplicate(final Control[] controls)
407  {
408    final ReusableTOTPBindRequest bindRequest =
409         new ReusableTOTPBindRequest(getAuthenticationID(),
410              getAuthorizationID(), sharedSecret, getStaticPassword(),
411              totpIntervalDurationSeconds, totpNumDigits, controls);
412    bindRequest.setResponseTimeoutMillis(getResponseTimeoutMillis(null));
413    return bindRequest;
414  }
415
416
417
418  /**
419   * {@inheritDoc}
420   */
421  @Override()
422  public void toCode(final List<String> lineList, final String requestID,
423                     final int indentSpaces, final boolean includeProcessing)
424  {
425    // Create the request variable.
426    final ArrayList<ToCodeArgHelper> constructorArgs = new ArrayList<>(7);
427    constructorArgs.add(ToCodeArgHelper.createString(getAuthenticationID(),
428         "Authentication ID"));
429    constructorArgs.add(ToCodeArgHelper.createString(getAuthorizationID(),
430         "Authorization ID"));
431    constructorArgs.add(ToCodeArgHelper.createByteArray(
432         "---redacted-secret---".getBytes(StandardCharsets.UTF_8), true,
433         "Shared Secret"));
434    constructorArgs.add(ToCodeArgHelper.createString(
435         ((getStaticPassword() == null) ? "null" : "---redacted-password---"),
436         "Static Password"));
437    constructorArgs.add(ToCodeArgHelper.createInteger(
438         totpIntervalDurationSeconds, "Interval Duration (seconds)"));
439    constructorArgs.add(ToCodeArgHelper.createInteger(totpNumDigits,
440         "Number of TOTP Digits"));
441
442    final Control[] controls = getControls();
443    if (controls.length > 0)
444    {
445      constructorArgs.add(ToCodeArgHelper.createControlArray(controls,
446           "Bind Controls"));
447    }
448
449    ToCodeHelper.generateMethodCall(lineList, indentSpaces,
450         "ReusableTOTPBindRequest", requestID + "Request",
451         "new ReusableTOTPBindRequest", constructorArgs);
452
453
454    // Add lines for processing the request and obtaining the result.
455    if (includeProcessing)
456    {
457      // Generate a string with the appropriate indent.
458      final StringBuilder buffer = new StringBuilder();
459      for (int i=0; i < indentSpaces; i++)
460      {
461        buffer.append(' ');
462      }
463      final String indent = buffer.toString();
464
465      lineList.add("");
466      lineList.add(indent + "try");
467      lineList.add(indent + '{');
468      lineList.add(indent + "  BindResult " + requestID +
469           "Result = connection.bind(" + requestID + "Request);");
470      lineList.add(indent + "  // The bind was processed successfully.");
471      lineList.add(indent + '}');
472      lineList.add(indent + "catch (LDAPException e)");
473      lineList.add(indent + '{');
474      lineList.add(indent + "  // The bind failed.  Maybe the following will " +
475           "help explain why.");
476      lineList.add(indent + "  // Note that the connection is now likely in " +
477           "an unauthenticated state.");
478      lineList.add(indent + "  ResultCode resultCode = e.getResultCode();");
479      lineList.add(indent + "  String message = e.getMessage();");
480      lineList.add(indent + "  String matchedDN = e.getMatchedDN();");
481      lineList.add(indent + "  String[] referralURLs = e.getReferralURLs();");
482      lineList.add(indent + "  Control[] responseControls = " +
483           "e.getResponseControls();");
484      lineList.add(indent + '}');
485    }
486  }
487}