cmac-rb 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/ext/cmac/cmac.c +112 -0
- data/ext/cmac/cmac.h +23 -0
- data/ext/cmac/extconf.rb +6 -0
- data/ext/cmac/wrapper.c +140 -0
- data/lib/cmac-rb.rb +2 -0
- data/lib/cmac-rb/version.rb +3 -0
- metadata +84 -0
data/ext/cmac/cmac.c
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
#include <stdio.h>
|
|
2
|
+
#include <string.h>
|
|
3
|
+
#include <openssl/crypto.h>
|
|
4
|
+
#include <openssl/evp.h>
|
|
5
|
+
#include <openssl/aes.h>
|
|
6
|
+
|
|
7
|
+
#include "cmac.h"
|
|
8
|
+
|
|
9
|
+
static void cmac_xor (unsigned char *out, const unsigned char *in) {
|
|
10
|
+
|
|
11
|
+
int i;
|
|
12
|
+
|
|
13
|
+
for (i = 0; i < AES_BLOCK_SIZE; i++) {
|
|
14
|
+
out[i] ^= in[i];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
static void cmac_pad (unsigned char *buf, int len) {
|
|
20
|
+
|
|
21
|
+
int i;
|
|
22
|
+
|
|
23
|
+
for ( i = len; i < AES_BLOCK_SIZE; i++ ) {
|
|
24
|
+
buf[i] = (i == len) ? 0x80 : 0x00;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
static unsigned char cmac_left_shift(unsigned char *out, unsigned char *in, unsigned char *overflow) {
|
|
30
|
+
|
|
31
|
+
int i;
|
|
32
|
+
|
|
33
|
+
for (i = AES_BLOCK_SIZE - 1; i >= 0; i--) {
|
|
34
|
+
out[i] = (in[i] << 1) | (*overflow);
|
|
35
|
+
(*overflow) = CMAC_MSB(&in[i]);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return;
|
|
39
|
+
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
static void cmac_generate_sub_key(unsigned char *out, unsigned char *in) {
|
|
43
|
+
|
|
44
|
+
int i; unsigned char overflow = 0;
|
|
45
|
+
|
|
46
|
+
cmac_left_shift(out, in, &overflow);
|
|
47
|
+
|
|
48
|
+
if (overflow) {
|
|
49
|
+
out[AES_BLOCK_SIZE-1] ^= 0x87;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
void cmac_encrypt (cmac_ctx *ctx, const unsigned char *msg, int msg_len, unsigned char *ct) {
|
|
57
|
+
|
|
58
|
+
int n, i, k;
|
|
59
|
+
|
|
60
|
+
unsigned char M[AES_BLOCK_SIZE];
|
|
61
|
+
unsigned char *cursor = (unsigned char *) msg;
|
|
62
|
+
|
|
63
|
+
memcpy(ct, zero_block, AES_BLOCK_SIZE);
|
|
64
|
+
memset(M, 0, AES_BLOCK_SIZE);
|
|
65
|
+
|
|
66
|
+
n = (msg_len + (AES_BLOCK_SIZE - 1)) / AES_BLOCK_SIZE - 1;
|
|
67
|
+
k = (msg_len % AES_BLOCK_SIZE);
|
|
68
|
+
|
|
69
|
+
for (i = 0; i < n; i++) {
|
|
70
|
+
cmac_xor(ct, cursor);
|
|
71
|
+
AES_ecb_encrypt(ct, ct, &ctx->cmac_key, AES_ENCRYPT);
|
|
72
|
+
cursor += AES_BLOCK_SIZE;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (k == 0) {
|
|
76
|
+
if (msg != NULL && msg_len != 0) {
|
|
77
|
+
memcpy(M, cursor, AES_BLOCK_SIZE);
|
|
78
|
+
cmac_xor(M, ctx->K1);
|
|
79
|
+
} else {
|
|
80
|
+
cmac_pad(M, 0); cmac_xor(M, ctx->K2);
|
|
81
|
+
}
|
|
82
|
+
} else {
|
|
83
|
+
memcpy(M, cursor, k);
|
|
84
|
+
cmac_pad(M, k); cmac_xor(M, ctx->K2);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
cmac_xor(ct, M);
|
|
88
|
+
|
|
89
|
+
AES_ecb_encrypt(ct, ct, &ctx->cmac_key, AES_ENCRYPT);
|
|
90
|
+
|
|
91
|
+
return;
|
|
92
|
+
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
int cmac_init (cmac_ctx *ctx, const unsigned char *key, int key_len)
|
|
96
|
+
{
|
|
97
|
+
|
|
98
|
+
unsigned char L[AES_BLOCK_SIZE];
|
|
99
|
+
|
|
100
|
+
memset((char *)ctx, 0, sizeof(cmac_ctx));
|
|
101
|
+
|
|
102
|
+
AES_set_encrypt_key(key, 128, &ctx->cmac_key);
|
|
103
|
+
AES_ecb_encrypt(zero_block, L, &ctx->cmac_key, AES_ENCRYPT);
|
|
104
|
+
|
|
105
|
+
cmac_generate_sub_key(ctx->K1, L);
|
|
106
|
+
cmac_generate_sub_key(ctx->K2, ctx->K1);
|
|
107
|
+
|
|
108
|
+
cmac_encrypt(ctx, zero_block, AES_BLOCK_SIZE, ctx->T);
|
|
109
|
+
|
|
110
|
+
return 1;
|
|
111
|
+
|
|
112
|
+
}
|
data/ext/cmac/cmac.h
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
#ifndef _CMAC_H_
|
|
2
|
+
#define _CMAC_H_
|
|
3
|
+
|
|
4
|
+
#include <openssl/aes.h>
|
|
5
|
+
|
|
6
|
+
typedef struct _cmac_ctx {
|
|
7
|
+
unsigned char K1[AES_BLOCK_SIZE];
|
|
8
|
+
unsigned char K2[AES_BLOCK_SIZE];
|
|
9
|
+
unsigned char T[AES_BLOCK_SIZE];
|
|
10
|
+
AES_KEY cmac_key;
|
|
11
|
+
} cmac_ctx;
|
|
12
|
+
|
|
13
|
+
#define CMAC_MSB(x) (((x)[0] & 0x80)?1:0)
|
|
14
|
+
|
|
15
|
+
static unsigned char zero_block[AES_BLOCK_SIZE] = {
|
|
16
|
+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
17
|
+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
int cmac_init(cmac_ctx * ctx, const unsigned char * key, int key_len);
|
|
21
|
+
void cmac_encrypt (cmac_ctx * ctx, const unsigned char * msg, int msg_len, unsigned char * ct);
|
|
22
|
+
|
|
23
|
+
#endif
|
data/ext/cmac/extconf.rb
ADDED
data/ext/cmac/wrapper.c
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
#include <ruby.h>
|
|
2
|
+
#include "cmac.h"
|
|
3
|
+
|
|
4
|
+
// Top-level objects for the native extension
|
|
5
|
+
static VALUE cmac_rb;
|
|
6
|
+
static VALUE cmac_rb_digest;
|
|
7
|
+
|
|
8
|
+
/*
|
|
9
|
+
* Initialize an CMAC::Digest object with a key.
|
|
10
|
+
* Performs basic length validation on the key.
|
|
11
|
+
*/
|
|
12
|
+
static VALUE cmac_rb_initialize(VALUE self, VALUE key) {
|
|
13
|
+
|
|
14
|
+
int keyLen;
|
|
15
|
+
|
|
16
|
+
// Replace key value with key.to_str
|
|
17
|
+
StringValue(key);
|
|
18
|
+
|
|
19
|
+
// Get the key length as an int.
|
|
20
|
+
keyLen = RSTRING_LEN(key);
|
|
21
|
+
|
|
22
|
+
// Make sure key is not empty
|
|
23
|
+
if (keyLen == 0) {
|
|
24
|
+
rb_raise(rb_eArgError, "Key must be non-empty.");
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Make sure key is acceptable size
|
|
28
|
+
if (keyLen != AES_BLOCK_SIZE) {
|
|
29
|
+
rb_raise(rb_eArgError, "Only 128-bit keys are supported.");
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Set key as instance variable
|
|
33
|
+
rb_iv_set(self, "@key", key);
|
|
34
|
+
|
|
35
|
+
return self;
|
|
36
|
+
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/*
|
|
40
|
+
* Get an cmac_ctx object for the current instance
|
|
41
|
+
* by fetching the @key instance variable, converting
|
|
42
|
+
* it to a byte array and feeding it into cmac_init.
|
|
43
|
+
*
|
|
44
|
+
* Returns 1 upon success, 0 upon failure.
|
|
45
|
+
*/
|
|
46
|
+
static int cmac_rb_get_ctx(VALUE self, cmac_ctx* ctx) {
|
|
47
|
+
|
|
48
|
+
VALUE key; unsigned char* cKey; int cKeyLen;
|
|
49
|
+
|
|
50
|
+
// Get the key instance variable
|
|
51
|
+
key = rb_iv_get(self, "@key");
|
|
52
|
+
|
|
53
|
+
// Convert the key to a byte array
|
|
54
|
+
cKey = (unsigned char*) RSTRING_PTR(key);
|
|
55
|
+
cKeyLen = RSTRING_LEN(key);
|
|
56
|
+
|
|
57
|
+
// Initialize the context with the key
|
|
58
|
+
if (cmac_init(ctx, cKey, cKeyLen * 8) < 0) {
|
|
59
|
+
return 0;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Return 1 upon successful initialization
|
|
63
|
+
return 1;
|
|
64
|
+
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/*
|
|
68
|
+
* Compute the digest of some plaintext
|
|
69
|
+
*/
|
|
70
|
+
static VALUE cmac_rb_update(VALUE self, VALUE plaintext) {
|
|
71
|
+
|
|
72
|
+
// Holds the CMAC context object.
|
|
73
|
+
cmac_ctx ctx;
|
|
74
|
+
|
|
75
|
+
// Input plaintext as byte array.
|
|
76
|
+
const unsigned char* cPlaintext;
|
|
77
|
+
|
|
78
|
+
// Length of the input plaintext.
|
|
79
|
+
int cPlaintextLen;
|
|
80
|
+
|
|
81
|
+
// Holds the CMAC ciphertext object.
|
|
82
|
+
unsigned char* cDigest;
|
|
83
|
+
|
|
84
|
+
// Holds the length of the CMAC digest.
|
|
85
|
+
int cDigestLen;
|
|
86
|
+
|
|
87
|
+
// Get the CMAC context based on the instance's key.
|
|
88
|
+
if (!cmac_rb_get_ctx(self, &ctx)) {
|
|
89
|
+
rb_raise(rb_eRuntimeError, "Could not get CMAC context");
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Replace the plaintext with plaintext.to_str
|
|
93
|
+
StringValue(plaintext);
|
|
94
|
+
|
|
95
|
+
// Convert the plaintext to a byte array.
|
|
96
|
+
cPlaintext = (const unsigned char*) RSTRING_PTR(plaintext);
|
|
97
|
+
cPlaintextLen = RSTRING_LEN(plaintext);
|
|
98
|
+
|
|
99
|
+
// Allocate space for the ciphertext.
|
|
100
|
+
cDigestLen = sizeof(unsigned char) * AES_BLOCK_SIZE;
|
|
101
|
+
cDigest = (unsigned char*) malloc(cDigestLen);
|
|
102
|
+
|
|
103
|
+
// Call aes_cmac with all parameters.
|
|
104
|
+
cmac_encrypt(&ctx, cPlaintext, cPlaintextLen, cDigest);
|
|
105
|
+
|
|
106
|
+
// Return a new Ruby string with the resulting value.
|
|
107
|
+
return rb_str_new((const char*) cDigest, cDigestLen);
|
|
108
|
+
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/*
|
|
112
|
+
* Main wrapper for the CMAC Ruby native extension.
|
|
113
|
+
*/
|
|
114
|
+
void Init_wrapper(void) {
|
|
115
|
+
|
|
116
|
+
// Define the top-level module
|
|
117
|
+
cmac_rb = rb_define_module("CMAC");
|
|
118
|
+
|
|
119
|
+
// Define the cipher class
|
|
120
|
+
cmac_rb_digest = rb_define_class_under(cmac_rb, "Digest", rb_cObject);
|
|
121
|
+
|
|
122
|
+
// Define the implemented methods.
|
|
123
|
+
rb_define_method(cmac_rb_digest, "initialize", cmac_rb_initialize, 1);
|
|
124
|
+
rb_define_method(cmac_rb_digest, "update", cmac_rb_update, 1);
|
|
125
|
+
|
|
126
|
+
return;
|
|
127
|
+
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/*
|
|
131
|
+
Debug helper method to print byte arrays in hex format.
|
|
132
|
+
|
|
133
|
+
void print_hex(const char* header, const unsigned char *bytes, int len) {
|
|
134
|
+
|
|
135
|
+
int i = 0; printf("\n%s (%d): ", header, len);
|
|
136
|
+
for (i = 0; i < len; ++i) printf("%x", bytes[i]);
|
|
137
|
+
printf("\n");
|
|
138
|
+
|
|
139
|
+
}
|
|
140
|
+
*/
|
data/lib/cmac-rb.rb
ADDED
metadata
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: cmac-rb
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.1
|
|
5
|
+
prerelease:
|
|
6
|
+
platform: ruby
|
|
7
|
+
authors:
|
|
8
|
+
- Louis Mullie
|
|
9
|
+
autorequire:
|
|
10
|
+
bindir: bin
|
|
11
|
+
cert_chain: []
|
|
12
|
+
date: 2014-04-25 00:00:00.000000000 Z
|
|
13
|
+
dependencies:
|
|
14
|
+
- !ruby/object:Gem::Dependency
|
|
15
|
+
name: rspec
|
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
|
17
|
+
none: false
|
|
18
|
+
requirements:
|
|
19
|
+
- - ~>
|
|
20
|
+
- !ruby/object:Gem::Version
|
|
21
|
+
version: 2.12.0
|
|
22
|
+
type: :development
|
|
23
|
+
prerelease: false
|
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
25
|
+
none: false
|
|
26
|
+
requirements:
|
|
27
|
+
- - ~>
|
|
28
|
+
- !ruby/object:Gem::Version
|
|
29
|
+
version: 2.12.0
|
|
30
|
+
- !ruby/object:Gem::Dependency
|
|
31
|
+
name: rake
|
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
|
33
|
+
none: false
|
|
34
|
+
requirements:
|
|
35
|
+
- - ! '>='
|
|
36
|
+
- !ruby/object:Gem::Version
|
|
37
|
+
version: '0'
|
|
38
|
+
type: :development
|
|
39
|
+
prerelease: false
|
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
41
|
+
none: false
|
|
42
|
+
requirements:
|
|
43
|
+
- - ! '>='
|
|
44
|
+
- !ruby/object:Gem::Version
|
|
45
|
+
version: '0'
|
|
46
|
+
description: ! ' Ruby C extension for the AES-CMAC keyed hash function. '
|
|
47
|
+
email:
|
|
48
|
+
- louis.mullie@gmail.com
|
|
49
|
+
executables: []
|
|
50
|
+
extensions:
|
|
51
|
+
- ext/cmac/extconf.rb
|
|
52
|
+
extra_rdoc_files: []
|
|
53
|
+
files:
|
|
54
|
+
- lib/cmac-rb/version.rb
|
|
55
|
+
- lib/cmac-rb.rb
|
|
56
|
+
- ext/cmac/cmac.c
|
|
57
|
+
- ext/cmac/wrapper.c
|
|
58
|
+
- ext/cmac/cmac.h
|
|
59
|
+
- ext/cmac/extconf.rb
|
|
60
|
+
homepage: https://github.com/cryodex/cmac-rb
|
|
61
|
+
licenses: []
|
|
62
|
+
post_install_message:
|
|
63
|
+
rdoc_options: []
|
|
64
|
+
require_paths:
|
|
65
|
+
- lib
|
|
66
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
67
|
+
none: false
|
|
68
|
+
requirements:
|
|
69
|
+
- - ! '>='
|
|
70
|
+
- !ruby/object:Gem::Version
|
|
71
|
+
version: '0'
|
|
72
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
73
|
+
none: false
|
|
74
|
+
requirements:
|
|
75
|
+
- - ! '>='
|
|
76
|
+
- !ruby/object:Gem::Version
|
|
77
|
+
version: '0'
|
|
78
|
+
requirements: []
|
|
79
|
+
rubyforge_project:
|
|
80
|
+
rubygems_version: 1.8.25
|
|
81
|
+
signing_key:
|
|
82
|
+
specification_version: 3
|
|
83
|
+
summary: Ruby C extension for the AES-CMAC keyed hash function.
|
|
84
|
+
test_files: []
|