/// <summary> /// Stores a given nonce and timestamp. /// </summary> /// <param name="context">The context, or namespace, within which the <paramref name="nonce"/> must be unique.</param> /// <param name="nonce">A series of random characters.</param> /// <param name="timestamp">The timestamp that together with the nonce string make it unique. /// The timestamp may also be used by the data store to clear out old nonces.</param> /// <returns> /// True if the nonce+timestamp (combination) was not previously in the database. /// False if the nonce was stored previously with the same timestamp. /// </returns> /// <remarks> /// The nonce must be stored for no less than the maximum time window a message may /// be processed within before being discarded as an expired message. /// If the binding element is applicable to your channel, this expiration window /// is retrieved or set using the /// <see cref="StandardExpirationBindingElement.MaximumMessageAge"/> property. /// </remarks> public bool StoreNonce(string context, string nonce, DateTime timestamp) { if (timestamp.ToUniversalTimeSafe() + this.maximumMessageAge < DateTime.UtcNow) { // The expiration binding element should have taken care of this, but perhaps // it's at the boundary case. We should fail just to be safe. return false; } // We just concatenate the context with the nonce to form a complete, namespace-protected nonce. string completeNonce = context + "\0" + nonce; lock (this.nonceLock) { List<string> nonces; if (!this.usedNonces.TryGetValue(timestamp, out nonces)) { this.usedNonces[timestamp] = nonces = new List<string>(4); } if (nonces.Contains(completeNonce)) { return false; } nonces.Add(completeNonce); // Clear expired nonces if it's time to take a moment to do that. // Unchecked so that this can int overflow without an exception. unchecked { this.nonceClearingCounter++; } if (this.nonceClearingCounter % AutoCleaningFrequency == 0) { this.ClearExpiredNonces(); } return true; } }
/// <summary> /// Re-instantiates an <see cref="Association"/> previously persisted in a database or some /// other shared store. /// </summary> /// <param name="handle"> /// The <see cref="Handle"/> property of the previous <see cref="Association"/> instance. /// </param> /// <param name="expiresUtc"> /// The UTC value of the <see cref="Expires"/> property of the previous <see cref="Association"/> instance. /// </param> /// <param name="privateData"> /// The byte array returned by a call to <see cref="SerializePrivateData"/> on the previous /// <see cref="Association"/> instance. /// </param> /// <returns> /// The newly dehydrated <see cref="Association"/>, which can be returned /// from a custom association store's /// IRelyingPartyAssociationStore.GetAssociation method. /// </returns> public static Association Deserialize(string handle, DateTime expiresUtc, byte[] privateData) { Requires.NotNullOrEmpty(handle, "handle"); Requires.NotNull(privateData, "privateData"); Contract.Ensures(Contract.Result<Association>() != null); expiresUtc = expiresUtc.ToUniversalTimeSafe(); TimeSpan remainingLifeLength = expiresUtc - DateTime.UtcNow; byte[] secret = privateData; // the whole of privateData is the secret key for now. // We figure out what derived type to instantiate based on the length of the secret. try { return HmacShaAssociation.Create(handle, secret, remainingLifeLength); } catch (ArgumentException ex) { throw new ArgumentException(OpenIdStrings.BadAssociationPrivateData, "privateData", ex); } }
/// <summary> /// Re-instantiates an <see cref="Association"/> previously persisted in a database or some /// other shared store. /// </summary> /// <param name="handle"> /// The <see cref="Handle"/> property of the previous <see cref="Association"/> instance. /// </param> /// <param name="expires"> /// The value of the <see cref="Expires"/> property of the previous <see cref="Association"/> instance. /// </param> /// <param name="privateData"> /// The byte array returned by a call to <see cref="SerializePrivateData"/> on the previous /// <see cref="Association"/> instance. /// </param> /// <returns> /// The newly dehydrated <see cref="Association"/>, which can be returned /// from a custom association store's /// <see cref="IAssociationStore<TKey>.GetAssociation(TKey, SecuritySettings)"/> method. /// </returns> public static Association Deserialize(string handle, DateTime expires, byte[] privateData) { if (string.IsNullOrEmpty(handle)) { throw new ArgumentNullException("handle"); } if (privateData == null) { throw new ArgumentNullException("privateData"); } expires = expires.ToUniversalTimeSafe(); TimeSpan remainingLifeLength = expires - DateTime.UtcNow; byte[] secret = privateData; // the whole of privateData is the secret key for now. // We figure out what derived type to instantiate based on the length of the secret. try { return HmacShaAssociation.Create(handle, secret, remainingLifeLength); } catch (ArgumentException ex) { throw new ArgumentException(OpenIdStrings.BadAssociationPrivateData, "privateData", ex); } }