mirror of
https://github.com/ziglang/zig.git
synced 2025-12-06 06:13:07 +00:00
std.crypto.tls: implement TLSv1.2
This commit is contained in:
parent
ee9f00d673
commit
c2a779ae79
@ -151,7 +151,9 @@ pub const Ed25519 = struct {
|
|||||||
a: Curve,
|
a: Curve,
|
||||||
expected_r: Curve,
|
expected_r: Curve,
|
||||||
|
|
||||||
fn init(sig: Signature, public_key: PublicKey) (NonCanonicalError || EncodingError || IdentityElementError)!Verifier {
|
pub const InitError = NonCanonicalError || EncodingError || IdentityElementError;
|
||||||
|
|
||||||
|
fn init(sig: Signature, public_key: PublicKey) InitError!Verifier {
|
||||||
const r = sig.r;
|
const r = sig.r;
|
||||||
const s = sig.s;
|
const s = sig.s;
|
||||||
try Curve.scalar.rejectNonCanonical(s);
|
try Curve.scalar.rejectNonCanonical(s);
|
||||||
@ -173,8 +175,11 @@ pub const Ed25519 = struct {
|
|||||||
self.h.update(msg);
|
self.h.update(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub const VerifyError = WeakPublicKeyError || IdentityElementError ||
|
||||||
|
SignatureVerificationError;
|
||||||
|
|
||||||
/// Verify that the signature is valid for the entire message.
|
/// Verify that the signature is valid for the entire message.
|
||||||
pub fn verify(self: *Verifier) (SignatureVerificationError || WeakPublicKeyError || IdentityElementError)!void {
|
pub fn verify(self: *Verifier) VerifyError!void {
|
||||||
var hram64: [Sha512.digest_length]u8 = undefined;
|
var hram64: [Sha512.digest_length]u8 = undefined;
|
||||||
self.h.final(&hram64);
|
self.h.final(&hram64);
|
||||||
const hram = Curve.scalar.reduce64(hram64);
|
const hram = Curve.scalar.reduce64(hram64);
|
||||||
@ -197,10 +202,10 @@ pub const Ed25519 = struct {
|
|||||||
s: CompressedScalar,
|
s: CompressedScalar,
|
||||||
|
|
||||||
/// Return the raw signature (r, s) in little-endian format.
|
/// Return the raw signature (r, s) in little-endian format.
|
||||||
pub fn toBytes(self: Signature) [encoded_length]u8 {
|
pub fn toBytes(sig: Signature) [encoded_length]u8 {
|
||||||
var bytes: [encoded_length]u8 = undefined;
|
var bytes: [encoded_length]u8 = undefined;
|
||||||
bytes[0..Curve.encoded_length].* = self.r;
|
bytes[0..Curve.encoded_length].* = sig.r;
|
||||||
bytes[Curve.encoded_length..].* = self.s;
|
bytes[Curve.encoded_length..].* = sig.s;
|
||||||
return bytes;
|
return bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -214,17 +219,26 @@ pub const Ed25519 = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Create a Verifier for incremental verification of a signature.
|
/// Create a Verifier for incremental verification of a signature.
|
||||||
pub fn verifier(self: Signature, public_key: PublicKey) (NonCanonicalError || EncodingError || IdentityElementError)!Verifier {
|
pub fn verifier(sig: Signature, public_key: PublicKey) Verifier.InitError!Verifier {
|
||||||
return Verifier.init(self, public_key);
|
return Verifier.init(sig, public_key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub const VerifyError = Verifier.InitError || Verifier.VerifyError;
|
||||||
|
|
||||||
/// Verify the signature against a message and public key.
|
/// Verify the signature against a message and public key.
|
||||||
/// Return IdentityElement or NonCanonical if the public key or signature are not in the expected range,
|
/// Return IdentityElement or NonCanonical if the public key or signature are not in the expected range,
|
||||||
/// or SignatureVerificationError if the signature is invalid for the given message and key.
|
/// or SignatureVerificationError if the signature is invalid for the given message and key.
|
||||||
pub fn verify(self: Signature, msg: []const u8, public_key: PublicKey) (IdentityElementError || NonCanonicalError || SignatureVerificationError || EncodingError || WeakPublicKeyError)!void {
|
pub fn verify(sig: Signature, msg: []const u8, public_key: PublicKey) VerifyError!void {
|
||||||
var st = try Verifier.init(self, public_key);
|
try sig.concatVerify(&.{msg}, public_key);
|
||||||
st.update(msg);
|
}
|
||||||
return st.verify();
|
|
||||||
|
/// Verify the signature against a concatenated message and public key.
|
||||||
|
/// Return IdentityElement or NonCanonical if the public key or signature are not in the expected range,
|
||||||
|
/// or SignatureVerificationError if the signature is invalid for the given message and key.
|
||||||
|
pub fn concatVerify(sig: Signature, msg: []const []const u8, public_key: PublicKey) VerifyError!void {
|
||||||
|
var st = try Verifier.init(sig, public_key);
|
||||||
|
for (msg) |part| st.update(part);
|
||||||
|
try st.verify();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -20,18 +20,18 @@ pub const Algorithm = enum {
|
|||||||
curveEd25519,
|
curveEd25519,
|
||||||
|
|
||||||
pub const map = std.StaticStringMap(Algorithm).initComptime(.{
|
pub const map = std.StaticStringMap(Algorithm).initComptime(.{
|
||||||
.{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x05 }, .sha1WithRSAEncryption },
|
.{ &.{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x05 }, .sha1WithRSAEncryption },
|
||||||
.{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B }, .sha256WithRSAEncryption },
|
.{ &.{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B }, .sha256WithRSAEncryption },
|
||||||
.{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0C }, .sha384WithRSAEncryption },
|
.{ &.{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0C }, .sha384WithRSAEncryption },
|
||||||
.{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0D }, .sha512WithRSAEncryption },
|
.{ &.{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0D }, .sha512WithRSAEncryption },
|
||||||
.{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0E }, .sha224WithRSAEncryption },
|
.{ &.{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0E }, .sha224WithRSAEncryption },
|
||||||
.{ &[_]u8{ 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x01 }, .ecdsa_with_SHA224 },
|
.{ &.{ 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x01 }, .ecdsa_with_SHA224 },
|
||||||
.{ &[_]u8{ 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x02 }, .ecdsa_with_SHA256 },
|
.{ &.{ 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x02 }, .ecdsa_with_SHA256 },
|
||||||
.{ &[_]u8{ 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x03 }, .ecdsa_with_SHA384 },
|
.{ &.{ 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x03 }, .ecdsa_with_SHA384 },
|
||||||
.{ &[_]u8{ 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x04 }, .ecdsa_with_SHA512 },
|
.{ &.{ 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x04 }, .ecdsa_with_SHA512 },
|
||||||
.{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x02 }, .md2WithRSAEncryption },
|
.{ &.{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x02 }, .md2WithRSAEncryption },
|
||||||
.{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x04 }, .md5WithRSAEncryption },
|
.{ &.{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x04 }, .md5WithRSAEncryption },
|
||||||
.{ &[_]u8{ 0x2B, 0x65, 0x70 }, .curveEd25519 },
|
.{ &.{ 0x2B, 0x65, 0x70 }, .curveEd25519 },
|
||||||
});
|
});
|
||||||
|
|
||||||
pub fn Hash(comptime algorithm: Algorithm) type {
|
pub fn Hash(comptime algorithm: Algorithm) type {
|
||||||
@ -49,13 +49,15 @@ pub const Algorithm = enum {
|
|||||||
|
|
||||||
pub const AlgorithmCategory = enum {
|
pub const AlgorithmCategory = enum {
|
||||||
rsaEncryption,
|
rsaEncryption,
|
||||||
|
rsassa_pss,
|
||||||
X9_62_id_ecPublicKey,
|
X9_62_id_ecPublicKey,
|
||||||
curveEd25519,
|
curveEd25519,
|
||||||
|
|
||||||
pub const map = std.StaticStringMap(AlgorithmCategory).initComptime(.{
|
pub const map = std.StaticStringMap(AlgorithmCategory).initComptime(.{
|
||||||
.{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01 }, .rsaEncryption },
|
.{ &.{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01 }, .rsaEncryption },
|
||||||
.{ &[_]u8{ 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01 }, .X9_62_id_ecPublicKey },
|
.{ &.{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0A }, .rsassa_pss },
|
||||||
.{ &[_]u8{ 0x2B, 0x65, 0x70 }, .curveEd25519 },
|
.{ &.{ 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01 }, .X9_62_id_ecPublicKey },
|
||||||
|
.{ &.{ 0x2B, 0x65, 0x70 }, .curveEd25519 },
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -74,18 +76,18 @@ pub const Attribute = enum {
|
|||||||
domainComponent,
|
domainComponent,
|
||||||
|
|
||||||
pub const map = std.StaticStringMap(Attribute).initComptime(.{
|
pub const map = std.StaticStringMap(Attribute).initComptime(.{
|
||||||
.{ &[_]u8{ 0x55, 0x04, 0x03 }, .commonName },
|
.{ &.{ 0x55, 0x04, 0x03 }, .commonName },
|
||||||
.{ &[_]u8{ 0x55, 0x04, 0x05 }, .serialNumber },
|
.{ &.{ 0x55, 0x04, 0x05 }, .serialNumber },
|
||||||
.{ &[_]u8{ 0x55, 0x04, 0x06 }, .countryName },
|
.{ &.{ 0x55, 0x04, 0x06 }, .countryName },
|
||||||
.{ &[_]u8{ 0x55, 0x04, 0x07 }, .localityName },
|
.{ &.{ 0x55, 0x04, 0x07 }, .localityName },
|
||||||
.{ &[_]u8{ 0x55, 0x04, 0x08 }, .stateOrProvinceName },
|
.{ &.{ 0x55, 0x04, 0x08 }, .stateOrProvinceName },
|
||||||
.{ &[_]u8{ 0x55, 0x04, 0x09 }, .streetAddress },
|
.{ &.{ 0x55, 0x04, 0x09 }, .streetAddress },
|
||||||
.{ &[_]u8{ 0x55, 0x04, 0x0A }, .organizationName },
|
.{ &.{ 0x55, 0x04, 0x0A }, .organizationName },
|
||||||
.{ &[_]u8{ 0x55, 0x04, 0x0B }, .organizationalUnitName },
|
.{ &.{ 0x55, 0x04, 0x0B }, .organizationalUnitName },
|
||||||
.{ &[_]u8{ 0x55, 0x04, 0x11 }, .postalCode },
|
.{ &.{ 0x55, 0x04, 0x11 }, .postalCode },
|
||||||
.{ &[_]u8{ 0x55, 0x04, 0x61 }, .organizationIdentifier },
|
.{ &.{ 0x55, 0x04, 0x61 }, .organizationIdentifier },
|
||||||
.{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x09, 0x01 }, .pkcs9_emailAddress },
|
.{ &.{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x09, 0x01 }, .pkcs9_emailAddress },
|
||||||
.{ &[_]u8{ 0x09, 0x92, 0x26, 0x89, 0x93, 0xF2, 0x2C, 0x64, 0x01, 0x19 }, .domainComponent },
|
.{ &.{ 0x09, 0x92, 0x26, 0x89, 0x93, 0xF2, 0x2C, 0x64, 0x01, 0x19 }, .domainComponent },
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -95,9 +97,9 @@ pub const NamedCurve = enum {
|
|||||||
X9_62_prime256v1,
|
X9_62_prime256v1,
|
||||||
|
|
||||||
pub const map = std.StaticStringMap(NamedCurve).initComptime(.{
|
pub const map = std.StaticStringMap(NamedCurve).initComptime(.{
|
||||||
.{ &[_]u8{ 0x2B, 0x81, 0x04, 0x00, 0x22 }, .secp384r1 },
|
.{ &.{ 0x2B, 0x81, 0x04, 0x00, 0x22 }, .secp384r1 },
|
||||||
.{ &[_]u8{ 0x2B, 0x81, 0x04, 0x00, 0x23 }, .secp521r1 },
|
.{ &.{ 0x2B, 0x81, 0x04, 0x00, 0x23 }, .secp521r1 },
|
||||||
.{ &[_]u8{ 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07 }, .X9_62_prime256v1 },
|
.{ &.{ 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07 }, .X9_62_prime256v1 },
|
||||||
});
|
});
|
||||||
|
|
||||||
pub fn Curve(comptime curve: NamedCurve) type {
|
pub fn Curve(comptime curve: NamedCurve) type {
|
||||||
@ -131,28 +133,28 @@ pub const ExtensionId = enum {
|
|||||||
netscape_comment,
|
netscape_comment,
|
||||||
|
|
||||||
pub const map = std.StaticStringMap(ExtensionId).initComptime(.{
|
pub const map = std.StaticStringMap(ExtensionId).initComptime(.{
|
||||||
.{ &[_]u8{ 0x55, 0x04, 0x03 }, .commonName },
|
.{ &.{ 0x55, 0x04, 0x03 }, .commonName },
|
||||||
.{ &[_]u8{ 0x55, 0x1D, 0x01 }, .authority_key_identifier },
|
.{ &.{ 0x55, 0x1D, 0x01 }, .authority_key_identifier },
|
||||||
.{ &[_]u8{ 0x55, 0x1D, 0x07 }, .subject_alt_name },
|
.{ &.{ 0x55, 0x1D, 0x07 }, .subject_alt_name },
|
||||||
.{ &[_]u8{ 0x55, 0x1D, 0x0E }, .subject_key_identifier },
|
.{ &.{ 0x55, 0x1D, 0x0E }, .subject_key_identifier },
|
||||||
.{ &[_]u8{ 0x55, 0x1D, 0x0F }, .key_usage },
|
.{ &.{ 0x55, 0x1D, 0x0F }, .key_usage },
|
||||||
.{ &[_]u8{ 0x55, 0x1D, 0x0A }, .basic_constraints },
|
.{ &.{ 0x55, 0x1D, 0x0A }, .basic_constraints },
|
||||||
.{ &[_]u8{ 0x55, 0x1D, 0x10 }, .private_key_usage_period },
|
.{ &.{ 0x55, 0x1D, 0x10 }, .private_key_usage_period },
|
||||||
.{ &[_]u8{ 0x55, 0x1D, 0x11 }, .subject_alt_name },
|
.{ &.{ 0x55, 0x1D, 0x11 }, .subject_alt_name },
|
||||||
.{ &[_]u8{ 0x55, 0x1D, 0x12 }, .issuer_alt_name },
|
.{ &.{ 0x55, 0x1D, 0x12 }, .issuer_alt_name },
|
||||||
.{ &[_]u8{ 0x55, 0x1D, 0x13 }, .basic_constraints },
|
.{ &.{ 0x55, 0x1D, 0x13 }, .basic_constraints },
|
||||||
.{ &[_]u8{ 0x55, 0x1D, 0x14 }, .crl_number },
|
.{ &.{ 0x55, 0x1D, 0x14 }, .crl_number },
|
||||||
.{ &[_]u8{ 0x55, 0x1D, 0x1F }, .crl_distribution_points },
|
.{ &.{ 0x55, 0x1D, 0x1F }, .crl_distribution_points },
|
||||||
.{ &[_]u8{ 0x55, 0x1D, 0x20 }, .certificate_policies },
|
.{ &.{ 0x55, 0x1D, 0x20 }, .certificate_policies },
|
||||||
.{ &[_]u8{ 0x55, 0x1D, 0x23 }, .authority_key_identifier },
|
.{ &.{ 0x55, 0x1D, 0x23 }, .authority_key_identifier },
|
||||||
.{ &[_]u8{ 0x55, 0x1D, 0x25 }, .ext_key_usage },
|
.{ &.{ 0x55, 0x1D, 0x25 }, .ext_key_usage },
|
||||||
.{ &[_]u8{ 0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x15, 0x01 }, .msCertsrvCAVersion },
|
.{ &.{ 0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x15, 0x01 }, .msCertsrvCAVersion },
|
||||||
.{ &[_]u8{ 0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01 }, .info_access },
|
.{ &.{ 0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01 }, .info_access },
|
||||||
.{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF6, 0x7D, 0x07, 0x41, 0x00 }, .entrustVersInfo },
|
.{ &.{ 0x2A, 0x86, 0x48, 0x86, 0xF6, 0x7D, 0x07, 0x41, 0x00 }, .entrustVersInfo },
|
||||||
.{ &[_]u8{ 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x14, 0x02 }, .enroll_certtype },
|
.{ &.{ 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x14, 0x02 }, .enroll_certtype },
|
||||||
.{ &[_]u8{ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x0c }, .pe_logotype },
|
.{ &.{ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x0c }, .pe_logotype },
|
||||||
.{ &[_]u8{ 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x01, 0x01 }, .netscape_cert_type },
|
.{ &.{ 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x01, 0x01 }, .netscape_cert_type },
|
||||||
.{ &[_]u8{ 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x01, 0x0d }, .netscape_comment },
|
.{ &.{ 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x01, 0x0d }, .netscape_comment },
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -185,6 +187,7 @@ pub const Parsed = struct {
|
|||||||
|
|
||||||
pub const PubKeyAlgo = union(AlgorithmCategory) {
|
pub const PubKeyAlgo = union(AlgorithmCategory) {
|
||||||
rsaEncryption: void,
|
rsaEncryption: void,
|
||||||
|
rsassa_pss: void,
|
||||||
X9_62_id_ecPublicKey: NamedCurve,
|
X9_62_id_ecPublicKey: NamedCurve,
|
||||||
curveEd25519: void,
|
curveEd25519: void,
|
||||||
};
|
};
|
||||||
@ -386,7 +389,7 @@ test "Parsed.checkHostName" {
|
|||||||
try expectEqual(true, Parsed.checkHostName("bar.ziglang.org", "*.Ziglang.ORG"));
|
try expectEqual(true, Parsed.checkHostName("bar.ziglang.org", "*.Ziglang.ORG"));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const ParseError = der.Element.ParseElementError || ParseVersionError || ParseTimeError || ParseEnumError || ParseBitStringError;
|
pub const ParseError = der.Element.ParseError || ParseVersionError || ParseTimeError || ParseEnumError || ParseBitStringError;
|
||||||
|
|
||||||
pub fn parse(cert: Certificate) ParseError!Parsed {
|
pub fn parse(cert: Certificate) ParseError!Parsed {
|
||||||
const cert_bytes = cert.buffer;
|
const cert_bytes = cert.buffer;
|
||||||
@ -413,13 +416,9 @@ pub fn parse(cert: Certificate) ParseError!Parsed {
|
|||||||
const pub_key_info = try der.Element.parse(cert_bytes, subject.slice.end);
|
const pub_key_info = try der.Element.parse(cert_bytes, subject.slice.end);
|
||||||
const pub_key_signature_algorithm = try der.Element.parse(cert_bytes, pub_key_info.slice.start);
|
const pub_key_signature_algorithm = try der.Element.parse(cert_bytes, pub_key_info.slice.start);
|
||||||
const pub_key_algo_elem = try der.Element.parse(cert_bytes, pub_key_signature_algorithm.slice.start);
|
const pub_key_algo_elem = try der.Element.parse(cert_bytes, pub_key_signature_algorithm.slice.start);
|
||||||
const pub_key_algo_tag = try parseAlgorithmCategory(cert_bytes, pub_key_algo_elem);
|
const pub_key_algo: Parsed.PubKeyAlgo = switch (try parseAlgorithmCategory(cert_bytes, pub_key_algo_elem)) {
|
||||||
var pub_key_algo: Parsed.PubKeyAlgo = undefined;
|
inline else => |tag| @unionInit(Parsed.PubKeyAlgo, @tagName(tag), {}),
|
||||||
switch (pub_key_algo_tag) {
|
.X9_62_id_ecPublicKey => pub_key_algo: {
|
||||||
.rsaEncryption => {
|
|
||||||
pub_key_algo = .{ .rsaEncryption = {} };
|
|
||||||
},
|
|
||||||
.X9_62_id_ecPublicKey => {
|
|
||||||
// RFC 5480 Section 2.1.1.1 Named Curve
|
// RFC 5480 Section 2.1.1.1 Named Curve
|
||||||
// ECParameters ::= CHOICE {
|
// ECParameters ::= CHOICE {
|
||||||
// namedCurve OBJECT IDENTIFIER
|
// namedCurve OBJECT IDENTIFIER
|
||||||
@ -428,12 +427,9 @@ pub fn parse(cert: Certificate) ParseError!Parsed {
|
|||||||
// }
|
// }
|
||||||
const params_elem = try der.Element.parse(cert_bytes, pub_key_algo_elem.slice.end);
|
const params_elem = try der.Element.parse(cert_bytes, pub_key_algo_elem.slice.end);
|
||||||
const named_curve = try parseNamedCurve(cert_bytes, params_elem);
|
const named_curve = try parseNamedCurve(cert_bytes, params_elem);
|
||||||
pub_key_algo = .{ .X9_62_id_ecPublicKey = named_curve };
|
break :pub_key_algo .{ .X9_62_id_ecPublicKey = named_curve };
|
||||||
},
|
},
|
||||||
.curveEd25519 => {
|
};
|
||||||
pub_key_algo = .{ .curveEd25519 = {} };
|
|
||||||
},
|
|
||||||
}
|
|
||||||
const pub_key_elem = try der.Element.parse(cert_bytes, pub_key_signature_algorithm.slice.end);
|
const pub_key_elem = try der.Element.parse(cert_bytes, pub_key_signature_algorithm.slice.end);
|
||||||
const pub_key = try parseBitString(cert, pub_key_elem);
|
const pub_key = try parseBitString(cert, pub_key_elem);
|
||||||
|
|
||||||
@ -731,7 +727,7 @@ pub fn parseVersion(bytes: []const u8, version_elem: der.Element) ParseVersionEr
|
|||||||
|
|
||||||
fn verifyRsa(
|
fn verifyRsa(
|
||||||
comptime Hash: type,
|
comptime Hash: type,
|
||||||
message: []const u8,
|
msg: []const u8,
|
||||||
sig: []const u8,
|
sig: []const u8,
|
||||||
pub_key_algo: Parsed.PubKeyAlgo,
|
pub_key_algo: Parsed.PubKeyAlgo,
|
||||||
pub_key: []const u8,
|
pub_key: []const u8,
|
||||||
@ -743,59 +739,14 @@ fn verifyRsa(
|
|||||||
if (exponent.len > modulus.len) return error.CertificatePublicKeyInvalid;
|
if (exponent.len > modulus.len) return error.CertificatePublicKeyInvalid;
|
||||||
if (sig.len != modulus.len) return error.CertificateSignatureInvalidLength;
|
if (sig.len != modulus.len) return error.CertificateSignatureInvalidLength;
|
||||||
|
|
||||||
const hash_der = switch (Hash) {
|
|
||||||
crypto.hash.Sha1 => [_]u8{
|
|
||||||
0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e,
|
|
||||||
0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14,
|
|
||||||
},
|
|
||||||
crypto.hash.sha2.Sha224 => [_]u8{
|
|
||||||
0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
|
|
||||||
0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04, 0x05,
|
|
||||||
0x00, 0x04, 0x1c,
|
|
||||||
},
|
|
||||||
crypto.hash.sha2.Sha256 => [_]u8{
|
|
||||||
0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
|
|
||||||
0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
|
|
||||||
0x00, 0x04, 0x20,
|
|
||||||
},
|
|
||||||
crypto.hash.sha2.Sha384 => [_]u8{
|
|
||||||
0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
|
|
||||||
0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05,
|
|
||||||
0x00, 0x04, 0x30,
|
|
||||||
},
|
|
||||||
crypto.hash.sha2.Sha512 => [_]u8{
|
|
||||||
0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
|
|
||||||
0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
|
|
||||||
0x00, 0x04, 0x40,
|
|
||||||
},
|
|
||||||
else => @compileError("unreachable"),
|
|
||||||
};
|
|
||||||
|
|
||||||
var msg_hashed: [Hash.digest_length]u8 = undefined;
|
|
||||||
Hash.hash(message, &msg_hashed, .{});
|
|
||||||
|
|
||||||
switch (modulus.len) {
|
switch (modulus.len) {
|
||||||
inline 128, 256, 384, 512 => |modulus_len| {
|
inline 128, 256, 384, 512 => |modulus_len| {
|
||||||
const ps_len = modulus_len - (hash_der.len + msg_hashed.len) - 3;
|
const public_key = rsa.PublicKey.fromBytes(exponent, modulus) catch
|
||||||
const em: [modulus_len]u8 =
|
return error.CertificateSignatureInvalid;
|
||||||
[2]u8{ 0, 1 } ++
|
rsa.PKCS1v1_5Signature.verify(modulus_len, sig[0..modulus_len].*, msg, public_key, Hash) catch
|
||||||
([1]u8{0xff} ** ps_len) ++
|
|
||||||
[1]u8{0} ++
|
|
||||||
hash_der ++
|
|
||||||
msg_hashed;
|
|
||||||
|
|
||||||
const public_key = rsa.PublicKey.fromBytes(exponent, modulus) catch return error.CertificateSignatureInvalid;
|
|
||||||
const em_dec = rsa.encrypt(modulus_len, sig[0..modulus_len].*, public_key) catch |err| switch (err) {
|
|
||||||
error.MessageTooLong => unreachable,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!mem.eql(u8, &em, &em_dec)) {
|
|
||||||
return error.CertificateSignatureInvalid;
|
return error.CertificateSignatureInvalid;
|
||||||
}
|
|
||||||
},
|
|
||||||
else => {
|
|
||||||
return error.CertificateSignatureUnsupportedBitCount;
|
|
||||||
},
|
},
|
||||||
|
else => return error.CertificateSignatureUnsupportedBitCount,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -908,9 +859,9 @@ pub const der = struct {
|
|||||||
pub const empty: Slice = .{ .start = 0, .end = 0 };
|
pub const empty: Slice = .{ .start = 0, .end = 0 };
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const ParseElementError = error{CertificateFieldHasInvalidLength};
|
pub const ParseError = error{CertificateFieldHasInvalidLength};
|
||||||
|
|
||||||
pub fn parse(bytes: []const u8, index: u32) ParseElementError!Element {
|
pub fn parse(bytes: []const u8, index: u32) Element.ParseError!Element {
|
||||||
var i = index;
|
var i = index;
|
||||||
const identifier = @as(Identifier, @bitCast(bytes[i]));
|
const identifier = @as(Identifier, @bitCast(bytes[i]));
|
||||||
i += 1;
|
i += 1;
|
||||||
@ -958,21 +909,41 @@ pub const rsa = struct {
|
|||||||
const Modulus = std.crypto.ff.Modulus(max_modulus_bits);
|
const Modulus = std.crypto.ff.Modulus(max_modulus_bits);
|
||||||
const Fe = Modulus.Fe;
|
const Fe = Modulus.Fe;
|
||||||
|
|
||||||
|
/// RFC 3447 8.1 RSASSA-PSS
|
||||||
pub const PSSSignature = struct {
|
pub const PSSSignature = struct {
|
||||||
pub fn fromBytes(comptime modulus_len: usize, msg: []const u8) [modulus_len]u8 {
|
pub fn fromBytes(comptime modulus_len: usize, msg: []const u8) [modulus_len]u8 {
|
||||||
var result = [1]u8{0} ** modulus_len;
|
var result: [modulus_len]u8 = undefined;
|
||||||
std.mem.copyForwards(u8, &result, msg);
|
@memcpy(result[0..msg.len], msg);
|
||||||
|
@memset(result[msg.len..], 0);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn verify(comptime modulus_len: usize, sig: [modulus_len]u8, msg: []const u8, public_key: PublicKey, comptime Hash: type) !void {
|
pub const VerifyError = EncryptError || error{InvalidSignature};
|
||||||
|
|
||||||
|
pub fn verify(
|
||||||
|
comptime modulus_len: usize,
|
||||||
|
sig: [modulus_len]u8,
|
||||||
|
msg: []const u8,
|
||||||
|
public_key: PublicKey,
|
||||||
|
comptime Hash: type,
|
||||||
|
) VerifyError!void {
|
||||||
|
try concatVerify(modulus_len, sig, &.{msg}, public_key, Hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn concatVerify(
|
||||||
|
comptime modulus_len: usize,
|
||||||
|
sig: [modulus_len]u8,
|
||||||
|
msg: []const []const u8,
|
||||||
|
public_key: PublicKey,
|
||||||
|
comptime Hash: type,
|
||||||
|
) VerifyError!void {
|
||||||
const mod_bits = public_key.n.bits();
|
const mod_bits = public_key.n.bits();
|
||||||
const em_dec = try encrypt(modulus_len, sig, public_key);
|
const em_dec = try encrypt(modulus_len, sig, public_key);
|
||||||
|
|
||||||
EMSA_PSS_VERIFY(msg, &em_dec, mod_bits - 1, Hash.digest_length, Hash) catch unreachable;
|
try EMSA_PSS_VERIFY(msg, &em_dec, mod_bits - 1, Hash.digest_length, Hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn EMSA_PSS_VERIFY(msg: []const u8, em: []const u8, emBit: usize, sLen: usize, comptime Hash: type) !void {
|
fn EMSA_PSS_VERIFY(msg: []const []const u8, em: []const u8, emBit: usize, sLen: usize, comptime Hash: type) VerifyError!void {
|
||||||
// 1. If the length of M is greater than the input limitation for
|
// 1. If the length of M is greater than the input limitation for
|
||||||
// the hash function (2^61 - 1 octets for SHA-1), output
|
// the hash function (2^61 - 1 octets for SHA-1), output
|
||||||
// "inconsistent" and stop.
|
// "inconsistent" and stop.
|
||||||
@ -986,7 +957,11 @@ pub const rsa = struct {
|
|||||||
|
|
||||||
// 2. Let mHash = Hash(M), an octet string of length hLen.
|
// 2. Let mHash = Hash(M), an octet string of length hLen.
|
||||||
var mHash: [Hash.digest_length]u8 = undefined;
|
var mHash: [Hash.digest_length]u8 = undefined;
|
||||||
Hash.hash(msg, &mHash, .{});
|
{
|
||||||
|
var hasher: Hash = .init(.{});
|
||||||
|
for (msg) |part| hasher.update(part);
|
||||||
|
hasher.final(&mHash);
|
||||||
|
}
|
||||||
|
|
||||||
// 3. If emLen < hLen + sLen + 2, output "inconsistent" and stop.
|
// 3. If emLen < hLen + sLen + 2, output "inconsistent" and stop.
|
||||||
if (emLen < Hash.digest_length + sLen + 2) {
|
if (emLen < Hash.digest_length + sLen + 2) {
|
||||||
@ -1082,25 +1057,14 @@ pub const rsa = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn MGF1(comptime Hash: type, out: []u8, seed: *const [Hash.digest_length]u8, len: usize) ![]u8 {
|
fn MGF1(comptime Hash: type, out: []u8, seed: *const [Hash.digest_length]u8, len: usize) ![]u8 {
|
||||||
var counter: usize = 0;
|
var counter: u32 = 0;
|
||||||
var idx: usize = 0;
|
var idx: usize = 0;
|
||||||
var c: [4]u8 = undefined;
|
var hash = seed.* ++ @as([4]u8, undefined);
|
||||||
var hash: [Hash.digest_length + c.len]u8 = undefined;
|
|
||||||
@memcpy(hash[0..Hash.digest_length], seed);
|
|
||||||
var hashed: [Hash.digest_length]u8 = undefined;
|
|
||||||
|
|
||||||
while (idx < len) {
|
while (idx < len) {
|
||||||
c[0] = @as(u8, @intCast((counter >> 24) & 0xFF));
|
std.mem.writeInt(u32, hash[seed.len..][0..4], counter, .big);
|
||||||
c[1] = @as(u8, @intCast((counter >> 16) & 0xFF));
|
Hash.hash(&hash, out[idx..][0..Hash.digest_length], .{});
|
||||||
c[2] = @as(u8, @intCast((counter >> 8) & 0xFF));
|
idx += Hash.digest_length;
|
||||||
c[3] = @as(u8, @intCast(counter & 0xFF));
|
|
||||||
|
|
||||||
std.mem.copyForwards(u8, hash[seed.len..], &c);
|
|
||||||
Hash.hash(&hash, &hashed, .{});
|
|
||||||
|
|
||||||
std.mem.copyForwards(u8, out[idx..], &hashed);
|
|
||||||
idx += hashed.len;
|
|
||||||
|
|
||||||
counter += 1;
|
counter += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1108,11 +1072,128 @@ pub const rsa = struct {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// RFC 3447 8.2 RSASSA-PKCS1-v1_5
|
||||||
|
pub const PKCS1v1_5Signature = struct {
|
||||||
|
pub fn fromBytes(comptime modulus_len: usize, msg: []const u8) [modulus_len]u8 {
|
||||||
|
var result: [modulus_len]u8 = undefined;
|
||||||
|
@memcpy(result[0..msg.len], msg);
|
||||||
|
@memset(result[msg.len..], 0);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const VerifyError = EncryptError || error{InvalidSignature};
|
||||||
|
|
||||||
|
pub fn verify(
|
||||||
|
comptime modulus_len: usize,
|
||||||
|
sig: [modulus_len]u8,
|
||||||
|
msg: []const u8,
|
||||||
|
public_key: PublicKey,
|
||||||
|
comptime Hash: type,
|
||||||
|
) VerifyError!void {
|
||||||
|
try concatVerify(modulus_len, sig, &.{msg}, public_key, Hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn concatVerify(
|
||||||
|
comptime modulus_len: usize,
|
||||||
|
sig: [modulus_len]u8,
|
||||||
|
msg: []const []const u8,
|
||||||
|
public_key: PublicKey,
|
||||||
|
comptime Hash: type,
|
||||||
|
) VerifyError!void {
|
||||||
|
const em_dec = try encrypt(modulus_len, sig, public_key);
|
||||||
|
const em = try EMSA_PKCS1_V1_5_ENCODE(msg, modulus_len, Hash);
|
||||||
|
if (!std.mem.eql(u8, &em_dec, &em)) return error.InvalidSignature;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn EMSA_PKCS1_V1_5_ENCODE(msg: []const []const u8, comptime emLen: usize, comptime Hash: type) VerifyError![emLen]u8 {
|
||||||
|
comptime var em_index = emLen;
|
||||||
|
var em: [emLen]u8 = undefined;
|
||||||
|
|
||||||
|
// 1. Apply the hash function to the message M to produce a hash value
|
||||||
|
// H:
|
||||||
|
//
|
||||||
|
// H = Hash(M).
|
||||||
|
//
|
||||||
|
// If the hash function outputs "message too long," output "message
|
||||||
|
// too long" and stop.
|
||||||
|
var hasher: Hash = .init(.{});
|
||||||
|
for (msg) |part| hasher.update(part);
|
||||||
|
em_index -= Hash.digest_length;
|
||||||
|
hasher.final(em[em_index..]);
|
||||||
|
|
||||||
|
// 2. Encode the algorithm ID for the hash function and the hash value
|
||||||
|
// into an ASN.1 value of type DigestInfo (see Appendix A.2.4) with
|
||||||
|
// the Distinguished Encoding Rules (DER), where the type DigestInfo
|
||||||
|
// has the syntax
|
||||||
|
//
|
||||||
|
// DigestInfo ::= SEQUENCE {
|
||||||
|
// digestAlgorithm AlgorithmIdentifier,
|
||||||
|
// digest OCTET STRING
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// The first field identifies the hash function and the second
|
||||||
|
// contains the hash value. Let T be the DER encoding of the
|
||||||
|
// DigestInfo value (see the notes below) and let tLen be the length
|
||||||
|
// in octets of T.
|
||||||
|
const hash_der: []const u8 = &switch (Hash) {
|
||||||
|
crypto.hash.Sha1 => .{
|
||||||
|
0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e,
|
||||||
|
0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14,
|
||||||
|
},
|
||||||
|
crypto.hash.sha2.Sha224 => .{
|
||||||
|
0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
|
||||||
|
0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04, 0x05,
|
||||||
|
0x00, 0x04, 0x1c,
|
||||||
|
},
|
||||||
|
crypto.hash.sha2.Sha256 => .{
|
||||||
|
0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
|
||||||
|
0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
|
||||||
|
0x00, 0x04, 0x20,
|
||||||
|
},
|
||||||
|
crypto.hash.sha2.Sha384 => .{
|
||||||
|
0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
|
||||||
|
0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05,
|
||||||
|
0x00, 0x04, 0x30,
|
||||||
|
},
|
||||||
|
crypto.hash.sha2.Sha512 => .{
|
||||||
|
0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
|
||||||
|
0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
|
||||||
|
0x00, 0x04, 0x40,
|
||||||
|
},
|
||||||
|
else => @compileError("unreachable"),
|
||||||
|
};
|
||||||
|
em_index -= hash_der.len;
|
||||||
|
@memcpy(em[em_index..][0..hash_der.len], hash_der);
|
||||||
|
|
||||||
|
// 3. If emLen < tLen + 11, output "intended encoded message length too
|
||||||
|
// short" and stop.
|
||||||
|
|
||||||
|
// 4. Generate an octet string PS consisting of emLen - tLen - 3 octets
|
||||||
|
// with hexadecimal value 0xff. The length of PS will be at least 8
|
||||||
|
// octets.
|
||||||
|
em_index -= 1;
|
||||||
|
@memset(em[2..em_index], 0xff);
|
||||||
|
|
||||||
|
// 5. Concatenate PS, the DER encoding T, and other padding to form the
|
||||||
|
// encoded message EM as
|
||||||
|
//
|
||||||
|
// EM = 0x00 || 0x01 || PS || 0x00 || T.
|
||||||
|
em[em_index] = 0x00;
|
||||||
|
em[1] = 0x01;
|
||||||
|
em[0] = 0x00;
|
||||||
|
|
||||||
|
// 6. Output EM.
|
||||||
|
return em;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
pub const PublicKey = struct {
|
pub const PublicKey = struct {
|
||||||
n: Modulus,
|
n: Modulus,
|
||||||
e: Fe,
|
e: Fe,
|
||||||
|
|
||||||
pub fn fromBytes(pub_bytes: []const u8, modulus_bytes: []const u8) !PublicKey {
|
pub const FromBytesError = error{CertificatePublicKeyInvalid};
|
||||||
|
|
||||||
|
pub fn fromBytes(pub_bytes: []const u8, modulus_bytes: []const u8) FromBytesError!PublicKey {
|
||||||
// Reject modulus below 512 bits.
|
// Reject modulus below 512 bits.
|
||||||
// 512-bit RSA was factored in 1999, so this limit barely means anything,
|
// 512-bit RSA was factored in 1999, so this limit barely means anything,
|
||||||
// but establish some limit now to ratchet in what we can.
|
// but establish some limit now to ratchet in what we can.
|
||||||
@ -1137,7 +1218,9 @@ pub const rsa = struct {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parseDer(pub_key: []const u8) !struct { modulus: []const u8, exponent: []const u8 } {
|
pub const ParseDerError = der.Element.ParseError || error{CertificateFieldHasWrongDataType};
|
||||||
|
|
||||||
|
pub fn parseDer(pub_key: []const u8) ParseDerError!struct { modulus: []const u8, exponent: []const u8 } {
|
||||||
const pub_key_seq = try der.Element.parse(pub_key, 0);
|
const pub_key_seq = try der.Element.parse(pub_key, 0);
|
||||||
if (pub_key_seq.identifier.tag != .sequence) return error.CertificateFieldHasWrongDataType;
|
if (pub_key_seq.identifier.tag != .sequence) return error.CertificateFieldHasWrongDataType;
|
||||||
const modulus_elem = try der.Element.parse(pub_key, pub_key_seq.slice.start);
|
const modulus_elem = try der.Element.parse(pub_key, pub_key_seq.slice.start);
|
||||||
@ -1156,7 +1239,9 @@ pub const rsa = struct {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
fn encrypt(comptime modulus_len: usize, msg: [modulus_len]u8, public_key: PublicKey) ![modulus_len]u8 {
|
const EncryptError = error{MessageTooLong};
|
||||||
|
|
||||||
|
fn encrypt(comptime modulus_len: usize, msg: [modulus_len]u8, public_key: PublicKey) EncryptError![modulus_len]u8 {
|
||||||
const m = Fe.fromBytes(public_key.n, &msg, .big) catch return error.MessageTooLong;
|
const m = Fe.fromBytes(public_key.n, &msg, .big) catch return error.MessageTooLong;
|
||||||
const e = public_key.n.powPublic(m, public_key.e) catch unreachable;
|
const e = public_key.n.powPublic(m, public_key.e) catch unreachable;
|
||||||
var res: [modulus_len]u8 = undefined;
|
var res: [modulus_len]u8 = undefined;
|
||||||
|
|||||||
@ -91,24 +91,33 @@ pub fn Ecdsa(comptime Curve: type, comptime Hash: type) type {
|
|||||||
s: Curve.scalar.CompressedScalar,
|
s: Curve.scalar.CompressedScalar,
|
||||||
|
|
||||||
/// Create a Verifier for incremental verification of a signature.
|
/// Create a Verifier for incremental verification of a signature.
|
||||||
pub fn verifier(self: Signature, public_key: PublicKey) (NonCanonicalError || EncodingError || IdentityElementError)!Verifier {
|
pub fn verifier(sig: Signature, public_key: PublicKey) Verifier.InitError!Verifier {
|
||||||
return Verifier.init(self, public_key);
|
return Verifier.init(sig, public_key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub const VerifyError = Verifier.InitError || Verifier.VerifyError;
|
||||||
|
|
||||||
/// Verify the signature against a message and public key.
|
/// Verify the signature against a message and public key.
|
||||||
/// Return IdentityElement or NonCanonical if the public key or signature are not in the expected range,
|
/// Return IdentityElement or NonCanonical if the public key or signature are not in the expected range,
|
||||||
/// or SignatureVerificationError if the signature is invalid for the given message and key.
|
/// or SignatureVerificationError if the signature is invalid for the given message and key.
|
||||||
pub fn verify(self: Signature, msg: []const u8, public_key: PublicKey) (IdentityElementError || NonCanonicalError || SignatureVerificationError)!void {
|
pub fn verify(sig: Signature, msg: []const u8, public_key: PublicKey) VerifyError!void {
|
||||||
var st = try Verifier.init(self, public_key);
|
try sig.concatVerify(&.{msg}, public_key);
|
||||||
st.update(msg);
|
}
|
||||||
return st.verify();
|
|
||||||
|
/// Verify the signature against a concatenated message and public key.
|
||||||
|
/// Return IdentityElement or NonCanonical if the public key or signature are not in the expected range,
|
||||||
|
/// or SignatureVerificationError if the signature is invalid for the given message and key.
|
||||||
|
pub fn concatVerify(sig: Signature, msg: []const []const u8, public_key: PublicKey) VerifyError!void {
|
||||||
|
var st = try Verifier.init(sig, public_key);
|
||||||
|
for (msg) |part| st.update(part);
|
||||||
|
try st.verify();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the raw signature (r, s) in big-endian format.
|
/// Return the raw signature (r, s) in big-endian format.
|
||||||
pub fn toBytes(self: Signature) [encoded_length]u8 {
|
pub fn toBytes(sig: Signature) [encoded_length]u8 {
|
||||||
var bytes: [encoded_length]u8 = undefined;
|
var bytes: [encoded_length]u8 = undefined;
|
||||||
@memcpy(bytes[0 .. encoded_length / 2], &self.r);
|
@memcpy(bytes[0 .. encoded_length / 2], &sig.r);
|
||||||
@memcpy(bytes[encoded_length / 2 ..], &self.s);
|
@memcpy(bytes[encoded_length / 2 ..], &sig.s);
|
||||||
return bytes;
|
return bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,23 +133,23 @@ pub fn Ecdsa(comptime Curve: type, comptime Hash: type) type {
|
|||||||
/// Encode the signature using the DER format.
|
/// Encode the signature using the DER format.
|
||||||
/// The maximum length of the DER encoding is der_encoded_length_max.
|
/// The maximum length of the DER encoding is der_encoded_length_max.
|
||||||
/// The function returns a slice, that can be shorter than der_encoded_length_max.
|
/// The function returns a slice, that can be shorter than der_encoded_length_max.
|
||||||
pub fn toDer(self: Signature, buf: *[der_encoded_length_max]u8) []u8 {
|
pub fn toDer(sig: Signature, buf: *[der_encoded_length_max]u8) []u8 {
|
||||||
var fb = io.fixedBufferStream(buf);
|
var fb = io.fixedBufferStream(buf);
|
||||||
const w = fb.writer();
|
const w = fb.writer();
|
||||||
const r_len = @as(u8, @intCast(self.r.len + (self.r[0] >> 7)));
|
const r_len = @as(u8, @intCast(sig.r.len + (sig.r[0] >> 7)));
|
||||||
const s_len = @as(u8, @intCast(self.s.len + (self.s[0] >> 7)));
|
const s_len = @as(u8, @intCast(sig.s.len + (sig.s[0] >> 7)));
|
||||||
const seq_len = @as(u8, @intCast(2 + r_len + 2 + s_len));
|
const seq_len = @as(u8, @intCast(2 + r_len + 2 + s_len));
|
||||||
w.writeAll(&[_]u8{ 0x30, seq_len }) catch unreachable;
|
w.writeAll(&[_]u8{ 0x30, seq_len }) catch unreachable;
|
||||||
w.writeAll(&[_]u8{ 0x02, r_len }) catch unreachable;
|
w.writeAll(&[_]u8{ 0x02, r_len }) catch unreachable;
|
||||||
if (self.r[0] >> 7 != 0) {
|
if (sig.r[0] >> 7 != 0) {
|
||||||
w.writeByte(0x00) catch unreachable;
|
w.writeByte(0x00) catch unreachable;
|
||||||
}
|
}
|
||||||
w.writeAll(&self.r) catch unreachable;
|
w.writeAll(&sig.r) catch unreachable;
|
||||||
w.writeAll(&[_]u8{ 0x02, s_len }) catch unreachable;
|
w.writeAll(&[_]u8{ 0x02, s_len }) catch unreachable;
|
||||||
if (self.s[0] >> 7 != 0) {
|
if (sig.s[0] >> 7 != 0) {
|
||||||
w.writeByte(0x00) catch unreachable;
|
w.writeByte(0x00) catch unreachable;
|
||||||
}
|
}
|
||||||
w.writeAll(&self.s) catch unreachable;
|
w.writeAll(&sig.s) catch unreachable;
|
||||||
return fb.getWritten();
|
return fb.getWritten();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -236,7 +245,9 @@ pub fn Ecdsa(comptime Curve: type, comptime Hash: type) type {
|
|||||||
s: Curve.scalar.Scalar,
|
s: Curve.scalar.Scalar,
|
||||||
public_key: PublicKey,
|
public_key: PublicKey,
|
||||||
|
|
||||||
fn init(sig: Signature, public_key: PublicKey) (IdentityElementError || NonCanonicalError)!Verifier {
|
pub const InitError = IdentityElementError || NonCanonicalError;
|
||||||
|
|
||||||
|
fn init(sig: Signature, public_key: PublicKey) InitError!Verifier {
|
||||||
const r = try Curve.scalar.Scalar.fromBytes(sig.r, .big);
|
const r = try Curve.scalar.Scalar.fromBytes(sig.r, .big);
|
||||||
const s = try Curve.scalar.Scalar.fromBytes(sig.s, .big);
|
const s = try Curve.scalar.Scalar.fromBytes(sig.s, .big);
|
||||||
if (r.isZero() or s.isZero()) return error.IdentityElement;
|
if (r.isZero() or s.isZero()) return error.IdentityElement;
|
||||||
@ -254,8 +265,11 @@ pub fn Ecdsa(comptime Curve: type, comptime Hash: type) type {
|
|||||||
self.h.update(data);
|
self.h.update(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub const VerifyError = IdentityElementError || NonCanonicalError ||
|
||||||
|
SignatureVerificationError;
|
||||||
|
|
||||||
/// Verify that the signature is valid for the entire message.
|
/// Verify that the signature is valid for the entire message.
|
||||||
pub fn verify(self: *Verifier) (IdentityElementError || NonCanonicalError || SignatureVerificationError)!void {
|
pub fn verify(self: *Verifier) VerifyError!void {
|
||||||
const ht = Curve.scalar.encoded_length;
|
const ht = Curve.scalar.encoded_length;
|
||||||
const h_len = @max(Hash.digest_length, ht);
|
const h_len = @max(Hash.digest_length, ht);
|
||||||
var h: [h_len]u8 = [_]u8{0} ** h_len;
|
var h: [h_len]u8 = [_]u8{0} ** h_len;
|
||||||
|
|||||||
@ -54,6 +54,8 @@ pub const close_notify_alert = [_]u8{
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub const ProtocolVersion = enum(u16) {
|
pub const ProtocolVersion = enum(u16) {
|
||||||
|
tls_1_0 = 0x0301,
|
||||||
|
tls_1_1 = 0x0302,
|
||||||
tls_1_2 = 0x0303,
|
tls_1_2 = 0x0303,
|
||||||
tls_1_3 = 0x0304,
|
tls_1_3 = 0x0304,
|
||||||
_,
|
_,
|
||||||
@ -69,14 +71,18 @@ pub const ContentType = enum(u8) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub const HandshakeType = enum(u8) {
|
pub const HandshakeType = enum(u8) {
|
||||||
|
hello_request = 0,
|
||||||
client_hello = 1,
|
client_hello = 1,
|
||||||
server_hello = 2,
|
server_hello = 2,
|
||||||
new_session_ticket = 4,
|
new_session_ticket = 4,
|
||||||
end_of_early_data = 5,
|
end_of_early_data = 5,
|
||||||
encrypted_extensions = 8,
|
encrypted_extensions = 8,
|
||||||
certificate = 11,
|
certificate = 11,
|
||||||
|
server_key_exchange = 12,
|
||||||
certificate_request = 13,
|
certificate_request = 13,
|
||||||
|
server_hello_done = 14,
|
||||||
certificate_verify = 15,
|
certificate_verify = 15,
|
||||||
|
client_key_exchange = 16,
|
||||||
finished = 20,
|
finished = 20,
|
||||||
key_update = 24,
|
key_update = 24,
|
||||||
message_hash = 254,
|
message_hash = 254,
|
||||||
@ -198,36 +204,36 @@ pub const AlertDescription = enum(u8) {
|
|||||||
_,
|
_,
|
||||||
|
|
||||||
pub fn toError(alert: AlertDescription) Error!void {
|
pub fn toError(alert: AlertDescription) Error!void {
|
||||||
return switch (alert) {
|
switch (alert) {
|
||||||
.close_notify => {}, // not an error
|
.close_notify => {}, // not an error
|
||||||
.unexpected_message => error.TlsAlertUnexpectedMessage,
|
.unexpected_message => return error.TlsAlertUnexpectedMessage,
|
||||||
.bad_record_mac => error.TlsAlertBadRecordMac,
|
.bad_record_mac => return error.TlsAlertBadRecordMac,
|
||||||
.record_overflow => error.TlsAlertRecordOverflow,
|
.record_overflow => return error.TlsAlertRecordOverflow,
|
||||||
.handshake_failure => error.TlsAlertHandshakeFailure,
|
.handshake_failure => return error.TlsAlertHandshakeFailure,
|
||||||
.bad_certificate => error.TlsAlertBadCertificate,
|
.bad_certificate => return error.TlsAlertBadCertificate,
|
||||||
.unsupported_certificate => error.TlsAlertUnsupportedCertificate,
|
.unsupported_certificate => return error.TlsAlertUnsupportedCertificate,
|
||||||
.certificate_revoked => error.TlsAlertCertificateRevoked,
|
.certificate_revoked => return error.TlsAlertCertificateRevoked,
|
||||||
.certificate_expired => error.TlsAlertCertificateExpired,
|
.certificate_expired => return error.TlsAlertCertificateExpired,
|
||||||
.certificate_unknown => error.TlsAlertCertificateUnknown,
|
.certificate_unknown => return error.TlsAlertCertificateUnknown,
|
||||||
.illegal_parameter => error.TlsAlertIllegalParameter,
|
.illegal_parameter => return error.TlsAlertIllegalParameter,
|
||||||
.unknown_ca => error.TlsAlertUnknownCa,
|
.unknown_ca => return error.TlsAlertUnknownCa,
|
||||||
.access_denied => error.TlsAlertAccessDenied,
|
.access_denied => return error.TlsAlertAccessDenied,
|
||||||
.decode_error => error.TlsAlertDecodeError,
|
.decode_error => return error.TlsAlertDecodeError,
|
||||||
.decrypt_error => error.TlsAlertDecryptError,
|
.decrypt_error => return error.TlsAlertDecryptError,
|
||||||
.protocol_version => error.TlsAlertProtocolVersion,
|
.protocol_version => return error.TlsAlertProtocolVersion,
|
||||||
.insufficient_security => error.TlsAlertInsufficientSecurity,
|
.insufficient_security => return error.TlsAlertInsufficientSecurity,
|
||||||
.internal_error => error.TlsAlertInternalError,
|
.internal_error => return error.TlsAlertInternalError,
|
||||||
.inappropriate_fallback => error.TlsAlertInappropriateFallback,
|
.inappropriate_fallback => return error.TlsAlertInappropriateFallback,
|
||||||
.user_canceled => {}, // not an error
|
.user_canceled => {}, // not an error
|
||||||
.missing_extension => error.TlsAlertMissingExtension,
|
.missing_extension => return error.TlsAlertMissingExtension,
|
||||||
.unsupported_extension => error.TlsAlertUnsupportedExtension,
|
.unsupported_extension => return error.TlsAlertUnsupportedExtension,
|
||||||
.unrecognized_name => error.TlsAlertUnrecognizedName,
|
.unrecognized_name => return error.TlsAlertUnrecognizedName,
|
||||||
.bad_certificate_status_response => error.TlsAlertBadCertificateStatusResponse,
|
.bad_certificate_status_response => return error.TlsAlertBadCertificateStatusResponse,
|
||||||
.unknown_psk_identity => error.TlsAlertUnknownPskIdentity,
|
.unknown_psk_identity => return error.TlsAlertUnknownPskIdentity,
|
||||||
.certificate_required => error.TlsAlertCertificateRequired,
|
.certificate_required => return error.TlsAlertCertificateRequired,
|
||||||
.no_application_protocol => error.TlsAlertNoApplicationProtocol,
|
.no_application_protocol => return error.TlsAlertNoApplicationProtocol,
|
||||||
_ => error.TlsAlertUnknown,
|
_ => return error.TlsAlertUnknown,
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -286,6 +292,20 @@ pub const NamedGroup = enum(u16) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub const CipherSuite = enum(u16) {
|
pub const CipherSuite = enum(u16) {
|
||||||
|
RSA_WITH_AES_128_CBC_SHA = 0x002F,
|
||||||
|
DHE_RSA_WITH_AES_128_CBC_SHA = 0x0033,
|
||||||
|
RSA_WITH_AES_256_CBC_SHA = 0x0035,
|
||||||
|
DHE_RSA_WITH_AES_256_CBC_SHA = 0x0039,
|
||||||
|
RSA_WITH_AES_128_CBC_SHA256 = 0x003C,
|
||||||
|
RSA_WITH_AES_256_CBC_SHA256 = 0x003D,
|
||||||
|
DHE_RSA_WITH_AES_128_CBC_SHA256 = 0x0067,
|
||||||
|
DHE_RSA_WITH_AES_256_CBC_SHA256 = 0x006B,
|
||||||
|
RSA_WITH_AES_128_GCM_SHA256 = 0x009C,
|
||||||
|
RSA_WITH_AES_256_GCM_SHA384 = 0x009D,
|
||||||
|
DHE_RSA_WITH_AES_128_GCM_SHA256 = 0x009E,
|
||||||
|
DHE_RSA_WITH_AES_256_GCM_SHA384 = 0x009F,
|
||||||
|
EMPTY_RENEGOTIATION_INFO_SCSV = 0x00FF,
|
||||||
|
|
||||||
AES_128_GCM_SHA256 = 0x1301,
|
AES_128_GCM_SHA256 = 0x1301,
|
||||||
AES_256_GCM_SHA384 = 0x1302,
|
AES_256_GCM_SHA384 = 0x1302,
|
||||||
CHACHA20_POLY1305_SHA256 = 0x1303,
|
CHACHA20_POLY1305_SHA256 = 0x1303,
|
||||||
@ -293,7 +313,98 @@ pub const CipherSuite = enum(u16) {
|
|||||||
AES_128_CCM_8_SHA256 = 0x1305,
|
AES_128_CCM_8_SHA256 = 0x1305,
|
||||||
AEGIS_256_SHA512 = 0x1306,
|
AEGIS_256_SHA512 = 0x1306,
|
||||||
AEGIS_128L_SHA256 = 0x1307,
|
AEGIS_128L_SHA256 = 0x1307,
|
||||||
|
|
||||||
|
ECDHE_ECDSA_WITH_AES_128_CBC_SHA = 0xC009,
|
||||||
|
ECDHE_ECDSA_WITH_AES_256_CBC_SHA = 0xC00A,
|
||||||
|
ECDHE_RSA_WITH_AES_128_CBC_SHA = 0xC013,
|
||||||
|
ECDHE_RSA_WITH_AES_256_CBC_SHA = 0xC014,
|
||||||
|
ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 = 0xC023,
|
||||||
|
ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 = 0xC024,
|
||||||
|
ECDHE_RSA_WITH_AES_128_CBC_SHA256 = 0xC027,
|
||||||
|
ECDHE_RSA_WITH_AES_256_CBC_SHA384 = 0xC028,
|
||||||
|
ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 = 0xC02B,
|
||||||
|
ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 = 0xC02C,
|
||||||
|
ECDHE_RSA_WITH_AES_128_GCM_SHA256 = 0xC02F,
|
||||||
|
ECDHE_RSA_WITH_AES_256_GCM_SHA384 = 0xC030,
|
||||||
|
|
||||||
|
ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 = 0xCCA8,
|
||||||
|
ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 = 0xCCA9,
|
||||||
|
DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 = 0xCCAA,
|
||||||
|
|
||||||
_,
|
_,
|
||||||
|
|
||||||
|
pub const With = enum {
|
||||||
|
AES_128_CBC_SHA,
|
||||||
|
AES_256_CBC_SHA,
|
||||||
|
AES_128_CBC_SHA256,
|
||||||
|
AES_256_CBC_SHA256,
|
||||||
|
AES_256_CBC_SHA384,
|
||||||
|
|
||||||
|
AES_128_GCM_SHA256,
|
||||||
|
AES_256_GCM_SHA384,
|
||||||
|
|
||||||
|
CHACHA20_POLY1305_SHA256,
|
||||||
|
|
||||||
|
AES_128_CCM_SHA256,
|
||||||
|
AES_128_CCM_8_SHA256,
|
||||||
|
|
||||||
|
AEGIS_256_SHA512,
|
||||||
|
AEGIS_128L_SHA256,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn with(cipher_suite: CipherSuite) With {
|
||||||
|
return switch (cipher_suite) {
|
||||||
|
.RSA_WITH_AES_128_CBC_SHA,
|
||||||
|
.DHE_RSA_WITH_AES_128_CBC_SHA,
|
||||||
|
.ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
|
||||||
|
.ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
||||||
|
=> .AES_128_CBC_SHA,
|
||||||
|
.RSA_WITH_AES_256_CBC_SHA,
|
||||||
|
.DHE_RSA_WITH_AES_256_CBC_SHA,
|
||||||
|
.ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
|
||||||
|
.ECDHE_RSA_WITH_AES_256_CBC_SHA,
|
||||||
|
=> .AES_256_CBC_SHA,
|
||||||
|
.RSA_WITH_AES_128_CBC_SHA256,
|
||||||
|
.DHE_RSA_WITH_AES_128_CBC_SHA256,
|
||||||
|
.ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
|
||||||
|
.ECDHE_RSA_WITH_AES_128_CBC_SHA256,
|
||||||
|
=> .AES_128_CBC_SHA256,
|
||||||
|
.RSA_WITH_AES_256_CBC_SHA256,
|
||||||
|
.DHE_RSA_WITH_AES_256_CBC_SHA256,
|
||||||
|
=> .AES_256_CBC_SHA256,
|
||||||
|
.ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,
|
||||||
|
.ECDHE_RSA_WITH_AES_256_CBC_SHA384,
|
||||||
|
=> .AES_256_CBC_SHA384,
|
||||||
|
|
||||||
|
.RSA_WITH_AES_128_GCM_SHA256,
|
||||||
|
.DHE_RSA_WITH_AES_128_GCM_SHA256,
|
||||||
|
.AES_128_GCM_SHA256,
|
||||||
|
.ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||||
|
.ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
||||||
|
=> .AES_128_GCM_SHA256,
|
||||||
|
.RSA_WITH_AES_256_GCM_SHA384,
|
||||||
|
.DHE_RSA_WITH_AES_256_GCM_SHA384,
|
||||||
|
.AES_256_GCM_SHA384,
|
||||||
|
.ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
|
||||||
|
.ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
||||||
|
=> .AES_256_GCM_SHA384,
|
||||||
|
|
||||||
|
.CHACHA20_POLY1305_SHA256,
|
||||||
|
.ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
|
||||||
|
.ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
|
||||||
|
.DHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
|
||||||
|
=> .CHACHA20_POLY1305_SHA256,
|
||||||
|
|
||||||
|
.AES_128_CCM_SHA256 => .AES_128_CCM_SHA256,
|
||||||
|
.AES_128_CCM_8_SHA256 => .AES_128_CCM_8_SHA256,
|
||||||
|
|
||||||
|
.AEGIS_256_SHA512 => .AEGIS_256_SHA512,
|
||||||
|
.AEGIS_128L_SHA256 => .AEGIS_128L_SHA256,
|
||||||
|
|
||||||
|
.EMPTY_RENEGOTIATION_INFO_SCSV => unreachable,
|
||||||
|
_ => unreachable,
|
||||||
|
};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const CertificateType = enum(u8) {
|
pub const CertificateType = enum(u8) {
|
||||||
@ -308,58 +419,108 @@ pub const KeyUpdateRequest = enum(u8) {
|
|||||||
_,
|
_,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn HandshakeCipherT(comptime AeadType: type, comptime HashType: type) type {
|
pub fn HandshakeCipherT(comptime AeadType: type, comptime HashType: type, comptime explicit_iv_length: comptime_int) type {
|
||||||
return struct {
|
return struct {
|
||||||
pub const AEAD = AeadType;
|
pub const A = ApplicationCipherT(AeadType, HashType, explicit_iv_length);
|
||||||
pub const Hash = HashType;
|
|
||||||
pub const Hmac = crypto.auth.hmac.Hmac(Hash);
|
|
||||||
pub const Hkdf = crypto.kdf.hkdf.Hkdf(Hmac);
|
|
||||||
|
|
||||||
handshake_secret: [Hkdf.prk_length]u8,
|
transcript_hash: A.Hash,
|
||||||
master_secret: [Hkdf.prk_length]u8,
|
version: union {
|
||||||
client_handshake_key: [AEAD.key_length]u8,
|
tls_1_2: struct {
|
||||||
server_handshake_key: [AEAD.key_length]u8,
|
server_verify_data: [12]u8,
|
||||||
client_finished_key: [Hmac.key_length]u8,
|
app_cipher: A.Tls_1_2,
|
||||||
server_finished_key: [Hmac.key_length]u8,
|
},
|
||||||
client_handshake_iv: [AEAD.nonce_length]u8,
|
tls_1_3: struct {
|
||||||
server_handshake_iv: [AEAD.nonce_length]u8,
|
handshake_secret: [A.Hkdf.prk_length]u8,
|
||||||
transcript_hash: Hash,
|
master_secret: [A.Hkdf.prk_length]u8,
|
||||||
|
client_handshake_key: [A.AEAD.key_length]u8,
|
||||||
|
server_handshake_key: [A.AEAD.key_length]u8,
|
||||||
|
client_finished_key: [A.Hmac.key_length]u8,
|
||||||
|
server_finished_key: [A.Hmac.key_length]u8,
|
||||||
|
client_handshake_iv: [A.AEAD.nonce_length]u8,
|
||||||
|
server_handshake_iv: [A.AEAD.nonce_length]u8,
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const HandshakeCipher = union(enum) {
|
pub const HandshakeCipher = union(enum) {
|
||||||
AES_128_GCM_SHA256: HandshakeCipherT(crypto.aead.aes_gcm.Aes128Gcm, crypto.hash.sha2.Sha256),
|
AES_128_GCM_SHA256: HandshakeCipherT(crypto.aead.aes_gcm.Aes128Gcm, crypto.hash.sha2.Sha256, 8),
|
||||||
AES_256_GCM_SHA384: HandshakeCipherT(crypto.aead.aes_gcm.Aes256Gcm, crypto.hash.sha2.Sha384),
|
AES_256_GCM_SHA384: HandshakeCipherT(crypto.aead.aes_gcm.Aes256Gcm, crypto.hash.sha2.Sha384, 8),
|
||||||
CHACHA20_POLY1305_SHA256: HandshakeCipherT(crypto.aead.chacha_poly.ChaCha20Poly1305, crypto.hash.sha2.Sha256),
|
CHACHA20_POLY1305_SHA256: HandshakeCipherT(crypto.aead.chacha_poly.ChaCha20Poly1305, crypto.hash.sha2.Sha256, 0),
|
||||||
AEGIS_256_SHA512: HandshakeCipherT(crypto.aead.aegis.Aegis256, crypto.hash.sha2.Sha512),
|
AEGIS_256_SHA512: HandshakeCipherT(crypto.aead.aegis.Aegis256, crypto.hash.sha2.Sha512, 0),
|
||||||
AEGIS_128L_SHA256: HandshakeCipherT(crypto.aead.aegis.Aegis128L, crypto.hash.sha2.Sha256),
|
AEGIS_128L_SHA256: HandshakeCipherT(crypto.aead.aegis.Aegis128L, crypto.hash.sha2.Sha256, 0),
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn ApplicationCipherT(comptime AeadType: type, comptime HashType: type) type {
|
pub fn ApplicationCipherT(comptime AeadType: type, comptime HashType: type, comptime explicit_iv_length: comptime_int) type {
|
||||||
return struct {
|
return union {
|
||||||
pub const AEAD = AeadType;
|
pub const AEAD = AeadType;
|
||||||
pub const Hash = HashType;
|
pub const Hash = HashType;
|
||||||
pub const Hmac = crypto.auth.hmac.Hmac(Hash);
|
pub const Hmac = crypto.auth.hmac.Hmac(Hash);
|
||||||
pub const Hkdf = crypto.kdf.hkdf.Hkdf(Hmac);
|
pub const Hkdf = crypto.kdf.hkdf.Hkdf(Hmac);
|
||||||
|
|
||||||
client_secret: [Hash.digest_length]u8,
|
pub const enc_key_length = AEAD.key_length;
|
||||||
server_secret: [Hash.digest_length]u8,
|
pub const fixed_iv_length = AEAD.nonce_length - explicit_iv_length;
|
||||||
client_key: [AEAD.key_length]u8,
|
pub const record_iv_length = explicit_iv_length;
|
||||||
server_key: [AEAD.key_length]u8,
|
pub const mac_length = AEAD.tag_length;
|
||||||
client_iv: [AEAD.nonce_length]u8,
|
pub const mac_key_length = Hmac.key_length_min;
|
||||||
server_iv: [AEAD.nonce_length]u8,
|
|
||||||
|
tls_1_2: Tls_1_2,
|
||||||
|
tls_1_3: Tls_1_3,
|
||||||
|
|
||||||
|
pub const Tls_1_2 = extern struct {
|
||||||
|
client_write_MAC_key: [mac_key_length]u8,
|
||||||
|
server_write_MAC_key: [mac_key_length]u8,
|
||||||
|
client_write_key: [enc_key_length]u8,
|
||||||
|
server_write_key: [enc_key_length]u8,
|
||||||
|
client_write_IV: [fixed_iv_length]u8,
|
||||||
|
server_write_IV: [fixed_iv_length]u8,
|
||||||
|
// non-standard entropy
|
||||||
|
client_salt: [record_iv_length]u8,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Tls_1_3 = struct {
|
||||||
|
client_secret: [Hash.digest_length]u8,
|
||||||
|
server_secret: [Hash.digest_length]u8,
|
||||||
|
client_key: [AEAD.key_length]u8,
|
||||||
|
server_key: [AEAD.key_length]u8,
|
||||||
|
client_iv: [AEAD.nonce_length]u8,
|
||||||
|
server_iv: [AEAD.nonce_length]u8,
|
||||||
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Encryption parameters for application traffic.
|
/// Encryption parameters for application traffic.
|
||||||
pub const ApplicationCipher = union(enum) {
|
pub const ApplicationCipher = union(enum) {
|
||||||
AES_128_GCM_SHA256: ApplicationCipherT(crypto.aead.aes_gcm.Aes128Gcm, crypto.hash.sha2.Sha256),
|
AES_128_GCM_SHA256: ApplicationCipherT(crypto.aead.aes_gcm.Aes128Gcm, crypto.hash.sha2.Sha256, 8),
|
||||||
AES_256_GCM_SHA384: ApplicationCipherT(crypto.aead.aes_gcm.Aes256Gcm, crypto.hash.sha2.Sha384),
|
AES_256_GCM_SHA384: ApplicationCipherT(crypto.aead.aes_gcm.Aes256Gcm, crypto.hash.sha2.Sha384, 8),
|
||||||
CHACHA20_POLY1305_SHA256: ApplicationCipherT(crypto.aead.chacha_poly.ChaCha20Poly1305, crypto.hash.sha2.Sha256),
|
CHACHA20_POLY1305_SHA256: ApplicationCipherT(crypto.aead.chacha_poly.ChaCha20Poly1305, crypto.hash.sha2.Sha256, 0),
|
||||||
AEGIS_256_SHA512: ApplicationCipherT(crypto.aead.aegis.Aegis256, crypto.hash.sha2.Sha512),
|
AEGIS_256_SHA512: ApplicationCipherT(crypto.aead.aegis.Aegis256, crypto.hash.sha2.Sha512, 0),
|
||||||
AEGIS_128L_SHA256: ApplicationCipherT(crypto.aead.aegis.Aegis128L, crypto.hash.sha2.Sha256),
|
AEGIS_128L_SHA256: ApplicationCipherT(crypto.aead.aegis.Aegis128L, crypto.hash.sha2.Sha256, 0),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub fn hmacExpandLabel(
|
||||||
|
comptime Hmac: type,
|
||||||
|
secret: []const u8,
|
||||||
|
label_then_seed: []const []const u8,
|
||||||
|
comptime len: usize,
|
||||||
|
) [len]u8 {
|
||||||
|
const initial_hmac: Hmac = .init(secret);
|
||||||
|
var a: [Hmac.mac_length]u8 = undefined;
|
||||||
|
var result: [std.mem.alignForwardAnyAlign(usize, len, Hmac.mac_length)]u8 = undefined;
|
||||||
|
var index: usize = 0;
|
||||||
|
while (index < result.len) : (index += Hmac.mac_length) {
|
||||||
|
var a_hmac = initial_hmac;
|
||||||
|
if (index > 0) a_hmac.update(&a) else for (label_then_seed) |part| a_hmac.update(part);
|
||||||
|
a_hmac.final(&a);
|
||||||
|
|
||||||
|
var result_hmac = initial_hmac;
|
||||||
|
result_hmac.update(&a);
|
||||||
|
for (label_then_seed) |part| result_hmac.update(part);
|
||||||
|
result_hmac.final(result[index..][0..Hmac.mac_length]);
|
||||||
|
}
|
||||||
|
return result[0..len].*;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn hkdfExpandLabel(
|
pub fn hkdfExpandLabel(
|
||||||
comptime Hkdf: type,
|
comptime Hkdf: type,
|
||||||
key: [Hkdf.prk_length]u8,
|
key: [Hkdf.prk_length]u8,
|
||||||
@ -418,19 +579,16 @@ pub inline fn enum_array(comptime E: type, comptime tags: []const E) [2 + @sizeO
|
|||||||
return array(2, result);
|
return array(2, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub inline fn int2(x: u16) [2]u8 {
|
pub inline fn int2(int: u16) [2]u8 {
|
||||||
return .{
|
var arr: [2]u8 = undefined;
|
||||||
@as(u8, @truncate(x >> 8)),
|
std.mem.writeInt(u16, &arr, int, .big);
|
||||||
@as(u8, @truncate(x)),
|
return arr;
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub inline fn int3(x: u24) [3]u8 {
|
pub inline fn int3(int: u24) [3]u8 {
|
||||||
return .{
|
var arr: [3]u8 = undefined;
|
||||||
@as(u8, @truncate(x >> 16)),
|
std.mem.writeInt(u24, &arr, int, .big);
|
||||||
@as(u8, @truncate(x >> 8)),
|
return arr;
|
||||||
@as(u8, @truncate(x)),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An abstraction to ensure that protocol-parsing code does not perform an
|
/// An abstraction to ensure that protocol-parsing code does not perform an
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -172,7 +172,13 @@ pub const HeadersParser = struct {
|
|||||||
const data_avail = r.next_chunk_length;
|
const data_avail = r.next_chunk_length;
|
||||||
|
|
||||||
if (skip) {
|
if (skip) {
|
||||||
try conn.fill();
|
conn.fill() catch |err| switch (err) {
|
||||||
|
error.EndOfStream => {
|
||||||
|
r.done = true;
|
||||||
|
return 0;
|
||||||
|
},
|
||||||
|
else => |e| return e,
|
||||||
|
};
|
||||||
|
|
||||||
const nread = @min(conn.peek().len, data_avail);
|
const nread = @min(conn.peek().len, data_avail);
|
||||||
conn.drop(@intCast(nread));
|
conn.drop(@intCast(nread));
|
||||||
@ -196,7 +202,13 @@ pub const HeadersParser = struct {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
.chunk_data_suffix, .chunk_data_suffix_r, .chunk_head_size, .chunk_head_ext, .chunk_head_r => {
|
.chunk_data_suffix, .chunk_data_suffix_r, .chunk_head_size, .chunk_head_ext, .chunk_head_r => {
|
||||||
try conn.fill();
|
conn.fill() catch |err| switch (err) {
|
||||||
|
error.EndOfStream => {
|
||||||
|
r.done = true;
|
||||||
|
return 0;
|
||||||
|
},
|
||||||
|
else => |e| return e,
|
||||||
|
};
|
||||||
|
|
||||||
const i = r.findChunkedLen(conn.peek());
|
const i = r.findChunkedLen(conn.peek());
|
||||||
conn.drop(@intCast(i));
|
conn.drop(@intCast(i));
|
||||||
@ -226,7 +238,13 @@ pub const HeadersParser = struct {
|
|||||||
const out_avail = buffer.len - out_index;
|
const out_avail = buffer.len - out_index;
|
||||||
|
|
||||||
if (skip) {
|
if (skip) {
|
||||||
try conn.fill();
|
conn.fill() catch |err| switch (err) {
|
||||||
|
error.EndOfStream => {
|
||||||
|
r.done = true;
|
||||||
|
return 0;
|
||||||
|
},
|
||||||
|
else => |e| return e,
|
||||||
|
};
|
||||||
|
|
||||||
const nread = @min(conn.peek().len, data_avail);
|
const nread = @min(conn.peek().len, data_avail);
|
||||||
conn.drop(@intCast(nread));
|
conn.drop(@intCast(nread));
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user