Licensing with OnGuard

Recently, I looked into using OnGuard as a way to help honest people abide by their principles. I agree with the views expressed by many here that no licensing system is going to protect you from someone who wants to use your software and does not want to pay for it. On the other hand, I would like to avoid making it too easy for someone else to create valid keys for my program.

After studying the manual and examples, I added the following to my main form's code:

const
  TheKey: TKey = ($4A,$62,$F3,$2B,$9C,$D2,$84,$BF,$CB,$04,$0A,$C3,$3D,$11,$47,$1A);


function TfrmMain1.MakeCode(sName, sNumber: String; dtExpiration: TDate): String;
var Key: TKey;
    Code: TCode;
    sCode: String;
begin
  Key := TheKey;
  InitRegCode(Key, sName + ' - ' + sNumber, dtExpiration, Code);
  sCode := BufferToHex(Code, SizeOf(Code));
  Insert('-', sCode, 13);
  Insert('-', sCode, 09);
  Insert('-', sCode, 05);
  Result := sCode
end;

function TfrmMain1.TestCode(sName, sNumber, sTestCode: String; dtExpiration: TDate): Boolean;
var Key: TKey;
    Code: TCode;
    sCode: String;
begin
  sCode := MakeCode(sName, sNumber, dtExpiration);

  Result := SameText(sCode, sTestCode);
end;

This brings up some questions:

  1. Does seem like the correct way to use this? I would rather not add their components to my form.

  2. Since the OnGuard source is available, couldn't a hacker reverse engineer the Key I will choose and produce valid release codes? Should I therefore add some additional obfuscation to the code or might I just weaken the system.

  3. The Key is set as a constant here. Won't it show up in the code as contiguous bytes and be easy to copy?

  4. My program will require (at least) annual updates and my plan is to license it with an annual subscription. Would it be stronger to add the year as a constant in my program and test dated user entries against that year in a few places.

There are 4 questions here that are closely related and pretty specific. It seemed like it would be more awkward to ask those in four separate entries and have to add references for context but I will be glad to do that if that would be preferable. Thank you for your help.

Jack

Answers


  constructor TLincenceManager.Create;
  begin
    FSpecialCode := TOgSpecialCode.Create(nil);
    FSpecialCode.OnGetModifier := OgNetCodeGetModifier;
    FSpecialCode.OnChecked := OgNetCodeChecked;
    FSpecialCode.OnGetCode := OgNetCodeGetCode;
    FSpecialCode.OnGetKey := OgNetCodeGetKey;
    FSpecialCode.AutoCheck := False;
  end;

  function TLincenceManager.InitializeLicenceCode: Boolean;
  begin
    Result := FSpecialCode.CheckCode(True) = ogValidCode;
  end;

  procedure TLincenceManager.OgNetCodeChecked(Sender: TObject; Status: TCodeStatus);
  begin
    case Status of
      ogValidCode    : FMaxUsers := FSpecialCode.GetValue;
      ogInvalidCode  : FMaxUsers := 0;
      ogPastEndDate  : FMaxUsers := 0;
      ogDayCountUsed : FMaxUsers := 0;
      ogRunCountUsed : FMaxUsers := 0;
      ogNetCountUsed : FMaxUsers := 0;
      ogCodeExpired  : FMaxUsers := 0;
    else
      FMaxUsers := 0;
    end;
  end;

  procedure TLincenceManager.OgNetCodeGetCode(Sender: TObject; var Code: TCode);
  var
    FileName: string;
    SerialData: string;
    LicenceData: TStringList;
  begin
    FileName := IncludeTrailingPathDelimiter(ExtractFilePath(ParamStr(0)));
    FileName := FileName + cLicenseFileName;
    SerialData := '';

    LicenceData := TStringList.Create;
    try
      if FileExists(FileName) then
      begin
        LicenceData.LoadFromFile(FileName);
        SerialData := LicenceData.Values['Serial'];
      end;

      {convert to proper form}
      HexToBuffer(SerialData, Code, SizeOf(Code));
    finally
      LicenceData.Free;
    end;
  end;

  procedure TLincenceManager.OgNetCodeGetKey(Sender: TObject; var Key: TKey);
  const
    CKey : TKey = ($4A,$62,$F3,$2B,$9C,$D2,$84,$BF,$CB,$04,$0A,$C3,$3D,$11,$47,$1A);
  begin
    Key := CKey;
  end;

  procedure TLincenceManager.OgNetCodeGetModifier(Sender: TObject; var Value: Integer);
  begin
    Value := GenerateMachineModifierPrim;
  end;
  1. I have posted the way I do it. I do not use "visual" components. The way I do it is the way to go, in your case you just apply the date modifier (i have machine modifier)

  2. No in theory no. The key with which you generate you licence is of essence. If you have the key you can crack the licences. But with only the code you cant. This is just like encryption algorithms. You can know how the algorithm works but if you don't have the key you cant crack it. Look at XTEA. It is very simple yet hard to crack.

  3. Yes the key can be extracted from binary if one knows what is doing. You could use some sort of obfuscation here. But I would not bother. For most people such protection is enough, so if you are not making the next MS Office I would not bother. People are way to paranaoid about their products. Sell it first and think about this later. Oh and since it is not a string it is a little harder to find anyway.

  4. Just look at the time trial demos that come with onGuard to know how to do the time limited licence. However be aware that just simple manipulation of the computer clock will be enough to fool it. In my opinion best trial software is such, that is lack some vital functionality (save button...). Good time trials are very hard to make in my opinion.


