2021-02-25 18:24:24 +00:00
package acme
import (
2021-03-01 06:49:20 +00:00
"crypto"
"encoding/base64"
2021-02-25 18:24:24 +00:00
"encoding/json"
2021-07-17 15:35:44 +00:00
"time"
2021-02-25 18:24:24 +00:00
"go.step.sm/crypto/jose"
2022-04-11 13:25:55 +00:00
"github.com/smallstep/certificates/authority/policy"
2021-02-25 18:24:24 +00:00
)
// Account is a subset of the internal account type containing only those
// attributes required for responses in the ACME protocol.
type Account struct {
2021-07-17 17:02:47 +00:00
ID string ` json:"-" `
Key * jose . JSONWebKey ` json:"-" `
Contact [ ] string ` json:"contact,omitempty" `
Status Status ` json:"status" `
OrdersURL string ` json:"orders" `
ExternalAccountBinding interface { } ` json:"externalAccountBinding,omitempty" `
2021-02-25 18:24:24 +00:00
}
// ToLog enables response logging.
func ( a * Account ) ToLog ( ) ( interface { } , error ) {
b , err := json . Marshal ( a )
if err != nil {
2021-03-05 07:10:46 +00:00
return nil , WrapErrorISE ( err , "error marshaling account for logging" )
2021-02-25 18:24:24 +00:00
}
return string ( b ) , nil
}
// IsValid returns true if the Account is valid.
func ( a * Account ) IsValid ( ) bool {
2021-02-28 18:09:06 +00:00
return Status ( a . Status ) == StatusValid
2021-02-25 18:24:24 +00:00
}
2021-03-01 06:49:20 +00:00
// KeyToID converts a JWK to a thumbprint.
func KeyToID ( jwk * jose . JSONWebKey ) ( string , error ) {
kid , err := jwk . Thumbprint ( crypto . SHA256 )
if err != nil {
2021-03-05 07:10:46 +00:00
return "" , WrapErrorISE ( err , "error generating jwk thumbprint" )
2021-03-01 06:49:20 +00:00
}
return base64 . RawURLEncoding . EncodeToString ( kid ) , nil
}
2021-07-17 15:35:44 +00:00
2022-04-07 12:11:53 +00:00
// PolicyNames contains ACME account level policy names
type PolicyNames struct {
DNSNames [ ] string ` json:"dns" `
IPRanges [ ] string ` json:"ips" `
}
// X509Policy contains ACME account level X.509 policy
type X509Policy struct {
2022-04-15 08:58:29 +00:00
Allowed PolicyNames ` json:"allow" `
Denied PolicyNames ` json:"deny" `
2022-04-07 12:11:53 +00:00
}
// Policy is an ACME Account level policy
type Policy struct {
X509 X509Policy ` json:"x509" `
}
2022-04-11 13:25:55 +00:00
func ( p * Policy ) GetAllowedNameOptions ( ) * policy . X509NameOptions {
if p == nil {
return nil
}
return & policy . X509NameOptions {
DNSDomains : p . X509 . Allowed . DNSNames ,
IPRanges : p . X509 . Allowed . IPRanges ,
}
}
func ( p * Policy ) GetDeniedNameOptions ( ) * policy . X509NameOptions {
if p == nil {
return nil
}
return & policy . X509NameOptions {
DNSDomains : p . X509 . Denied . DNSNames ,
IPRanges : p . X509 . Denied . IPRanges ,
}
}
2022-04-19 08:24:52 +00:00
// IsWildcardLiteralAllowed returns true by default for
// ACME account policies, as authorization is performed on DNS
// level.
func ( p * Policy ) IsWildcardLiteralAllowed ( ) bool {
return true
}
// ShouldVerifySubjectCommonName returns true by default
// for ACME account policies, as this is embedded in the
// protocol.
func ( p * Policy ) ShouldVerifySubjectCommonName ( ) bool {
return true
}
2021-10-08 11:18:23 +00:00
// ExternalAccountKey is an ACME External Account Binding key.
2021-07-17 15:35:44 +00:00
type ExternalAccountKey struct {
2022-01-07 15:59:55 +00:00
ID string ` json:"id" `
ProvisionerID string ` json:"provisionerID" `
Reference string ` json:"reference" `
AccountID string ` json:"-" `
KeyBytes [ ] byte ` json:"-" `
CreatedAt time . Time ` json:"createdAt" `
BoundAt time . Time ` json:"boundAt,omitempty" `
2022-04-07 12:11:53 +00:00
Policy * Policy ` json:"policy,omitempty" `
2021-07-17 15:35:44 +00:00
}
2021-07-17 17:02:47 +00:00
2021-10-08 11:18:23 +00:00
// AlreadyBound returns whether this EAK is already bound to
// an ACME Account or not.
2021-07-22 21:48:41 +00:00
func ( eak * ExternalAccountKey ) AlreadyBound ( ) bool {
return ! eak . BoundAt . IsZero ( )
}
2021-10-08 11:18:23 +00:00
// BindTo binds the EAK to an Account.
// It returns an error if it's already bound.
func ( eak * ExternalAccountKey ) BindTo ( account * Account ) error {
if eak . AlreadyBound ( ) {
return NewError ( ErrorUnauthorizedType , "external account binding key with id '%s' was already bound to account '%s' on %s" , eak . ID , eak . AccountID , eak . BoundAt )
}
2021-07-17 17:02:47 +00:00
eak . AccountID = account . ID
eak . BoundAt = time . Now ( )
2021-07-22 21:48:41 +00:00
eak . KeyBytes = [ ] byte { } // clearing the key bytes; can only be used once
2021-10-08 11:18:23 +00:00
return nil
2021-07-17 17:02:47 +00:00
}