diff --git a/demo/fw/include/platform/endian.h b/demo/fw/include/platform/endian.h new file mode 100644 index 0000000..fa6379d --- /dev/null +++ b/demo/fw/include/platform/endian.h @@ -0,0 +1,189 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2002 Thomas Moestl + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ +/* + * Portions copyright (c) 2018, ARM Limited and Contributors. + * All rights reserved. + */ + +#ifndef ENDIAN_H +#define ENDIAN_H + +#include + +/* + * General byte order swapping functions. + */ +#define bswap16(x) __bswap16(x) +#define bswap32(x) __bswap32(x) +#define bswap64(x) __bswap64(x) + +/* + * Host to big endian, host to little endian, big endian to host, and little + * endian to host byte order functions as detailed in byteorder(9). + */ +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +#define htobe16(x) bswap16((x)) +#define htobe32(x) bswap32((x)) +#define htobe64(x) bswap64((x)) +#define htole16(x) ((uint16_t)(x)) +#define htole32(x) ((uint32_t)(x)) +#define htole64(x) ((uint64_t)(x)) + +#define be16toh(x) bswap16((x)) +#define be32toh(x) bswap32((x)) +#define be64toh(x) bswap64((x)) +#define le16toh(x) ((uint16_t)(x)) +#define le32toh(x) ((uint32_t)(x)) +#define le64toh(x) ((uint64_t)(x)) +#else /* __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__ */ +#define htobe16(x) ((uint16_t)(x)) +#define htobe32(x) ((uint32_t)(x)) +#define htobe64(x) ((uint64_t)(x)) +#define htole16(x) bswap16((x)) +#define htole32(x) bswap32((x)) +#define htole64(x) bswap64((x)) + +#define be16toh(x) ((uint16_t)(x)) +#define be32toh(x) ((uint32_t)(x)) +#define be64toh(x) ((uint64_t)(x)) +#define le16toh(x) bswap16((x)) +#define le32toh(x) bswap32((x)) +#define le64toh(x) bswap64((x)) +#endif /* __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ */ + +/* Alignment-agnostic encode/decode bytestream to/from little/big endian. */ + +static __inline uint16_t +be16dec(const void *pp) +{ + uint8_t const *p = (uint8_t const *)pp; + + return ((p[0] << 8) | p[1]); +} + +static __inline uint32_t +be32dec(const void *pp) +{ + uint8_t const *p = (uint8_t const *)pp; + + return (((unsigned)p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]); +} + +static __inline uint64_t +be64dec(const void *pp) +{ + uint8_t const *p = (uint8_t const *)pp; + + return (((uint64_t)be32dec(p) << 32) | be32dec(p + 4)); +} + +static __inline uint16_t +le16dec(const void *pp) +{ + uint8_t const *p = (uint8_t const *)pp; + + return ((p[1] << 8) | p[0]); +} + +static __inline uint32_t +le32dec(const void *pp) +{ + uint8_t const *p = (uint8_t const *)pp; + + return (((unsigned)p[3] << 24) | (p[2] << 16) | (p[1] << 8) | p[0]); +} + +static __inline uint64_t +le64dec(const void *pp) +{ + uint8_t const *p = (uint8_t const *)pp; + + return (((uint64_t)le32dec(p + 4) << 32) | le32dec(p)); +} + +static __inline void +be16enc(void *pp, uint16_t u) +{ + uint8_t *p = (uint8_t *)pp; + + p[0] = (u >> 8) & 0xff; + p[1] = u & 0xff; +} + +static __inline void +be32enc(void *pp, uint32_t u) +{ + uint8_t *p = (uint8_t *)pp; + + p[0] = (u >> 24) & 0xff; + p[1] = (u >> 16) & 0xff; + p[2] = (u >> 8) & 0xff; + p[3] = u & 0xff; +} + +static __inline void +be64enc(void *pp, uint64_t u) +{ + uint8_t *p = (uint8_t *)pp; + + be32enc(p, (uint32_t)(u >> 32)); + be32enc(p + 4, (uint32_t)(u & 0xffffffffU)); +} + +static __inline void +le16enc(void *pp, uint16_t u) +{ + uint8_t *p = (uint8_t *)pp; + + p[0] = u & 0xff; + p[1] = (u >> 8) & 0xff; +} + +static __inline void +le32enc(void *pp, uint32_t u) +{ + uint8_t *p = (uint8_t *)pp; + + p[0] = u & 0xff; + p[1] = (u >> 8) & 0xff; + p[2] = (u >> 16) & 0xff; + p[3] = (u >> 24) & 0xff; +} + +static __inline void +le64enc(void *pp, uint64_t u) +{ + uint8_t *p = (uint8_t *)pp; + + le32enc(p, (uint32_t)(u & 0xffffffffU)); + le32enc(p + 4, (uint32_t)(u >> 32)); +} + +#endif /* ENDIAN_H */ diff --git a/demo/fw/src/cage.c b/demo/fw/src/cage.c index f69f12a..b60e157 100644 --- a/demo/fw/src/cage.c +++ b/demo/fw/src/cage.c @@ -25,7 +25,7 @@ do { \ int mbedtls_rc = fun_call; \ if (mbedtls_rc) { \ - LOG_PRINTF("Error: " #fun_call ": " msg ": rc=-0x%x\n", __VA_ARGS__ -mbedtls_rc); \ + LOG_PRINTF("Error: " #fun_call ": " msg ": rc=-0x%x\r\n", __VA_ARGS__ -mbedtls_rc); \ err = err_value; \ goto cleanup; \ } \ @@ -112,11 +112,11 @@ enum ca_error parse_age_buf(struct ca_keystore *ks, const char *buf, size_t bufl enum ca_error err = CA_ERR_CORRUPTED_STATE; if (buflen <= 0) { - LOG_PRINTF("Error: parse_age_buf: (buflen == %d) <= 0\n", buflen); + LOG_PRINTF("Error: parse_age_buf: (buflen == %d) <= 0\r\n", buflen); return CA_ERR_INVALID_HEADER; } if (strnlen(buf, buflen) >= buflen) { - LOG_PRINTF("Error: parse_age_buf: Buffer is not null-terminated\n"); + LOG_PRINTF("Error: parse_age_buf: Buffer is not null-terminated\r\n"); return CA_ERR_INVALID_HEADER; /* buffer is not null-terminated */ } @@ -124,13 +124,13 @@ enum ca_error parse_age_buf(struct ca_keystore *ks, const char *buf, size_t bufl const char *line_end = strchr(buf, '\n'); if (!line_end) { - LOG_PRINTF("Error: parse_age_buf: newline in header not found\n"); + LOG_PRINTF("Error: parse_age_buf: newline in header not found\r\n"); return CA_ERR_INVALID_HEADER; /* header has to contain at least magic string, one stanza and payload separator line */ } const char *header_start = "age-encryption.org/v"; if (strncmp(current_line, header_start, strlen(header_start))) { - LOG_PRINTF("Error: parse_age_buf: header magic string corrupt or missing\n"); + LOG_PRINTF("Error: parse_age_buf: header magic string corrupt or missing\r\n"); return CA_ERR_INVALID_HEADER; /* header magic string corrupt or missing */ } @@ -138,12 +138,12 @@ enum ca_error parse_age_buf(struct ca_keystore *ks, const char *buf, size_t bufl unsigned long header_version = strtoul(current_line + strlen(header_start), &endptr, 10); if (current_line[strlen(header_start)] == '\0' || header_version == ULONG_MAX || *endptr != '\n') { - LOG_PRINTF("Error: parse_age_buf: corrupt version number\n"); + LOG_PRINTF("Error: parse_age_buf: corrupt version number\r\n"); return CA_ERR_INVALID_HEADER; /* corrupt version number */ } if (header_version > 1) { - LOG_PRINTF("Error: parse_age_buf: file version too new\n"); + LOG_PRINTF("Error: parse_age_buf: file version too new\r\n"); return CA_ERR_FILE_FORMAT_TOO_NEW; } @@ -155,7 +155,7 @@ enum ca_error parse_age_buf(struct ca_keystore *ks, const char *buf, size_t bufl line_end = strchr(current_line, '\n'); if (!line_end) { - LOG_PRINTF("Error: parse_age_buf: header does not end in newline\n"); + LOG_PRINTF("Error: parse_age_buf: header does not end in newline\r\n"); return CA_ERR_INVALID_HEADER; /* the header must always end with '\n' after the --- separator line */ } @@ -205,7 +205,7 @@ enum ca_error parse_age_buf(struct ca_keystore *ks, const char *buf, size_t bufl */ } } else { - LOG_PRINTF("Error: parse_age_buf: header does not contain any stanzas\n"); + LOG_PRINTF("Error: parse_age_buf: header does not contain any stanzas\r\n"); return CA_ERR_INVALID_HEADER; /* the header must contain at least one stanza */ } @@ -214,7 +214,7 @@ enum ca_error parse_age_buf(struct ca_keystore *ks, const char *buf, size_t bufl } else { /* b64 stanza content */ if (line_end - current_line > 64) { - LOG_PRINTF("Error: parse_age_buf: base64 stanza body wider than 64 chars\n"); + LOG_PRINTF("Error: parse_age_buf: base64 stanza body wider than 64 chars\r\n"); return CA_ERR_INVALID_HEADER; /* b64 must be wrapped to exactly 64 chars */ } } @@ -236,7 +236,7 @@ enum ca_error parse_stanza(struct ca_keystore *ks, const char *stanza_head, size char *line_end = strchr(stanza_head, '\n'); if (!line_end) { - LOG_PRINTF("Error: parse_stanza: end of line not found (bug, should not happen!)\n"); + LOG_PRINTF("Error: parse_stanza: end of line not found (bug, should not happen!)\r\n"); return CA_ERR_CORRUPTED_STATE; } @@ -244,7 +244,7 @@ enum ca_error parse_stanza(struct ca_keystore *ks, const char *stanza_head, size const char **stanza_args = calloc(head_len, sizeof(char *)); if (!stanza_args) { - LOG_PRINTF("Error: parse_stanza: cannot allocate stanza arg memory\n"); + LOG_PRINTF("Error: parse_stanza: cannot allocate stanza arg memory\r\n"); return CA_ERR_OUT_OF_MEMORY; } @@ -252,7 +252,7 @@ enum ca_error parse_stanza(struct ca_keystore *ks, const char *stanza_head, size unsigned char *payload = malloc(max_payload_len); if (!payload) { free(stanza_args); - LOG_PRINTF("Error: parse_stanza: cannot allocate stanza body memory\n"); + LOG_PRINTF("Error: parse_stanza: cannot allocate stanza body memory\r\n"); return CA_ERR_OUT_OF_MEMORY; } @@ -270,7 +270,7 @@ enum ca_error parse_stanza(struct ca_keystore *ks, const char *stanza_head, size if (head_len + 1 < len) { /* has payload */ const char *endp = base64_decode(payload, max_payload_len, &b64_len, line_end + 1, len - head_len - 1); if (!endp || endp != stanza_head + len - 1) { - LOG_PRINTF("Error: parse_stanza: cannot parse stanza body base64\n"); + LOG_PRINTF("Error: parse_stanza: cannot parse stanza body base64\r\n"); err = CA_ERR_BROKEN_BASE64; goto errout; } @@ -291,7 +291,7 @@ enum ca_error parse_stanza_x25519(struct ca_keystore *ks, size_t nargs, const ch /* body length: 16 bytes ciphertext of 128 bit file key + 16 bytes Poly1305 tag */ if (nargs != 1 || body_len != 32) { - LOG_PRINTF("Error: parse_stanza_x25519: (nargs == %zu) != 1 || (body_len == %zu) != 32\n", nargs, body_len); + LOG_PRINTF("Error: parse_stanza_x25519: (nargs == %zu) != 1 || (body_len == %zu) != 32\r\n", nargs, body_len); return CA_ERR_INVALID_HEADER_ARG; } @@ -299,18 +299,18 @@ enum ca_error parse_stanza_x25519(struct ca_keystore *ks, size_t nargs, const ch unsigned char ecp_ciphertext[32]; const char *arg_newline = strchr(args[0], '\n'); if (!arg_newline) { - LOG_PRINTF("Error: parse_stanza_x25519: First argument does not end in '\\n' (this is a bug!)\n"); + LOG_PRINTF("Error: parse_stanza_x25519: First argument does not end in '\\n' (this is a bug!)\r\n"); return CA_ERR_CORRUPTED_STATE; } size_t arglen = arg_newline - args[0]; const char *endp = base64_decode(ecp_ciphertext, sizeof(ecp_ciphertext), &ciphertext_len, args[0], arglen); if (!endp || endp != arg_newline) { - LOG_PRINTF("Error: parse_stanza_x25519: Cannot decode ciphertext header arg base64 %p %p\n", endp, arg_newline); + LOG_PRINTF("Error: parse_stanza_x25519: Cannot decode ciphertext header arg base64 %p %p\r\n", endp, arg_newline); return CA_ERR_INVALID_HEADER_ARG; } if (ciphertext_len != 32) { - LOG_PRINTF("Error: parse_stanza_x25519: Ciphertext header arg has wrong length\n"); + LOG_PRINTF("Error: parse_stanza_x25519: Ciphertext header arg has wrong length\r\n"); return CA_ERR_INVALID_HEADER_ARG; } @@ -358,14 +358,14 @@ enum ca_error parse_stanza_x25519(struct ca_keystore *ks, size_t nargs, const ch */ if (ecp_plaintext_len != 32) { - LOG_PRINTF("Error: parse_stanza_x25519: (ecp_plaintext_len == %d) != 32 (bug, should not happen!)\n", ecp_plaintext_len); + LOG_PRINTF("Error: parse_stanza_x25519: (ecp_plaintext_len == %d) != 32 (bug, should not happen!)\r\n", ecp_plaintext_len); err = CA_ERR_CORRUPTED_STATE; goto cleanup; } const mbedtls_md_info_t *sha256_md = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256); if (!sha256_md) { - LOG_PRINTF("Error: mbedtls_md_info_from_type: cannot load SHA256\n"); + LOG_PRINTF("Error: mbedtls_md_info_from_type: cannot load SHA256\r\n"); err = CA_ERR_MBEDTLS_ERROR; goto cleanup; } @@ -376,7 +376,7 @@ enum ca_error parse_stanza_x25519(struct ca_keystore *ks, size_t nargs, const ch MBEDTLS_CHECK( mbedtls_ecp_point_write_binary(&x25519_grp, &(ks->x25519_kp.Q), MBEDTLS_ECP_PF_UNCOMPRESSED, &pbin_olen, salt+32, 32) ); if (pbin_olen != 32) { - LOG_PRINTF("Error: mbedtls_ecp_point_write_binary returned invalid output len: %d != 32\n", pbin_olen); + LOG_PRINTF("Error: mbedtls_ecp_point_write_binary returned invalid output len: %d != 32\r\n", pbin_olen); err = CA_ERR_CORRUPTED_STATE; goto cleanup; } @@ -425,7 +425,7 @@ enum ca_error check_file_key(const unsigned char *buf, size_t buflen, const unsi const mbedtls_md_info_t *sha256_md = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256); if (!sha256_md) { - LOG_PRINTF("Error: mbedtls_md_info_from_type: cannot load SHA256\n"); + LOG_PRINTF("Error: mbedtls_md_info_from_type: cannot load SHA256\r\n"); return CA_ERR_MBEDTLS_ERROR; } @@ -452,12 +452,12 @@ enum ca_error check_file_key(const unsigned char *buf, size_t buflen, const unsi size_t mac_osize = 0; const char *endp = base64_decode(mac, sizeof(mac), &mac_osize, (const char *)found + 1 /* for " " */, buflen - ((const unsigned char *)found - buf)); if (!endp || *endp != '\n') { - LOG_PRINTF("Error: check_file_key: Cannot decode ciphertext header arg base64\n"); + LOG_PRINTF("Error: check_file_key: Cannot decode ciphertext header arg base64\r\n"); return CA_ERR_INVALID_HEADER_ARG; } if (mac_osize != 32) { - LOG_PRINTF("Error: check_file_key: (mac_osize == %d) != 32\n", mac_osize); + LOG_PRINTF("Error: check_file_key: (mac_osize == %d) != 32\r\n", mac_osize); return CA_ERR_INVALID_HEADER; } @@ -500,7 +500,7 @@ enum ca_error stream_decrypt(unsigned char *out, size_t outlen, size_t *out_writ size_t aead_tagsize = 16; if (outlen < stream_bytes - aead_tagsize * stream_blocks) { - LOG_PRINTF("Error: stream_decrypt: caller-provided buffer too small for decryption result: %zu < %zu\n", outlen, stream_bytes - aead_tagsize * stream_blocks); + LOG_PRINTF("Error: stream_decrypt: caller-provided buffer too small for decryption result: %zu < %zu\r\n", outlen, stream_bytes - aead_tagsize * stream_blocks); return CA_ERR_NOT_ENOUGH_SPACE; } @@ -517,7 +517,7 @@ enum ca_error stream_decrypt(unsigned char *out, size_t outlen, size_t *out_writ const mbedtls_md_info_t *sha256_md = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256); if (!sha256_md) { - LOG_PRINTF("Error: mbedtls_md_info_from_type: cannot load SHA256 MD info\n"); + LOG_PRINTF("Error: mbedtls_md_info_from_type: cannot load SHA256 MD info\r\n"); return CA_ERR_MBEDTLS_ERROR; } diff --git a/demo/fw/src/main.c b/demo/fw/src/main.c index 0fc27af..d70c843 100644 --- a/demo/fw/src/main.c +++ b/demo/fw/src/main.c @@ -402,8 +402,18 @@ int main(void) dec_buf[decrypted_size-1] = '\0'; /* overwrite trailing \n */ con_printf_blocking("decrypted payload: %s\r\n", dec_buf); - spif_printf(&spif, "\033[H\033[0m\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\033[H"); - spif_printf(&spif, "\033[1;92mDecrypted data:\033[0m\n\n%s\n", dec_buf); + spif_printf(&spif, "\033[H\033[1;40;32m[tachibana/age decrypted message]\n\033[0;107;30m \n", dec_buf); + char *p = dec_buf; + char *endl = __strchrnul(dec_buf, '\n'); + bool done = false; + while (!done) { + done = *endl == '\0'; + *endl = '\0'; + spif_printf(&spif, " %s\n", p); + p = endl + 1; + endl = __strchrnul(p, '\n'); + } + spif_printf(&spif, " \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n", dec_buf); //for (int j=0; j<1000000; j++) { /* pass */ diff --git a/demo/fw/tools/create_age_email.py b/demo/fw/tools/create_age_email.py index 4a5077b..99d75e1 100644 --- a/demo/fw/tools/create_age_email.py +++ b/demo/fw/tools/create_age_email.py @@ -6,9 +6,11 @@ import math import subprocess from email.message import EmailMessage from email.utils import make_msgid +import smtplib import textwrap import io import binascii +import base64 import click import bdfparser @@ -27,31 +29,42 @@ def age_encrypt(content, keys): input=content.encode('utf-8')) return proc.stdout -BORDER_COLOR = '#000000' -BACKGROUND_COLOR = '#d61ca1' -FONT_FG_COLOR = '#e1e31b' -def pack_html(payload_img_cid, hint_img_cid): +def data_url(data): + return f'data:image/png;base64,{base64.b64encode(data).decode("utf-8")}' +BORDER_COLOR = '#000000' +BACKGROUND_COLOR = '#d020a0' +FONT_FG_COLOR = '#e0e010' + +def pack_html(payload_img_cid, hint_img_data): return textwrap.dedent(f'''\ - + -
- ''').strip() -def create_hint_img(text, max_w=200, fgcolor=FONT_FG_COLOR, bgcolor=BACKGROUND_COLOR): +def create_hint_img(text, max_w=200, fgcolor=FONT_FG_COLOR, bgcolor=BACKGROUND_COLOR, border=10): relative_path = (Path(__file__).parent / '../../../upstream/terminus-font-4.49.1/ter-u16b.bdf').resolve() font_file = os.getenv('HINT_FONT', str(relative_path)) font = bdfparser.Font(font_file) hex_to_np = lambda s: np.frombuffer(binascii.unhexlify(s.lstrip('#')), dtype=np.uint8) colors = np.array([hex_to_np(bgcolor), hex_to_np(fgcolor)]) - img = font.draw(text, linelimit=max_w) - return Image.fromarray(colors[img.todata(2)]) + imgs = [] + for line in text.splitlines(): + img = colors[np.array(font.draw(line, linelimit=max_w).todata(2))] + bg = np.tile(colors[np.newaxis, np.newaxis, 0], (img.shape[0], max_w, 1)) + w = img.shape[1] + bg[:, (max_w-w)//2 : (max_w-w)//2 + w, :] = img + imgs.append(bg) + img = np.vstack(imgs) + bg = np.tile(colors[np.newaxis, np.newaxis, 0], (img.shape[0]+2*border, img.shape[1]+2*border, 1)) + bg[border:-border, border:-border, :] = img + return Image.fromarray(bg) # because inexplicably, pillow doesn't have this already def img_to_bytes(img, fmt='png'): @@ -62,13 +75,17 @@ def img_to_bytes(img, fmt='png'): @click.command() @click.option('-f', '--from', 'sender') -@click.option('-t', '--to', 'recipients') -@click.option('-c', '--cc', 'cc') -@click.option('-b', '--bcc', 'bcc') +@click.option('-t', '--to', 'recipients', multiple=True) +@click.option('-c', '--cc', 'cc', multiple=True) +@click.option('-b', '--bcc', 'bcc', multiple=True) +@click.option('--smtp-server') +@click.option('--smtp-user') +@click.option('--smtp-password') +@click.option('--smtp-port', type=int, default=465) @click.option('-k', '--key', 'keys', multiple=True, required=True) @click.option('-s', '--subject', default='tachibana/age encrypted message') @click.argument('content', type=click.File('r')) -def create_age_email(content, sender, recipients, cc, bcc, keys, subject): +def create_age_email(content, sender, recipients, cc, bcc, keys, subject, smtp_server, smtp_port, smtp_user, smtp_password): encrypted = age_encrypt(content.read(), keys) @@ -83,21 +100,32 @@ def create_age_email(content, sender, recipients, cc, bcc, keys, subject): if bcc: msg['Bcc'] = ', '.join(bcc) - payload_img_cid, hint_img_cid = make_msgid(), make_msgid() + hint_img_data = img_to_bytes(create_hint_img('tachibana/age\nencrypted email', max_w=200)) + + payload_img_cid = make_msgid() # remove <...> angle brackets from cids in tags - msg.set_content(pack_html(payload_img_cid[1:-1], hint_img_cid[1:-1]), subtype='html') + msg.set_content(pack_html(payload_img_cid[1:-1], hint_img_data), subtype='html') w = max(200, math.ceil(math.sqrt(len(encrypted)))) h = math.ceil((len(encrypted) + 64) / w) img_bytes = img_to_bytes(tb_data_encoder.data_encode(encrypted, w, h)) msg.add_related(img_bytes, 'image', 'png', cid=payload_img_cid) - img_bytes = img_to_bytes(create_hint_img('tachibana/age\nencrypted email', max_w=200)) - msg.add_related(img_bytes, 'image', 'png', cid=hint_img_cid) - - sys.stdout.buffer.write(bytes(msg)) + if not smtp_server: + sys.stdout.buffer.write(bytes(msg)) + else: + smtp = smtplib.SMTP_SSL(smtp_server, smtp_port) + if smtp_user: + if smtp_password is None: + smtp_password = click.prompt('Please input SMTP password', hide_input=True) + smtp.login(smtp_user, smtp_password) + smtp.send_message(msg) + smtp.quit() + print('Message sent!') if __name__ == '__main__': #print(pack_html('foobar')) + #with open('/tmp/test.png', 'wb') as f: + # f.write(img_to_bytes(create_hint_img('tachibana/age\nencrypted email', max_w=200))) create_age_email()