sb 1-2
This commit is contained in:
1 parent
1eba2934d3
commit
a2e0206604
15 files changed
+1311
-2
No files matched your search
@@ -0,0 +1,29 @@
|
||||
.PHONY: clean
|
||||
|
||||
OBJS = md5.o md_common.c reg.o sb.o snak.o snakext.o
|
||||
CFLAGS += -std=c11 -D_POSIX_C_SOURCE=2
|
||||
|
||||
all: sb
|
||||
|
||||
md5.o: md5.c md5.h
|
||||
md_common.o: md_common.c md5.h
|
||||
reg.o: h.h md5.h
|
||||
sb.o: h.h
|
||||
snak.o: snak.c h.h
|
||||
snakext.o: snakext.c h.h md5.h
|
||||
|
||||
sb: $(OBJS)
|
||||
$(CC) -o sb $(OBJS)
|
||||
|
||||
clean:
|
||||
rm -f sb *.o
|
||||
|
||||
dist:
|
||||
mkdir sb-1.2
|
||||
cp *.c *.h README Makefile sb-1.2
|
||||
cp -r doc/ sb-1.2
|
||||
groff -Tpdf -mdoc doc/sb.1 > sb-1.2/doc/sb.1.pdf
|
||||
groff -Tpdf -ms doc/drm.ms > sb-1.2/doc/drm.ms.pdf
|
||||
tar czf sb-1.2.tar.gz --owner=root --group=root --format=ustar sb-1.2
|
||||
rm -rf sb-1.2
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
Overview
|
||||
--------
|
||||
|
||||
sb, the SCO Breaker, generates serial numbers, activation keys and
|
||||
registration keys for SCO products, such as UnixWare 7.1.4.
|
||||
|
||||
It still works for versions released after SCO was acquired by Xinuos.
|
||||
Products made by Xinuos themselves (namely OpenServer 10) do not employ any
|
||||
kind of DRM.
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
MIT
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
$ make
|
||||
$ install -s sb /usr/local/bin
|
||||
$ install -m 0644 doc/sb.1 /usr/local/man/man1
|
||||
|
||||
The install step is optional; sb does not depend on any files and can thus be
|
||||
run from any directory.
|
||||
|
||||
Usage and Documentation
|
||||
-----------------------
|
||||
|
||||
See doc/sb.1 for using sb and doc/drm.ms for a description of the DRM
|
||||
mechanisms employed by SCO.
|
||||
|
||||
For those with no access to troff implementation, a PDF version of the
|
||||
documents have been included.
|
||||
|
||||
Motivation
|
||||
----------
|
||||
|
||||
UNIX wants to be free.
|
||||
It's an integral part of the UNIX experience to be able to read and understand
|
||||
the source code of the system.
|
||||
If you haven't done so yet, use something from the V6 through 4.4BSD era.
|
||||
You'll see what I mean.
|
||||
|
||||
So this is a very elaborate "Fuck you" to SCO.
|
||||
I hope it's to your liking.
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
# sco-breaker
|
||||
Generates keys for an aptly named Operating System
|
||||
+274
@@ -0,0 +1,274 @@
|
||||
.de UX
|
||||
\s-1UNIX\s0\\$1
|
||||
..
|
||||
.RP
|
||||
.TL
|
||||
Digital Rights Management Mechanisms in the SCO Product Line
|
||||
.AU
|
||||
Anonymous
|
||||
.ND
|
||||
.AB
|
||||
This document attempts to explain the primary mechanisms with which SCO
|
||||
has attempted to protect their intellectual property as well as how they fell
|
||||
short of doing so.
|
||||
.PP
|
||||
In particular, the serial number and activation key (SNAK) combination, the
|
||||
license data and the registration key shall be covered.
|
||||
.AE
|
||||
.SH
|
||||
Overview
|
||||
.LP
|
||||
The product line of SCO Group, Inc. (SCO) consists of the OpenServer and
|
||||
UnixWare operating systems as well as associated add-on software, such as user
|
||||
licenses or development kits.
|
||||
After SCO had gone bankrupt, UnXis, Inc. bought their
|
||||
.UX
|
||||
business and continued to offer it.
|
||||
UnXis, Inc. later renamed to Xinuos, Inc.
|
||||
The new OpenServer 10 operating system offered by Xinuos, Inc. is
|
||||
.I not
|
||||
covered by this document;
|
||||
OpenServer 10 eschews digital rights management entirely.
|
||||
Because of this, ``SCO products'' is still used, even though the products are
|
||||
now maintained and sold by a different entity.
|
||||
.PP
|
||||
SCO's digital rights management consists of a number of different parts:
|
||||
.IP 1.
|
||||
the serial number (henceforth
|
||||
.B serno )
|
||||
and activation key,
|
||||
.B actkey ), (
|
||||
forming the
|
||||
.B SNAK '', ``
|
||||
.IP 2.
|
||||
the license data indicating additional information about a product, and
|
||||
.IP 3.
|
||||
the registration procedure.
|
||||
.LP
|
||||
The purchase of an SCO product provides the buyer with a SNAK and license data
|
||||
if required.
|
||||
For continued use, a product usually also needs to be
|
||||
.I registered .
|
||||
When the system prompts for registration, a registration lock
|
||||
.B reglock ) (
|
||||
is displayed.
|
||||
The reglock is then supposed to be entered together with personally
|
||||
identifying information on a Xinuos, Inc. web portal.
|
||||
.SH
|
||||
Serial Number and Activation Key
|
||||
.LP
|
||||
The pair of serial number and activation key, internally also called
|
||||
``SNAK'', consists of a nine-character serno and an eight-character actkey.
|
||||
The actkey encodes the product ID, product version and whether license data is
|
||||
required as well as a checksum over the serno and the data contained in the
|
||||
actkey.
|
||||
The first three characters of the actkey denote the product ID.
|
||||
The second triplet denotes the product version and whether license data is
|
||||
required.
|
||||
The final two characters are the checksum.
|
||||
While the serno is effectively an arbitrary string, the actkey is an
|
||||
all-lowercase string.
|
||||
For example, a valid SNAK would be the serno ``SCO524572'' and the actkey
|
||||
``mnridxjo''.
|
||||
There are two layers of protection: obscurity and a checksum.
|
||||
.PP
|
||||
The
|
||||
.B actkey
|
||||
is encrypted.
|
||||
Presumably because of cryptography export restrictions in the United States of
|
||||
America, the encryption algorithm is weak.
|
||||
The ciphertext (i.e. the actkey) is processed in reverse, starting with the
|
||||
NUL.
|
||||
Each character is
|
||||
.I rotated
|
||||
in the alphabet (a-z) by the previous character's offset in the alphabet.
|
||||
The first character to be decrypted, which is the last character in the
|
||||
string, will always be unchanged by this algorithm.
|
||||
See
|
||||
.I incfrp ()
|
||||
(decryption) and
|
||||
.I decfrp ()
|
||||
(encryption)
|
||||
functions for the implementation.
|
||||
.PP
|
||||
The second layer of protection is a simple checksum over the serno and
|
||||
the first six characters of the decrypted actkey.
|
||||
See the
|
||||
.I mnsnc ()
|
||||
function for the implementation.\(dg
|
||||
.FS
|
||||
\(dg ``incfrp'' and ``mnsnc'' are the names used by SCO.
|
||||
It is unclear what they stand for.
|
||||
``decfrp'' has been named that way because the opposite of increment is
|
||||
decrement, assuming ``inc'' in ``incfrp'' stands for ``increment''.
|
||||
.FE
|
||||
.PP
|
||||
After decrypting the actkey and validating the checksum,
|
||||
the decrypted actkey is parsed.
|
||||
Both of the data triplets in the actkey are encoded in base 26 with alphabet
|
||||
a-z; the most significant digit is written first.
|
||||
For the example above, the decrypted actkey is ``ahvneoco''.
|
||||
``ahv'' decodes to 0xCB (203), which is the product ID.
|
||||
``neo'' decodes to 0x22CA (8906), which contains the flag if license data is
|
||||
required and the product version.
|
||||
The version is contained in the lower three nibbles:
|
||||
0x22CA & 0xFFF equals 714.
|
||||
The last decimal digit (4) denotes the minor version, the other ones denote
|
||||
the major version (71).
|
||||
``co'' is the checksum value.
|
||||
.SH
|
||||
License Data
|
||||
.LP
|
||||
License data is required if bits 11\(en15 of the second triplet in the actkey
|
||||
equal 3.
|
||||
In the given example, this wasn't the case; the relevant bits equal 2 instead.
|
||||
Values other than 2 and 3 are invalid.
|
||||
License data is used to supply additional information about a license and
|
||||
enforce restrictions.
|
||||
If license data is required, it is printed alongside the SNAK on Certificates
|
||||
of License and Authenticity.
|
||||
For example, license data could look like this: ``c4;k0;mjqs8r7''.
|
||||
.PP
|
||||
The core of the algorithm is an MD5 hash over a secret value, the serno, the
|
||||
actkey and the rest of the license data.
|
||||
Then, this hash is translated to a custom base 32 alphabet.
|
||||
It is important to note that the license data is ordered by the flag
|
||||
characters, except for the m flag always being at the end.
|
||||
See the
|
||||
.Pa snakext.c
|
||||
file for the implementation.
|
||||
.PP
|
||||
This algorithm is actually susceptible to length-extension attacks because the
|
||||
secret is merely prepended and MD5 has no mitigations for length-extension
|
||||
attacks.
|
||||
However, because license data is fairly strict in validating the input, it is
|
||||
not actually practical.
|
||||
.PP
|
||||
The following fields are known:
|
||||
.IP c
|
||||
number of CPUs;
|
||||
.IP d
|
||||
expiration of license in days;
|
||||
this is used for evaluation licenses;
|
||||
.IP k
|
||||
if no argument or a non-zero argument is given, registration is required, but
|
||||
more than one user can access the system;
|
||||
if the argument is zero, no registration is required, but only one user can
|
||||
access the system and possibly the network stack is gimped;
|
||||
.IP u
|
||||
number of users that may access the system at the same time;
|
||||
.IP m
|
||||
MD5 hash over the license data secret, the serno, the actkey and a
|
||||
canonicalized version of the string until this flag.
|
||||
.LP
|
||||
More flags exist.
|
||||
At least b, g, q have been observed but their format and effects are unknown.
|
||||
.SH
|
||||
Registration Key
|
||||
.LP
|
||||
Some products must be registered, else they expire after a set amount of time.
|
||||
This involves a per-installation identifier, the host ID (also referred to as
|
||||
node ID).
|
||||
The host ID and serial number are then combined to generate the registration
|
||||
key.
|
||||
Because the host ID is different for each installation of a product, storing
|
||||
registration keys is meaningless.
|
||||
Most likely, SCO realized the deficiencies of the simple SNAK scheme and added
|
||||
another layer of protection.
|
||||
Customers are meant to use an online portal to register their products,
|
||||
which also checks for double registrations of the same product, allowing the
|
||||
identification of serial numbers that have been shared.
|
||||
.I "/etc/brand -k serno"
|
||||
or
|
||||
.I scoadmin
|
||||
can be used to generate the registration lock.
|
||||
Some of the data there is superfluous for registration code generation;
|
||||
it is surmised that they are collected for statistical purposes.
|
||||
This also implies that SCO has customers, which may be a stretch in the first
|
||||
place, especially considering the USD 2,500 price tag.
|
||||
.PP
|
||||
The core of the algorithm is an MD5 hash over a secret value, the host ID and
|
||||
the serno.
|
||||
The secret value differs from the one used for the license data.
|
||||
After that, the first four bytes of the hash are swapped and then encoded in
|
||||
base16 with a custom alphabet and a two-character checksum.
|
||||
Because this scheme is symmetric, no actual interaction with SCO is required.
|
||||
Furthermore, no measures were taken to obscure the secret \(en it is stored in
|
||||
the
|
||||
.I /etc/brand
|
||||
binary with no obfuscation whatsoever.
|
||||
The secret value used to sign a registration key is the same as the one used
|
||||
to create the m flag for the registration lock.
|
||||
There is something truly, profoundly wrong with this; I believe I need not
|
||||
spell it out.
|
||||
See the
|
||||
.I reg.c
|
||||
file for the implementation.
|
||||
.PP
|
||||
A registration lock may look like this:
|
||||
``d180120;e380106;i203/71.4;oSCO310807;uorxrrwjwxz;mg7fuxu''.
|
||||
Like the license data, it is a flag string.
|
||||
The following flags are known:
|
||||
.IP i
|
||||
the product ID and version;
|
||||
the product ID is separated by a slash, the product major and minor versions
|
||||
are separated by a dot;
|
||||
.IP o
|
||||
the serno;
|
||||
.IP u
|
||||
the host ID, encoded in some variation of base16 with a custom alphabet
|
||||
(kbwtacorhzgsejqx) with an appended checksum;
|
||||
.IP m
|
||||
MD5 hash over the registration secret, and a canonicalized version of the
|
||||
string until this flag;
|
||||
the canonicalization algorithm is the same as for license data.
|
||||
.LP
|
||||
Other flags exist, namely d and e, but their purpose is unknown.
|
||||
.SH
|
||||
Conclusions
|
||||
.LP
|
||||
SCO has done everything wrong that could possibly be done wrong, while also
|
||||
making matters much more complicated for themselves than necessary.
|
||||
There are a total of four checksums:
|
||||
one in the SNAK, a different one in the flag string for the license data, a
|
||||
different one in the flag string for the registration lock and a different one
|
||||
for the registration key.
|
||||
Furthermore, there are two secrets:
|
||||
the one for the flag string in the license data and the one used for both
|
||||
sides of the registration process.
|
||||
And then there are three different encoding schemes:
|
||||
the encryption of the activation key, the encoding of the activation key (base
|
||||
26), the base 32 encoding in the m flag for license data and registration
|
||||
lock and the base 16 encoding for the registration key.
|
||||
It would not have been necessary to keep this many separate encodings and
|
||||
algorithms around.
|
||||
.PP
|
||||
Due to poor operational security, a mostly complete code dump of SCO UnixWare
|
||||
leaked on the Internet.
|
||||
They realized that keeping the
|
||||
.I /etc/brand
|
||||
utility in the main tree would be dangerous, so it was checked in only as a
|
||||
binary file.
|
||||
However, the binary was neither optimized nor stripped, making reverse
|
||||
engineering effectively trivial.
|
||||
.PP
|
||||
Because all secrets in this DRM mechanism are known to both SCO and
|
||||
.I /etc/brand ,
|
||||
reverse engineering is all that is required to break every layer of
|
||||
protection;
|
||||
there is no cryptographic layer of protection, such as asymmetric signatures
|
||||
over the registration key.
|
||||
Elliptic curve signatures in a base64 encoding would likely have been
|
||||
tolerable for users.
|
||||
Alternatively, a truncated RSA signature could have been used \(en a full
|
||||
signature would be too long for users to type into the terminal.
|
||||
The short Schnorr signatures would have been another option, used by Microsoft
|
||||
in the Windows XP era.
|
||||
.PP
|
||||
None of this has a real-world impact and the estimated amount of lost sales
|
||||
tends towards zero.
|
||||
The only reasons to buy an SCO product are either legacy applications or the
|
||||
support contract that comes with it.
|
||||
Legacy applications generally do not generate new sales.
|
||||
Breaking the DRM mechanisms does not cause a support contract to come into
|
||||
existence out of thin air.
|
||||
Binary file not shown.
@@ -0,0 +1,160 @@
|
||||
.Dd January 27, 2018
|
||||
.Dt SB 1
|
||||
.Os
|
||||
.
|
||||
.Sh NAME
|
||||
.Nm sb
|
||||
.Nd generate serial numbers, activation keys and registration keys
|
||||
.
|
||||
.Sh SYNOPSIS
|
||||
.Nm
|
||||
.Ar product_id
|
||||
.Ar major_ver
|
||||
.Ar minor_ver
|
||||
.Op Ar license_data
|
||||
.Nm
|
||||
.Fl r
|
||||
.Ar serial_number
|
||||
.Ar host_id
|
||||
.Nm
|
||||
.Fl r
|
||||
.Ar registration_lock
|
||||
.
|
||||
.Sh DESCRIPTION
|
||||
.Nm
|
||||
generates serial numbers, activation keys and registration keys for SCO
|
||||
products.
|
||||
SCO products from 1992 and earlier may not be compatible with the licensing
|
||||
information generated by
|
||||
.Nm .
|
||||
.Pp
|
||||
During installation of an SCO product, you will be prompted for at least a
|
||||
serial number and an activation key.
|
||||
This information can be generated by calling
|
||||
.Nm
|
||||
.Em without
|
||||
.Fl r .
|
||||
In order to generate a serial number and activation key
|
||||
.Dq ( SNAK ) ,
|
||||
you need the correct
|
||||
product ID, major version and minor version.
|
||||
The product ID is an SCO-internal integer that denotes the licensed product.
|
||||
The major and minor versions denote the version of the licensed product.
|
||||
.Sy This may not be the same as the version the product is marketed as .
|
||||
For example, UnixWare 7.1.4 has major version 71 and minor version 4.
|
||||
The distinction between 7.1.4, 7.1.4+ and 7.1.4 Definitive is made through the
|
||||
product ID, rather than the version fields.
|
||||
.Pp
|
||||
On an SCO UNIX installation, you can use
|
||||
.Cm /etc/brand -d
|
||||
to list all known product IDs for that installation.
|
||||
Usually, there is also a pre-installation and post-installation script bundled
|
||||
with the SCO product that shows the specific expectations for product ID and
|
||||
version values;
|
||||
search those scripts for
|
||||
.Dq Cm brand -Q .
|
||||
.Pp
|
||||
Optionally, you can supply
|
||||
.Em license data .
|
||||
License data is a semicolon-delimited list of fields that specify various
|
||||
aspects about the license in question.
|
||||
Each field consists of a character and then a value, e.g.
|
||||
.Dq c4;u100
|
||||
denotes a license for four CPUs and 100 users.
|
||||
License data may also only consist of a single field.
|
||||
The following characters are known:
|
||||
.Bl -tag -width Ds
|
||||
.It Cm c
|
||||
Number of CPUs.
|
||||
.It Cm d
|
||||
Expiry time of the license in days.
|
||||
This is used for evaluation licenses.
|
||||
.It Cm k
|
||||
If no argument or a non-zero argument is given, registration is required, but
|
||||
more than one user can access the system.
|
||||
If the argument is zero, no registration is required, but only one user can
|
||||
access the system and possibly the network stack is gimped.
|
||||
.It Cm u
|
||||
Number of users.
|
||||
.It Cm m
|
||||
Checksum over the other fields.
|
||||
.Sy You must not supply this value .
|
||||
.El
|
||||
.Pp
|
||||
The characters g, q and x have also been observed, but their effect is
|
||||
unknown.
|
||||
.Sy This program does not check license data is not checked for validity .
|
||||
.Sy You must order the license data alphabetically .
|
||||
.Pp
|
||||
After installation, you may also be asked for a
|
||||
.Em registration key
|
||||
for continued operation.
|
||||
.Nm
|
||||
will generate a registration key if the
|
||||
.Fl r
|
||||
flag is passed.
|
||||
You need to supply either the serial number of the product and the host ID, or
|
||||
a valid registration lock.
|
||||
The host ID differs for every installation, even for the same product and
|
||||
serial number.
|
||||
If you have forgotten the serial number of the product, it can be found with
|
||||
.Cm /etc/brand -L .
|
||||
The host ID can be found with
|
||||
.Pa /etc/brand Fl I .
|
||||
If you supply a registration lock, please be aware that it contains semicolons
|
||||
(;).
|
||||
You will have to quote it to prevent the shell from parsing the semicolons as
|
||||
command delimiters.
|
||||
.
|
||||
.Sh EXIT STATUS
|
||||
.Ex -std
|
||||
.
|
||||
.Sh EXAMPLES
|
||||
Generate a new serial number and activation key for UnixWare 7.1.4 Definitive
|
||||
2018 (product ID 203, version major 71, version minor 4) with no license data:
|
||||
.Bd -literal -offset indent
|
||||
$ sb 203 71 4
|
||||
.Ed
|
||||
.Pp
|
||||
Register a product for your host:
|
||||
.Bd -literal -offset indent
|
||||
$ /etc/brand -I
|
||||
orxrrwjwxz
|
||||
$ sb -r SCO539702 orxrrwjwxz
|
||||
.Ed
|
||||
.
|
||||
.Sh DIAGNOSTICS
|
||||
.Bl -diag
|
||||
.It "%s not a number"
|
||||
The supplied product ID, major version or minor version is not actually a
|
||||
number.
|
||||
.It "%s out of range (max %u)"
|
||||
The supplied product ID, major version or minor version is out of range.
|
||||
If you are
|
||||
.Em absolutely certain
|
||||
that you have the correct parameters, please contact the author to fix the
|
||||
limit.
|
||||
The limits are theoretical maximum limits, not ones observed in use by SCO.
|
||||
.It "malloc"
|
||||
Memory allocation has failed.
|
||||
If you had enough memory to display this manual page, you can probably just
|
||||
try again.
|
||||
You may need to check
|
||||
.Pa /etc/malloc.conf
|
||||
if the issue persists.
|
||||
.It "internal: limit > UINT16_MAX"
|
||||
You should never see this.
|
||||
If you do, your platform is almost certainly clinically insane.
|
||||
Please stop trying to run
|
||||
.Nm
|
||||
on a literal Game Boy.
|
||||
.El
|
||||
.
|
||||
.Sh CAVEATS
|
||||
While this code appears to work, it may be possible that the product
|
||||
nonetheless phones home over IP.
|
||||
No efforts have been made to try and observe such activity.
|
||||
It may prove advantageous to first strictly firewall an SCO UNIX installation.
|
||||
If the outgoing connections look okay for approximately 24 hours, it may be
|
||||
safe to let loose on the Internet.
|
||||
More cautious users may wish to wait up to 31 days.
|
||||
Binary file not shown.
@@ -0,0 +1,24 @@
|
||||
#ifndef SB_H_H
|
||||
#define SB_H_H
|
||||
|
||||
/* sb.c */
|
||||
void usage(bool fail);
|
||||
_Noreturn void die(const char *msgfmt, ...);
|
||||
|
||||
/* md_common.c */
|
||||
char *BSCanon(const char *s);
|
||||
void extmd(char md[static 7], unsigned int nstr, ...);
|
||||
|
||||
/* reg.c */
|
||||
int gen_regcode(int argc, char *argv[]);
|
||||
|
||||
/* snak.c */
|
||||
int gen_snak(int argc, char *argv[]);
|
||||
|
||||
/* snakext.c */
|
||||
void mdsnakext(const char *serno, const char *actkey, const char *snakext,
|
||||
char snakextmd[static 7]);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,246 @@
|
||||
/* $OpenBSD: md5.c,v 1.11 2015/09/11 09:18:27 guenther Exp $ */
|
||||
|
||||
/*
|
||||
* This code implements the MD5 message-digest algorithm.
|
||||
* The algorithm is due to Ron Rivest. This code was
|
||||
* written by Colin Plumb in 1993, no copyright is claimed.
|
||||
* This code is in the public domain; do with it what you wish.
|
||||
*
|
||||
* Equivalent code is available from RSA Data Security, Inc.
|
||||
* This code has been tested against that, and is equivalent,
|
||||
* except that you don't need to include two pages of legalese
|
||||
* with every copy.
|
||||
*
|
||||
* To compute the message digest of a chunk of bytes, declare an
|
||||
* MD5Context structure, pass it to MD5Init, call MD5Update as
|
||||
* needed on buffers full of bytes, and then call MD5Final, which
|
||||
* will fill a supplied 16-byte array with the digest.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <string.h>
|
||||
#include "md5.h"
|
||||
|
||||
#define PUT_64BIT_LE(cp, value) do { \
|
||||
(cp)[7] = (value) >> 56; \
|
||||
(cp)[6] = (value) >> 48; \
|
||||
(cp)[5] = (value) >> 40; \
|
||||
(cp)[4] = (value) >> 32; \
|
||||
(cp)[3] = (value) >> 24; \
|
||||
(cp)[2] = (value) >> 16; \
|
||||
(cp)[1] = (value) >> 8; \
|
||||
(cp)[0] = (value); } while (0)
|
||||
|
||||
#define PUT_32BIT_LE(cp, value) do { \
|
||||
(cp)[3] = (value) >> 24; \
|
||||
(cp)[2] = (value) >> 16; \
|
||||
(cp)[1] = (value) >> 8; \
|
||||
(cp)[0] = (value); } while (0)
|
||||
|
||||
static uint8_t PADDING[MD5_BLOCK_LENGTH] = {
|
||||
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
|
||||
/*
|
||||
* Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
|
||||
* initialization constants.
|
||||
*/
|
||||
void
|
||||
MD5Init(MD5_CTX *ctx)
|
||||
{
|
||||
ctx->count = 0;
|
||||
ctx->state[0] = 0x67452301;
|
||||
ctx->state[1] = 0xefcdab89;
|
||||
ctx->state[2] = 0x98badcfe;
|
||||
ctx->state[3] = 0x10325476;
|
||||
}
|
||||
|
||||
/*
|
||||
* Update context to reflect the concatenation of another buffer full
|
||||
* of bytes.
|
||||
*/
|
||||
void
|
||||
MD5Update(MD5_CTX *ctx, const unsigned char *input, size_t len)
|
||||
{
|
||||
size_t have, need;
|
||||
|
||||
/* Check how many bytes we already have and how many more we need. */
|
||||
have = (size_t)((ctx->count >> 3) & (MD5_BLOCK_LENGTH - 1));
|
||||
need = MD5_BLOCK_LENGTH - have;
|
||||
|
||||
/* Update bitcount */
|
||||
ctx->count += (uint64_t)len << 3;
|
||||
|
||||
if (len >= need) {
|
||||
if (have != 0) {
|
||||
memcpy(ctx->buffer + have, input, need);
|
||||
MD5Transform(ctx->state, ctx->buffer);
|
||||
input += need;
|
||||
len -= need;
|
||||
have = 0;
|
||||
}
|
||||
|
||||
/* Process data in MD5_BLOCK_LENGTH-byte chunks. */
|
||||
while (len >= MD5_BLOCK_LENGTH) {
|
||||
MD5Transform(ctx->state, input);
|
||||
input += MD5_BLOCK_LENGTH;
|
||||
len -= MD5_BLOCK_LENGTH;
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle any remaining bytes of data. */
|
||||
if (len != 0)
|
||||
memcpy(ctx->buffer + have, input, len);
|
||||
}
|
||||
|
||||
/*
|
||||
* Pad pad to 64-byte boundary with the bit pattern
|
||||
* 1 0* (64-bit count of bits processed, MSB-first)
|
||||
*/
|
||||
void
|
||||
MD5Pad(MD5_CTX *ctx)
|
||||
{
|
||||
uint8_t count[8];
|
||||
size_t padlen;
|
||||
|
||||
/* Convert count to 8 bytes in little endian order. */
|
||||
PUT_64BIT_LE(count, ctx->count);
|
||||
|
||||
/* Pad out to 56 mod 64. */
|
||||
padlen = MD5_BLOCK_LENGTH -
|
||||
((ctx->count >> 3) & (MD5_BLOCK_LENGTH - 1));
|
||||
if (padlen < 1 + 8)
|
||||
padlen += MD5_BLOCK_LENGTH;
|
||||
MD5Update(ctx, PADDING, padlen - 8); /* padlen - 8 <= 64 */
|
||||
MD5Update(ctx, count, 8);
|
||||
}
|
||||
|
||||
/*
|
||||
* Final wrapup--call MD5Pad, fill in digest and zero out ctx.
|
||||
*/
|
||||
void
|
||||
MD5Final(unsigned char digest[MD5_DIGEST_LENGTH], MD5_CTX *ctx)
|
||||
{
|
||||
int i;
|
||||
|
||||
MD5Pad(ctx);
|
||||
for (i = 0; i < 4; i++)
|
||||
PUT_32BIT_LE(digest + i * 4, ctx->state[i]);
|
||||
}
|
||||
|
||||
|
||||
/* The four core functions - F1 is optimized somewhat */
|
||||
|
||||
/* #define F1(x, y, z) (x & y | ~x & z) */
|
||||
#define F1(x, y, z) (z ^ (x & (y ^ z)))
|
||||
#define F2(x, y, z) F1(z, x, y)
|
||||
#define F3(x, y, z) (x ^ y ^ z)
|
||||
#define F4(x, y, z) (y ^ (x | ~z))
|
||||
|
||||
/* This is the central step in the MD5 algorithm. */
|
||||
#define MD5STEP(f, w, x, y, z, data, s) \
|
||||
( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
|
||||
|
||||
/*
|
||||
* The core of the MD5 algorithm, this alters an existing MD5 hash to
|
||||
* reflect the addition of 16 longwords of new data. MD5Update blocks
|
||||
* the data and converts bytes into longwords for this routine.
|
||||
*/
|
||||
void
|
||||
MD5Transform(uint32_t state[4], const uint8_t block[MD5_BLOCK_LENGTH])
|
||||
{
|
||||
uint32_t a, b, c, d, in[MD5_BLOCK_LENGTH / 4];
|
||||
|
||||
#if BYTE_ORDER == LITTLE_ENDIAN
|
||||
memcpy(in, block, sizeof(in));
|
||||
#else
|
||||
for (a = 0; a < MD5_BLOCK_LENGTH / 4; a++) {
|
||||
in[a] = (uint32_t)(
|
||||
(uint32_t)(block[a * 4 + 0]) |
|
||||
(uint32_t)(block[a * 4 + 1]) << 8 |
|
||||
(uint32_t)(block[a * 4 + 2]) << 16 |
|
||||
(uint32_t)(block[a * 4 + 3]) << 24);
|
||||
}
|
||||
#endif
|
||||
|
||||
a = state[0];
|
||||
b = state[1];
|
||||
c = state[2];
|
||||
d = state[3];
|
||||
|
||||
MD5STEP(F1, a, b, c, d, in[ 0] + 0xd76aa478, 7);
|
||||
MD5STEP(F1, d, a, b, c, in[ 1] + 0xe8c7b756, 12);
|
||||
MD5STEP(F1, c, d, a, b, in[ 2] + 0x242070db, 17);
|
||||
MD5STEP(F1, b, c, d, a, in[ 3] + 0xc1bdceee, 22);
|
||||
MD5STEP(F1, a, b, c, d, in[ 4] + 0xf57c0faf, 7);
|
||||
MD5STEP(F1, d, a, b, c, in[ 5] + 0x4787c62a, 12);
|
||||
MD5STEP(F1, c, d, a, b, in[ 6] + 0xa8304613, 17);
|
||||
MD5STEP(F1, b, c, d, a, in[ 7] + 0xfd469501, 22);
|
||||
MD5STEP(F1, a, b, c, d, in[ 8] + 0x698098d8, 7);
|
||||
MD5STEP(F1, d, a, b, c, in[ 9] + 0x8b44f7af, 12);
|
||||
MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
|
||||
MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
|
||||
MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
|
||||
MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
|
||||
MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
|
||||
MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
|
||||
|
||||
MD5STEP(F2, a, b, c, d, in[ 1] + 0xf61e2562, 5);
|
||||
MD5STEP(F2, d, a, b, c, in[ 6] + 0xc040b340, 9);
|
||||
MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
|
||||
MD5STEP(F2, b, c, d, a, in[ 0] + 0xe9b6c7aa, 20);
|
||||
MD5STEP(F2, a, b, c, d, in[ 5] + 0xd62f105d, 5);
|
||||
MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
|
||||
MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
|
||||
MD5STEP(F2, b, c, d, a, in[ 4] + 0xe7d3fbc8, 20);
|
||||
MD5STEP(F2, a, b, c, d, in[ 9] + 0x21e1cde6, 5);
|
||||
MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
|
||||
MD5STEP(F2, c, d, a, b, in[ 3] + 0xf4d50d87, 14);
|
||||
MD5STEP(F2, b, c, d, a, in[ 8] + 0x455a14ed, 20);
|
||||
MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
|
||||
MD5STEP(F2, d, a, b, c, in[ 2] + 0xfcefa3f8, 9);
|
||||
MD5STEP(F2, c, d, a, b, in[ 7] + 0x676f02d9, 14);
|
||||
MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
|
||||
|
||||
MD5STEP(F3, a, b, c, d, in[ 5] + 0xfffa3942, 4);
|
||||
MD5STEP(F3, d, a, b, c, in[ 8] + 0x8771f681, 11);
|
||||
MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
|
||||
MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
|
||||
MD5STEP(F3, a, b, c, d, in[ 1] + 0xa4beea44, 4);
|
||||
MD5STEP(F3, d, a, b, c, in[ 4] + 0x4bdecfa9, 11);
|
||||
MD5STEP(F3, c, d, a, b, in[ 7] + 0xf6bb4b60, 16);
|
||||
MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
|
||||
MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
|
||||
MD5STEP(F3, d, a, b, c, in[ 0] + 0xeaa127fa, 11);
|
||||
MD5STEP(F3, c, d, a, b, in[ 3] + 0xd4ef3085, 16);
|
||||
MD5STEP(F3, b, c, d, a, in[ 6] + 0x04881d05, 23);
|
||||
MD5STEP(F3, a, b, c, d, in[ 9] + 0xd9d4d039, 4);
|
||||
MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
|
||||
MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
|
||||
MD5STEP(F3, b, c, d, a, in[2 ] + 0xc4ac5665, 23);
|
||||
|
||||
MD5STEP(F4, a, b, c, d, in[ 0] + 0xf4292244, 6);
|
||||
MD5STEP(F4, d, a, b, c, in[7 ] + 0x432aff97, 10);
|
||||
MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
|
||||
MD5STEP(F4, b, c, d, a, in[5 ] + 0xfc93a039, 21);
|
||||
MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
|
||||
MD5STEP(F4, d, a, b, c, in[3 ] + 0x8f0ccc92, 10);
|
||||
MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
|
||||
MD5STEP(F4, b, c, d, a, in[1 ] + 0x85845dd1, 21);
|
||||
MD5STEP(F4, a, b, c, d, in[8 ] + 0x6fa87e4f, 6);
|
||||
MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
|
||||
MD5STEP(F4, c, d, a, b, in[6 ] + 0xa3014314, 15);
|
||||
MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
|
||||
MD5STEP(F4, a, b, c, d, in[4 ] + 0xf7537e82, 6);
|
||||
MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
|
||||
MD5STEP(F4, c, d, a, b, in[2 ] + 0x2ad7d2bb, 15);
|
||||
MD5STEP(F4, b, c, d, a, in[9 ] + 0xeb86d391, 21);
|
||||
|
||||
state[0] += a;
|
||||
state[1] += b;
|
||||
state[2] += c;
|
||||
state[3] += d;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
#include <stdint.h>
|
||||
|
||||
/* $OpenBSD: md5.h,v 1.17 2012/12/05 23:19:57 deraadt Exp $ */
|
||||
|
||||
/*
|
||||
* This code implements the MD5 message-digest algorithm.
|
||||
* The algorithm is due to Ron Rivest. This code was
|
||||
* written by Colin Plumb in 1993, no copyright is claimed.
|
||||
* This code is in the public domain; do with it what you wish.
|
||||
*
|
||||
* Equivalent code is available from RSA Data Security, Inc.
|
||||
* This code has been tested against that, and is equivalent,
|
||||
* except that you don't need to include two pages of legalese
|
||||
* with every copy.
|
||||
*/
|
||||
|
||||
#ifndef _MD5_H_
|
||||
#define _MD5_H_
|
||||
|
||||
#define MD5_BLOCK_LENGTH 64
|
||||
#define MD5_DIGEST_LENGTH 16
|
||||
#define MD5_DIGEST_STRING_LENGTH (MD5_DIGEST_LENGTH * 2 + 1)
|
||||
|
||||
typedef struct MD5Context {
|
||||
uint32_t state[4]; /* state */
|
||||
uint64_t count; /* number of bits, mod 2^64 */
|
||||
uint8_t buffer[MD5_BLOCK_LENGTH]; /* input buffer */
|
||||
} MD5_CTX;
|
||||
|
||||
void MD5Init(MD5_CTX *);
|
||||
void MD5Update(MD5_CTX *, const uint8_t *, size_t);
|
||||
void MD5Pad(MD5_CTX *);
|
||||
void MD5Final(uint8_t [MD5_DIGEST_LENGTH], MD5_CTX *);
|
||||
void MD5Transform(uint32_t [4], const uint8_t [MD5_BLOCK_LENGTH]);
|
||||
|
||||
#endif /* _MD5_H_ */
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
#include <ctype.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "md5.h"
|
||||
#include "h.h"
|
||||
|
||||
const char *fromHextet = "0123456789abcdefghjkmnpqrstuwxyz";
|
||||
|
||||
static int
|
||||
asciiCode(int c)
|
||||
{
|
||||
static const int fromAlpha[] = {
|
||||
0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0x10, 0x11, 1, 0x12, 0x13, 1,
|
||||
0x14, 0x15, 0, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1b,
|
||||
0x1c, 0x1d, 0x1e, 0x1f
|
||||
};
|
||||
|
||||
if (c >= '0' && c <= '9')
|
||||
return c - '0';
|
||||
if (isupper(c))
|
||||
c = tolower(c);
|
||||
if (islower(c))
|
||||
return fromAlpha[c - 'a'];
|
||||
return -1;
|
||||
}
|
||||
|
||||
char *
|
||||
BSCanon(const char *s)
|
||||
{
|
||||
char *ret;
|
||||
int c;
|
||||
|
||||
ret = malloc(strlen(s) + 1);
|
||||
if (ret == NULL)
|
||||
die("malloc");
|
||||
strcpy(ret, s);
|
||||
|
||||
for (char *p = ret; *p; ++p) {
|
||||
c = asciiCode(*p);
|
||||
if (c != -1)
|
||||
*p = fromHextet[c];
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
extmd(char md[static 7], unsigned int nstr, ...)
|
||||
{
|
||||
char *str;
|
||||
MD5_CTX ctx;
|
||||
char digest[MD5_DIGEST_LENGTH];
|
||||
va_list ap;
|
||||
|
||||
MD5Init(&ctx);
|
||||
va_start(ap, nstr);
|
||||
|
||||
for (unsigned int i = 0; i < nstr; ++i) {
|
||||
str = va_arg(ap, char *);
|
||||
MD5Update(&ctx, (const uint8_t *)str, strlen(str));
|
||||
}
|
||||
|
||||
MD5Final(digest, &ctx);
|
||||
for (size_t i = 0; i < 6; ++i)
|
||||
md[i] = fromHextet[digest[i] & 0x1F];
|
||||
md[6] = '\0';
|
||||
}
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "h.h"
|
||||
#include "md5.h"
|
||||
|
||||
static const char *regSecret = "\x75\xf8\xe8\x5e\x83\xc4\x5e\x4c\xff\x75\x5e\x48\xe8\x65\x5e\x46\x59\x8b\x45";
|
||||
|
||||
static char *
|
||||
toAsciiBase16(uint32_t in, unsigned char ckbase)
|
||||
{
|
||||
static const char *charmap = "\005k\001b\002w\003t\004a\005c\001o\002r\003h\004z\005g\001s\002e\003j\004q\005x\001";
|
||||
static char buf[11];
|
||||
unsigned int a, i;
|
||||
unsigned char ck;
|
||||
|
||||
ck = ckbase;
|
||||
for (i = 8; i > 0; --i) {
|
||||
a = 15 - (in & 0xF);
|
||||
buf[i - 1] = charmap[2 * a + 1];
|
||||
ck += a * (i);
|
||||
in >>= 4;
|
||||
}
|
||||
|
||||
/* checksum */
|
||||
for (i = 9; i >= 8; --i) {
|
||||
buf[i] = charmap[2 * (ck & 0xF) + 1];
|
||||
ck >>= 4;
|
||||
}
|
||||
|
||||
buf[10] = '\0';
|
||||
return buf;
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
l32be(uint8_t i[4])
|
||||
{
|
||||
return (uint32_t)i[3]
|
||||
| ((uint32_t)i[2] << 8)
|
||||
| ((uint32_t)i[1] << 16)
|
||||
| ((uint32_t)i[0] << 24);
|
||||
}
|
||||
|
||||
static char *
|
||||
generateRegistrationID(const char *serno, const char *szHostid)
|
||||
{
|
||||
static char buf[11];
|
||||
uint32_t regkey;
|
||||
MD5_CTX ctx;
|
||||
uint8_t digest[MD5_DIGEST_LENGTH];
|
||||
|
||||
MD5Init(&ctx);
|
||||
MD5Update(&ctx, (const uint8_t *)regSecret, strlen(regSecret));
|
||||
MD5Update(&ctx, (const uint8_t *)szHostid, strlen(szHostid));
|
||||
MD5Update(&ctx, (const uint8_t *)serno, strlen(serno));
|
||||
MD5Final(digest, &ctx);
|
||||
|
||||
regkey = l32be(digest);
|
||||
snprintf(buf, sizeof(buf), "%s", toAsciiBase16(regkey, 3));
|
||||
return buf;
|
||||
}
|
||||
|
||||
static bool
|
||||
valid_reglock(char *reglock)
|
||||
{
|
||||
char *canon, *theirs, *p;
|
||||
char mine[7];
|
||||
|
||||
if ((p = strstr(reglock, ";m")) == NULL)
|
||||
return false;
|
||||
if (*(p + 2) == '\0')
|
||||
return false;
|
||||
|
||||
*p = '\0';
|
||||
theirs = p + 2;
|
||||
|
||||
canon = BSCanon(reglock);
|
||||
extmd(mine, 2, regSecret, canon);
|
||||
free(canon);
|
||||
|
||||
return (strcmp(mine, theirs) == 0);
|
||||
}
|
||||
|
||||
static void
|
||||
parse_reglock(char *reglock, char **serno, char **hostid)
|
||||
{
|
||||
char *last, *p;
|
||||
|
||||
if (!valid_reglock(reglock))
|
||||
die("registration lock %s invalid; check for typos", reglock);
|
||||
|
||||
/* Assumption: Nobody generates bogus reglocks, so they're well-behaved
|
||||
* after the MD5 checks out.
|
||||
*/
|
||||
for (p = strtok_r(reglock, ";", &last);
|
||||
p != NULL;
|
||||
p = strtok_r(NULL, ";", &last)) {
|
||||
switch (*p) {
|
||||
case 'o':
|
||||
*serno = p + 1;
|
||||
break;
|
||||
case 'u':
|
||||
*hostid = p + 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
gen_regcode(int argc, char *argv[])
|
||||
{
|
||||
char *serno, *hostid, *regcode;
|
||||
size_t serno_len, hostid_len;
|
||||
|
||||
if (argc < 1) {
|
||||
usage(true);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (argc == 2) {
|
||||
serno = argv[0];
|
||||
hostid = argv[1];
|
||||
serno_len = strlen(serno);
|
||||
hostid_len = strlen(hostid);
|
||||
if (serno_len == 10 && hostid_len == 9) {
|
||||
/* Arguments are probably swapped. */
|
||||
serno = argv[1];
|
||||
hostid = argv[0];
|
||||
} else if (serno_len != 9 || hostid_len != 10) {
|
||||
die("invalid length for serno or hostid");
|
||||
}
|
||||
} else {
|
||||
parse_reglock(argv[0], &serno, &hostid);
|
||||
}
|
||||
|
||||
regcode = generateRegistrationID(serno, hostid);
|
||||
printf("Registration Key: %s\n", regcode);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
#include <libgen.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "h.h"
|
||||
|
||||
static char *progname;
|
||||
|
||||
void
|
||||
usage(bool fail)
|
||||
{
|
||||
fprintf(fail ? stderr : stdout,
|
||||
"usage: %s product_id major_ver minor_ver "
|
||||
"[license_data]\n"
|
||||
" %s -r serial_number host_id\n"
|
||||
" %s -r registration_lock\n",
|
||||
progname, progname, progname);
|
||||
}
|
||||
|
||||
_Noreturn void
|
||||
die(const char *msgfmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
fprintf(stderr, "%s: ", progname);
|
||||
|
||||
va_start(ap, msgfmt);
|
||||
vfprintf(stderr, msgfmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
putchar('\n');
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
int c;
|
||||
int ret = EXIT_SUCCESS;
|
||||
bool want_regcode = false;
|
||||
|
||||
progname = basename((argv[0] != NULL) ? argv[0] : "sb");
|
||||
|
||||
while ((c = getopt(argc, argv, "hr")) != -1) {
|
||||
switch (c) {
|
||||
case 'r':
|
||||
want_regcode = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
ret = EXIT_FAILURE;
|
||||
case 'h':
|
||||
usage(ret == EXIT_FAILURE);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
|
||||
if (want_regcode)
|
||||
ret = gen_regcode(argc, argv);
|
||||
else
|
||||
ret = gen_snak(argc, argv);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,183 @@
|
||||
#include <ctype.h>
|
||||
#include <inttypes.h>
|
||||
#include <limits.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "h.h"
|
||||
|
||||
static bool
|
||||
overflow15(uint32_t a, uint32_t b)
|
||||
{
|
||||
return (a + b > 0x7FFF);
|
||||
}
|
||||
|
||||
static char *
|
||||
mnsnc(const char *s)
|
||||
{
|
||||
static char buf[3];
|
||||
uint16_t a;
|
||||
uint16_t flag;
|
||||
int c = s[8] % 16;
|
||||
|
||||
for (a = 0; *s != '\0'; ++s) {
|
||||
if (overflow15(a, *s))
|
||||
flag = 1;
|
||||
else
|
||||
flag = 0;
|
||||
a = flag | (2 * (a + *s));
|
||||
}
|
||||
|
||||
for (; c > 0; --c)
|
||||
a = ((a & 0x8000U) >> 15) | (uint16_t)(2 * a);
|
||||
|
||||
buf[2] = 0;
|
||||
buf[1] = a % 26 + 'a';
|
||||
a /= 26;
|
||||
buf[0] = a % 26 + 'a';
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
static void
|
||||
strbn(char *out, unsigned int in)
|
||||
{
|
||||
static const char *alphabet = "abcdefghijklmnopqrstuvwxyz";
|
||||
size_t i;
|
||||
|
||||
for (i = 3; i > 0; --i) {
|
||||
out[i - 1] = alphabet[in % 26];
|
||||
in /= 26;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
decfrp(char *s)
|
||||
{
|
||||
char *p;
|
||||
unsigned char a, b;
|
||||
|
||||
a = b = 0;
|
||||
|
||||
for (p = s + strlen(s) - 1; p >= s; --p) {
|
||||
a = (*p - 'a' + b) % 26;
|
||||
a = (a + 'a') & 0xFF;
|
||||
b += a;
|
||||
b %= 26;
|
||||
*p = a;
|
||||
}
|
||||
}
|
||||
|
||||
/* Implementation of xorshift* without retaining seed state. */
|
||||
static uint64_t
|
||||
rnd(uint64_t seed)
|
||||
{
|
||||
seed ^= seed >> 12;
|
||||
seed ^= seed << 25;
|
||||
seed ^= seed >> 27;
|
||||
return seed * 0x2545F4914F6CDD1D;
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
mkver(unsigned int lictype, unsigned int major, unsigned int minor)
|
||||
{
|
||||
return ((lictype << 12) | (major * 10 + minor));
|
||||
}
|
||||
|
||||
static void
|
||||
mksnak(bool has_snakext, uint16_t product_id, uint16_t major, uint16_t minor,
|
||||
char *serno, char *actkey)
|
||||
{
|
||||
const char *cksum;
|
||||
uint64_t serial;
|
||||
unsigned int version = mkver((has_snakext ? 3 : 2), major, minor);
|
||||
char merged[18];
|
||||
|
||||
memset(serno, 0, 10);
|
||||
memset(actkey, 0, 9);
|
||||
#ifdef DBG
|
||||
printf("has_snakext: %d, major: %u, minor: %u, version: %u\n",
|
||||
!!has_snakext, major, minor, version);
|
||||
#endif
|
||||
|
||||
/* bitmask to ensure at most six digits */
|
||||
serial = rnd((uintptr_t)serno * time(NULL)) & 0xEFFFF;
|
||||
snprintf(serno, 10, "SCO%06" PRIu64, serial);
|
||||
|
||||
strbn(actkey, product_id);
|
||||
strbn(actkey + 3, version);
|
||||
snprintf(merged, sizeof(merged), "%s%s", serno, actkey);
|
||||
|
||||
cksum = mnsnc(merged);
|
||||
actkey[6] = cksum[0];
|
||||
actkey[7] = cksum[1];
|
||||
|
||||
decfrp(actkey);
|
||||
}
|
||||
|
||||
static uint16_t
|
||||
strtou16lim(const char *in, unsigned long limit)
|
||||
{
|
||||
char *end;
|
||||
unsigned long i;
|
||||
|
||||
if (limit > UINT16_MAX)
|
||||
die("internal: limit > UINT16_MAX");
|
||||
|
||||
i = strtoul(in, &end, 10);
|
||||
if (*in == '\0' || *end != '\0')
|
||||
die("%s not a number", in);
|
||||
if (i > limit)
|
||||
die("%s out of range (max %"PRIu16")", in, limit);
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
int
|
||||
gen_snak(int argc, char *argv[])
|
||||
{
|
||||
char *snakext = NULL;
|
||||
uint16_t product_id, major, minor;
|
||||
char serno[10], actkey[9], snakextmd[7];
|
||||
|
||||
if (argc < 3) {
|
||||
usage(true);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* "zzz" is the maximum possible product ID encoded value.
|
||||
* This decodes to a value of 17575.
|
||||
*/
|
||||
product_id = strtou16lim(argv[0], 17575);
|
||||
|
||||
/* License type (whether snakext is to be read), version major and
|
||||
* version minor share an integer, max encoded as "zzz".
|
||||
* The license type is shifted up by 12, leaving 0xFFF (4095) for the
|
||||
* version major and minor.
|
||||
* Of that, the version major is all the upper digits, and the minor is
|
||||
* the bottom digit (i.e. version/10 => major, version%10 => minor).
|
||||
*
|
||||
* Without doing too much checking, the maximum major version is 409 and
|
||||
* the maximum minor version is 9.
|
||||
*/
|
||||
major = strtou16lim(argv[1], 409);
|
||||
minor = strtou16lim(argv[2], 9);
|
||||
|
||||
if (argc >= 4)
|
||||
snakext = argv[3];
|
||||
|
||||
mksnak(snakext != NULL, product_id, major, minor, serno, actkey);
|
||||
printf("Serial number: %s\n"
|
||||
"Activation key: %s\n", serno, actkey);
|
||||
|
||||
if (snakext != NULL) {
|
||||
mdsnakext(serno, actkey, snakext, snakextmd);
|
||||
printf("License data: %s;m%s\n", snakext, snakextmd);
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
#include <ctype.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "md5.h"
|
||||
#include "h.h"
|
||||
|
||||
static const char *extSecret = "\x5e\x4f\xbe\x45\x5e\x4c\x8d\x40\x9f\xeb\x26\x5e\x4f\xbe\x45\x5e\x4c\x3d\x30\x7c";
|
||||
|
||||
void
|
||||
mdsnakext(const char *serno, const char *actkey, const char *snakext,
|
||||
char snakextmd[static 7])
|
||||
{
|
||||
char *canon;
|
||||
|
||||
canon = BSCanon(snakext);
|
||||
extmd(snakextmd, 4, extSecret, serno, actkey, canon);
|
||||
free(canon);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user