smartid

package module
v1.3.1 Latest Latest
Warning

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

Go to latest
Published: Mar 30, 2023 License: MIT Imports: 21 Imported by: 0

README

Smart-ID in Go language

Smart ID Go Library

Tests) Go Report Card Go Reference

Package smartid implements an interface in Go to work with the Smart-ID API (https://www.smart-id.com). Smart-ID is used to easily and safely authenticate and sign documents online using only a smart phone. Smart-ID is a popular method in the Baltic countries of Estonia, Latvia, and Lithuania for authenticating and signing documents online for banks, social media, government offices, and other institutions.

Official Smart-ID technical documentation.

Installation

go get github.com/dknight/go-smartid

Usage

The bare minimum required to make an authentication request. Demonstarates synchronous way.

For more examples see full docs.

Sync request
semid := NewSemanticIdentifier(IdentifierTypePNO, CountryEE, "30303039914")
client := NewClient("https:sid.demo.sk.ee/smart-id-rp/v2/", 5000)
request := AuthRequest{
	// Replace in production with real RelyingPartyUUID.
	RelyingPartyUUID: "00000000-0000-0000-0000-000000000000",
	// Replace in production with real RelyingPartyName.
	RelyingPartyName: "DEMO",
	// It is good to generate new has for security reasons.
	Hash: GenerateAuthHash(SHA512),
 	// We use personal ID as Identifier, also possible to use document number.
	Identifier: semid,
}

// This blocks thread until it completes
resp, err := client.AuthenticateSync(context.TODO(), &request)
if err != nil {
	log.Fatalln(err)
}

if _, err := resp.Validate(); err != nil {
	log.Fatalln(err)
}

// It is also good to verify the certificate over secure. But it isn't
// mandatory, but strongly recommended.
//
certPaths := []string{"./certs/TEST_of_EID-SK_2016.pem.crt"}
if ok, err := resp.Cert.Verify(certPaths); !ok {
 	log.Fatalln(err)
}

identity := resp.GetIdentity()
fmt.Println("Name:", identity.CommonName)
fmt.Println("Personal ID:", identity.SerialNumber)
fmt.Println("Country:", identity.Country)
// Output:
// Name: TESTNUMBER,QUALIFIED OK1
// Personal ID: PNOEE-30303039914
// Country: EE
Async way using channel

Another example contains many more quest parameters for the signing method. Sign and Authenticate methods are similar and you can use the same AuthRequest parameters for both of them.

This examples is asynchronous uses channel.

semid := NewSemanticIdentifier(IdentifierTypePNO, CountryEE, "30303039914")
client := NewClient("https://sid.demo.sk.ee/smart-id-rp/v2/", 5000)
request := AuthRequest{
	// Replace in production with real RelyingPartyUUID.
	RelyingPartyUUID: "00000000-0000-0000-0000-000000000000",
	// Replace in production with real RelyingPartyName.
	RelyingPartyName: "DEMO",
	// It is good to generate new has for security reasons.
	Hash: GenerateAuthHash(SHA384),
	// HashType should be the same as in GenerateAuthHash.
	HashType: SHA384,
	// We use personal ID as Identifier, also possible to use document
	// number.
	Identifier: semid,
	AuthType:   AuthTypeEtsi,
	CertificateLevel: CertLevelQualified,
	AllowedInteractionsOrder: []AllowedInteractionsOrder{
		{
			Type:          InteractionVerificationCodeChoice,
			DisplayText60: "Welcome to Smart-ID!",
		},
		{
			Type:          InteractionDisplayTextAndPIN,
			DisplayText200: "Welcome to Smart-ID! A bit longer text.",
		},
	},
}

resp := <-client.Sign(context.TODO(), &request)
if _, err := resp.Validate(); err != nil {
	log.Fatalln(err)
}

// It is also good to verify the certificate over secure. But it isn't
// mandatory, but strongly recommended.
//
certPaths := []string{"./certs/TEST_of_EID-SK_2016.pem.crt"}
if ok, err := resp.Cert.Verify(certPaths); !ok {
	log.Fatalln(err)
}

identity := resp.GetIdentity()
fmt.Println("Name:", identity.CommonName)
fmt.Println("Personal ID:", identity.SerialNumber)
fmt.Println("Country:", identity.Country)
// Output:
// Name: TESTNUMBER,QUALIFIED OK1
// Personal ID: PNOEE-30303039914
// Country: EE

For more examples see docs.

What is not included

  1. :private/ endpoint.
  2. Better certificated parsing and data extraction. You can get certificated from response, verify and parse it in own way response.Cert.GetX509Cert().
  3. Smart-ID API version v1 is not supported, only v2.

Testing

SK test environment is very unstable. Possible technical problems might be:

  1. Problems with service availability. Doesn't work too often.
  2. They change test data without any warning.
  3. Problems with certificates.
  4. Problems with performance requests can last very long time, sometimes 408 Timeout will be given as response.
go test

Troubleshooting

Problems with certificates

x509: certificate signed by unknown authority

If in development you get an error x509: certificate signed by unknown authority. Then you need to install SK test certificates to your system. Install certificates from directory ./certs to your operating system.

Fedora Linux example:

sudo cp ./certs/TEST_of_* /usr/share/pki/ca-trust-source/anchors/
sudo update-ca-trust

Then you can verify your certificate, but don't forget to replace with your personal certificate in production.

certPaths := []string{"./certs/TEST_of_EID-SK_2016.pem.crt"}
if ok, err := resp.Cert.Verify(certPaths); !ok {
 	log.Fatalln(err)
}

Contribution

Any help is appreciated. Found a bug, typo, inaccuracy, etc.? Please do not hesitate and make pull request or issue.

License

MIT 2022-2023

Documentation

Overview

Package smartid implements an interface in Go to work with the Smart-ID API (https://www.smart-id.com). Smart-ID is used to easily and safely authenticate and sign documents online using only a smart phone. Smart-ID is a popular method in the Baltic countries of Estonia, Latvia, and Lithuania for authenticating and signing documents online for banks, social media, government offices, and other institutions.

The bare minimum required to make an authentication request. Demonstarates synchronous way.

semid := NewSemanticIdentifier(IdentifierTypePNO, CountryEE, "30303039914")
client := NewClient("https://sid.demo.sk.ee/smart-id-rp/v2/", 5000)
request := AuthRequest{
	// Replace in production with real RelyingPartyUUID.
	RelyingPartyUUID: "00000000-0000-0000-0000-000000000000",
	// Replace in production with real RelyingPartyName.
	RelyingPartyName: "DEMO",
	// It is good to generate new has for security reasons.
	Hash: GenerateAuthHash(SHA512),
	// We use personal ID as Identifier, also possible to use document
	// number.
	Identifier: semid,
	AuthType:   AuthTypeEtsi,
}

// This blocks thread until it completes
resp, err := client.AuthenticateSync(&request)
if err != nil {
	log.Fatalln(err)
}

if _, err := resp.Validate(); err != nil {
	log.Fatalln(err)
}

// It is also good to verify the certificate over secure. But it isn't
// mandatory, but strongly recommended.
//
// If you always get an error: x509: certificate signed by unknown
// authority. Most probably you need install ca-certificates for
// example for GNU Linux.
//
// sudo apt-get install ca-certificates
// sudo dnf install ca-certificates
certPaths := []string{"./certs/TEST_of_EID-SK_2016.pem.crt"}
 if ok, err := resp.Cert.Verify(certPaths); !ok {
	log.Fatalln(err)
}

identity := resp.GetIdentity()
fmt.Println("Name:", identity.CommonName)
fmt.Println("Personal ID:", identity.SerialNumber)
fmt.Println("Country:", identity.Country)
// Output:
// Name: TESTNUMBER,QUALIFIED OK1
// Personal ID: PNOEE-30303039914
// Country: EE

Another example contains many more quest parameters for the signing method. Sign and Authenticate methods are similar and you can use the same AuthRequest parameters for both of them.

This examples is asynchronous uses channel.

semid := NewSemanticIdentifier(IdentifierTypePNO, CountryEE, "30303039914")
client := NewClient("https://sid.demo.sk.ee/smart-id-rp/v2/", 5000)
request := AuthRequest{
	// Replace in production with real RelyingPartyUUID.
	RelyingPartyUUID: "00000000-0000-0000-0000-000000000000",
	// Replace in production with real RelyingPartyName.
	RelyingPartyName: "DEMO",
	// It is good to generate new has for security reasons.
	Hash: GenerateAuthHash(SHA384),
	// HashType should be the same as in GenerateAuthHash.
	HashType: SHA384,
	// We use personal ID as Identifier, also possible to use document
	// number.
	Identifier: semid,
	AuthType:   AuthTypeEtsi,
	CertificateLevel: CertLevelQualified,
	AllowedInteractionsOrder: []AllowedInteractionsOrder{
		{
			Type:          InteractionVerificationCodeChoice,
			DisplayText60: "Welcome to Smart-ID!",
		},
		{
			Type:          InteractionDisplayTextAndPIN,
			DisplayText200: "Welcome to Smart-ID! A bit longer text."
		},
	},
}

resp := <-client.Sign(&request)

if _, err := resp.Validate(); err != nil {
	log.Fatalln(err)
}

// It is also good to verify the certificate over secure. But it isn't
// mandatory, but strongly recommended.
//
// If you always get an error: x509: certificate signed by unknown
// authority. Most probably you need install ca-certificates for
// example for GNU Linux.
//
// sudo apt-get install ca-certificates
// sudo dnf install ca-certificates
certPaths := []string{"./certs/TEST_of_EID-SK_2016.pem.crt"}
 if ok, err := resp.Cert.Verify(certPaths); !ok {
	log.Fatalln(err)
}

identity := resp.GetIdentity()
fmt.Println("Name:", identity.CommonName)
fmt.Println("Personal ID:", identity.SerialNumber)
fmt.Println("Country:", identity.Country)
// Output:
// Name: TESTNUMBER,QUALIFIED OK1
// Personal ID: PNOEE-30303039914
// Country: EE

Demonstration of siging with document number.

docid := "PNOEE-30303039914-1Q3P-Q"
client := NewClient("https://sid.demo.sk.ee/smart-id-rp/v2/", 5000)
request := AuthRequest{
	RelyingPartyUUID: "00000000-0000-0000-0000-000000000000",
	RelyingPartyName: "DEMO",
	Hash: GenerateAuthHash(SHA512),
	Identifier: docid,
	AuthType:   AuthTypeDocument,
}
resp := <-client.Authenticate(&request)
fmt.Printf("%+v\n", resp)

Index

Examples

Constants

View Source
const (
	// SHA256 algorithm for encryption.
	SHA256 = "SHA256"

	// SHA384 algorithm for encryption.
	SHA384 = "SHA384"

	// SHA512 algorithm for encryption.
	SHA512 = "SHA512"
)

Supported hashing algorithms.

View Source
const (
	// CertLevelQualified QUALIFIED level of certificate.
	CertLevelQualified = "QUALIFIED" // recommended

	// CertLevelAdvanced ADVANCED level of certificate.
	CertLevelAdvanced = "ADVANCED"
)

Certificate levels. QUALIFIED is the more modern way to use it. Some accounts or services might support only non-qualified certificates, which are known as basic accounts (ADVANCED).

View Source
const (
	AuthTypeEtsi     = "etsi" // (default)
	AuthTypeDocument = "document"
)

Authentication by ETSI or by document number.

View Source
const (
	// InteractionDisplayTextAndPIN shows PIN and message in the
	// user's app.
	InteractionDisplayTextAndPIN = "displayTextAndPIN"

	// InteractionVerificationCodeChoice allows user to choice
	// verification code in the user's app.
	InteractionVerificationCodeChoice = "verificationCodeChoice"

	// InteractionConfirmationMessage shows confirmation messages in the user's app.
	InteractionConfirmationMessage = "confirmationMessage"

	// InteractionConfirmationMessageAndVerificationCodeChoice shows both
	// code and message in the user's app.
	InteractionConfirmationMessageAndVerificationCodeChoice = "confirmationMessageAndVerificationCodeChoice"
)
View Source
const (
	EndpointAuthentication = "authentication" // default
	EndpointSignature      = "signature"
)

API endpoints. There are currently supported 2 endpoints for requests.

View Source
const (
	CountryEE = "EE" // Estonia
	CountryLV = "LV" // Latvia
	CountryLT = "LT" // Lithuania
	CountryKZ = "KZ" // Kazakhstan
)

Supported countries by Smart-ID.

View Source
const (
	// IdentifierTypePAS for identification based on passport number.
	IdentifierTypePAS = "PAS"

	// IdentifierTypeIDC for identification based on national identity
	// card number.
	IdentifierTypeIDC = "IDC"

	// IdentifierPNO for identification based on (national) personal
	// number (national civic registration number).
	IdentifierTypePNO = "PNO"
)

People can be identified by their ETSI Natural Person Semantics Identifier specified in ETSI319412-1. Other way it might be passport number id card number, this depends on country or company internal politics.

View Source
const (
	// SessionStatusComplete indicates that request is completed.
	SessionStatusComplete = "COMPLETE"

	// SessionStatusRunning indicates that request is still running.
	SessionStatusRunning = "RUNNING"
)

Session response status. There are only 2 statuses available for Smart-ID service.

View Source
const (
	SessionResultOK                                         = "OK"
	SessionResultUserRefusedCertChoice                      = "USER_REFUSED_CERT_CHOICE"
	SessionResultUserRefusedDisplayTextAndPIN               = "USER_REFUSED_DISPLAYTEXTANDPIN"
	SessionResultUserRefusedVCChoice                        = "USER_REFUSED_VC_CHOICE"
	SessionResultUserRefusedConfirmationMessage             = "USER_REFUSED_CONFIRMATIONMESSAGE"
	SessionResultUserRefusedConfirmationMessageWithVCChoice = "USER_REFUSED_CONFIRMATIONMESSAGE_WITH_VC_CHOICE"
	SessionResultWrongVC                                    = "WRONG_VC"
	SessionResultTimeout                                    = "TIMEOUT"
	SessionResultRequiredInteractionNotSupportedByApp       = "REQUIRED_INTERACTION_NOT_SUPPORTED_BY_APP"
)

Session response result codes.

Variables

View Source
var (
	// ErrCertNoCertGiven error when no certificates used in Verify()
	// function.
	ErrCertNoCertGiven = errors.New("No certs given")
)

Functions

func NewSemanticIdentifier

func NewSemanticIdentifier(typ, country, id string) string

NewSemanticIdentifier creates new semantic identifier as string.

Example
semid := NewSemanticIdentifier(IdentifierTypePNO, CountryEE, "12345678901")
fmt.Println(semid)
Output:

PNOEE-12345678901

Types

type AllowedInteractionsOrder

type AllowedInteractionsOrder struct {
	// Type is used to define interaction type.
	// There are 4 interaction types:
	//
	//	- displayTextAndPIN
	//	- verificationCodeChoice
	//	- confirmationMessage
	//	- confirmationMessageAndVerificationCodeChoice
	Type string `json:"type"`

	// DisplayText60 allows to enter up to 60 characters of text.
	DisplayText60 string `json:"displayText60,omitempty"`

	// DisplayText200 allows to enter up to 200 characters of text.
	DisplayText200 string `json:"displayText200,omitempty"`
}

AllowedInteractionsOrder allows you to interact with the user's app. For example, display the message or choose a verification code. Not all apps can support all interaction types. You can use many of them for fallback. The most common one is displayTextAndPIN. It should be used as the last fallback if you are going to use AllowedInteractionsOrder.

type AuthHash

type AuthHash []byte

AuthHash contains the hash sum for authentication and signing requests and the calculation of the verification code.

func GenerateAuthHash

func GenerateAuthHash(algo string) AuthHash

GenerateAuthHash generates a new random hashe.

Example
hash := GenerateAuthHash(SHA512)
fmt.Println(hash)
Output:

[46 117 219 69 255 193 115 74 0 96 133 66 216 167 99 93 127 89 158 75 218 203 252 240 196 213 171 133 188 200 23 170 70 31 27 209 213 109 225 183 46 78 169 27 148 118 58 120 142 199 100 164 235 69 107 157 219 201 143 1 112 244 171 183]

func (AuthHash) CalculateVerificationCode

func (h AuthHash) CalculateVerificationCode() string

CalculateVerificationCode computes the verification 4-digit verification that you can show it to use. Examine how VerificationCode is computed. https://github.com/SK-EID/smart-id-documentation#23122-computing-the-verification-code

Example
hash := GenerateAuthHash(SHA512)
fmt.Println(hash.CalculateVerificationCode())
Output:

3174

func (AuthHash) EncodeBase64

func (h AuthHash) EncodeBase64() []byte

EncodeBase64 encodes hash sum to base64. Normally you should not convert hash to base64 manually. Library does it automatically on the request.

Example
hash := GenerateAuthHash(SHA512)
fmt.Println(hash.EncodeBase64())
Output:

[76 110 88 98 82 102 47 66 99 48 111 65 89 73 86 67 50 75 100 106 88 88 57 90 110 107 118 97 121 47 122 119 120 78 87 114 104 98 122 73 70 54 112 71 72 120 118 82 49 87 51 104 116 121 53 79 113 82 117 85 100 106 112 52 106 115 100 107 112 79 116 70 97 53 51 98 121 89 56 66 99 80 83 114 116 119 61 61]

func (AuthHash) ToBase64String

func (h AuthHash) ToBase64String() string

ToBase64String coverts AuthHash to base64 string.

Example
hash := GenerateAuthHash(SHA512)
fmt.Println(hash.ToBase64String())
Output:

LnXbRf/Bc0oAYIVC2KdjXX9Znkvay/zwxNWrhbzIF6pGHxvR1W3hty5OqRuUdjp4jsdkpOtFa53byY8BcPSrtw==

type AuthRequest

type AuthRequest struct {
	// RelyingPartyUUID is UUID of relying party. Issued by SK.
	RelyingPartyUUID string `json:"relyingPartyUUID"`

	// RelyingPartyName is the name of the relying party. Issued by SK.
	RelyingPartyName string `json:"relyingPartyName"`

	// CertificateLevel is the level of certificate. Used possible values:
	//	QUALIFIED (recommended, default)
	//	ADVANCED
	CertificateLevel string `json:"certificateLevel"`

	// Base64 encoded hash function output to be signed (base64 encoding
	// according to rfc4648).
	Hash AuthHash `json:"hash"`

	// Hash algorithm. At the moment used only SHA512
	HashType string `json:"hashType"`

	// Nonce set behavior when requester wants, it can override the
	// idempotent behavior inside of this timeframe using an optional
	// nonce parameter present for all POST requests. Normally, that
	// parameter can be omitted. Read more
	// https://github.com/SK-EID/smart-id-documentation#235-idempotent-behaviour
	Nonce string `json:"nonce,omitempty"`

	// Used only when agreed with Smart-ID provider.
	// When omitted request capabilities are derived from
	// CertificateLevel parameter.
	Capabilities []string `json:"capabilities,omitempty"`

	// An app can support different interaction flows and a relying party can
	// demand a particular flow with or without a fall back possibility.
	// Different interaction flows can support different amount of data to
	// display information to user.
	AllowedInteractionsOrder []AllowedInteractionsOrder `json:"allowedInteractionsOrder,omitempty"`

	// AuthType is the type of authentication. Can be etsi or document.
	//	 AuthTypeEtsi is for authentication by semantic identifier (default).
	//	 AuthTypeDocument is for authentication by document number.
	AuthType string

	// Identifier is the semantic identifier or document number. This
	// is identifier used for person's identication.
	Identifier string
	// contains filtered or unexported fields
}

AuthRequest represents structure that contains authentication and signing properties required for request.

type AuthResponse

type AuthResponse struct {
	// SessionID is UUID format.
	SessionID string

	// Response is embedded response to store return code and message.
	Response
}

AuthResponse is returned from authentication and signing endpoint responses.

type Cert

type Cert struct {
	// Value is the base64 encoded string of certificate.
	Value string `json:"value"`

	// CertificateLevel is the level of the certificate:
	//	QUALIFIED
	//	ADVANCED
	CertificateLevel string `json:"certificateLevel"`
	// contains filtered or unexported fields
}

Cert represents certificate from session response.

func (*Cert) GetIssuer added in v1.0.3

func (c *Cert) GetIssuer() *pkix.Name

GetIssuer get issuer from certificate in PKIX format.

func (*Cert) GetSubject

func (c *Cert) GetSubject() *pkix.Name

GetSubject get subject from certificate in PKIX format.

func (*Cert) GetX509Cert

func (c *Cert) GetX509Cert() *x509.Certificate

GetX509Cert returns X509 certificate from response.

func (*Cert) IsExpired

func (c *Cert) IsExpired() bool

IsExpired checks that certificate has expired.

func (*Cert) IsNotActive

func (c *Cert) IsNotActive() bool

IsNotActive checks that certificate is not yet active.

func (*Cert) IsSameLevel

func (c *Cert) IsSameLevel(lvl string) bool

IsSameLevel checks that certificate is the same level as argument.

func (*Cert) Verify

func (c *Cert) Verify(paths []string) (bool, error)

Verify certificate by file system paths.

type Client

type Client struct {
	// APIUrl sets the base API URL. Check official documentation
	// https://github.com/SK-EID/smart-id-documentation/wiki/Environment-technical-parameters
	APIUrl string

	// Poll defines poll timeout value in milliseconds. The upper bound of
	// timeout is 120000, the minimum is 1000. If not specified by the client,
	// a value halfway between maximum and minimum is used.
	Poll uint32
	// contains filtered or unexported fields
}

Client is used to interact with endpoints and make requests and receive, responses.

func NewClient

func NewClient(url string, poll uint32, opts ...Option) *Client

NewClient creates a new client instance. Poll will be in range 1000ms to 120000ms.

func (*Client) Authenticate

func (c *Client) Authenticate(ctx context.Context, req *AuthRequest) chan *SessionResponse

Authenticate does authentication in asynchronous way using channel.

Example
semid := NewSemanticIdentifier(IdentifierTypePNO, CountryEE, "30303039914")
client := NewClient("https://sid.demo.sk.ee/smart-id-rp/v2/", 5000)
request := AuthRequest{
	// Replace in production with real RelyingPartyUUID.
	RelyingPartyUUID: "00000000-0000-0000-0000-000000000000",
	// Replace in production with real RelyingPartyName.
	RelyingPartyName: "DEMO",
	// It is good to generate new has for security reasons.
	Hash: GenerateAuthHash(SHA512),
	// We use personal ID as Identifier, also possible to use document
	// number.
	Identifier: semid,
	AuthType:   AuthTypeEtsi,
}

resp := <-client.Authenticate(context.TODO(), &request)
if _, err := resp.Validate(); err != nil {
	log.Fatalln(err)
}

// It is also good to verify the certificate over secure. But it isn't
// mandatory, but strongly recommended.
//
// If you always get an error: x509: certificate signed by unknown
// authority. Most probably you need install ca-certificates for
// example for GNU Linux.
//
// sudo apt-get install ca-certificates
// sudo dnf install ca-certificates
certPaths := []string{"./certs/TEST_of_EID-SK_2016.pem.crt"}
if ok, err := resp.Cert.Verify(certPaths); !ok {
	log.Fatalln(err)
}

identity := resp.GetIdentity()
fmt.Println("Name:", identity.CommonName)
fmt.Println("Personal ID:", identity.SerialNumber)
fmt.Println("Country:", identity.Country)
Output:

Name: TESTNUMBER,OK
Personal ID: PNOEE-30303039914
Country: EE

func (*Client) AuthenticateSync

func (c *Client) AuthenticateSync(ctx context.Context, req *AuthRequest) (*SessionResponse, error)

AuthenticateSync does authentication in synchronous way.

Example
semid := NewSemanticIdentifier(IdentifierTypePNO, CountryEE, "30303039914")
client := NewClient("https://sid.demo.sk.ee/smart-id-rp/v2/", 5000)
request := AuthRequest{
	// Replace in production with real RelyingPartyUUID.
	RelyingPartyUUID: "00000000-0000-0000-0000-000000000000",
	// Replace in production with real RelyingPartyName.
	RelyingPartyName: "DEMO",
	// It is good to generate new has for security reasons.
	Hash: GenerateAuthHash(SHA512),
	// We use personal ID as Identifier, also possible to use document
	// number.
	Identifier: semid,
	AuthType:   AuthTypeEtsi,
}

resp, err := client.AuthenticateSync(context.TODO(), &request)
if err != nil {
	log.Fatalln(err)
}

if _, err := resp.Validate(); err != nil {
	log.Fatalln(err)
}

// It is also good to verify the certificate over secure. But it isn't
// mandatory, but strongly recommended.
//
// If you always get an error: x509: certificate signed by unknown
// authority. Most probably you need install ca-certificates for
// example for GNU Linux.
//
// sudo apt-get install ca-certificates
// sudo dnf install ca-certificates
certPaths := []string{"./certs/TEST_of_EID-SK_2016.pem.crt"}
if ok, err := resp.Cert.Verify(certPaths); !ok {
	log.Fatalln(err)
}

identity := resp.GetIdentity()
fmt.Println("Name:", identity.CommonName)
fmt.Println("Personal ID:", identity.SerialNumber)
fmt.Println("Country:", identity.Country)
Output:

Name: TESTNUMBER,OK
Personal ID: PNOEE-30303039914
Country: EE

func (*Client) Sign

func (c *Client) Sign(ctx context.Context, req *AuthRequest) chan *SessionResponse

Sign does signing in asynchronous way using channel. Sign is very similar to Authenticate, but uses other endpoint.

Example
semid := NewSemanticIdentifier(IdentifierTypePNO, CountryEE, "30303039914")
client := NewClient("https://sid.demo.sk.ee/smart-id-rp/v2/", 5000)
request := AuthRequest{
	// Replace in production with real RelyingPartyUUID.
	RelyingPartyUUID: "00000000-0000-0000-0000-000000000000",
	// Replace in production with real RelyingPartyName.
	RelyingPartyName: "DEMO",
	// It is good to generate new has for security reasons.
	Hash: GenerateAuthHash(SHA512),
	// We use personal ID as Identifier, also possible to use document
	// number.
	Identifier: semid,
	AuthType:   AuthTypeEtsi,
}

resp := <-client.Authenticate(context.TODO(), &request)
if _, err := resp.Validate(); err != nil {
	log.Fatalln(err)
}

// It is also good to verify the certificate over secure. But it isn't
// mandatory, but strongly recommended.
//
// If you always get an error: x509: certificate signed by unknown
// authority. Most probably you need install ca-certificates for
// example for GNU Linux.
//
// sudo apt-get install ca-certificates
// sudo dnf install ca-certificates
certPaths := []string{"./certs/TEST_of_EID-SK_2016.pem.crt"}
if ok, err := resp.Cert.Verify(certPaths); !ok {
	log.Fatalln(err)
}

identity := resp.GetIdentity()
fmt.Println("Name:", identity.CommonName)
fmt.Println("Personal ID:", identity.SerialNumber)
fmt.Println("Country:", identity.Country)
Output:

Name: TESTNUMBER,OK
Personal ID: PNOEE-30303039914
Country: EE

func (*Client) SignSync

func (c *Client) SignSync(ctx context.Context, req *AuthRequest) (*SessionResponse, error)

SignSync does signing in synchronous way. SignSync is very similar to AuthenticateSync, but uses other endpoint.

Example
semid := NewSemanticIdentifier(IdentifierTypePNO, CountryEE, "30303039914")
client := NewClient("https://sid.demo.sk.ee/smart-id-rp/v2/", 5000)
request := AuthRequest{
	// Replace in production with real RelyingPartyUUID.
	RelyingPartyUUID: "00000000-0000-0000-0000-000000000000",
	// Replace in production with real RelyingPartyName.
	RelyingPartyName: "DEMO",
	// It is good to generate new has for security reasons.
	Hash: GenerateAuthHash(SHA512),
	// We use personal ID as Identifier, also possible to use document
	// number.
	Identifier: semid,
	AuthType:   AuthTypeEtsi,
}

resp, err := client.AuthenticateSync(context.TODO(), &request)
if err != nil {
	log.Fatalln(err)
}

if _, err := resp.Validate(); err != nil {
	log.Fatalln(err)
}

// It is also good to verify the certificate over secure. But it isn't
// mandatory, but strongly recommended.
//
// If you always get an error: x509: certificate signed by unknown
// authority. Most probably you need install ca-certificates for
// example for GNU Linux.
//
// sudo apt-get install ca-certificates
// sudo dnf install ca-certificates
certPaths := []string{"./certs/TEST_of_EID-SK_2016.pem.crt"}
if ok, err := resp.Cert.Verify(certPaths); !ok {
	log.Fatalln(err)
}

identity := resp.GetIdentity()
fmt.Println("Name:", identity.CommonName)
fmt.Println("Personal ID:", identity.SerialNumber)
fmt.Println("Country:", identity.Country)
Output:

Name: TESTNUMBER,OK
Personal ID: PNOEE-30303039914
Country: EE

type Error added in v1.1.0

type Error struct {
	Err     error
	Code    int
	Message string
}

Error represents error for the response.

func (*Error) Error added in v1.1.0

func (e *Error) Error() string

type Identity

type Identity struct {
	Country            string
	Organization       string
	OrganizationalUnit string
	Locality           string
	Province           string
	StreetAddress      string
	PostalCode         string
	SerialNumber       string
	CommonName         string
}

Identity represents simpler format of PKIX Subject.

type Option added in v1.1.1

type Option interface {
	// contains filtered or unexported methods
}

Option interface used for setting optional Client properties.

func WithHTTPClient added in v1.3.0

func WithHTTPClient(httpClient *http.Client) Option

WithHTTPClient specifies which http client to use.

type Response

type Response struct {
	// Code is HTTP status or internal error code.
	Code int `json:"code"`

	// Messages is HTTP status text or internal message.
	Message string `json:"message"`
}

Response is a basic response structure that holds code (usually HTTP status) and a message (usually HTTP status text). It is typically only used for HTTP code for responses.

func (*Response) IsStatusOK

func (r *Response) IsStatusOK() bool

IsStatusOK checks if response has HTTP Status 200 (OK).

type Result

type Result struct {
	EndResult      string `json:"endResult"`
	DocumentNumber string `json:"documentNumber"`
}

Result represents the result from response.

type SemanticIdentifier

type SemanticIdentifier struct {
	Type, Country, ID string
}

SemanticIdentifier is identifier to identify document type, country, and civic personal id.

From official guide:

Objects referenced by etsi/:semantics-identifier are persons identified by their ETSI Natural Person Sematics Identifier specified in ETSI319412-1. See more https://github.com/SK-EID/smart-id-documentation#2322-etsisemantics-identifier

func (SemanticIdentifier) String

func (sd SemanticIdentifier) String() string

type Session

type Session struct {
	// SessionID is the session identified in UUID format.
	SessionID string `json:"sessionID"`
	// contains filtered or unexported fields
}

Session represents information about session.

type SessionRequest

type SessionRequest struct {
	// SessionID is the session identified in UUID format.
	SessionID string
	// contains filtered or unexported fields
}

SessionRequest represents structure that contains session properties required for request.

type SessionResponse

type SessionResponse struct {
	// State is the status of session. There are only 2 statuses:
	//	- RUNNING
	//	- COMPLETE
	State string `json:"state"`

	// Result shows what the end result of the response. See result codes.
	Result Result `json:"result"`

	// Signature contains signature (Value and Algorithm).
	// Empty if not OK.
	Signature Signature `json:"signature"`

	// Cert contains signature (Value and CertificateLevel).
	// Empty if not OK.
	Cert Cert `json:"cert"`

	// InteractionFlowUsed show which interaction flow was used.
	InteractionFlowUsed string `json:"interactionFlowUsed,omitempty"`

	// DeviceIpAddress give IP address for the device where user's used
	// the app.
	DeviceIPAddress string `json:"deviceIpAddress"`

	// Session contains request parameters like SessionID and Hash.
	Session

	// Response contains code and message.
	Response
}

SessionResponse is used for session endpoint response.

func (*SessionResponse) GetFailureReason

func (r *SessionResponse) GetFailureReason() string

GetFailureReason returns result code for the session failure. If the return value is SESSION_RESULT_OK, this means there is no failure.

Possible result codes are:

  • SessionResultOK
  • SessionResultUserRefused
  • SessionResultUserRefusedDisplayTextAndPIN
  • SessionResultUserRefusedVCChoice
  • SessionResultUserRefusedConfirmationMessage
  • SessionResultUserRefusedConfirmationMessageWithVCChoice
  • SessionResultUserWrongVC
  • SessionResultUserTimeout

func (*SessionResponse) GetIdentity

func (r *SessionResponse) GetIdentity() *Identity

GetIdentity gets user identity based on certificated.

func (*SessionResponse) GetIssuerIdentity added in v1.0.3

func (r *SessionResponse) GetIssuerIdentity() *Identity

GetIssuerIdentity gets user identity based on certificated.

func (*SessionResponse) IsCompleted

func (r *SessionResponse) IsCompleted() bool

IsCompleted checks that response has completed. If the return value is empty, it also means not completed.

func (*SessionResponse) IsFailed

func (r *SessionResponse) IsFailed() bool

IsFailed checks that response is not successful.

func (*SessionResponse) IsValidSignature

func (r *SessionResponse) IsValidSignature() bool

IsValidSignature checks validity of the signature.

func (*SessionResponse) Validate

func (r *SessionResponse) Validate() (bool, error)

Validate checks is session response is valid.

type Signature

type Signature struct {
	// Value is the base64 encoded string of signature.
	Value string `json:"value"`

	// Algorithm represents the algorithm used to encrypt the signature.
	Algorithm string `json:"algorithm"`
}

Signature represents signature from session response.

func (Signature) IsValid

func (sig Signature) IsValid(c Cert, h AuthHash) bool

IsValid checks the validity of signature.

Jump to

Keyboard shortcuts

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