Software /
code /
prosody
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); |