package telegram import "errors" /* "crypto/aes" "crypto/cipher" "crypto/rand" "crypto/rsa" "crypto/sha1" //nolint: gosec "crypto/sha256" "crypto/sha512" "encoding/base64" */ type ( // PassportData contains information about Telegram Passport data shared with the bot by the user. PassportData struct { // Array with information about documents and other Telegram Passport elements that was shared with the bot Data []*EncryptedPassportElement `json:"data"` // Encrypted credentials required to decrypt the data Credentials *EncryptedCredentials `json:"credentials"` } // PassportFile represents a file uploaded to Telegram Passport. Currently all Telegram Passport files are in JPEG format when decrypted and don't exceed 10MB. PassportFile struct { // Identifier for this file, which can be used to download or reuse the file FileID string `json:"file_id"` // Unique identifier for this file, which is supposed to be the same over time and for different bots. Can't be used to download or reuse the file. FileUniqueID string `json:"file_unique_id"` // File size FileSize int `json:"file_size"` // Unix time when the file was uploaded FileDate int `json:"file_date"` } // EncryptedPassportElement contains information about documents or other Telegram Passport elements shared with the bot by the user. EncryptedPassportElement struct { // Element type. Type string `json:"type"` // Base64-encoded encrypted Telegram Passport element data provided by the user, available for "personal_details", "passport", "driver_license", "identity_card", "identity_passport" and "address" types. Can be decrypted and verified using the accompanying EncryptedCredentials. Data string `json:"data,omitempty"` // User's verified phone number, available only for "phone_number" type PhoneNumber string `json:"phone_number,omitempty"` // User's verified email address, available only for "email" type Email string `json:"email,omitempty"` // Array of encrypted files with documents provided by the user, available for "utility_bill", "bank_statement", "rental_agreement", "passport_registration" and "temporary_registration" types. Files can be decrypted and verified using the accompanying EncryptedCredentials. Files []*PassportFile `json:"files,omitempty"` // Encrypted file with the front side of the document, provided by the user. Available for "passport", "driver_license", "identity_card" and "internal_passport". The file can be decrypted and verified using the accompanying EncryptedCredentials. FrontSide *PassportFile `json:"front_side,omitempty"` // Encrypted file with the reverse side of the document, provided by the user. Available for "driver_license" and "identity_card". The file can be decrypted and verified using the accompanying EncryptedCredentials. ReverseSide *PassportFile `json:"reverse_side,omitempty"` // Encrypted file with the selfie of the user holding a document, provided by the user; available for "passport", "driver_license", "identity_card" and "internal_passport". The file can be decrypted and verified using the accompanying EncryptedCredentials. Selfie *PassportFile `json:"selfie,omitempty"` // Array of encrypted files with translated versions of documents provided by the user. Available if requested for “passport”, “driver_license”, “identity_card”, “internal_passport”, “utility_bill”, “bank_statement”, “rental_agreement”, “passport_registration” and “temporary_registration” types. Files can be decrypted and verified using the accompanying EncryptedCredentials. Translation []*PassportFile `json:"translation,omitempty"` // Base64-encoded element hash for using in PassportElementErrorUnspecified Hash string `json:"hash"` } // EncryptedCredentials contains data required for decrypting and authenticating EncryptedPassportElement. See the Telegram Passport Documentation for a complete description of the data decryption and authentication processes. EncryptedCredentials struct { // Base64-encoded encrypted JSON-serialized data with unique user's payload, data hashes and secrets required for EncryptedPassportElement decryption and authentication Data string `json:"data"` // Base64-encoded data hash for data authentication Hash string `json:"hash"` // Base64-encoded secret, encrypted with the bot's public RSA key, required for data decryption Secret string `json:"secret"` } // PassportElementError represents an error in the Telegram Passport element which was submitted that should be resolved by the user. PassportElementError interface { PassportElementErrorMessage() string PassportElementErrorSource() string PassportElementErrorType() string } // PassportElementErrorDataField represents an issue in one of the data fields that was provided by the user. The error is considered resolved when the field's value changes. PassportElementErrorDataField struct { // Error source, must be data Source string `json:"source"` // The section of the user's Telegram Passport which has the error, one of "personal_details", "passport", "driver_license", "identity_card", "internal_passport", "address" Type string `json:"type"` // Name of the data field which has the error FieldName string `json:"field_name"` // Base64-encoded data hash DataHash string `json:"data_hash"` // Error message Message string `json:"message"` } // PassportElementErrorFrontSide represents an issue with the front side of a document. The error is considered resolved when the file with the front side of the document changes. PassportElementErrorFrontSide struct { // Error source, must be front_side Source string `json:"source"` // The section of the user's Telegram Passport which has the issue, one of "passport", "driver_license", "identity_card", "internal_passport" Type string `json:"type"` // Base64-encoded hash of the file with the front side of the document FileHash string `json:"file_hash"` // Error message Message string `json:"message"` } // PassportElementErrorReverseSide represents an issue with the reverse side of a document. The error is considered resolved when the file with reverse side of the document changes. PassportElementErrorReverseSide struct { // Error source, must be reverse_side Source string `json:"source"` // The section of the user's Telegram Passport which has the issue, one of "driver_license", "identity_card" Type string `json:"type"` // Base64-encoded hash of the file with the reverse side of the document FileHash string `json:"file_hash"` // Error message Message string `json:"message"` } // PassportElementErrorSelfie represents an issue with the selfie with a document. The error is considered resolved when the file with the selfie changes. PassportElementErrorSelfie struct { // Error source, must be selfie Source string `json:"source"` // The section of the user's Telegram Passport which has the issue, one of "passport", "driver_license", "identity_card", "internal_passport" Type string `json:"type"` // Base64-encoded hash of the file with the selfie FileHash string `json:"file_hash"` // Error message Message string `json:"message"` } // PassportElementErrorFile represents an issue with a document scan. The error is considered resolved when the file with the document scan changes. PassportElementErrorFile struct { // Error source, must be file Source string `json:"source"` // The section of the user's Telegram Passport which has the issue, one of "utility_bill", "bank_statement", "rental_agreement", "passport_registration", "temporary_registration" Type string `json:"type"` // Base64-encoded file hash FileHash string `json:"file_hash"` // Error message Message string `json:"message"` } // PassportElementErrorFiles represents an issue with a list of scans. The error is considered resolved when the list of files containing the scans changes. PassportElementErrorFiles struct { // Error source, must be files Source string `json:"source"` // The section of the user's Telegram Passport which has the issue, one of "utility_bill", "bank_statement", "rental_agreement", "passport_registration", "temporary_registration" Type string `json:"type"` // List of base64-encoded file hashes FileHashes []string `json:"file_hashes"` // Error message Message string `json:"message"` } // PassportElementErrorTranslationFile represents an issue with one of the files that constitute the translation of a document. The error is considered resolved when the file changes. PassportElementErrorTranslationFile struct { // Error source, must be translation_file Source string `json:"source"` // Type of element of the user's Telegram Passport which has the issue, one of “passport”, “driver_license”, “identity_card”, “internal_passport”, “utility_bill”, “bank_statement”, “rental_agreement”, “passport_registration”, “temporary_registration” Type string `json:"type"` // Base64-encoded file hash FileHash string `json:"file_hash"` // Error message Message string `json:"message"` } // PassportElementErrorTranslationFiles represents an issue with the translated version of a document. The error is considered resolved when a file with the document translation change. PassportElementErrorTranslationFiles struct { // Error source, must be translation_files Source string `json:"source"` // Type of element of the user's Telegram Passport which has the issue, one of “passport”, “driver_license”, “identity_card”, “internal_passport”, “utility_bill”, “bank_statement”, “rental_agreement”, “passport_registration”, “temporary_registration” Type string `json:"type"` // List of base64-encoded file hashes FileHashes []string `json:"file_hashes"` // Error message Message string `json:"message"` } // PassportElementErrorUnspecified represents an issue in an unspecified place. The error is considered resolved when new data is added. PassportElementErrorUnspecified struct { // Error source, must be unspecified Source string `json:"source"` // Type of element of the user's Telegram Passport which has the issue Type string `json:"type"` // Base64-encoded element hash ElementHash string `json:"element_hash"` // Error message Message string `json:"message"` } SetPassportDataErrors struct { // User identifier UserID int64 `json:"user_id"` // A JSON-serialized array describing the errors Errors []PassportElementError `json:"errors"` } // AuthParameters represent a Telegram Passport auth parameters for SDK. Auth struct { // Unique identifier for the b. You can get it from bot token. For example, for the bot token 1234567:4TT8bAc8GHUspu3ERYn-KGcvsvGB9u_n4ddy, the bot id is 1234567. BotID int64 `json:"bot_id"` // A JSON-serialized object describing the data you want to request Scope *PassportScope `json:"scope"` // Public key of the bot PublicKey string `json:"public_key"` // Bot-specified nonce. // // Important: For security purposes it should be a cryptographically secure unique identifier of the request. In particular, it should be long enough and it should be generated using a cryptographically secure pseudorandom number generator. You should never accept credentials with the same nonce twice. Nonce string `json:"nonce"` } // PassportScope represents the data to be requested. PassportScope struct { // List of requested elements, each type may be used only once in the entire array of PassportScopeElement objects Data []*PassportScopeElement `json:"data"` // Scope version, must be 1 V int `json:"v"` } // PassportScopeElement represents a requested element. PassportScopeElement interface { PassportScopeElementTranslation() bool PassportScopeElementSelfie() bool } // PassportScopeElementOneOfSeveral represents several elements one of which must be provided. PassportScopeElementOneOfSeveral struct { // List of elements one of which must be provided; OneOf []*PassportScopeElementOne `json:"one_of"` // Use this parameter if you want to request a selfie with the document from this list that the user chooses to upload. Selfie bool `json:"selfie,omitempty"` // Use this parameter if you want to request a translation of the document from this list that the user chooses to upload. Note: We suggest to only request translations after you have received a valid document that requires one. Translation bool `json:"translation,omitempty"` } // PassportScopeElementOne represents one particular element that must be provided. If no options are needed, String can be used instead of this object to specify the type of the element. PassportScopeElementOne struct { // Element type. Type string `json:"type"` // Use this parameter if you want to request a selfie with the document as well. Selfie bool `json:"selfie,omitempty"` // Use this parameter if you want to request a translation of the document as well. Translation bool `json:"translation,omitempty"` // Use this parameter to request the first, last and middle name of the user in the language of the user's country of residence. NativeNames bool `json:"native_names,omitempty"` } Passport struct { // Personal Details PersonalDetails struct { Data *PersonalDetails `json:"data"` } `json:"personal_details"` // Passport Passport struct { Data *IDDocumentData `json:"data"` FrontSide *PassportFile `json:"front_side"` Selfie *PassportFile `json:"selfie,omitempty"` Translation []*PassportFile `json:"translation,omitempty"` } `json:"passport"` // Internal Passport InternalPassport struct { Data *IDDocumentData `json:"data"` FrontSide *PassportFile `json:"front_side"` Selfie *PassportFile `json:"selfie,omitempty"` Translation []*PassportFile `json:"translation,omitempty"` } `json:"internal_passport"` // Driver License DriverLicense struct { Data *IDDocumentData `json:"data"` FrontSide *PassportFile `json:"front_side"` ReverseSide *PassportFile `json:"reverse_side"` Selfie *PassportFile `json:"selfie,omitempty"` Translation []*PassportFile `json:"translation,omitempty"` } `json:"driver_license"` // Identity Card IdentityCard struct { Data *IDDocumentData `json:"data"` FrontSide *PassportFile `json:"front_side"` ReverseSide *PassportFile `json:"reverse_side"` Selfie *PassportFile `json:"selfie,omitempty"` Translation []*PassportFile `json:"translation,omitempty"` } `json:"identity_card"` // Address Address struct { Data *ResidentialAddress `json:"data"` } `json:"address"` // Utility Bill UtilityBill struct { Files []*PassportFile `json:"files"` Translation []*PassportFile `json:"translation,omitempty"` } `json:"utility_bill"` // Bank Statement BankStatement struct { Files []*PassportFile `json:"files"` Translation []*PassportFile `json:"translation,omitempty"` } `json:"bank_statement"` // Rental Agreement RentalAgreement struct { Files []*PassportFile `json:"files"` Translation []*PassportFile `json:"translation,omitempty"` } `json:"rental_agreement"` // Registration Page in the Internal Passport PassportRegistration struct { Files []*PassportFile `json:"files"` Translation []*PassportFile `json:"translation,omitempty"` } `json:"passport_registration"` // Temporary Registration TemporaryRegistration struct { Files []*PassportFile `json:"files"` Translation []*PassportFile `json:"translation,omitempty"` } `json:"temporary_registration"` // Phone number PhoneNumber string `json:"phone_number"` // Email Email string `json:"email"` } // PersonalDetails represents personal details. PersonalDetails struct { // First Name FirstName string `json:"first_name"` // Last Name LastName string `json:"last_name"` // Middle Name MiddleName string `json:"middle_name,omitempty"` // Date of birth in DD.MM.YYYY format BirthDate string `json:"birth_date"` // Gender, male or female Gender string `json:"gender"` // Citizenship (ISO 3166-1 alpha-2 country code) CountryCode string `json:"country_code"` // Country of residence (ISO 3166-1 alpha-2 country code) ResidenceCountryCode string `json:"residence_country_code"` // First Name in the language of the user's country of residence FirstNameNative string `json:"first_name_native"` // Last Name in the language of the user's country of residence LastNameNative string `json:"last_name_native"` // Middle Name in the language of the user's country of residence MiddleNameNative string `json:"middle_name_native,omitempty"` } // ResidentialAddress represents a residential address. ResidentialAddress struct { // First line for the address StreetLine1 string `json:"street_line1"` // Second line for the address StreetLine2 string `json:"street_line2,omitempty"` // City City string `json:"city"` // State State string `json:"state,omitempty"` // ISO 3166-1 alpha-2 country code CountryCode string `json:"country_code"` // Address post code PostCode string `json:"post_code"` } // IDDocumentData represents the data of an identity document. IDDocumentData struct { // Document number DocumentNo string `json:"document_no"` // Date of expiry, in DD.MM.YYYY format ExpiryDate string `json:"expiry_date,omitempty"` } // Credentials is a JSON-serialized object. Credentials struct { // Credentials for encrypted data SecureData *SecureData `json:"secure_data"` // Bot-specified nonce Nonce string `json:"nonce"` } // SecureData represents the credentials required to decrypt encrypted data. All fields are optional and depend on fields that were requested. SecureData struct { // Credentials for encrypted personal details PersonalDetails *SecureValue `json:"personal_details,omitempty"` // Credentials for encrypted passport Passport *SecureValue `json:"passport,omitempty"` // Credentials for encrypted internal passport InternalPassport *SecureValue `json:"internal_passport,omitempty"` // Credentials for encrypted driver license DriverLicense *SecureValue `json:"driver_license,omitempty"` // Credentials for encrypted ID card IdentityCard *SecureValue `json:"identity_card,omitempty"` // Credentials for encrypted residential address Address *SecureValue `json:"address,omitempty"` // Credentials for encrypted utility bill UtilityBill *SecureValue `json:"utility_bill,omitempty"` // Credentials for encrypted bank statement BankStatement *SecureValue `json:"bank_statement,omitempty"` // Credentials for encrypted rental agreement RentalAgreement *SecureValue `json:"rental_agreement,omitempty"` // Credentials for encrypted registration from internal passport PassportRegistration *SecureValue `json:"passport_registration,omitempty"` // Credentials for encrypted temporary registration TemporaryRegistration *SecureValue `json:"temporary_registration,omitempty"` } // SecureValue represents the credentials required to decrypt encrypted values. All fields are optional and depend on the type of fields that were requested. SecureValue struct { // Credentials for encrypted Telegram Passport data. Data *DataCredentials `json:"data,omitempty"` // Credentials for an encrypted document's front side. FrontSide *FileCredentials `json:"front_side,omitempty"` // Credentials for an encrypted document's reverse side. ReverseSide *FileCredentials `json:"reverse_side,omitempty"` // Credentials for an encrypted selfie of the user with a document. Selfie *FileCredentials `json:"selfie,omitempty"` // Credentials for an encrypted translation of the document. Translation []*FileCredentials `json:"translation,omitempty"` // Credentials for encrypted files. Files []*FileCredentials `json:"files,omitempty"` } // DataCredentials can be used to decrypt encrypted data from the data field in EncryptedPassportElement. DataCredentials struct { // Checksum of encrypted data DataHash string `json:"data_hash"` // Secret of encrypted data Secret string `json:"secret"` } // FileCredentials can be used to decrypt encrypted files from the front_side, reverse_side, selfie, files and translation fields in EncryptedPassportElement. FileCredentials struct { // Checksum of encrypted file FileHash string `json:"file_hash"` // Secret of encrypted file Secret string `json:"secret"` } ) var ErrNotEqual = errors.New("credentials hash and credentials data hash is not equal") // SetPassportDataErrors informs a user that some of the Telegram Passport elements they provided contains errors. The user will not be able to re-submit their Passport to you until the errors are fixed (the contents of the field for which you returned the error must change). Returns True on success. // // Use this if the data submitted by the user doesn't satisfy the standards your service requires for any reason. For example, if a birthday date seems invalid, a submitted document is blurry, a scan shows evidence of tampering, etc. Supply some details in the error message to make sure the user knows how to correct the issues. func (b Bot) SetPassportDataErrors(uid int64, errors ...PassportElementError) (ok bool, err error) { src, err := b.Do(MethodSetPassportDataErrors, SetPassportDataErrors{ UserID: uid, Errors: errors, }) if err != nil { return ok, err } if err = parseResponseError(b.marshler, src, &ok); err != nil { return } return } /* TODO(toby3d) func (b *Bot) DecryptFile(pf *PassportFile, fc *FileCredentials) (data []byte, err error) { secret, err := decodeField(fc.Secret) if err != nil { return nil, err } hash, err := decodeField(fc.FileHash) if err != nil { return nil, err } key, iv := decryptSecretHash(secret, hash) file, err := b.GetFile(pf.FileID) if err != nil { return nil, err } if _, data, err = b.Client.Get(nil, b.NewFileURL(file.FilePath).String()); err != nil { return nil, err } if data, err = decryptData(key, iv, data); err != nil { return nil, err } if !match(hash, data) { err = ErrNotEqual return nil, err } offset := int(data[0]) data = data[offset:] return data, nil } func (dc *DataCredentials) decrypt(d string) (data []byte, err error) { secret, err := decodeField(dc.Secret) if err != nil { return } hash, err := decodeField(dc.DataHash) if err != nil { return } key, iv := decryptSecretHash(secret, hash) if data, err = decodeField(d); err != nil { return } if data, err = decryptData(key, iv, data); err != nil { return } if !match(hash, data) { err = ErrNotEqual } offset := int(data[0]) data = data[offset:] return } func (ec *EncryptedCredentials) Decrypt(pk *rsa.PrivateKey) (*Credentials, error) { if ec == nil || pk == nil { return nil, nil } data, err := decrypt(pk, ec.Secret, ec.Hash, ec.Data) if err != nil { return nil, err } var c Credentials if err = json.ConfigFastest.Unmarshal(data, &c); err != nil { return nil, Error{Description: err.Error()} } return &c, err } func (epe *EncryptedPassportElement) DecryptPersonalDetails(sv *SecureValue) (*PersonalDetails, error) { if !epe.IsPersonalDetails() || !sv.HasData() { return nil, nil } body, err := sv.Data.decrypt(epe.Data) if err != nil { return nil, err } var pd PersonalDetails if err = json.ConfigFastest.Unmarshal(body, &pd); err != nil { return nil, Error{Description: err.Error()} } return &pd, err } func (epe *EncryptedPassportElement) DecryptPassport(sv *SecureValue, b *Bot) (*IDDocumentData, []byte, []byte, [][]byte, error) { if !epe.IsPassport() || !sv.HasData() || !sv.HasFrontSide() { return nil, nil, nil, nil, nil } body, err := sv.Data.decrypt(epe.Data) if err != nil { return nil, nil, nil, nil, err } var idd IDDocumentData if err = b.marshler.Unmarshal(body, &idd); err != nil { return nil, nil, nil, nil, err } fs, err := b.DecryptFile(epe.FrontSide, sv.FrontSide) if err != nil { return &idd, nil, nil, nil, err } var s []byte if sv.HasSelfie() { if s, err = b.DecryptFile(epe.Selfie, sv.Selfie); err != nil { return &idd, fs, nil, nil, err } } t := make([][]byte, len(sv.Translation)) if sv.HasTranslation() { for i := range t { if t[i], err = b.DecryptFile(epe.Translation[i], sv.Translation[i]); err != nil { return &idd, fs, s, nil, err } } } return &idd, fs, s, t, nil } func (epe *EncryptedPassportElement) DecryptInternalPassport(sv *SecureValue, b *Bot) (*IDDocumentData, []byte, []byte, [][]byte, error) { if !epe.IsInternalPassport() || !sv.HasData() || !sv.HasFrontSide() { return nil, nil, nil, nil, nil } body, err := sv.Data.decrypt(epe.Data) if err != nil { return nil, nil, nil, nil, err } var idd IDDocumentData if err = b.marshler.Unmarshal(body, &idd); err != nil { return nil, nil, nil, nil, err } fs, err := b.DecryptFile(epe.FrontSide, sv.FrontSide) if err != nil { return &idd, nil, nil, nil, err } var s []byte if sv.HasSelfie() { if s, err = b.DecryptFile(epe.Selfie, sv.Selfie); err != nil { return &idd, fs, nil, nil, err } } t := make([][]byte, len(sv.Translation)) if sv.HasTranslation() { for i := range t { if t[i], err = b.DecryptFile(epe.Translation[i], sv.Translation[i]); err != nil { return &idd, fs, s, nil, err } } } return &idd, fs, s, t, nil } func (epe *EncryptedPassportElement) DecryptDriverLicense(sv *SecureValue, b *Bot) (*IDDocumentData, []byte, []byte, []byte, [][]byte, error) { if !epe.IsDriverLicense() || !sv.HasData() || !sv.HasFrontSide() || !sv.HasReverseSide() { return nil, nil, nil, nil, nil, nil } body, err := sv.Data.decrypt(epe.Data) if err != nil { return nil, nil, nil, nil, nil, err } var idd IDDocumentData if err = b.marshler.Unmarshal(body, &idd); err != nil { return nil, nil, nil, nil, nil, err } fs, err := b.DecryptFile(epe.FrontSide, sv.FrontSide) if err != nil { return &idd, nil, nil, nil, nil, err } rs, err := b.DecryptFile(epe.ReverseSide, sv.ReverseSide) if err != nil { return &idd, nil, nil, nil, nil, err } var s []byte if sv.HasSelfie() { if s, err = b.DecryptFile(epe.Selfie, sv.Selfie); err != nil { return &idd, fs, rs, nil, nil, err } } t := make([][]byte, len(sv.Translation)) if sv.HasTranslation() { for i := range t { if t[i], err = b.DecryptFile(epe.Translation[i], sv.Translation[i]); err != nil { return &idd, fs, rs, s, nil, err } } } return &idd, fs, rs, s, t, nil } func (epe *EncryptedPassportElement) IsAddress() bool { return epe != nil && strings.EqualFold(epe.Type, TypeAddress) } func (epe *EncryptedPassportElement) IsBankStatement() bool { return epe != nil && strings.EqualFold(epe.Type, TypeBankStatement) } func (epe *EncryptedPassportElement) IsDriverLicense() bool { return epe != nil && strings.EqualFold(epe.Type, TypeDriverLicense) } func (epe *EncryptedPassportElement) IsEmail() bool { return epe != nil && strings.EqualFold(epe.Type, TypeEmail) } func (epe *EncryptedPassportElement) IsIdentityCard() bool { return epe != nil && strings.EqualFold(epe.Type, TypeIdentityCard) } func (epe *EncryptedPassportElement) IsInternalPassport() bool { return epe != nil && strings.EqualFold(epe.Type, TypeInternalPassport) } func (epe *EncryptedPassportElement) IsPassport() bool { return epe != nil && strings.EqualFold(epe.Type, TypePassport) } func (epe *EncryptedPassportElement) IsPassportRegistration() bool { return epe != nil && strings.EqualFold(epe.Type, TypePassportRegistration) } func (epe *EncryptedPassportElement) IsPersonalDetails() bool { return epe != nil && strings.EqualFold(epe.Type, TypePersonalDetails) } func (epe *EncryptedPassportElement) IsPhoneNumber() bool { return epe != nil && strings.EqualFold(epe.Type, TypePhoneNumber) } func (epe *EncryptedPassportElement) IsRentalAgreement() bool { return epe != nil && strings.EqualFold(epe.Type, TypeRentalAgreement) } func (epe *EncryptedPassportElement) IsTemporaryRegistration() bool { return epe != nil && strings.EqualFold(epe.Type, TypeTemporaryRegistration) } func (epe *EncryptedPassportElement) IsUtilityBill() bool { return epe != nil && strings.EqualFold(epe.Type, TypeUtilityBill) } func (idd *IDDocumentData) ExpiryTime() *time.Time { if idd == nil || idd.ExpiryDate == "" { return nil } et, err := time.Parse("02.01.2006", idd.ExpiryDate) if err != nil { return nil } return &et } func decrypt(pk *rsa.PrivateKey, s, h, d string) (obj []byte, err error) { // Note that all base64-encoded fields should be decoded before use. secret, err := decodeField(s) if err != nil { return nil, err } hash, err := decodeField(h) if err != nil { return nil, err } data, err := decodeField(d) if err != nil { return nil, err } if pk != nil { // Decrypt the credentials secret (secret field in EncryptedCredentials) // using your private key if secret, err = decryptSecret(pk, secret); err != nil { return nil, err } } // Use this secret and the credentials hash (hash field in // EncryptedCredentials) to calculate credentials_key and credentials_iv key, iv := decryptSecretHash(secret, hash) // Decrypt the credentials data (data field in EncryptedCredentials) by // AES256-CBC using these credentials_key and credentials_iv. if data, err = decryptData(key, iv, data); err != nil { return nil, err } // IMPORTANT: At this step, make sure that the credentials hash is equal // to SHA256(credentials_data) if !match(hash, data) { return nil, ErrNotEqual } // Credentials data is padded with 32 to 255 random padding bytes to make // its length divisible by 16 bytes. The first byte contains the length // of this padding (including this byte). Remove the padding to get the // data. return data[int(data[0]):], nil } func decodeField(rawField string) (field []byte, err error) { return base64.StdEncoding.DecodeString(rawField) } func decryptSecret(pk *rsa.PrivateKey, s []byte) (secret []byte, err error) { return rsa.DecryptOAEP(sha1.New(), rand.Reader, pk, s, nil) //nolint: gosec } func decryptSecretHash(s, h []byte) (key, iv []byte) { var err error hash := sha512.New() if _, err = hash.Write(s); err != nil { return } if _, err = hash.Write(h); err != nil { return } sh := hash.Sum(nil) return sh[0:32], sh[32 : 32+16] } func match(h, d []byte) bool { dh := sha256.New() if _, err := dh.Write(d); err != nil { return false } return bytes.EqualFold(h, dh.Sum(nil)) } func decryptData(key, iv, data []byte) (buf []byte, err error) { block, err := aes.NewCipher(key) if err != nil { return } buf = make([]byte, len(data)) cipher.NewCBCDecrypter(block, iv).CryptBlocks(buf, data) return } func (pd *PersonalDetails) BirthTime() *time.Time { if pd == nil || pd.BirthDate == "" { return nil } bt, err := time.Parse("02.01.2006", pd.BirthDate) if err != nil { return nil } return &bt } func (pd PersonalDetails) FullName() string { return pd.FirstName + " " + pd.LastName } func (pd PersonalDetails) FullNameNative() string { return pd.FirstNameNative + " " + pd.LastNameNative } func (sv *SecureValue) HasData() bool { return sv != nil && sv.Data != nil } func (sv *SecureValue) HasFiles() bool { return sv != nil && len(sv.Files) > 0 } func (sv *SecureValue) HasFrontSide() bool { return sv != nil && sv.FrontSide != nil } func (sv *SecureValue) HasReverseSide() bool { return sv != nil && sv.ReverseSide != nil } func (sv *SecureValue) HasSelfie() bool { return sv != nil && sv.Selfie != nil } func (sv *SecureValue) HasTranslation() bool { return sv != nil && len(sv.Translation) > 0 } */