10

The goal is to come up with a way to protect your QML code from plagiarism. It is a problem, since the way QML was designed and implemented seems to be inexplicably unprotected in this regard. The only QML types which are somewhat protected are those implemented entirely in C++.

  • Qt resource files don't support any degree of protection
  • even if you compress the resource file, extracting data from it is still fairly trivial to anyone with moderate experience
  • QML files stored on the file system are practically there for the taking
  • the same applies to any remote QML files, aside from adding dependency on internet connection, it is easy to sniff on the network access and get the QML files through their urls
  • QML doesn't provide seem to provide any public API to allow users enough control over QML type resolution to protect their code

All in all, it almost looks like Qt deliberately skimps on QML code protection, one obvious candidate reason would be to force people into buying the insanely expressive commercial license, which features the QML compiler.

So absent any stock method of protecting QML sources, the only solution that currently comes to my mind is control over how QML types are resolved. There are several ways of registering types to QML:

  • register in the application executable
  • register in a plugin
  • register via a QML module

However, what I need is to manually resolve QML types, much like you can create a custom QQuickImageProvider which inputs a URL string and outputs an image, I need the QML engine to request a string with the type to my custom component provider which outputs a ready for object instantiation component.

This would be easy if any custom instantiation mechanism is used, but I need those types to be usable in regular QML sources. Ideally this should be the first mechanism used to resolve the type, before looking in the available import paths or even internally registered types.

Alternatively, it would be just as useful if there is a way to define a QML module entirely in C++, without any external QML files, without a qmldir file and so on.

As a last resort, and falling short from ideally, I would also settle for registering QML (not C++) types to the runtime, this could also be useful, but I'd prefer to have full control over the resolving process.

A QML plugin does not do the trick, as it registers C++ types, and I want to register QML types, that is, QQmlComponents created from string sources and referencing each other.

dtech
  • 47,916
  • 17
  • 112
  • 190
  • So, you want to load a component from a character string in memory that contains some QML source code? It's the `QQmlComponent::setData` function. – Velkan Jan 17 '16 at 11:05
  • @Velkan - far from it. I want to register components as QML types for use in declarative QML code. Or more accurately, I want to have a `SomeType {...}` in QML and have manual control to associate "SomeType" to a QML component. – dtech Jan 17 '16 at 12:06
  • Maybe do `Loader { sourceComponent: someType1; ... (properties of the item) }` and change `sourceComponent` to be pointing to the right component. Changing the meaning of the `SomeType {}` doesn't look promising because the type id is determined at the QML compile time (http://www.kdab.com/qml-engine-internals-part-1-qml-file-loading/). – Velkan Jan 17 '16 at 14:00
  • @Velkan - QML code is not compiled, it is interpreted. The QML engine internals lookup the QML types from import folders and imported modules. It is not applicable to use a `Loader`, the components need to be directly usable as QML types in sources. – dtech Jan 17 '16 at 15:07
  • It's called 'compiled' in the Qt terminology. What about the non-template function `qmlRegisterType(const QUrl &url, ...)` that allows to map a specific QML source file to the QML type? (before the load, of course) – Velkan Jan 17 '16 at 16:04
  • @Velkan - it still needs an URL, so it requires a QML file to be present somewhere - the very thing I want to eliminate. – dtech Jan 17 '16 at 16:11

4 Answers4

3

The (ideal) Solution: Precompile it

The Qt Quick Compiler is a development add-on for Qt Quick applications which allows you to compile QML source code into the final binary. As it's description says, it will help on preventing plagiarism and it will also enhance your application launch times and provide other benefits.

This is as close as you can get to protecting your QML source code, even when it's not yet fully optimized

Update

As of Qt 5.11 the solution is in place and getting better fast.

Update (2)

Seems the QML compiler is already opensource from 5.11 I can't tell about tooling but Lars Knoll explains it on the blog post.

