    Rem Program to encrypt object files to run on only a single computer
    Rem REVISION HISTORY:
    Rem First Revision: 8/14/81
    Rem 3/18/82 SERIALIZE1.0b IDB
    Rem         Modified to handle :01,:02 or :03 Start records
    Rem 4/29/82 SERIALIZE 1.0c IDB
    Rem         Modified SDOSSecretKey$ to be stored initially in inverted form
    Rem         This enhances security of program

    Rem THIS PROGRAM SHOULD ONLY BE DELIVERED AS AN ENCRYPTED PROGRAM!!!
    Dim ProgramName$/"Serialize V1.0c"/
    Dim Randomizer$/:55,:a6,:0a,:9c,:e3,:57,:ac,:39/
    dim buffer$(8),Temp$(50)/"MAGICKEY"/,Filename$(50),EncryptionKey$(8)
    dim ObjectByte$(1)
    dim in/1/,out/2/
    dim CPUtype$/:01/
    Rem "MAGICKEY" string initial value is purely a Red Herring
    Rem ****************************************************************
    Rem The Super-Secret SDOS key used to generate encrypt/decryption keys
    Rem if we were being obvious, the following dim statement would be used
    Rem dim SDOSSecretKey$/:4c,:b0,:b7,:4e,:9b,:65,:72,:c9/
    Rem but we're being devious, so it starts with the complement instead!
        dim SDOSSecretKey$/:b3,:4f,:48,:b1,:64,:9a,:8d,:36/
    Rem It is transformed to true form when program starts
    Rem ****************************************************************
!
!   Functions
!
Def NextByte
    Rem Return next byte of object file
    Read #in,ObjectByte$
    If EOF(in)
    Then EndOfObjectFile=True\Return 0
    Else Return ObjectByte$[1]
End

Def NextObjectByte
    Rem Return next object byte that is not part of a skip record
    If EndOfObjectFile then Return 0
    On Objectfilescanstate Goto GetStartRecord,GSR1,GSR2,GSR3,GSR4,...
&                               Type21,Type22,Type23,Type24,Type2Byte,...
&                               Type31,Type32,Type33,Type34,Type3Byte
NewRecord: ! Process new record start
    On NextByte+1 Goto SkipRecord,BadLoadRecord,Type2Start,Type3Start

BadLoadRecord: ! Illegal Load record encountered
    Error :40C

SkipRecord: ! Start of skip record seen, gobble it up!
    For Bytecount=1 to Nextbyte*256+Nextbyte do bitbucket=Nextbyte
    Goto NewRecord

GetStartRecord: ! Eat start record
    CPUtype$(1)=Nextbyte
    If CPUtype$(1)<>1 and CPUtype$(1)<>2 and CPUtype$(1)<>3
    Then Error :404 \ ! Not a load format file
    Let Objectfilescanstate=2 \ ! Next time, get 1st byte of start address
    Return CPUtype$(1)

GSR1: Let Objectfilescanstate=3 \ ! Next time, get 2nd byte of start address
    Return Nextbyte
GSR2: Let Objectfilescanstate=4 \ ! Next time, get 1st byte of comp'd address
    Return Nextbyte
GSR3: Let Objectfilescanstate=5 \ ! Next Time, get 2nd byte of comp'd address
    Return Nextbyte
GSR4: Let Objectfilescanstate=0 \ ! Next time, start a new record
    Return Nextbyte

Type2Start: Let Objectfilescanstate=6 \ ! Next time, get 1st byte of address
    Return 2
Type21: Let Objectfilescanstate=7 \ ! Next time, get 2nd byte of address
    Return Nextbyte \ ! Upper byte of address
Type22: Let Objectfilescanstate=8 \ ! Next time, get upper byte of count
    Return Nextbyte \ ! Lower byte of address
Type23: Let Bytecount=Nextbyte \ ! Upper 8 bits of byte count
    Let Objectfilescanstate=9 \ ! Next time, get 2nd byte of count
    Return Bytecount
Type24: Let Bytecount=(Bytecount**8)+Nextbyte \ ! Add 8 bits of byte count
    Let Objectfilescanstate=10
    Return Bytecount&:FF
Type2Byte: If Bytecount>0
           Then Bytecount=Bytecount-1\Return Nextbyte
           Else NewRecord

Type3Start: Let Objectfilescanstate=11 \ ! Next time, get 1st byte of address
    Return 3
Type31: Let Objectfilescanstate=12 \ ! Next time, get 2nd byte of address
    Return Nextbyte \ ! Upper byte of address
Type32: Let Objectfilescanstate=13 \ ! Next time, get upper byte of count
    Return Nextbyte \ ! Lower byte of address
