mirror of
https://github.com/slackhq/nebula.git
synced 2026-05-15 20:37:36 +02:00
* add sshd.sandbox_dir config option Sanitize SSH profile paths (ssh.go:514,683,719) — restrict os.Create(a[0]) to a safe directory. Add a config option in the config file to specify the sandbox directory. For backwards compatibility, if the config is not specified, keep the current behavior. * update default and example * use os.TempDir() for sshd.sandbox_dir default * split sandbox path validation into separate conditionals Separate the combined && check in sshSanitizeFilePath into two distinct conditionals with specific error messages: one for paths resolving to the sandbox directory itself, and one for paths outside the sandbox. Co-Authored-By: Claude <svc-devxp-claude@slack-corp.com> * fix: trim leading zeros from p256 signature swap result bigmod.Nat.Bytes() returns fixed-size 32-byte slices, but ASN.1 INTEGER parsing strips leading zeros. This caused a flaky test failure (~1/256 chance) when the S value's high byte was zero. Co-Authored-By: Claude <svc-devxp-claude@slack-corp.com> --------- Co-authored-by: Claude <svc-devxp-claude@slack-corp.com>
128 lines
2.6 KiB
Go
128 lines
2.6 KiB
Go
package p256
|
|
|
|
import (
|
|
"crypto/elliptic"
|
|
"errors"
|
|
"math/big"
|
|
|
|
"filippo.io/bigmod"
|
|
|
|
"golang.org/x/crypto/cryptobyte"
|
|
"golang.org/x/crypto/cryptobyte/asn1"
|
|
)
|
|
|
|
var halfN = new(big.Int).Rsh(elliptic.P256().Params().N, 1)
|
|
var nMod *bigmod.Modulus
|
|
|
|
func init() {
|
|
n, err := bigmod.NewModulus(elliptic.P256().Params().N.Bytes())
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
nMod = n
|
|
}
|
|
|
|
func IsNormalized(sig []byte) (bool, error) {
|
|
r, s, err := parseSignature(sig)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
return checkLowS(r, s), nil
|
|
}
|
|
|
|
func checkLowS(_, s []byte) bool {
|
|
bigS := new(big.Int).SetBytes(s)
|
|
// Check if S <= (N/2), because we want to include the midpoint in the set of low-s
|
|
return bigS.Cmp(halfN) <= 0
|
|
}
|
|
|
|
func swap(r, s []byte) ([]byte, []byte, error) {
|
|
var err error
|
|
bigS, err := bigmod.NewNat().SetBytes(s, nMod)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
sNormalized := nMod.Nat().Sub(bigS, nMod)
|
|
|
|
result := sNormalized.Bytes(nMod)
|
|
for len(result) > 1 && result[0] == 0 {
|
|
result = result[1:]
|
|
}
|
|
|
|
return r, result, nil
|
|
}
|
|
|
|
func Normalize(sig []byte) ([]byte, error) {
|
|
r, s, err := parseSignature(sig)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if checkLowS(r, s) {
|
|
return sig, nil
|
|
}
|
|
|
|
newR, newS, err := swap(r, s)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return encodeSignature(newR, newS)
|
|
}
|
|
|
|
// Swap will change sig between its current form to the opposite high or low form.
|
|
func Swap(sig []byte) ([]byte, error) {
|
|
r, s, err := parseSignature(sig)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
newR, newS, err := swap(r, s)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return encodeSignature(newR, newS)
|
|
}
|
|
|
|
// parseSignature taken exactly from crypto/ecdsa/ecdsa.go
|
|
func parseSignature(sig []byte) (r, s []byte, err error) {
|
|
var inner cryptobyte.String
|
|
input := cryptobyte.String(sig)
|
|
if !input.ReadASN1(&inner, asn1.SEQUENCE) ||
|
|
!input.Empty() ||
|
|
!inner.ReadASN1Integer(&r) ||
|
|
!inner.ReadASN1Integer(&s) ||
|
|
!inner.Empty() {
|
|
return nil, nil, errors.New("invalid ASN.1")
|
|
}
|
|
return r, s, nil
|
|
}
|
|
|
|
func encodeSignature(r, s []byte) ([]byte, error) {
|
|
var b cryptobyte.Builder
|
|
b.AddASN1(asn1.SEQUENCE, func(b *cryptobyte.Builder) {
|
|
addASN1IntBytes(b, r)
|
|
addASN1IntBytes(b, s)
|
|
})
|
|
return b.Bytes()
|
|
}
|
|
|
|
// addASN1IntBytes encodes in ASN.1 a positive integer represented as
|
|
// a big-endian byte slice with zero or more leading zeroes.
|
|
func addASN1IntBytes(b *cryptobyte.Builder, bytes []byte) {
|
|
for len(bytes) > 0 && bytes[0] == 0 {
|
|
bytes = bytes[1:]
|
|
}
|
|
if len(bytes) == 0 {
|
|
b.SetError(errors.New("invalid integer"))
|
|
return
|
|
}
|
|
b.AddASN1(asn1.INTEGER, func(c *cryptobyte.Builder) {
|
|
if bytes[0]&0x80 != 0 {
|
|
c.AddUint8(0)
|
|
}
|
|
c.AddBytes(bytes)
|
|
})
|
|
}
|