Ariel M.
  • 896
  • 8
  • 24
  • Too rudimentary at this stage, bugs and limitations. Hopefully by the time 5.8 is out it will improve. – dtech Apr 01 '16 at 19:50
  • 1
    Yes, but the solution is officially in place. And being opensource it will only get better. Furthermore I provided a link to another free compiler. That's the best bet AFAIK. – Ariel M. Apr 02 '16 at 20:42
  • Hopefully, by the time the compiler is fully integrated in Qt it will remove some of the limitations, in particular the inability to load QML files from the network and mix regular with compiled QML files is rendering it useless for me. – dtech Apr 27 '16 at 12:07
  • It's still in the roadmap, and they had talks about it. Im still confident. http://lists.qt-project.org/pipermail/releasing/2016-October/004353.html – Ariel M. Nov 02 '16 at 22:02
  • https://wiki.qt.io/New_Features_in_Qt_5.8 - I don't see it, to my knowledge it was "replaced" by that "caching of code and data structures" thingie... – dtech Nov 04 '16 at 16:21
  • AFAIK there is already some catching of precompiled binaries, it is easy to handraft your own qml precompilation tool if you do not want to buy the QtQuick Compiler. Anyway, the plan is to opensource it once it's complete. – Ariel M. Mar 31 '17 at 17:36
  • For [Qt 9.10](https://www1.qt.io/licensing-comparison) it is still not available for opensource – ymoreau Feb 27 '18 at 14:27
1

Option A) use qtquick compiler

Option B) use encrypted resources:

  1. compile resources into separated file: rcc -binary your_resource.qrc -o extresources.rcc

  2. encrypt extresources.rcc to extresources.rcc.cr (for example with gnupg)

  3. create a new resource file APP.rcc, only with extresources.rcc.cr file

  4. on startup, load ":/extresources.rcc.cr" and decrypt them into buffer (you need a cryptographic library like Libgcrypt ...hide private key for decompilers and debuggers etc)

  5. Q_CLEANUP_RESOURCE(APP); (optional, clear APP.rcc for saving memory)

  6. Resource::registerResource((unsigned char *) myBuffer.constData()))

//now, you have available decrypted resources...for example

engine.load(QUrl("qrc:/main.qml"))

Real implementation is not trivial, but works very good...

1

Actually, you can register QML types in C++. The function qmlRegisterType has an overlapped form which accepts a QUrl denoting to a qml file in qrc:

qmlRegisterType(QUrl("qrc:/YourQMLModule.qml"), "YourModule", 1, 0, "YourQMLModule");

It just looks like you register a normal C++ class. It is commonly used in Qt's official sources, though be missing on the documentation.

Jimmy Chen
  • 490
  • 3
  • 13
  • It does nothing to protect `qrc:/YourQMLModule.qml` from plagiarism. It is still there in the QRC, at best in a zipped form, still fairly trivial to extract. – dtech Jan 10 '20 at 11:21
  • Yes, you're right. I suddenly find another solution: if protecting QML directly is so hard, why just let it be and try to separate confidential codes/logics to the CPP part? It's just my personal thought, though. Maybe your QML codes themselves are very confidential and cannot be separated into CPPs. – Jimmy Chen Jan 14 '20 at 02:06
  • Separation codes like above is the common manner for web development. You cannot encrypt js to protect them, but you can try to separate and protect confidential codes on the server side. – Jimmy Chen Jan 14 '20 at 02:08
0

After some digging around I found two directions that might be worth pursuing:

  • using a custom QQmlAbstractUrlInterceptor for the QML engine, that resolves QML types and returs a QUrl, in the case of "protected" types, the interceptor can prepend a custom scheme. The using a custom QNetworkAccessManager to intercept that url, calls the default implementation for unprotected types and for protected types decrypts the data and returns it in a QNetworkReply.

  • another, simpler but less flexible solution involves only the second part of the previous solution, and the qmlRegisterType(const QUrl &url, ...) function to expose as QML types, avoiding the usage of the interceptor.

I will post updates as I investigate those two. Note that this is not 100% secure either, as the network reply with the decrypted code itself will at least temporarily stay in RAM, so given enough competence it would still be possible to get to the code, however it is not as trivial as taking it directly from the application binary. A possible direction to go even further would be to resort to a custom QNetworkReply which doesn't contain the decrypted data, but overloads the QIODevice part to act as an accessor to the encrypted data that decrypts it along the way while reading it.

dtech
  • 47,916
  • 17
  • 112
  • 190