# SPDX-License-Identifier: BSD-2-Clause
BEGIN {
if(!KEY_FILE) {
KEY_FILE = "/usr/local/etc/lacrum/key"
}
# ciphertext must be base64-encoded (as well as, you know,
# encrypted)
ENCRYPT_COMMAND = ("openssl enc -aes-256-ctr -pbkdf2 -a -kfile " KEY_FILE)
DECRYPT_COMMAND = ("openssl enc -d -aes-256-ctr -pbkdf2 -a -kfile " KEY_FILE)
S_REDACTED = "(redacted)"
S_ENCRYPTED = "(encrypted)"
}
# eval cases for encrypt, decrypt, unsafe-reveal are in string.awk.
# eval.awk also has a case for when an encrypted string is evaluated.
function _obscure_string_in_array(arr, n) {
gsub(/./, "x", arr[n])
}
function _decrypt_encrypted_string(n, env, d, hdlrs, st, errp, line, out, status, dcmd, tfn, oors) {
# attempts to write via pipe and read via pipe from the same
# subprocess have failed. so we use a temporary file. note that we
# put the ciphertext, not the plaintext, in the temporary file.
if( (status = ("mktemp" | getline tfn)) < 0) {
return _error(_cons(_string("could not mktemp"),_nil()))
}
close("mktemp")
if(!tfn)
return _error(_cons(_string("no filename resulted from mktemp"),
_nil()), env, d, hdlrs, st, errp)
# ok now we have a temp file name
print _ENCRYPTED_STRING[n] > tfn
close(tfn)
dcmd = DECRYPT_COMMAND " < " tfn
while( (status = (dcmd | getline line)) > 0 ) {
out = out line
}
close(dcmd)
if(status < 0) {
return _error(_cons(_string("decrypting an encrypted string failed"),
_nil()), env, d, hdlrs, st, errp)
}
_SECRET_STRING[n] = out
_TYPE[n] = "S"
_obscure_string_in_array(_ENCRYPTED_STRING, n)
delete _ENCRYPTED_STRING[n]
system("rm -f " tfn)
return n
}
function _encrypt_string_base(typename, arr, n, env, d, hdlrs, st, errp, line, out, status, ecmd, tfn, oors) {
# attempts to write via pipe and read via pipe from the same
# subprocess have failed. so we use a temporary file. note that we
# put the ciphertext, not the plaintext, in the temporary file.
if( (status = ("mktemp" | getline tfn)) < 0) {
return _error(_cons(_string("could not mktemp"),_nil()))
}
close("mktemp")
if(!tfn)
return _error(_cons(_string("no filename resulted from mktemp"),
_nil()), env, d, hdlrs, st, errp)
# ok now we have a temp file name
ecmd = ENCRYPT_COMMAND " > " tfn
oors = ORS
ORS = ""
print arr[n] | ecmd
ORS = oors
close(ecmd)
while( (status = (getline line < tfn)) > 0 ) {
out = out line
}
close(tfn)
if(!out) {
return _error(_cons(_string("encrypting a " typename " failed"),
_nil()))
}
_ENCRYPTED_STRING[n] = out
_TYPE[n] = "e"
_obscure_string_in_array(arr, n)
delete arr[n]
system("rm -f " tfn)
return n
}
function _encrypt_secret_string(n, env, d, hdlrs, st, errp) {
return _encrypt_string_base("secret string", _SECRET_STRING, n,
env, d, hdlrs, st, errp)
}
function _encrypt_string(n, env, d, hdlrs, st, errp) {
return _encrypt_string_base("string", _STRING, n,
env, d, hdlrs, st, errp)
}
function _encrypt(n, env, d, hdlrs, st, errp, t) {
if(_atom_awk(n)) {
if(!_is_literal(n)) {
t = _TYPE[n]
if(t == "S")
return _encrypt_secret_string(n)
else if(t == "s")
return _encrypt_string(n)
else if(t == "e")
return _error(_cons(_string("cannot encrypt an encrypted string"),
_nil()), env, d, hdlrs, st, errp)
}
}
return _error(_cons(_string("cannot encrypt %s: " \
"need a string or secret string"),
_cons(_string(_repr(n)),
_nil())),
env, d, hdlrs, st, errp)
}
function _decrypt(n, env, d, hdlrs, st, errp, t) {
if(_atom_awk(n)) {
if(!_is_literal(n)) {
t = _TYPE[n]
if(t == "e")
return _decrypt_encrypted_string(n, env, d, hdlrs, st, errp)
}
}
return _error(_cons(_string("cannot decrypt %s: " \
"need an encrypted string"),
_cons(_string(_repr(n)),
_nil())),
env, d, hdlrs, st, errp)
}
function _unsafe_reveal(n, env, d, hdlrs, st, errp, t) {
if(_atom_awk(n)) {
if(!_is_literal(n)) {
t = _TYPE[n]
if(t == "S") {
_STRING[n] = _SECRET_STRING[n]
_TYPE[n] = "s"
_obscure_string_in_array(_SECRET_STRING, n)
delete _SECRET_STRING[n]
return n
} else if(t == "s") {
return n
} else if(t == "e") {
return _unsafe_reveal(_decrypt(n), env, d, hdlrs, st, errp)
}
}
}
return _error(_cons(_string("cannot reveal %s: " \
"need a secret string"),
_cons(_string(_repr(n)),
_nil())),
env, d, hdlrs, st, errp)
}
function _reveal_encrypted(n, env, d, hdlrs, st, errp, t) {
if(_atom_awk(n)) {
if(!_is_literal(n)) {
t = _TYPE[n]
if(t == "s") {
n = _encrypt_string(n, env, d, hdlrs, st, errp)
if(_is_null(n)) return n # an error happened
t = _TYPE[n]
} else if(t == "S") {
n = _encrypt_secret_string(n, env, d, hdlrs, st, errp)
if(_is_null(n)) return n # an error happened
t = _TYPE[n]
}
# not else if. after the above, or because it was already,
# t should now be "e".
if(t == "e") {
return _string(_ENCRYPTED_STRING[n])
}
}
}
# if we have not returned, n was or is a weird type
return _error(_cons(_string("cannot reveal-encrypted %s: must be a string or secret string"),
_cons(n, _nil())),
env, d, hdlrs, st, errp)
}