Cabbage Logo
Back to Cabbage Site

Implementing a simple antipiracy security

Hi,

I am working on the release of my first commercial VST. I don’t want to implement a super complex antipiracy feature, however I’m worried users might share the files too easily…

After talking with a few friends, I think I have a simple solution and I was wondering if it was possible to implement it in Csound/Cabbage.

  • the user gets a serial key when he downloads the vst
  • when the vst loads, it asks for a serial key
  • the user enters its serial key and it is written in a specific folder
  • next time the vst loads, he finds a serial key in this specific folder and it runs smoothly

I wouldnt encrypt the key. I think a simple solution would be to sum up the characters and then it would be equal to a specific number (very basic).

Of course users could share serial keys, but I just want to prevent the vst folder to be uploaded without any basic security feature.

In your opinion, is it something I could do with Cabbage?

Thanks!

yes, I have been there and done something like it, so it is do-able for sure

I remember I also used some of the https://cabbageaudio.com/docs/reserved_channels/
to make the serial more complex

Perfect thanks! It looks like USER_APPLICATION_DIRECTORY will help me…

Here is a new idea: encode the user_home_directory name in a text file so it cant be shared since it’s different for every user

I am working on the feature (it’s been 2 hours…). Did you manage to make prinks working? Or do you use another opcode to write a string? Thanks :slight_smile:

Here is the error I have:

fprints "./test.txt", "test string"

INIT ERROR in instr 2 (opcode fprints) line 31: error opening file './test.txt'
 from file /Users/julienbeisel/Documents/git-repos/nymano_transient_shaper/test_anticopy.csd (1),	fprints	"./test.txt"	"test string"	
		   T  0.940 - note deleted.  i2 had 1 init errors

Also, @rorywalsh, I tried your custom strToFile opcode and it does not seem to work

*** strToFile could not open file for writing *** <— this is what a c/c of the example produces

I think I have to find a workaround haha. Since I can write .wav files, I could write a wav file and get the encrypted title, but it looks sketchy…

You should be using an absolute path. Most DAWs will change the current working directory, so ./test.txt will no longer be relative to your .csd file. You can use the “CSD_PATH” reserved channel to get the current path :+1:

Thanks that’s the issue!!

And do you know if USER_APPLICATION_DIRECTORY is accessible in Cabbage directly? Or only in DAWs?

Thank you so much

It’s a reserved channel accessible via chnget in Csound. :+1:

https://cabbageaudio.com/docs/reserved_channels/

Perfect thank you :slight_smile:

I was stuck 2 hours on the “absolute path error”, I was getting crazy haha

2 Likes

It’s 1:30 AM and I finally managed to have a working snippet…

It was so hard to use readf/readfi. For some reasons I had perfs errors with readfi so I rewrote everything with inits, I still don’t know what happened :smiley:

<Cabbage>
form caption("Cabbage Security") size(400, 400), guiMode("queue") pluginId("def1")

label bounds(110, 98, 167, 18) channel("writtenresult") text("")
texteditor bounds(32, 308, 339, 50) fontSize(16), channel("orcText")  fontColour(124, 210, 0), colour(0, 0, 0, 100), mode("single")
</Cabbage>
<CsoundSynthesizer>
<CsOptions>
-n -d -+rtmidi=NULL -M0 -m0d 
</CsOptions>
<CsInstruments>

; Initialize the global variables. 

ksmps = 32
nchnls = 2
0dbfs = 1
instr 1
    
    SuserHome chnget "USER_HOME_DIRECTORY"
    SFileName strcat SuserHome, "/Library/Application Support/Nymano Audio/test.txt"
    Stext cabbageGetValue "orcText"
 
    READ:  
    
    iOpen fiopen SFileName, 1
    Sline, iLinNum readfi SFileName ; read
    cabbageSet "writtenresult", "text", Sline
    ficlose iOpen
    rireturn  
    
    goto listener
        
    WRITE:
   
    iOpen fiopen SFileName, 0 
    if strlen(Stext)>0 then
        fprints SFileName, Stext ; write
    endif
    ficlose iOpen
    
    rireturn  

    listener:
   
    if changed:k(Stext) == 1 then

        reinit WRITE
        reinit READ 
        reinit READ 
        
    endif
endin

</CsInstruments>
<CsScore>
;causes Csound to run for about 7000 years...
f0 z
;starts instrument 1 and runs it for a week
i1 0 [60*60*24*7] 
</CsScore>
</CsoundSynthesizer>

It is still a bit sketchy, but it looks like it’s working.

When the plugin loads, it reads a file called test.txt in a specific folder (my installer will place this file in the folder, I’m sure it exists). Then I will add a check “if the content == this condition then the plugin runs”.

If it’s not the case, the csound script goes to the “listener” part. When the user enters a new key, it is written in the file and the script re-reads the file again. I don’t know why I have to reinit it 2 times to make it work, it’s probably too late to figure it out

I just have to code the serial key check and I think it’s almost done. Do you think this is a correct solution to add a “weak protection” to the plugin and prevent copy/paste?

Julien

