cmac-rb 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|