attest

package module
v0.0.0-...-f588663 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 22, 2022 License: MIT Imports: 13 Imported by: 3

README

App-Attest

App-Attest is a go package implements the server-side validation of both attestations and assertions that can be obtained using the DCAppAttestService.

  • (NOT SUPPORT) Request and analyze risk data from server-to-server calls using recipes

System Requirements

  • Go 1.11 (or newer)

Installation

# go 1.16 and newer versions. 
go install github.com/takimoto3/app-attest
# other
go get -u github.com/takimoto3/app-attest

Usage

Attestation

Generate a key pair and attestation in your app as specified in the documentation.

Validate the attestation by calling:

var keyID = []byte(.....) // DCAppAttestService.generateKey returned value and base64.StdEncoding.DecodeString
var challenge = []byte(.....) // one-time challenge from the server
var clientDataHash = sha256.Sum256(challenge)
var attestation = []byte(.....) // DCAppAttestService.attestKey returned value

attestationObj := &attest.AttestationObject{}
err := attestationObj.Unmarshal(attestation)
if err != nil {
    // handle error...
}
service := attest.AttestationService{
    PathForRootCA: "certs/Apple_App_Attestation_Root_CA.pem",
    AppID: "<TEAM ID>.<Bundle ID>",
}
result, err := service.Verify(attestObject, clientDataHash[:], keyID)
if err != nil {
    // handle error...
}

// use result ....

The Verify function return attest.Result(contain the public key and receipt and enviroment) if the validation succeeds. The public key and receipt should be save.

Assertion

If the attestation is successful, your app will create and validate the assertion as specified in the documentation.

Verify the assertion by calling:

var challenge = []byte(....) // one-time challenge from the server
var cliendData = []byte("{..., "challenge":"<challenge data>", .....}") // client request(JSON data case)
var assertion = []byte(....) // DCAppAttestService.generateAssertion returned value

assertionObj := &attest.AssertionObject{}
err := assertionObject.Unmarshal(assertion)
if err != nil {
    // handle error...
}

service := attest.AssertionService{
    AppID:     "<TEAM ID>.<Bundle ID>",
    Challenge: <stored_challenge>,
	Counter:   <stored_counter>,
	PublicKey: <stored_publickey>,
}
newCounter, err := service.Verify(assertionObject, challenge, cliendData)
if err != nil {
    // handle error...
}

If the assertion is successful, get a new counter and save it

Testing

The certificate contained in the attestation has a short expiration date, and you should have an up-to-date attestation to run the test.

Swift

The Swift code for creating the test data looks like this:

Swift Package: AppAttest("https://github.com/iansampson/AppAttest")
import DeviceCheck
import CryptoKit
import AppAttest

func generate() async throws {
    let keyId = try await DCAppAttestService.shared.generateKey()
    let attestChallenge = Data(base64URLEncoded: "l5YkqI0Md8fmcBkw")!
     clientDataHash = Data(SHA256.hash(data: attestChallenge))
    let attest = try await DCAppAttestService.shared.attestKey(keyId, clientDataHash: clientDataHash)
    let attestRequest = AppAttest.AttestationRequest(attestation: attest, keyID: Data(base64Encoded: keyId)!)
    let appId = AppAttest.AppID(teamID: "AAJ6QYVL7U", bundleID: "org.sample.AttestSample")
    let result = try AppAttest.verifyAttestation(challenge: attestChallenge, request: attestRequest, appID: appId)

    let clientData = "{\"levelId\":\"1234\",\"action\":\"getGameLevel\",\"challenge\":\"bBjeLwdQD4KYRpzL\"}".data(using: .utf8)
    let assert = try await DCAppAttestService.shared.generateAssertion(keyId, clientDataHash: Data(SHA256.hash(data: clientData!)))
    print("""
{
  "appID": "\(appId.teamID).\(appId.bundleID)",
  "keyID": "\(keyId)",
  "attestation": "\(attest.base64EncodedString())",
  "publicKey": "\(result.publicKey.rawRepresentation.map{ String(format: "%02hhx", $0)}.joined())",
  "assertion": "\(assert.base64EncodedString())"
}
""")
}

Run it on the actual machine instead of the simulator and save the output of the XCode console as a test file.

  • testdata/attestation.json
  • testdata/attestation_expired.json (Old file)

Finally run test

Acknowledgements

I referred to appattest by Bas Doorn when creating this library. thanks!

License

App-Attest is available under the MIT license. See the LICENSE file for more info.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrInvalidSignature = errors.New("invalid the assertion signature")
	ErrUnmatchRPIDHash  = errors.New("RP Hash mismatch")
)

Functions

func VerifyASN1

func VerifyASN1(pub *ecdsa.PublicKey, hash, sig []byte) bool

[backport golang 1.15]ecdsa.VerifyASN1 VerifyASN1 verifies the ASN.1 encoded signature, sig, of hash using the public key, pub. Its return value records whether the signature is valid.

Types

type AssertionObject

type AssertionObject struct {
	// ASN.1 Encoded as a Sequence of two integers
	Signature []byte `cbor:"signature"`
	AuthData  []byte `cbor:"authenticatorData"`
}

func (*AssertionObject) Unmarshal

func (obj *AssertionObject) Unmarshal(rawBytes []byte) error

type AssertionService

type AssertionService struct {
	AppID     string
	Challenge string
	PublicKey *ecdsa.PublicKey
	Counter   uint32
}

func (*AssertionService) Verify

func (service *AssertionService) Verify(assertObject *AssertionObject, challenge string, clientData []byte) (uint32, error)

type AttestationObject

type AttestationObject struct {
	// The byteform version of the authenticator data, used in part for signature validation
	AuthData []byte `cbor:"authData"`
	// The format of the Attestation data.
	Format string `cbor:"fmt"`
	// The attestation statement data sent back if attestation is requested.
	AttStatement map[string]interface{} `cbor:"attStmt,omitempty"`
}

func (*AttestationObject) Unmarshal

func (obj *AttestationObject) Unmarshal(rawBytes []byte) error

type AttestationService

type AttestationService struct {
	// Apple’s App Attest root certificate file path
	PathForRootCA string
	// App Identifier (format: teamID + "." + bundleID)
	AppID string
}

func (*AttestationService) Verify

func (service *AttestationService) Verify(attestObj *AttestationObject, clientDataHash, keyID []byte) (*Result, error)

Verify validate a single attestation object and return result object.

type AttestedCredential

type AttestedCredential struct {
	AAGUID       []byte
	CredentialID []byte
	// The raw credential public key bytes received from the attestation data
	CredentialPublicKey []byte
}

type AuthenticatorData

type AuthenticatorData struct {
	RPIDHash       []byte
	Flags          byte
	Counter        uint32
	CredentialData AttestedCredential
}

func (*AuthenticatorData) HasAttestedCredentialData

func (auth *AuthenticatorData) HasAttestedCredentialData() bool

func (*AuthenticatorData) Unmarshal

func (auth *AuthenticatorData) Unmarshal(rawBytes []byte) error

type Environment

type Environment int
const (
	None                    = 0
	Development Environment = 1 // the App Attest sandbox environment.
	Production  Environment = 2 // The App Attest production environment.
)

attestation envirom

func (Environment) String

func (e Environment) String() string

type Result

type Result struct {
	Environment Environment
	Receipt     []byte
	PublicKey   *ecdsa.PublicKey
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL