Commit 21fb9b21 authored by ale's avatar ale

Use the unified composite type package from ai3/go-common

Avoid implementing our own version of asp/userenckey/u2f serialization
and deserialization.
parent a5907c63
Pipeline #2556 passed with stages
in 5 minutes and 39 seconds
...@@ -8,6 +8,7 @@ import ( ...@@ -8,6 +8,7 @@ import (
"strings" "strings"
"testing" "testing"
ct "git.autistici.org/ai3/go-common/ldap/compositetypes"
"git.autistici.org/ai3/go-common/pwhash" "git.autistici.org/ai3/go-common/pwhash"
sso "git.autistici.org/id/go-sso" sso "git.autistici.org/id/go-sso"
) )
...@@ -20,7 +21,7 @@ type fakeBackend struct { ...@@ -20,7 +21,7 @@ type fakeBackend struct {
passwords map[string]string passwords map[string]string
recoveryPasswords map[string]string recoveryPasswords map[string]string
appSpecificPasswords map[string][]*AppSpecificPasswordInfo appSpecificPasswords map[string][]*AppSpecificPasswordInfo
encryptionKeys map[string][]*UserEncryptionKey encryptionKeys map[string][]*ct.EncryptedKey
} }
func (b *fakeBackend) NewTransaction() (TX, error) { func (b *fakeBackend) NewTransaction() (TX, error) {
...@@ -148,11 +149,11 @@ func (b *fakeBackend) SetResourcePassword(_ context.Context, r *Resource, passwo ...@@ -148,11 +149,11 @@ func (b *fakeBackend) SetResourcePassword(_ context.Context, r *Resource, passwo
return nil return nil
} }
func (b *fakeBackend) GetUserEncryptionKeys(_ context.Context, user *User) ([]*UserEncryptionKey, error) { func (b *fakeBackend) GetUserEncryptionKeys(_ context.Context, user *User) ([]*ct.EncryptedKey, error) {
return b.encryptionKeys[user.Name], nil return b.encryptionKeys[user.Name], nil
} }
func (b *fakeBackend) SetUserEncryptionKeys(_ context.Context, user *User, keys []*UserEncryptionKey) error { func (b *fakeBackend) SetUserEncryptionKeys(_ context.Context, user *User, keys []*ct.EncryptedKey) error {
b.encryptionKeys[user.Name] = keys b.encryptionKeys[user.Name] = keys
b.users[user.Name].HasEncryptionKeys = true b.users[user.Name].HasEncryptionKeys = true
return nil return nil
...@@ -233,7 +234,7 @@ func createFakeBackend() *fakeBackend { ...@@ -233,7 +234,7 @@ func createFakeBackend() *fakeBackend {
passwords: make(map[string]string), passwords: make(map[string]string),
recoveryPasswords: make(map[string]string), recoveryPasswords: make(map[string]string),
appSpecificPasswords: make(map[string][]*AppSpecificPasswordInfo), appSpecificPasswords: make(map[string][]*AppSpecificPasswordInfo),
encryptionKeys: make(map[string][]*UserEncryptionKey), encryptionKeys: make(map[string][]*ct.EncryptedKey),
} }
fb.addUser(&User{ fb.addUser(&User{
Name: testUser, Name: testUser,
......
package backend package backend
import ( import (
"errors"
"fmt"
"strings"
as "git.autistici.org/ai3/accountserver" as "git.autistici.org/ai3/accountserver"
"github.com/tstranex/u2f" ct "git.autistici.org/ai3/go-common/ldap/compositetypes"
) )
// Extend the AppSpecificPasswordInfo type, which only contains public func newAppSpecificPassword(info as.AppSpecificPasswordInfo, pw string) *ct.AppSpecificPassword {
// information, with the encrypted password. return &ct.AppSpecificPassword{
type appSpecificPassword struct { ID: info.ID,
as.AppSpecificPasswordInfo Service: info.Service,
Password string Comment: info.Comment,
} EncryptedPassword: pw,
func (p *appSpecificPassword) Encode() string {
return strings.Join([]string{
p.Service,
p.Password,
p.Comment,
}, ":")
}
func newAppSpecificPassword(info as.AppSpecificPasswordInfo, pw string) *appSpecificPassword {
return &appSpecificPassword{
AppSpecificPasswordInfo: info,
Password: pw,
} }
} }
func parseAppSpecificPassword(asp string) (*appSpecificPassword, error) { func decodeAppSpecificPasswords(values []string) []*ct.AppSpecificPassword {
parts := strings.Split(asp, ":") var out []*ct.AppSpecificPassword
if len(parts) != 3 { for _, value := range values {
return nil, errors.New("badly encoded app-specific password") if asp, err := ct.UnmarshalAppSpecificPassword(value); err == nil {
out = append(out, asp)
}
} }
return newAppSpecificPassword(as.AppSpecificPasswordInfo{ return out
Service: parts[0],
Comment: parts[2],
}, parts[1]), nil
} }
func decodeAppSpecificPasswords(values []string) []*appSpecificPassword { func excludeASPFromList(asps []*ct.AppSpecificPassword, id string) []*ct.AppSpecificPassword {
var out []*appSpecificPassword var out []*ct.AppSpecificPassword
for _, value := range values { for _, asp := range asps {
if asp, err := parseAppSpecificPassword(value); err == nil { if asp.ID != id {
out = append(out, asp) out = append(out, asp)
} }
} }
return out return out
} }
func encodeAppSpecificPasswords(asps []*appSpecificPassword) []string { func encodeAppSpecificPasswords(asps []*ct.AppSpecificPassword) []string {
var out []string var out []string
for _, asp := range asps { for _, asp := range asps {
out = append(out, asp.Encode()) out = append(out, asp.Marshal())
} }
return out return out
} }
func getASPInfo(asps []*appSpecificPassword) []*as.AppSpecificPasswordInfo { func getASPInfo(asps []*ct.AppSpecificPassword) []*as.AppSpecificPasswordInfo {
var out []*as.AppSpecificPasswordInfo var out []*as.AppSpecificPasswordInfo
for _, asp := range asps { for _, asp := range asps {
out = append(out, &asp.AppSpecificPasswordInfo) out = append(out, &as.AppSpecificPasswordInfo{
ID: asp.ID,
Service: asp.Service,
Comment: asp.Comment,
})
} }
return out return out
} }
func decodeUserEncryptionKeys(values []string) []*as.UserEncryptionKey { func decodeUserEncryptionKeys(values []string) []*ct.EncryptedKey {
var out []*as.UserEncryptionKey var out []*ct.EncryptedKey
for _, value := range values { for _, value := range values {
idx := strings.IndexByte(value, ':') k, err := ct.UnmarshalEncryptedKey(value)
if idx < 0 { if err != nil {
continue continue
} }
out = append(out, &as.UserEncryptionKey{ out = append(out, k)
ID: value[:idx],
Key: []byte(value[idx+1:]),
})
} }
return out return out
} }
func encodeUserEncryptionKeys(keys []*as.UserEncryptionKey) []string { func encodeUserEncryptionKeys(keys []*ct.EncryptedKey) []string {
var out []string var out []string
for _, key := range keys { for _, key := range keys {
out = append(out, fmt.Sprintf("%s:%s", key.ID, string(key.Key))) out = append(out, key.Marshal())
} }
return out return out
} }
func decodeU2FRegistration(enc string) (*as.U2FRegistration, error) {
var reg u2f.Registration
if err := reg.UnmarshalBinary([]byte(enc)); err != nil {
return nil, err
}
return &as.U2FRegistration{Registration: &reg}, nil
}
func encodeU2FRegistration(r *as.U2FRegistration) string {
// MarshalBinary can't fail, ignore error.
b, _ := r.MarshalBinary() // nolint
return string(b)
}
func decodeU2FRegistrations(encRegs []string) []*as.U2FRegistration { func decodeU2FRegistrations(encRegs []string) []*as.U2FRegistration {
var out []*as.U2FRegistration var out []*as.U2FRegistration
for _, enc := range encRegs { for _, enc := range encRegs {
if r, err := decodeU2FRegistration(enc); err == nil { if r, err := ct.UnmarshalU2FRegistration(enc); err == nil {
out = append(out, r) // Mirror ct.U2FRegistration -> as.U2FRegistration.
out = append(out, &as.U2FRegistration{
KeyHandle: r.KeyHandle,
PublicKey: r.PublicKey,
})
} }
} }
return out return out
...@@ -118,7 +91,12 @@ func decodeU2FRegistrations(encRegs []string) []*as.U2FRegistration { ...@@ -118,7 +91,12 @@ func decodeU2FRegistrations(encRegs []string) []*as.U2FRegistration {
func encodeU2FRegistrations(regs []*as.U2FRegistration) []string { func encodeU2FRegistrations(regs []*as.U2FRegistration) []string {
var out []string var out []string
for _, r := range regs { for _, r := range regs {
out = append(out, encodeU2FRegistration(r)) // Mirror as.U2FRegistration -> ct.U2FRegistration.
ctr := ct.U2FRegistration{
KeyHandle: r.KeyHandle,
PublicKey: r.PublicKey,
}
out = append(out, ctr.Marshal())
} }
return out return out
} }
...@@ -9,6 +9,7 @@ import ( ...@@ -9,6 +9,7 @@ import (
"time" "time"
ldaputil "git.autistici.org/ai3/go-common/ldap" ldaputil "git.autistici.org/ai3/go-common/ldap"
ct "git.autistici.org/ai3/go-common/ldap/compositetypes"
"gopkg.in/ldap.v3" "gopkg.in/ldap.v3"
as "git.autistici.org/ai3/accountserver" as "git.autistici.org/ai3/accountserver"
...@@ -319,7 +320,7 @@ func (tx *backendTX) DeleteAccountRecoveryHint(ctx context.Context, user *as.Use ...@@ -319,7 +320,7 @@ func (tx *backendTX) DeleteAccountRecoveryHint(ctx context.Context, user *as.Use
return nil return nil
} }
func (tx *backendTX) SetUserEncryptionKeys(ctx context.Context, user *as.User, keys []*as.UserEncryptionKey) error { func (tx *backendTX) SetUserEncryptionKeys(ctx context.Context, user *as.User, keys []*ct.EncryptedKey) error {
encKeys := encodeUserEncryptionKeys(keys) encKeys := encodeUserEncryptionKeys(keys)
for _, r := range user.GetResourcesByType(as.ResourceTypeEmail) { for _, r := range user.GetResourcesByType(as.ResourceTypeEmail) {
dn, err := tx.backend.resources.GetDN(r.ID) dn, err := tx.backend.resources.GetDN(r.ID)
...@@ -342,16 +343,6 @@ func (tx *backendTX) SetUserEncryptionPublicKey(ctx context.Context, user *as.Us ...@@ -342,16 +343,6 @@ func (tx *backendTX) SetUserEncryptionPublicKey(ctx context.Context, user *as.Us
return nil return nil
} }
func excludeASPFromList(asps []*appSpecificPassword, id string) []*appSpecificPassword {
var out []*appSpecificPassword
for _, asp := range asps {
if asp.ID != id {
out = append(out, asp)
}
}
return out
}
func (tx *backendTX) SetApplicationSpecificPassword(ctx context.Context, user *as.User, info *as.AppSpecificPasswordInfo, encryptedPassword string) error { func (tx *backendTX) SetApplicationSpecificPassword(ctx context.Context, user *as.User, info *as.AppSpecificPasswordInfo, encryptedPassword string) error {
dn := tx.getUserDN(user) dn := tx.getUserDN(user)
asps := decodeAppSpecificPasswords(tx.readAttributeValues(ctx, dn, aspLDAPAttr)) asps := decodeAppSpecificPasswords(tx.readAttributeValues(ctx, dn, aspLDAPAttr))
......
...@@ -10,6 +10,7 @@ import ( ...@@ -10,6 +10,7 @@ import (
as "git.autistici.org/ai3/accountserver" as "git.autistici.org/ai3/accountserver"
"git.autistici.org/ai3/accountserver/ldaptest" "git.autistici.org/ai3/accountserver/ldaptest"
ct "git.autistici.org/ai3/go-common/ldap/compositetypes"
) )
const ( const (
...@@ -307,10 +308,10 @@ func TestModel_SetUserEncryptionKeys_Add(t *testing.T) { ...@@ -307,10 +308,10 @@ func TestModel_SetUserEncryptionKeys_Add(t *testing.T) {
defer stop() defer stop()
tx, _ := b.NewTransaction() tx, _ := b.NewTransaction()
keys := []*as.UserEncryptionKey{ keys := []*ct.EncryptedKey{
{ {
ID: as.UserEncryptionKeyMainID, ID: as.UserEncryptionKeyMainID,
Key: []byte("very secret key"), EncryptedKey: []byte("very secret key"),
}, },
} }
if err := tx.SetUserEncryptionKeys(context.Background(), &user.User, keys); err != nil { if err := tx.SetUserEncryptionKeys(context.Background(), &user.User, keys); err != nil {
...@@ -326,10 +327,10 @@ func TestModel_SetUserEncryptionKeys_Replace(t *testing.T) { ...@@ -326,10 +327,10 @@ func TestModel_SetUserEncryptionKeys_Replace(t *testing.T) {
defer stop() defer stop()
tx, _ := b.NewTransaction() tx, _ := b.NewTransaction()
keys := []*as.UserEncryptionKey{ keys := []*ct.EncryptedKey{
{ {
ID: as.UserEncryptionKeyMainID, ID: as.UserEncryptionKeyMainID,
Key: []byte("very secret key"), EncryptedKey: []byte("very secret key"),
}, },
} }
if err := tx.SetUserEncryptionKeys(context.Background(), &user.User, keys); err != nil { if err := tx.SetUserEncryptionKeys(context.Background(), &user.User, keys); err != nil {
......
...@@ -18,6 +18,7 @@ import ( ...@@ -18,6 +18,7 @@ import (
"git.autistici.org/ai3/accountserver/backend" "git.autistici.org/ai3/accountserver/backend"
"git.autistici.org/ai3/accountserver/ldaptest" "git.autistici.org/ai3/accountserver/ldaptest"
"git.autistici.org/ai3/accountserver/server" "git.autistici.org/ai3/accountserver/server"
ct "git.autistici.org/ai3/go-common/ldap/compositetypes"
"git.autistici.org/ai3/go-common/pwhash" "git.autistici.org/ai3/go-common/pwhash"
"git.autistici.org/ai3/go-common/userenckey" "git.autistici.org/ai3/go-common/userenckey"
"git.autistici.org/id/go-sso" "git.autistici.org/id/go-sso"
...@@ -199,10 +200,10 @@ func checkUserInvariants(t *testing.T, be as.Backend, username, primaryPassword ...@@ -199,10 +200,10 @@ func checkUserInvariants(t *testing.T, be as.Backend, username, primaryPassword
return user return user
} }
func keysToBytes(keys []*as.UserEncryptionKey) [][]byte { func keysToBytes(keys []*ct.EncryptedKey) [][]byte {
var rawKeys [][]byte var rawKeys [][]byte
for _, k := range keys { for _, k := range keys {
rawKeys = append(rawKeys, k.Key) rawKeys = append(rawKeys, k.EncryptedKey)
} }
return rawKeys return rawKeys
} }
package accountserver
import (
ct "git.autistici.org/ai3/go-common/ldap/compositetypes"
"git.autistici.org/ai3/go-common/userenckey"
)
// A list of encrypted keys, all copies of the same key but encrypted with
// different passwords.
type encryptedKeyList []*ct.EncryptedKey
func newEncryptionKeys(encryptionPassword string) ([]byte, encryptedKeyList, error) {
pub, priv, err := userenckey.GenerateKey()
if err != nil {
return nil, nil, err
}
encrypted, err := userenckey.Encrypt(priv, []byte(encryptionPassword))
if err != nil {
return nil, nil, err
}
l := encryptedKeyList([]*ct.EncryptedKey{
&ct.EncryptedKey{
ID: UserEncryptionKeyMainID,
EncryptedKey: encrypted,
},
})
return pub, l, nil
}
func keysToBytes(keys []*ct.EncryptedKey) [][]byte {
var rawKeys [][]byte
for _, k := range keys {
rawKeys = append(rawKeys, k.EncryptedKey)
}
return rawKeys
}
func (l encryptedKeyList) add(keyID, unlockPassword, encryptionPassword string) (encryptedKeyList, error) {
decrypted, err := userenckey.Decrypt(keysToBytes(l), []byte(unlockPassword))
if err != nil {
return nil, err
}
encrypted, err := userenckey.Encrypt(decrypted, []byte(encryptionPassword))
if err != nil {
return nil, err
}
l = l.deleteByID(keyID)
return append(l, &ct.EncryptedKey{
ID: keyID,
EncryptedKey: encrypted,
}), nil
}
func (l encryptedKeyList) deleteByID(keyID string) encryptedKeyList {
var out encryptedKeyList
for _, k := range l {
if k.ID != keyID {
out = append(out, k)
}
}
return out
}
// Return the ID for the encrypted key associated with an app-specific
// password.
func aspKeyID(aspID string) string {
return "asp_" + aspID
}
...@@ -6,6 +6,7 @@ import ( ...@@ -6,6 +6,7 @@ import (
"log" "log"
"time" "time"
ct "git.autistici.org/ai3/go-common/ldap/compositetypes"
"git.autistici.org/id/go-sso" "git.autistici.org/id/go-sso"
umdb "git.autistici.org/id/usermetadb" umdb "git.autistici.org/id/usermetadb"
umdbc "git.autistici.org/id/usermetadb/client" umdbc "git.autistici.org/id/usermetadb/client"
...@@ -55,7 +56,7 @@ type TX interface { ...@@ -55,7 +56,7 @@ type TX interface {
SetUserPassword(context.Context, *User, string) error SetUserPassword(context.Context, *User, string) error
SetAccountRecoveryHint(context.Context, *User, string, string) error SetAccountRecoveryHint(context.Context, *User, string, string) error
DeleteAccountRecoveryHint(context.Context, *User) error DeleteAccountRecoveryHint(context.Context, *User) error
SetUserEncryptionKeys(context.Context, *User, []*UserEncryptionKey) error SetUserEncryptionKeys(context.Context, *User, []*ct.EncryptedKey) error
SetUserEncryptionPublicKey(context.Context, *User, []byte) error SetUserEncryptionPublicKey(context.Context, *User, []byte) error
SetApplicationSpecificPassword(context.Context, *User, *AppSpecificPasswordInfo, string) error SetApplicationSpecificPassword(context.Context, *User, *AppSpecificPasswordInfo, string) error
DeleteApplicationSpecificPassword(context.Context, *User, string) error DeleteApplicationSpecificPassword(context.Context, *User, string) error
......
...@@ -8,8 +8,6 @@ import ( ...@@ -8,8 +8,6 @@ import (
"time" "time"
"git.autistici.org/ai3/go-common/pwhash" "git.autistici.org/ai3/go-common/pwhash"
"git.autistici.org/ai3/go-common/userenckey"
"github.com/tstranex/u2f"
) )
// Possible values for user status. // Possible values for user status.
...@@ -339,63 +337,6 @@ func (u *RawUser) deleteAllApplicationSpecificPasswords(ctx context.Context, tx ...@@ -339,63 +337,6 @@ func (u *RawUser) deleteAllApplicationSpecificPasswords(ctx context.Context, tx
return nil return nil
} }
// A list of encrypted keys, all copies of the same key but encrypted with
// different passwords.
type encryptedKeyList []*UserEncryptionKey
func newEncryptionKeys(encryptionPassword string) ([]byte, encryptedKeyList, error) {
pub, priv, err := userenckey.GenerateKey()
if err != nil {
return nil, nil, err
}
encrypted, err := userenckey.Encrypt(priv, []byte(encryptionPassword))
if err != nil {
return nil, nil, err
}
l := encryptedKeyList([]*UserEncryptionKey{
&UserEncryptionKey{
ID: UserEncryptionKeyMainID,
Key: encrypted,
},
})
return pub, l, nil
}
func keysToBytes(keys []*UserEncryptionKey) [][]byte {
var rawKeys [][]byte
for _, k := range keys {
rawKeys = append(rawKeys, k.Key)
}
return rawKeys
}
func (l encryptedKeyList) add(keyID, unlockPassword, encryptionPassword string) (encryptedKeyList, error) {
decrypted, err := userenckey.Decrypt(keysToBytes(l), []byte(unlockPassword))
if err != nil {
return nil, err
}
encrypted, err := userenckey.Encrypt(decrypted, []byte(encryptionPassword))
if err != nil {
return nil, err
}
l = l.deleteByID(keyID)
return append(l, &UserEncryptionKey{
ID: keyID,
Key: encrypted,
}), nil
}
func (l encryptedKeyList) deleteByID(keyID string) encryptedKeyList {
var out encryptedKeyList
for _, k := range l {
if k.ID != keyID {
out = append(out, k)
}
}
return out
}
// AppSpecificPasswordInfo stores public information about an // AppSpecificPasswordInfo stores public information about an
// app-specific password. // app-specific password.
type AppSpecificPasswordInfo struct { type AppSpecificPasswordInfo struct {
...@@ -404,10 +345,6 @@ type AppSpecificPasswordInfo struct { ...@@ -404,10 +345,6 @@ type AppSpecificPasswordInfo struct {
Comment string `json:"comment"` Comment string `json:"comment"`
} }
func aspKeyID(aspID string) string {
return "asp_" + aspID
}
// Well-known user encryption key types, corresponding to primary and // Well-known user encryption key types, corresponding to primary and
// secondary passwords. // secondary passwords.
const ( const (
...@@ -415,13 +352,6 @@ const ( ...@@ -415,13 +352,6 @@ const (
UserEncryptionKeyRecoveryID = "recovery" UserEncryptionKeyRecoveryID = "recovery"
) )
// UserEncryptionKey stores a password-encrypted secret key for the
// user's encrypted storage.
type UserEncryptionKey struct {
ID string `json:"id"`
Key []byte `json:"key"`
}
// Resource types. // Resource types.
const ( const (
ResourceTypeEmail = "email" ResourceTypeEmail = "email"
...@@ -659,32 +589,12 @@ func getHostingDir(path, siteRoot string) string { ...@@ -659,32 +589,12 @@ func getHostingDir(path, siteRoot string) string {
return path return path
} }
// U2FRegistration is just a thin wrapper for u2f.Registration that // U2FRegistration stores information on a single U2F device registration.
// supports serialization and deserialization to JSON. The serialized //
// format is simply the raw registration data (base64-encoded for // This is a mirror of compositetypes.U2FRegistration, but it is
// transport). // duplicated since this is part of our public interface, and
// compositetypes is a detail of the LDAP backend implementation.
type U2FRegistration struct { type U2FRegistration struct {
*u2f.Registration KeyHandle []byte `json:"key_handle"`
} PublicKey []byte `json:"public_key"`
// MarshalJSON implements the json.Marshaler interface.
func (r *U2FRegistration) MarshalJSON() ([]byte, error) {
data, err := r.Registration.MarshalBinary()
if err != nil {
return nil, err
}
return json.Marshal(data)
}
// UnmarshalJSON implements the json.Unmarshaler interface.
func (r *U2FRegistration) UnmarshalJSON(data []byte) error {
if data == nil {
return nil
}
var b []byte
if err := json.Unmarshal(data, &b); err != nil {
return err
}
r.Registration = new(u2f.Registration)
return r.Registration.UnmarshalBinary(b)
} }
package accountserver
import (
"encoding/json"
"testing"
)
var testReg = `"BQRymreLJraApU27oZ0dpjFW7VsjwfRUwblUfhEPUr9zOiryBSvCmVAL0pqJAMuV70qu6U0t70hMo0cUxec0evdKQFlPUJcS2P0HgoyNq8JHGQ5LjF26gbYRDSkCmdqxjP/i8lPSTiFqAucnLhAhQaAw7khfLJ3nszzNNCrgExmE6ocwggGHMIIBLqADAgECAgkAmb7osQyi7BwwCQYHKoZIzj0EATAhMR8wHQYDVQQDDBZZdWJpY28gVTJGIFNvZnQgRGV2aWNlMB4XDTEzMDcxNzE0MjEwM1oXDTE2MDcxNjE0MjEwM1owITEfMB0GA1UEAwwWWXViaWNvIFUyRiBTb2Z0IERldmljZTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABDvhl91zfpg9n7DeCedcQ8gGXUnemiXoi+JEAxz+EIhkVsMPAyzhtJZ4V3CqMZ+MOUgICt2aMxacMX9cIa8dgS2jUDBOMB0GA1UdDgQWBBQNqL+TV04iaO6mS5tjGE6ShfexnjAfBgNVHSMEGDAWgBQNqL+TV04iaO6mS5tjGE6ShfexnjAMBgNVHRMEBTADAQH/MAkGByqGSM49BAEDSAAwRQIgXJWZdbvOWdhVaG7IJtn44o21Kmi8EHsDk4cAfnZ0r38CIQD6ZPi3Pl4lXxbY7BXFyrpkiOvCpdyNdLLYbSTbvIBQOTBGAiEA2oN5nlqtnpgV2UQNv2bIa29vyoTB+oBj9xkaGU8Ozm8CIQDueoTlbdFV7sLLMpj5jwpqktwl6UiPZnmQtpUyYmc21w=="`
func TestU2FRegistration_Unmarshal(t *testing.T) {
var reg U2FRegistration
if err := json.Unmarshal([]byte(testReg), &reg); err != nil {
t.Fatal(err)
}
}
// Package compositetypes provides Go types for the composite values
// stored in our LDAP database, so that various authentication
// packages can agree on their serialized representation.
//
// These are normally 1-to-many associations that are wrapped into
// repeated LDAP attributes instead of separate nested objects, for
// simplicity and latency reasons.
//
// Whenever there is an 'id' field, it's a unique (per-user)
// identifier used to recognize a specific entry on modify/delete.
//
// The serialized values can be arbitrary []byte sequences (the LDAP
// schema should specify the right types for the associated
// attributes).
//
package compositetypes
import (
"crypto/elliptic"
"errors"
"strings"
"github.com/tstranex/u2f"
)
// AppSpecificPassword stores information on an application-specific
// password.
//
// Serialized as colon-separated fields with the format:
//
// id:service:encrypted_password:comment
//
// Where 'comment' is free-form and can contain colons, no escaping is
// performed.
type AppSpecificPassword struct {
ID string `json:"id"`
Service string `json:"service"`
EncryptedPassword string `json:"encrypted_password"`
Comment string `json:"comment"`
}
// Marshal returns the serialized format.
func (p *AppSpecificPassword) Marshal() string {
return strings.Join([]string{
p.ID,
p.Service,
p.EncryptedPassword,
p.Comment,
}, ":")
}
// UnmarshalAppSpecificPassword parses a serialized representation of
// an AppSpecificPassword.
func UnmarshalAppSpecificPassword(s string) (*AppSpecificPassword, error) {
parts := strings.SplitN(s, ":", 4)
if len(parts) != 4 {
return nil, errors.New("badly encoded app-specific password")
}
return &AppSpecificPassword{
ID: parts[0],
Service: parts[1],
EncryptedPassword: parts[2],
Comment: parts[3],
}, nil
}
// EncryptedKey stores a password-encrypted secret key.
//
// Serialized as colon-separated fields with the format:
//