Permalink
Browse files

[dev.boringcrypto] crypto/tls/fipsonly: new package to force FIPS-all…

…owed TLS settings

Change-Id: I3268cab2de8aed9e2424e9c3bc7667083bc5e1ce
Reviewed-on: https://go-review.googlesource.com/65250
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Adam Langley <agl@golang.org>
  • Loading branch information...
rsc committed Sep 20, 2017
1 parent 2ba7615 commit 3ed08db261fcb470b5880791a1a087e28c086b8c
@@ -11,7 +11,10 @@ package boring
// #include "goboringcrypto.h"
import "C"
import "math/big"
import (
"crypto/internal/boring/sig"
"math/big"
)
const available = true
@@ -20,6 +23,7 @@ func init() {
if C._goboringcrypto_FIPS_mode() != 1 {
panic("boringcrypto: not in FIPS mode")
}
sig.BoringCrypto()
}
// Unreachable marks code that should be unreachable
@@ -0,0 +1,10 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// runtime_arg0 is declared in tls.go without a body.
// It's provided by package runtime,
// but the go command doesn't know that.
// Having this assembly file keeps the go command
// from complaining about the missing body
// (because the implementation might be here).
@@ -0,0 +1,49 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package fipstls allows control over whether crypto/tls requires FIPS-approved settings.
// This package's effects are independent of the use of the BoringCrypto implementation.
package fipstls
import "sync/atomic"
var required uint32
// Force forces crypto/tls to restrict TLS configurations to FIPS-approved settings.
// By design, this call is impossible to undo (except in tests).
//
// Note that this call has an effect even in programs using
// standard crypto (that is, even when Enabled = false).
func Force() {
atomic.StoreUint32(&required, 1)
}
// Abandon allows non-FIPS-approved settings.
// If called from a non-test binary, it panics.
func Abandon() {
// Note: Not using boring.UnreachableExceptTests because we want
// this test to happen even when boring.Enabled = false.
name := runtime_arg0()
// Allow _test for Go command, .test for Bazel,
// NaClMain for NaCl (where all binaries run as NaClMain),
// and empty string for Windows (where runtime_arg0 can't easily find the name).
// Since this is an internal package, testing that this isn't used on the
// other operating systems should suffice to catch any mistakes.
if !hasSuffix(name, "_test") && !hasSuffix(name, ".test") && name != "NaClMain" && name != "" {
panic("fipstls: invalid use of Abandon in " + name)
}
atomic.StoreUint32(&required, 0)
}
// provided by runtime
func runtime_arg0() string
func hasSuffix(s, t string) bool {
return len(s) > len(t) && s[len(s)-len(t):] == t
}
// Required reports whether FIPS-approved settings are required.
func Required() bool {
return atomic.LoadUint32(&required) != 0
}
@@ -9,6 +9,7 @@ package boring
import (
"crypto"
"crypto/cipher"
"crypto/internal/boring/sig"
"hash"
"math/big"
)
@@ -17,7 +18,12 @@ const available = false
// Unreachable marks code that should be unreachable
// when BoringCrypto is in use. It is a no-op without BoringCrypto.
func Unreachable() {}
func Unreachable() {
// Code that's unreachable when using BoringCrypto
// is exactly the code we want to detect for reporting
// standard Go crypto.
sig.StandardCrypto()
}
// UnreachableExceptTests marks code that should be unreachable
// when BoringCrypto is in use. It is a no-op without BoringCrypto.
@@ -0,0 +1,17 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package sig holds “code signatures” that can be called
// and will result in certain code sequences being linked into
// the final binary. The functions themselves are no-ops.
package sig
// BoringCrypto indicates that the BoringCrypto module is present.
func BoringCrypto()
// FIPSOnly indicates that package crypto/tls/fipsonly is present.
func FIPSOnly()
// StandardCrypto indicates that standard Go crypto is present.
func StandardCrypto()
@@ -0,0 +1,54 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#include "textflag.h"
// These functions are no-ops, but you can search for their implementations
// to find out whether they are linked into a particular binary.
//
// Each function consists of a two-byte jump over the next 29-bytes,
// then a 5-byte indicator sequence unlikely to occur in real x86 instructions,
// then a randomly-chosen 24-byte sequence, and finally a return instruction
// (the target of the jump).
//
// These sequences are known to rsc.io/goversion.
#define START \
BYTE $0xEB; BYTE $0x1D; BYTE $0xF4; BYTE $0x48; BYTE $0xF4; BYTE $0x4B; BYTE $0xF4
#define END \
BYTE $0xC3
// BoringCrypto indicates that BoringCrypto (in particular, its func init) is present.
TEXT ·BoringCrypto(SB),NOSPLIT,$0
START
BYTE $0xB3; BYTE $0x32; BYTE $0xF5; BYTE $0x28;
BYTE $0x13; BYTE $0xA3; BYTE $0xB4; BYTE $0x50;
BYTE $0xD4; BYTE $0x41; BYTE $0xCC; BYTE $0x24;
BYTE $0x85; BYTE $0xF0; BYTE $0x01; BYTE $0x45;
BYTE $0x4E; BYTE $0x92; BYTE $0x10; BYTE $0x1B;
BYTE $0x1D; BYTE $0x2F; BYTE $0x19; BYTE $0x50;
END
// StandardCrypto indicates that standard Go crypto is present.
TEXT ·StandardCrypto(SB),NOSPLIT,$0
START
BYTE $0xba; BYTE $0xee; BYTE $0x4d; BYTE $0xfa;
BYTE $0x98; BYTE $0x51; BYTE $0xca; BYTE $0x56;
BYTE $0xa9; BYTE $0x11; BYTE $0x45; BYTE $0xe8;
BYTE $0x3e; BYTE $0x99; BYTE $0xc5; BYTE $0x9c;
BYTE $0xf9; BYTE $0x11; BYTE $0xcb; BYTE $0x8e;
BYTE $0x80; BYTE $0xda; BYTE $0xf1; BYTE $0x2f;
END
// FIPSOnly indicates that crypto/tls/fipsonly is present.
TEXT ·FIPSOnly(SB),NOSPLIT,$0
START
BYTE $0x36; BYTE $0x3C; BYTE $0xB9; BYTE $0xCE;
BYTE $0x9D; BYTE $0x68; BYTE $0x04; BYTE $0x7D;
BYTE $0x31; BYTE $0xF2; BYTE $0x8D; BYTE $0x32;
BYTE $0x5D; BYTE $0x5C; BYTE $0xA5; BYTE $0x87;
BYTE $0x3F; BYTE $0x5D; BYTE $0x80; BYTE $0xCA;
BYTE $0xF6; BYTE $0xD6; BYTE $0x15; BYTE $0x1B;
END
@@ -0,0 +1,19 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// These functions are no-ops.
// On amd64 they have recognizable implementations, so that you can
// search a particular binary to see if they are present.
// On other platforms (those using this source file), they don't.
// +build !amd64
TEXT ·BoringCrypto(SB),$0
RET
TEXT ·FIPSOnly(SB),$0
RET
TEXT ·StandardCrypto(SB),$0
RET
View
@@ -0,0 +1,121 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package tls
import (
"crypto/ecdsa"
"crypto/internal/boring/fipstls"
"crypto/rsa"
"crypto/x509"
)
// needFIPS returns fipstls.Required(); it avoids a new import in common.go.
func needFIPS() bool {
return fipstls.Required()
}
// fipsMinVersion replaces c.minVersion in FIPS-only mode.
func fipsMinVersion(c *Config) uint16 {
// FIPS requires TLS 1.2.
return VersionTLS12
}
// fipsMaxVersion replaces c.maxVersion in FIPS-only mode.
func fipsMaxVersion(c *Config) uint16 {
// FIPS requires TLS 1.2.
return VersionTLS12
}
// default defaultFIPSCurvePreferences is the FIPS-allowed curves,
// in preference order (most preferable first).
var defaultFIPSCurvePreferences = []CurveID{CurveP256, CurveP384, CurveP521}
// fipsCurvePreferences replaces c.curvePreferences in FIPS-only mode.
func fipsCurvePreferences(c *Config) []CurveID {
if c == nil || len(c.CurvePreferences) == 0 {
return defaultFIPSCurvePreferences
}
var list []CurveID
for _, id := range c.CurvePreferences {
for _, allowed := range defaultFIPSCurvePreferences {
if id == allowed {
list = append(list, id)
break
}
}
}
return list
}
// default FIPSCipherSuites is the FIPS-allowed cipher suites,
// in preference order (most preferable first).
var defaultFIPSCipherSuites = []uint16{
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
TLS_RSA_WITH_AES_128_GCM_SHA256,
TLS_RSA_WITH_AES_256_GCM_SHA384,
}
// fipsCipherSuites replaces c.cipherSuites in FIPS-only mode.
func fipsCipherSuites(c *Config) []uint16 {
if c == nil || c.CipherSuites == nil {
return defaultFIPSCipherSuites
}
var list []uint16
for _, id := range c.CipherSuites {
for _, allowed := range defaultFIPSCipherSuites {
if id == allowed {
list = append(list, id)
break
}
}
}
return list
}
// isBoringCertificate reports whether a certificate may be used
// when constructing a verified chain.
// It is called for each leaf, intermediate, and root certificate.
func isBoringCertificate(c *x509.Certificate) bool {
if !needFIPS() {
// Everything is OK if we haven't forced FIPS-only mode.
return true
}
// Otherwise the key must be RSA 2048, RSA 3072, or ECDSA P-256.
switch k := c.PublicKey.(type) {
default:
return false
case *rsa.PublicKey:
if size := k.N.BitLen(); size != 2048 && size != 3072 {
return false
}
case *ecdsa.PublicKey:
if name := k.Curve.Params().Name; name != "P-256" && name != "P-384" {
return false
}
}
return true
}
// supportedSignatureAlgorithms returns the supported signature algorithms.
// It knows that the FIPS-allowed ones are all at the beginning of
// defaultSupportedSignatureAlgorithms.
func supportedSignatureAlgorithms() []signatureAndHash {
all := defaultSupportedSignatureAlgorithms
if !needFIPS() {
return all
}
i := 0
for i < len(all) && all[i].hash != hashSHA1 {
i++
}
return all[:i]
}
var testingOnlyForceClientHelloSignatureAndHashes []signatureAndHash
Oops, something went wrong.

0 comments on commit 3ed08db

Please sign in to comment.