Comparison

spec/util_crypto_spec.lua @ 12693:7c5afbdcbc77

util.crypto: New wrapper for some operations in OpenSSL's libcrypto Specifically, ED25519 key generation/import/export, sign/verify operations, and AES encrypt/decrypt.
author Matthew Wild <mwild1@gmail.com>
date Fri, 24 Jun 2022 16:56:16 +0100
child 12700:899c057781cd
comparison
equal deleted inserted replaced
12692:b001b0f42512 12693:7c5afbdcbc77
1 local test_keys = {
2 ecdsa_private_pem = [[
3 -----BEGIN PRIVATE KEY-----
4 MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg7taVK6bPtPz4ah32
5 aD9CfvOah5omBxRVtzypwQXvZeahRANCAAQpKFeNIy27+lVo6bJslO6r2ty5rlb5
6 xEiCx8GrrbJ8S7b5IPZCS7OrBaO2iqgOf7NMsgO12eLCfMZRnA+gCC34
7 -----END PRIVATE KEY-----
8 ]];
9
10 ecdsa_public_pem = [[
11 -----BEGIN PUBLIC KEY-----
12 MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEKShXjSMtu/pVaOmybJTuq9rcua5W
13 +cRIgsfBq62yfEu2+SD2QkuzqwWjtoqoDn+zTLIDtdniwnzGUZwPoAgt+A==
14 -----END PUBLIC KEY-----
15 ]];
16
17 eddsa_private_pem = [[
18 -----BEGIN PRIVATE KEY-----
19 MC4CAQAwBQYDK2VwBCIEIOmrajEfnqdzdJzkJ4irQMCGbYRqrl0RlwPHIw+a5b7M
20 -----END PRIVATE KEY-----
21 ]];
22
23 eddsa_public_pem = [[
24 -----BEGIN PUBLIC KEY-----
25 MCowBQYDK2VwAyEAFipbSXeGvPVK7eA4+hIOdutZTUUyXswVSbMGi0j1QKE=
26 -----END PUBLIC KEY-----
27 ]];
28
29 };
30
31 describe("util.crypto", function ()
32 local crypto = require "util.crypto";
33 local random = require "util.random";
34
35 describe("generate_ed25519_keypair", function ()
36 local keypair = crypto.generate_ed25519_keypair();
37 assert.is_not_nil(keypair);
38 assert.equal("ED25519", keypair:get_type());
39 end)
40
41 describe("import_private_pem", function ()
42 it("can import ECDSA keys", function ()
43 local ecdsa_key = crypto.import_private_pem(test_keys.ecdsa_private_pem);
44 assert.equal("id-ecPublicKey", ecdsa_key:get_type());
45 end);
46
47 it("can import EdDSA (Ed25519) keys", function ()
48 local ed25519_key = crypto.import_private_pem(crypto.generate_ed25519_keypair():private_pem());
49 assert.equal("ED25519", ed25519_key:get_type());
50 end);
51
52 it("can import RSA keys", function ()
53 -- TODO
54 end);
55
56 it("rejects invalid keys", function ()
57 assert.is_nil(crypto.import_private_pem(test_keys.eddsa_public_pem));
58 assert.is_nil(crypto.import_private_pem(test_keys.ecdsa_public_pem));
59 assert.is_nil(crypto.import_private_pem("foo"));
60 assert.is_nil(crypto.import_private_pem(""));
61 end);
62 end);
63
64 describe("import_public_pem", function ()
65 it("can import ECDSA public keys", function ()
66 local ecdsa_key = crypto.import_public_pem(test_keys.ecdsa_public_pem);
67 assert.equal("id-ecPublicKey", ecdsa_key:get_type());
68 end);
69
70 it("can import EdDSA (Ed25519) public keys", function ()
71 local ed25519_key = crypto.import_public_pem(test_keys.eddsa_public_pem);
72 assert.equal("ED25519", ed25519_key:get_type());
73 end);
74
75 it("can import RSA public keys", function ()
76 -- TODO
77 end);
78 end);
79
80 describe("PEM export", function ()
81 it("works", function ()
82 local ecdsa_key = crypto.import_public_pem(test_keys.ecdsa_public_pem);
83 assert.equal("id-ecPublicKey", ecdsa_key:get_type());
84 assert.equal(test_keys.ecdsa_public_pem, ecdsa_key:public_pem());
85
86 assert.has_error(function ()
87 -- Fails because private key is not available
88 ecdsa_key:private_pem();
89 end);
90
91 local ecdsa_private_key = crypto.import_private_pem(test_keys.ecdsa_private_pem);
92 assert.equal(test_keys.ecdsa_private_pem, ecdsa_private_key:private_pem());
93 end);
94 end);
95
96 describe("sign/verify with", function ()
97 local test_cases = {
98 ed25519 = {
99 crypto.ed25519_sign, crypto.ed25519_verify;
100 key = crypto.import_private_pem(test_keys.eddsa_private_pem);
101 sig_length = 64;
102 };
103 ecdsa = {
104 crypto.ecdsa_sha256_sign, crypto.ecdsa_sha256_verify;
105 key = crypto.import_private_pem(test_keys.ecdsa_private_pem);
106 };
107 };
108 for test_name, test in pairs(test_cases) do
109 local key = test.key;
110 describe(test_name, function ()
111 it("works", function ()
112 local sign, verify = test[1], test[2];
113 local sig = assert(sign(key, "Hello world"));
114 assert.is_string(sig);
115 if test.sig_length then
116 assert.equal(test.sig_length, #sig);
117 end
118
119 do
120 local ok = verify(key, "Hello world", sig);
121 assert.is_truthy(ok);
122 end
123 do -- Incorrect signature
124 local ok = verify(key, "Hello world", sig:sub(1, -2)..string.char((sig:byte(-1)+1)%255));
125 assert.is_falsy(ok);
126 end
127 do -- Incorrect message
128 local ok = verify(key, "Hello earth", sig);
129 assert.is_falsy(ok);
130 end
131 do -- Incorrect message (embedded NUL)
132 local ok = verify(key, "Hello world\0foo", sig);
133 assert.is_falsy(ok);
134 end
135 end);
136 end);
137 end
138 end);
139
140 describe("ECDSA signatures", function ()
141 local hex = require "util.hex";
142 local sig = hex.decode((([[
143 304402203e936e7b0bc62887e0e9d675afd08531a930384cfcf301
144 f25d13053a2ebf141d02205a5a7c7b7ac5878d004cb79b17b39346
145 6b0cd1043718ffc31c153b971d213a8e
146 ]]):gsub("%s+", "")));
147 it("can be parsed", function ()
148 local r, s = crypto.parse_ecdsa_signature(sig);
149 assert.is_string(r);
150 assert.is_string(s);
151 assert.equal(32, #r);
152 assert.equal(32, #s);
153 end);
154 it("fails to parse invalid signatures", function ()
155 local invalid_sigs = {
156 "";
157 "\000";
158 string.rep("\000", 64);
159 string.rep("\000", 72);
160 string.rep("\000", 256);
161 string.rep("\255", 72);
162 string.rep("\255", 3);
163 };
164 for _, sig in ipairs(invalid_sigs) do
165 local r, s = crypto.parse_ecdsa_signature("");
166 assert.is_nil(r);
167 assert.is_nil(s);
168 end
169
170 end);
171 it("can be built", function ()
172 local r, s = crypto.parse_ecdsa_signature(sig);
173 local rebuilt_sig = crypto.build_ecdsa_signature(r, s);
174 assert.equal(sig, rebuilt_sig);
175 end);
176 end);
177
178 describe("AES-GCM encryption", function ()
179 it("works", function ()
180 local message = "foo\0bar";
181 local key_128_bit = random.bytes(16);
182 local key_256_bit = random.bytes(32);
183 local test_cases = {
184 { crypto.aes_128_gcm_encrypt, crypto.aes_128_gcm_decrypt, key = key_128_bit };
185 { crypto.aes_256_gcm_encrypt, crypto.aes_256_gcm_decrypt, key = key_256_bit };
186 };
187 for _, params in pairs(test_cases) do
188 local iv = params.iv or random.bytes(12);
189 local encrypted = params[1](params.key, iv, message);
190 assert.not_equal(message, encrypted);
191 local decrypted = params[2](params.key, iv, encrypted);
192 assert.equal(message, decrypted);
193 end
194 end);
195 end);
196 end);