Skip to content
Merged
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
98 changes: 35 additions & 63 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
on: [push, pull_request]
on:
push:
branches:
- v2
pull_request:

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
Expand All @@ -17,12 +21,10 @@ jobs:
steps:
- name: Install build tools
run: sudo apt-get install -y build-essential
- name: Install Go
uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
- uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
with:
go-version: ${{ matrix.go-version }}
- name: Checkout code
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Verify go generate leaves no changes
run: |
go generate ./...
Expand Down Expand Up @@ -71,50 +73,23 @@ jobs:
env:
GO_OPENSSL_VERSION_OVERRIDE: ${{ matrix.openssl-version }}

wintest:
runs-on: windows-2022
strategy:
fail-fast: false
matrix:
go-version: [1.24.x, 1.25.x]
openssl-version: [libcrypto-3-x64.dll]
steps:
- name: Install Go
uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
with:
go-version: ${{ matrix.go-version }}
- name: Checkout code
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Run Test
run: go test -shuffle=on -gcflags=all=-d=checkptr -count 10 -v ./...
env:
GO_OPENSSL_VERSION_OVERRIDE: ${{ matrix.openssl-version }}
CGO_ENABLED: 1
- name: Run Test CGO disabled
run: go test -shuffle=on -count 10 -v ./...
env:
GO_OPENSSL_VERSION_OVERRIDE: ${{ matrix.openssl-version }}
CGO_ENABLED: 0
GOFLAGS: -tags=goexperiment.ms_nocgo_opensslcrypto

mactest:
ossltest:
strategy:
fail-fast: false
matrix:
go-version: [1.24.x, 1.25.x]
host: [
# the non-intel macOS runners use ARM64
{os: macos-15-intel, openssl-version: /usr/local/opt/openssl@3/lib/libcrypto.3.dylib},
{os: macos-15, openssl-version: /opt/homebrew/opt/openssl@3/lib/libcrypto.3.dylib}
{os: macos-15, openssl-version: /opt/homebrew/opt/openssl@3/lib/libcrypto.3.dylib},
{os: windows-2022, openssl-version: libcrypto-3-x64.dll}
]
runs-on: ${{ matrix.host.os }}
steps:
- name: Install Go
uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
- uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
with:
go-version: ${{ matrix.go-version }}
- name: Checkout code
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Run Test
run: go test -shuffle=on -gcflags=all=-d=checkptr -count 10 -v ./...
env:
Expand All @@ -127,28 +102,15 @@ jobs:
CGO_ENABLED: 0
GOFLAGS: -tags=goexperiment.ms_nocgo_opensslcrypto

azurelinux:
runs-on: ubuntu-latest
container: mcr.microsoft.com/oss/go/microsoft/golang:1.24-azurelinux3.0
steps:
- name: Checkout code
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Run Test
run: go test -shuffle=on -v ./...
env:
CGO_ENABLED: 1
- name: Run Test CGO disabled
run: go test -shuffle=on -v ./...
env:
CGO_ENABLED: 0
GOFLAGS: -tags=goexperiment.ms_nocgo_opensslcrypto

mariner2:
container:
strategy:
fail-fast: false
matrix:
image: [mcr.microsoft.com/oss/go/microsoft/golang:1.24-azurelinux3.0, mcr.microsoft.com/oss/go/microsoft/golang:1.24-cbl-mariner2.0]
runs-on: ubuntu-latest
container: mcr.microsoft.com/oss/go/microsoft/golang:1.24-cbl-mariner2.0
container: ${{ matrix.image }}
steps:
- name: Checkout code
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Run Test
run: go test -shuffle=on -v ./...
env:
Expand All @@ -167,12 +129,10 @@ jobs:
go-version: [1.24, 1.25]
architecture: [ppc64le, s390x, riscv64]
steps:
- name: Set up QEMU
uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3.7.0
- uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3.7.0
with:
platforms: ${{ matrix.architecture }}
- name: Checkout code
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Run tests on ${{matrix.architecture}}
run: |
docker run --rm --platform linux/${{matrix.architecture}} \
Expand All @@ -198,7 +158,19 @@ jobs:
cgolessbuild:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Run Build
run: CGO_ENABLED=0 go build ./...

