Advertisement
Guest User

Untitled

a guest
Feb 24th, 2017
82
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 7.36 KB | None | 0 0
  1. import base64, strutils, sha256, os
  2. import libsodium.sodium
  3. import libsodium.sodium_sizes
  4. const
  5. usage = """
  6. DDCATool - Tool to establish and manage a DDNet CA
  7. usage:
  8. ddcatool <command>
  9.  
  10. Commands:
  11. genKey <keyOut> [seed]
  12. request <csrOut> <host> <canIssue> <keyIn>
  13. sign <certOut> <csrIn> <issuingHost> <privIn>
  14. inspect <fileIn>
  15. """
  16.  
  17. certVersion = 1
  18. certHeader = "#BEGIN DDCERT#"
  19. certFooter = "#END DDCERT#"
  20. certLen = 32 + 32 + 1 + 32 + 64
  21. keyHeader = "#BEGIN DDKEY#"
  22. keyFooter = "#END DDKEY#"
  23. keyLen = 64
  24. csrHeader = "#BEGIN DDCRL#"
  25. csrFooter = "#END DDCRL#"
  26. csrLen = 32 + 1 + 32 + 64
  27.  
  28. type
  29. Cert = tuple[hostHash: string, issuerHash: string, canIssue: bool, pubKey: string, signature: string]
  30. Pub = string
  31. Key = string
  32. KeyPair = tuple[public: Pub, private: Key]
  33. Csr = tuple[hostHash: string, canIssue: bool, pubKey: string, signature: string]
  34.  
  35. proc canIssueRaw(x: bool): char =
  36. result = if x: '\1'
  37. else: '\0'
  38.  
  39. proc generateCertContent(c: Cert): string =
  40. result = c[0] & c[1] & canIssueRaw(c[2]) & c[3] & c[4]
  41.  
  42. proc generateCsrContent(c: Csr): string =
  43. result = c[0] & canIssueRaw(c[1]) & c[2] & c[3]
  44.  
  45. template createFileImpl(lines: varargs[string]) =
  46. var file: File
  47. if not file.open(f, fmWrite):
  48. quit("Couldn't write file.", QuitFailure) #Couldnt be assed with exceptions
  49. for line in lines:
  50. file.writeLine(line)
  51. file.close
  52.  
  53. proc createCertFile(f:string, c: Cert) =
  54. createFileImpl(certHeader, $certVersion, encode(generateCertContent(c)), certFooter)
  55.  
  56. proc createKeyFile(f: string, c: Key) = #libsodium doesn't have nice symmetric enc :(
  57. createFileImpl(keyHeader, encode(c), keyFooter)
  58.  
  59. proc createCsrFile(f: string, c: Csr) =
  60. createFileImpl(csrHeader, encode(generateCsrContent(c)), csrFooter)
  61.  
  62. proc parseCertFile(f: string): seq[Cert] =
  63. result = @[]
  64.  
  65. let buf = readFile(f)
  66.  
  67. var cert = ""
  68. for line in buf.splitLines:
  69. if line.startsWith(certHeader):
  70. let version = parseInt(line.split('#')[2])
  71. assert(version == certVersion) #Invalid version cert in file
  72. elif line != certHeader and line != certFooter:
  73. cert.add(line)
  74. elif line == certFooter:
  75. let raw = decode(cert)
  76. assert(raw.len == certLen)
  77. let
  78. hostHash = raw[0..31] #32 bytes SHA256
  79. issuerHash = raw[32..63] #32 bytes SHA256
  80. canIssueRaw = raw[64] #1 byte bool
  81. pubKey = raw[65..96] #32 bytes ED25519 Public Key
  82. signature = raw[97..160] #64 bytes ED25519 Signature
  83. canIssue = if canIssueRaw == '\1': true
  84. else: false
  85.  
  86. result.add((hostHash, issuerHash, canIssue, pubKey, signature))
  87. cert = ""
  88.  
  89. proc parseCsrFile(f: string): Csr =
  90. let
  91. buf = readFile(f)
  92. count = buf.count(csrHeader)
  93.  
  94. assert(count == 1)
  95.  
  96. var csr = ""
  97. for line in buf.splitLines:
  98. if line != csrHeader and line != csrFooter:
  99. csr.add(line)
  100. elif line == csrFooter:
  101. let raw = decode(csr)
  102. assert(raw.len == csrLen)
  103. let
  104. hostHash = raw[0..31] #32 bytes SHA256
  105. canIssueRaw = raw[32] #1 byte bool
  106. pubKey = raw[33..64] #32 byte ED25519 Public Key
  107. signature = raw[65..128] #32 byte ED25519 Signature
  108. canIssue = if canIssueRaw == '\1': true
  109. else: false
  110. result = (hostHash, canIssue, pubKey, signature)
  111. return
  112.  
  113. proc parseKeyFile(f: string): Key =
  114. let
  115. buf = readFile(f)
  116. count = buf.count(keyHeader)
  117.  
  118. assert(count == 1)
  119.  
  120. var key = ""
  121. for line in buf.splitLines:
  122. if line != keyHeader and line != keyFooter:
  123. key.add(line)
  124. elif line == keyFooter:
  125. let raw = decode(key)
  126. assert(raw.len == keyLen)
  127.  
  128. result = raw
  129.  
  130. proc getPair(key: Key): KeyPair =
  131. echo key.len
  132. result.private = key
  133. result.public = crypto_sign_ed25519_sk_to_pk(key)
  134.  
  135. proc verifySig(pubKey, message, signature: string): bool =
  136. result = true
  137. try:
  138. crypto_sign_verify_detached(pubKey, message, signature)
  139. except SodiumError:
  140. result = false
  141.  
  142. proc sign(csr: Csr, issuerHash: string, key: Key): Cert =
  143. if not verifySig(csr.pubKey, csr[0] & canIssueRaw(csr[1]) & csr[2], csr.signature):
  144. quit("Forged CSR file!", QuitFailure)
  145.  
  146. result.hostHash = csr.hostHash
  147. result.issuerHash = issuerHash
  148. result.canIssue = csr.canIssue
  149. result.pubKey = csr.pubKey
  150. result.signature = crypto_sign_detached(
  151. secret_key = key,
  152. message = result[0] & result[1] & canIssueRaw(result[2]) & result[3])
  153.  
  154. proc request(hostHash: string, canIssue: bool, pair: KeyPair): Csr =
  155. result.hostHash = hostHash
  156. result.canIssue = canIssue
  157. result.pubKey = pair.public
  158. result.signature = crypto_sign_detached(
  159. secret_key = pair.private,
  160. message = result[0] & canIssueRaw(result[1]) & result[2])
  161.  
  162. proc genKey(keyOut: string, seed = "") =
  163. let key = if seed == "": crypto_sign_keypair()[1]
  164. else: crypto_sign_seed_keypair(SHA256(seed))[1]
  165.  
  166. createKeyFile(keyOut, key)
  167.  
  168. proc requestF(csrOut: string, host: string, canIssue: bool, keyIn: string) =
  169. let
  170. hostHash = SHA256(host)
  171. key = parseKeyFile(keyIn)
  172. pair = getPair(key)
  173. csr = request(hostHash, canIssue, pair)
  174.  
  175. createCsrFile(csrOut, csr)
  176.  
  177. proc signCsr(certOut: string, csrIn: string, issuingHost: string, privIn: string) =
  178. let
  179. csr = parseCsrFile(csrIn)
  180. key = parseKeyFile(privIn)
  181. issuerHash = SHA256(issuingHost)
  182. cert = sign(csr, issuerHash, key)
  183.  
  184. createCertFile(certOut, cert)
  185.  
  186. proc inspectCert(f: string) =
  187. let cert = parseCertFile(f)
  188. for i, c in cert:
  189. echo "Cert " & $i & ':'
  190. echo "\tHost hash : " & toHex(c.hostHash)
  191. echo "\tIssuer hash: " & toHex(c.issuerHash)
  192. echo "\tCan issue : " & $c.canIssue
  193. echo "\tPublic Key : " & toHex(c.pubKey)
  194. echo "\tSignature : " & toHex(c.signature)
  195.  
  196. proc inspectCsr(f: string) =
  197. let csr = parseCsrFile(f)
  198. echo "CSR:"
  199. echo "\tHost hash : " & toHex(csr.hostHash)
  200. echo "\tCan issue : " & $csr.canIssue
  201. echo "\tPublic Key: " & toHex(csr.pubKey)
  202. echo "\tSignature : " & toHex(csr.signature)
  203. echo "\tValidity : " & $verifySig(csr.pubKey, csr[0] & canIssueRaw(csr[1]) & csr[2], csr.signature)
  204.  
  205. proc inspect(file: string) =
  206. let buf = readFile(file)
  207.  
  208. for line in buf.splitLines:
  209. case line
  210. of certHeader:
  211. inspectCert(file)
  212. of csrHeader:
  213. inspectCsr(file)
  214. else: discard
  215.  
  216. if paramCount() == 0:
  217. echo "No command"
  218. quit(usage, QuitFailure)
  219.  
  220. let command = paramStr(1)
  221. case command
  222. of "genkey":
  223. if paramCount() > 3:
  224. echo "Too many arguments for genkey."
  225. quit(usage, QuitFailure)
  226.  
  227. if paramCount() == 2:
  228. genKey(paramStr(2))
  229. else:
  230. genKey(paramStr(2), paramStr(3))
  231. of "request":
  232. if paramCount() != 5:
  233. echo "Argument count invalid for request."
  234. quit(usage, QuitFailure)
  235.  
  236. var canIssue = false
  237. case paramStr(4)
  238. of "true", "1":
  239. canIssue = true
  240. of "false", "0":
  241. canIssue = false
  242. else:
  243. echo "CanIssue can be true/1 or false/0."
  244. quit(usage, QuitFailure)
  245.  
  246. requestF(paramStr(2), paramStr(3), canIssue, paramStr(5))
  247. of "sign":
  248. if paramCount() != 5:
  249. echo "Argument count invalid for sign."
  250. quit(usage, QuitFailure)
  251.  
  252. signCsr(paramStr(2), paramStr(3), paramStr(4), paramStr(5))
  253. of "inspect":
  254. if paramCount() != 2:
  255. echo "Inspect needs one argument."
  256. quit(usage, QuitFailure)
  257.  
  258. inspect(paramStr(2))
  259. else:
  260. echo "Invalid command " % command
  261. quit(usage, QuitFailure)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement