lf2-data-crypt.scm 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. (library (lf2-data-crypt)
  2. (export
  3. trash-bytes
  4. shift
  5. reverse-shift
  6. ;; crypt-u8
  7. decrypt-u8
  8. encrypt-u8
  9. decrypt
  10. encrypt
  11. secret->vector)
  12. (import
  13. (except (rnrs base) let-values)
  14. (only (guile)
  15. lambda* λ
  16. error
  17. eof-object?
  18. simple-format
  19. current-output-port
  20. current-input-port
  21. call-with-output-string
  22. call-with-input-string
  23. call-with-output-file
  24. remainder
  25. array-for-each
  26. array-ref)
  27. (prefix (logging) log:)
  28. (ice-9 binary-ports)
  29. ;; Bytevectors are created by procedures from (ice-9 binary-ports), but to
  30. ;; work with them, other imports are needed.
  31. (rnrs bytevectors)
  32. (ice-9 textual-ports)
  33. (port-utils)
  34. (bytevector-utils)))
  35. ;; At the beginning of the stage.dat file there are 123 bytes of data, which
  36. ;; have either unknown meaning, or are simply trash, possibly serving as a means
  37. ;; of obfuscation.
  38. (define trash-bytes
  39. #vu8(160 176 182 147 194 175 185 146 189 144 160 197 190 183 142 181 174 202 139 178 175 182 193 186 169 152 171 152 183 157 180 183 194 147 177 180 175 155 185 184 143 197 198 191 146 181 147 157 189 181 170 136 188 181 181 138 167 170 169 189 188 186 157 188 145 204 161 169 184 195 154 194 184 176 153 170 201 137 190 196 172 140 200 153 146 191 187 169 152 188 182 180 154 166 187 180 196 180 189 152 170 154 191 165 180 183 198 139 182 200 185 158 183 200 161 205 194 186 140 191 142 144 195))
  40. (define default-secret "odBearBecauseHeIsVeryGoodSiuHungIsAGo")
  41. (define secret->vector
  42. (λ (secret)
  43. "Transform the secret, which is a string into a vector, containing the
  44. unsigned 8 bit integer values of the secret key characters."
  45. ;; TODO: Actually this vector could contain other things than only unsinged
  46. ;; 8 bit integers. It could contain any integer. Perhaps a true bytevector
  47. ;; should be used?
  48. (list->vector
  49. (map char->integer
  50. (string->list secret)))))
  51. (define shift
  52. (lambda* (int amount #:key (rest-class 256))
  53. "Shift a single unsigned 8 bit integer by the given amount, modulo the
  54. specified rest-class."
  55. (remainder (+ int amount) rest-class)))
  56. (define reverse-shift
  57. (lambda* (int amount #:key (rest-class 256))
  58. "Reverse shift a single unsigned 8 bit integer by the given amount, modulo
  59. the specified rest-class."
  60. (let ([reverse-shifted (shift int (* amount -1) #:rest-class rest-class)])
  61. ;; The reverse shifted integer could be negative, so we add 128. If it was
  62. ;; already a positive number, then adding 128 would make it leave the
  63. ;; bounds specified by rest-class, so we need to calculate the remainder
  64. ;; again.
  65. (remainder (+ reverse-shifted rest-class) rest-class))))
  66. (define crypt-u8
  67. (lambda* (u8 index-in-input secret-vector #:key (shift-proc shift))
  68. "Decrypt a character based on its position, a payload offset, and secret."
  69. (let* ([key-length (bytevector-length secret-vector)]
  70. [pos-in-key
  71. ;; For each byte to decrypt use one character of the secret. Go back
  72. ;; to the first character of the secret again, when the key has no
  73. ;; more characters left. Alternative explanation: Use the secret as
  74. ;; a ring.
  75. (remainder index-in-input key-length)])
  76. ;; substract from the encrypted u8 or byte the byte of the corresponding
  77. ;; character of the secret.
  78. (shift-proc u8
  79. (array-ref secret-vector pos-in-key)
  80. #:rest-class (expt 2 8)))))
  81. (define decrypt-u8
  82. (λ (u8 index-in-input secret-vector)
  83. "Decrypt an unsigned 8 bit integer based on its position, a payload offset,
  84. and secret."
  85. (crypt-u8 u8
  86. index-in-input
  87. secret-vector
  88. #:shift-proc reverse-shift)))
  89. (define encrypt-u8
  90. (λ (u8 index-in-input secret-vector)
  91. "Encrypt an unsigned 8 bit integer based on its position, a payload offset,
  92. and secret."
  93. (crypt-u8 u8
  94. index-in-input
  95. secret-vector
  96. #:shift-proc shift)))
  97. (define decrypt
  98. (lambda* (#:key
  99. (port (current-input-port))
  100. (secret default-secret)
  101. ;; For some reason (obfuscation?) the first 123 bytes are trash.
  102. (num-ignored-bytes 123)
  103. (verbose #f))
  104. "Provided a file port, read byte after byte, decrypting
  105. them, until all bytes are decrypted and the file ends."
  106. (log:debug "decrypting file\n")
  107. (let ([secret-vector (string->bytevector secret)])
  108. ;; Write decrypted bytes to a string, avoiding using string-append for
  109. ;; each character.
  110. (call-with-output-string
  111. (λ (string-port)
  112. ;; For some reason (obfuscation of the encryption algorithm?) the
  113. ;; first n bytes of a data file are trash.
  114. (log:debug "skipping trash bytes at the start of the file")
  115. (log:debug "trash bytes:" (get-bytevector-n port num-ignored-bytes))
  116. (port-u8-for-each (λ (index u8)
  117. (let ([decrypted-char
  118. (integer->char
  119. (decrypt-u8 u8 index secret-vector))])
  120. (put-char string-port decrypted-char)))
  121. port))))))
  122. (define encrypt
  123. (lambda* (plain-text
  124. #:key
  125. (port (current-output-port))
  126. (secret default-secret)
  127. (trash-bytes trash-bytes)
  128. (verbose #f))
  129. "Provided a file port, encrypt bytewise and write bytes to the file port,
  130. until all bytes are written."
  131. (let ([secret-vector (string->bytevector secret)]
  132. [payload-offset (bytevector-length trash-bytes)])
  133. ;; Read string from port, to avoid having to use substring for each
  134. ;; character.
  135. (call-with-input-string plain-text
  136. (λ (string-port)
  137. ;; Write encrypted bytes to a file port, avoiding using string-append
  138. ;; for each character.
  139. ;; Write trash bytes first.
  140. (log:debug "putting trash bytes\n")
  141. (array-for-each (λ (u8) (put-u8 port u8)) trash-bytes)
  142. (log:debug "trash bytes put\n")
  143. ;; Write encrypted text to output port.
  144. (port-u8-for-each (λ (index u8)
  145. (put-u8 port
  146. (encrypt-u8 u8 index secret-vector)))
  147. string-port))))))