mirror of
https://github.com/vlang/v.git
synced 2025-08-03 09:47:15 -04:00
crypto.sha3: add support for Keccak-256 and Keccak-512 (#23058)
This commit is contained in:
parent
8cc4a81aa6
commit
b471887df2
@ -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) }
|
||||
|
@ -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,
|
||||
|
95
vlib/crypto/sha3/usage_test.v
Normal file
95
vlib/crypto/sha3/usage_test.v
Normal 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'
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user