1/*	$OpenBSD: bn_print.c,v 1.47 2024/03/02 09:18:28 tb Exp $ */
2
3/*
4 * Copyright (c) 2023 Theo Buehler <tb@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19#include <ctype.h>
20#include <stdarg.h>
21#include <stdio.h>
22#include <stdint.h>
23#include <stdlib.h>
24
25#include <openssl/bio.h>
26#include <openssl/bn.h>
27
28#include "bio_local.h"
29#include "bn_local.h"
30#include "bytestring.h"
31
32static int
33bn_print_zero(BIO *bio, const BIGNUM *bn)
34{
35	if (!BN_is_zero(bn))
36		return 0;
37	if (BIO_printf(bio, " 0\n") <= 0)
38		return 0;
39	return 1;
40}
41
42static int
43bn_print_word(BIO *bio, const BIGNUM *bn)
44{
45	unsigned long long word;
46	const char *neg = "";
47
48	if (BN_is_zero(bn) || BN_num_bytes(bn) > BN_BYTES)
49		return 0;
50
51	if (BN_is_negative(bn))
52		neg = "-";
53
54	word = BN_get_word(bn);
55	if (BIO_printf(bio, " %s%llu (%s0x%llx)\n", neg, word, neg, word) <= 0)
56		return 0;
57
58	return 1;
59}
60
61static int
62bn_print_bignum(BIO *bio, const BIGNUM *bn, int indent)
63{
64	CBS cbs;
65	char *hex = NULL;
66	size_t hex_len = 0;
67	size_t octets = 0;
68	uint8_t hi, lo;
69	const char *sep = ":";
70	int ret = 0;
71
72	if (BN_num_bytes(bn) <= BN_BYTES)
73		goto err;
74
75	/* Secondary indent is 4 spaces, capped at 128. */
76	if (indent > 124)
77		indent = 124;
78	indent += 4;
79	if (indent < 0)
80		indent = 0;
81
82	if (!bn_bn2hex_nosign(bn, &hex, &hex_len))
83		goto err;
84
85	CBS_init(&cbs, hex, hex_len);
86
87	if (BN_is_negative(bn)) {
88		if (BIO_printf(bio, " (Negative)") <= 0)
89			goto err;
90	}
91
92	while (CBS_len(&cbs) > 0) {
93		if (!CBS_get_u8(&cbs, &hi))
94			goto err;
95		if (!CBS_get_u8(&cbs, &lo))
96			goto err;
97		if (octets++ % 15 == 0) {
98			if (BIO_printf(bio, "\n%*s", indent, "") <= 0)
99				goto err;
100		}
101		/* First nibble has the high bit set. Insert leading 0 octet. */
102		if (octets == 1 && hi >= '8') {
103			if (BIO_printf(bio, "00:") <= 0)
104				goto err;
105			octets++;
106		}
107		if (CBS_len(&cbs) == 0)
108			sep = "";
109		if (BIO_printf(bio, "%c%c%s", tolower(hi), tolower(lo), sep) <= 0)
110			goto err;
111	}
112
113	if (BIO_printf(bio, "\n") <= 0)
114		goto err;
115
116	ret = 1;
117
118 err:
119	freezero(hex, hex_len);
120
121	return ret;
122}
123
124int
125bn_printf(BIO *bio, const BIGNUM *bn, int indent, const char *fmt, ...)
126{
127	va_list ap;
128	int rv;
129
130	if (bn == NULL)
131		return 1;
132
133	if (!BIO_indent(bio, indent, 128))
134		return 0;
135
136	va_start(ap, fmt);
137	rv = BIO_vprintf(bio, fmt, ap);
138	va_end(ap);
139	if (rv < 0)
140		return 0;
141
142	if (BN_is_zero(bn))
143		return bn_print_zero(bio, bn);
144
145	if (BN_num_bytes(bn) <= BN_BYTES)
146		return bn_print_word(bio, bn);
147
148	return bn_print_bignum(bio, bn, indent);
149}
150
151int
152BN_print(BIO *bio, const BIGNUM *bn)
153{
154	char *hex = NULL;
155	size_t hex_len = 0;
156	int ret = 0;
157
158	if (!bn_bn2hex_nibbles(bn, &hex, &hex_len))
159		goto err;
160	if (BIO_printf(bio, "%s", hex) <= 0)
161		goto err;
162
163	ret = 1;
164
165 err:
166	freezero(hex, hex_len);
167
168	return ret;
169}
170LCRYPTO_ALIAS(BN_print);
171
172int
173BN_print_fp(FILE *fp, const BIGNUM *bn)
174{
175	char *hex = NULL;
176	size_t hex_len = 0;
177	int ret = 0;
178
179	if (!bn_bn2hex_nibbles(bn, &hex, &hex_len))
180		goto err;
181	if (fprintf(fp, "%s", hex) < 0)
182		goto err;
183
184	ret = 1;
185
186 err:
187	freezero(hex, hex_len);
188
189	return ret;
190}
191LCRYPTO_ALIAS(BN_print_fp);
192