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