diff --git a/Project.toml b/Project.toml index ab4e49e..583a563 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "CryptoSignatures" uuid = "35cc5888-0c46-470e-89c7-eafcaf79a1aa" authors = ["Janis Erdmanis "] -version = "0.3.3" +version = "0.4.0" [deps] CryptoGroups = "bc997328-bedd-407e-bcd3-5758e064a52d" @@ -11,7 +11,7 @@ Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" [compat] CryptoGroups = "0.5" -CryptoPRG = "0.1.1" +CryptoPRG = "0.1" Nettle = "1" julia = "1" diff --git a/README.md b/README.md index fcda4b9..b3d2635 100644 --- a/README.md +++ b/README.md @@ -12,11 +12,11 @@ The first step is to select a curve to make a cryptographic signature with an el using CryptoSignatures import CryptoGroups -curve = CryptoGroups.curve("secp192r1") -ctx = ECDSAContext(curve, "sha1") +curve = CryptoGroups.spec(:secp192r1) +ctx = DSAContext(curve, "sha1") ``` -where `ctx` stores all relevant parameters on how to make and verify signatures. The second argument specifies a hash function name, which is forwarded to `Nettle`. In case hashing is done externally to avoid hashing twice, nothing can be passed as an argument like `ECDSAContext(Curve_P_192, nothing)`. +where `ctx` stores all relevant parameters on how to make and verify signatures. The second argument specifies a hash function name, which is forwarded to `Nettle`. In case hashing is done externally to avoid hashing twice, nothing can be passed as an argument like `DSAContext(Curve_P_192, nothing)`. To make a signature, first, we need to pick a key and calculate a corresponding public key: @@ -49,13 +49,9 @@ To use an ordinary DSA with modular arithmetics, we need to instantiate the `DSA ```julia using CryptoSignatures -import CryptoGroups.Specs: generate_pq, generate_g, MODP - -p, q = generate_qp(100) # group order with 100 bits as an example (use > 2000)! -g = generate_g(p, q) - -group = MODP(; p, q, g) +import CryptoGroups +group = CryptoGroups.spec(:RFC5114_2048_224) ctx = DSAContext(group, "sha1") ``` diff --git a/src/CryptoSignatures.jl b/src/CryptoSignatures.jl index b9950c5..66b1a7e 100644 --- a/src/CryptoSignatures.jl +++ b/src/CryptoSignatures.jl @@ -1,8 +1,8 @@ module CryptoSignatures -using CryptoGroups: CryptoGroups, generator, concretize_type, octet, order, PGroup -using CryptoGroups.Curves: ECPoint, gx, gy -using CryptoGroups.Specs: MODP, ECP, EC2N, Koblitz, modulus +using CryptoGroups: CryptoGroups, generator, concretize_type, octet, order, PGroup, ECGroup, Group +#using CryptoGroups.Curves: ECPoint, gx, gy +using CryptoGroups.Specs: MODP, ECP, EC2N, Koblitz, GroupSpec using CryptoGroups.Utils: octet2int, int2octet, @check using CryptoPRG: bitlength @@ -37,23 +37,24 @@ function generate_key(order::Integer) end end -function generate_k(order::Integer, key::BigInt, message::Vector{UInt8}, counter::UInt8 = 0x00) +#function generate_k(order::Integer, key::BigInt, message::Vector{UInt8}, counter::UInt8 = 0x00) +function generate_k(order::Integer, key::BigInt, e::BigInt, counter::UInt8 = 0x00) n = bitlength(order) key_bytes = int2octet(key, n) + e_bytes = int2octet(e, n) - prg = PRG("sha256"; s = UInt8[SEED..., key_bytes..., message..., counter]) + prg = PRG("sha256"; s = UInt8[SEED..., key_bytes..., e_bytes..., counter]) k = rand(prg, BigInt; n) % order if k == 0 || k == 1 - return generate_k(order, key, message, counter + 0x01) + return generate_k(order, key, e, counter + 0x01) else return k end end - struct DSA r::BigInt s::BigInt @@ -61,199 +62,122 @@ end Base.:(==)(x::DSA, y::DSA) = x.r == y.r && x.s == y.s -struct ECDSAContext - curve::Union{ECP, EC2N, Koblitz} - hasher::Union{String, Nothing} -end - -CryptoGroups.generator(ctx::ECDSAContext) = generator(ctx.curve) - -generate_key(ctx::ECDSAContext) = generate_key(order(ctx.curve)) - -H(message::Vector{UInt8}, hasher) = octet2int(hex2bytes(hexdigest(hasher, message))) -H(message::Vector{UInt8}, ::Nothing) = octet2int(message) # for situations where hash is computed externally - - -function generator_octet(spec::Union{ECP, EC2N, Koblitz}) - - P = concretize_type(ECPoint, spec) - point = P(generator(spec)) - - return octet(point) -end - -generator_octet(ctx::ECDSAContext) = generator_octet(ctx.curve) - - -function sign(ctx::ECDSAContext, message::Vector{UInt8}, generator::Vector{UInt8}, key::BigInt; counter::UInt8 = 0x00, k::BigInt = generate_k(order(ctx.curve), key, message, counter)) - - P = concretize_type(ECPoint, ctx.curve) # additional parameters could be passed here if needed for different backends - G = P(generator) # in this setting P can also be soft typed - - e = H(message, ctx.hasher) - - R = k*G +function sign(e::BigInt, g::G, key::BigInt; counter::UInt8 = 0x00, k::BigInt = generate_k(order(G), key, e, counter)) where G <: Group - x̄ = gx(R) + q = order(G) - n = order(P) - r = x̄ % n + r = g^k % q - s = invmod(k, n) * (e + key * r) % n + s = invmod(k, q) * (e + key * r) % q - if 1 < r < n - 1 && 1 < s < n - 1 + if 1 < r < q - 1 && 1 < s < q - 1 return DSA(r, s) else - return sign(ctx, message, generator, key; counter = counter + 0x01) + return sign(e, g, key; counter = counter + 0x01) end end - -sign(ctx::ECDSAContext, message::Vector{UInt8}, key::BigInt; kwargs...) = sign(ctx, message, generator_octet(ctx), key; kwargs...) - - -function verify(ctx::ECDSAContext, message::Vector{UInt8}, generator::Vector{UInt8}, pbkey::Vector{UInt8}, signature::DSA) +function verify(e::BigInt, g::G, y::G, signature::DSA) where G <: Group (; r, s) = signature - P = concretize_type(ECPoint, ctx.curve) - G = P(generator) - Q = P(pbkey) - - e = H(message, ctx.hasher) - n = order(P) - - @check 1 < r < n - 1 - @check 1 < s < n - 1 + q = order(G) - c = invmod(s, n) + @check 1 < r < q - 1 + @check 1 < s < q - 1 - u₁ = e*c % n - u₂ = r*c % n + w = invmod(s, q) + + u1 = e * w % q + u2 = r * w % q - if u₁ == 0 - W = u₂*Q - elseif u₂ == 0 - W = u₁*G + # # Raising group element to 0 not allowed. Perhaps need to change that. + if u1 == 0 + v = y^u2 % q + elseif u2 == 0 + v = g^u1 % q else - W = u₁*G + u₂*Q + v = g^u1 * y^u2 % q end - x̄ = gx(W) - ν = x̄ % n # I could also rewrite it as ν = W % n - - return ν == r + return v == r end -verify(ctx::ECDSAContext, message::Vector{UInt8}, pbkey::Vector{UInt8}, signature::DSA) = verify(ctx, message, generator_octet(ctx), pbkey, signature) - - - -function public_key(ctx::ECDSAContext, generator::Vector{UInt8}, private_key::BigInt; mode=:compressed) - - P = concretize_type(ECPoint, ctx.curve) - G = P(generator) - - Q = private_key * G - - return octet(Q; mode) +struct DSAContext + group::GroupSpec + hasher::Union{String, Nothing} end -public_key(ctx::ECDSAContext, private_key::BigInt; mode=:compressed) = public_key(ctx, generator_octet(ctx), private_key; mode) +CryptoGroups.generator(ctx::DSAContext) = generator(ctx.group) +generate_key(ctx::DSAContext) = generate_key(order(ctx.group)) -struct DSAContext - group::MODP - hasher::Union{String, Nothing} -end +H(message::Vector{UInt8}, hasher) = octet2int(hex2bytes(hexdigest(hasher, message))) +H(message::Vector{UInt8}, ::Nothing) = octet2int(message) # for situations where hash is computed externally -CryptoGroups.generator(ctx::DSAContext) = generator(ctx.group) +function generator_octet(spec::GroupSpec) + G = initialize_spec_type(spec) + g = G(generator(spec)) -function generator_octet(spec::MODP) - g = generator(spec) - return int2octet(g, bitlength(modulus(spec))) + return octet(g) end generator_octet(ctx::DSAContext) = generator_octet(ctx.group) +initialize_spec_type(curve::Union{ECP, EC2N, Koblitz}) = concretize_type(ECGroup, curve) +initialize_spec_type(modp::MODP) = concretize_type(PGroup, modp) -function sign(ctx::DSAContext, message::Vector{UInt8}, generator::Vector{UInt8}, key::BigInt; counter::UInt8 = 0x00, k::BigInt = generate_k(order(ctx.group), key, message, counter)) +function sign(ctx::DSAContext, message::Vector{UInt8}, generator::Vector{UInt8}, key::BigInt; k = nothing) - G = concretize_type(PGroup, ctx.group) - g = G(generator) + G = initialize_spec_type(ctx.group) # additional parameters could be passed here if needed for different backends + g = G(generator) # in this setting P can also be soft typed e = H(message, ctx.hasher) - q = order(G) - - r = g^k % q - - s = invmod(k, q) * (e + key * r) % q - if 1 < r < q - 1 && 1 < s < q - 1 - return DSA(r, s) + # Is there a more idiomatic way to do this? + if isnothing(k) + return sign(e, g, key) else - return sign(ctx, message, generator, key; counter = counter + 0x01) + return sign(e, g, key; k) end end - -sign(ctx::DSAContext, message::Vector{UInt8}, key::BigInt; kwargs...) = sign(ctx, message, generator_octet(ctx), key; kwargs...) - +sign(ctx::DSAContext, message::Vector{UInt8}, key::BigInt; k = nothing) = sign(ctx, message, generator_octet(ctx), key; k) function verify(ctx::DSAContext, message::Vector{UInt8}, generator::Vector{UInt8}, pbkey::Vector{UInt8}, signature::DSA) - (; r, s) = signature - G = concretize_type(PGroup, ctx.group) + G = initialize_spec_type(ctx.group) - g = G(generator) + g = G(generator) y = G(pbkey) + e = H(message, ctx.hasher) - e = H(message, ctx.hasher) - q = order(G) - - @check 1 < r < q - 1 - @check 1 < s < q - 1 - - w = invmod(s, q) - - u1 = e * w % q - u2 = r * w % q - - # # Raising group element to 0 not allowed. Perhaps need to change that. - if u1 == 0 - v = y^u2 % q - elseif u2 == 0 - v = g^u1 % q - else - v = g^u1 * y^u2 % q - end - - return v == r + return verify(e, g, y, signature) end verify(ctx::DSAContext, message::Vector{UInt8}, pbkey::Vector{UInt8}, signature::DSA) = verify(ctx, message, generator_octet(ctx), pbkey, signature) - - -function public_key(ctx::DSAContext, generator::Vector{UInt8}, private_key::BigInt) - - G = concretize_type(PGroup, ctx.group) - +function public_key(ctx::DSAContext, generator::Vector{UInt8}, private_key::BigInt; mode=:compressed) + + G = initialize_spec_type(ctx.group) g = G(generator) - Q = g^private_key + y = g^private_key - return octet(Q) + if ctx.group isa MODP + return octet(y) + else + return octet(y; mode) + end end -public_key(ctx::DSAContext, private_key::BigInt) = public_key(ctx, generator_octet(ctx), private_key) +public_key(ctx::DSAContext, private_key::BigInt; mode=:compressed) = public_key(ctx, generator_octet(ctx), private_key; mode) -generate_key(ctx::DSAContext) = generate_key(order(ctx.group)) -export sign, verify, DSA, ECDSAContext, public_key, DSAContext +export sign, verify, generate_key, public_key, DSA, DSAContext end # module diff --git a/test/degenracy.jl b/test/degenracy.jl index 8650b5a..86e70ac 100644 --- a/test/degenracy.jl +++ b/test/degenracy.jl @@ -20,7 +20,7 @@ end curve = ECP(; p = 23, a = 1, b = 4, n = 29, cofactor = 1, Gx = 0, Gy = 2) -ctx = ECDSAContext(curve, "sha256") +ctx = DSAContext(curve, "sha256") for i in 0:255 diff --git a/test/ec2n.jl b/test/ec2n.jl index 59f8c4e..99b6ab4 100644 --- a/test/ec2n.jl +++ b/test/ec2n.jl @@ -14,7 +14,7 @@ curve = EC2N(basis; cofactor = 2 ) -ctx = ECDSAContext(curve, "sha1") +ctx = DSAContext(curve, "sha1") d = 1275552191113212300012030439187146164646146646466749494799 diff --git a/test/ecdsa_example.jl b/test/ecdsa_example.jl index 96be29d..9ef3689 100644 --- a/test/ecdsa_example.jl +++ b/test/ecdsa_example.jl @@ -2,8 +2,8 @@ using Test using CryptoSignatures import CryptoGroups -curve = CryptoGroups.curve("secp192r1") -ctx = ECDSAContext(curve, "sha1") +curve = CryptoGroups.spec(:secp192r1) +ctx = DSAContext(curve, "sha1") private_key = CryptoSignatures.generate_key(ctx) public_key = CryptoSignatures.public_key(ctx, private_key; mode = :uncompressed) diff --git a/test/ecp.jl b/test/ecp.jl index e1f46a8..6caa1ac 100644 --- a/test/ecp.jl +++ b/test/ecp.jl @@ -12,7 +12,7 @@ curve = ECP(; G = hex"03 188DA80E B03090F6 7CBF20EB 43A18800 F4FF0AFD 82FF1012", ) -ctx = ECDSAContext(curve, "sha1") +ctx = DSAContext(curve, "sha1") d = 651056770906015076056810763456358567190100156695615665659