use my keccak PR

This commit is contained in:
Guillaume Ballet 2025-09-30 16:17:15 -04:00
parent c14034a487
commit cbf86b4715

View file

@ -14,85 +14,17 @@
// You should have received a copy of the GNU Lesser General Public License // You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
//go:build ziren
// +build ziren
package crypto package crypto
import ( import (
"errors" "github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime"
"syscall"
"unsafe"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
originalcrypto "github.com/ethereum/go-ethereum/crypto" originalcrypto "github.com/ethereum/go-ethereum/crypto"
) )
// Ziren zkVM system call numbers
const (
// SYS_KECCAK_SPONGE is the system call number for keccak sponge compression in Ziren zkVM
// This performs the keccak-f[1600] permutation on a 1600-bit (200-byte) state
SYS_KECCAK_SPONGE = 0x010109
)
// Keccak256 constants
const (
keccakRate = 136 // 1088 bits = 136 bytes for keccak256
keccakCapacity = 64 // 512 bits = 64 bytes
keccakStateSize = 200 // 1600 bits = 200 bytes
)
// zirenKeccakSponge calls the Ziren zkVM keccak sponge compression function
// This performs the keccak-f[1600] permutation on the 200-byte state
func zirenKeccakSponge(state *[keccakStateSize]byte) error {
_, _, errno := syscall.Syscall(
SYS_KECCAK_SPONGE,
uintptr(unsafe.Pointer(state)), // State pointer (input/output)
0, 0, // Unused parameters
)
if errno != 0 {
return errors.New("keccak sponge syscall failed")
}
return nil
}
// zirenKeccak256 implements full keccak256 using the Ziren sponge syscall
func zirenKeccak256(data []byte) []byte {
// Initialize state to zeros
var state [keccakStateSize]byte
// Pad input according to keccak256 specification
// Padding: append 0x01, then zero or more 0x00 bytes, then 0x80
padded := make([]byte, len(data))
copy(padded, data)
padded = append(padded, 0x01) // Domain separator for keccak256
// Pad to multiple of rate (136 bytes for keccak256)
for len(padded)%keccakRate != (keccakRate - 1) {
padded = append(padded, 0x00)
}
padded = append(padded, 0x80) // Final padding bit
// Absorb phase: process input in chunks of rate size
for i := 0; i < len(padded); i += keccakRate {
// XOR current chunk with state
for j := 0; j < keccakRate && i+j < len(padded); j++ {
state[j] ^= padded[i+j]
}
// Apply keccak-f[1600] permutation via syscall
if err := zirenKeccakSponge(&state); err != nil {
// Fallback to standard implementation on error
return originalcrypto.Keccak256(data)
}
}
// Squeeze phase: extract 32 bytes (256 bits) for keccak256
result := make([]byte, 32)
copy(result, state[:32])
return result
}
// Re-export everything from original crypto package except the parts we're overriding // Re-export everything from original crypto package except the parts we're overriding
var ( var (
S256 = originalcrypto.S256 S256 = originalcrypto.S256
@ -116,142 +48,16 @@ type (
KeccakState = originalcrypto.KeccakState KeccakState = originalcrypto.KeccakState
) )
// zirenKeccakState implements crypto.KeccakState using the Ziren sponge precompile // Keccak256 calculates and returns the Keccak256 hash using the Ziren zkvm_runtime implementation.
type zirenKeccakState struct {
state [keccakStateSize]byte // 200-byte keccak state
absorbed int // Number of bytes absorbed into current block
buffer [keccakRate]byte // Rate-sized buffer for current block
finalized bool // Whether absorption is complete
}
func (k *zirenKeccakState) Reset() {
for i := range k.state {
k.state[i] = 0
}
for i := range k.buffer {
k.buffer[i] = 0
}
k.absorbed = 0
k.finalized = false
}
func (k *zirenKeccakState) Clone() KeccakState {
clone := &zirenKeccakState{
absorbed: k.absorbed,
finalized: k.finalized,
}
copy(clone.state[:], k.state[:])
copy(clone.buffer[:], k.buffer[:])
return clone
}
func (k *zirenKeccakState) Write(data []byte) (int, error) {
if k.finalized {
panic("write to finalized keccak state")
}
written := 0
for len(data) > 0 {
// Fill current block
canWrite := keccakRate - k.absorbed
if canWrite > len(data) {
canWrite = len(data)
}
copy(k.buffer[k.absorbed:], data[:canWrite])
k.absorbed += canWrite
data = data[canWrite:]
written += canWrite
// If block is full, absorb it
if k.absorbed == keccakRate {
k.absorbBlock()
}
}
return written, nil
}
// absorbBlock XORs the current buffer into state and applies the sponge permutation
func (k *zirenKeccakState) absorbBlock() {
// XOR buffer into state
for i := 0; i < keccakRate; i++ {
k.state[i] ^= k.buffer[i]
}
// Apply keccak-f[1600] permutation via Ziren syscall
if err := zirenKeccakSponge(&k.state); err != nil {
// On error, fallback to standard Go implementation
// This shouldn't happen in production but provides safety
fallbackState := originalcrypto.NewKeccakState()
fallbackState.Reset()
fallbackState.Write(k.buffer[:k.absorbed])
fallbackState.Read(k.state[:32])
}
// Reset buffer
k.absorbed = 0
for i := range k.buffer {
k.buffer[i] = 0
}
}
func (k *zirenKeccakState) Read(hash []byte) (int, error) {
if len(hash) < 32 {
return 0, errors.New("hash slice too short")
}
if !k.finalized {
k.finalize()
}
copy(hash[:32], k.state[:32])
return 32, nil
}
// finalize completes the absorption phase with padding
func (k *zirenKeccakState) finalize() {
// Add keccak256 padding: 0x01, then zeros, then 0x80
k.buffer[k.absorbed] = 0x01
k.absorbed++
// Pad with zeros until we have room for final bit
for k.absorbed < keccakRate-1 {
k.buffer[k.absorbed] = 0x00
k.absorbed++
}
// Add final padding bit
k.buffer[keccakRate-1] = 0x80
k.absorbed = keccakRate
// Absorb final block
k.absorbBlock()
k.finalized = true
}
func (k *zirenKeccakState) Sum(data []byte) []byte {
hash := make([]byte, 32)
k.Read(hash)
return append(data, hash...)
}
func (k *zirenKeccakState) Size() int {
return 32
}
func (k *zirenKeccakState) BlockSize() int {
return 136 // keccak256 block size
}
// Keccak256 calculates and returns the Keccak256 hash using the ziren platform precompile.
func Keccak256(data ...[]byte) []byte { func Keccak256(data ...[]byte) []byte {
// For multiple data chunks, concatenate them // For multiple data chunks, concatenate them
if len(data) == 0 { if len(data) == 0 {
return zirenKeccak256(nil) result := zkvm_runtime.Keccak256(nil)
return result[:]
} }
if len(data) == 1 { if len(data) == 1 {
return zirenKeccak256(data[0]) result := zkvm_runtime.Keccak256(data[0])
return result[:]
} }
// Concatenate multiple data chunks // Concatenate multiple data chunks
@ -265,17 +71,20 @@ func Keccak256(data ...[]byte) []byte {
combined = append(combined, d...) combined = append(combined, d...)
} }
return zirenKeccak256(combined) result := zkvm_runtime.Keccak256(combined)
return result[:]
} }
// Keccak256Hash calculates and returns the Keccak256 hash as a Hash using the ziren platform precompile. // Keccak256Hash calculates and returns the Keccak256 hash as a Hash using the Ziren zkvm_runtime implementation.
func Keccak256Hash(data ...[]byte) (h common.Hash) { func Keccak256Hash(data ...[]byte) (h common.Hash) {
hash := Keccak256(data...) hash := Keccak256(data...)
copy(h[:], hash) copy(h[:], hash)
return h return h
} }
// NewKeccakState returns a new keccak state hasher using the ziren platform precompile. // NewKeccakState returns a new keccak state hasher.
// For now, we fallback to the original implementation for the stateful interface.
// TODO: Implement a stateful wrapper around zkvm_runtime.Keccak256 if needed.
func NewKeccakState() KeccakState { func NewKeccakState() KeccakState {
return &zirenKeccakState{} return originalcrypto.NewKeccakState()
} }