Skip to content

Commit

Permalink
Move files and crypto utilities into gofrog (#66)
Browse files Browse the repository at this point in the history
  • Loading branch information
omerzi authored Jun 6, 2024
1 parent f35fee9 commit 577296f
Show file tree
Hide file tree
Showing 14 changed files with 832 additions and 21 deletions.
8 changes: 4 additions & 4 deletions .github/workflows/analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ jobs:
uses: actions/checkout@v4

- name: Install Go
uses: actions/setup-go@v3
uses: actions/setup-go@v5
with:
go-version: 1.20.x
go-version: 1.22.x

- name: Static Code Analysis
uses: golangci/golangci-lint-action@v3
Expand All @@ -32,9 +32,9 @@ jobs:
uses: actions/checkout@v4

- name: Install Go
uses: actions/setup-go@v3
uses: actions/setup-go@v5
with:
go-version: 1.20.x
go-version: 1.22.x

- name: Run Gosec Security Scanner
uses: securego/gosec@master
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: 1.20.x
go-version: 1.22.x
cache: false

- name: Tests
Expand Down
179 changes: 179 additions & 0 deletions crypto/checksum.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
package crypto

import (
"bufio"
"regexp"

// #nosec G501 -- md5 is supported by Artifactory.
"crypto/md5"
// #nosec G505 -- sha1 is supported by Artifactory.
"crypto/sha1"
"fmt"
"hash"
"io"
"os"

ioutils "github.com/jfrog/gofrog/io"
"github.com/minio/sha256-simd"
)

type Algorithm int

const (
MD5 Algorithm = iota
SHA1
SHA256
)

var algorithmFunc = map[Algorithm]func() hash.Hash{
// Go native crypto algorithms:
MD5: md5.New,
SHA1: sha1.New,
// sha256-simd algorithm:
SHA256: sha256.New,
}

type Checksum struct {
Sha1 string `json:"sha1,omitempty"`
Md5 string `json:"md5,omitempty"`
Sha256 string `json:"sha256,omitempty"`
}

func (c *Checksum) IsEmpty() bool {
return c.Md5 == "" && c.Sha1 == "" && c.Sha256 == ""
}

// If the 'other' checksum matches the current one, return true.
// 'other' checksum may contain regex values for sha1, sha256 and md5.
func (c *Checksum) IsEqual(other Checksum) (bool, error) {
match, err := regexp.MatchString(other.Md5, c.Md5)
if !match || err != nil {
return false, err
}
match, err = regexp.MatchString(other.Sha1, c.Sha1)
if !match || err != nil {
return false, err
}
match, err = regexp.MatchString(other.Sha256, c.Sha256)
if !match || err != nil {
return false, err
}

return true, nil
}

func GetFileChecksums(filePath string, checksumType ...Algorithm) (checksums map[Algorithm]string, err error) {
file, err := os.Open(filePath)
if err != nil {
return
}
defer ioutils.Close(file, &err)
return CalcChecksums(file, checksumType...)
}

// CalcChecksums calculates all hashes at once using AsyncMultiWriter. The file is therefore read only once.
func CalcChecksums(reader io.Reader, checksumType ...Algorithm) (map[Algorithm]string, error) {
hashes, err := calcChecksums(reader, checksumType...)
if err != nil {
return nil, err
}
results := sumResults(hashes)
return results, nil
}

// CalcChecksumsBytes calculates hashes like `CalcChecksums`, returns result as bytes
func CalcChecksumsBytes(reader io.Reader, checksumType ...Algorithm) (map[Algorithm][]byte, error) {
hashes, err := calcChecksums(reader, checksumType...)
if err != nil {
return nil, err
}
results := sumResultsBytes(hashes)
return results, nil
}

func calcChecksums(reader io.Reader, checksumType ...Algorithm) (map[Algorithm]hash.Hash, error) {
hashes := getChecksumByAlgorithm(checksumType...)
var multiWriter io.Writer
pageSize := os.Getpagesize()
sizedReader := bufio.NewReaderSize(reader, pageSize)
var hashWriter []io.Writer
for _, v := range hashes {
hashWriter = append(hashWriter, v)
}
multiWriter = ioutils.AsyncMultiWriter(pageSize, hashWriter...)
_, err := io.Copy(multiWriter, sizedReader)
if err != nil {
return nil, err
}
return hashes, nil
}

func sumResults(hashes map[Algorithm]hash.Hash) map[Algorithm]string {
results := map[Algorithm]string{}
for k, v := range hashes {
results[k] = fmt.Sprintf("%x", v.Sum(nil))
}
return results
}

func sumResultsBytes(hashes map[Algorithm]hash.Hash) map[Algorithm][]byte {
results := map[Algorithm][]byte{}
for k, v := range hashes {
results[k] = v.Sum(nil)
}
return results
}

func getChecksumByAlgorithm(checksumType ...Algorithm) map[Algorithm]hash.Hash {
hashes := map[Algorithm]hash.Hash{}
if len(checksumType) == 0 {
for k, v := range algorithmFunc {
hashes[k] = v()
}
return hashes
}

for _, v := range checksumType {
hashes[v] = algorithmFunc[v]()
}
return hashes
}

func CalcChecksumDetails(filePath string) (checksum Checksum, err error) {
file, err := os.Open(filePath)
if err != nil {
return
}
defer ioutils.Close(file, &err)

checksums, err := CalcChecksums(file)
if err != nil {
return Checksum{}, err
}
checksum = Checksum{Md5: checksums[MD5], Sha1: checksums[SHA1], Sha256: checksums[SHA256]}
return
}

type FileDetails struct {
Checksum Checksum
Size int64
}

func GetFileDetails(filePath string, includeChecksums bool) (details *FileDetails, err error) {
details = new(FileDetails)
if includeChecksums {
details.Checksum, err = CalcChecksumDetails(filePath)
if err != nil {
return
}
} else {
details.Checksum = Checksum{}
}

fileInfo, err := os.Stat(filePath)
if err != nil {
return
}
details.Size = fileInfo.Size()
return
}
42 changes: 42 additions & 0 deletions crypto/checksum_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package crypto

import (
"os"
"testing"

"github.com/stretchr/testify/assert"
)

const (
fileContent = "Why did the robot bring a ladder to the bar? It heard the drinks were on the house."
expectedMd5 = "70bd6370a86813f2504020281e4a2e2e"
expectedSha1 = "8c3578ac814c9f02803001a5d3e5d78a7fd0f9cc"
expectedSha256 = "093d901b28a59f7d95921f3f4fb97a03fe7a1cf8670507ffb1d6f9a01b3e890a"
)

func TestGetFileChecksums(t *testing.T) {
// Create a temporary file
tempFile, err := os.CreateTemp("", "TestGetFileChecksums")
assert.NoError(t, err)
defer func() {
assert.NoError(t, tempFile.Close())
assert.NoError(t, os.Remove(tempFile.Name()))
}()

// Write something to the file
_, err = tempFile.Write([]byte(fileContent))
assert.NoError(t, err)

// Calculate only sha1 and match
checksums, err := GetFileChecksums(tempFile.Name(), SHA1)
assert.NoError(t, err)
assert.Len(t, checksums, 1)
assert.Equal(t, expectedSha1, checksums[SHA1])

// Calculate md5, sha1 and sha256 checksums and match
checksums, err = GetFileChecksums(tempFile.Name())
assert.NoError(t, err)
assert.Equal(t, expectedMd5, checksums[MD5])
assert.Equal(t, expectedSha1, checksums[SHA1])
assert.Equal(t, expectedSha256, checksums[SHA256])
}
6 changes: 4 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
module github.com/jfrog/gofrog

go 1.20
go 1.22

require (
github.com/jfrog/archiver/v3 v3.6.0
github.com/minio/sha256-simd v1.0.1
github.com/pkg/errors v0.9.1
github.com/schollz/progressbar/v3 v3.14.2
github.com/stretchr/testify v1.8.4
github.com/stretchr/testify v1.9.0
)

require (
Expand All @@ -15,6 +16,7 @@ require (
github.com/dsnet/compress v0.0.1 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/klauspost/compress v1.17.4 // indirect
github.com/klauspost/cpuid/v2 v2.2.3 // indirect
github.com/klauspost/pgzip v1.2.6 // indirect
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
github.com/nwaples/rardecode v1.1.3 // indirect
Expand Down
9 changes: 7 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,13 @@ github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0
github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4=
github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/cpuid/v2 v2.2.3 h1:sxCkb+qR91z4vsqw4vGGZlDgPz3G7gjaLyK3V8y70BU=
github.com/klauspost/cpuid/v2 v2.2.3/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU=
github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM=
github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8=
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ=
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw=
github.com/nwaples/rardecode v1.1.3 h1:cWCaZwfM5H7nAD6PyEdcVnczzV8i/JtotnyW/dD9lEc=
Expand All @@ -34,15 +38,16 @@ github.com/schollz/progressbar/v3 v3.14.2 h1:EducH6uNLIWsr560zSV1KrTeUb/wZGAHqyM
github.com/schollz/progressbar/v3 v3.14.2/go.mod h1:aQAZQnhF4JGFtRJiw/eobaXpsqpVQAftEQ+hLGXaRc4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8=
github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo=
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos=
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
Expand Down
Loading

0 comments on commit 577296f

Please sign in to comment.