Type33: Let Bytecount=Nextbyte \ ! Upper 8 bits of byte count
    Let Objectfilescanstate=14 \ ! Next time, get 2nd byte of count
    Return Bytecount
Type34: Let Bytecount=(Bytecount**8)+Nextbyte \ ! Add 8 bits of byte count
    Let Objectfilescanstate=15
    Return Bytecount&:FF
Type3Byte: If Bytecount>0
           Then Bytecount=Bytecount-1\Return Nextbyte
           Else EndofObjectFile=True\Return 0
End
!
!   ****** Main Program starts here
!
Serialize:
    Print ProgramName$
    Print "Serializes object program for a CPU-specific serial number."
    Print "Copyright (C) 1981 Software Dynamics"

    Rem Verify that Decrypt is the inverse function of Encrypt
    Call Encrypt(SDOSSecretKey$,Temp$)
    Call Decrypt(SDOSSecretKey$,Temp$)
    If Temp$<>"MAGICKEY"
    Then Print "ABORTED: Encrypt/Decrypt are NOT inverse functions!!"\Exit

    Input "Name of object file to serialize: " Filename$
    Open #in,Filename$
    Input "Name of place to put serialized results: " Filename$
    Create #out,Filename$

    Rem now convert secret key to true format before using
    For i=1 to len(SDOSSecretKey$)
        SDOSSecretKey$(i)=SDOSSecretKey$(i) xor :ff \ ! form true SecretKey
    Next i

    Rem Now generate a Type 5 object record Header in Temp$:
    Rem :05, <count (of 1)>, <6 byte random number>
    Print "Enter application suite decryption key generator"
    Input "(Default is random): " Temp$
    If Temp$=""
    Then
        Rem Choose completely random number for random number...
        Rem thus ensuring that decryption key is (virtually) unique
        Let Temp$=Time$ \ ! Set Seed of random number generator to current time
        let temp=1\For i=1 to len(temp$) do temp=temp*Temp$[i]
        Let rnd=temp
        For i=3 to 8 do let Temp$(i)=int(256*rnd) \ ! generate 6 byte random #
    Else
        For i=len(Temp$) to 8 do let Temp$(i)=0 \ ! pad until we have 8 bytes
        Rem Set the 6 "Random" bytes to a fixed function of the password...
        Rem ensuring that the decryption keys of all modules "serialized"
        Rem with the same password for the same machine, are identical
        Rem This allows encrypted application modules to chain to 1 another...
        Rem while being protected from dummy modules! (see SDOS loader)
        Call Encrypt(SDOSSecretKey$,Temp$)
    Fi
    Let Len(Temp$)=8
    Temp$(1)=:05 \ ! Record type
    Temp$(2)=1 \ ! Number of serial numbers in the :05 type record

    Write #out,Temp$ \ ! Send Header of type 5 record to output file

EncryptType5Header:
    Rem Generate Encryption key by performing Cascaded DECRYPTION of...
    Rem All bytes in the type 5 record
    Rem For a type 5 record with count = 1, this is identical to:
    Rem EncryptKey=Decrypt(Decrypt(SDOSSecretKey,Type5header),Serialnumber)
    Let EncryptionKey$=Temp$
    Call Decrypt(SDOSSecretKey$,EncryptionKey$) \ ! Using SDOS's Secret Key
AskSerialNumber:
    Print "CPU Serial Number:>****************<";
    For i=1 to 17 do print chr$(8);
    Input '' Temp$
    If len(Temp$)<>16 then AskSerialNumber
    For i=0 to 7
        If Error When Temp$[i+1]=Val(":" Cat Temp$[i*2+1,2])
        Then AskSerialNumber
    Next i
    let len(Temp$)=8
    Write #out,Temp$ \ ! Send body of type 5 record to output file
    Rem Encrypt the CPU serial number according to Cascaded Key, above.
    Call Decrypt(EncryptionKey$,Temp$)
    Let EncryptionKey$=Temp$
    Print "Actual object code encryption key is: "
    Call DisplayKey(EncryptionKey$)

    print "encrypting..."
    EndofObjectfile=False
    Objectfilescanstate=1 \ ! Start record is next
    Len(buffer$)=8
    Until EndofObjectfile Do
        For i=1 to 8 do buffer$(i)=NextObjectByte
        call Encrypt(EncryptionKey$,buffer$)
        write #out,buffer$
    End
    print "Serialization Complete."
    exit


Subroutine DisplayKey(DisplayKey$)
    For i=1 to 8 do print hex$(DisplayKey$[i])[4,2];
    Print
    Return Subroutine
End

