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