But if a user shares the text file, alongside the plugin bundle, it could be used on someone else’s machine, or not? Sorry, it’s early here :rofl:

You’re right, that’s why I was thinking about writing USER_HOME_DIRECTORY or something in the file. The users could share the serial key anyway…

Actually when I woke up I was re-considering this implementation and I’m way too worried about edges cases and what if it doesnt work for some users… Maybe using 0 copy protection but offering many updates + tips for users who paid is a better way of “protecting the plugin against piracy”…

I think this is a good approach. You could also just include a file, any file that gets installed with the official version. You could install it somewhere less obvious to the end user, i.e., outside of /Library/Application Support/Nymano Audio/... Csound can verify this file exists when it opens. If not, revert to trial mode. So at least if someone tries to copy the bundle, and the application support folder, to share with friends, it will still only run in trial mode.

Yes that would be a great idea. For example /Library/Application Support/nmnaudio/.. so it’s not clear it’s linked to my company haha

The file needs to get written by the plugin once the serial is entered and it’s not that easy in Csound. If it is created by the installer, then sharing the installer might be as easy as sharing the vst3 directly.

I’m not sure this protection is worth the efforts needed honestly. Using readfi “on the fly” to read the written file was a pain and I’m worried it might break… I think I just have to make a lot of tests to make sure it’s robust enough. I’m having a hard time trying to compare strings at the moment :smiley:

No, I’m not talking about using a unique serial number. Just a generic file that gets installed somewhere on the machine by your official installer. You don’t need to modify or change anything in your Csound code. Just check if the file exists. If it does, you can assume/hope that it’s there because the user ran the official installer on their machine. it’s obviously extremely flimsy, but probably gives you about as much protection as the other approach, with a lot less work?

What would prevent users from sharing the installer then?

The serial key was just an idea of “another step” to prevent them from doing that

Yeah, you’re right, did I mention it was still early here :rofl: I think all roads lead to it’s not worth the bother. But one last think we could consider is writing a Cabbage specific opcode that somehow manages this kind of thing. I’d have no interest in server handshakes and what not, but if something simple could be thrashed out, it might be worth considering?

I’m not sure what would be the “most generic simple approach” with a custom Cabbage opcode… The design would have to be well defined first. Maybe opcodes that would make the writing/reading of txt files easier? I know you released the Csound opcodes strToFile and fileToStr, it might be a good way to write my security script.

The code snippet above is a good start but is too sketchy imo… I will work on installers today and take some time to think about it before spending too much time on it

That what I was thinking. Just a way to make this process easier to manage.

yes I would love it!

Current script + demo

<Cabbage>
form caption("Cabbage Security") size(400, 400), guiMode("queue") pluginId("def1")

label bounds(110, 156, 167, 18) channel("activated") colour("white")
label bounds(110, 10, 167, 18) channel("writtenresult") colour("white")
texteditor bounds(32, 308, 339, 50) fontSize(16), channel("orcText")  fontColour(124, 210, 0), colour(0, 0, 0, 100), mode("single")
</Cabbage>
<CsoundSynthesizer>
<CsOptions>
-n -d -+rtmidi=NULL -M0 -m0d 
</CsOptions>
<CsInstruments>

; Initialize the global variables. 

ksmps = 32
nchnls = 2
0dbfs = 1
instr 1
    
    SuserHome chnget "USER_HOME_DIRECTORY"
    SFileName strcat SuserHome, "/Library/Application Support/Nymano Audio/test.txt"
    Stext cabbageGetValue "orcText"
 
    READ:  
    
    prints "read "
    
    iOpen fiopen SFileName, 1
    Sline, iLinNum readfi SFileName ; read
    prints Sline, 1  
 
    cabbageSet "writtenresult", "text", Sline
    
    ipos strindex Sline, "password"

    if (ipos>=0) then
    
        prints Sline
        cabbageSet "activated", "text", "Activated"
        
    else
    
    cabbageSet "activated", "text", "Not activated"
    
    endif
    
    ficlose iOpen
    rireturn  
    
    goto listener
        
    WRITE:
    
    prints "write " 
    iOpen fiopen SFileName, 0 
    
    if strlen(Stext)>0 then
        fprints SFileName, Stext ; write
    endif
    
    ficlose iOpen
    
    rireturn  

    listener:
   
    if changed:k(Stext) == 1 then

        reinit WRITE
        reinit READ 
        reinit READ 
        
    endif
endin

</CsInstruments>
<CsScore>
;causes Csound to run for about 7000 years...
f0 z
;starts instrument 1 and runs it for a week
i1 0 [60*60*24*7] 
</CsScore>
</CsoundSynthesizer>

@rorywalsh this is the idea we discussed. It writes a password in a file in a specific folder. If the instr reads the file and finds the right string (here it’s “password”, but we can do something better with a password linked with the user) then it’s in “activated mode”.

Do you think this is something I could use in production? I just need to make sure I use the right paths, but otherwise it looks correct

Thanks & have a nice day

Julien

ps: i tried it on ableton and it works well! I can activate it and then everything works well