// SPDX-License-Identifier: GPL-3.0-only // Copyright (c) 2022, Sylvain Huet, Ambermind // Minimacy (r) System use core.util.base64;; use core.crypto.des;; use core.crypto.aes;; use core.crypto.block;; use core.crypto.hash;; fun _filterPem(l, headers)= if l<>nil then let head(l)->str in let strPos(str, ":", 0) -> i in if i<>nil then _filterPem(tail(l), [strTrim(strLeft(str, i)), strTrim(strTail(str, i+1))]::headers) else [listReverse(headers), strBuild(l)];; fun _pemExtractEnd(l)= if l<>nil then let head(l) -> str in if '-'<>strGet(str, 0) then str::_pemExtractEnd(tail(l));; fun _pemExtract(l)= if l<>nil then let head(l) -> str in let strPos(str, "-----BEGIN ", 0) -> i in if i==nil then _pemExtract(tail(l)) else let strReplace(strTail(str, 11), "-----", "") -> name in let _pemExtractEnd(tail(l)) -> lines in let _filterPem(lines, nil) -> [headers, b64] in [name, headers, b64Decode(b64)]::_pemExtract(tail(l));; fun pemRead(str)= let if strContains(str, "\r\n") then strReplace(str, "\r", "") else if strContains(str, "\r") then strReplace(str, "\r", "\n") else str -> str in // now str has only unix end of line let strSplit("\n", str) -> lines in _pemExtract(lines);; fun _headerValue(headers, field)= if headers<>nil then let head(headers) ->[f, val] in if field==f then val else _headerValue(tail(headers), field);; fun _decodeBin(pwd, headers, bin)= if headers==nil then bin else if "4,ENCRYPTED"==_headerValue(headers, "Proc-Type") then let _headerValue(headers, "DEK-Info") -> codec in let strSplit(",", codec) -> prefix::iv::_ in match prefix with "DES-EDE3-CBC" -> let strFromHex(strTrim(iv)) -> iv in let bytesToKey(pwd, iv, 24) -> keys in // use iv as salt let des3DecryptCbc(keys, iv, bin) -> data in strLeft(data, unPaddingPKCS_5(data)), "AES-256-CBC" -> let strFromHex(strTrim(iv)) -> iv in let bytesToKey(pwd, strLeft(iv, 8), 32) -> key in // this one was tough! (use iv first 8 bytes as salt) let aesDecryptCbc(key, iv, bin) -> data in strLeft(data, unPaddingPKCS_5(data));; fun pemDecode(pem, pwd)= if pem<>nil then let pemRead(pem) -> blocks in listMap(blocks, lambda(b)= let b -> [name, headers, der] in [name, _decodeBin(pwd, headers, der)] );; fun _cut64(src, i)= if i iv in // let bytesToKey pwd (strLeft iv 8) keyLen -> keys in // use iv as salt // let strConcat der (paddingPKCS_5 der blockLen) -> bin in // let call fCipher keys iv bin -> data in // strJoin "\n" // (strFormat "-----BEGIN *-----" name ): // "Proc-Type: 4,ENCRYPTED": // (strFormat "DEK-Info: *,*" cipherName strUppercase hexFromStr iv): // "": // listConcat (_cut64 (b64Encode data) 0) // (strFormat "-----END *-----" name ): // nil;; //fun pemMakeWithDes3 name der pwd= _pemMakeEncrypted name der pwd 8 24 8 "DES-EDE3-CBC" #des3EncryptCbc;; //fun pemMakeWithAes256 name der pwd= _pemMakeEncrypted name der pwd 16 32 16 "AES-256-CBC" #aesEncryptCbc;;