conclusion:
needs: [test, ossltest, container, test-qemu, cgolessbuild]
runs-on: ubuntu-latest
if: ${{ always() }}
steps:
- name: Result
run: |
if [[ "${{ contains(needs.*.result, 'failure') }}" == "true" || "${{ contains(needs.*.result, 'cancelled') }}" == "true" ]]; then
echo "One or more jobs failed or were cancelled."
exit 1
fi
echo "All jobs passed."
2 changes: 2 additions & 0 deletions cmd/checkheader/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,8 @@ func generate(header string) (string, error) {
conds = append(conds, "OPENSSL_VERSION_NUMBER >= 0x10101000L")
case "3", "init_3":
conds = append(conds, "OPENSSL_VERSION_NUMBER >= 0x30000000L")
case "33":
conds = append(conds, "OPENSSL_VERSION_NUMBER >= 0x30300000L")
}
for _, cond := range conds {
fmt.Fprintf(w, "#if %s\n", cond)
Expand Down
4 changes: 4 additions & 0 deletions const.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ const ( //checkheader:ignore
_KeyTypeMLKEM768 cString = "ML-KEM-768\x00"
_KeyTypeMLKEM1024 cString = "ML-KEM-1024\x00"

// Digest names
_DigestNameSHAKE128 cString = "SHAKE-128\x00"
_DigestNameSHAKE256 cString = "SHAKE-256\x00"

// KDF names
_OSSL_KDF_NAME_HKDF cString = "HKDF\x00"
_OSSL_KDF_NAME_PBKDF2 cString = "PBKDF2\x00"
Expand Down
219 changes: 219 additions & 0 deletions cshake.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
//go:build !cmd_go_bootstrap && (cgo || goexperiment.ms_nocgo_opensslcrypto)

package openssl

import (
"runtime"
"strconv"
"sync"

"github.com/golang-fips/openssl/v2/internal/ossl"
)

// shakeOneShot applies the SHAKE extendable output function to data and
// writes the output to out.
func shakeOneShot(secuirtyBits int, data []byte, out []byte) {
// Can't use EVP_Digest because it doesn't support output lengths
// larger than the block size, while crypto/sha3 supports any length.
alg := loadShake(secuirtyBits)
if alg == nil {
panic("openssl: unsupported SHAKE" + strconv.Itoa(secuirtyBits) + " function")
}
ctx, err := ossl.EVP_MD_CTX_new()
if err != nil {
panic(err)
}
defer ossl.EVP_MD_CTX_free(ctx)
if _, err := ossl.EVP_DigestInit_ex(ctx, alg.md, nil); err != nil {
panic(err)
}
if _, err := ossl.EVP_DigestUpdate(ctx, data); err != nil {
panic(err)
}
if _, err := ossl.EVP_DigestFinalXOF(ctx, out, len(out)); err != nil {
panic(err)
}
}

// SumSHAKE128 applies the SHAKE128 extendable output function to data and
// returns an output of the given length in bytes.
func SumSHAKE128(data []byte, length int) []byte {
out := make([]byte, length)
shakeOneShot(128, data, out)
return out
}

// SumSHAKE256 applies the SHAKE256 extendable output function to data and
// returns an output of the given length in bytes.
func SumSHAKE256(data []byte, length int) []byte {
out := make([]byte, length)
shakeOneShot(256, data, out)
return out
}

// SupportsSHAKE returns true if the SHAKE extendable output functions
// with the given securityBits are supported.
func SupportsSHAKE(securityBits int) bool {
if vMajor == 1 || (vMajor == 3 && vMinor < 3) {
// SHAKE MD's are supported since OpenSSL 1.1.1,
// but EVP_DigestSqueeze is only supported since 3.3,
// and we need it to implement [sha3.SHAKE].
return false
}
return loadShake(securityBits) != nil
}

// SupportsCSHAKE returns true if the CSHAKE extendable output functions
// with the given securityBits are supported.
func SupportsCSHAKE(securityBits int) bool {
// OpenSSL tracker issue https://github.com/openssl/openssl/issues/28358
return false
}

// SHAKE is an instance of a SHAKE extendable output function.
type SHAKE struct {
alg *shakeAlgorithm
ctx ossl.EVP_MD_CTX_PTR
lastXofLen int
}

// NewSHAKE128 creates a new SHAKE128 XOF.
func NewSHAKE128() *SHAKE {
return newSHAKE(128)
}

// NewSHAKE256 creates a new SHAKE256 XOF.
func NewSHAKE256() *SHAKE {
return newSHAKE(256)
}

// NewCSHAKE128 creates a new cSHAKE128 XOF.
//
// N is used to define functions based on cSHAKE, it can be empty when plain
// cSHAKE is desired. S is a customization byte string used for domain
// separation. When N and S are both empty, this is equivalent to NewSHAKE128.
func NewCSHAKE128(N, S []byte) *SHAKE {
if len(N) == 0 && len(S) == 0 {
return NewSHAKE128()
}
return nil
}

// NewCSHAKE256 creates a new cSHAKE256 XOF.
//
// N is used to define functions based on cSHAKE, it can be empty when plain
// cSHAKE is desired. S is a customization byte string used for domain
// separation. When N and S are both empty, this is equivalent to NewSHAKE256.
func NewCSHAKE256(N, S []byte) *SHAKE {
if len(N) == 0 && len(S) == 0 {
return NewSHAKE256()
}
return nil
}

func newSHAKE(securityBits int) *SHAKE {
alg := loadShake(securityBits)
if alg == nil {
panic("openssl: unsupported SHAKE" + strconv.Itoa(securityBits) + " function")
}
ctx, err := ossl.EVP_MD_CTX_new()
if err != nil {
panic(err)
}
if _, err := ossl.EVP_DigestInit_ex(ctx, alg.md, nil); err != nil {
ossl.EVP_MD_CTX_free(ctx)
panic(err)
}
s := &SHAKE{alg: alg, ctx: ctx}
runtime.SetFinalizer(s, (*SHAKE).finalize)
return s
}

func (s *SHAKE) finalize() {
ossl.EVP_MD_CTX_free(s.ctx)
}

// Write absorbs more data into the XOF's state.
//
// It panics if any output has already been read.
func (s *SHAKE) Write(p []byte) (n int, err error) {
defer runtime.KeepAlive(s)
if len(p) == 0 {
return 0, nil
}
if _, err := ossl.EVP_DigestUpdate(s.ctx, p); err != nil {
panic(err)
}
return len(p), nil
}

// Read squeezes more output from the XOF.
//
// Any call to Write after a call to Read will panic.
func (s *SHAKE) Read(p []byte) (n int, err error) {
defer runtime.KeepAlive(s)
if len(p) == 0 {
return 0, nil
}
if len(p) != s.lastXofLen {
if _, err := ossl.EVP_MD_CTX_ctrl(s.ctx, ossl.EVP_MD_CTRL_XOF_LEN, int32(len(p)), nil); err != nil {
panic(err)
}
s.lastXofLen = len(p)
}
if _, err := ossl.EVP_DigestSqueeze(s.ctx, p); err != nil {
panic(err)
}
return len(p), nil
}

// Reset resets the XOF to its initial state.
func (s *SHAKE) Reset() {
defer runtime.KeepAlive(s)
if _, err := ossl.EVP_DigestInit_ex(s.ctx, nil, nil); err != nil {
panic(err)
}
s.lastXofLen = 0
}

// BlockSize returns the rate of the XOF.
func (s *SHAKE) BlockSize() int {
return s.alg.blockSize
}

type shakeAlgorithm struct {
md ossl.EVP_MD_PTR
blockSize int
}

var cacheSHAKE sync.Map

// loadShake converts a crypto.Hash to a EVP_MD.
func loadShake(securityBits int) (alg *shakeAlgorithm) {
if v, ok := cacheSHAKE.Load(securityBits); ok {
return v.(*shakeAlgorithm)
}
defer func() {
cacheSHAKE.Store(securityBits, alg)
}()

var name cString
switch securityBits {
case 128:
name = _DigestNameSHAKE128
case 256:
name = _DigestNameSHAKE256
default:
return nil
}

md, err := ossl.EVP_MD_fetch(nil, name.ptr(), nil)
if err != nil || md == nil {
return nil
}

alg = new(shakeAlgorithm)
alg.md = md
alg.blockSize = int(ossl.EVP_MD_get_block_size(md))
return alg
}
Loading
Loading