git.sr.ht/~pingoo/stdx@v0.0.0-20240218134121-094174641f6e/zign/verify.go (about) 1 package zign 2 3 import ( 4 "bytes" 5 "encoding/base64" 6 "encoding/binary" 7 "errors" 8 "fmt" 9 "io" 10 11 "git.sr.ht/~pingoo/stdx/crypto" 12 "github.com/zeebo/blake3" 13 ) 14 15 type VerifyInput struct { 16 Reader io.Reader 17 HashBlake3 []byte 18 Signature []byte 19 } 20 21 func Verify(base64PublicKey string, input VerifyInput) (err error) { 22 err = VerifyMany(base64PublicKey, []VerifyInput{input}) 23 if err != nil { 24 return 25 } 26 27 return 28 } 29 30 func VerifyMany(base64PublicKey string, input []VerifyInput) (err error) { 31 publicKeyBytes, err := base64.StdEncoding.DecodeString(base64PublicKey) 32 if err != nil { 33 err = fmt.Errorf("zign.Verify: decoding public key (%s): %w", base64PublicKey, err) 34 return 35 } 36 37 publicKey, err := crypto.NewEd25519PublicKeyFromBytes(publicKeyBytes) 38 if err != nil { 39 err = fmt.Errorf("zign.Verify: parsing public key (%s): %w", base64PublicKey, err) 40 return 41 } 42 43 for _, file := range input { 44 err = hashDataAndVerifySignature(publicKey, file) 45 if err != nil { 46 return 47 } 48 } 49 50 return 51 } 52 53 func hashDataAndVerifySignature(publicKey crypto.Ed25519PublicKey, file VerifyInput) (err error) { 54 hasher := blake3.New() 55 var size int64 56 57 size, err = io.Copy(hasher, file.Reader) 58 if err != nil { 59 err = fmt.Errorf("zign.Verify: hashing file: %w", err) 60 return 61 } 62 63 hash := hasher.Sum(nil) 64 65 if !crypto.ConstantTimeCompare(hash, file.HashBlake3) { 66 err = errors.New("zign.Verify: hash is not valid") 67 return 68 } 69 70 // size of an uint64 and hash 71 sizeUint64 := uint64(size) 72 message := bytes.NewBuffer(make([]byte, 0, 8+crypto.HashSize256)) 73 err = binary.Write(message, binary.BigEndian, sizeUint64) 74 if err != nil { 75 err = fmt.Errorf("zign.Verify: writing size: %w", err) 76 return 77 } 78 79 _, err = message.Write(hash) 80 if err != nil { 81 err = fmt.Errorf("zign.Verify: writing hash: %w", err) 82 return 83 } 84 85 verified := false 86 verified, err = publicKey.Verify(message.Bytes(), file.Signature) 87 if err != nil { 88 err = fmt.Errorf("zign.Verify: verifying signature (%s): %w", base64.StdEncoding.EncodeToString(file.Signature), err) 89 return 90 } 91 if verified { 92 return 93 } 94 95 err = errors.New("zign.Verify: signature is not valid") 96 97 return 98 }