crypto.sha3: add support for Keccak-256 and Keccak-512 (#23058)

This commit is contained in:
SyoBoN 2024-12-05 08:14:15 +09:00 committed by GitHub
parent 8cc4a81aa6
commit b471887df2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 198 additions and 37 deletions

View File

@ -33,12 +33,6 @@ pub const xof_rate_128 = 168
// xof_rate_256 is the capacity, in bytes, of a 256 bit extended output function sponge
pub const xof_rate_256 = 136
// the low order pad bits for a hash function
const hash_pad = u8(0x06)
// the low order pad bits for an extended output function
const xof_pad = u8(0x1f)
// new512 initializes the digest structure for a sha3 512 bit hash
pub fn new512() !&Digest {
return new_digest(rate_512, size_512)!
@ -59,6 +53,16 @@ pub fn new224() !&Digest {
return new_digest(rate_224, size_224)!
}
// new256keccak initializes the digest structure for a keccak 256 bit hash
pub fn new256keccak() !&Digest {
return new_digest(rate_256, size_256, padding: .keccak)!
}
// new512keccak initializes the digest structure for a keccak 512 bit hash
pub fn new512keccak() !&Digest {
return new_digest(rate_512, size_512, padding: .keccak)!
}
// new256_xof initializes the digest structure for a sha3 256 bit extended output function
pub fn new256xof(output_len int) !&Digest {
return new_xof_digest(xof_rate_256, output_len)!
@ -108,22 +112,66 @@ fn (err XOFSizeError) msg() string {
struct Digest {
rate int // the number of bytes absorbed per permutation
suffix u8 // the domain suffix, 0x06 for hash, 0x1f for extended output
suffix u8 // the domain suffix, 0x06 for hash, 0x01 for keccak, 0x1f for extended output
output_len int // the number of bytes to output
mut:
input_buffer []u8 // temporary holding buffer for input bytes
s State // the state of a kaccak-p[1600, 24] sponge
}
// the low order pad bits for a hash function
pub enum Padding as u8 {
keccak = 0x01
sha3 = 0x06
xof = 0x1f
}
@[params]
pub struct PaddingConfig {
pub:
padding Padding = .sha3
}
// new_digest creates an initialized digest structure based on
// the hash size and whether or not you specify a MAC key.
// the hash size.
//
// absorption_rate is the number of bytes to be absorbed into the
// sponge per permutation.
//
// hash_size - the number if bytes in the generated hash.
// Legal values are 224, 256, 384, and 512.
pub fn new_digest(absorption_rate int, hash_size int) !&Digest {
//
// config - the padding setting for hash generation. .sha3 should be used for FIPS PUB 202 compliant SHA3-224, SHA3-256, SHA3-384 and SHA3-512. Use .keccak if you want a legacy Keccak-224, Keccak-256, Keccak-384 or Keccak-512 algorithm. .xof is for extended output functions.
pub fn new_digest(absorption_rate int, hash_size int, config PaddingConfig) !&Digest {
match config.padding {
.sha3, .keccak { validate_sha3(absorption_rate, hash_size)! }
.xof { validate_xof(absorption_rate, hash_size)! }
}
d := Digest{
rate: absorption_rate
suffix: u8(config.padding)
output_len: hash_size
s: State{}
}
return &d
}
// new_xof_digest creates an initialized digest structure based on
// the absorption rate and how many bytes of output you need
//
// absorption_rate is the number of bytes to be absorbed into the
// sponge per permutation. Legal values are xof_rate_128 and
// xof_rate_256.
//
// hash_size - the number if bytes in the generated hash.
// Legal values are positive integers.
pub fn new_xof_digest(absorption_rate int, hash_size int) !&Digest {
return new_digest(absorption_rate, hash_size, padding: .xof)
}
fn validate_sha3(absorption_rate int, hash_size int) ! {
match hash_size {
size_224 {
if absorption_rate != rate_224 {
@ -163,27 +211,9 @@ pub fn new_digest(absorption_rate int, hash_size int) !&Digest {
}
}
}
d := Digest{
rate: absorption_rate
suffix: hash_pad
output_len: hash_size
s: State{}
}
return &d
}
// new_xof_digest creates an initialized digest structure based on
// the absorption rate and how many bytes of output you need
//
// absorption_rate is the number of bytes to be absorbed into the
// sponge per permutation. Legal values are xof_rate_128 and
// xof_rate_256.
//
// hash_size - the number if bytes in the generated hash.
// Legal values are positive integers.
pub fn new_xof_digest(absorption_rate int, hash_size int) !&Digest {
fn validate_xof(absorption_rate int, hash_size int) ! {
match absorption_rate {
xof_rate_128, xof_rate_256 {
if hash_size < 1 {
@ -198,15 +228,6 @@ pub fn new_xof_digest(absorption_rate int, hash_size int) !&Digest {
}
}
}
d := Digest{
rate: absorption_rate
suffix: xof_pad
output_len: hash_size
s: State{}
}
return &d
}
// write adds bytes to the sponge.
@ -345,6 +366,20 @@ pub fn sum224(data []u8) []u8 {
return d.checksum_internal() or { panic(err) }
}
// keccak256 returns the keccak 256 bit checksum of the data.
pub fn keccak256(data []u8) []u8 {
mut d := new256keccak() or { panic(err) }
d.write(data) or { panic(err) }
return d.checksum_internal() or { panic(err) }
}
// keccak512 returns the keccak 512 bit checksum of the data.
pub fn keccak512(data []u8) []u8 {
mut d := new512keccak() or { panic(err) }
d.write(data) or { panic(err) }
return d.checksum_internal() or { panic(err) }
}
// shake256 returns the sha3 shake256 bit extended output
pub fn shake256(data []u8, output_len int) []u8 {
mut d := new256xof(output_len) or { panic(err) }

View File

@ -15,6 +15,14 @@ const empty_message_sha3_512 = [u8(0xA6), 0x9F, 0x73, 0xCC, 0xA2, 0x3A, 0x9A, 0x
0xDC, 0xC1, 0x47, 0x5C, 0x80, 0xA6, 0x15, 0xB2, 0x12, 0x3A, 0xF1, 0xF5, 0xF9, 0x4C, 0x11, 0xE3,
0xE9, 0x40, 0x2C, 0x3A, 0xC5, 0x58, 0xF5, 0x00, 0x19, 0x9D, 0x95, 0xB6, 0xD3, 0xE3, 0x01, 0x75,
0x85, 0x86, 0x28, 0x1D, 0xCD, 0x26]
const empty_message_keccak_256 = [u8(0xC5), 0xD2, 0x46, 0x01, 0x86, 0xF7, 0x23, 0x3C, 0x92, 0x7E,
0x7D, 0xB2, 0xDC, 0xC7, 0x03, 0xC0, 0xE5, 0x00, 0xB6, 0x53, 0xCA, 0x82, 0x27, 0x3B, 0x7B, 0xFA,
0xD8, 0x04, 0x5D, 0x85, 0xA4, 0x70]
const empty_message_keccak_512 = [u8(0x0E), 0xAB, 0x42, 0xDE, 0x4C, 0x3C, 0xEB, 0x92, 0x35, 0xFC,
0x91, 0xAC, 0xFF, 0xE7, 0x46, 0xB2, 0x9C, 0x29, 0xA8, 0xC3, 0x66, 0xB7, 0xC6, 0x0E, 0x4E, 0x67,
0xC4, 0x66, 0xF3, 0x6A, 0x43, 0x04, 0xC0, 0x0F, 0xA9, 0xCA, 0xF9, 0xD8, 0x79, 0x76, 0xBA, 0x46,
0x9B, 0xCB, 0xE0, 0x67, 0x13, 0xB4, 0x35, 0xF0, 0x91, 0xEF, 0x27, 0x69, 0xFB, 0x16, 0x0C, 0xDA,
0xB3, 0x3D, 0x36, 0x70, 0x68, 0x0E]
fn test_0_length_hash() {
input := []u8{}
@ -30,6 +38,12 @@ fn test_0_length_hash() {
output_512 := sum512(input)
assert output_512 == empty_message_sha3_512
output_keccak_256 := keccak256(input)
assert output_keccak_256 == empty_message_keccak_256
output_keccak_512 := keccak512(input)
assert output_keccak_512 == empty_message_keccak_512
}
const input_200 = [u8(0xa3), 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3,
@ -65,6 +79,17 @@ const test_200_message_sha3_512 = [u8(0xE7), 0x6D, 0xFA, 0xD2, 0x20, 0x84, 0xA8,
0x84, 0x73, 0x9A, 0x2D, 0xF4, 0x6B, 0xE5, 0x89, 0xC5, 0x1C, 0xA1, 0xA4, 0xA8, 0x41, 0x6D, 0xF6,
0x54, 0x5A, 0x1C, 0xE8, 0xBA, 0x00]
// The values are taken from the output of Go's sha3 library
const test_200_message_keccak_256 = [u8(0x3A), 0x57, 0x66, 0x6B, 0x04, 0x87, 0x77, 0xF2, 0xC9,
0x53, 0xDC, 0x44, 0x56, 0xF4, 0x5A, 0x25, 0x88, 0xE1, 0xCB, 0x6F, 0x2D, 0xA7, 0x60, 0x12, 0x2D,
0x53, 0x0A, 0xC2, 0xCE, 0x60, 0x7D, 0x4A]
const test_200_message_keccak_512 = [u8(0xF4), 0xF8, 0x46, 0xD1, 0x40, 0x84, 0x75, 0x39, 0xF5,
0x3C, 0x3F, 0x08, 0x2C, 0xC4, 0xE6, 0x81, 0x0E, 0x14, 0x3A, 0x5B, 0x4F, 0xC6, 0x2A, 0x20, 0x59,
0x7B, 0x5D, 0x76, 0x04, 0x32, 0x46, 0xB8, 0x6B, 0xD7, 0x14, 0x9B, 0x90, 0x61, 0x40, 0xBB, 0x96,
0x65, 0xA6, 0xCE, 0x83, 0xD9, 0x91, 0xF0, 0x32, 0xF2, 0x29, 0x1D, 0x2F, 0xAE, 0x80, 0xEE, 0xDF,
0xC6, 0xF8, 0x45, 0xCC, 0x16, 0xD5, 0xAE]
fn test_200_length_hash() {
output_224 := sum224(input_200)
assert output_224 == test_200_message_sha3_224
@ -77,6 +102,12 @@ fn test_200_length_hash() {
output_512 := sum512(input_200)
assert output_512 == test_200_message_sha3_512
output_keccak_256 := keccak256(input_200)
assert output_keccak_256 == test_200_message_keccak_256
output_keccak_512 := keccak512(input_200)
assert output_keccak_512 == test_200_message_keccak_512
}
const empty_message_shake128 = [u8(0x7F), 0x9C, 0x2B, 0xA4, 0xE8, 0x8F, 0x82, 0x7D, 0x61, 0x60,

View File

@ -0,0 +1,95 @@
import crypto.sha3
fn test_api_call() {
input := 'abc'.bytes()
// Digest generation
mut digest_224 := sha3.new_digest(sha3.rate_224, sha3.size_224)!
mut digest_256 := sha3.new_digest(sha3.rate_256, sha3.size_256)!
mut digest_384 := sha3.new_digest(sha3.rate_384, sha3.size_384)!
mut digest_512 := sha3.new_digest(sha3.rate_512, sha3.size_512)!
mut digest_keccak_224 := sha3.new_digest(sha3.rate_224, sha3.size_224, padding: .keccak)!
mut digest_keccak_256 := sha3.new_digest(sha3.rate_256, sha3.size_256, padding: .keccak)!
mut digest_keccak_384 := sha3.new_digest(sha3.rate_384, sha3.size_384, padding: .keccak)!
mut digest_keccak_512 := sha3.new_digest(sha3.rate_512, sha3.size_512, padding: .keccak)!
// Result should be same as shortened version
assert digest_224 == sha3.new224()!
assert digest_256 == sha3.new256()!
assert digest_384 == sha3.new384()!
assert digest_512 == sha3.new512()!
assert digest_keccak_256 == sha3.new256keccak()!
assert digest_keccak_512 == sha3.new512keccak()!
// Should not panic
digest_224.write(input)!
digest_256.write(input)!
digest_384.write(input)!
digest_512.write(input)!
digest_keccak_256.write(input)!
digest_keccak_512.write(input)!
// Result should be same as shortened version
assert digest_224.checksum() == sha3.sum224(input)
assert digest_256.checksum() == sha3.sum256(input)
assert digest_384.checksum() == sha3.sum384(input)
assert digest_512.checksum() == sha3.sum512(input)
assert digest_keccak_256.checksum() == sha3.keccak256(input)
assert digest_keccak_512.checksum() == sha3.keccak512(input)
for size in 1 .. 65 {
// Digest generation
mut digest_xof_128 := sha3.new_digest(sha3.xof_rate_128, size, padding: .xof)!
mut digest_xof_256 := sha3.new_digest(sha3.xof_rate_256, size, padding: .xof)!
// Result should be same as shortened version
assert digest_xof_128 == sha3.new_xof_digest(sha3.xof_rate_128, size)!
assert digest_xof_256 == sha3.new_xof_digest(sha3.xof_rate_256, size)!
assert digest_xof_128 == sha3.new128xof(size)!
assert digest_xof_256 == sha3.new256xof(size)!
// Should not panic
digest_xof_128.write(input)!
digest_xof_256.write(input)!
}
// Should be AbsorptionRateError
if should_be_error := sha3.new_digest(sha3.rate_224, sha3.size_256) {
assert false, 'new_digest(sha3.rate_224, sha3.size_256) should be error'
}
if should_be_error := sha3.new_digest(sha3.rate_224, sha3.size_256, padding: .keccak) {
assert false, 'new_digest(sha3.rate_224, sha3.size_256, padding: .keccak) should be error'
}
if should_be_error := sha3.new_digest(0, sha3.size_256) {
assert false, 'new_digest(0, sha3.size_256) should be error'
}
if should_be_error := sha3.new_digest(0, sha3.size_256, padding: .keccak) {
assert false, 'new_digest(0, sha3.size_256, padding: .keccak) should be error'
}
// Should be HashSizeError
if should_be_error := sha3.new_digest(sha3.rate_256, -1) {
assert false, 'new_digest(sha3.rate_256, -1) should be error'
}
if should_be_error := sha3.new_digest(sha3.rate_256, -1, padding: .keccak) {
assert false, 'new_digest(sha3.rate_256, -1, padding: .keccak) should be error'
}
if should_be_error := sha3.new_digest(sha3.rate_256, 100) {
assert false, 'new_digest(sha3.rate_256, -1) should be error'
}
if should_be_error := sha3.new_digest(sha3.rate_256, 100, padding: .keccak) {
assert false, 'new_digest(sha3.rate_256, -1, padding: .keccak) should be error'
}
// Should be XOFSizeError
if should_be_error := sha3.new128xof(-1) {
assert false, 'new128xof(-1) should be error'
}
if should_be_error := sha3.new256xof(-1) {
assert false, 'new256xof(-1) should be error'
}
// Shoud be XOFRateError
if should_be_error := sha3.new_xof_digest(0, sha3.size_256) {
assert false, 'new_xof_digest(0, sha3.size_256) should be error'
}
}