Commit 10f2d62789

Frank Denis <124872+jedisct1@users.noreply.github.com>
2021-04-20 19:57:27
std/crypto: use finer-grained error sets in function signatures (#8558)
std/crypto: use finer-grained error sets in function signatures Returning the `crypto.Error` error set for all crypto operations was very convenient to ensure that errors were used consistently, and to avoid having multiple error names for the same thing. The flipside is that callers were forced to always handle all possible errors, even those that could never be returned by a function. This PR makes all functions return union sets of the actual errors they can return. The error sets themselves are all limited to a single error. Larger sets are useful for platform-specific APIs, but we don't have any of these in `std/crypto`, and I couldn't find any meaningful way to build larger sets.
1 parent 1e06a74
lib/std/crypto/25519/curve25519.zig
@@ -4,7 +4,11 @@
 // The MIT license requires this copyright notice to be included in all copies
 // and substantial portions of the software.
 const std = @import("std");
-const Error = std.crypto.Error;
+const crypto = std.crypto;
+
+const IdentityElementError = crypto.errors.IdentityElementError;
+const NonCanonicalError = crypto.errors.NonCanonicalError;
+const WeakPublicKeyError = crypto.errors.WeakPublicKeyError;
 
 /// Group operations over Curve25519.
 pub const Curve25519 = struct {
@@ -29,12 +33,12 @@ pub const Curve25519 = struct {
     pub const basePoint = Curve25519{ .x = Fe.curve25519BasePoint };
 
     /// Check that the encoding of a Curve25519 point is canonical.
-    pub fn rejectNonCanonical(s: [32]u8) Error!void {
+    pub fn rejectNonCanonical(s: [32]u8) NonCanonicalError!void {
         return Fe.rejectNonCanonical(s, false);
     }
 
     /// Reject the neutral element.
-    pub fn rejectIdentity(p: Curve25519) Error!void {
+    pub fn rejectIdentity(p: Curve25519) IdentityElementError!void {
         if (p.x.isZero()) {
             return error.IdentityElement;
         }
@@ -45,7 +49,7 @@ pub const Curve25519 = struct {
         return p.dbl().dbl().dbl();
     }
 
-    fn ladder(p: Curve25519, s: [32]u8, comptime bits: usize) Error!Curve25519 {
+    fn ladder(p: Curve25519, s: [32]u8, comptime bits: usize) IdentityElementError!Curve25519 {
         var x1 = p.x;
         var x2 = Fe.one;
         var z2 = Fe.zero;
@@ -86,7 +90,7 @@ pub const Curve25519 = struct {
     /// way to use Curve25519 for a DH operation.
     /// Return error.IdentityElement if the resulting point is
     /// the identity element.
-    pub fn clampedMul(p: Curve25519, s: [32]u8) Error!Curve25519 {
+    pub fn clampedMul(p: Curve25519, s: [32]u8) IdentityElementError!Curve25519 {
         var t: [32]u8 = s;
         scalar.clamp(&t);
         return try ladder(p, t, 255);
@@ -96,16 +100,16 @@ pub const Curve25519 = struct {
     /// Return error.IdentityElement if the resulting point is
     /// the identity element or error.WeakPublicKey if the public
     /// key is a low-order point.
-    pub fn mul(p: Curve25519, s: [32]u8) Error!Curve25519 {
+    pub fn mul(p: Curve25519, s: [32]u8) (IdentityElementError || WeakPublicKeyError)!Curve25519 {
         const cofactor = [_]u8{8} ++ [_]u8{0} ** 31;
         _ = ladder(p, cofactor, 4) catch |_| return error.WeakPublicKey;
         return try ladder(p, s, 256);
     }
 
     /// Compute the Curve25519 equivalent to an Edwards25519 point.
-    pub fn fromEdwards25519(p: std.crypto.ecc.Edwards25519) Error!Curve25519 {
+    pub fn fromEdwards25519(p: crypto.ecc.Edwards25519) IdentityElementError!Curve25519 {
         try p.clearCofactor().rejectIdentity();
-        const one = std.crypto.ecc.Edwards25519.Fe.one;
+        const one = crypto.ecc.Edwards25519.Fe.one;
         const x = one.add(p.y).mul(one.sub(p.y).invert()); // xMont=(1+yEd)/(1-yEd)
         return Curve25519{ .x = x };
     }
lib/std/crypto/25519/ed25519.zig
@@ -8,8 +8,15 @@ const crypto = std.crypto;
 const debug = std.debug;
 const fmt = std.fmt;
 const mem = std.mem;
+
 const Sha512 = crypto.hash.sha2.Sha512;
-const Error = crypto.Error;
+
+const EncodingError = crypto.errors.EncodingError;
+const IdentityElementError = crypto.errors.IdentityElementError;
+const NonCanonicalError = crypto.errors.NonCanonicalError;
+const SignatureVerificationError = crypto.errors.SignatureVerificationError;
+const KeyMismatchError = crypto.errors.KeyMismatchError;
+const WeakPublicKeyError = crypto.errors.WeakPublicKeyError;
 
 /// Ed25519 (EdDSA) signatures.
 pub const Ed25519 = struct {
@@ -41,7 +48,7 @@ pub const Ed25519 = struct {
         ///
         /// For this reason, an EdDSA secret key is commonly called a seed,
         /// from which the actual secret is derived.
-        pub fn create(seed: ?[seed_length]u8) Error!KeyPair {
+        pub fn create(seed: ?[seed_length]u8) IdentityElementError!KeyPair {
             const ss = seed orelse ss: {
                 var random_seed: [seed_length]u8 = undefined;
                 crypto.random.bytes(&random_seed);
@@ -51,7 +58,7 @@ pub const Ed25519 = struct {
             var h = Sha512.init(.{});
             h.update(&ss);
             h.final(&az);
-            const p = try Curve.basePoint.clampedMul(az[0..32].*);
+            const p = Curve.basePoint.clampedMul(az[0..32].*) catch return error.IdentityElement;
             var sk: [secret_length]u8 = undefined;
             mem.copy(u8, &sk, &ss);
             const pk = p.toBytes();
@@ -72,7 +79,7 @@ pub const Ed25519 = struct {
     /// Sign a message using a key pair, and optional random noise.
     /// Having noise creates non-standard, non-deterministic signatures,
     /// but has been proven to increase resilience against fault attacks.
-    pub fn sign(msg: []const u8, key_pair: KeyPair, noise: ?[noise_length]u8) Error![signature_length]u8 {
+    pub fn sign(msg: []const u8, key_pair: KeyPair, noise: ?[noise_length]u8) (IdentityElementError || WeakPublicKeyError || KeyMismatchError)![signature_length]u8 {
         const seed = key_pair.secret_key[0..seed_length];
         const public_key = key_pair.secret_key[seed_length..];
         if (!mem.eql(u8, public_key, &key_pair.public_key)) {
@@ -113,7 +120,7 @@ pub const Ed25519 = struct {
 
     /// Verify an Ed25519 signature given a message and a public key.
     /// Returns error.SignatureVerificationFailed is the signature verification failed.
-    pub fn verify(sig: [signature_length]u8, msg: []const u8, public_key: [public_length]u8) Error!void {
+    pub fn verify(sig: [signature_length]u8, msg: []const u8, public_key: [public_length]u8) (SignatureVerificationError || WeakPublicKeyError || EncodingError || NonCanonicalError || IdentityElementError)!void {
         const r = sig[0..32];
         const s = sig[32..64];
         try Curve.scalar.rejectNonCanonical(s.*);
@@ -146,7 +153,7 @@ pub const Ed25519 = struct {
     };
 
     /// Verify several signatures in a single operation, much faster than verifying signatures one-by-one
-    pub fn verifyBatch(comptime count: usize, signature_batch: [count]BatchElement) Error!void {
+    pub fn verifyBatch(comptime count: usize, signature_batch: [count]BatchElement) (SignatureVerificationError || IdentityElementError || WeakPublicKeyError || EncodingError || NonCanonicalError)!void {
         var r_batch: [count][32]u8 = undefined;
         var s_batch: [count][32]u8 = undefined;
         var a_batch: [count]Curve = undefined;
@@ -180,7 +187,7 @@ pub const Ed25519 = struct {
 
         var z_batch: [count]Curve.scalar.CompressedScalar = undefined;
         for (z_batch) |*z| {
-            std.crypto.random.bytes(z[0..16]);
+            crypto.random.bytes(z[0..16]);
             mem.set(u8, z[16..], 0);
         }
 
@@ -233,8 +240,8 @@ test "ed25519 batch verification" {
         const key_pair = try Ed25519.KeyPair.create(null);
         var msg1: [32]u8 = undefined;
         var msg2: [32]u8 = undefined;
-        std.crypto.random.bytes(&msg1);
-        std.crypto.random.bytes(&msg2);
+        crypto.random.bytes(&msg1);
+        crypto.random.bytes(&msg2);
         const sig1 = try Ed25519.sign(&msg1, key_pair, null);
         const sig2 = try Ed25519.sign(&msg2, key_pair, null);
         var signature_batch = [_]Ed25519.BatchElement{
lib/std/crypto/25519/edwards25519.zig
@@ -4,10 +4,16 @@
 // The MIT license requires this copyright notice to be included in all copies
 // and substantial portions of the software.
 const std = @import("std");
+const crypto = std.crypto;
 const debug = std.debug;
 const fmt = std.fmt;
 const mem = std.mem;
-const Error = std.crypto.Error;
+
+const EncodingError = crypto.errors.EncodingError;
+const IdentityElementError = crypto.errors.IdentityElementError;
+const NonCanonicalError = crypto.errors.NonCanonicalError;
+const NotSquareError = crypto.errors.NotSquareError;
+const WeakPublicKeyError = crypto.errors.WeakPublicKeyError;
 
 /// Group operations over Edwards25519.
 pub const Edwards25519 = struct {
@@ -26,7 +32,7 @@ pub const Edwards25519 = struct {
     is_base: bool = false,
 
     /// Decode an Edwards25519 point from its compressed (Y+sign) coordinates.
-    pub fn fromBytes(s: [encoded_length]u8) Error!Edwards25519 {
+    pub fn fromBytes(s: [encoded_length]u8) EncodingError!Edwards25519 {
         const z = Fe.one;
         const y = Fe.fromBytes(s);
         var u = y.sq();
@@ -56,7 +62,7 @@ pub const Edwards25519 = struct {
     }
 
     /// Check that the encoding of a point is canonical.
-    pub fn rejectNonCanonical(s: [32]u8) Error!void {
+    pub fn rejectNonCanonical(s: [32]u8) NonCanonicalError!void {
         return Fe.rejectNonCanonical(s, true);
     }
 
@@ -81,7 +87,7 @@ pub const Edwards25519 = struct {
     const identityElement = Edwards25519{ .x = Fe.zero, .y = Fe.one, .z = Fe.one, .t = Fe.zero };
 
     /// Reject the neutral element.
-    pub fn rejectIdentity(p: Edwards25519) Error!void {
+    pub fn rejectIdentity(p: Edwards25519) IdentityElementError!void {
         if (p.x.isZero()) {
             return error.IdentityElement;
         }
@@ -177,7 +183,7 @@ pub const Edwards25519 = struct {
     // Based on real-world benchmarks, we only use this for multi-scalar multiplication.
     // NAF could be useful to half the size of precomputation tables, but we intentionally
     // avoid these to keep the standard library lightweight.
-    fn pcMul(pc: [9]Edwards25519, s: [32]u8, comptime vartime: bool) Error!Edwards25519 {
+    fn pcMul(pc: [9]Edwards25519, s: [32]u8, comptime vartime: bool) IdentityElementError!Edwards25519 {
         std.debug.assert(vartime);
         const e = nonAdjacentForm(s);
         var q = Edwards25519.identityElement;
@@ -197,7 +203,7 @@ pub const Edwards25519 = struct {
     }
 
     // Scalar multiplication with a 4-bit window and the first 15 multiples.
-    fn pcMul16(pc: [16]Edwards25519, s: [32]u8, comptime vartime: bool) Error!Edwards25519 {
+    fn pcMul16(pc: [16]Edwards25519, s: [32]u8, comptime vartime: bool) IdentityElementError!Edwards25519 {
         var q = Edwards25519.identityElement;
         var pos: usize = 252;
         while (true) : (pos -= 4) {
@@ -233,12 +239,12 @@ pub const Edwards25519 = struct {
     };
 
     /// Multiply an Edwards25519 point by a scalar without clamping it.
-    /// Return error.WeakPublicKey if the resulting point is
-    /// the identity element.
-    pub fn mul(p: Edwards25519, s: [32]u8) Error!Edwards25519 {
+    /// Return error.WeakPublicKey if the base generates a small-order group,
+    /// and error.IdentityElement if the result is the identity element.
+    pub fn mul(p: Edwards25519, s: [32]u8) (IdentityElementError || WeakPublicKeyError)!Edwards25519 {
         const pc = if (p.is_base) basePointPc else pc: {
             const xpc = precompute(p, 15);
-            xpc[4].rejectIdentity() catch |_| return error.WeakPublicKey;
+            xpc[4].rejectIdentity() catch return error.WeakPublicKey;
             break :pc xpc;
         };
         return pcMul16(pc, s, false);
@@ -246,7 +252,7 @@ pub const Edwards25519 = struct {
 
     /// Multiply an Edwards25519 point by a *PUBLIC* scalar *IN VARIABLE TIME*
     /// This can be used for signature verification.
-    pub fn mulPublic(p: Edwards25519, s: [32]u8) Error!Edwards25519 {
+    pub fn mulPublic(p: Edwards25519, s: [32]u8) (IdentityElementError || WeakPublicKeyError)!Edwards25519 {
         if (p.is_base) {
             return pcMul16(basePointPc, s, true);
         } else {
@@ -258,7 +264,7 @@ pub const Edwards25519 = struct {
 
     /// Multiscalar multiplication *IN VARIABLE TIME* for public data
     /// Computes ps0*ss0 + ps1*ss1 + ps2*ss2... faster than doing many of these operations individually
-    pub fn mulMulti(comptime count: usize, ps: [count]Edwards25519, ss: [count][32]u8) Error!Edwards25519 {
+    pub fn mulMulti(comptime count: usize, ps: [count]Edwards25519, ss: [count][32]u8) (IdentityElementError || WeakPublicKeyError)!Edwards25519 {
         var pcs: [count][9]Edwards25519 = undefined;
         for (ps) |p, i| {
             if (p.is_base) {
@@ -297,14 +303,14 @@ pub const Edwards25519 = struct {
     /// This is strongly recommended for DH operations.
     /// Return error.WeakPublicKey if the resulting point is
     /// the identity element.
-    pub fn clampedMul(p: Edwards25519, s: [32]u8) Error!Edwards25519 {
+    pub fn clampedMul(p: Edwards25519, s: [32]u8) (IdentityElementError || WeakPublicKeyError)!Edwards25519 {
         var t: [32]u8 = s;
         scalar.clamp(&t);
         return mul(p, t);
     }
 
     // montgomery -- recover y = sqrt(x^3 + A*x^2 + x)
-    fn xmontToYmont(x: Fe) Error!Fe {
+    fn xmontToYmont(x: Fe) NotSquareError!Fe {
         var x2 = x.sq();
         const x3 = x.mul(x2);
         x2 = x2.mul32(Fe.edwards25519a_32);
@@ -367,7 +373,7 @@ pub const Edwards25519 = struct {
 
     fn stringToPoints(comptime n: usize, ctx: []const u8, s: []const u8) [n]Edwards25519 {
         debug.assert(n <= 2);
-        const H = std.crypto.hash.sha2.Sha512;
+        const H = crypto.hash.sha2.Sha512;
         const h_l: usize = 48;
         var xctx = ctx;
         var hctx: [H.digest_length]u8 = undefined;
@@ -485,8 +491,8 @@ test "edwards25519 packing/unpacking" {
 test "edwards25519 point addition/substraction" {
     var s1: [32]u8 = undefined;
     var s2: [32]u8 = undefined;
-    std.crypto.random.bytes(&s1);
-    std.crypto.random.bytes(&s2);
+    crypto.random.bytes(&s1);
+    crypto.random.bytes(&s2);
     const p = try Edwards25519.basePoint.clampedMul(s1);
     const q = try Edwards25519.basePoint.clampedMul(s2);
     const r = p.add(q).add(q).sub(q).sub(q);
lib/std/crypto/25519/field.zig
@@ -4,9 +4,12 @@
 // The MIT license requires this copyright notice to be included in all copies
 // and substantial portions of the software.
 const std = @import("std");
+const crypto = std.crypto;
 const readIntLittle = std.mem.readIntLittle;
 const writeIntLittle = std.mem.writeIntLittle;
-const Error = std.crypto.Error;
+
+const NonCanonicalError = crypto.errors.NonCanonicalError;
+const NotSquareError = crypto.errors.NotSquareError;
 
 pub const Fe = struct {
     limbs: [5]u64,
@@ -113,7 +116,7 @@ pub const Fe = struct {
     }
 
     /// Reject non-canonical encodings of an element, possibly ignoring the top bit
-    pub fn rejectNonCanonical(s: [32]u8, comptime ignore_extra_bit: bool) Error!void {
+    pub fn rejectNonCanonical(s: [32]u8, comptime ignore_extra_bit: bool) NonCanonicalError!void {
         var c: u16 = (s[31] & 0x7f) ^ 0x7f;
         comptime var i = 30;
         inline while (i > 0) : (i -= 1) {
@@ -413,7 +416,7 @@ pub const Fe = struct {
     }
 
     /// Compute the square root of `x2`, returning `error.NotSquare` if `x2` was not a square
-    pub fn sqrt(x2: Fe) Error!Fe {
+    pub fn sqrt(x2: Fe) NotSquareError!Fe {
         var x2_copy = x2;
         const x = x2.uncheckedSqrt();
         const check = x.sq().sub(x2_copy);
lib/std/crypto/25519/ristretto255.zig
@@ -5,7 +5,11 @@
 // and substantial portions of the software.
 const std = @import("std");
 const fmt = std.fmt;
-const Error = std.crypto.Error;
+
+const EncodingError = std.crypto.errors.EncodingError;
+const IdentityElementError = std.crypto.errors.IdentityElementError;
+const NonCanonicalError = std.crypto.errors.NonCanonicalError;
+const WeakPublicKeyError = std.crypto.errors.WeakPublicKeyError;
 
 /// Group operations over Edwards25519.
 pub const Ristretto255 = struct {
@@ -35,7 +39,7 @@ pub const Ristretto255 = struct {
         return .{ .ratio_is_square = @boolToInt(has_m_root) | @boolToInt(has_p_root), .root = x.abs() };
     }
 
-    fn rejectNonCanonical(s: [encoded_length]u8) Error!void {
+    fn rejectNonCanonical(s: [encoded_length]u8) NonCanonicalError!void {
         if ((s[0] & 1) != 0) {
             return error.NonCanonical;
         }
@@ -43,7 +47,7 @@ pub const Ristretto255 = struct {
     }
 
     /// Reject the neutral element.
-    pub fn rejectIdentity(p: Ristretto255) callconv(.Inline) Error!void {
+    pub fn rejectIdentity(p: Ristretto255) callconv(.Inline) IdentityElementError!void {
         return p.p.rejectIdentity();
     }
 
@@ -51,7 +55,7 @@ pub const Ristretto255 = struct {
     pub const basePoint = Ristretto255{ .p = Curve.basePoint };
 
     /// Decode a Ristretto255 representative.
-    pub fn fromBytes(s: [encoded_length]u8) Error!Ristretto255 {
+    pub fn fromBytes(s: [encoded_length]u8) (NonCanonicalError || EncodingError)!Ristretto255 {
         try rejectNonCanonical(s);
         const s_ = Fe.fromBytes(s);
         const ss = s_.sq(); // s^2
@@ -154,7 +158,7 @@ pub const Ristretto255 = struct {
     /// Multiply a Ristretto255 element with a scalar.
     /// Return error.WeakPublicKey if the resulting element is
     /// the identity element.
-    pub fn mul(p: Ristretto255, s: [encoded_length]u8) callconv(.Inline) Error!Ristretto255 {
+    pub fn mul(p: Ristretto255, s: [encoded_length]u8) callconv(.Inline) (IdentityElementError || WeakPublicKeyError)!Ristretto255 {
         return Ristretto255{ .p = try p.p.mul(s) };
     }
 
lib/std/crypto/25519/scalar.zig
@@ -5,7 +5,8 @@
 // and substantial portions of the software.
 const std = @import("std");
 const mem = std.mem;
-const Error = std.crypto.Error;
+
+const NonCanonicalError = std.crypto.errors.NonCanonicalError;
 
 /// 2^252 + 27742317777372353535851937790883648493
 pub const field_size = [32]u8{
@@ -19,7 +20,7 @@ pub const CompressedScalar = [32]u8;
 pub const zero = [_]u8{0} ** 32;
 
 /// Reject a scalar whose encoding is not canonical.
-pub fn rejectNonCanonical(s: [32]u8) Error!void {
+pub fn rejectNonCanonical(s: [32]u8) NonCanonicalError!void {
     var c: u8 = 0;
     var n: u8 = 1;
     var i: usize = 31;
lib/std/crypto/25519/x25519.zig
@@ -9,7 +9,10 @@ const mem = std.mem;
 const fmt = std.fmt;
 
 const Sha512 = crypto.hash.sha2.Sha512;
-const Error = crypto.Error;
+
+const EncodingError = crypto.errors.EncodingError;
+const IdentityElementError = crypto.errors.IdentityElementError;
+const WeakPublicKeyError = crypto.errors.WeakPublicKeyError;
 
 /// X25519 DH function.
 pub const X25519 = struct {
@@ -32,7 +35,7 @@ pub const X25519 = struct {
         secret_key: [secret_length]u8,
 
         /// Create a new key pair using an optional seed.
-        pub fn create(seed: ?[seed_length]u8) Error!KeyPair {
+        pub fn create(seed: ?[seed_length]u8) IdentityElementError!KeyPair {
             const sk = seed orelse sk: {
                 var random_seed: [seed_length]u8 = undefined;
                 crypto.random.bytes(&random_seed);
@@ -45,7 +48,7 @@ pub const X25519 = struct {
         }
 
         /// Create a key pair from an Ed25519 key pair
-        pub fn fromEd25519(ed25519_key_pair: crypto.sign.Ed25519.KeyPair) Error!KeyPair {
+        pub fn fromEd25519(ed25519_key_pair: crypto.sign.Ed25519.KeyPair) (IdentityElementError || EncodingError)!KeyPair {
             const seed = ed25519_key_pair.secret_key[0..32];
             var az: [Sha512.digest_length]u8 = undefined;
             Sha512.hash(seed, &az, .{});
@@ -60,13 +63,13 @@ pub const X25519 = struct {
     };
 
     /// Compute the public key for a given private key.
-    pub fn recoverPublicKey(secret_key: [secret_length]u8) Error![public_length]u8 {
+    pub fn recoverPublicKey(secret_key: [secret_length]u8) IdentityElementError![public_length]u8 {
         const q = try Curve.basePoint.clampedMul(secret_key);
         return q.toBytes();
     }
 
     /// Compute the X25519 equivalent to an Ed25519 public eky.
-    pub fn publicKeyFromEd25519(ed25519_public_key: [crypto.sign.Ed25519.public_length]u8) Error![public_length]u8 {
+    pub fn publicKeyFromEd25519(ed25519_public_key: [crypto.sign.Ed25519.public_length]u8) (IdentityElementError || EncodingError)![public_length]u8 {
         const pk_ed = try crypto.ecc.Edwards25519.fromBytes(ed25519_public_key);
         const pk = try Curve.fromEdwards25519(pk_ed);
         return pk.toBytes();
@@ -75,7 +78,7 @@ pub const X25519 = struct {
     /// Compute the scalar product of a public key and a secret scalar.
     /// Note that the output should not be used as a shared secret without
     /// hashing it first.
-    pub fn scalarmult(secret_key: [secret_length]u8, public_key: [public_length]u8) Error![shared_length]u8 {
+    pub fn scalarmult(secret_key: [secret_length]u8, public_key: [public_length]u8) IdentityElementError![shared_length]u8 {
         const q = try Curve.fromBytes(public_key).clampedMul(secret_key);
         return q.toBytes();
     }
lib/std/crypto/aegis.zig
@@ -8,7 +8,7 @@ const std = @import("std");
 const mem = std.mem;
 const assert = std.debug.assert;
 const AesBlock = std.crypto.core.aes.Block;
-const Error = std.crypto.Error;
+const AuthenticationError = std.crypto.errors.AuthenticationError;
 
 const State128L = struct {
     blocks: [8]AesBlock,
@@ -137,7 +137,7 @@ pub const Aegis128L = struct {
     /// ad: Associated Data
     /// npub: public nonce
     /// k: private key
-    pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) Error!void {
+    pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) AuthenticationError!void {
         assert(c.len == m.len);
         var state = State128L.init(key, npub);
         var src: [32]u8 align(16) = undefined;
@@ -299,7 +299,7 @@ pub const Aegis256 = struct {
     /// ad: Associated Data
     /// npub: public nonce
     /// k: private key
-    pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) Error!void {
+    pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) AuthenticationError!void {
         assert(c.len == m.len);
         var state = State256.init(key, npub);
         var src: [16]u8 align(16) = undefined;
lib/std/crypto/aes_gcm.zig
@@ -12,7 +12,7 @@ const debug = std.debug;
 const Ghash = std.crypto.onetimeauth.Ghash;
 const mem = std.mem;
 const modes = crypto.core.modes;
-const Error = crypto.Error;
+const AuthenticationError = crypto.errors.AuthenticationError;
 
 pub const Aes128Gcm = AesGcm(crypto.core.aes.Aes128);
 pub const Aes256Gcm = AesGcm(crypto.core.aes.Aes256);
@@ -60,7 +60,7 @@ fn AesGcm(comptime Aes: anytype) type {
             }
         }
 
-        pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) Error!void {
+        pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) AuthenticationError!void {
             assert(c.len == m.len);
 
             const aes = Aes.initEnc(key);
lib/std/crypto/aes_ocb.zig
@@ -10,7 +10,7 @@ const aes = crypto.core.aes;
 const assert = std.debug.assert;
 const math = std.math;
 const mem = std.mem;
-const Error = crypto.Error;
+const AuthenticationError = crypto.errors.AuthenticationError;
 
 pub const Aes128Ocb = AesOcb(aes.Aes128);
 pub const Aes256Ocb = AesOcb(aes.Aes256);
@@ -179,7 +179,7 @@ fn AesOcb(comptime Aes: anytype) type {
         /// ad: Associated Data
         /// npub: public nonce
         /// k: secret key
-        pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) Error!void {
+        pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) AuthenticationError!void {
             assert(c.len == m.len);
 
             const aes_enc_ctx = Aes.initEnc(key);
lib/std/crypto/bcrypt.zig
@@ -12,7 +12,8 @@ const mem = std.mem;
 const debug = std.debug;
 const testing = std.testing;
 const utils = crypto.utils;
-const Error = crypto.Error;
+const EncodingError = crypto.errors.EncodingError;
+const PasswordVerificationError = crypto.errors.PasswordVerificationError;
 
 const salt_length: usize = 16;
 const salt_str_length: usize = 22;
@@ -179,7 +180,7 @@ const Codec = struct {
         debug.assert(j == b64.len);
     }
 
-    fn decode(bin: []u8, b64: []const u8) Error!void {
+    fn decode(bin: []u8, b64: []const u8) EncodingError!void {
         var i: usize = 0;
         var j: usize = 0;
         while (j < bin.len) {
@@ -204,7 +205,7 @@ const Codec = struct {
     }
 };
 
-fn strHashInternal(password: []const u8, rounds_log: u6, salt: [salt_length]u8) Error![hash_length]u8 {
+fn strHashInternal(password: []const u8, rounds_log: u6, salt: [salt_length]u8) ![hash_length]u8 {
     var state = State{};
     var password_buf: [73]u8 = undefined;
     const trimmed_len = math.min(password.len, password_buf.len - 1);
@@ -252,14 +253,14 @@ fn strHashInternal(password: []const u8, rounds_log: u6, salt: [salt_length]u8)
 /// IMPORTANT: by design, bcrypt silently truncates passwords to 72 bytes.
 /// If this is an issue for your application, hash the password first using a function such as SHA-512,
 /// and then use the resulting hash as the password parameter for bcrypt.
-pub fn strHash(password: []const u8, rounds_log: u6) Error![hash_length]u8 {
+pub fn strHash(password: []const u8, rounds_log: u6) ![hash_length]u8 {
     var salt: [salt_length]u8 = undefined;
     crypto.random.bytes(&salt);
     return strHashInternal(password, rounds_log, salt);
 }
 
 /// Verify that a previously computed hash is valid for a given password.
-pub fn strVerify(h: [hash_length]u8, password: []const u8) Error!void {
+pub fn strVerify(h: [hash_length]u8, password: []const u8) (EncodingError || PasswordVerificationError)!void {
     if (!mem.eql(u8, "$2", h[0..2])) return error.InvalidEncoding;
     if (h[3] != '$' or h[6] != '$') return error.InvalidEncoding;
     const rounds_log_str = h[4..][0..2];
lib/std/crypto/chacha20.zig
@@ -13,7 +13,7 @@ const testing = std.testing;
 const maxInt = math.maxInt;
 const Vector = std.meta.Vector;
 const Poly1305 = std.crypto.onetimeauth.Poly1305;
-const Error = std.crypto.Error;
+const AuthenticationError = std.crypto.errors.AuthenticationError;
 
 /// IETF-variant of the ChaCha20 stream cipher, as designed for TLS.
 pub const ChaCha20IETF = ChaChaIETF(20);
@@ -521,7 +521,7 @@ fn ChaChaPoly1305(comptime rounds_nb: usize) type {
         /// npub: public nonce
         /// k: private key
         /// NOTE: the check of the authentication tag is currently not done in constant time
-        pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) Error!void {
+        pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) AuthenticationError!void {
             assert(c.len == m.len);
 
             var polyKey = [_]u8{0} ** 32;
@@ -583,7 +583,7 @@ fn XChaChaPoly1305(comptime rounds_nb: usize) type {
         /// ad: Associated Data
         /// npub: public nonce
         /// k: private key
-        pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) Error!void {
+        pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) AuthenticationError!void {
             const extended = extend(k, npub, rounds_nb);
             return ChaChaPoly1305(rounds_nb).decrypt(m, c, tag, ad, extended.nonce, extended.key);
         }
lib/std/crypto/error.zig
@@ -1,34 +0,0 @@
-pub const Error = error{
-    /// MAC verification failed - The tag doesn't verify for the given ciphertext and secret key
-    AuthenticationFailed,
-
-    /// The requested output length is too long for the chosen algorithm
-    OutputTooLong,
-
-    /// Finite field operation returned the identity element
-    IdentityElement,
-
-    /// Encoded input cannot be decoded
-    InvalidEncoding,
-
-    /// The signature does't verify for the given message and public key
-    SignatureVerificationFailed,
-
-    /// Both a public and secret key have been provided, but they are incompatible
-    KeyMismatch,
-
-    /// Encoded input is not in canonical form
-    NonCanonical,
-
-    /// Square root has no solutions
-    NotSquare,
-
-    /// Verification string doesn't match the provided password and parameters
-    PasswordVerificationFailed,
-
-    /// Parameters would be insecure to use
-    WeakParameters,
-
-    /// Public key would be insecure to use
-    WeakPublicKey,
-};
lib/std/crypto/errors.zig
@@ -0,0 +1,35 @@
+/// MAC verification failed - The tag doesn't verify for the given ciphertext and secret key
+pub const AuthenticationError = error{AuthenticationFailed};
+
+/// The requested output length is too long for the chosen algorithm
+pub const OutputTooLongError = error{OutputTooLong};
+
+/// Finite field operation returned the identity element
+pub const IdentityElementError = error{IdentityElement};
+
+/// Encoded input cannot be decoded
+pub const EncodingError = error{InvalidEncoding};
+
+/// The signature does't verify for the given message and public key
+pub const SignatureVerificationError = error{SignatureVerificationFailed};
+
+/// Both a public and secret key have been provided, but they are incompatible
+pub const KeyMismatchError = error{KeyMismatch};
+
+/// Encoded input is not in canonical form
+pub const NonCanonicalError = error{NonCanonical};
+
+/// Square root has no solutions
+pub const NotSquareError = error{NotSquare};
+
+/// Verification string doesn't match the provided password and parameters
+pub const PasswordVerificationError = error{PasswordVerificationFailed};
+
+/// Parameters would be insecure to use
+pub const WeakParametersError = error{WeakParameters};
+
+/// Public key would be insecure to use
+pub const WeakPublicKeyError = error{WeakPublicKey};
+
+/// Any error related to cryptography operations
+pub const Error = AuthenticationError || OutputTooLongError || IdentityElementError || EncodingError || SignatureVerificationError || KeyMismatchError || NonCanonicalError || NotSquareError || PasswordVerificationError || WeakParametersError || WeakPublicKeyError;
lib/std/crypto/gimli.zig
@@ -20,7 +20,7 @@ const assert = std.debug.assert;
 const testing = std.testing;
 const htest = @import("test.zig");
 const Vector = std.meta.Vector;
-const Error = std.crypto.Error;
+const AuthenticationError = std.crypto.errors.AuthenticationError;
 
 pub const State = struct {
     pub const BLOCKBYTES = 48;
@@ -393,7 +393,7 @@ pub const Aead = struct {
     /// npub: public nonce
     /// k: private key
     /// NOTE: the check of the authentication tag is currently not done in constant time
-    pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) Error!void {
+    pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) AuthenticationError!void {
         assert(c.len == m.len);
 
         var state = Aead.init(ad, npub, k);
lib/std/crypto/isap.zig
@@ -3,7 +3,7 @@ const debug = std.debug;
 const mem = std.mem;
 const math = std.math;
 const testing = std.testing;
-const Error = std.crypto.Error;
+const AuthenticationError = std.crypto.errors.AuthenticationError;
 
 /// ISAPv2 is an authenticated encryption system hardened against side channels and fault attacks.
 /// https://csrc.nist.gov/CSRC/media/Projects/lightweight-cryptography/documents/round-2/spec-doc-rnd2/isap-spec-round2.pdf
@@ -218,7 +218,7 @@ pub const IsapA128A = struct {
         tag.* = mac(c, ad, npub, key);
     }
 
-    pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) Error!void {
+    pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) AuthenticationError!void {
         var computed_tag = mac(c, ad, npub, key);
         var acc: u8 = 0;
         for (computed_tag) |_, j| {
lib/std/crypto/pbkdf2.zig
@@ -7,7 +7,8 @@
 const std = @import("std");
 const mem = std.mem;
 const maxInt = std.math.maxInt;
-const Error = std.crypto.Error;
+const OutputTooLongError = std.crypto.errors.OutputTooLongError;
+const WeakParametersError = std.crypto.errors.WeakParametersError;
 
 // RFC 2898 Section 5.2
 //
@@ -55,7 +56,7 @@ const Error = std.crypto.Error;
 ///         the dk. It is common to tune this parameter to achieve approximately 100ms.
 ///
 /// Prf: Pseudo-random function to use. A common choice is `std.crypto.auth.hmac.HmacSha256`.
-pub fn pbkdf2(dk: []u8, password: []const u8, salt: []const u8, rounds: u32, comptime Prf: type) Error!void {
+pub fn pbkdf2(dk: []u8, password: []const u8, salt: []const u8, rounds: u32, comptime Prf: type) (WeakParametersError || OutputTooLongError)!void {
     if (rounds < 1) return error.WeakParameters;
 
     const dk_len = dk.len;
lib/std/crypto/salsa20.zig
@@ -15,7 +15,10 @@ const Vector = std.meta.Vector;
 const Poly1305 = crypto.onetimeauth.Poly1305;
 const Blake2b = crypto.hash.blake2.Blake2b;
 const X25519 = crypto.dh.X25519;
-const Error = crypto.Error;
+
+const AuthenticationError = crypto.errors.AuthenticationError;
+const IdentityElementError = crypto.errors.IdentityElementError;
+const WeakPublicKeyError = crypto.errors.WeakPublicKeyError;
 
 const Salsa20VecImpl = struct {
     const Lane = Vector(4, u32);
@@ -399,7 +402,7 @@ pub const XSalsa20Poly1305 = struct {
     /// ad: Associated Data
     /// npub: public nonce
     /// k: private key
-    pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) Error!void {
+    pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) AuthenticationError!void {
         debug.assert(c.len == m.len);
         const extended = extend(k, npub);
         var block0 = [_]u8{0} ** 64;
@@ -447,7 +450,7 @@ pub const SecretBox = struct {
 
     /// Verify and decrypt `c` using a nonce `npub` and a key `k`.
     /// `m` must be exactly `tag_length` smaller than `c`, as `c` includes an authentication tag in addition to the encrypted message.
-    pub fn open(m: []u8, c: []const u8, npub: [nonce_length]u8, k: [key_length]u8) Error!void {
+    pub fn open(m: []u8, c: []const u8, npub: [nonce_length]u8, k: [key_length]u8) AuthenticationError!void {
         if (c.len < tag_length) {
             return error.AuthenticationFailed;
         }
@@ -482,20 +485,20 @@ pub const Box = struct {
     pub const KeyPair = X25519.KeyPair;
 
     /// Compute a secret suitable for `secretbox` given a recipent's public key and a sender's secret key.
-    pub fn createSharedSecret(public_key: [public_length]u8, secret_key: [secret_length]u8) Error![shared_length]u8 {
+    pub fn createSharedSecret(public_key: [public_length]u8, secret_key: [secret_length]u8) (IdentityElementError || WeakPublicKeyError)![shared_length]u8 {
         const p = try X25519.scalarmult(secret_key, public_key);
         const zero = [_]u8{0} ** 16;
         return Salsa20Impl.hsalsa20(zero, p);
     }
 
     /// Encrypt and authenticate a message using a recipient's public key `public_key` and a sender's `secret_key`.
-    pub fn seal(c: []u8, m: []const u8, npub: [nonce_length]u8, public_key: [public_length]u8, secret_key: [secret_length]u8) Error!void {
+    pub fn seal(c: []u8, m: []const u8, npub: [nonce_length]u8, public_key: [public_length]u8, secret_key: [secret_length]u8) (IdentityElementError || WeakPublicKeyError)!void {
         const shared_key = try createSharedSecret(public_key, secret_key);
         return SecretBox.seal(c, m, npub, shared_key);
     }
 
     /// Verify and decrypt a message using a recipient's secret key `public_key` and a sender's `public_key`.
-    pub fn open(m: []u8, c: []const u8, npub: [nonce_length]u8, public_key: [public_length]u8, secret_key: [secret_length]u8) Error!void {
+    pub fn open(m: []u8, c: []const u8, npub: [nonce_length]u8, public_key: [public_length]u8, secret_key: [secret_length]u8) (IdentityElementError || WeakPublicKeyError || AuthenticationError)!void {
         const shared_key = try createSharedSecret(public_key, secret_key);
         return SecretBox.open(m, c, npub, shared_key);
     }
@@ -528,7 +531,7 @@ pub const SealedBox = struct {
 
     /// Encrypt a message `m` for a recipient whose public key is `public_key`.
     /// `c` must be `seal_length` bytes larger than `m`, so that the required metadata can be added.
-    pub fn seal(c: []u8, m: []const u8, public_key: [public_length]u8) Error!void {
+    pub fn seal(c: []u8, m: []const u8, public_key: [public_length]u8) (WeakPublicKeyError || IdentityElementError)!void {
         debug.assert(c.len == m.len + seal_length);
         var ekp = try KeyPair.create(null);
         const nonce = createNonce(ekp.public_key, public_key);
@@ -539,7 +542,7 @@ pub const SealedBox = struct {
 
     /// Decrypt a message using a key pair.
     /// `m` must be exactly `seal_length` bytes smaller than `c`, as `c` also includes metadata.
-    pub fn open(m: []u8, c: []const u8, keypair: KeyPair) Error!void {
+    pub fn open(m: []u8, c: []const u8, keypair: KeyPair) (IdentityElementError || WeakPublicKeyError || AuthenticationError)!void {
         if (c.len < seal_length) {
             return error.AuthenticationFailed;
         }
lib/std/crypto.zig
@@ -154,7 +154,7 @@ pub const random = &@import("crypto/tlcsprng.zig").interface;
 
 const std = @import("std.zig");
 
-pub const Error = @import("crypto/error.zig").Error;
+pub const errors = @import("crypto/errors.zig");
 
 test "crypto" {
     const please_windows_dont_oom = std.Target.current.os.tag == .windows;