001/*
002 * Copyright 2016-2018 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2016-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.extensions;
022
023
024
025import com.unboundid.asn1.ASN1Element;
026import com.unboundid.asn1.ASN1OctetString;
027import com.unboundid.asn1.ASN1Sequence;
028import com.unboundid.ldap.sdk.Control;
029import com.unboundid.ldap.sdk.ExtendedResult;
030import com.unboundid.ldap.sdk.LDAPException;
031import com.unboundid.ldap.sdk.ResultCode;
032import com.unboundid.util.Debug;
033import com.unboundid.util.NotMutable;
034import com.unboundid.util.StaticUtils;
035import com.unboundid.util.ThreadSafety;
036import com.unboundid.util.ThreadSafetyLevel;
037import com.unboundid.util.Validator;
038
039import static com.unboundid.ldap.sdk.unboundidds.extensions.ExtOpMessages.*;
040
041
042
043/**
044 * This class provides an implementation of an extended result that may be used
045 * to provide the client with a TOTP shared secret generated by the server in
046 * response to a {@link GenerateTOTPSharedSecretExtendedRequest}.
047 * <BR>
048 * <BLOCKQUOTE>
049 *   <B>NOTE:</B>  This class, and other classes within the
050 *   {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only
051 *   supported for use against Ping Identity, UnboundID, and Alcatel-Lucent 8661
052 *   server products.  These classes provide support for proprietary
053 *   functionality or for external specifications that are not considered stable
054 *   or mature enough to be guaranteed to work in an interoperable way with
055 *   other types of LDAP servers.
056 * </BLOCKQUOTE>
057 * <BR>
058 * If the extended request was processed successfully, then this result will
059 * have an OID of 1.3.6.1.4.1.30221.2.6.57 and a value with the following
060 * encoding:
061 * <BR><BR>
062 * <PRE>
063 *   GenerateTOTPSharedSecretResult ::= SEQUENCE {
064 *        totpSharedSecret     [0] OCTET STRING }
065 *        ... }
066 * </PRE>
067 */
068@NotMutable()
069@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
070public final class GenerateTOTPSharedSecretExtendedResult
071       extends ExtendedResult
072{
073  /**
074   * The OID (1.3.6.1.4.1.30221.2.6.57) for the generate TOTP shared secret
075   * extended result.
076   */
077  public static final String GENERATE_TOTP_SHARED_SECRET_RESULT_OID =
078       "1.3.6.1.4.1.30221.2.6.57";
079
080
081
082  /**
083   * The BER type for the TOTP shared secret element of the result value
084   * sequence.
085   */
086  private static final byte TYPE_TOTP_SHARED_SECRET = (byte) 0x80;
087
088
089
090  /**
091   * The serial version UID for this serializable class.
092   */
093  private static final long serialVersionUID = 8505040895542971346L;
094
095
096
097  // The base32-encoded representation TOTP shared secret generated by the
098  // server.
099  private final String totpSharedSecret;
100
101
102
103  /**
104   * Generates a new generate TOTP shared secret extended result for the case in
105   * which the server was able to generate the requested TOTP shared secret.
106   *
107   * @param  messageID         The message ID for the LDAP message that is
108   *                           associated with this LDAP result.
109   * @param  totpSharedSecret  The base32-encoded representation of the TOTP
110   *                            shared secret generated by the server.  It must
111   *                           not be {@code null}.
112   * @param  responseControls  The set of controls from the response, if
113   *                           available.
114   */
115  public GenerateTOTPSharedSecretExtendedResult(final int messageID,
116              final String totpSharedSecret, final Control... responseControls)
117  {
118    this(messageID, ResultCode.SUCCESS, null, null, null, totpSharedSecret,
119         responseControls);
120  }
121
122
123
124  /**
125   * Creates a new generate TOTP shared secret extended result with the provided
126   * information.
127   *
128   * @param  messageID          The message ID for the LDAP message that is
129   *                            associated with this LDAP result.
130   * @param  resultCode         The result code from the response.
131   * @param  diagnosticMessage  The diagnostic message from the response, if
132   *                            available.
133   * @param  matchedDN          The matched DN from the response, if available.
134   * @param  referralURLs       The set of referral URLs from the response, if
135   *                            available.
136   * @param  totpSharedSecret   The base32-encoded representation of the TOTP
137   *                            shared secret generated by the server, if
138   *                            available.
139   * @param  responseControls   The set of controls from the response, if
140   *                            available.
141   */
142  public GenerateTOTPSharedSecretExtendedResult(final int messageID,
143              final ResultCode resultCode, final String diagnosticMessage,
144              final String matchedDN, final String[] referralURLs,
145              final String totpSharedSecret, final Control... responseControls)
146  {
147    super(messageID, resultCode, diagnosticMessage, matchedDN, referralURLs,
148         ((totpSharedSecret == null)
149              ? null
150              : GENERATE_TOTP_SHARED_SECRET_RESULT_OID),
151         encodeValue(totpSharedSecret), responseControls);
152
153    this.totpSharedSecret = totpSharedSecret;
154
155    if (totpSharedSecret == null)
156    {
157      Validator.ensureTrue((resultCode != ResultCode.SUCCESS),
158           "If the result code is SUCCESS, the TOTP shared secret must be " +
159                "non-null");
160    }
161  }
162
163
164
165  /**
166   * Creates a new generate TOTP shared secret extended result from the provided
167   * extended result.
168   *
169   * @param  extendedResult  The extended result to be decoded as a generate
170   *                         TOTP shared secret extended result.  It must not be
171   *                         {@code null}.
172   *
173   * @throws  LDAPException  If the provided extended result cannot be decoded
174   *                         as a generate TOTP shared secret result.
175   */
176  public GenerateTOTPSharedSecretExtendedResult(
177              final ExtendedResult extendedResult)
178         throws LDAPException
179  {
180    super(extendedResult);
181
182    final ASN1OctetString value = extendedResult.getValue();
183    if (value == null)
184    {
185      totpSharedSecret = null;
186    }
187    else
188    {
189      try
190      {
191        final ASN1Element[] elements =
192             ASN1Sequence.decodeAsSequence(value.getValue()).elements();
193        totpSharedSecret =
194             ASN1OctetString.decodeAsOctetString(elements[0]).stringValue();
195      }
196      catch (final Exception e)
197      {
198        Debug.debugException(e);
199        throw new LDAPException(ResultCode.DECODING_ERROR,
200             ERR_GEN_TOTP_SECRET_RESULT_ERROR_DECODING_VALUE.get(
201                  StaticUtils.getExceptionMessage(e)));
202      }
203    }
204  }
205
206
207
208  /**
209   * Encodes the provided information into an ASN.1 octet string suitable for
210   * use as the value of this extended result.
211   *
212   * @param  totpSharedSecret   The base32-encoded representation of the TOTP
213   *                            shared secret generated by the server, if
214   *                            available.
215   *
216   * @return  The ASN.1 octet string suitable for use as the value of this
217   *          extended result, or {@code null} if there should be no value.
218   */
219  private static ASN1OctetString encodeValue(final String totpSharedSecret)
220  {
221    if (totpSharedSecret == null)
222    {
223      return null;
224    }
225
226    return new ASN1OctetString(new ASN1Sequence(new ASN1OctetString(
227         TYPE_TOTP_SHARED_SECRET, totpSharedSecret)).encode());
228  }
229
230
231
232  /**
233   * Retrieves the base32-encoded representation of the TOTP shared secret
234   * generated by the server, if available.
235   *
236   * @return  The base32-encoded representation of the TOTP shared secret
237   *          generated by the server, or {@code null} if none was provided.
238   */
239  public String getTOTPSharedSecret()
240  {
241    return totpSharedSecret;
242  }
243
244
245
246  /**
247   * {@inheritDoc}
248   */
249  @Override()
250  public String getExtendedResultName()
251  {
252    return INFO_GEN_TOTP_SECRET_RESULT_NAME.get();
253  }
254
255
256
257  /**
258   * Appends a string representation of this extended result to the provided
259   * buffer.
260   *
261   * @param  buffer  The buffer to which a string representation of this
262   *                 extended result will be appended.
263   */
264  @Override()
265  public void toString(final StringBuilder buffer)
266  {
267    buffer.append("GenerateTOTPSharedSecretExtendedResult(resultCode=");
268    buffer.append(getResultCode());
269
270    final int messageID = getMessageID();
271    if (messageID >= 0)
272    {
273      buffer.append(", messageID=");
274      buffer.append(messageID);
275    }
276
277    final String diagnosticMessage = getDiagnosticMessage();
278    if (diagnosticMessage != null)
279    {
280      buffer.append(", diagnosticMessage='");
281      buffer.append(diagnosticMessage);
282      buffer.append('\'');
283    }
284
285    final String matchedDN = getMatchedDN();
286    if (matchedDN != null)
287    {
288      buffer.append(", matchedDN='");
289      buffer.append(matchedDN);
290      buffer.append('\'');
291    }
292
293    final String[] referralURLs = getReferralURLs();
294    if (referralURLs.length > 0)
295    {
296      buffer.append(", referralURLs={");
297      for (int i=0; i < referralURLs.length; i++)
298      {
299        if (i > 0)
300        {
301          buffer.append(", ");
302        }
303
304        buffer.append('\'');
305        buffer.append(referralURLs[i]);
306        buffer.append('\'');
307      }
308      buffer.append('}');
309    }
310
311    final Control[] responseControls = getResponseControls();
312    if (responseControls.length > 0)
313    {
314      buffer.append(", responseControls={");
315      for (int i=0; i < responseControls.length; i++)
316      {
317        if (i > 0)
318        {
319          buffer.append(", ");
320        }
321
322        buffer.append(responseControls[i]);
323      }
324      buffer.append('}');
325    }
326
327    buffer.append(')');
328  }
329}