001/* 002 * Copyright 2012-2018 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2015-2018 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 Alcatel-Lucent 8661 056 * server products. These classes provide support for proprietary 057 * functionality or for external specifications that are not considered stable 058 * or mature enough to be guaranteed to work in an interoperable way with 059 * 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); 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 = 427 new ArrayList<ToCodeArgHelper>(7); 428 constructorArgs.add(ToCodeArgHelper.createString(getAuthenticationID(), 429 "Authentication ID")); 430 constructorArgs.add(ToCodeArgHelper.createString(getAuthorizationID(), 431 "Authorization ID")); 432 constructorArgs.add(ToCodeArgHelper.createByteArray( 433 "---redacted-secret---".getBytes(StandardCharsets.UTF_8), true, 434 "Shared Secret")); 435 constructorArgs.add(ToCodeArgHelper.createString( 436 ((getStaticPassword() == null) ? "null" : "---redacted-password---"), 437 "Static Password")); 438 constructorArgs.add(ToCodeArgHelper.createInteger( 439 totpIntervalDurationSeconds, "Interval Duration (seconds)")); 440 constructorArgs.add(ToCodeArgHelper.createInteger(totpNumDigits, 441 "Number of TOTP Digits")); 442 443 final Control[] controls = getControls(); 444 if (controls.length > 0) 445 { 446 constructorArgs.add(ToCodeArgHelper.createControlArray(controls, 447 "Bind Controls")); 448 } 449 450 ToCodeHelper.generateMethodCall(lineList, indentSpaces, 451 "ReusableTOTPBindRequest", requestID + "Request", 452 "new ReusableTOTPBindRequest", constructorArgs); 453 454 455 // Add lines for processing the request and obtaining the result. 456 if (includeProcessing) 457 { 458 // Generate a string with the appropriate indent. 459 final StringBuilder buffer = new StringBuilder(); 460 for (int i=0; i < indentSpaces; i++) 461 { 462 buffer.append(' '); 463 } 464 final String indent = buffer.toString(); 465 466 lineList.add(""); 467 lineList.add(indent + "try"); 468 lineList.add(indent + '{'); 469 lineList.add(indent + " BindResult " + requestID + 470 "Result = connection.bind(" + requestID + "Request);"); 471 lineList.add(indent + " // The bind was processed successfully."); 472 lineList.add(indent + '}'); 473 lineList.add(indent + "catch (LDAPException e)"); 474 lineList.add(indent + '{'); 475 lineList.add(indent + " // The bind failed. Maybe the following will " + 476 "help explain why."); 477 lineList.add(indent + " // Note that the connection is now likely in " + 478 "an unauthenticated state."); 479 lineList.add(indent + " ResultCode resultCode = e.getResultCode();"); 480 lineList.add(indent + " String message = e.getMessage();"); 481 lineList.add(indent + " String matchedDN = e.getMatchedDN();"); 482 lineList.add(indent + " String[] referralURLs = e.getReferralURLs();"); 483 lineList.add(indent + " Control[] responseControls = " + 484 "e.getResponseControls();"); 485 lineList.add(indent + '}'); 486 } 487 } 488}