summaryrefslogtreecommitdiffstats
path: root/demos/mac/poly1305.c
blob: 15c9c0097d97f1596ea0323a4b347c7633d8fa06 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
/*
 * Copyright 2021-2023 The OpenSSL Project Authors. All Rights Reserved.
 *
 * Licensed under the Apache License 2.0 (the "License").  You may not use
 * this file except in compliance with the License.  You can obtain a copy
 * in the file LICENSE in the source distribution or at
 * https://www.openssl.org/source/license.html
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/core_names.h>
#include <openssl/evp.h>
#include <openssl/params.h>
#include <openssl/err.h>

/*
 * This is a demonstration of how to compute Poly1305-AES using the OpenSSL
 * Poly1305 and AES providers and the EVP API.
 *
 * Please note that:
 *
 *   - Poly1305 must never be used alone and must be used in conjunction with
 *     another primitive which processes the input nonce to be secure;
 *
 *   - you must never pass a nonce to the Poly1305 primitive directly;
 *
 *   - Poly1305 exhibits catastrophic failure (that is, can be broken) if a
 *     nonce is ever reused for a given key.
 *
 * If you are looking for a general purpose MAC, you should consider using a
 * different MAC and looking at one of the other examples, unless you have a
 * good familiarity with the details and caveats of Poly1305.
 *
 * This example uses AES, as described in the original paper, "The Poly1305-AES
 * message authentication code":
 *   https://cr.yp.to/mac/poly1305-20050329.pdf
 *
 * The test vectors below are from that paper.
 */

/*
 * Hard coding the key into an application is very bad.
 * It is done here solely for educational purposes.
 * These are the "r" and "k" inputs to Poly1305-AES.
 */
static const unsigned char test_r[] = {
    0x85, 0x1f, 0xc4, 0x0c, 0x34, 0x67, 0xac, 0x0b,
    0xe0, 0x5c, 0xc2, 0x04, 0x04, 0xf3, 0xf7, 0x00
};

static const unsigned char test_k[] = {
    0xec, 0x07, 0x4c, 0x83, 0x55, 0x80, 0x74, 0x17,
    0x01, 0x42, 0x5b, 0x62, 0x32, 0x35, 0xad, 0xd6
};

/*
 * Hard coding a nonce must not be done under any circumstances and is done here
 * purely for demonstration purposes. Please note that Poly1305 exhibits
 * catastrophic failure (that is, can be broken) if a nonce is ever reused for a
 * given key.
 */
static const unsigned char test_n[] = {
    0xfb, 0x44, 0x73, 0x50, 0xc4, 0xe8, 0x68, 0xc5,
    0x2a, 0xc3, 0x27, 0x5c, 0xf9, 0xd4, 0x32, 0x7e
};

/* Input message. */
static const unsigned char test_m[] = {
    0xf3, 0xf6
};

static const unsigned char expected_output[] = {
    0xf4, 0xc6, 0x33, 0xc3, 0x04, 0x4f, 0xc1, 0x45,
    0xf8, 0x4f, 0x33, 0x5c, 0xb8, 0x19, 0x53, 0xde
};

/*
 * A property query used for selecting the POLY1305 implementation.
 */
static char *propq = NULL;

