Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
name: CI

on:
push:
branches: [ main, nsm-modernize-with-tests ]
pull_request:
branches: [ main ]

permissions:
contents: read
pull-requests: read

jobs:
test:
name: Test
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v5
with:
go-version-file: 'go.mod'
check-latest: true

- name: Verify dependencies
run: go mod verify

- name: Build
run: go build -v ./...

- name: Vet
run: go vet ./...

- name: Test with coverage
run: make test coverage

lint:
name: Lint
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v5
with:
go-version-file: 'go.mod'
check-latest: true

- name: Run lint via Makefile
run: make lint

35 changes: 35 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# If you prefer the allow list template instead of the deny list, see community template:
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
#
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib

# Test binary, built with `go test -c`
*.test

# Code coverage profiles and other test artifacts
*.out
coverage.*
*.coverprofile
profile.cov

# Dependency directories (remove the comment below to include it)
# vendor/

# Go workspace file
go.work
go.work.sum

# env file
.env

# Editor/IDE
# .idea/
# .vscode/

# AI
.claude
44 changes: 44 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
run:
timeout: 5m
tests: true
modules-download-mode: readonly

linters:
enable:
- bodyclose
- errcheck
- gofmt
- goimports
- gosec
- gosimple
- govet
- ineffassign
- misspell
- revive
- staticcheck
- unconvert
- unused
- gocyclo
- goconst
- unparam

linters-settings:
gocyclo:
min-complexity: 15
goconst:
min-len: 2
min-occurrences: 2
misspell:
locale: US
gosec:
excludes:
- G304 # File path provided by user input - acceptable for NSM device access

issues:
exclude-rules:
- path: _test\.go
linters:
- gosec
- path: example/
linters:
- gosec
56 changes: 56 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
.PHONY: all test lint coverage clean build fmt

all: fmt lint test coverage

# Build all packages
build:
go build -v ./...

# Run tests with race detection and coverage
test:
go test -race -coverprofile=coverage.out -covermode=atomic ./...

# Format code
fmt:
go fmt ./...
go run golang.org/x/tools/cmd/goimports@latest -w .

# Run linting (depends on fmt)
lint: fmt
go run github.com/golangci/golangci-lint/cmd/golangci-lint@v1.61.0 run --timeout=5m

# Generate and display coverage report
coverage: test
go tool cover -func=coverage.out
@echo ""
@COVERAGE=$$(go tool cover -func=coverage.out | grep total | awk '{print $$3}' | sed 's/%//'); \
echo "Total coverage: $${COVERAGE}%"; \
if [ "$$(echo "$${COVERAGE} < 60.0" | bc -l)" -eq 1 ]; then \
echo "❌ Coverage $${COVERAGE}% is below minimum 60%"; \
exit 1; \
else \
echo "✅ Coverage $${COVERAGE}% meets minimum threshold"; \
fi

# Generate HTML coverage report
coverage-html: test
go tool cover -html=coverage.out -o coverage.html
@echo "Coverage report generated: coverage.html"

# Run go vet
vet:
go vet ./...

# Verify dependencies
verify:
go mod verify
go mod tidy -diff

# Clean build artifacts
clean:
go clean -testcache
rm -f coverage.out coverage.html

# Run all checks (CI simulation)
ci: verify build vet fmt lint test coverage
@echo "✅ All CI checks passed"
16 changes: 7 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,10 @@ import (

func generateBigPrime() (*big.Int, error) {
sess, err := nsm.OpenDefaultSession()
defer sess.Close()

if nil != err {
if err != nil {
return nil, err
}
defer sess.Close()

return rand.Prime(sess, 2048)
}
Expand All @@ -94,26 +93,25 @@ import (

func attest(nonce, userData, publicKey []byte) ([]byte, error) {
sess, err := nsm.OpenDefaultSession()
defer sess.Close()

if nil != err {
if err != nil {
return nil, err
}
defer sess.Close()

res, err := sess.Send(&request.Attestation{
Nonce: nonce,
UserData: userData,
PublicKey: publicKey,
})
if nil != err {
if err != nil {
return nil, err
}

if "" != res.Error {
if res.Error != "" {
return nil, errors.New(string(res.Error))
}

if nil == res.Attestation || nil == res.Attestation.Document {
if res.Attestation == nil || res.Attestation.Document == nil {
return nil, errors.New("NSM device did not return an attestation")
}

Expand Down
14 changes: 7 additions & 7 deletions example/attestation/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,33 +4,33 @@ import (
"encoding/base64"
"errors"
"fmt"
"time"

"github.com/hf/nsm"
"github.com/hf/nsm/request"
"time"
)

func attest(nonce, userData, publicKey []byte) ([]byte, error) {
sess, err := nsm.OpenDefaultSession()
defer sess.Close()

if nil != err {
if err != nil {
return nil, err
}
defer sess.Close()

res, err := sess.Send(&request.Attestation{
Nonce: nonce,
UserData: userData,
PublicKey: publicKey,
})
if nil != err {
if err != nil {
return nil, err
}

if "" != res.Error {
if res.Error != "" {
return nil, errors.New(string(res.Error))
}

if nil == res.Attestation || nil == res.Attestation.Document {
if res.Attestation == nil || res.Attestation.Document == nil {
return nil, errors.New("NSM device did not return an attestation")
}

Expand Down
10 changes: 4 additions & 6 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
module github.com/hf/nsm

go 1.15
go 1.23

require (
github.com/fxamacker/cbor/v2 v2.2.0
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5 // indirect
golang.org/x/tools v0.0.0-20210105210202-9ed45478a130 // indirect
)
require github.com/fxamacker/cbor/v2 v2.7.0

require github.com/x448/float16 v0.8.4 // indirect
31 changes: 2 additions & 29 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,31 +1,4 @@
github.com/fxamacker/cbor/v2 v2.2.0 h1:6eXqdDDe588rSYAi1HfZKbx6YYQO4mxQ9eC6xYpU/JQ=
github.com/fxamacker/cbor/v2 v2.2.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo=
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5 h1:2M3HP5CCK1Si9FQhwnzYhXdG6DXeebvUHFpre8QvbyI=
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7 h1:EBZoQjiKKPaLbPrbpssUfuHtwM6KV/vb4U85g/cigFY=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20210105210202-9ed45478a130 h1:8qSBr5nyKsEgkP918Pu5FFDZpTtLIjXSo6mrtdVOFfk=
golang.org/x/tools v0.0.0-20210105210202-9ed45478a130/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
27 changes: 27 additions & 0 deletions ioc/ioc_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package ioc

import (
"testing"
)

// TestCommand tests the IOCTL command calculation
func TestCommand(t *testing.T) {
// Test that Command returns a non-zero value for valid inputs
result := Command(READ|WRITE, 0x0A, 0, 16)
if result == 0 {
t.Error("Command should return non-zero value")
}

// Test that different inputs produce different results
result1 := Command(READ, 0x0A, 0, 16)
result2 := Command(WRITE, 0x0A, 0, 16)
if result1 == result2 {
t.Error("Different directions should produce different commands")
}

// Test with actual NSM values used in the main package
nsmCommand := Command(READ|WRITE, 0x0A, 0, 16)
if nsmCommand == 0 {
t.Error("NSM command should be non-zero")
}
}
Loading