struct PSSSignature [src]
RFC 3447 8.1 RSASSA-PSS
Members
- concatVerify (Function)
- fromBytes (Function)
- verify (Function)
- VerifyError (Error Set)
Source
pub const PSSSignature = 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 mod_bits = public_key.n.bits();
const em_dec = try encrypt(modulus_len, sig, public_key);
try EMSA_PSS_VERIFY(msg, &em_dec, mod_bits - 1, Hash.digest_length, Hash);
}
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
// the hash function (2^61 - 1 octets for SHA-1), output
// "inconsistent" and stop.
// All the cryptographic hash functions in the standard library have a limit of >= 2^61 - 1.
// Even then, this check is only there for paranoia. In the context of TLS certificates, emBit cannot exceed 4096.
if (emBit >= 1 << 61) return error.InvalidSignature;
// emLen = \ceil(emBits/8)
const emLen = ((emBit - 1) / 8) + 1;
std.debug.assert(emLen == em.len);
// 2. Let mHash = Hash(M), an octet string of length hLen.
var mHash: [Hash.digest_length]u8 = undefined;
{
var hasher: Hash = .init(.{});
for (msg) |part| hasher.update(part);
hasher.final(&mHash);
}
// 3. If emLen < hLen + sLen + 2, output "inconsistent" and stop.
if (emLen < Hash.digest_length + sLen + 2) {
return error.InvalidSignature;
}
// 4. If the rightmost octet of EM does not have hexadecimal value
// 0xbc, output "inconsistent" and stop.
if (em[em.len - 1] != 0xbc) {
return error.InvalidSignature;
}
// 5. Let maskedDB be the leftmost emLen - hLen - 1 octets of EM,
// and let H be the next hLen octets.
const maskedDB = em[0..(emLen - Hash.digest_length - 1)];
const h = em[(emLen - Hash.digest_length - 1)..(emLen - 1)][0..Hash.digest_length];
// 6. If the leftmost 8emLen - emBits bits of the leftmost octet in
// maskedDB are not all equal to zero, output "inconsistent" and
// stop.
const zero_bits = emLen * 8 - emBit;
var mask: u8 = maskedDB[0];
var i: usize = 0;
while (i < 8 - zero_bits) : (i += 1) {
mask = mask >> 1;
}
if (mask != 0) {
return error.InvalidSignature;
}
// 7. Let dbMask = MGF(H, emLen - hLen - 1).
const mgf_len = emLen - Hash.digest_length - 1;
var mgf_out_buf: [512]u8 = undefined;
if (mgf_len > mgf_out_buf.len) { // Modulus > 4096 bits
return error.InvalidSignature;
}
const mgf_out = mgf_out_buf[0 .. ((mgf_len - 1) / Hash.digest_length + 1) * Hash.digest_length];
var dbMask = try MGF1(Hash, mgf_out, h, mgf_len);
// 8. Let DB = maskedDB \xor dbMask.
i = 0;
while (i < dbMask.len) : (i += 1) {
dbMask[i] = maskedDB[i] ^ dbMask[i];
}
// 9. Set the leftmost 8emLen - emBits bits of the leftmost octet
// in DB to zero.
i = 0;
mask = 0;
while (i < 8 - zero_bits) : (i += 1) {
mask = mask << 1;
mask += 1;
}
dbMask[0] = dbMask[0] & mask;
// 10. If the emLen - hLen - sLen - 2 leftmost octets of DB are not
// zero or if the octet at position emLen - hLen - sLen - 1 (the
// leftmost position is "position 1") does not have hexadecimal
// value 0x01, output "inconsistent" and stop.
if (dbMask[mgf_len - sLen - 2] != 0x00) {
return error.InvalidSignature;
}
if (dbMask[mgf_len - sLen - 1] != 0x01) {
return error.InvalidSignature;
}
// 11. Let salt be the last sLen octets of DB.
const salt = dbMask[(mgf_len - sLen)..];
// 12. Let
// M' = (0x)00 00 00 00 00 00 00 00 || mHash || salt ;
// M' is an octet string of length 8 + hLen + sLen with eight
// initial zero octets.
if (sLen > Hash.digest_length) { // A seed larger than the hash length would be useless
return error.InvalidSignature;
}
var m_p_buf: [8 + Hash.digest_length + Hash.digest_length]u8 = undefined;
var m_p = m_p_buf[0 .. 8 + Hash.digest_length + sLen];
std.mem.copyForwards(u8, m_p, &([_]u8{0} ** 8));
std.mem.copyForwards(u8, m_p[8..], &mHash);
std.mem.copyForwards(u8, m_p[(8 + Hash.digest_length)..], salt);
// 13. Let H' = Hash(M'), an octet string of length hLen.
var h_p: [Hash.digest_length]u8 = undefined;
Hash.hash(m_p, &h_p, .{});
// 14. If H = H', output "consistent". Otherwise, output
// "inconsistent".
if (!std.mem.eql(u8, h, &h_p)) {
return error.InvalidSignature;
}
}
fn MGF1(comptime Hash: type, out: []u8, seed: *const [Hash.digest_length]u8, len: usize) ![]u8 {
var counter: u32 = 0;
var idx: usize = 0;
var hash = seed.* ++ @as([4]u8, undefined);
while (idx < len) {
std.mem.writeInt(u32, hash[seed.len..][0..4], counter, .big);
Hash.hash(&hash, out[idx..][0..Hash.digest_length], .{});
idx += Hash.digest_length;
counter += 1;
}
return out[0..len];
}
}