int main(int argc, char **argv)
{
    int ret = EXIT_FAILURE;
    EVP_CIPHER *aes = NULL;
    EVP_CIPHER_CTX *aesctx = NULL;
    EVP_MAC *mac = NULL;
    EVP_MAC_CTX *mctx = NULL;
    unsigned char composite_key[32];
    unsigned char out[16];
    OSSL_LIB_CTX *library_context = NULL;
    size_t out_len = 0;
    int aes_len = 0;

    library_context = OSSL_LIB_CTX_new();
    if (library_context == NULL) {
        fprintf(stderr, "OSSL_LIB_CTX_new() returned NULL\n");
        goto end;
    }

    /* Fetch the Poly1305 implementation */
    mac = EVP_MAC_fetch(library_context, "POLY1305", propq);
    if (mac == NULL) {
        fprintf(stderr, "EVP_MAC_fetch() returned NULL\n");
        goto end;
    }

    /* Create a context for the Poly1305 operation */
    mctx = EVP_MAC_CTX_new(mac);
    if (mctx == NULL) {
        fprintf(stderr, "EVP_MAC_CTX_new() returned NULL\n");
        goto end;
    }

    /* Fetch the AES implementation */
    aes = EVP_CIPHER_fetch(library_context, "AES-128-ECB", propq);
    if (aes == NULL) {
        fprintf(stderr, "EVP_CIPHER_fetch() returned NULL\n");
        goto end;
    }

    /* Create a context for AES */
    aesctx = EVP_CIPHER_CTX_new();
    if (aesctx == NULL) {
        fprintf(stderr, "EVP_CIPHER_CTX_new() returned NULL\n");
        goto end;
    }

    /* Initialize the AES cipher with the 128-bit key k */
    if (!EVP_EncryptInit_ex(aesctx, aes, NULL, test_k, NULL)) {
        fprintf(stderr, "EVP_EncryptInit_ex() failed\n");
        goto end;
    }

    /*
     * Disable padding for the AES cipher. We do not strictly need to do this as
     * we are encrypting a single block and thus there are no alignment or
     * padding concerns, but this ensures that the operation below fails if
     * padding would be required for some reason, which in this circumstance
     * would indicate an implementation bug.
     */
    if (!EVP_CIPHER_CTX_set_padding(aesctx, 0)) {
        fprintf(stderr, "EVP_CIPHER_CTX_set_padding() failed\n");
        goto end;
    }

    /*
     * Computes the value AES_k(n) which we need for our Poly1305-AES
     * computation below.
     */
    if (!EVP_EncryptUpdate(aesctx, composite_key + 16, &aes_len,
                           test_n, sizeof(test_n))) {
        fprintf(stderr, "EVP_EncryptUpdate() failed\n");
        goto end;
    }

    /*
     * The Poly1305 provider expects the key r to be passed as the first 16
     * bytes of the "key" and the processed nonce (that is, AES_k(n)) to be
     * passed as the second 16 bytes of the "key". We already put the processed
     * nonce in the correct place above, so copy r into place.
     */
    memcpy(composite_key, test_r, 16);

    /* Initialise the Poly1305 operation */
    if (!EVP_MAC_init(mctx, composite_key, sizeof(composite_key), NULL)) {
        fprintf(stderr, "EVP_MAC_init() failed\n");
        goto end;
    }

    /* Make one or more calls to process the data to be authenticated */
    if (!EVP_MAC_update(mctx, test_m, sizeof(test_m))) {
        fprintf(stderr, "EVP_MAC_update() failed\n");
        goto end;
    }

    /* Make one call to the final to get the MAC */
    if (!EVP_MAC_final(mctx, out, &out_len, sizeof(out))) {
        fprintf(stderr, "EVP_MAC_final() failed\n");
        goto end;
    }

    printf("Generated MAC:\n");
    BIO_dump_indent_fp(stdout, out, out_len, 2);
    putchar('\n');

    if (out_len != sizeof(expected_output)) {
        fprintf(stderr, "Generated MAC has an unexpected length\n");
        goto end;
    }

    if (CRYPTO_memcmp(expected_output, out, sizeof(expected_output)) != 0) {
        fprintf(stderr, "Generated MAC does not match expected value\n");
        goto end;
    }

    ret = EXIT_SUCCESS;
end:
    EVP_CIPHER_CTX_free(aesctx);
    EVP_CIPHER_free(aes);
    EVP_MAC_CTX_free(mctx);
    EVP_MAC_free(mac);
    OSSL_LIB_CTX_free(library_context);
    if (ret != EXIT_SUCCESS)
        ERR_print_errors_fp(stderr);
    return ret;
}