/// <summary> /// Validates a token pair against a given action. /// </summary> /// <param name="tokenPair">Token pair to validate.</param> /// <param name="actionId">ID of the action to validate.</param> /// <returns>Whether the token pair is valid.</returns> public bool ValidateTokenPair(ActionTokenPair tokenPair, string actionId) { if (tokenPair == null || tokenPair.Client == null || tokenPair.Server == null || !tokenPair.Client.IsClientPart || tokenPair.Server.IsClientPart) { return(false); } var aclen = AbstractionUtilities.UTF8.GetByteCount(actionId); var tkc = tokenPair.Client; var tks = tokenPair.Server; if (!AbstractionUtilities.CompareVectorized(tkc.State, tks.State)) { return(false); } var state = tkc.State; Span <byte> sigclient = stackalloc byte[SignatureSize]; Span <byte> sigserver = stackalloc byte[SignatureSize]; if (!this.GenerateSignatures(actionId, state, tkc.Key, tks.Key, sigclient, sigserver)) { return(false); } if (!AbstractionUtilities.CompareVectorized(sigclient, tkc.Signature) || !AbstractionUtilities.CompareVectorized(sigserver, tks.Signature)) { return(false); } // round-trip some random data // if the data round-trips successfully, the tokens are a pair // otherwise they are not, and validation fails Span <byte> round0 = stackalloc byte[128]; Span <byte> round1 = stackalloc byte[128]; Span <byte> roundE = stackalloc byte[256]; round1.Fill(0); using (var rng = new SecureRandom()) rng.GetNonZeroBytes(round0); using (var rsa = RSA.Create(RsaSize)) { rsa.ImportRSAPublicKey(tkc.Key, out _); if (!rsa.TryEncrypt(round0, roundE, RSAEncryptionPadding.OaepSHA256, out _)) // will always be 256 { return(false); } } using (var rsa = RSA.Create(RsaSize)) { rsa.ImportRSAPrivateKey(tks.Key, out _); if (!rsa.TryDecrypt(roundE, round1, RSAEncryptionPadding.OaepSHA256, out var decw) || decw != round0.Length) { return(false); } } return(AbstractionUtilities.CompareVectorized(round0, round1)); }