Subroutine Encrypt(Key$,Data$)
    Rem This Subroutine Encrypts Data$ according to...
    Rem Ira B's Nasty Little Encryption Algorithm,
    Rem As Defined by the following code:
    Rem (This code is designed to be easy and relatively fast in assembler)
    Rem DEBUG ONLY Print "ENCRYPT: Encryption Key";\call displaykey(key$)
    Rem DEBUG ONLY Call DisplayKey(Data$)
    Rem Step 1. Make Most Significant byte depend on all 64 bits.
    Rem This step lets us get away with only 8-12 iterations, instead of 64.
    Temp=0
    For Byte=1 to 8 do temp=temp XOR Data$[Byte]
    Let Data$[1]=Temp
    Rem DEBUG ONLY Print "Step 1: ";\Call DisplayKey(data$)
    Rem Step 2. Now encrypt using MSB as encryption control bits
    For IterationCount=1 to 8+(Key$[8]&:3)
        If Data$[1]&:80
        Then
            Rem Print "Encrypt Step";
            Let Byte=8
            Let Temp=((Data$[1] XOR Key$[1])&:80)/128 \ ! "Encrypt" control bit
            Repeat
                Let Temp=((Data$[Byte] XOR Key$[Byte])**1)+Temp
                Let Data$[Byte]=Temp&:FF
                Let Temp=(Temp&:100)/256 \! Carry into Next byte
                Byte=Byte-1
            Unless Byte=0 End
        Else
            Rem Print "Randomize Step";
            Let Byte=8
            Let Temp=((Data$[1] XOR Key$[1])&:80)/128 \ ! "Encrypt" control bit
            Repeat
                Let Temp=((Data$[Byte] XOR Randomizer$[Byte])**1)+Temp
                Let Data$[Byte]=Temp&:FF
                Let Temp=(Temp&:100)/256 \! Carry into Next byte
                Byte=Byte-1
            Unless Byte=0 End
        Fi
        Rem DEBUG ONLY Print IterationCount;\Call DisplayKey(Data$)
    Next IterationCount
    Rem Step 3. Make Least Significant byte depend on all 64 bits.
    Rem This step makes Encrypt and Decrypt equally good at encrypting.
    Temp=0
    For Byte=1 to 8 do temp=temp XOR Data$[Byte]
    Let Data$[8]=Temp
    Rem DEBUG ONLY Print "Encrypt Final: ";\Call DisplayKey(data$)
    Return Subroutine
End

Subroutine Decrypt(Key1$,Data1$)
    Rem This Subroutine Decrypts Data1$ according to Key1$
    Rem For Ira B's Nasty Little Encryption Algorithm,
    Rem As Defined by the following code:
    Rem (This code is designed to be easy and relatively fast in assembler)
    Rem Undo Encrypt Step 3 (makes Decrypt as good as Encrypt at encrypting)
    Rem DEBUG ONLY Print "DECRYPT: Decryption Key=";\Call displaykey(key1$)
    Rem DEBUG ONLY Call DisplayKey(Data1$)
    Temp=0
    For Byte=1 to 8 do temp=temp XOR Data1$[Byte]
    Let Data1$[8]=Temp
    Rem DEBUG ONLY Print "Step 3 Undone: ";\call displaykey(data1$)
    Rem Undo Encrypt Step 2.
    For IterationCount=1 to 8+(Key1$[8]&:3)
        Rem Decode the Encryption control bit...
        If ((Data1$[8]**7) XOR Key1$[1])&:80
        Then
            Rem Print "Undo Encrypt Step";
            Let Byte=1
            Let Temp=Data1$[8]&1 \ ! "Encrypt" control bit
            Repeat
                Let Temp=(Temp**8)+Data1$[Byte]
                Let Data1$[Byte]=((Temp&:1FE)/2) XOR Key1$[Byte]
                Let Temp=Temp&1 \! Carry into Next byte
                Byte=Byte+1
            Unless Byte=9 End
        Else
            Rem Print "Undo Randomize Step:";
            Let Byte=1
            Let Temp=0 \ ! "Encrypt" control bit (this works becuz MSB(Rnd)=0)
            Repeat
                Let Temp=(Temp**8)+Data1$[Byte]
                Let Data1$[Byte]=((Temp&:1FE)/2) XOR Randomizer$[Byte]
                Let Temp=Temp&1 \! Carry into Next byte
                Byte=Byte+1
            Unless Byte=9 End
        Fi
        Rem DEBUG ONLY Print IterationCount;\Call DisplayKey(Data1$)
    Next IterationCount
    Rem Now Re-generate Most significant byte by undoing Step 1.
    Temp=0
    For Byte=1 to 8 do temp=temp XOR Data1$[Byte]
    Let Data1$[1]=Temp
    Rem DEBUG ONLY Print "Decrypt done: ";\call displaykey(data1$)
    Return Subroutine
End

END