I use the lower level OnGuard APIs rather than the classes as suggested by Runner. Either will work fine, the classes end up calling the lower level APIs anyway. Here are my wrapper utility functions for these lower level API methods.

{ Used by you to generate the unlock code you send to your customer, do not include in the customer software }

function GenerateReleaseCode(const inAppKey : string; inExpiryDate : TDateTime; inRegCode : string) : string;
(* inAppKey is the byte sequence key you already have
   inRegCode can be anything, typically customers name
   Returns the release code for the customer to type into the software *)
var
  releaseCode : TCode;
  key : TKey;
begin
  HexToBuffer(inAppKey, key, SizeOf(TKey));
  InitRegCode(key, inRegCode, inExpiryDate, releaseCode);
  Result := BufferToHex(unlockCode, SizeOf(releaseCode));
end;

{ Used in your program to authenticate if the release code is valid - does not check for expiry }

function AuthenticateReleaseCode(const inReleaseCodeHexString : string; const inAppKey : TKey) : Boolean;
var
  releaseCode : TCode;
begin
  HexToBuffer(inReleaseCodeHexString, releaseCode, SizeOf(releaseCode));
  Result := IsRegCodeValid(inAppKey, releaseCode);
end;

{ Used in your program to test if the license has expired }

function UnlockCodeExpiryDate(const inReleaseCodeHexString : string; const inAppKey : TKey) : TDateTime;
var
  releaseCode : TCode;
begin
  HexToBuffer(inReleaseCodeHexString, releaseCode, SizeOf(releaseCode));
  Result := GetExpirationDate(inAppKey, releaseCode);
end;

I do use OnGuard extensively but only for enterprise software where piracy isn't such an issue. If you're selling consumer software and your worried about piracy I'd recommend a stronger solution such as a commercial copy protection library that encrypts the exe.

Even then you can slow the crackers down, but you can't stop them.


3) You should "scatter" the key around and maybe having part of it computed someway. The simpler to identify the key, of course the simpler to bypass protection. But even a complex key is useless if a simple JMP instruction in the proper place will bypass the whole protection check. Those checks should be also more than one, and again, scattered around.

4) Be careful with such kind of licenses - usually users don't like them unless the annual fee means also some perceived value (i.e. an antivirus gives you update signatures, or a GPS application updated maps). Just forcing users to pay annually may look good for you, but not for users, even if you add new features they may not regard as useful. If the application stop working is even worse. That's one of the issue that killed many Unix applications when Windows application with the same features but without yearly fees became available. I know that many companies are thinking to return to that model - if it will be successful is yet to be seen.


Need Your Help

AES Library or code for C++

c++ encryption aes twofish

I'm supposed to overload the class below twice. Once I'm supposed to implement it with AES (Rijndael) and once with Twofish. However, I can't seem to find a simple implementation that allows to enc...

Node js async.series not working with Express app — response happening too soon

javascript node.js asynchronous express

I have a Node js Express app where I am setting cookies from many different places (the server, Twitter, another database, etc). These all need to happen in series. To avoid a callback nightmare,...

About UNIX Resources Network

Original, collect and organize Developers related documents, information and materials, contains jQuery, Html, CSS, MySQL, .NET, ASP.NET, SQL, objective-c, iPhone, Ruby on Rails, C, SQL Server, Ruby, Arrays, Regex, ASP.NET MVC, WPF, XML, Ajax, DataBase, and so on.