extreme difference in time between AES-CBC + HMAC and AES-GCM - c

So I've been searching far and wide for different AES implementations for CBC and GCM, i do not want to implement this my self in case I make mistakes so i have found the following AES CBC codes and tested the speed of them on my RX63NB (Rennesas test board).
Encrypt Decrypt
bytes speed (us) bytes speed (us)
Tiny AES 64 1500 64 8900
128 2880 128 17820
aes-byte-29-08-08 64 1250 64 4900
128 1220 128 9740
Cyclone 64 230 64 237
128 375 128 387
I was suprised about how much faster Cyclone was, to clarify I took the AES, CBC and Endian files from CycloneSSL and only used those.
Then I tried GCM from CycloneSSl and this was the output:
Encrypt Decrypt
bytes speed μs bytes speed μs
Cyclone GCM 64 9340 64 9340
128 14900 128 14900
I have examained the HMAC time (from CycloneSSL) to see how much that would take:
HMAC bytes speed μs
Sha1 64 746
128 857
Sha224 64 918
128 1066
Sha256 64 918
128 1066
Sha384 64 2395
128 2840
Sha512 64 2400
128 2840
Sha512_224 64 2390
128 2835
Sha512_356 64 2390
128 2835
MD5 64 308
128 345
Whirlpool 64 5630
128 6420
Tiger 64 832
128 952
The slowest of which is whirlpool.
if you add the cbc encryption time for 128 bytes to the hmac of whirlpool with 128 bytes you get 6795 μs which is about half the time GCM takes.
now I can understand that a GHASH takes a bit longer than HMAC because of the galios field and such but beeing 2 times slower compared to the slowest HASH algorithm I know is insane.
So i've started to wonder if i did anything wrong or if the CycloneSLL gcm implementation is just really show. unfortunatly I have not found an other easy to use GCM implementation in c to compare it with.
All the code i used can be found on pastebin, the different files are separated by --------------------
This is the code i use to encrypt with GCM:
static void test_encrypt(void)
{
uint8_t key[] = { 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c };
uint8_t iv[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f };
uint8_t in[] = { 0x48, 0x61, 0x6c, 0x6c, 0x6f, 0x20, 0x68, 0x6f, 0x65, 0x20, 0x67, 0x61, 0x61, 0x74, 0x20, 0x68,
0x65, 0x74, 0x20, 0x6d, 0x65, 0x74, 0x20, 0x6a, 0x6f, 0x75, 0x20, 0x76, 0x61, 0x6e, 0x64, 0x61,
0x61, 0x67, 0x2c, 0x20, 0x6d, 0x65, 0x74, 0x20, 0x6d, 0x69, 0x6a, 0x20, 0x67, 0x61, 0x61, 0x74,
0x20, 0x68, 0x65, 0x74, 0x20, 0x67, 0x6f, 0x65, 0x64, 0x20, 0x68, 0x6f, 0x6f, 0x72, 0x2e, 0x21,
0x48, 0x61, 0x6c, 0x6c, 0x6f, 0x20, 0x68, 0x6f, 0x65, 0x20, 0x67, 0x61, 0x61, 0x74, 0x20, 0x68,
0x65, 0x74, 0x20, 0x6d, 0x65, 0x74, 0x20, 0x6a, 0x6f, 0x75, 0x20, 0x76, 0x61, 0x6e, 0x64, 0x61,
0x61, 0x67, 0x2c, 0x20, 0x6d, 0x65, 0x74, 0x20, 0x6d, 0x69, 0x6a, 0x20, 0x67, 0x61, 0x61, 0x74,
0x20, 0x68, 0x65, 0x74, 0x20, 0x67, 0x6f, 0x65, 0x64, 0x20, 0x68, 0x6f, 0x6f, 0x72, 0x2e, 0x21};
AesContext context;
aesInit(&context, key, 16 ); // 16 byte = 128 bit
error_crypto_t error = gcmEncrypt(AES_CIPHER_ALGO, &context, iv, 16, 0, 0, in, in, 128, key, 16);
}
static void test_decrypt(void)
{
uint8_t key[] = { 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c };
uint8_t tag[] = { 0x56, 0x56, 0x5C, 0xCD, 0x5C, 0x57, 0x36, 0x66, 0x73, 0xF7, 0xFF, 0x2A, 0x17, 0x49, 0x0E, 0xC4};
uint8_t iv[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f };
uint8_t out[] = { 0x05, 0x7C, 0x51, 0xFF, 0xE4, 0x9F, 0x8C, 0x90, 0xF1, 0x7D, 0x56, 0xFB, 0x87, 0xB9, 0x44, 0x79,
0xB1, 0x04, 0x32, 0x39, 0x78, 0xFF, 0x51, 0x60, 0x48, 0x0B, 0x21, 0x77, 0xF2, 0x26, 0x0B, 0x94,
0x7B, 0xA7, 0x26, 0x74, 0x87, 0xA8, 0x2C, 0x5A, 0xA1, 0x19, 0x03, 0x17, 0x66, 0x3A, 0x46, 0x9F,
0xE6, 0x1D, 0x3B, 0x65, 0xFD, 0xC0, 0xBA, 0xC0, 0xD9, 0x45, 0xE7, 0x17, 0x74, 0x0F, 0xB7, 0x4B,
0x0F, 0xF0, 0x16, 0xF6, 0xE8, 0x4F, 0xFD, 0x96, 0x64, 0x5E, 0xDB, 0x9E, 0x3A, 0x0B, 0x93, 0x8F,
0x87, 0x83, 0x90, 0xF8, 0xF9, 0xE6, 0xA3, 0xE7, 0x5E, 0x72, 0x3C, 0xB5, 0x98, 0x54, 0x11, 0xD7,
0xB4, 0x7C, 0xFF, 0xA3, 0x51, 0x1A, 0xB0, 0x69, 0x4F, 0x57, 0xBB, 0x83, 0x40, 0x2A, 0xE6, 0x75,
0x8B, 0xB5, 0xCA, 0xA4, 0x84, 0x82, 0x1D, 0xA8, 0x94, 0x03, 0x77, 0x9C, 0x3B, 0xF8, 0xA0, 0x60};
AesContext context;
aesInit(&context, key, 16 ); // 16 byte = 128 bit
error_crypto_t error = gcmDecrypt(AES_CIPHER_ALGO, &context, iv, 16, 0, 0, out, out, 128, tag, 16);
}
the data in the out[] is the gcm encrypted data from the in[] and it all works properly. (decrypts correctly and passes authentication.
Question
Are all GCM implementations this slow?
Are there other (better) GCM implementations?
Should I just use HMAC if i want a fast encryption + verification?
EDIT
I have been able to get the GCM method from mbedTLS (PolarSSL) to work which is about 11 times faster than cyclone (it takes 880us do encrypt/decrypt 128 bytes). and it produces the same output as the cylcone GCM so i'm confident this works properly.
gcm_context gcm_ctx;
gcm_init(&gcm_ctx, POLARSSL_CIPHER_ID_AES,key, 128);
int error = gcm_auth_decrypt(&gcm_ctx, 128,iv, 16, NULL, 0, tag, 16, out, buffer );

Your numbers seem odd, 128 bytes for aes-byte-29-08-08 takes less time than 64 bytes for encryption?
Assuming RX63N is comparable to Cortex-M (they both are 32 bit, no vector unit, and it's difficult to find information on RX63N):
The claimed benchmark for SharkSSL puts CBC at a bit more than twice as fast as GCM, 2.6 if optimized for speed. 9340 is way way larger than 340.
Cifra's benchmark shows a 10x difference between their AES and AES-GCM, although the GCM test also included auth-data. Still nowhere close to your differential between straight AES and GCM.
So in relative terms, to answer 1, I don't think all GCM implementations are that slow, relative to plain AES.
As for other GCM implementations, there's the aforementioned Cifra (although I haven't heard of it until just now, and it only has 3 stars on GitHub (if that means anything), so the level of vetting is likely to be rather low), and maybe you can rip out the AES-GCM implementation from FreeBSD. I can't speak about performance though in absolute terms on your platform.
HMAC is likely to be faster on platforms w/o hardware support like AES-NI support though (CLMUL), regardless of the implementation. How performance critical is this? Do you have to use AES or a block cipher? Perhaps ChaCha20+Poly1305 suits your performance needs better (see performance numbers from Cifra). That's now being used in OpenSSH - chacha.* and poly1305.*
Be aware of side channel attacks. Software implementations of AES can be sensitive to cache timing attacks, although I don't think this is applicable to microcontrollers where everything is in SRAM anyway.
*Salsa20 is ChaCha20's predecessor

Related

API for setting ECC Key mbedTLS

I am trying to set the ECC private key explicitly with mbedTLS for ECDSA signing. The key has been generated externally from mbedTLS and consists of the following arrays for the private key and the public key in the NIST secp256r1 curve (below). In all the of the mbedTLS ECDSA exmaples that I have seen, the key is generated with a random number generator with mbedtls_ecp_gen_key() but this doesn't work for me since I need to generate the key pair outside of the code and then set explicitly in the code.
const uint8_t Private_Key[] =
{
0x0a, 0x75, 0xde, 0x36, 0x78, 0x73, 0x50, 0x8b, 0x25, 0x1e, 0x19, 0xbe, 0xf4, 0x7b, 0x74,
0xfc, 0xd6, 0x97, 0x44, 0x12, 0x5f, 0x1c, 0x49, 0x89, 0x98, 0x0b, 0x65, 0x6c, 0x48, 0xa7, 0x8c, 0x5c
};
const uint8_t Public_Key[] =
{
0x3b, 0x08, 0xd7, 0x1a, 0x1b, 0x5a, 0xd0, 0x3e, 0x41, 0x5d, 0x8f, 0x68, 0xe9, 0x78,0x47, 0x6b,
0x35, 0x5c, 0xe2, 0x90, 0x8d, 0xb9, 0xc1, 0x46, 0xb1, 0x44, 0x77, 0x1f, 0x92, 0x57, 0xbf, 0x8e,
0x7c, 0xed, 0xdf, 0x3b, 0xea, 0xed, 0x5d, 0xea, 0x1d, 0x77, 0x39, 0xdb, 0xb7, 0x42, 0xe3, 0x6a,
0x07, 0x74, 0xca, 0x50, 0x8b, 0x19, 0xf5, 0x37, 0xd5, 0x2d, 0x57, 0x71, 0x70, 0x7e, 0xc7, 0x16
};
You can have a look at mbedtls_ecp_read_key for importing secret key and mbedtls_ecp_point_read_binary for importing public key from key data generated outside. Notice that mbedtls_ecp_point_read_binary expects binary data in uncompressed public key format, i.e 0x04 followed by X followed by Y, which means you should add a 0x04 to the head of the Public_Key data in your code.

Sending LLDP multicast packet from raw socket

I am sending LLDP packet to mock switch, because I am testing some DCB settings and I can see packet going out in tcpdump, but I can't see it coming to the link partner.
My code:
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <linux/if_packet.h>
#include <net/ethernet.h>
#include <string.h>
#include <net/if.h>
#include <errno.h>
#include <sys/ioctl.h>
int main() {
char buf[414] = {0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e, 0xb4, 0x96, 0x91, 0x94, 0xaa, 0x25, 0x88, 0xcc, 0x02, 0x07,
0x04, 0x4c, 0x76, 0x25, 0xee, 0xd3, 0x40, 0x04, 0x11, 0x05, 0x68, 0x75, 0x6e, 0x64, 0x72, 0x65,
0x64, 0x47, 0x69, 0x67, 0x45, 0x20, 0x31, 0x2f, 0x32, 0x34, 0x06, 0x02, 0x00, 0x78, 0x0a, 0x0a,
0x5a, 0x39, 0x31, 0x30, 0x30, 0x2d, 0x4f, 0x4e, 0x36, 0x31, 0x0c, 0xd6, 0x44, 0x65, 0x6c, 0x6c,
0x20, 0x52, 0x65, 0x61, 0x6c, 0x20, 0x54, 0x69, 0x6d, 0x65, 0x20, 0x4f, 0x70, 0x65, 0x72, 0x61,
0x74, 0x69, 0x6e, 0x67, 0x20, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x20, 0x53, 0x6f, 0x66, 0x74,
0x77, 0x61, 0x72, 0x65, 0x2e, 0x20, 0x44, 0x65, 0x6c, 0x6c, 0x20, 0x4f, 0x70, 0x65, 0x72, 0x61,
0x74, 0x69, 0x6e, 0x67, 0x20, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x20, 0x56, 0x65, 0x72, 0x73,
0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x32, 0x2e, 0x30, 0x2e, 0x20, 0x44, 0x65, 0x6c, 0x6c, 0x20, 0x41,
0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x53, 0x6f, 0x66, 0x74, 0x77,
0x61, 0x72, 0x65, 0x20, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x39, 0x2e, 0x31,
0x31, 0x28, 0x30, 0x2e, 0x30, 0x50, 0x36, 0x29, 0x20, 0x43, 0x6f, 0x70, 0x79, 0x72, 0x69, 0x67,
0x68, 0x74, 0x20, 0x28, 0x63, 0x29, 0x20, 0x31, 0x39, 0x39, 0x39, 0x2d, 0x32, 0x30, 0x31, 0x37,
0x44, 0x65, 0x6c, 0x6c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x20, 0x41, 0x6c, 0x6c, 0x20, 0x52, 0x69,
0x67, 0x68, 0x74, 0x73, 0x20, 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x2e, 0x42, 0x75,
0x69, 0x6c, 0x64, 0x20, 0x54, 0x69, 0x6d, 0x65, 0x3a, 0x20, 0x4d, 0x6f, 0x6e, 0x20, 0x46, 0x65,
0x62, 0x20, 0x32, 0x37, 0x20, 0x31, 0x36, 0x3a, 0x35, 0x37, 0x3a, 0x32, 0x30, 0x20, 0x32, 0x30,
0x31, 0x37, 0x0e, 0x04, 0x00, 0x16, 0x00, 0x16, 0x10, 0x0c, 0x05, 0x01, 0x0a, 0xed, 0x5f, 0x37,
0x02, 0x00, 0x90, 0x00, 0x01, 0x00, 0xfe, 0x06, 0x00, 0x80, 0xc2, 0x0b, 0x08, 0x18, 0xfe, 0x19,
0x00, 0x80, 0xc2, 0x09, 0x07, 0x12, 0x34, 0x56, 0x70, 0x0d, 0x0d, 0x0d, 0x0d, 0x0c, 0x0c, 0x0c,
0x0c, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0xfe, 0x19, 0x00, 0x80, 0xc2, 0x0a, 0x00,
0x01, 0x23, 0x45, 0x67, 0x0d, 0x0d, 0x0d, 0x0d, 0x0c, 0x0c, 0x0c, 0x0c, 0x02, 0x02, 0x02, 0x02,
0x02, 0x02, 0x02, 0x02, 0xfe, 0x0e, 0x00, 0x80, 0xc2, 0x0c, 0x00, 0x61, 0x89, 0x06, 0x61, 0x89,
0x14, 0x82, 0x0c, 0xbc, 0x00, 0x00};
struct ifreq ifr;
strncpy((char *)ifr.ifr_name, "eth1", IFNAMSIZ);
int sock_r = socket(AF_PACKET,SOCK_RAW,htons(ETH_P_ALL));
if (sock_r < 0)
printf("socket errno: %s\n", strerror(errno));
if (ioctl(sock_r, SIOCGIFINDEX, &ifr))
printf("ioctl errno: %s\n", strerror(errno));
struct sockaddr_ll sll = {.sll_family = AF_PACKET, .sll_ifindex = ifr.ifr_ifindex};
if(sendto(sock_r, buf, sizeof(buf), 0, (struct sockaddr *) &sll, sizeof(sll)))
printf("sendto errno: %s\n", strerror(errno));
close(sock_r);
return 0;
}
in tcpdump I see:
13:40:33.472198 LLDP, length 400
Chassis ID TLV (1), length 7
Subtype MAC address (4): 4c:76:25:ee:d3:40
0x0000: 044c 7625 eed3 40
Port ID TLV (2), length 17
Subtype Interface Name (5): hundredGigE 1/24
0x0000: 0568 756e 6472 6564 4769 6745 2031 2f32
0x0010: 34
Time to Live TLV (3), length 2: TTL 120s
0x0000: 0078
System Name TLV (5), length 10: Z9100-ON61
0x0000: 5a39 3130 302d 4f4e 3631
System Description TLV (6), length 214
Dell Real Time Operating System Software. Dell Operating System Version: 2.0. Dell Application Software Version: 9.11(0.0P6) Copyright (c) 1999-2017Dell Inc. All Rights Reserved.Build Time: Mon Feb 27 16:57:20 2017
0x0000: 4465 6c6c 2052 6561 6c20 5469 6d65 204f
0x0010: 7065 7261 7469 6e67 2053 7973 7465 6d20
0x0020: 536f 6674 7761 7265 2e20 4465 6c6c 204f
0x0030: 7065 7261 7469 6e67 2053 7973 7465 6d20
0x0040: 5665 7273 696f 6e3a 2032 2e30 2e20 4465
0x0050: 6c6c 2041 7070 6c69 6361 7469 6f6e 2053
0x0060: 6f66 7477 6172 6520 5665 7273 696f 6e3a
0x0070: 2039 2e31 3128 302e 3050 3629 2043 6f70
0x0080: 7972 6967 6874 2028 6329 2031 3939 392d
0x0090: 3230 3137 4465 6c6c 2049 6e63 2e20 416c
0x00a0: 6c20 5269 6768 7473 2052 6573 6572 7665
0x00b0: 642e 4275 696c 6420 5469 6d65 3a20 4d6f
0x00c0: 6e20 4665 6220 3237 2031 363a 3537 3a32
0x00d0: 3020 3230 3137
System Capabilities TLV (7), length 4
System Capabilities [Repeater, Bridge, Router] (0x0016)
Enabled Capabilities [Repeater, Bridge, Router] (0x0016)
0x0000: 0016 0016
Management Address TLV (8), length 12
Management Address length 5, AFI IPv4 (1): 10.237.95.55
Interface Index Interface Numbering (2): 9437185
0x0000: 0501 0aed 5f37 0200 9000 0100
Organization specific TLV (127), length 6: OUI Ethernet bridged (0x0080c2)
Priority Flow Control Configuration Subtype (11)
Willing: 0, MBC: 0, RES: 0, PFC cap:8
PFC Enable
Priority : 0 1 2 3 4 5 6 7
Value : 0 0 0 1 1 0 0 0
0x0000: 0080 c20b 0818
Organization specific TLV (127), length 25: OUI Ethernet bridged (0x0080c2)
ETS Configuration Subtype (9)
Willing:0, CBS:0, RES:0, Max TCs:7
Priority Assignment Table
Priority : 0 1 2 3 4 5 6 7
Value : 1 2 3 4 5 6 7 0
TC Bandwidth Table
TC% : 0 1 2 3 4 5 6 7
Value : 13 13 13 13 12 12 12 12
TSA Assignment Table
Traffic Class: 0 1 2 3 4 5 6 7
Value : 2 2 2 2 2 2 2 2
0x0000: 0080 c209 0712 3456 700d 0d0d 0d0c 0c0c
0x0010: 0c02 0202 0202 0202 02
Organization specific TLV (127), length 25: OUI Ethernet bridged (0x0080c2)
ETS Recommendation Subtype (10)
RES: 0
Priority Assignment Table
Priority : 0 1 2 3 4 5 6 7
Value : 0 1 2 3 4 5 6 7
TC Bandwidth Table
TC% : 0 1 2 3 4 5 6 7
Value : 13 13 13 13 12 12 12 12
TSA Assignment Table
Traffic Class: 0 1 2 3 4 5 6 7
Value : 2 2 2 2 2 2 2 2
0x0000: 0080 c20a 0001 2345 670d 0d0d 0d0c 0c0c
0x0010: 0c02 0202 0202 0202 02
Organization specific TLV (127), length 14: OUI Ethernet bridged (0x0080c2)
Application Priority Subtype (12)
RES: 0
Application Priority Table
Priority: 3, RES: 0, Sel: 1, Protocol ID: 24969
Priority: 3, RES: 0, Sel: 1, Protocol ID: 24969
Priority: 4, RES: 0, Sel: 2, Protocol ID: 33292
0x0000: 0080 c20c 0061 8906 6189 1482 0cbc
End TLV (0), length 0
Should I e.g. set something in raw socket props, or enable something in the driver or is it vendor specific and I'm in a pickle?
Turned out it was hardware-specific problem. Apparently some devices do not support transmitting spoofed LLDP frames for security reasons. Changing adapter to Niantic worked.

Verifying signature created using OpenSSL with BearSSL

I am trying to verify a ECDSA signature created with OpenSSL on an embedded device using BearSSL.
First I created a private key using OpenSSL and extracted the public key:
$ openssl ecparam -name secp256r1 -genkey -noout -out private.pem
$ openssl ec -in private.pem -pubout -out public.pem
Then I extracted the raw public key:
$ openssl ec -noout -text -inform PEM -in public.pem -pubin
read EC key
Public-Key: (256 bit)
pub:
04:28:4b:54:a4:d4:92:6c:82:2d:da:8a:e1:be:4b:
49:61:5d:91:2b:2d:f5:f2:66:f8:9b:d1:be:cb:fb:
db:fc:4f:68:cf:52:68:55:36:53:0f:8e:8d:69:3f:
40:3a:06:62:ad:5b:5a:66:e6:1d:31:c6:13:08:f3:
4f:94:7b:59:7a
I have a text.txt that contains nothing but hello world and sign this using the following command:
$ cat text.txt
hello world
$ openssl dgst -sha256 -sign private.pem text.txt > signature
This now contains the signature in ASN1 format and is 72 bytes long as to be expected:
$ hexdump signature
0000000 4530 2002 ac54 51af 8ac0 cee8 dc74 4120
0000010 105c b65d a085 06c5 8e9f 1527 12f5 8e50
0000020 9d19 9b30 2102 a900 d2a5 343e 3a10 0bdd
0000030 e0a8 82f8 de2a 4f2d 51bf a775 bc42 2d2e
0000040 19c0 874f d85e 004b
Now to the embedded part. I include the data, signature and the public key first:
uint8_t text[] = "hello world\n";
size_t textlen = 12;
uint8_t signature[] = {
0x45, 0x30, 0x20, 0x02, 0xac, 0x54, 0x51, 0xaf, 0x8a, 0xc0, 0xce, 0xe8, 0xdc, 0x74, 0x41, 0x20,
0x10, 0x5c, 0xb6, 0x5d, 0xa0, 0x85, 0x06, 0xc5, 0x8e, 0x9f, 0x15, 0x27, 0x12, 0xf5, 0x8e, 0x50,
0x9d, 0x19, 0x9b, 0x30, 0x21, 0x02, 0xa9, 0x00, 0xd2, 0xa5, 0x34, 0x3e, 0x3a, 0x10, 0x0b, 0xdd,
0xe0, 0xa8, 0x82, 0xf8, 0xde, 0x2a, 0x4f, 0x2d, 0x51, 0xbf, 0xa7, 0x75, 0xbc, 0x42, 0x2d, 0x2e,
0x19, 0xc0, 0x87, 0x4f, 0xd8, 0x5e, 0x00, 0x4b };
static const uint8_t public_bytes[] = {
0x04, 0x28, 0x4b, 0x54, 0xa4, 0xd4, 0x92, 0x6c, 0x82, 0x2d, 0xda, 0x8a, 0xe1, 0xbe, 0x4b,
0x49, 0x61, 0x5d, 0x91, 0x2b, 0x2d, 0xf5, 0xf2, 0x66, 0xf8, 0x9b, 0xd1, 0xbe, 0xcb, 0xfb,
0xdb, 0xfc, 0x4f, 0x68, 0xcf, 0x52, 0x68, 0x55, 0x36, 0x53, 0x0f, 0x8e, 0x8d, 0x69, 0x3f,
0x40, 0x3a, 0x06, 0x62, 0xad, 0x5b, 0x5a, 0x66, 0xe6, 0x1d, 0x31, 0xc6, 0x13, 0x08, 0xf3,
0x4f, 0x94, 0x7b, 0x59, 0x7a };
static const br_ec_public_key public_key = {
.curve = BR_EC_secp256r1,
.q = (void *)public_bytes,
.qlen = sizeof(public_bytes)
};
Also, out of paranoia, I compared the md5 sum of both the text.txt and my string before going any further as a quick check:
$ md5sum text.txt
6f5902ac237024bdd0c176cb93063dc4 text.txt
uint8_t sum[br_md5_SIZE];
br_md5_context md5ctx;
br_md5_init(&md5ctx);
br_md5_update(&md5ctx, text, textlen);
br_md5_out(&md5ctx, sum); // sum is the same as md5sum command output
I told OpenSSL to use sha256 previously to hash the payload for the signing process using ECDSA, so I am doing the same in BearSSL now and try to verify the signature using secp256r1:
br_sha256_context sha256ctx;
br_sha256_init(&sha256ctx);
br_sha256_update(&sha256ctx, text, textlen);
br_sha256_out(&sha256ctx, hash);
uint32_t result = br_ecdsa_i15_vrfy_asn1(&br_ec_prime_i15, hash, sizeof(hash), &public_key, signature, sizeof(signature));
My expectation would be that the verify works, as I give it the same hash, hash function, curve, the corresponding public key and the signature. However, this doesn't work. I am probably missing something obvious but can't work it out.
The signature file contents shown as
$ hexdump signature
0000000 4530 2002 ac54 51af 8ac0 cee8 dc74 4120
...
is displayed as 16-bit values.
The signature in the C program is defined as an array of 8-bit values
uint8_t signature[] = {
0x45, 0x30, 0x20, 0x02, 0xac, 0x54, 0x51, 0xaf, 0x8a, 0xc0, 0xce, 0xe8, 0xdc, 0x74, 0x41, 0x20,
...
};
Depending on the byte order this may or may not be correct. Does 4530 correspond to 45, 30 or 30, 45?
With little-endian byte-order, the hex dump
4530 2002 ...
would correspond to (*)
uint8_t signature[] = {
0x30, 0x45, 0x02, 0x20, ...
};
I suggest to display the hex dump as 8-bit values, e.g. by using
od -t x1 signature
and, if necessary, fix the array initialization in the C code.
According to dave_thompson_085's comment, the correct byte order is 0x30, 0x45, so the proposed fix (*) is the solution.
And an ECDSA signature on a 256-bit group definitely starts with first tag=SEQUENCE+constructed (always 0x30) then body length usually 68 to 70 (0x44 to 0x46)

Access violation when using OpenSSL's HMAC

I'm trying to do an HMAC-SHA512 on some data using OpenSSL. I get an "Exception thrown at 0x... (libcrypto-1_1-x64.dll) in Program.exe: 0xC0000005: Access violation writing location 0x..." error when executing the following code:
int main(int argc, char** argv)
{
uint8_t* data[] = { 0x14, 0xf7, 0xbd, 0x95, 0x57, 0x9a, 0x7e, 0xa1, 0x5c, 0xf7, 0x27, 0x91, 0x0d, 0x61, 0x58, 0x01, 0xa3, 0x12, 0x17, 0x54, 0x0b, 0x2e, 0xb4, 0xc5, 0xb1, 0xeb, 0xab, 0xe0, 0x43, 0x9b, 0x8e, 0x1f, 0x39, 0x7d, 0x85, 0x1a, 0x3a, 0x4b, 0x9c, 0xf4, 0xbf, 0x31, 0x55, 0x72, 0x41, 0xf5, 0xdb, 0xcb, 0xb3, 0xa6, 0xb5, 0xb8, 0x82, 0xe5, 0xef, 0x18, 0x72, 0xa0, 0x59, 0x08, 0x9b, 0xfa, 0x17, 0xa3 };
uint8_t* key = "some_rand_pw";
uint8_t* result = malloc(64);
memset(result, 0, 64);
HMAC(EVP_sha512(), key, 12, data, 64, result, (unsigned int)64); //ERROR
}
I would use uint8_t* result = HMAC(EVP_sha512(), key, 12, data, 64, NULL, NULL), but it isn't thread safe, and this will be a multithreaded program. Anyone have any idea what I did wrong here?
I'm using Visual Studio 2017 with 64-bit OpenSSL pre-built for Windows.
Your code is wrong. data must be an array of uint8, but you declared it as an array of pointers to uint8.
Furthermore the last parameter of HMAC must be a pointer to unsigned int but you provided an unsigned int, that's the reason for the crash.
Your compiler should have warned you. Compile with -Wall.
Corrected (untested) code:
int main(int argc, char** argv)
{
uint8_t data[] = { 0x14, 0xf7, 0xbd, 0x95, 0x57, 0x9a, 0x7e, 0xa1, 0x5c, 0xf7, 0x27, 0x91, 0x0d, 0x61, 0x58, 0x01, 0xa3, 0x12, 0x17, 0x54, 0x0b, 0x2e, 0xb4, 0xc5, 0xb1, 0xeb, 0xab, 0xe0, 0x43, 0x9b, 0x8e, 0x1f, 0x39, 0x7d, 0x85, 0x1a, 0x3a, 0x4b, 0x9c, 0xf4, 0xbf, 0x31, 0x55, 0x72, 0x41, 0xf5, 0xdb, 0xcb, 0xb3, 0xa6, 0xb5, 0xb8, 0x82, 0xe5, 0xef, 0x18, 0x72, 0xa0, 0x59, 0x08, 0x9b, 0xfa, 0x17, 0xa3 };
uint8_t* key = "some_rand_pw";
uint8_t* result = malloc(64);
unsigned int len;
memset(result, 0, 64);
HMAC(EVP_sha512(), key, 12, data, 64, result, &len);
}
There is still room for improvement though.

Why doesn't it work to convert EXE-file to binary code, add to C and re-create?

This is purely experimental and has no real usefulness (at least not according to my knowledge). I’m reading some literature about the PE-format and wanted to try this out if this was possible.
I created a simple program that prints ”Hello World” in C and compiled it to an EXE on a Windows computer, after that I opened the file in a hex editor and saved the content as a C-file (the hex editor has that as an option) and added it as a string in another C script.
The C-program:
#include <stdio.h>
int main(void)
{
printf("Hello World");
return 0;
}
Binary code for C:
0x23, 0x69, 0x6E, 0x63, 0x6C, 0x75, 0x64, 0x65, 0x20, 0x3C, 0x73, 0x74,
0x64, 0x69, 0x6F, 0x2E, 0x68, 0x3E, 0x0D, 0x0A, 0x23, 0x69, 0x6E, 0x63,
0x29, 0x3B, 0x0D, 0x0A, 0x09, 0x0D, 0x0A, 0x09, 0x72, 0x65, 0x74, 0x75,
0x72, 0x6E, 0x20, 0x30, 0x3B, 0x0D, 0x0A, 0x7D, 0x0D, 0x0A [...]
This other C script writes out the content in the string to the disk and saves it as an EXE-file that (according to my theory) would be able to execute. However, when I execute it I receive the message ”This app can’t run on your PC”.
The other C-program:
#include <stdio.h>
int main()
{
FILE* file_ptr = fopen("test_file.exe", "w");
unsigned char rawData[106] = {
0x23, 0x69, 0x6E, 0x63, 0x6C, 0x75, 0x64, 0x65, 0x20, 0x3C, 0x73, 0x74,
0x64, 0x69, 0x6F, 0x2E, 0x68, 0x3E, 0x0D, 0x0A, 0x23, 0x69, 0x6E, 0x63,
[...]
0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x57, 0x6F, 0x72, 0x6C, 0x64, 0x22,
0x29, 0x3B, 0x0D, 0x0A, 0x09, 0x0D, 0x0A, 0x09, 0x72, 0x65, 0x74, 0x75,
0x72, 0x6E, 0x20, 0x30, 0x3B, 0x0D, 0x0A, 0x7D, 0x0D, 0x0A
};
fprintf(file_ptr, "%s", rawData);
fclose(file_ptr);
return 0;
}
Why doesn't this work? I mean, it's pure machine code of the EXE-file which worked perfectly before. I guess something must happen in the compiller but I don't understand what exactly.
Anybody who could explain?
The format specifier "%s" is for null-terminated byte strings. The null-terminator equals the byte 0 (0x00). It's very likely that it exists somewhere in the array data. You need some other way to write it, like e.g. fwrite.
Also, since you're writing a binary file, you should open it in binary mode as well. Otherwise any byte corresponding to the newline character ('\n', 0x0a in ASCII encoding) will be translated to the Windows newline sequence "\r\n".
Lastly, the attributes of the file you create will not include the executable flag.
fprintf is for writing text to a file or stream. The %s format specifier will stop on a null character, which is obviously not going to work for binary data which can contain nulls anywhere.
Just the fact that it's binary data should be enough to deter you from using fprintf and other text IO functions, but in particular the PE-EXE format is guaranteed to have nulls within its content by virtue of its very header which begins with the magic number "PE\0\0".
Use the function fwrite to write binary data to a file:
fwrite(rawData, 1, sizeof(rawData), file_ptr);
The "binary" file you are writing is a source file, not an executable. If I translate your raw data:
unsigned char rawData[106] = {
0x23, 0x69, 0x6E, 0x63, 0x6C, 0x75, 0x64, 0x65, 0x20, 0x3C, 0x73, 0x74,
0x64, 0x69, 0x6F, 0x2E, 0x68, 0x3E, 0x0D, 0x0A, 0x23, 0x69, 0x6E, 0x63,
It corresponds to the text:
#include
(etc)
This is not an executable, it is the source file!
You cannot simply save a C source file with an "exe" extension and have it be able to run. You need to actually compile it.

Resources