Skip to content

Commit 28cd0d0

Browse files
committed
crypto: add ML-DSA algorithm (fixes #26683)
1 parent 550dee3 commit 28cd0d0

3 files changed

Lines changed: 153 additions & 0 deletions

File tree

cmd/tools/modules/testing/common.v

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ module testing
22

33
import os
44
import os.cmdline
5+
import semver
56
import time
67
import term
78
import benchmark
@@ -947,7 +948,28 @@ fn check_openssl_present() bool {
947948
}
948949
}
949950

951+
fn check_modern_openssl_present() bool {
952+
if !is_openssl_present {
953+
return false
954+
}
955+
mut version_cmd := 'openssl version'
956+
$if openbsd {
957+
version_cmd = 'eopenssl35 version'
958+
}
959+
res := os.execute(version_cmd)
960+
if res.exit_code != 0 {
961+
return false
962+
}
963+
line := res.output.trim_space()
964+
if !line.starts_with('OpenSSL ') {
965+
return false
966+
}
967+
version := semver.coerce(line) or { return false }
968+
return version.satisfies('>=3.5.0')
969+
}
970+
950971
pub const is_openssl_present = check_openssl_present()
972+
pub const is_modern_openssl_present = check_modern_openssl_present()
951973

952974
// is_started_mysqld is true, when the test runner determines that there is a running mysql server
953975
pub const is_started_mysqld = find_started_process('mysqld') or { '' }
@@ -997,6 +1019,9 @@ pub fn (mut ts TestSession) setup_build_environment() {
9971019
if is_openssl_present {
9981020
defines << 'present_openssl'
9991021
}
1022+
if is_modern_openssl_present {
1023+
defines << 'has_modern_openssl'
1024+
}
10001025

10011026
// detect the linux distribution as well when possible:
10021027
if os.is_file('/etc/os-release') {

vlib/x/crypto/mldsa/mldsa.c.v

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
// Copyright (c) V contributors. All rights reserved.
2+
// Use of this source code is governed by an MIT license
3+
// that can be found in the LICENSE file.
4+
module mldsa
5+
6+
// TODO: remove this when OpenSSL 3.5 is more widely available by default.
7+
#flag linux -I/opt/ssl/include/openssl
8+
#flag linux -I/opt/ssl/include/crypto
9+
#flag linux -I/opt/ssl/include
10+
#flag linux -L/opt/ssl/lib64
11+
12+
// Standard path
13+
#flag linux -I/usr/local/include/openssl
14+
#flag linux -I/usr/local/include/crypto
15+
#flag linux -L/usr/local/lib64
16+
17+
#flag darwin -L /opt/homebrew/opt/openssl/lib -I /opt/homebrew/opt/openssl/include
18+
19+
#flag -I/usr/include/openssl
20+
#flag -lcrypto
21+
22+
#flag darwin -I/usr/local/opt/openssl/include
23+
#flag darwin -L/usr/local/opt/openssl/lib
24+
25+
#flag openbsd -I/usr/local/include/eopenssl35
26+
#flag openbsd -L/usr/local/lib/eopenssl35 -Wl,-rpath,/usr/local/lib/eopenssl35
27+
28+
#include <openssl/obj_mac.h>
29+
#include <openssl/evp.h>
30+
#include <openssl/param_build.h>
31+
32+
@[typedef]
33+
struct C.EVP_PKEY {}
34+
35+
@[typedef]
36+
struct C.EVP_PKEY_CTX {}
37+
38+
@[typedef]
39+
struct C.EVP_SIGNATURE {}
40+
41+
@[typedef]
42+
struct C.OSSL_PARAM {}
43+
44+
@[typedef]
45+
struct C.OSSL_PARAM_BLD {}
46+
47+
fn C.OpenSSL_version_num() u64
48+
49+
fn C.EVP_PKEY_new() &C.EVP_PKEY
50+
fn C.EVP_PKEY_Q_keygen(ctx voidptr, propq &char, tipe &char) &C.EVP_PKEY
51+
fn C.EVP_PKEY_dup(key &C.EVP_PKEY) &C.EVP_PKEY
52+
fn C.EVP_PKEY_free(key &C.EVP_PKEY)
53+
fn C.EVP_PKEY_size(key &C.EVP_PKEY) i32
54+
fn C.EVP_PKEY_get0_type_name(key &C.EVP_PKEY) &char
55+
fn C.EVP_PKEY_get_octet_string_param(pkey &C.EVP_PKEY, key_name &char, buf &u8, max_buf_sz usize, out_len &usize) i32
56+
fn C.EVP_PKEY_set_octet_string_param(key &C.EVP_PKEY, key_name &char, buf voidptr, bsize usize) i32
57+
fn C.EVP_PKEY_keygen_init(ctx &C.EVP_PKEY_CTX) i32
58+
fn C.EVP_PKEY_keygen(ctx &C.EVP_PKEY_CTX, ppkey &&C.EVP_PKEY) i32
59+
fn C.EVP_PKEY_CTX_set_params(ctx &C.EVP_PKEY_CTX, params &C.OSSL_PARAM) i32
60+
fn C.EVP_PKEY_fromdata_init(ctx &C.EVP_PKEY_CTX) i32
61+
fn C.EVP_PKEY_fromdata(ctx &C.EVP_PKEY_CTX, ppkey &&C.EVP_PKEY, selection i32, params &C.OSSL_PARAM) i32
62+
fn C.EVP_PKEY_sign_message_init(ctx &C.EVP_PKEY_CTX, algo &C.EVP_SIGNATURE, params &C.OSSL_PARAM) i32
63+
fn C.EVP_PKEY_sign(ctx &C.EVP_PKEY_CTX, sig &u8, siglen &usize, tbs &u8, tbslen usize) i32
64+
fn C.EVP_PKEY_verify_message_init(ctx &C.EVP_PKEY_CTX, algo &C.EVP_SIGNATURE, params &C.OSSL_PARAM) i32
65+
fn C.EVP_PKEY_verify(ctx &C.EVP_PKEY_CTX, sig &u8, siglen usize, tbs &u8, tbslen usize) i32
66+
67+
fn C.EVP_PKEY_CTX_new_from_name(libctx voidptr, name &char, pq voidptr) &C.EVP_PKEY_CTX
68+
fn C.EVP_PKEY_CTX_new_from_pkey(libctx voidptr, pkey &C.EVP_PKEY, pq voidptr) &C.EVP_PKEY_CTX
69+
fn C.EVP_PKEY_CTX_free(ctx &C.EVP_PKEY_CTX)
70+
71+
fn C.EVP_SIGNATURE_free(signature &C.EVP_SIGNATURE)
72+
fn C.EVP_SIGNATURE_fetch(ctx voidptr, algorithm &char, properties voidptr) &C.EVP_SIGNATURE
73+
74+
fn C.OSSL_PARAM_free(params &C.OSSL_PARAM)
75+
fn C.OSSL_PARAM_BLD_free(param_bld &C.OSSL_PARAM_BLD)
76+
fn C.OSSL_PARAM_BLD_new() &C.OSSL_PARAM_BLD
77+
fn C.OSSL_PARAM_BLD_push_int(bld &C.OSSL_PARAM_BLD, key &char, val i32) i32
78+
fn C.OSSL_PARAM_BLD_push_octet_string(bld &C.OSSL_PARAM_BLD, key &char, buf voidptr, bsize usize) i32
79+
fn C.OSSL_PARAM_BLD_to_param(bld &C.OSSL_PARAM_BLD) &C.OSSL_PARAM

vlib/x/crypto/mldsa/usage_test.v

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// vtest build: has_modern_openssl?
2+
module mldsa_test
3+
4+
import x.crypto.mldsa
5+
6+
fn test_mldsa_basic_sign_and_verify() ! {
7+
mut pv := mldsa.PrivateKey.new()!
8+
defer {
9+
pv.free()
10+
}
11+
mut pb := pv.public_key()!
12+
defer {
13+
pb.free()
14+
}
15+
msg := 'ml-dsa basic roundtrip'.bytes()
16+
sig := pv.sign(msg)!
17+
assert sig.len > 0
18+
assert pv.verify(sig, msg)!
19+
assert pb.verify(sig, msg)!
20+
assert !pb.verify(sig, 'ml-dsa mismatch'.bytes())!
21+
}
22+
23+
fn test_mldsa_seed_and_raw_key_roundtrip() ! {
24+
kind := mldsa.Kind.ml_dsa_65
25+
mut pv := mldsa.PrivateKey.new(kind: kind)!
26+
defer {
27+
pv.free()
28+
}
29+
seed := pv.seed()!
30+
priv := pv.bytes()!
31+
pub_bytes := pv.public_bytes()!
32+
assert seed.len == 32
33+
assert priv.len == kind.private_key_size()
34+
assert pub_bytes.len == kind.public_key_size()
35+
36+
mut pv_from_seed := mldsa.PrivateKey.from_seed(seed, kind)!
37+
defer {
38+
pv_from_seed.free()
39+
}
40+
assert pv_from_seed.bytes()! == priv
41+
42+
mut pb := mldsa.PublicKey.from_bytes(pub_bytes, kind)!
43+
defer {
44+
pb.free()
45+
}
46+
msg := 'ml-dsa imported public key'.bytes()
47+
sig := pv.sign(msg, deterministic: 1)!
48+
assert pb.verify(sig, msg)!
49+
}

0 commit comments

Comments
 (0)