/// <inheritsdoc />
        public override bool TryUnwrapKey(ReadOnlySpan <byte> keyBytes, Span <byte> destination, JwtHeaderDocument header, out int bytesWritten)
        {
            if (!header.TryGetHeaderParameter(JwtHeaderParameterNames.P2c.EncodedUtf8Bytes, out JwtElement p2c))
            {
                ThrowHelper.ThrowJwtDescriptorException_HeaderIsRequired(JwtHeaderParameterNames.P2c);
            }

            if (!header.TryGetHeaderParameter(JwtHeaderParameterNames.P2s.EncodedUtf8Bytes, out JwtElement p2s))
            {
                ThrowHelper.ThrowJwtDescriptorException_HeaderIsRequired(JwtHeaderParameterNames.P2s);
            }

            int iterationCount = (int)p2c.GetInt64();
            var b64p2s         = p2s.GetRawValue();
            int p2sLength      = Base64Url.GetArraySizeRequiredToDecode(b64p2s.Length);


            Span <byte> salt = stackalloc byte[p2sLength + 1 + _algorithmNameLength];

            Base64Url.Decode(b64p2s.Span, salt.Slice(_algorithmNameLength + 1));
            salt[_algorithmNameLength] = 0x00;
            _algorithm.EncodedUtf8Bytes.CopyTo(salt);

            Span <byte> derivedKey = stackalloc byte[_keySizeInBytes];

            Pbkdf2.DeriveKey(_password, salt, _hashAlgorithm, (uint)iterationCount, derivedKey);

            using var keyUnwrapper = new AesKeyUnwrapper(derivedKey, EncryptionAlgorithm, _keyManagementAlgorithm);
            return(keyUnwrapper.TryUnwrapKey(keyBytes, destination, header, out bytesWritten));
        }
Example #2
0
        /// <inheritsdoc />
        public override SymmetricJwk WrapKey(Jwk?staticKey, JwtHeader header, Span <byte> destination)
        {
            if (header is null)
            {
                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.header);
            }

            var         contentEncryptionKey = CreateSymmetricKey(EncryptionAlgorithm, (SymmetricJwk?)staticKey);
            Span <byte> buffer = stackalloc byte[_saltSizeInBytes + 1 + _algorithmNameLength];

            _saltGenerator.Generate(buffer.Slice(_algorithmNameLength + 1));
            buffer[_algorithmNameLength] = 0x00;
            _algorithm.EncodedUtf8Bytes.CopyTo(buffer);

            Span <byte> derivedKey = stackalloc byte[_keySizeInBytes];

            Pbkdf2.DeriveKey(_password, buffer, _hashAlgorithm, _iterationCount, derivedKey);

            Span <byte> salt    = buffer.Slice(_algorithmNameLength + 1, _saltSizeInBytes);
            Span <byte> b64Salt = stackalloc byte[Base64Url.GetArraySizeRequiredToEncode(salt.Length)];

            Base64Url.Encode(salt, b64Salt);
            header.Add(JwtHeaderParameterNames.P2s, Utf8.GetString(b64Salt));
            header.Add(JwtHeaderParameterNames.P2c, _iterationCount);

            using var keyWrapper = new AesKeyWrapper(derivedKey, EncryptionAlgorithm, _keyManagementAlgorithm);
            return(keyWrapper.WrapKey(contentEncryptionKey, header, destination));
        }
Example #3
0
        /// <inheritsdoc />
        public override SymmetricJwk WrapKey(Jwk?staticKey, JwtHeader header, Span <byte> destination)
        {
            if (header is null)
            {
                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.header);
            }

            var contentEncryptionKey = CreateSymmetricKey(EncryptionAlgorithm, (SymmetricJwk?)staticKey);
            int bufferSize           = _saltSizeInBytes + _algorithmNameLength + 1;

            byte[]? bufferToReturn = null;
            Span <byte> buffer = bufferSize > Pbkdf2.SaltSizeThreshold + 18 + 1  // 18 = max alg name length
              ? (bufferToReturn = ArrayPool <byte> .Shared.Rent(bufferSize))
              : stackalloc byte[Pbkdf2.SaltSizeThreshold + 18 + 1];

            buffer = buffer.Slice(0, bufferSize);
            _saltGenerator.Generate(buffer.Slice(_algorithmNameLength + 1));
            buffer[_algorithmNameLength] = 0x00;
            _algorithm.EncodedUtf8Bytes.CopyTo(buffer);

            Span <byte> derivedKey = stackalloc byte[KeySizeThreshold].Slice(0, _keySizeInBytes);

            Pbkdf2.DeriveKey(_password, buffer, _hashAlgorithm, _iterationCount, derivedKey);

            Span <byte> salt          = buffer.Slice(_algorithmNameLength + 1, _saltSizeInBytes);
            int         saltLengthB64 = Base64Url.GetArraySizeRequiredToEncode(salt.Length);

            byte[]? arrayToReturn = null;
            Span <byte> b64Salt = saltLengthB64 > Pbkdf2.SaltSizeThreshold * 4 / 3
                ? (arrayToReturn = ArrayPool <byte> .Shared.Rent(saltLengthB64))
                : stackalloc byte[Pbkdf2.SaltSizeThreshold * 4 / 3];

            try
            {
                int length = Base64Url.Encode(salt, b64Salt);
                header.Add(JwtHeaderParameterNames.P2s, Utf8.GetString(b64Salt.Slice(0, saltLengthB64)));
                header.Add(JwtHeaderParameterNames.P2c, _iterationCount);

                using var keyWrapper = new AesKeyWrapper(derivedKey, EncryptionAlgorithm, _keyManagementAlgorithm);
                return(keyWrapper.WrapKey(contentEncryptionKey, header, destination));
            }
            finally
            {
                if (bufferToReturn != null)
                {
                    ArrayPool <byte> .Shared.Return(bufferToReturn);
                }

                if (arrayToReturn != null)
                {
                    ArrayPool <byte> .Shared.Return(arrayToReturn);
                }
            }
        }