authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2024-02-02 19:14:27+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-02-02 17:27:26-08:00
log5e791e8e0786ea050f0f09da5de4657f3f9caad1
tree3dae1f320318f60b85ac597422254b69fed42fbf
parent92deebcd668750bd4042c5874bf35b447828cd8a

tls: support ed25519 signatures

Which were claimed to be supported during the handshake but were not actually implemented.

3 files changed, 60 insertions(+), 10 deletions(-)

lib/std/crypto/25519/ed25519.zig+4-4
...@@ -199,8 +199,8 @@ pub const Ed25519 = struct {...@@ -199,8 +199,8 @@ pub const Ed25519 = struct {
199 /// Return the raw signature (r, s) in little-endian format.199 /// Return the raw signature (r, s) in little-endian format.
200 pub fn toBytes(self: Signature) [encoded_length]u8 {200 pub fn toBytes(self: Signature) [encoded_length]u8 {
201 var bytes: [encoded_length]u8 = undefined;201 var bytes: [encoded_length]u8 = undefined;
202 bytes[0 .. encoded_length / 2].* = self.r;202 bytes[0..Curve.encoded_length].* = self.r;
203 bytes[encoded_length / 2 ..].* = self.s;203 bytes[Curve.encoded_length..].* = self.s;
204 return bytes;204 return bytes;
205 }205 }
206206
...@@ -208,8 +208,8 @@ pub const Ed25519 = struct {...@@ -208,8 +208,8 @@ pub const Ed25519 = struct {
208 /// EdDSA always assumes little-endian.208 /// EdDSA always assumes little-endian.
209 pub fn fromBytes(bytes: [encoded_length]u8) Signature {209 pub fn fromBytes(bytes: [encoded_length]u8) Signature {
210 return Signature{210 return Signature{
211 .r = bytes[0 .. encoded_length / 2].*,211 .r = bytes[0..Curve.encoded_length].*,
212 .s = bytes[encoded_length / 2 ..].*,212 .s = bytes[Curve.encoded_length..].*,
213 };213 };
214 }214 }
215215
lib/std/crypto/Certificate.zig+39-1
...@@ -17,6 +17,7 @@ pub const Algorithm = enum {...@@ -17,6 +17,7 @@ pub const Algorithm = enum {
17 ecdsa_with_SHA512,17 ecdsa_with_SHA512,
18 md2WithRSAEncryption,18 md2WithRSAEncryption,
19 md5WithRSAEncryption,19 md5WithRSAEncryption,
20 curveEd25519,
2021
21 pub const map = std.ComptimeStringMap(Algorithm, .{22 pub const map = std.ComptimeStringMap(Algorithm, .{
22 .{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x05 }, .sha1WithRSAEncryption },23 .{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x05 }, .sha1WithRSAEncryption },
...@@ -30,6 +31,7 @@ pub const Algorithm = enum {...@@ -30,6 +31,7 @@ pub const Algorithm = enum {
30 .{ &[_]u8{ 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x04 }, .ecdsa_with_SHA512 },31 .{ &[_]u8{ 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x04 }, .ecdsa_with_SHA512 },
31 .{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x02 }, .md2WithRSAEncryption },32 .{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x02 }, .md2WithRSAEncryption },
32 .{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x04 }, .md5WithRSAEncryption },33 .{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x04 }, .md5WithRSAEncryption },
34 .{ &[_]u8{ 0x2B, 0x65, 0x70 }, .curveEd25519 },
33 });35 });
3436
35 pub fn Hash(comptime algorithm: Algorithm) type {37 pub fn Hash(comptime algorithm: Algorithm) type {
...@@ -38,7 +40,7 @@ pub const Algorithm = enum {...@@ -38,7 +40,7 @@ pub const Algorithm = enum {
38 .ecdsa_with_SHA224, .sha224WithRSAEncryption => crypto.hash.sha2.Sha224,40 .ecdsa_with_SHA224, .sha224WithRSAEncryption => crypto.hash.sha2.Sha224,
39 .ecdsa_with_SHA256, .sha256WithRSAEncryption => crypto.hash.sha2.Sha256,41 .ecdsa_with_SHA256, .sha256WithRSAEncryption => crypto.hash.sha2.Sha256,
40 .ecdsa_with_SHA384, .sha384WithRSAEncryption => crypto.hash.sha2.Sha384,42 .ecdsa_with_SHA384, .sha384WithRSAEncryption => crypto.hash.sha2.Sha384,
41 .ecdsa_with_SHA512, .sha512WithRSAEncryption => crypto.hash.sha2.Sha512,43 .ecdsa_with_SHA512, .sha512WithRSAEncryption, .curveEd25519 => crypto.hash.sha2.Sha512,
42 .md2WithRSAEncryption => @compileError("unimplemented"),44 .md2WithRSAEncryption => @compileError("unimplemented"),
43 .md5WithRSAEncryption => crypto.hash.Md5,45 .md5WithRSAEncryption => crypto.hash.Md5,
44 };46 };
...@@ -48,10 +50,12 @@ pub const Algorithm = enum {...@@ -48,10 +50,12 @@ pub const Algorithm = enum {
48pub const AlgorithmCategory = enum {50pub const AlgorithmCategory = enum {
49 rsaEncryption,51 rsaEncryption,
50 X9_62_id_ecPublicKey,52 X9_62_id_ecPublicKey,
53 curveEd25519,
5154
52 pub const map = std.ComptimeStringMap(AlgorithmCategory, .{55 pub const map = std.ComptimeStringMap(AlgorithmCategory, .{
53 .{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01 }, .rsaEncryption },56 .{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01 }, .rsaEncryption },
54 .{ &[_]u8{ 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01 }, .X9_62_id_ecPublicKey },57 .{ &[_]u8{ 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01 }, .X9_62_id_ecPublicKey },
58 .{ &[_]u8{ 0x2B, 0x65, 0x70 }, .curveEd25519 },
55 });59 });
56};60};
5761
...@@ -182,6 +186,7 @@ pub const Parsed = struct {...@@ -182,6 +186,7 @@ pub const Parsed = struct {
182 pub const PubKeyAlgo = union(AlgorithmCategory) {186 pub const PubKeyAlgo = union(AlgorithmCategory) {
183 rsaEncryption: void,187 rsaEncryption: void,
184 X9_62_id_ecPublicKey: NamedCurve,188 X9_62_id_ecPublicKey: NamedCurve,
189 curveEd25519: void,
185 };190 };
186191
187 pub const Validity = struct {192 pub const Validity = struct {
...@@ -287,6 +292,13 @@ pub const Parsed = struct {...@@ -287,6 +292,13 @@ pub const Parsed = struct {
287 .md2WithRSAEncryption, .md5WithRSAEncryption => {292 .md2WithRSAEncryption, .md5WithRSAEncryption => {
288 return error.CertificateSignatureAlgorithmUnsupported;293 return error.CertificateSignatureAlgorithmUnsupported;
289 },294 },
295
296 .curveEd25519 => return verifyEd25519(
297 parsed_subject.message(),
298 parsed_subject.signature(),
299 parsed_issuer.pub_key_algo,
300 parsed_issuer.pubKey(),
301 ),
290 }302 }
291 }303 }
292304
...@@ -415,6 +427,9 @@ pub fn parse(cert: Certificate) ParseError!Parsed {...@@ -415,6 +427,9 @@ pub fn parse(cert: Certificate) ParseError!Parsed {
415 const named_curve = try parseNamedCurve(cert_bytes, params_elem);427 const named_curve = try parseNamedCurve(cert_bytes, params_elem);
416 pub_key_algo = .{ .X9_62_id_ecPublicKey = named_curve };428 pub_key_algo = .{ .X9_62_id_ecPublicKey = named_curve };
417 },429 },
430 .curveEd25519 => {
431 pub_key_algo = .{ .curveEd25519 = {} };
432 },
418 }433 }
419 const pub_key_elem = try der.Element.parse(cert_bytes, pub_key_signature_algorithm.slice.end);434 const pub_key_elem = try der.Element.parse(cert_bytes, pub_key_signature_algorithm.slice.end);
420 const pub_key = try parseBitString(cert, pub_key_elem);435 const pub_key = try parseBitString(cert, pub_key_elem);
...@@ -818,6 +833,29 @@ fn verify_ecdsa(...@@ -818,6 +833,29 @@ fn verify_ecdsa(
818 }833 }
819}834}
820835
836fn verifyEd25519(
837 message: []const u8,
838 encoded_sig: []const u8,
839 pub_key_algo: Parsed.PubKeyAlgo,
840 encoded_pub_key: []const u8,
841) !void {
842 if (pub_key_algo != .curveEd25519) return error.CertificateSignatureAlgorithmMismatch;
843 const Ed25519 = crypto.sign.Ed25519;
844 if (encoded_sig.len != Ed25519.Signature.encoded_length) return error.CertificateSignatureInvalid;
845 const sig = Ed25519.Signature.fromBytes(encoded_sig[0..Ed25519.Signature.encoded_length].*);
846 if (encoded_pub_key.len != Ed25519.PublicKey.encoded_length) return error.CertificateSignatureInvalid;
847 const pub_key = Ed25519.PublicKey.fromBytes(encoded_pub_key[0..Ed25519.PublicKey.encoded_length].*) catch |err| switch (err) {
848 error.NonCanonical => return error.CertificateSignatureInvalid,
849 };
850 sig.verify(message, pub_key) catch |err| switch (err) {
851 error.IdentityElement => return error.CertificateSignatureInvalid,
852 error.NonCanonical => return error.CertificateSignatureInvalid,
853 error.SignatureVerificationFailed => return error.CertificateSignatureInvalid,
854 error.InvalidEncoding => return error.CertificateSignatureInvalid,
855 error.WeakPublicKey => return error.CertificateSignatureInvalid,
856 };
857}
858
821const std = @import("../std.zig");859const std = @import("../std.zig");
822const crypto = std.crypto;860const crypto = std.crypto;
823const mem = std.mem;861const mem = std.mem;
lib/std/crypto/tls/Client.zig+17-5
...@@ -132,6 +132,7 @@ pub fn InitError(comptime Stream: type) type {...@@ -132,6 +132,7 @@ pub fn InitError(comptime Stream: type) type {
132 InvalidSignature,132 InvalidSignature,
133 NotSquare,133 NotSquare,
134 NonCanonical,134 NonCanonical,
135 WeakPublicKey,
135 };136 };
136}137}
137138
...@@ -166,13 +167,9 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In...@@ -166,13 +167,9 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In
166 }) ++ tls.extension(.signature_algorithms, enum_array(tls.SignatureScheme, &.{167 }) ++ tls.extension(.signature_algorithms, enum_array(tls.SignatureScheme, &.{
167 .ecdsa_secp256r1_sha256,168 .ecdsa_secp256r1_sha256,
168 .ecdsa_secp384r1_sha384,169 .ecdsa_secp384r1_sha384,
169 .ecdsa_secp521r1_sha512,
170 .rsa_pss_rsae_sha256,170 .rsa_pss_rsae_sha256,
171 .rsa_pss_rsae_sha384,171 .rsa_pss_rsae_sha384,
172 .rsa_pss_rsae_sha512,172 .rsa_pss_rsae_sha512,
173 .rsa_pkcs1_sha256,
174 .rsa_pkcs1_sha384,
175 .rsa_pkcs1_sha512,
176 .ed25519,173 .ed25519,
177 })) ++ tls.extension(.supported_groups, enum_array(tls.NamedGroup, &.{174 })) ++ tls.extension(.supported_groups, enum_array(tls.NamedGroup, &.{
178 .x25519_kyber768d00,175 .x25519_kyber768d00,
...@@ -618,6 +615,15 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In...@@ -618,6 +615,15 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In
618 },615 },
619 }616 }
620 },617 },
618 inline .ed25519 => |comptime_scheme| {
619 if (main_cert_pub_key_algo != .curveEd25519) return error.TlsBadSignatureScheme;
620 const Eddsa = SchemeEddsa(comptime_scheme);
621 if (encoded_sig.len != Eddsa.Signature.encoded_length) return error.InvalidEncoding;
622 const sig = Eddsa.Signature.fromBytes(encoded_sig[0..Eddsa.Signature.encoded_length].*);
623 if (main_cert_pub_key.len != Eddsa.PublicKey.encoded_length) return error.InvalidEncoding;
624 const key = try Eddsa.PublicKey.fromBytes(main_cert_pub_key[0..Eddsa.PublicKey.encoded_length].*);
625 try sig.verify(verify_bytes, key);
626 },
621 else => {627 else => {
622 return error.TlsBadSignatureScheme;628 return error.TlsBadSignatureScheme;
623 },629 },
...@@ -1297,7 +1303,6 @@ fn SchemeEcdsa(comptime scheme: tls.SignatureScheme) type {...@@ -1297,7 +1303,6 @@ fn SchemeEcdsa(comptime scheme: tls.SignatureScheme) type {
1297 return switch (scheme) {1303 return switch (scheme) {
1298 .ecdsa_secp256r1_sha256 => crypto.sign.ecdsa.EcdsaP256Sha256,1304 .ecdsa_secp256r1_sha256 => crypto.sign.ecdsa.EcdsaP256Sha256,
1299 .ecdsa_secp384r1_sha384 => crypto.sign.ecdsa.EcdsaP384Sha384,1305 .ecdsa_secp384r1_sha384 => crypto.sign.ecdsa.EcdsaP384Sha384,
1300 .ecdsa_secp521r1_sha512 => crypto.sign.ecdsa.EcdsaP512Sha512,
1301 else => @compileError("bad scheme"),1306 else => @compileError("bad scheme"),
1302 };1307 };
1303}1308}
...@@ -1311,6 +1316,13 @@ fn SchemeHash(comptime scheme: tls.SignatureScheme) type {...@@ -1311,6 +1316,13 @@ fn SchemeHash(comptime scheme: tls.SignatureScheme) type {
1311 };1316 };
1312}1317}
13131318
1319fn SchemeEddsa(comptime scheme: tls.SignatureScheme) type {
1320 return switch (scheme) {
1321 .ed25519 => crypto.sign.Ed25519,
1322 else => @compileError("bad scheme"),
1323 };
1324}
1325
1314/// Abstraction for sending multiple byte buffers to a slice of iovecs.1326/// Abstraction for sending multiple byte buffers to a slice of iovecs.
1315const VecPut = struct {1327const VecPut = struct {
1316 iovecs: []const std.os.iovec,1328 iovecs: []const std.os.iovec,